feat: BREAKING GPS now must be float

This commit is contained in:
Mr Finchum 2025-01-21 19:05:48 +00:00
parent 8e56565b29
commit 37831e1c02
6 changed files with 70 additions and 26 deletions

View file

@ -1,6 +1,17 @@
# Changelog # Changelog
## 0.6.x ## 0.6.x
### 0.7.0
- **BREAKING CHANGE:** GPS location must now be provided as a float instead of a string.
### 0.6.6
- Added function to insert exif data into image file (i.e. without modifying image)
### 0.6.5 / -a
- No breaking changes to backward compatibility yet.
- Updated the `process` function: an image can now be returned in a modified form without saving. It is returned as a Qt image, which is required for the new UI functionality.
- No change from alpha to *stable*
### 0.6.4 ### 0.6.4
- Released a stable-ish version to ensure compatibility with the current GUI in OptimaLab35 (v0.1.0). - Released a stable-ish version to ensure compatibility with the current GUI in OptimaLab35 (v0.1.0).
- This version serves as a baseline before potential breaking changes in future updates. - This version serves as a baseline before potential breaking changes in future updates.

2
pip_README.md Normal file
View file

@ -0,0 +1,2 @@
Uses pillow and piexif to modify images, see [optima35](https://gitlab.com/CodeByMrFinchum/optima35) gitlab for more information.
Install [OptimaLab35](https://pypi.org/project/OptimaLab35/) in pip for a GUI.

View file

@ -6,8 +6,8 @@ build-backend = "hatchling.build"
name = "optima35" name = "optima35"
dynamic = ["version"] dynamic = ["version"]
authors = [{ name = "Mr. Finchum" }] authors = [{ name = "Mr. Finchum" }]
description = "OPTIMA35 is a package to modify images with pillow and piexif." description = "optima35 is a package to modify images, using pillow and piexif."
readme = "README.md" readme = "pip_README.md"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["piexif", "pillow"] dependencies = ["piexif", "pillow"]
classifiers = [ classifiers = [

View file

@ -1,6 +1,7 @@
from . import __version__ from . import __version__
# From ChatGPT # From ChatGPT
def main(): def main():
print("(C) 2024-2025 Mr. Finchum aka CodeByMrFinchum")
print(f"optima35 (v{__version__}) is a core library and not intended to be run directly.") print(f"optima35 (v{__version__}) is a core library and not intended to be run directly.")
print("Please use OptimaLab35 for a UI, run pip install OptimaLab35 and start with OptimaLab35.") print("Please use OptimaLab35 for a UI, run pip install OptimaLab35 and start with OptimaLab35.")

View file

@ -6,15 +6,15 @@ from optima35 import __version__
class OptimaManager: class OptimaManager:
def __init__(self): def __init__(self):
self.name = "OPTIMA35" self.name = "optima35"
self.version = __version__ self.version = __version__
self.image_processor = ImageProcessor() self.image_processor = ImageProcessor()
self.exif_handler = ExifHandler() self.exif_handler = ExifHandler()
def modify_timestamp_in_exif(self, data_for_exif: dict, filename: str): def modify_timestamp_in_exif(self, data_for_exif: dict, filename: str):
""""Takes a dict formated for exif use by piexif and adjusts the date_time_original, changing the minutes and seconds to fit the number of the filname.""" """"Takes a dict formated for exif use by piexif and adjusts the date_time_original, changing the minutes and seconds to fit the number of the filname."""
last_tree = filename[-3:len(filename)] last_three = filename[-3:len(filename)]
total_seconds = int(re.sub(r'\D+', '', last_tree)) total_seconds = int(re.sub(r'\D+', '', last_three))
minutes = total_seconds // 60 minutes = total_seconds // 60
seconds = total_seconds % 60 seconds = total_seconds % 60
time = datetime.strptime(data_for_exif["date_time_original"], "%Y:%m:%d %H:%M:%S") # change date time string back to an time object for modification time = datetime.strptime(data_for_exif["date_time_original"], "%Y:%m:%d %H:%M:%S") # change date time string back to an time object for modification
@ -22,13 +22,13 @@ class OptimaManager:
data_for_exif["date_time_original"] = new_time.strftime("%Y:%m:%d %H:%M:%S") data_for_exif["date_time_original"] = new_time.strftime("%Y:%m:%d %H:%M:%S")
return data_for_exif return data_for_exif
def process_image(self, def process_image(self, # TODO: split into two classes, one for modification for one saving..
image_input_file, image_input_file,
image_output_file, image_output_file,
file_type, file_type = "jpg",
quality, quality = 90,
compressing, compressing = 6,
optimize, optimize = False,
resize = None, resize = None,
watermark = None, watermark = None,
font_size = 2, font_size = 2,
@ -37,17 +37,17 @@ class OptimaManager:
contrast = None, contrast = None,
dict_for_exif = None, dict_for_exif = None,
gps = None, gps = None,
copy_exif = False): copy_exif = False,
save = True):
# Partly optimized by ChatGPT # Partly optimized by ChatGPT
# Open the image file # Open the image file
with self.image_processor.open_image(image_input_file) as img: with self.image_processor.open_image(image_input_file) as img:
processed_img = img processed_img = img
image_name = os.path.basename(image_output_file) # for date adjustment image_name = os.path.basename(image_output_file) # for date adjustment
# Resize # Resize
if resize is not None: if resize is not None:
processed_img = self.image_processor.resize_image( processed_img = self.image_processor.resize_image(
image=processed_img, percent=resize image=processed_img, percent = resize
) )
# Watermark # Watermark
@ -78,7 +78,7 @@ class OptimaManager:
selected_exif = dict_for_exif selected_exif = dict_for_exif
if "date_time_original" in dict_for_exif: if "date_time_original" in dict_for_exif:
selected_exif = self.modify_timestamp_in_exif(selected_exif, image_name) selected_exif = self.modify_timestamp_in_exif(selected_exif, image_name)
exif_piexif_format = self.exif_handler.build_exif_dict( exif_piexif_format = self.exif_handler.build_exif_bytes(
selected_exif, self.image_processor.get_image_size(processed_img) selected_exif, self.image_processor.get_image_size(processed_img)
) )
@ -97,6 +97,7 @@ class OptimaManager:
except Exception: except Exception:
print("Copying EXIF data selected, but no EXIF data is available in the original image file.") print("Copying EXIF data selected, but no EXIF data is available in the original image file.")
if save:
# Save the processed image # Save the processed image
self.image_processor.save_image( self.image_processor.save_image(
image = processed_img, image = processed_img,
@ -107,3 +108,24 @@ class OptimaManager:
png_compressing = compressing, png_compressing = compressing,
optimize = optimize optimize = optimize
) )
else:
return self.image_processor.convert_pil_to_qtimage(processed_img)
def insert_dict_to_image(self, exif_dict, image_path, gps = None):
image_name, ending = os.path.splitext(os.path.basename(image_path))
img = self.image_processor.open_image(image_path)
selected_exif = exif_dict
if "date_time_original" in exif_dict:
selected_exif = self.modify_timestamp_in_exif(selected_exif, image_name)
exif_piexif_format = self.exif_handler.build_exif_bytes(
selected_exif, self.image_processor.get_image_size(img)
)
# GPS data
if gps is not None:
latitude = gps[0]
longitude = gps[1]
exif_piexif_format = self.exif_handler.add_geolocation_to_exif(exif_piexif_format, latitude, longitude)
self.exif_handler.insert_exif(exif_dict = exif_piexif_format, img_path = image_path)

View file

@ -1,4 +1,4 @@
from PIL import Image, ImageDraw, ImageFont, ImageEnhance from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageQt
import piexif import piexif
from fractions import Fraction from fractions import Fraction
@ -91,6 +91,10 @@ class ImageProcessor:
except Exception as e: except Exception as e:
print(f"Failed to save image: {e}") print(f"Failed to save image: {e}")
def convert_pil_to_qtimage(self, pillow_image):
qt_image = ImageQt.ImageQt(pillow_image)
return qt_image
class ExifHandler: class ExifHandler:
"""Function using piexif are here.""" """Function using piexif are here."""
def __init__(self): def __init__(self):
@ -99,7 +103,7 @@ class ExifHandler:
def get_exif_info(self, image): def get_exif_info(self, image):
return(piexif.load(image.info['exif'])) return(piexif.load(image.info['exif']))
def build_exif_dict(self, user_data, imagesize): def build_exif_bytes(self, user_data, imagesize):
"""Build a piexif-compatible EXIF dictionary from a dicts.""" """Build a piexif-compatible EXIF dictionary from a dicts."""
# Mostly made by ChatGPT, some adjustment # Mostly made by ChatGPT, some adjustment
zeroth_ifd = { zeroth_ifd = {
@ -197,3 +201,7 @@ class ExifHandler:
return exif_data return exif_data
except Exception as e: except Exception as e:
print(f"Error: {str(e)}") print(f"Error: {str(e)}")
def insert_exif(self, exif_dict, img_path):
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, img_path)