Last active
January 20, 2024 18:51
-
-
Save Mega-JC/d92d9e07506818f3030696844839ce2c to your computer and use it in GitHub Desktop.
ce_logo_overlayer_gui.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import glob | |
import os | |
import time | |
import sys | |
from gooey import Gooey, GooeyParser | |
from PIL import Image | |
def overlay_images( | |
source_path, | |
dest_images, | |
output_dir, | |
reference_corner, | |
border_offset_factor_horizontal, | |
border_offset_factor_vertical, | |
overlay_width_bounded_scale_factor, | |
overlay_height_bounded_scale_factor, | |
): | |
last_dest_size = None | |
# Load source image with alpha transparency | |
source = last_source_resized = Image.open(source_path).convert("RGBA") | |
# Ensure there are destination images | |
if not dest_images: | |
raise ValueError("No destination images provided.") | |
dest_images_count = len(dest_images) | |
# Iterate over destination images | |
for i, dest_path in enumerate(dest_images): | |
dest_output_path = os.path.join( | |
output_dir, | |
f"{os.path.basename(dest_path)}", | |
) | |
if os.path.exists(dest_output_path): | |
print( | |
f"Skipping '{os.path.basename(dest_path)}' as it was already processed " | |
f"and saved at '{dest_output_path}' ({i+1}/{dest_images_count} completed)" | |
) | |
continue | |
try: | |
# Attempt to open the file as an image | |
dest = Image.open(dest_path).convert("RGBA") | |
except (IOError, OSError, Image.DecompressionBombError): | |
# Skip if the file is not a valid image | |
continue | |
# Calculate final pasting position after considering border offsets | |
border_offset_x = border_offset_y = 0 | |
if border_offset_factor_horizontal and border_offset_factor_vertical: | |
border_offset_x = round(dest.width * border_offset_factor_horizontal) | |
border_offset_y = round(dest.height * border_offset_factor_vertical) | |
elif border_offset_factor_horizontal: | |
border_offset_x = border_offset_y = round( | |
dest.width * border_offset_factor_horizontal | |
) | |
elif border_offset_factor_vertical: | |
border_offset_y = border_offset_x = round( | |
dest.height * border_offset_factor_vertical | |
) | |
if dest.size == last_dest_size: | |
source_resized = last_source_resized | |
elif overlay_width_bounded_scale_factor and overlay_height_bounded_scale_factor: | |
resized_width = round(dest.width * overlay_width_bounded_scale_factor) | |
resized_height = round(dest.height * overlay_height_bounded_scale_factor) | |
source_resized = source.resize( | |
(resized_width, resized_height), | |
Image.BICUBIC, | |
) | |
elif overlay_width_bounded_scale_factor: | |
resized_width = round(dest.width * overlay_width_bounded_scale_factor) | |
source_resized = source.resize( | |
(resized_width, round(resized_width * (source.height / source.width))), | |
Image.BICUBIC, | |
) | |
elif overlay_height_bounded_scale_factor: | |
resized_height = round(dest.height * overlay_height_bounded_scale_factor) | |
source_resized = source.resize( | |
( | |
round(resized_height * (source.width / source.height)), | |
resized_height, | |
), | |
Image.BICUBIC, | |
) | |
else: | |
source_resized = source | |
if reference_corner == "top-left": | |
final_x = border_offset_x | |
final_y = border_offset_y | |
elif reference_corner == "top-right": | |
final_x = dest.width - source_resized.width - border_offset_x | |
final_y = border_offset_y | |
elif reference_corner == "bottom-left": | |
final_x = border_offset_x | |
final_y = dest.height - source_resized.height - border_offset_y | |
elif reference_corner == "bottom-right": | |
final_x = dest.width - source_resized.width - border_offset_x | |
final_y = dest.height - source_resized.height - border_offset_y | |
result = Image.new("RGBA", dest.size) | |
result.paste(dest, (0, 0)) | |
# Composite source image onto the destination image at specified position | |
result.alpha_composite(source_resized, (final_x, final_y)) | |
result.convert("RGB").save( | |
dest_output_path, | |
) | |
last_dest_size = dest.size | |
last_source_resized = source_resized | |
print( | |
f"Overlay complete for '{os.path.basename(dest_path)}'. Result saved at " | |
f"'{dest_output_path}' ({i+1}/{dest_images_count} completed)" | |
) | |
time.sleep(0.01) | |
@Gooey( | |
program_name="CE Image Overlay Tool v2.0.1", | |
default_size=(800, 600), | |
navigation="SIDEBAR", | |
tabbed_groups=True, | |
progress_regex=r"^.+\((?P<current>\d+)\/(?P<total>\d+) +completed\)$", | |
progress_expr="current / total * 100", | |
) | |
def main(): | |
parser = GooeyParser( | |
description="Overlay a source PNG image with alpha transparency on destination images." | |
) | |
main_group = parser.add_argument_group("Main") | |
main_group.add_argument( | |
"source_image", | |
widget="FileChooser", | |
help="The source PNG image to use as an overlay.", | |
) | |
main_group.add_argument( | |
"destination_images_folder", | |
widget="DirChooser", | |
help="A folder with the destination images.", | |
) | |
main_group.add_argument( | |
"output_folder", | |
widget="DirChooser", | |
help="The output folder where overlay results will be saved.", | |
) | |
advanced_group = parser.add_argument_group( | |
"Advanced", | |
description="Advanced options. Only change things here if you know what you " | |
"are doing.\n\n ", | |
) | |
advanced_group.add_argument( | |
"--reference-corner", | |
type=str, | |
nargs="?", | |
default="bottom-right", | |
choices=["top-left", "top-right", "bottom-left", "bottom-right"], | |
help="The reference corner for positioning the overlay image.", | |
) | |
advanced_group.add_argument( | |
"--border-offset-factor-horizontal", | |
type=float, | |
default=2.7, | |
help="The factor in percent (%) by which to offset the overlay from the " | |
"selected reference corner on a destination image horizontally, relative " | |
"to the size of that destination image's width.", | |
) | |
advanced_group.add_argument( | |
"--border-offset-factor-vertical", | |
type=float, | |
help="The factor in percent (%) by which to offset the overlay from the " | |
"selected reference corner on a destination image vertically, relative " | |
"to the size of that destination image's height.", | |
) | |
advanced_group.add_argument( | |
"--overlay-width-bounded-scale-factor", | |
nargs="?", | |
type=float, | |
default=10, | |
help="The factor in percent (%) by which to scale the overlay's width, " | |
"relative to the width of a destination image's width.", | |
) | |
advanced_group.add_argument( | |
"--overlay-height-bounded-scale-factor", | |
nargs="?", | |
type=float, | |
default=None, | |
help="The factor in percent (%) by which to scale the overlay's height, " | |
"relative to the size of a destination image's height.", | |
) | |
args = parser.parse_args() | |
overlay_images( | |
args.source_image, | |
glob.glob(os.path.join(args.destination_images_folder, "*")), | |
args.output_folder, | |
args.reference_corner, | |
args.border_offset_factor_horizontal / 100 | |
if args.border_offset_factor_horizontal | |
else None, | |
args.border_offset_factor_vertical / 100 | |
if args.border_offset_factor_vertical | |
else None, | |
args.overlay_width_bounded_scale_factor / 100 | |
if args.overlay_width_bounded_scale_factor | |
else None, | |
args.overlay_height_bounded_scale_factor / 100 | |
if args.overlay_height_bounded_scale_factor | |
else None, | |
) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment