functions to add GPS.

This commit is contained in:
Mr Finchum 2024-12-30 21:07:54 +01:00
parent 4aba29c7fc
commit d273236b36

View file

@ -1,6 +1,10 @@
from PIL import Image, ImageDraw, ImageFont, ImageEnhance from PIL import Image, ImageDraw, ImageFont, ImageEnhance
import piexif import piexif
import time import time
from fractions import Fraction
from debug import my_debugging_tools
d = my_debugging_tools()
class ImageProcessor: class ImageProcessor:
"""Functions using pillow are in here.""" """Functions using pillow are in here."""
@ -38,15 +42,16 @@ class ImageProcessor:
resized_image = image.resize((new_size),resample=Image.Resampling.NEAREST) resized_image = image.resize((new_size),resample=Image.Resampling.NEAREST)
return resized_image 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) drawer = ImageDraw.Draw(image)
imagewidth, imageheight = image.size imagewidth, imageheight = image.size
margin = (imageheight / 100 ) * 2 # margin dynamic, 2% of 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 try: # Try loading front, if notaviable return unmodified image
font = ImageFont.truetype("OpenDyslexic3-Regular.ttf", font_size) font = ImageFont.load_default(font_size)
except: except Exception as e:
print("Error loading font for watermark, please ensure font is installed...\n") print(f"Error {e}\nloading font for watermark, please ensure font is installed...\n")
time.sleep(0.1) time.sleep(0.1)
return image return image
@ -64,7 +69,7 @@ class ImageProcessor:
return image 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 # partly optimized by chatGPT
""" """
Save an image to the specified path with optional EXIF data and optimization. Save an image to the specified path with optional EXIF data and optimization.
@ -72,7 +77,7 @@ class ImageProcessor:
file_type = file_type.lower() file_type = file_type.lower()
save_params = {"optimize": optimize} save_params = {"optimize": optimize}
# Add file-specific parameters # Add file-specific parameters
if file_type == "jpg": if file_type == "jpg" or "webp":
save_params["quality"] = jpg_quality save_params["quality"] = jpg_quality
elif file_type == "png": elif file_type == "png":
save_params["compress_level"] = png_compressing save_params["compress_level"] = png_compressing
@ -102,21 +107,97 @@ class ExifHandler:
"""Build a piexif-compatible EXIF dictionary from user data.""" """Build a piexif-compatible EXIF dictionary from user data."""
# Mostly made by ChatGPT, some adjustment # Mostly made by ChatGPT, some adjustment
zeroth_ifd = { zeroth_ifd = {
piexif.ImageIFD.Make: user_data["make"], piexif.ImageIFD.Make: user_data["make"].encode("utf-8"),
piexif.ImageIFD.Model: user_data["model"], piexif.ImageIFD.Model: user_data["model"].encode("utf-8"),
piexif.ImageIFD.Software: user_data["software"], piexif.ImageIFD.Software: user_data["software"].encode("utf-8"),
piexif.ImageIFD.Copyright: user_data["copyright_info"], piexif.ImageIFD.Copyright: user_data["copyright_info"].encode("utf-8"),
piexif.ImageIFD.Artist: user_data["artist"], piexif.ImageIFD.Artist: user_data["artist"].encode("utf-8"),
piexif.ImageIFD.ImageDescription: user_data["image_description"], piexif.ImageIFD.ImageDescription: user_data["image_description"].encode("utf-8"),
piexif.ImageIFD.XResolution: (72, 1), piexif.ImageIFD.XResolution: (72, 1),
piexif.ImageIFD.YResolution: (72, 1), piexif.ImageIFD.YResolution: (72, 1),
} }
exif_ifd = { exif_ifd = {
piexif.ExifIFD.UserComment: user_data["user_comment"], piexif.ExifIFD.UserComment: user_data["user_comment"].encode("utf-8"),
piexif.ExifIFD.ISOSpeedRatings: int(user_data["iso"]), piexif.ExifIFD.ISOSpeedRatings: int(user_data["iso"].encode("utf-8")),
piexif.ExifIFD.PixelXDimension: imagesize[0], piexif.ExifIFD.PixelXDimension: imagesize[0],
piexif.ExifIFD.PixelYDimension: imagesize[1], piexif.ExifIFD.PixelYDimension: imagesize[1],
} }
if "date_time_original" in user_data: if "date_time_original" in user_data:
exif_ifd[piexif.ExifIFD.DateTimeOriginal] = user_data["date_time_original"].encode("utf-8") exif_ifd[piexif.ExifIFD.DateTimeOriginal] = user_data["date_time_original"].encode("utf-8")
return {"0th": zeroth_ifd, "Exif": exif_ifd} 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 northsouth position coordinate
:param longitude: the eastwest 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)}")