|
|
"""
|
|
|
main.py - OPTIMIZED
|
|
|
Complete mosaic generator with Numba + parallel optimizations.
|
|
|
"""
|
|
|
|
|
|
import numpy as np
|
|
|
import cv2
|
|
|
from pathlib import Path
|
|
|
from typing import Tuple, Dict, Optional
|
|
|
import time
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
from ImagePreprocessor import ImagePreprocessor
|
|
|
from contextual_Mosaic_Builder import ContextualMosaicGenerator, ImageContext
|
|
|
from Performance_metrics import PerformanceEvaluator
|
|
|
|
|
|
|
|
|
def create_advanced_mosaic(image_path: str,
|
|
|
tile_folder: str = "extracted_images",
|
|
|
grid_size: Tuple[int, int] = (64, 64),
|
|
|
tile_size: Tuple[int, int] = (32, 32),
|
|
|
diversity_factor: float = 0.15,
|
|
|
enable_rotation: bool = True,
|
|
|
enable_face_detection: bool = False,
|
|
|
colour_bins: int = 8,
|
|
|
apply_quantization: bool = False,
|
|
|
n_colors: int = 12,
|
|
|
use_numba: bool = True,
|
|
|
use_parallel: bool = True,
|
|
|
evaluate_quality: bool = False,
|
|
|
show_visualizations: bool = False,
|
|
|
verbose: bool = False) -> Tuple[np.ndarray, Optional[Dict], Optional[ImageContext]]:
|
|
|
"""
|
|
|
Create advanced mosaic - FULLY OPTIMIZED.
|
|
|
|
|
|
Args:
|
|
|
image_path: Path to input image
|
|
|
tile_folder: Path to tile images folder
|
|
|
grid_size: Grid dimensions (rows, cols)
|
|
|
tile_size: Individual tile size (width, height)
|
|
|
diversity_factor: Tile diversity factor (0.0-0.5)
|
|
|
enable_rotation: Whether to enable 4-way tile rotation
|
|
|
enable_face_detection: Whether to enable face detection
|
|
|
colour_bins: Number of color bins for subgrouping
|
|
|
apply_quantization: Whether to apply color quantization
|
|
|
n_colors: Number of colors for quantization
|
|
|
use_numba: Use Numba JIT compilation (recommended)
|
|
|
use_parallel: Use parallel processing (recommended)
|
|
|
evaluate_quality: Whether to calculate quality metrics
|
|
|
show_visualizations: Whether to display analysis
|
|
|
verbose: Enable detailed logging
|
|
|
|
|
|
Returns:
|
|
|
Tuple of (mosaic_image, quality_metrics, context_analysis)
|
|
|
"""
|
|
|
|
|
|
if verbose:
|
|
|
print(f"Advanced Mosaic Generator (Numba={'ON' if use_numba else 'OFF'}, Parallel={'ON' if use_parallel else 'OFF'})")
|
|
|
print(f"Image: {image_path}")
|
|
|
print(f"Grid: {grid_size[0]}x{grid_size[1]} = {np.prod(grid_size):,} tiles")
|
|
|
print(f"Tile size: {tile_size[0]}x{tile_size[1]}")
|
|
|
|
|
|
total_start = time.time()
|
|
|
|
|
|
|
|
|
cache_filename = f"cache_{tile_size[0]}x{tile_size[1]}_bins{colour_bins}{'_rot' if enable_rotation else ''}.pkl"
|
|
|
|
|
|
generator = ContextualMosaicGenerator(
|
|
|
cache_file=cache_filename,
|
|
|
tile_folder=tile_folder,
|
|
|
tile_size=tile_size,
|
|
|
colour_bins=colour_bins,
|
|
|
enable_rotation=enable_rotation,
|
|
|
enable_face_detection=enable_face_detection,
|
|
|
verbose=verbose,
|
|
|
use_numba=use_numba
|
|
|
)
|
|
|
|
|
|
|
|
|
target_width = grid_size[1] * tile_size[0]
|
|
|
target_height = grid_size[0] * tile_size[1]
|
|
|
|
|
|
preprocessor = ImagePreprocessor(
|
|
|
target_resolution=(target_width, target_height),
|
|
|
grid_size=grid_size,
|
|
|
verbose=verbose
|
|
|
)
|
|
|
|
|
|
processed_image = preprocessor.load_and_preprocess_image(
|
|
|
image_path,
|
|
|
apply_quantization=apply_quantization,
|
|
|
n_colors=n_colors
|
|
|
)
|
|
|
|
|
|
if processed_image is None:
|
|
|
raise ValueError("Failed to preprocess image")
|
|
|
|
|
|
|
|
|
context = None
|
|
|
if enable_face_detection or show_visualizations:
|
|
|
context = generator.analyze_image_context(processed_image)
|
|
|
|
|
|
|
|
|
mosaic, context = generator.create_contextual_mosaic(
|
|
|
processed_image,
|
|
|
grid_size,
|
|
|
diversity_factor,
|
|
|
context=context,
|
|
|
use_parallel=use_parallel
|
|
|
)
|
|
|
|
|
|
|
|
|
if show_visualizations and context is not None:
|
|
|
generator.visualize_context_analysis(processed_image, context)
|
|
|
|
|
|
|
|
|
metrics = None
|
|
|
if evaluate_quality:
|
|
|
evaluator = PerformanceEvaluator()
|
|
|
metrics = evaluator.evaluate_mosaic_quality(processed_image, mosaic, image_path)
|
|
|
|
|
|
if show_visualizations:
|
|
|
evaluator.visualize_quality_comparison(processed_image, mosaic, metrics)
|
|
|
|
|
|
total_time = time.time() - total_start
|
|
|
|
|
|
if verbose:
|
|
|
print(f"\n✅ Total time: {total_time:.2f}s")
|
|
|
if metrics:
|
|
|
print(f"Quality score: {metrics['overall_quality']:.1f}/100")
|
|
|
|
|
|
return mosaic, metrics, context
|
|
|
|
|
|
|
|
|
def create_mosaic_for_gradio(image_array: np.ndarray,
|
|
|
tile_folder: str = "extracted_images",
|
|
|
grid_size: int = 64,
|
|
|
tile_size: int = 32,
|
|
|
diversity_factor: float = 0.15,
|
|
|
enable_rotation: bool = True) -> Tuple[np.ndarray, str]:
|
|
|
"""Gradio-optimized mosaic creation."""
|
|
|
try:
|
|
|
grid_tuple = (grid_size, grid_size)
|
|
|
tile_tuple = (tile_size, tile_size)
|
|
|
|
|
|
temp_path = "temp_gradio_input.jpg"
|
|
|
cv2.imwrite(temp_path, cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR))
|
|
|
|
|
|
mosaic, metrics, context = create_advanced_mosaic(
|
|
|
image_path=temp_path,
|
|
|
tile_folder=tile_folder,
|
|
|
grid_size=grid_tuple,
|
|
|
tile_size=tile_tuple,
|
|
|
diversity_factor=diversity_factor,
|
|
|
enable_rotation=enable_rotation,
|
|
|
enable_face_detection=False,
|
|
|
use_numba=True,
|
|
|
use_parallel=True,
|
|
|
evaluate_quality=True,
|
|
|
show_visualizations=False,
|
|
|
verbose=False
|
|
|
)
|
|
|
|
|
|
Path(temp_path).unlink(missing_ok=True)
|
|
|
|
|
|
status = f"""Mosaic Generated Successfully!
|
|
|
|
|
|
Configuration:
|
|
|
• Grid: {grid_size}×{grid_size} = {grid_size**2:,} tiles
|
|
|
• Tile size: {tile_size}×{tile_size} pixels
|
|
|
• Optimizations: Numba JIT + Parallel
|
|
|
• Quality score: {metrics['overall_quality']:.1f}/100"""
|
|
|
|
|
|
return mosaic, status
|
|
|
|
|
|
except Exception as e:
|
|
|
return None, f"Error: {str(e)}"
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
IMAGE_PATH = "Images/EmmaPotrait.jpg"
|
|
|
TILE_FOLDER = "extracted_images"
|
|
|
|
|
|
if not Path(TILE_FOLDER).exists():
|
|
|
print(f"Tile folder not found: {TILE_FOLDER}")
|
|
|
exit(1)
|
|
|
|
|
|
if not Path(IMAGE_PATH).exists():
|
|
|
print(f"Image not found: {IMAGE_PATH}")
|
|
|
exit(1)
|
|
|
|
|
|
print("Creating optimized mosaic (Numba + Parallel)...")
|
|
|
|
|
|
mosaic, metrics, context = create_advanced_mosaic(
|
|
|
image_path=IMAGE_PATH,
|
|
|
tile_folder=TILE_FOLDER,
|
|
|
grid_size=(64, 64),
|
|
|
tile_size=(64, 64),
|
|
|
diversity_factor=0.15,
|
|
|
enable_rotation=True,
|
|
|
enable_face_detection=False,
|
|
|
apply_quantization=False,
|
|
|
use_numba=True,
|
|
|
use_parallel=True,
|
|
|
evaluate_quality=True,
|
|
|
show_visualizations=False,
|
|
|
verbose=True
|
|
|
)
|
|
|
|
|
|
if mosaic is not None:
|
|
|
print("✅ Mosaic generation completed!")
|
|
|
if metrics:
|
|
|
print(f"Quality score: {metrics['overall_quality']:.1f}/100")
|
|
|
|
|
|
cv2.imwrite("output_mosaic.jpg", cv2.cvtColor(mosaic, cv2.COLOR_RGB2BGR))
|
|
|
print("Saved: output_mosaic.jpg") |