| |
| """ |
| app.py — Optimised Mosaic Generator (Lab 5) |
| |
| This powers the Gradio Space using: |
| - crop_to_multiple() |
| - compute_cell_means_lab() |
| - TileManager |
| - MosaicBuilder |
| - MSE / SSIM metrics |
| """ |
|
|
| import gradio as gr |
| import numpy as np |
| import time |
| import os |
| from PIL import Image, ImageDraw |
|
|
| from mosaic_generator.image_processor import crop_to_multiple, compute_cell_means_lab |
| from mosaic_generator.tile_manager import TileManager |
| from mosaic_generator.mosaic_builder import MosaicBuilder |
| from mosaic_generator.metrics import mse, ssim_rgb |
|
|
|
|
| |
| |
| |
| TM = TileManager() |
| TM.load(sample_size=20000) |
|
|
|
|
| |
| |
| |
| def run_pipeline( |
| img, grid_size, tile_px, tile_sample, |
| quantize_on, quantize_colors, show_grid |
| ): |
| """Full mosaic generation pipeline with error handling.""" |
|
|
| if img is None: |
| return None, None, None, "Upload an image first." |
|
|
| img_np = np.array(img.convert("RGB")) |
| grid_n = int(grid_size) |
|
|
| |
| base = crop_to_multiple(img_np, grid_n) |
|
|
| |
| if quantize_on: |
| try: |
| q = Image.fromarray(base).quantize( |
| colors=int(quantize_colors), |
| method=Image.MEDIANCUT, |
| dither=Image.Dither.NONE |
| ).convert("RGB") |
| base = np.array(q) |
| except Exception as e: |
| return None, None, None, f"Quantization failed: {e}" |
|
|
| |
| try: |
| t0 = time.perf_counter() |
| cell_means, dims = compute_cell_means_lab(base, grid_n) |
| t1 = time.perf_counter() |
| except Exception as e: |
| return None, None, None, f"LAB conversion failed: {e}" |
|
|
| w, h, cell_w, cell_h = dims |
|
|
| |
| TM.prepare_scaled_tiles(cell_w, cell_h) |
|
|
| |
| try: |
| idxs = TM.lookup_tiles(cell_means) |
| except Exception as e: |
| return None, None, None, f"Tile lookup failed: {e}" |
|
|
| |
| builder = MosaicBuilder(TM) |
| try: |
| mosaic_np = builder.build(idxs, dims, grid_n) |
| t2 = time.perf_counter() |
| except Exception as e: |
| return None, None, None, f"Mosaic build failed: {e}" |
|
|
| |
| try: |
| mse_val = mse(base, mosaic_np) |
| ssim_val = ssim_rgb(base, mosaic_np) |
| except: |
| mse_val, ssim_val = -1, -1 |
|
|
| |
| segmented = Image.fromarray(base) |
| if show_grid: |
| seg = segmented.copy() |
| draw = ImageDraw.Draw(seg) |
| for x in range(0, w, cell_w): |
| draw.line([(x, 0), (x, h)], fill="red", width=1) |
| for y in range(0, h, cell_h): |
| draw.line([(0, y), (w, y)], fill="red", width=1) |
| segmented = seg |
|
|
| |
| report = ( |
| f"MSE: {mse_val:.2f}\n" |
| f"SSIM: {ssim_val:.4f}\n\n" |
| f"Preprocessing Time: {t1 - t0:.3f}s\n" |
| f"Mosaic Build Time: {t2 - t1:.3f}s\n" |
| f"Total Time: {t2 - t0:.3f}s\n" |
| ) |
|
|
| return ( |
| Image.fromarray(base), |
| segmented, |
| Image.fromarray(mosaic_np), |
| report |
| ) |
|
|
|
|
| |
| |
| |
| def build_demo(): |
| with gr.Blocks(title="High-Performance Mosaic Generator") as demo: |
|
|
| gr.Markdown("# ⚡ High-Performance Mosaic Generator (Lab 5)") |
| gr.Markdown("Ultra-fast FAISS + OpenCV + LAB mosaic generator.\n") |
|
|
| with gr.Row(): |
|
|
| |
| |
| |
| with gr.Column(scale=1): |
|
|
| img_in = gr.Image(type="pil", label="Upload Image") |
|
|
| grid_size = gr.Radio( |
| ["16", "32", "64", "128"], |
| value="32", |
| label="Grid Size (cells per side)" |
| ) |
| tile_px = gr.Radio( |
| ["8", "16", "24", "32"], |
| value="16", |
| label="Tile Resolution (px)" |
| ) |
|
|
| tile_sample = gr.Slider( |
| 512, 20000, step=256, value=2048, |
| label="Tile Sample Size" |
| ) |
|
|
| quantize_on = gr.Checkbox(True, label="Enable Color Quantization") |
| quantize_colors = gr.Slider( |
| 8, 128, value=32, step=8, |
| label="Quantization Palette Size" |
| ) |
|
|
| show_grid = gr.Checkbox(True, label="Show Grid Overlay") |
|
|
| run_btn = gr.Button("Generate Mosaic", variant="primary") |
|
|
| |
| |
| |
| gr.Markdown("### Example Images") |
|
|
| example_files = [ |
| "725px-Mona_Lisa_by_Leonardo_da_Vinci_from_C2RMF_retouched-e1660680153902.webp", |
| "WhatsApp Image 2025-11-08 at 01.39.58_cddcf540.jpg", |
| ] |
|
|
| example_list = [[f] for f in example_files] |
|
|
| gr.Examples( |
| examples=example_list, |
| inputs=[img_in], |
| label="", |
| cache_examples=False, |
| ) |
|
|
| |
| |
| |
| with gr.Column(scale=2): |
|
|
| with gr.Tab("Original"): |
| img_orig = gr.Image() |
|
|
| with gr.Tab("Grid View"): |
| img_seg = gr.Image() |
|
|
| with gr.Tab("Mosaic Output"): |
| img_mosaic = gr.Image() |
|
|
| report = gr.Textbox(label="Timing & Metrics", lines=12) |
|
|
| run_btn.click( |
| fn=run_pipeline, |
| inputs=[img_in, grid_size, tile_px, tile_sample, |
| quantize_on, quantize_colors, show_grid], |
| outputs=[img_orig, img_seg, img_mosaic, report] |
| ) |
|
|
| return demo |
|
|
|
|
| |
| |
| |
| if __name__ == "__main__": |
| demo = build_demo() |
| demo.launch() |
|
|