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

include performance and modify mosaic class

Browse files
Files changed (3) hide show
  1. app.py +21 -6
  2. performance.py +169 -0
  3. simple_mosaic.py +1 -1
app.py CHANGED
@@ -3,7 +3,9 @@ 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
 
@@ -36,6 +38,10 @@ def process_mosaic(image, start_size, min_size, threshold, tile_type, max_per_cl
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)
@@ -53,14 +59,22 @@ def process_mosaic(image, start_size, min_size, threshold, tile_type, max_per_cl
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)
@@ -119,7 +133,7 @@ custom_css = """
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
 
@@ -151,7 +165,7 @@ with gr.Blocks(css=custom_css, title="Interactive Image Mosaic Generator", lang=
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
  )
@@ -211,9 +225,10 @@ with gr.Blocks(css=custom_css, title="Interactive Image Mosaic Generator", lang=
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
 
3
  import os
4
  from simple_mosaic import SimpleMosaicImage
5
  from tile_library import build_cifar10_tile_library, build_cifar100_tile_library
6
+ from performance import calculate_all_metrics, format_metrics_report
7
 
8
+ MAX_IMAGE_SIZE = 4500
9
  # Global cache for tile libraries to avoid reloading
10
  tile_cache = {}
11
 
 
38
  # Load and process image
39
  loader = SimpleMosaicImage(tmp_file.name)
40
 
41
+ # Apply Resize if needed
42
+ if max(loader.width, loader.height) > MAX_IMAGE_SIZE:
43
+ loader.resize(MAX_IMAGE_SIZE);
44
+
45
  # Apply color quantization if requested
46
  if quantize_colors > 0:
47
  loader.quantize_colors(quantize_colors)
 
59
  # Generate mosaic based on tile type
60
  if tile_type == "None (Average Colors)":
61
  result_img = loader.mosaic_average_color_adaptive(cells)
62
+ basic_info = f"Generated mosaic with {len(cells)} adaptive cells using average colors."
63
  else:
64
  tiles, tile_means, _ = get_tile_library(tile_type, int(max_per_class))
65
  if tiles is None:
66
  return None, f"Failed to load {tile_type} tile library."
67
 
68
  result_img = loader.mosaic_with_tiles_adaptive(cells, tiles, tile_means)
69
+ basic_info = f"Generated mosaic with {len(cells)} cells using {len(tiles)} {tile_type} tiles."
70
+
71
+ # Calculate performance metrics
72
+ try:
73
+ metrics = calculate_all_metrics(image, result_img)
74
+ metrics_report = format_metrics_report(metrics)
75
+ info = f"{basic_info}\n\n{metrics_report}"
76
+ except Exception as e:
77
+ info = f"{basic_info}\n\nMetrics calculation failed: {str(e)}"
78
 
79
  # Clean up temporary file
80
  os.unlink(tmp_file.name)
 
133
  """
134
 
135
  # Create Gradio interface
136
+ with gr.Blocks(css=custom_css, title="Interactive Image Mosaic Generator") as demo:
137
  gr.Markdown("# Interactive Image Mosaic Generator")
138
  gr.Markdown("Transform your images into mosaics with adaptive grid technology")
139
 
 
165
  )
166
 
167
  threshold = gr.Slider(
168
+ minimum=0.1, maximum=20.0, value=5.0, step=0.1,
169
  label="Subdivision Threshold",
170
  info="Lower values = more subdivision"
171
  )
 
225
 
226
  # Info output
227
  info_output = gr.Textbox(
228
+ label="Processing Info & Performance Metrics",
229
  interactive=False,
230
+ max_lines=10,
231
+ lines=8
232
  )
233
 
234
  # Event handlers
performance.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Performance metrics for comparing original and mosaic images
3
+ """
4
+ import numpy as np
5
+ from PIL import Image
6
+ from skimage.metrics import structural_similarity as ssim
7
+ import cv2
8
+
9
+
10
+ def resize_to_match(original, mosaic):
11
+ """Resize mosaic to match original image dimensions"""
12
+ return mosaic.resize(original.size, Image.BICUBIC)
13
+
14
+
15
+ def calculate_mse(original, mosaic):
16
+ """
17
+ Calculate Mean Squared Error between two images
18
+ Lower values indicate better similarity (0 = perfect match)
19
+ """
20
+ mosaic_resized = resize_to_match(original, mosaic)
21
+
22
+ orig_array = np.array(original, dtype=np.float32)
23
+ mosaic_array = np.array(mosaic_resized, dtype=np.float32)
24
+
25
+ mse = np.mean((orig_array - mosaic_array) ** 2)
26
+ return float(mse)
27
+
28
+
29
+ def calculate_ssim(original, mosaic):
30
+ """
31
+ Calculate Structural Similarity Index
32
+ Range: [-1, 1], higher values indicate better similarity (1 = perfect match)
33
+ """
34
+ mosaic_resized = resize_to_match(original, mosaic)
35
+
36
+ # Convert to grayscale for SSIM calculation
37
+ orig_gray = np.array(original.convert('L'))
38
+ mosaic_gray = np.array(mosaic_resized.convert('L'))
39
+
40
+ ssim_value = ssim(orig_gray, mosaic_gray)
41
+ return float(ssim_value)
42
+
43
+
44
+ def calculate_histogram_correlation(original, mosaic):
45
+ """
46
+ Calculate histogram correlation for each RGB channel
47
+ Range: [-1, 1], higher values indicate better similarity
48
+ """
49
+ mosaic_resized = resize_to_match(original, mosaic)
50
+
51
+ orig_array = np.array(original)
52
+ mosaic_array = np.array(mosaic_resized)
53
+
54
+ correlations = []
55
+ for channel in range(3): # RGB channels
56
+ hist_orig = cv2.calcHist([orig_array[:, :, channel]], [0], None, [256], [0, 256])
57
+ hist_mosaic = cv2.calcHist([mosaic_array[:, :, channel]], [0], None, [256], [0, 256])
58
+
59
+ # Normalize histograms
60
+ hist_orig = hist_orig.flatten() / np.sum(hist_orig)
61
+ hist_mosaic = hist_mosaic.flatten() / np.sum(hist_mosaic)
62
+
63
+ # Calculate correlation
64
+ correlation = np.corrcoef(hist_orig, hist_mosaic)[0, 1]
65
+ correlations.append(correlation)
66
+
67
+ return float(np.mean(correlations))
68
+
69
+
70
+ def calculate_edge_similarity(original, mosaic):
71
+ """
72
+ Calculate edge similarity using Canny edge detection
73
+ Range: [0, 1], higher values indicate better similarity
74
+ """
75
+ mosaic_resized = resize_to_match(original, mosaic)
76
+
77
+ # Convert to grayscale
78
+ orig_gray = np.array(original.convert('L'))
79
+ mosaic_gray = np.array(mosaic_resized.convert('L'))
80
+
81
+ # Apply Canny edge detection
82
+ orig_edges = cv2.Canny(orig_gray, 50, 150)
83
+ mosaic_edges = cv2.Canny(mosaic_gray, 50, 150)
84
+
85
+ # Calculate intersection over union (IoU) of edges
86
+ intersection = np.logical_and(orig_edges, mosaic_edges).sum()
87
+ union = np.logical_or(orig_edges, mosaic_edges).sum()
88
+
89
+ if union == 0:
90
+ return 1.0 # No edges in either image
91
+
92
+ edge_similarity = intersection / union
93
+ return float(edge_similarity)
94
+
95
+
96
+ def calculate_all_metrics(original, mosaic):
97
+ """
98
+ Calculate all performance metrics and return as dictionary
99
+ """
100
+ metrics = {}
101
+
102
+ try:
103
+ metrics['MSE'] = calculate_mse(original, mosaic)
104
+ metrics['SSIM'] = calculate_ssim(original, mosaic)
105
+ metrics['Histogram_Correlation'] = calculate_histogram_correlation(original, mosaic)
106
+ metrics['Edge_Similarity'] = calculate_edge_similarity(original, mosaic)
107
+
108
+ # Calculate overall quality score
109
+ metrics['Overall_Quality'] = (
110
+ metrics['SSIM'] * 0.4 +
111
+ metrics['Histogram_Correlation'] * 0.3 +
112
+ metrics['Edge_Similarity'] * 0.3
113
+ )
114
+
115
+ except Exception as e:
116
+ print(f"Error calculating metrics: {e}")
117
+ # Return default values if calculation fails
118
+ metrics = {
119
+ 'MSE': float('nan'),
120
+ 'SSIM': float('nan'),
121
+ 'Histogram_Correlation': float('nan'),
122
+ 'Edge_Similarity': float('nan'),
123
+ 'Overall_Quality': float('nan')
124
+ }
125
+
126
+ return metrics
127
+
128
+
129
+ def format_metrics_report(metrics):
130
+ """
131
+ Format metrics into a readable report string
132
+ """
133
+ if not metrics:
134
+ return "No metrics calculated"
135
+
136
+ report = "Performance Metrics:\n\n"
137
+
138
+ # Core metrics
139
+ mse = metrics.get('MSE', float('nan'))
140
+ ssim_val = metrics.get('SSIM', float('nan'))
141
+ hist_corr = metrics.get('Histogram_Correlation', float('nan'))
142
+ edge_sim = metrics.get('Edge_Similarity', float('nan'))
143
+
144
+ if not np.isnan(mse):
145
+ report += f"MSE: {mse:.2f} (lower is better)\n"
146
+ if not np.isnan(ssim_val):
147
+ report += f"SSIM: {ssim_val:.4f} (higher is better)\n"
148
+ if not np.isnan(hist_corr):
149
+ report += f"Histogram Correlation: {hist_corr:.4f} (higher is better)\n"
150
+ if not np.isnan(edge_sim):
151
+ report += f"Edge Similarity: {edge_sim:.4f} (higher is better)\n\n"
152
+
153
+ # Overall quality
154
+ overall = metrics.get('Overall_Quality', float('nan'))
155
+ if not np.isnan(overall):
156
+ report += f"Overall Quality Score: {overall:.4f}\n"
157
+
158
+ if overall > 0.8:
159
+ report += "Quality: Excellent"
160
+ elif overall > 0.6:
161
+ report += "Quality: Good"
162
+ elif overall > 0.4:
163
+ report += "Quality: Fair"
164
+ elif overall > 0.2:
165
+ report += "Quality: Poor"
166
+ else:
167
+ report += "Quality: Very Poor"
168
+
169
+ return report
simple_mosaic.py CHANGED
@@ -12,7 +12,7 @@ class SimpleMosaicImage:
12
  self.width, self.height = self.img.size
13
  print(f"[INFO] Loaded: {path} | size={self.width}x{self.height}")
14
 
15
- def resize(self, longest_side: int = 512) -> "SimpleMosaicImage":
16
  w, h = self.width, self.height
17
  scale = longest_side / max(w, h)
18
  if scale < 1.0:
 
12
  self.width, self.height = self.img.size
13
  print(f"[INFO] Loaded: {path} | size={self.width}x{self.height}")
14
 
15
+ def resize(self, longest_side: int = 4000) -> "SimpleMosaicImage":
16
  w, h = self.width, self.height
17
  scale = longest_side / max(w, h)
18
  if scale < 1.0: