diff --git a/utils/image_handler.py b/utils/image_handler.py index 91b247a..f8433cd 100644 --- a/utils/image_handler.py +++ b/utils/image_handler.py @@ -1,6 +1,10 @@ from PIL import Image, ImageDraw, ImageFont, ImageEnhance import piexif import time +from fractions import Fraction + +from debug import my_debugging_tools +d = my_debugging_tools() class ImageProcessor: """Functions using pillow are in here.""" @@ -38,15 +42,16 @@ class ImageProcessor: resized_image = image.resize((new_size),resample=Image.Resampling.NEAREST) return resized_image - def add_watermark(self, image, text, font_size_scale = 70): + def add_watermark(self, image, text, font_size_percentage): drawer = ImageDraw.Draw(image) imagewidth, imageheight = image.size margin = (imageheight / 100 ) * 2 # margin dynamic, 2% of image size - font_size = imagewidth / font_size_scale # Scaling the font size + font_size = imagewidth * (font_size_percentage / 100) + try: # Try loading front, if notaviable return unmodified image - font = ImageFont.truetype("OpenDyslexic3-Regular.ttf", font_size) - except: - print("Error loading font for watermark, please ensure font is installed...\n") + font = ImageFont.load_default(font_size) + except Exception as e: + print(f"Error {e}\nloading font for watermark, please ensure font is installed...\n") time.sleep(0.1) return image @@ -64,7 +69,7 @@ class ImageProcessor: return image - def save_image(self, image, path, file_type, jpg_quality, png_compressing, optimize, exif_data = None): + def save_image(self, image, path, file_type, jpg_quality, png_compressing, optimize, exif_data): # partly optimized by chatGPT """ Save an image to the specified path with optional EXIF data and optimization. @@ -72,7 +77,7 @@ class ImageProcessor: file_type = file_type.lower() save_params = {"optimize": optimize} # Add file-specific parameters - if file_type == "jpg": + if file_type == "jpg" or "webp": save_params["quality"] = jpg_quality elif file_type == "png": save_params["compress_level"] = png_compressing @@ -102,21 +107,97 @@ class ExifHandler: """Build a piexif-compatible EXIF dictionary from user data.""" # Mostly made by ChatGPT, some adjustment zeroth_ifd = { - piexif.ImageIFD.Make: user_data["make"], - piexif.ImageIFD.Model: user_data["model"], - piexif.ImageIFD.Software: user_data["software"], - piexif.ImageIFD.Copyright: user_data["copyright_info"], - piexif.ImageIFD.Artist: user_data["artist"], - piexif.ImageIFD.ImageDescription: user_data["image_description"], + piexif.ImageIFD.Make: user_data["make"].encode("utf-8"), + piexif.ImageIFD.Model: user_data["model"].encode("utf-8"), + piexif.ImageIFD.Software: user_data["software"].encode("utf-8"), + piexif.ImageIFD.Copyright: user_data["copyright_info"].encode("utf-8"), + piexif.ImageIFD.Artist: user_data["artist"].encode("utf-8"), + piexif.ImageIFD.ImageDescription: user_data["image_description"].encode("utf-8"), piexif.ImageIFD.XResolution: (72, 1), piexif.ImageIFD.YResolution: (72, 1), } exif_ifd = { - piexif.ExifIFD.UserComment: user_data["user_comment"], - piexif.ExifIFD.ISOSpeedRatings: int(user_data["iso"]), + piexif.ExifIFD.UserComment: user_data["user_comment"].encode("utf-8"), + piexif.ExifIFD.ISOSpeedRatings: int(user_data["iso"].encode("utf-8")), piexif.ExifIFD.PixelXDimension: imagesize[0], piexif.ExifIFD.PixelYDimension: imagesize[1], } if "date_time_original" in user_data: exif_ifd[piexif.ExifIFD.DateTimeOriginal] = user_data["date_time_original"].encode("utf-8") + return {"0th": zeroth_ifd, "Exif": exif_ifd} + + def deg_to_dms(self, decimal_coordinate, cardinal_directions): + """ + This function converts decimal coordinates into the DMS (degrees, minutes and seconds) format. + It also determines the cardinal direction of the coordinates. + + :param decimal_coordinate: the decimal coordinates, such as 34.0522 + :param cardinal_directions: the locations of the decimal coordinate, such as ["S", "N"] or ["W", "E"] + :return: degrees, minutes, seconds and compass_direction + :rtype: int, int, float, string + """ + if decimal_coordinate < 0: + compass_direction = cardinal_directions[0] + elif decimal_coordinate > 0: + compass_direction = cardinal_directions[1] + else: + compass_direction = "" + degrees = int(abs(decimal_coordinate)) + decimal_minutes = (abs(decimal_coordinate) - degrees) * 60 + minutes = int(decimal_minutes) + seconds = Fraction((decimal_minutes - minutes) * 60).limit_denominator(100) + return degrees, minutes, seconds, compass_direction + + def dms_to_exif_format(self, dms_degrees, dms_minutes, dms_seconds): + """ + This function converts DMS (degrees, minutes and seconds) to values that can + be used with the EXIF (Exchangeable Image File Format). + + :param dms_degrees: int value for degrees + :param dms_minutes: int value for minutes + :param dms_seconds: fractions.Fraction value for seconds + :return: EXIF values for the provided DMS values + :rtype: nested tuple + """ + exif_format = ( + (dms_degrees, 1), + (dms_minutes, 1), + (int(dms_seconds.limit_denominator(100).numerator), int(dms_seconds.limit_denominator(100).denominator)) + ) + return exif_format + + def add_geolocation_to_exif(self, exif_data, latitude, longitude): + """ + https://stackoverflow.com/questions/77015464/adding-exif-gps-data-to-jpg-files-using-python-and-piexif + This function adds GPS values to an image using the EXIF format. + This fumction calls the functions deg_to_dms and dms_to_exif_format. + + :param image_path: image to add the GPS data to + :param latitude: the north–south position coordinate + :param longitude: the east–west position coordinate + """ + # converts the latitude and longitude coordinates to DMS + latitude_dms = self.deg_to_dms(latitude, ["S", "N"]) + longitude_dms = self.deg_to_dms(longitude, ["W", "E"]) + + # convert the DMS values to EXIF values + exif_latitude = self.dms_to_exif_format(latitude_dms[0], latitude_dms[1], latitude_dms[2]) + exif_longitude = self.dms_to_exif_format(longitude_dms[0], longitude_dms[1], longitude_dms[2]) + + try: + # https://exiftool.org/TagNames/GPS.html + # Create the GPS EXIF data + coordinates = { + piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0), + piexif.GPSIFD.GPSLatitude: exif_latitude, + piexif.GPSIFD.GPSLatitudeRef: latitude_dms[3], + piexif.GPSIFD.GPSLongitude: exif_longitude, + piexif.GPSIFD.GPSLongitudeRef: longitude_dms[3] + } + # Update the EXIF data with the GPS information + exif_data["GPS"] = coordinates + + return exif_data + except Exception as e: + print(f"Error: {str(e)}")