Skip to content

Instantly share code, notes, and snippets.

@UserUnknownFactor
Last active August 22, 2024 15:37
Show Gist options
  • Save UserUnknownFactor/97d8906e9912b0ad7e8596d75531831f to your computer and use it in GitHub Desktop.
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
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