Files
filmsim/compare.py
2025-06-19 15:31:45 -04:00

163 lines
6.7 KiB
Python

import os
from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt
import numpy as np
def create_advanced_comparison_poster(
image_paths,
output_path="05.jpg",
patch_size=(300, 300),
zoom_level=2.0
):
"""
Generates a poster optimized for side-by-side patch comparison from a
series of high-resolution images.
The layout is organized in rows:
- Row 1: Scaled-down full images.
- Row 2: Patch 1 from all images.
- Row 3: Patch 2 from all images.
- ... and so on.
- Final Row: Histograms for all images.
Args:
image_paths (list): A list of file paths for the images to compare.
output_path (str, optional): Path to save the output poster.
patch_size (tuple, optional): The (width, height) of the area to crop
from the source image.
zoom_level (float, optional): The factor to enlarge the cropped patches.
"""
if not image_paths:
print("No image paths were provided.")
return
# --- Layout & Font Configuration ---
padding = 25
header_height = 40
row_title_width = 150
histogram_height = 200
patch_display_size = (int(patch_size[0] * zoom_level), int(patch_size[1] * zoom_level))
scaled_full_image_width = patch_display_size[0]
try:
title_font = ImageFont.truetype("arialbd.ttf", 20)
header_font = ImageFont.truetype("arial.ttf", 16)
except IOError:
title_font = ImageFont.load_default()
header_font = ImageFont.load_default()
# --- Determine Poster Dimensions from Master Image ---
with Image.open(image_paths[0]) as master_image:
master_width, master_height = master_image.size
# Define patch locations relative to image dimensions
patch_definitions = {
"Top Left": (0, 0, patch_size[0], patch_size[1]),
"Top Right": (master_width - patch_size[0], 0, master_width, patch_size[1]),
"Center": (
(master_width - patch_size[0]) // 2,
(master_height - patch_size[1]) // 2,
(master_width + patch_size[0]) // 2,
(master_height + patch_size[1]) // 2,
),
"Bottom Left": (0, master_height - patch_size[1], patch_size[0], master_height),
"Bottom Right": (
master_width - patch_size[0],
master_height - patch_size[1],
master_width,
master_height,
),
}
scaled_full_image_height = int(master_height * (scaled_full_image_width / master_width))
num_images = len(image_paths)
num_patch_rows = len(patch_definitions)
# Calculate final poster dimensions
poster_width = row_title_width + num_images * (patch_display_size[0] + padding) + padding
total_rows_height = header_height + scaled_full_image_height + num_patch_rows * patch_display_size[1] + histogram_height
total_padding_height = (3 + num_patch_rows) * padding
poster_height = total_rows_height + total_padding_height
# --- Create Poster Canvas ---
poster = Image.new("RGB", (poster_width, poster_height), "white")
draw = ImageDraw.Draw(poster)
# --- 1. Draw Column Headers (Filenames) ---
y_offset = padding
for i, image_path in enumerate(image_paths):
filename = os.path.basename(image_path)
x_offset = row_title_width + i * (patch_display_size[0] + padding)
draw.text((x_offset, y_offset), filename, fill="black", font=header_font)
y_offset += header_height
# --- 2. Draw Row 1: Scaled Full Images ---
draw.text((padding, y_offset + scaled_full_image_height // 2), "Full View", fill="black", font=title_font)
for i, image_path in enumerate(image_paths):
with Image.open(image_path) as img:
img.thumbnail((scaled_full_image_width, scaled_full_image_height))
x_offset = row_title_width + i * (patch_display_size[0] + padding)
poster.paste(img, (x_offset, y_offset))
y_offset += scaled_full_image_height + padding
# --- 3. Draw Patch Rows ---
for patch_name, patch_area in patch_definitions.items():
draw.text((padding, y_offset + patch_display_size[1] // 2), patch_name, fill="black", font=title_font)
for i, image_path in enumerate(image_paths):
with Image.open(image_path) as img:
patch = img.crop(patch_area)
zoomed_patch = patch.resize(patch_display_size, Image.Resampling.LANCZOS)
x_offset = row_title_width + i * (patch_display_size[0] + padding)
poster.paste(zoomed_patch, (x_offset, y_offset))
# Add a border for clarity
draw.rectangle(
(x_offset, y_offset, x_offset + patch_display_size[0], y_offset + patch_display_size[1]),
outline="gray", width=1
)
y_offset += patch_display_size[1] + padding
# --- 4. Draw Final Row: Histograms ---
draw.text((padding, y_offset + histogram_height // 2), "Histogram", fill="black", font=title_font)
for i, image_path in enumerate(image_paths):
histogram_path = f"temp_hist_{i}.png"
with Image.open(image_path) as img:
luminance_data = np.array(img.convert("L"))
plt.figure(figsize=(6, 3))
plt.hist(luminance_data.ravel(), bins=256, range=[0, 256], color='gray', ec='gray')
plt.title("Luminance")
plt.xlabel("Pixel Intensity")
plt.ylabel("Frequency")
plt.tight_layout()
plt.savefig(histogram_path)
plt.close()
with Image.open(histogram_path) as hist_img:
hist_img.thumbnail((patch_display_size[0], histogram_height))
x_offset = row_title_width + i * (patch_display_size[0] + padding)
poster.paste(hist_img, (x_offset, y_offset))
os.remove(histogram_path)
# --- Save Final Poster ---
poster.save(output_path)
print(f"Advanced comparison poster saved to {output_path}")
if __name__ == '__main__':
# --- Example Usage ---
# This block creates a set of dummy high-resolution images to demonstrate the script.
test_dir = "high_res_test_images"
if not os.path.exists(test_dir):
os.makedirs(test_dir)
# Using 4000x3000 as a stand-in for "high resolution" to keep the example fast.
# The script logic works identically for 50MP+ images.
width, height = 4000, 3000
# list .jpg in dir
jpgdir = '/home/dubey/projects/filmsim/test_images/v1.4/05.DNG/'
image_files = [os.path.join(jpgdir, f) for f in os.listdir(jpgdir) if f.endswith('.jpg')]
# --- Generate the poster ---
# For high-res images, a larger patch size from the source is better.
create_advanced_comparison_poster(image_files, patch_size=(1000, 1000), zoom_level=2.5)