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)