simonfrnd commited on
Commit
b5bd5fd
·
verified ·
1 Parent(s): ec93044

Update lines and patch matching

Browse files
Files changed (1) hide show
  1. main.py +52 -3
main.py CHANGED
@@ -183,13 +183,14 @@ def make_correspondence_figure(image_a, image_b, patches_a, patches_b, grid_h, g
183
  gap = 30
184
  canvas_w = img_w * 2 + gap
185
  fig, ax = plt.subplots(1, 1, figsize=(14, 6))
186
- fig.patch.set_facecolor('#0f172a') # Slate 900 for dark mode frontend
187
 
188
- canvas = Image.new("RGB", (canvas_w, img_h), (15, 23, 42))
189
  canvas.paste(img_a_resized, (0, 0))
190
  canvas.paste(img_b_resized, (img_w + gap, 0))
191
  ax.imshow(canvas)
192
 
 
193
  cmap = plt.cm.get_cmap("spring", max(len(matches), 1))
194
 
195
  for i, (idx_a, idx_b, score) in enumerate(matches):
@@ -198,9 +199,27 @@ def make_correspondence_figure(image_a, image_b, patches_a, patches_b, grid_h, g
198
  xb_canvas = xb + img_w + gap
199
  color = cmap(i % 20)
200
 
201
- ax.plot([xa, xb_canvas], [ya, yb], color=color, linewidth=2, alpha=0.9)
 
202
  ax.scatter([xa, xb_canvas], [ya, yb], color=color, s=50, zorder=5, edgecolors="white", linewidths=0.5)
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  ax.axis("off")
205
  fig.tight_layout(pad=0)
206
  return fig, len(matches)
@@ -253,10 +272,40 @@ async def analyze_artworks(file_a: UploadFile = File(...), file_b: UploadFile =
253
  cls_g_a, patches_g_a, _, _ = extract_dino_features(img_a, "grayscale")
254
  cls_g_b, patches_g_b, _, _ = extract_dino_features(img_b, "grayscale")
255
 
 
 
 
256
  _, a2b_c, b2a_c = compute_patch_matches(patches_c_a, patches_c_b)
257
  _, a2b_g, b2a_g = compute_patch_matches(patches_g_a, patches_g_b)
258
  _, a2b_e, b2a_e = compute_patch_matches(patches_e_a, patches_e_b)
259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  # The missing magic: Combine domains to defeat color-shifting
261
  a2b_best = torch.max(torch.max(a2b_c, a2b_g), a2b_e)
262
  b2a_best = torch.max(torch.max(b2a_c, b2a_g), b2a_e)
 
183
  gap = 30
184
  canvas_w = img_w * 2 + gap
185
  fig, ax = plt.subplots(1, 1, figsize=(14, 6))
186
+ fig.patch.set_facecolor('#0f172a') # Slate 900 matching React frontend
187
 
188
+ canvas = Image.new("RGB", (canvas_w, img_h), (15, 23, 42)) # Slate 950 canvas
189
  canvas.paste(img_a_resized, (0, 0))
190
  canvas.paste(img_b_resized, (img_w + gap, 0))
191
  ax.imshow(canvas)
192
 
193
+ # Keep the clean, unified 'spring' colormap
194
  cmap = plt.cm.get_cmap("spring", max(len(matches), 1))
195
 
196
  for i, (idx_a, idx_b, score) in enumerate(matches):
 
199
  xb_canvas = xb + img_w + gap
200
  color = cmap(i % 20)
201
 
202
+ # Draw the clean lines and dots
203
+ ax.plot([xa, xb_canvas], [ya, yb], color=color, linewidth=2, alpha=0.8)
204
  ax.scatter([xa, xb_canvas], [ya, yb], color=color, s=50, zorder=5, edgecolors="white", linewidths=0.5)
205
 
206
+ # --- THE NEW CLEAN SCORE BADGES ---
207
+ # Calculate the exact midpoint of the line
208
+ mx = (xa + xb_canvas) / 2
209
+ my = (ya + yb) / 2
210
+
211
+ # Style a sleek dark pill with a glowing border matching the line color
212
+ bbox_props = dict(
213
+ boxstyle="round,pad=0.25",
214
+ fc="#1e293b", # Slate 800 background
215
+ ec=color, # Border matches the line color
216
+ alpha=0.95, # High opacity so it covers the line behind it
217
+ lw=1 # Thin, crisp border
218
+ )
219
+
220
+ ax.text(mx, my, f"{score:.2f}", fontsize=8, color="white", fontweight="bold",
221
+ ha="center", va="center", zorder=10, bbox=bbox_props)
222
+
223
  ax.axis("off")
224
  fig.tight_layout(pad=0)
225
  return fig, len(matches)
 
272
  cls_g_a, patches_g_a, _, _ = extract_dino_features(img_a, "grayscale")
273
  cls_g_b, patches_g_b, _, _ = extract_dino_features(img_b, "grayscale")
274
 
275
+ cls_e_a, patches_e_a, _, _ = extract_dino_features(img_a, "edges")
276
+ cls_e_b, patches_e_b, _, _ = extract_dino_features(img_b, "edges")
277
+
278
  _, a2b_c, b2a_c = compute_patch_matches(patches_c_a, patches_c_b)
279
  _, a2b_g, b2a_g = compute_patch_matches(patches_g_a, patches_g_b)
280
  _, a2b_e, b2a_e = compute_patch_matches(patches_e_a, patches_e_b)
281
 
282
+ # 1. Combine domains to defeat color-shifting (used for Heatmap stats)
283
+ a2b_best = torch.max(torch.max(a2b_c, a2b_g), a2b_e)
284
+ b2a_best = torch.max(torch.max(b2a_c, b2a_g), b2a_e)
285
+
286
+ corr_thresh = (a2b_best.mean() + 0.5 * a2b_best.std()).item()
287
+ corr_thresh = min(max(corr_thresh, 0.4), 0.75)
288
+
289
+ # 2. THE FIX: Dynamically select the best patch domain for the visual lines
290
+ mode_scores = {
291
+ "color": a2b_c.mean().item(),
292
+ "grayscale": a2b_g.mean().item(),
293
+ "edges": a2b_e.mean().item()
294
+ }
295
+ best_mode = max(mode_scores, key=mode_scores.get)
296
+ mode_patches = {
297
+ "color": (patches_c_a, patches_c_b),
298
+ "grayscale": (patches_g_a, patches_g_b),
299
+ "edges": (patches_e_a, patches_e_b),
300
+ }
301
+ corr_pa, corr_pb = mode_patches[best_mode]
302
+
303
+ # 3. Pass the dynamically selected patches to the figure generator
304
+ corr_fig, match_count = make_correspondence_figure(
305
+ img_a, img_b, corr_pa, corr_pb, gh, gw, score_thresh=corr_thresh
306
+ )
307
+ correspondence_map_b64 = fig_to_base64(corr_fig)
308
+
309
  # The missing magic: Combine domains to defeat color-shifting
310
  a2b_best = torch.max(torch.max(a2b_c, a2b_g), a2b_e)
311
  b2a_best = torch.max(torch.max(b2a_c, b2a_g), b2a_e)