| """ |
| Gradio interface functions for the Mosaic Generator. |
| """ |
|
|
| import gradio as gr |
| import numpy as np |
| from PIL import Image |
| import time |
| from typing import Tuple, Dict, List |
|
|
| from .config import Config, Implementation, MatchSpace |
| from .pipeline import MosaicPipeline |
| from .metrics import calculate_comprehensive_metrics, interpret_metrics |
|
|
|
|
| def create_default_config( |
| grid_size: int = 32, |
| tile_size: int = 32, |
| output_width: int = 768, |
| output_height: int = 768, |
| color_matching: str = "Lab (perceptual)", |
| use_uniform_quantization: bool = False, |
| quantization_levels: int = 8, |
| use_kmeans_quantization: bool = False, |
| kmeans_colors: int = 8, |
| normalize_tile_brightness: bool = False |
| ) -> Config: |
| """Create configuration from Gradio interface parameters.""" |
| |
| |
| match_space = MatchSpace.LAB if color_matching == "Lab (perceptual)" else MatchSpace.RGB |
| |
| return Config( |
| grid=grid_size, |
| tile_size=tile_size, |
| out_w=output_width, |
| out_h=output_height, |
| impl=Implementation.VECT, |
| match_space=match_space, |
| use_uniform_q=use_uniform_quantization, |
| q_levels=quantization_levels, |
| use_kmeans_q=use_kmeans_quantization, |
| k_colors=kmeans_colors, |
| tile_norm_brightness=normalize_tile_brightness |
| ) |
|
|
|
|
| def generate_mosaic( |
| image: Image.Image, |
| grid_size: int, |
| tile_size: int, |
| output_width: int, |
| output_height: int, |
| color_matching: str, |
| use_uniform_quantization: bool, |
| quantization_levels: int, |
| use_kmeans_quantization: bool, |
| kmeans_colors: int, |
| normalize_tile_brightness: bool, |
| progress=gr.Progress() |
| ) -> Tuple[Image.Image, Image.Image, str, str]: |
| """ |
| Generate mosaic from input image with given parameters. |
| |
| Returns: |
| Tuple of (mosaic_image, processed_image, metrics_text, timing_text) |
| """ |
| if image is None: |
| return None, None, "Please upload an image.", "" |
| |
| try: |
| |
| config = create_default_config( |
| grid_size, tile_size, output_width, output_height, |
| color_matching, use_uniform_quantization, |
| quantization_levels, use_kmeans_quantization, kmeans_colors, |
| normalize_tile_brightness |
| ) |
| |
| |
| pipeline = MosaicPipeline(config) |
| |
| |
| progress(0.1, desc="Initializing pipeline...") |
| |
| |
| progress(0.2, desc="Loading tiles (first time only)...") |
| progress(0.4, desc="Generating mosaic...") |
| results = pipeline.run_full_pipeline(image) |
| |
| progress(0.7, desc="Calculating metrics...") |
| |
| |
| mosaic_img = results['outputs']['mosaic'] |
| processed_img = results['outputs']['processed_image'] |
| |
| |
| metrics = results['metrics'] |
| interpretations = results['metrics_interpretation'] |
| |
| metrics_text = f""" |
| **Quality Metrics:** |
| - **MSE (Mean Squared Error):** {metrics['mse']:.6f} - {interpretations['mse']} |
| - **PSNR (Peak Signal-to-Noise Ratio):** {metrics['psnr']:.2f} dB - {interpretations['psnr']} |
| - **SSIM (Structural Similarity):** {metrics['ssim']:.4f} - {interpretations['ssim']} |
| - **RMSE (Root Mean Squared Error):** {metrics['rmse']:.6f} |
| - **MAE (Mean Absolute Error):** {metrics['mae']:.6f} |
| |
| **Color Analysis:** |
| - **Color MSE:** {metrics['color_mse']:.6f} |
| - **Histogram Correlation:** {metrics['histogram_correlation']:.4f} |
| """ |
| |
| |
| timing = results['timing'] |
| timing_text = f""" |
| **Processing Times:** |
| - **Preprocessing:** {timing['preprocessing']:.3f} seconds |
| - **Grid Analysis:** {timing['grid_analysis']:.3f} seconds |
| - **Tile Mapping:** {timing['tile_mapping']:.3f} seconds |
| - **Total Time:** {timing['total']:.3f} seconds |
| |
| **Configuration:** |
| - **Grid Size:** {config.grid}x{config.grid} ({config.grid**2} tiles total) |
| - **Tile Size:** {config.tile_size}x{config.tile_size} pixels |
| - **Output Resolution:** {mosaic_img.width}x{mosaic_img.height} |
| - **Implementation:** {config.impl.value} |
| - **Color Matching:** {config.match_space.value} |
| """ |
| |
| progress(1.0, desc="Complete!") |
| |
| return mosaic_img, processed_img, metrics_text, timing_text |
| |
| except Exception as e: |
| error_msg = f"Error generating mosaic: {str(e)}" |
| print(error_msg) |
| return None, None, error_msg, "" |
|
|
|
|
|
|
|
|
| def benchmark_grid_sizes( |
| image: Image.Image, |
| grid_sizes: str, |
| progress=gr.Progress() |
| ) -> str: |
| """Benchmark different grid sizes.""" |
| if image is None: |
| return "Please upload an image for benchmarking." |
| |
| try: |
| |
| sizes = [int(x.strip()) for x in grid_sizes.split(',')] |
| |
| results = [] |
| total_tests = len(sizes) |
| |
| for i, grid_size in enumerate(sizes): |
| progress((i + 1) / total_tests, desc=f"Testing grid size {grid_size}x{grid_size}...") |
| |
| config = create_default_config(grid_size, 32, 768, 768) |
| pipeline = MosaicPipeline(config) |
| |
| start_time = time.time() |
| pipeline_results = pipeline.run_full_pipeline(image) |
| processing_time = time.time() - start_time |
| |
| results.append({ |
| 'grid_size': grid_size, |
| 'processing_time': processing_time, |
| 'total_tiles': grid_size * grid_size, |
| 'tiles_per_second': (grid_size * grid_size) / processing_time, |
| 'mse': pipeline_results['metrics']['mse'], |
| 'ssim': pipeline_results['metrics']['ssim'] |
| }) |
| |
| |
| report = "**Grid Size Performance Analysis:**\n\n" |
| |
| for result in results: |
| report += f"**Grid {result['grid_size']}x{result['grid_size']}:**\n" |
| report += f"- Processing Time: {result['processing_time']:.3f}s\n" |
| report += f"- Total Tiles: {result['total_tiles']}\n" |
| report += f"- Tiles per Second: {result['tiles_per_second']:.1f}\n" |
| report += f"- MSE: {result['mse']:.6f}\n" |
| report += f"- SSIM: {result['ssim']:.4f}\n\n" |
| |
| |
| if len(results) >= 2: |
| first = results[0] |
| last = results[-1] |
| tile_ratio = last['total_tiles'] / first['total_tiles'] |
| time_ratio = last['processing_time'] / first['processing_time'] |
| |
| report += "**Scaling Analysis:**\n" |
| report += f"- Tile increase ratio: {tile_ratio:.2f}x\n" |
| report += f"- Time increase ratio: {time_ratio:.2f}x\n" |
| report += f"- Scaling efficiency: {tile_ratio/time_ratio:.2f}\n" |
| report += f"- Linear scaling: {'Yes' if abs(time_ratio - tile_ratio) / tile_ratio < 0.1 else 'No'}\n" |
| |
| return report |
| |
| except Exception as e: |
| return f"Error during grid size benchmarking: {str(e)}" |
|
|
|
|
| def create_interface(): |
| """Create the Gradio interface.""" |
| |
| with gr.Blocks(title="Mosaic Generator", theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# 🎨 Mosaic Generator") |
| gr.Markdown("Generate beautiful mosaic-style images from your photos using advanced image processing techniques.") |
| |
| with gr.Tab("Generate Mosaic"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| |
| gr.Markdown("## Upload & Configure") |
| |
| input_image = gr.Image( |
| type="pil", |
| label="Upload Image", |
| height=300 |
| ) |
| |
| with gr.Accordion("Basic Settings", open=True): |
| grid_size = gr.Slider( |
| minimum=8, maximum=128, step=8, value=32, |
| label="Grid Size (N×N tiles)" |
| ) |
| tile_size = gr.Slider( |
| minimum=4, maximum=64, step=4, value=32, |
| label="Tile Size (pixels)" |
| ) |
| output_width = gr.Slider( |
| minimum=256, maximum=1024, step=64, value=768, |
| label="Output Width" |
| ) |
| output_height = gr.Slider( |
| minimum=256, maximum=1024, step=64, value=768, |
| label="Output Height" |
| ) |
| |
| with gr.Accordion("Advanced Settings", open=False): |
| color_matching = gr.Radio( |
| choices=["Lab (perceptual)", "RGB (euclidean)"], |
| value="Lab (perceptual)", |
| label="Color Matching Space" |
| ) |
| |
| gr.Markdown("**Color Quantization:**") |
| use_uniform_quantization = gr.Checkbox( |
| label="Use Uniform Quantization", |
| value=False |
| ) |
| quantization_levels = gr.Slider( |
| minimum=4, maximum=16, step=2, value=8, |
| label="Quantization Levels", |
| visible=True |
| ) |
| |
| use_kmeans_quantization = gr.Checkbox( |
| label="Use K-means Quantization", |
| value=False |
| ) |
| kmeans_colors = gr.Slider( |
| minimum=4, maximum=32, step=2, value=8, |
| label="K-means Colors" |
| ) |
| |
| normalize_tile_brightness = gr.Checkbox( |
| label="Normalize Tile Brightness", |
| value=False |
| ) |
| |
| generate_btn = gr.Button("Generate Mosaic", variant="primary", size="lg") |
| |
| with gr.Column(scale=2): |
| |
| gr.Markdown("## Results") |
| |
| with gr.Row(): |
| mosaic_output = gr.Image( |
| label="Generated Mosaic", |
| height=400 |
| ) |
| processed_output = gr.Image( |
| label="Processed Input", |
| height=400 |
| ) |
| |
| with gr.Row(): |
| metrics_output = gr.Markdown(label="Quality Metrics") |
| timing_output = gr.Markdown(label="Processing Information") |
| |
| with gr.Tab("Performance Analysis"): |
| gr.Markdown("## Performance Benchmarking") |
| |
| with gr.Row(): |
| with gr.Column(): |
| benchmark_image = gr.Image( |
| type="pil", |
| label="Image for Benchmarking", |
| height=200 |
| ) |
| |
| gr.Markdown("### Grid Size Benchmarking") |
| grid_sizes_input = gr.Textbox( |
| value="16,32,48,64", |
| label="Grid Sizes (comma-separated)", |
| placeholder="16,32,48,64" |
| ) |
| benchmark_grid_btn = gr.Button("Benchmark Grid Sizes", variant="secondary") |
| |
| with gr.Column(): |
| benchmark_output = gr.Markdown(label="Benchmark Results") |
| |
| with gr.Tab("About"): |
| gr.Markdown(""" |
| ## About the Mosaic Generator |
| |
| This application implements a complete mosaic generation pipeline with the following features: |
| |
| **Note**: The first time you generate a mosaic, it will load tiles from the Hugging Face dataset. This may take a few moments, but subsequent generations will be much faster as tiles are cached. |
| |
| ### Core Functionality |
| - **Image Preprocessing**: Resize and crop images to fit grid requirements |
| - **Color Quantization**: Optional uniform and K-means quantization |
| - **Grid Analysis**: Vectorized operations for efficient processing |
| - **Tile Mapping**: Replace grid cells with matching image tiles |
| - **Quality Metrics**: MSE, PSNR, SSIM, and color similarity analysis |
| |
| ### Performance Features |
| - **Vectorized Operations**: NumPy-based efficient processing |
| - **Grid Size Benchmarking**: Performance analysis across different resolutions |
| - **Real-time Metrics**: Processing time and quality measurements |
| |
| ### Technical Details |
| - Uses Hugging Face datasets for tile sources |
| - Supports LAB and RGB color space matching |
| - Configurable grid sizes from 8×8 to 128×128 |
| - Adjustable tile sizes and output resolutions |
| |
| ### Assignment Requirements Met |
| ✅ Image selection and preprocessing |
| ✅ Grid division and thresholding |
| ✅ Vectorized NumPy operations |
| ✅ Tile mapping and replacement |
| ✅ Gradio interface with parameter controls |
| ✅ Similarity metrics (MSE, SSIM) |
| ✅ Performance analysis and benchmarking |
| """) |
| |
| |
| generate_btn.click( |
| fn=generate_mosaic, |
| inputs=[ |
| input_image, grid_size, tile_size, output_width, output_height, |
| color_matching, use_uniform_quantization, |
| quantization_levels, use_kmeans_quantization, kmeans_colors, |
| normalize_tile_brightness |
| ], |
| outputs=[mosaic_output, processed_output, metrics_output, timing_output] |
| ) |
| |
| benchmark_grid_btn.click( |
| fn=benchmark_grid_sizes, |
| inputs=[benchmark_image, grid_sizes_input], |
| outputs=[benchmark_output] |
| ) |
| |
| |
| use_uniform_quantization.change( |
| fn=lambda x: gr.Slider(visible=x), |
| inputs=[use_uniform_quantization], |
| outputs=[quantization_levels] |
| ) |
| |
| use_kmeans_quantization.change( |
| fn=lambda x: gr.Slider(visible=x), |
| inputs=[use_kmeans_quantization], |
| outputs=[kmeans_colors] |
| ) |
| |
| return demo |
|
|