Created
August 22, 2024 12:00
-
-
Save UserUnknownFactor/8d855994b68d1d53b016e84c47ed28b7 to your computer and use it in GitHub Desktop.
Python script for automatic diffing of PNGs
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
# -*- coding: utf-8 -*- | |
# This script finds all PNGs of similar looks/size in the specified | |
# folder and produces a base image and a series of diffs with it. | |
import os | |
import sys | |
from PIL import Image | |
import numpy as np | |
from collections import defaultdict | |
DIFF_THRESHOLD = 0 # Adjust this based on diff sensitivity needed | |
SIMILARITY_THRESHOLD = 10 # Adjust this to control how images are clustered | |
terminal_width = 80 # Fallback width | |
try: | |
terminal_width = os.get_terminal_size().columns | |
except: | |
pass | |
def image_similarity(img1, img2): | |
"""Calculates the average pixel difference between two images.""" | |
return np.mean(np.abs(np.array(img1) - np.array(img2))) | |
def group_by_dims_and_similarity(images): | |
"""Groups images by dimensions first, then by similarity.""" | |
groups_by_dimension = defaultdict(list) | |
for img in images: | |
groups_by_dimension[img.size].append(img) | |
final_groups = {} | |
group_id = 0 | |
for size, images_with_same_size in groups_by_dimension.items(): | |
similarity_groups = defaultdict(list) | |
for img in images_with_same_size: | |
added_to_group = False | |
for existing_group_id, group_images in similarity_groups.items(): | |
similarity = image_similarity(img, group_images[0]) | |
print(similarity) | |
if similarity <= SIMILARITY_THRESHOLD: | |
similarity_groups[existing_group_id].append(img) | |
added_to_group = True | |
break | |
if not added_to_group: | |
similarity_groups[len(similarity_groups)].append(img) | |
# Add the similarity groups to the final groups | |
for _, group in similarity_groups.items(): | |
final_groups[group_id] = group | |
group_id += 1 | |
return final_groups | |
def find_base_image(images): | |
np_images = [np.array(img.convert('RGBA')) for img in images] | |
base_image_array = np.median(np.stack(np_images), axis=0).astype(np.uint8) | |
return Image.fromarray(base_image_array, 'RGBA') | |
def compute_difference(base, img): | |
base_array = np.array(base) | |
img_array = np.array(img) | |
diff = np.abs(base_array - img_array) | |
mask = (diff.sum(axis=2) <= DIFF_THRESHOLD) | |
img_array[mask] = 0 # Set RGBA to 0 where there's no difference | |
return Image.fromarray(img_array, 'RGBA') | |
def process_images(directory): | |
images = [] | |
for file in os.listdir(directory): | |
if file.endswith('.png') and not file.startswith('base_') and not file.startswith('diff_'): | |
img = Image.open(os.path.join(directory, file)) | |
images.append(img) | |
grouped_images = group_by_dims_and_similarity(images) | |
for group_id, img_group in grouped_images.items(): | |
print(f"Processing image group {group_id}") | |
base_image = find_base_image(img_group) | |
base_name = f'base_group_{group_id}' | |
base_image.save(f'{base_name}.png') | |
#max_digits = max(3, math.ceil(math.log10(len(img_group)))) | |
for i, img in enumerate(img_group): | |
filename = os.path.basename(img.filename) | |
padding = max(0, terminal_width - len(filename) - 12) | |
proc_text = f'Processing: {filename}' | |
sys.stdout.write(f"{proc_text}{' ' * (padding - len(proc_text))}\r") | |
sys.stdout.flush() | |
diff_img = compute_difference(base_image, img) | |
diff_img.save(f'diff_{os.path.splitext(filename)[0]}_{base_name}.png', optimize=True) | |
print(f"Finished {' ' * (terminal_width - 9)}") | |
process_images('.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment