Compare commits

...

2 Commits

Author SHA1 Message Date
Adriano 5002515b41 fix: rimuove edge spuri sui bordi template warpato (apparivano come ROI)
Bug: per ogni match l'overlay edge del modello includeva anche il
PERIMETRO del template warpato (transizione bordo nero borderValue=0
→ scena = forte gradient artefatto). Con N match si vedevano N
rettangoli verdi attorno ai pezzi, simili a "ROI ripetute".

Fix:
- Warpa anche _train_mask alla pose
- Erode di (2*spread_radius+1) per scartare la fascia di transizione
  bordo che produce gradient spurio
- Maschera edge_mask con warped_mask: solo edge interni al pezzo
  vengono visualizzati

Risultato: overlay edge pulito che mostra solo i veri edge del
modello allineati al pezzo trovato, niente cornici fasulle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:13:07 +02:00
Adriano 8029a1e12b merge: UCS coerente centro pose 2026-05-05 12:04:24 +02:00
+16 -1
View File
@@ -161,7 +161,10 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
cx, cy = int(round(m.cx)), int(round(m.cy)) cx, cy = int(round(m.cx)), int(round(m.cy))
# Overlay edge del modello orientato (richiesta utente): # Overlay edge del modello orientato (richiesta utente):
# warpa template alla pose, applica hysteresis identica al matcher, # warpa template alla pose, applica hysteresis identica al matcher,
# disegna pixel edge come overlay verde tenue. # disegna pixel edge come overlay verde tenue. Maschera col
# _train_mask warpato + erode per rimuovere edge sui BORDI del
# rettangolo template (transizione bordo nero → scena = falso edge
# che appariva come "ROI" attorno a ogni match).
if template_gray is not None and matcher is not None: if template_gray is not None and matcher is not None:
t = template_gray t = template_gray
th, tw = t.shape th, tw = t.shape
@@ -172,11 +175,23 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
warped_gray = cv2.warpAffine( warped_gray = cv2.warpAffine(
t, M, (W_scene, H_scene), t, M, (W_scene, H_scene),
flags=cv2.INTER_LINEAR, borderValue=0) flags=cv2.INTER_LINEAR, borderValue=0)
# Maschera: train_mask se disponibile, altrimenti rettangolo pieno
mask_src = (matcher._train_mask if matcher._train_mask is not None
else np.full((th, tw), 255, dtype=np.uint8))
warped_mask = cv2.warpAffine(
mask_src, M, (W_scene, H_scene),
flags=cv2.INTER_NEAREST, borderValue=0)
# Erode di spread_radius per scartare la fascia di transizione
# bordo che produce gradient spurio
er_k = max(3, 2 * matcher.spread_radius + 1)
kernel_er = np.ones((er_k, er_k), np.uint8)
warped_mask = cv2.erode(warped_mask, kernel_er)
mag, _ = matcher._gradient(warped_gray) mag, _ = matcher._gradient(warped_gray)
if matcher.weak_grad < matcher.strong_grad: if matcher.weak_grad < matcher.strong_grad:
edge_mask = matcher._hysteresis_mask(mag) edge_mask = matcher._hysteresis_mask(mag)
else: else:
edge_mask = mag >= matcher.strong_grad edge_mask = mag >= matcher.strong_grad
edge_mask = edge_mask & (warped_mask > 0)
if edge_mask.any(): if edge_mask.any():
edge_overlay = np.zeros_like(out) edge_overlay = np.zeros_like(out)
edge_overlay[edge_mask] = (0, 220, 0) # verde brillante edge_overlay[edge_mask] = (0, 220, 0) # verde brillante