VanKee commited on
Commit
4f2f2dd
·
1 Parent(s): bdaf195

include frontend display

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