kamp0010 commited on
Commit
24ae88a
·
verified ·
1 Parent(s): 7fa2777

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +86 -84
src/streamlit_app.py CHANGED
@@ -55,32 +55,35 @@ class DominantColorDetector:
55
  self.edge_multiplier = edge_multiplier
56
 
57
  def _preprocess(self, img: Image.Image):
58
- """Resize, keep aspect ratio, return float32 pixels."""
59
  if img.mode != "RGB":
60
  img = img.convert("RGB")
61
- w, h = img.size
62
- aspect = h / w
63
- new_h = max(1, int(self.resize_dim * aspect))
64
- img = img.resize((self.resize_dim, new_h), Image.LANCZOS)
65
- return np.array(img).reshape(-1, 3).astype(np.float32)
66
-
67
- def _build_edge_mask(self, img_array_flat, orig_shape):
68
- """Create a weight mask highlighting edge regions."""
69
- h, w, _ = orig_shape
70
- mask = np.ones(h * w, dtype=np.float32)
71
-
72
- # 10% strips at top/bottom/left/right
73
  edge_frac = 0.10
74
  top_height = int(h * edge_frac)
75
  bottom_height = int(h * edge_frac)
76
  left_width = int(w * edge_frac)
77
  right_width = int(w * edge_frac)
78
-
79
  for y in range(h):
80
  for x in range(w):
81
  idx = y * w + x
82
- if y < top_height or y >= h - bottom_height or \
83
- x < left_width or x >= w - right_width:
 
 
 
 
84
  mask[idx] = self.edge_multiplier
85
  return mask
86
 
@@ -181,75 +184,74 @@ class DominantColorDetector:
181
  }
182
 
183
  def detect_properties(self, img: Image.Image, include_palette=True):
184
- orig_shape = img.size # (width, height) before flatten
185
- pixels_rgb = self._preprocess(img)
186
- resized_shape = img.size # after resize
187
-
188
- # Build edge mask and create weighted sample pool
189
- edge_mask = self._build_edge_mask(pixels_rgb, resized_shape[::-1]) # h,w
190
- max_pixels = 10000
191
- if len(pixels_rgb) > max_pixels:
192
- # Weighted random choice
193
- probs = edge_mask / edge_mask.sum()
194
- idx = np.random.choice(len(pixels_rgb), max_pixels, replace=False, p=probs)
195
- sampled_pixels = pixels_rgb[idx]
196
- # Track edge influence for each sampled pixel
197
- sampled_mask = edge_mask[idx] # >1 for edges
198
- else:
199
- sampled_pixels = pixels_rgb
200
- sampled_mask = edge_mask
201
-
202
- pixels_lab = self._pixels_to_lab(sampled_pixels)
203
- kmeans = KMeans(n_clusters=self.num_colors, random_state=42,
204
- n_init=3, max_iter=100, algorithm="elkan")
205
- kmeans.fit(pixels_lab)
206
- centroids_lab = kmeans.cluster_centers_
207
- labels = kmeans.labels_
208
- label_counts = Counter(labels)
209
- total = len(labels)
210
- centroids_rgb = self._lab_to_rgb(centroids_lab)
211
-
212
- color_list = []
213
- for i in range(self.num_colors):
214
- lab = centroids_lab[i]
215
- rgb = centroids_rgb[i]
216
- pf = label_counts[i] / total
217
- if pf < self.MIN_PIXEL_FRACTION:
218
- continue
219
- # Edge influence: fraction of this cluster's samples that were edge pixels
220
- cluster_mask = labels == i
221
- edge_frac = np.mean(sampled_mask[cluster_mask] > 1.0) # proportion from edges
222
- r, g, b = int(rgb[0]), int(rgb[1]), int(rgb[2])
223
- color_list.append({
224
- "lab": lab,
225
- "rgb": {"red": r, "green": g, "blue": b},
226
- "color": rgb_to_hex((r, g, b)),
227
- "pixelFraction": float(pf),
228
- "score": self._score_color(lab, pf),
229
- "chroma": round(lab_chroma(lab), 2),
230
- "isNearBlack": bool(self._is_near_black(lab)),
231
- "isNearWhite": bool(self._is_near_white(lab)),
232
- "isNearGray": bool(self._is_near_gray(lab)),
233
- "edgeInfluence": round(edge_frac, 3),
234
- })
235
- color_list.sort(key=lambda x: x["score"], reverse=True)
236
- color_list = self._remove_near_duplicates(color_list)
237
- dominant_colors = [
238
- {k: v for k, v in c.items() if k != "lab"}
239
- for c in color_list
240
- ]
241
- for c in dominant_colors:
242
- c["score"] = round(c["score"], 4)
243
- c["pixelFraction"] = round(c["pixelFraction"], 4)
244
-
245
- result = {
246
- "imagePropertiesAnnotation": {
247
- "dominantColors": {"colors": dominant_colors}
248
- }
249
  }
250
- if include_palette:
251
- result["suggestedPalette"] = self._build_adaptive_palette(color_list)
252
- return result
 
253
 
254
  # -------------------------------------------------------------------
255
  # Helper functions (global)
 
55
  self.edge_multiplier = edge_multiplier
56
 
57
  def _preprocess(self, img: Image.Image):
 
58
  if img.mode != "RGB":
59
  img = img.convert("RGB")
60
+ original_w, original_h = img.size
61
+ aspect = original_h / original_w
62
+ new_h = max(1, int(self.resize_dim * aspect))
63
+ img = img.resize((self.resize_dim, new_h), Image.LANCZOS)
64
+ resized_w, resized_h = img.size
65
+ pixels = np.array(img).reshape(-1, 3).astype(np.float32)
66
+ return pixels, (resized_w, resized_h)
67
+
68
+ def _build_edge_mask(self, total_pixels, resized_dims):
69
+ w, h = resized_dims
70
+ mask = np.ones(total_pixels, dtype=np.float32)
71
+
72
  edge_frac = 0.10
73
  top_height = int(h * edge_frac)
74
  bottom_height = int(h * edge_frac)
75
  left_width = int(w * edge_frac)
76
  right_width = int(w * edge_frac)
77
+
78
  for y in range(h):
79
  for x in range(w):
80
  idx = y * w + x
81
+ if (
82
+ y < top_height
83
+ or y >= h - bottom_height
84
+ or x < left_width
85
+ or x >= w - right_width
86
+ ):
87
  mask[idx] = self.edge_multiplier
88
  return mask
89
 
 
184
  }
185
 
186
  def detect_properties(self, img: Image.Image, include_palette=True):
187
+ pixels_rgb, (resized_w, resized_h) = self._preprocess(img)
188
+ total_pixels = len(pixels_rgb)
189
+
190
+ # Build edge mask for the full resized image
191
+ edge_mask = self._build_edge_mask(total_pixels, (resized_w, resized_h))
192
+
193
+ max_pixels = 10000
194
+ if total_pixels > max_pixels:
195
+ probs = edge_mask / edge_mask.sum()
196
+ idx = np.random.choice(total_pixels, max_pixels, replace=False, p=probs)
197
+ sampled_pixels = pixels_rgb[idx]
198
+ sampled_mask = edge_mask[idx]
199
+ else:
200
+ sampled_pixels = pixels_rgb
201
+ sampled_mask = edge_mask
202
+
203
+ pixels_lab = self._pixels_to_lab(sampled_pixels)
204
+ kmeans = KMeans(n_clusters=self.num_colors, random_state=42,
205
+ n_init=3, max_iter=100, algorithm="elkan")
206
+ kmeans.fit(pixels_lab)
207
+ centroids_lab = kmeans.cluster_centers_
208
+ labels = kmeans.labels_
209
+ label_counts = Counter(labels)
210
+ total = len(labels)
211
+ centroids_rgb = self._lab_to_rgb(centroids_lab)
212
+
213
+ color_list = []
214
+ for i in range(self.num_colors):
215
+ lab = centroids_lab[i]
216
+ rgb = centroids_rgb[i]
217
+ pf = label_counts[i] / total
218
+ if pf < self.MIN_PIXEL_FRACTION:
219
+ continue
220
+ cluster_mask = labels == i
221
+ edge_frac = np.mean(sampled_mask[cluster_mask] > 1.0)
222
+ r, g, b = int(rgb[0]), int(rgb[1]), int(rgb[2])
223
+ color_list.append({
224
+ "lab": lab,
225
+ "rgb": {"red": r, "green": g, "blue": b},
226
+ "color": rgb_to_hex((r, g, b)),
227
+ "pixelFraction": float(pf),
228
+ "score": self._score_color(lab, pf),
229
+ "chroma": round(lab_chroma(lab), 2),
230
+ "isNearBlack": bool(self._is_near_black(lab)),
231
+ "isNearWhite": bool(self._is_near_white(lab)),
232
+ "isNearGray": bool(self._is_near_gray(lab)),
233
+ "edgeInfluence": round(edge_frac, 3),
234
+ })
235
+
236
+ color_list.sort(key=lambda x: x["score"], reverse=True)
237
+ color_list = self._remove_near_duplicates(color_list)
238
+
239
+ dominant_colors = [
240
+ {k: v for k, v in c.items() if k != "lab"}
241
+ for c in color_list
242
+ ]
243
+ for c in dominant_colors:
244
+ c["score"] = round(c["score"], 4)
245
+ c["pixelFraction"] = round(c["pixelFraction"], 4)
246
+
247
+ result = {
248
+ "imagePropertiesAnnotation": {
249
+ "dominantColors": {"colors": dominant_colors}
 
 
250
  }
251
+ }
252
+ if include_palette:
253
+ result["suggestedPalette"] = self._build_adaptive_palette(color_list)
254
+ return result
255
 
256
  # -------------------------------------------------------------------
257
  # Helper functions (global)