File size: 8,678 Bytes
b041b2b 4f2f2dd df29645 b041b2b df29645 4f2f2dd b041b2b 4f2f2dd df29645 4f2f2dd df29645 4f2f2dd df29645 4f2f2dd df29645 4f2f2dd df29645 4f2f2dd df29645 4f2f2dd df29645 4f2f2dd 8386dbd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
import gradio as gr
import tempfile
import os
from simple_mosaic import SimpleMosaicImage
from tile_library import build_cifar10_tile_library, build_cifar100_tile_library
from performance import calculate_all_metrics, format_metrics_report
MAX_IMAGE_SIZE = 4500
# Global cache for tile libraries to avoid reloading
tile_cache = {}
def get_tile_library(tile_type, max_per_class):
"""Get cached tile library or create new one"""
cache_key = f"{tile_type}_{max_per_class}"
if cache_key not in tile_cache:
if tile_type == "CIFAR-10":
tiles, means, labels = build_cifar10_tile_library(max_per_class=max_per_class)
elif tile_type == "CIFAR-100":
tiles, means, labels = build_cifar100_tile_library(max_per_class=max_per_class)
else:
return None, None, None
tile_cache[cache_key] = (tiles, means, labels)
return tile_cache[cache_key]
def process_mosaic(image, start_size, min_size, threshold, tile_type, max_per_class, quantize_colors):
"""Process image to create mosaic"""
if image is None:
return None, "Please upload an image first."
try:
# Create temporary file for processing
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp_file:
image.save(tmp_file.name)
# Load and process image
loader = SimpleMosaicImage(tmp_file.name)
# Apply Resize if needed
if max(loader.width, loader.height) > MAX_IMAGE_SIZE:
loader.resize(MAX_IMAGE_SIZE);
# Apply color quantization if requested
if quantize_colors > 0:
loader.quantize_colors(quantize_colors)
# Smart boundary handling
loader.crop_to_grid(2)
# Build adaptive cells
cells = loader.build_adaptive_cells(
start_size=int(start_size),
min_size=int(min_size),
threshold=float(threshold)
)
# Generate mosaic based on tile type
if tile_type == "None (Average Colors)":
result_img = loader.mosaic_average_color_adaptive(cells)
basic_info = f"Generated mosaic with {len(cells)} adaptive cells using average colors."
else:
tiles, tile_means, _ = get_tile_library(tile_type, int(max_per_class))
if tiles is None:
return None, f"Failed to load {tile_type} tile library."
result_img = loader.mosaic_with_tiles_adaptive(cells, tiles, tile_means)
basic_info = f"Generated mosaic with {len(cells)} cells using {len(tiles)} {tile_type} tiles."
# Calculate performance metrics
try:
metrics = calculate_all_metrics(image, result_img)
metrics_report = format_metrics_report(metrics)
info = f"{basic_info}\n\n{metrics_report}"
except Exception as e:
info = f"{basic_info}\n\nMetrics calculation failed: {str(e)}"
# Clean up temporary file
os.unlink(tmp_file.name)
return result_img, info
except Exception as e:
return None, f"Error processing image: {str(e)}"
def update_max_per_class_visibility(tile_type):
"""Show/hide max_per_class slider based on tile type"""
if tile_type in ["CIFAR-10", "CIFAR-100"]:
return gr.update(visible=True)
else:
return gr.update(visible=False)
def get_max_per_class_range(tile_type):
"""Get appropriate range for max_per_class slider"""
if tile_type == "CIFAR-10":
return gr.update(maximum=1000, value=500)
elif tile_type == "CIFAR-100":
return gr.update(maximum=400, value=200)
else:
return gr.update(maximum=1000, value=500)
# Simple CSS - white background, black text
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
/* White background everywhere */
* {
background: #ffffff !important;
background-color: #ffffff !important;
font-family: 'Inter', sans-serif !important;
}
/* Black text everywhere */
* {
color: #000000 !important;
}
/* Button exception - keep dark */
.gr-button-primary {
background: #000000 !important;
background-color: #000000 !important;
color: #ffffff !important;
border: 1px solid #000000 !important;
border-radius: 6px !important;
padding: 8px 16px !important;
}
.gr-button-primary:hover {
background: #333333 !important;
background-color: #333333 !important;
}
"""
# Create Gradio interface
with gr.Blocks(css=custom_css, title="Interactive Image Mosaic Generator") as demo:
gr.Markdown("# Interactive Image Mosaic Generator")
gr.Markdown("Transform your images into mosaics with adaptive grid technology")
with gr.Row():
# Left panel - Controls
with gr.Column(scale=1, min_width=300, elem_classes=["control-panel"]):
gr.Markdown("### Controls")
# Image upload
input_image = gr.Image(
label="Upload Image",
type="pil",
height=200,
interactive=True
)
# Grid parameters
gr.Markdown("**Grid Parameters**")
start_size = gr.Slider(
minimum=16, maximum=128, value=64, step=8,
label="Initial Block Size",
info="Starting grid size (larger = fewer details)"
)
min_size = gr.Slider(
minimum=2, maximum=32, value=4, step=2,
label="Minimum Block Size",
info="Smallest allowed grid size"
)
threshold = gr.Slider(
minimum=0.1, maximum=20.0, value=5.0, step=0.1,
label="Subdivision Threshold",
info="Lower values = more subdivision"
)
# Color quantization
quantize_colors = gr.Slider(
minimum=0, maximum=128, value=16, step=1,
label="Color Quantization",
info="Number of colors (0 = no quantization)"
)
# Tile library selection
gr.Markdown("**Tile Library**")
tile_type = gr.Radio(
choices=["None (Average Colors)", "CIFAR-10", "CIFAR-100"],
value="CIFAR-10",
label="Tile Type",
info="Choose tile source or use average colors"
)
max_per_class = gr.Slider(
minimum=10, maximum=1000, value=500, step=10,
label="Tiles per Class",
info="Number of tiles per category",
visible=True
)
# Process button
process_btn = gr.Button(
"Generate Mosaic",
variant="primary",
size="lg"
)
# Right panel - Images
with gr.Column(scale=2, elem_classes=["image-display"]):
gr.Markdown("### Results")
with gr.Row():
# Original image preview
with gr.Column():
gr.Markdown("**Original**")
original_display = gr.Image(
label="Original Image",
height=400,
interactive=False
)
# Result image
with gr.Column():
gr.Markdown("**Mosaic Result**")
result_image = gr.Image(
label="Mosaic Image",
height=400,
interactive=False
)
# Info output
info_output = gr.Textbox(
label="Processing Info & Performance Metrics",
interactive=False,
max_lines=10,
lines=8
)
# Event handlers
tile_type.change(
fn=update_max_per_class_visibility,
inputs=[tile_type],
outputs=[max_per_class]
)
tile_type.change(
fn=get_max_per_class_range,
inputs=[tile_type],
outputs=[max_per_class]
)
input_image.change(
fn=lambda img: img,
inputs=[input_image],
outputs=[original_display]
)
process_btn.click(
fn=process_mosaic,
inputs=[
input_image, start_size, min_size, threshold,
tile_type, max_per_class, quantize_colors
],
outputs=[result_image, info_output]
)
if __name__ == "__main__":
demo.launch() |