diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/image_handler.py b/utils/image_handler.py new file mode 100644 index 0000000..6631a93 --- /dev/null +++ b/utils/image_handler.py @@ -0,0 +1,115 @@ +from PIL import Image, ImageDraw, ImageFont, ImageEnhance +import piexif +import time + +class ImageProcessor: + """Functions using pillow are in here.""" + def __init__(self): + pass + + def open_image(self, path): + """Open an image from path, returns image object.""" + return Image.open(path) + + def get_image_size(self, image): + """Simply get image size.""" + return image.size + + def grayscale(self, image): + """Change to grayscale""" + return image.convert("L") + + def change_contrast(self, image, change): + enhancer = ImageEnhance.Contrast(image) + new_img = enhancer.enhance(1 + (change/100)) + return new_img + + def change_brightness(self, image, change): + enhancer = ImageEnhance.Brightness(image) + new_img = enhancer.enhance(1 + (change/100)) + return new_img + + def resize_image(self, image, percent, resample = True): + """Resize an image by giving a percent.""" + new_size = tuple(int(x * (percent / 100)) for x in image.size) + if resample: + resized_image = image.resize(new_size) + else: + resized_image = image.resize((new_size),resample=Image.Resampling.NEAREST) + return resized_image + + def add_watermark(self, image, text, font_size_scale = 70): + 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 + 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") + time.sleep(0.3) + return image + + c, w, textwidth, textheight, = drawer.textbbox(xy = (0, 0), text = text, font = font) # Getting text size, only need the last two values + x = imagewidth - textwidth - margin + y = imageheight - textheight - margin + drawer.text((x, y), text, font = font) + + return image + + def save_image(self, image, path, file_type, jpg_quality, png_compressing, optimize, exif_data = None): + # partly optimized by chatGPT + """ + Save an image to the specified path with optional EXIF data and optimization. + """ + file_type = file_type.lower() + save_params = {"optimize": optimize} + # Add file-specific parameters + if file_type == "jpg": + save_params["quality"] = jpg_quality + elif file_type == "png": + save_params["compress_level"] = png_compressing + elif file_type not in ["webp", "jpg", "png"]: + input(f"Type: {file_type} is not supported. Press Enter to continue...") + return + # Add EXIF data if available + if exif_data is not None: + save_params["exif"] = piexif.dump(exif_data) + if file_type == "webp": + print("File format webp does not support all exif features, some information might get lost...\n") + time.sleep(0.1) + try: + image.save(f"{path}.{file_type}", **save_params) + except Exception as e: + print(f"Failed to save image: {e}") + +class ExifHandler: + """Function using piexif are here.""" + def __init__(self): + pass + + def get_exif_info(self, image): + return(piexif.load(image.info['exif'])) + + def build_exif_dict(self, user_data, imagesize): + """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.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.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} diff --git a/utils/utility.py b/utils/utility.py new file mode 100644 index 0000000..6f09597 --- /dev/null +++ b/utils/utility.py @@ -0,0 +1,47 @@ +import yaml + +class Utilities: + def __init__(self): + pass + + def read_yaml(self, yaml_file): + try: + with open(yaml_file, "r") as file: + data = yaml.safe_load(file) + return data + except (FileNotFoundError, PermissionError) as e: + print(f"Error loading settings file: {e}") + return + + def write_yaml(self, yaml_file, data): + try: + with open(yaml_file, "w") as file: + yaml.dump(data, file) + except PermissionError as e: + print(f"Error saving setings: {e}") + + def yes_no(self, str): + """Ask user y/n question""" + while True: + choice = input(f"{str} (y/n): ") + if choice == "y": + return True + elif choice == "n": + return False + else: + print("Not a valid option, try again.") + + def progress_bar(self, current, total, barsize = 50): + if current > total: + print("\033[91mThis bar has exceeded its limits!\033[0m Maybe the current value needs some restraint?") + print(f"{(current - total) * '\033[92mHonk, Honk!\033[0m '}") + return + progress = int((barsize / total) * current) + rest = barsize - progress + if rest <= 2: rest = 0 + # Determine the number of digits in total + total_digits = len(str(total)) + # Format current with leading zeros + current_formatted = f"{current:0{total_digits}}" + print(f"{current_formatted}|{progress * '-'}>{rest * ' '}|{total}", end="\r") + if current == total: print("")