Last active
August 22, 2024 15:37
-
-
Save UserUnknownFactor/97d8906e9912b0ad7e8596d75531831f to your computer and use it in GitHub Desktop.
Tool to find images named prefix_NN_postfix.png and convert them to APNGs
This file contains hidden or 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 os, re, glob | |
import numpy as np | |
from PIL import Image, ImageChops | |
from collections import defaultdict | |
# Finds sequences of images with common name part+_<frame#>_etc.png | |
# and merges them into a single animated PNG. | |
INITIAL_SPEED = 20 | |
TYPE2 = True | |
IMAGE_FOLDER = '.' | |
def calculate_difference2(frame1, frame2): | |
"""Calculate the visual difference between two images. """ | |
diff = ImageChops.difference(frame1.convert('L'), frame2.convert('L')) | |
total_difference = np.sum(np.array(diff)) | |
return total_difference / (frame1.size[0] * frame1.size[1] * 255) | |
def calculate_difference1(frame1, frame2): | |
"""Calculate the visual difference between two images. """ | |
mse = np.mean((np.array(frame1.convert('L')) - np.array(frame2.convert('L'))) ** 2) | |
return int(100 / (1 + mse)) | |
calculate_difference = calculate_difference2 if TYPE2 else calculate_difference1 | |
def create_animation(images, output_filename, speed=100, use_difference=False): | |
"""Create a APNG from a list of PIL Image objects. """ | |
if use_difference and len(images) > 1: | |
frame_delays = [calculate_difference(images[i], images[i+1]) for i in range(len(images)-1)] | |
max_diff = max(frame_delays, default=1) | |
frame_delays = [(1 - (diff / max_diff)) * speed for diff in frame_delays] | |
frame_delays.append(frame_delays[-1]) | |
else: | |
frame_delays = [speed] * len(images) | |
frame_delays = [int(delay) for delay in frame_delays] | |
images[0].save(output_filename, save_all=True, append_images=images[1:], duration=frame_delays, loop=0) | |
def detect_and_process_sequences(image_folder, speed=100, use_difference=False): | |
"""Detect common prefixes in filenames, indicating sequences, and animate each sequence.""" | |
files = glob.glob(f'{image_folder}\\**\\*.png', recursive=True) | |
prefix_pattern = re.compile(r'^(.+)_\d+.*?\.png$') | |
sequences = defaultdict(list) | |
for file in files: | |
match = prefix_pattern.match(file) | |
if match: | |
prefix = match.group(1) | |
sequences[prefix].append(file) | |
for prefix, files in sequences.items(): | |
# Sort files by frame number | |
files.sort(key=lambda x: int(re.search(r'.+_(\d+).*?\.png$', x).group(1))) | |
images = [Image.open(os.path.join(image_folder, f)) for f in files] | |
output_filename = f"{prefix}.apng" | |
print(f"Creating {output_filename}... ", end='') | |
create_animation(images, output_filename, speed=speed, use_difference=use_difference) | |
print(f"OK") | |
out_name_new = output_filename.replace('.apng', '.png') | |
i = 1 | |
while os.path.isfile(out_name_new): | |
out_name_new = re.sub(r' _\(\d+\)\.png', '.png', out_name_new) | |
out_name_new = out_name_new.replace('.png', f'_({i}).png') | |
i+=1 | |
os.rename(output_filename, out_name_new) | |
detect_and_process_sequences(IMAGE_FOLDER, speed=INITIAL_SPEED, use_difference=False) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment