fix: duplicati, score saturato e angolo impreciso
3 problemi visibili da test interattivo: 1. Match duplicati: stesso oggetto trovato da varianti angolari diverse, NMS pre-refine non basta perche refine sposta i match. Aggiunto NMS post-refine cross-variant. 2. Score sempre alto/saturato a 1.0: NCC era opzionale (skip>=0.85) e non veniva mescolato nello score. Ora ncc_skip_above=1.01 (NCC sempre) e score finale = (shape + NCC) / 2: piu discriminante. 3. Angolo impreciso: _refine_angle aveva early-exit per shape-score >= 0.99, ma quel valore satura facile (con pyramid_propagate o spread ampio) senza garantire angolo preciso. Rimosso early-exit: refine angolare e' sempre essenziale per orientamento sub-step. Inoltre: pyramid_propagate default False (era True), riduce duplicati da picchi propagati su angle-vicini. propagate_topk default 4 (era 8). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-10
@@ -570,9 +570,11 @@ class LineShapeMatcher:
|
|||||||
l'angolo con score massimo (parabolic fit sulle 3 score centrali).
|
l'angolo con score massimo (parabolic fit sulle 3 score centrali).
|
||||||
Ritorna (angle_refined, score, cx_refined, cy_refined).
|
Ritorna (angle_refined, score, cx_refined, cy_refined).
|
||||||
"""
|
"""
|
||||||
# Se il match grezzo è già quasi perfetto, NON refinare
|
# NB: rimosso early-skip su score >= 0.99. Lo score linemod/shape
|
||||||
if original_score is not None and original_score >= 0.99:
|
# satura facilmente a 1.0 (specie con pyramid_propagate o spread
|
||||||
return (angle_deg, original_score, cx, cy)
|
# ampio) ma NON garantisce angolo preciso: l'angolo grezzo della
|
||||||
|
# variante e' quantizzato a multipli di angle_step (5 deg default).
|
||||||
|
# Refine angolare e' essenziale per orientamento sub-step.
|
||||||
if search_radius is None:
|
if search_radius is None:
|
||||||
search_radius = self._effective_angle_step() / 2.0
|
search_radius = self._effective_angle_step() / 2.0
|
||||||
|
|
||||||
@@ -750,13 +752,13 @@ class LineShapeMatcher:
|
|||||||
subpixel: bool = True,
|
subpixel: bool = True,
|
||||||
verify_ncc: bool = True,
|
verify_ncc: bool = True,
|
||||||
verify_threshold: float = 0.4,
|
verify_threshold: float = 0.4,
|
||||||
ncc_skip_above: float = 0.85,
|
ncc_skip_above: float = 1.01, # disabilitato di default: NCC sempre
|
||||||
coarse_angle_factor: int = 2,
|
coarse_angle_factor: int = 2,
|
||||||
coarse_stride: int = 1,
|
coarse_stride: int = 1,
|
||||||
scale_penalty: float = 0.0,
|
scale_penalty: float = 0.0,
|
||||||
search_roi: tuple[int, int, int, int] | None = None,
|
search_roi: tuple[int, int, int, int] | None = None,
|
||||||
pyramid_propagate: bool = True,
|
pyramid_propagate: bool = False, # off di default: meno duplicati
|
||||||
propagate_topk: int = 8,
|
propagate_topk: int = 4,
|
||||||
refine_pose_joint: bool = False,
|
refine_pose_joint: bool = False,
|
||||||
greediness: float = 0.0,
|
greediness: float = 0.0,
|
||||||
batch_top: bool = False,
|
batch_top: bool = False,
|
||||||
@@ -1096,14 +1098,18 @@ class LineShapeMatcher:
|
|||||||
search_radius=self._effective_angle_step() / 2.0,
|
search_radius=self._effective_angle_step() / 2.0,
|
||||||
original_score=score,
|
original_score=score,
|
||||||
)
|
)
|
||||||
# NCC verify lazy (Halcon-style): skip se shape-score gia molto
|
# NCC verify (Halcon-style): se ncc_skip_above < 1.0 salta
|
||||||
# alto (probabilita falso positivo trascurabile). NCC e l'op
|
# il calcolo per shape-score gia alti. Default 1.01 = NCC sempre,
|
||||||
# piu costosa per match (warp + corr), quindi vale la pena
|
# piu sicuro contro falsi positivi (lo shape-score satura facile).
|
||||||
# saltarlo quando il gradiente shape e gia conclusivo.
|
# Quando NCC viene calcolato, lo score finale e' la MEDIA tra
|
||||||
|
# shape-score e NCC: rende lo score piu discriminante per
|
||||||
|
# ranking/visualizzazione (uno score 1.0 vero richiede sia
|
||||||
|
# match shape sia template gray identici).
|
||||||
if verify_ncc and float(score_f) < ncc_skip_above:
|
if verify_ncc and float(score_f) < ncc_skip_above:
|
||||||
ncc = self._verify_ncc(gray0, cx_f, cy_f, ang_f, var.scale)
|
ncc = self._verify_ncc(gray0, cx_f, cy_f, ang_f, var.scale)
|
||||||
if ncc < verify_threshold:
|
if ncc < verify_threshold:
|
||||||
continue
|
continue
|
||||||
|
score_f = (float(score_f) + max(0.0, ncc)) * 0.5
|
||||||
|
|
||||||
# Ri-traslo coord da spazio crop ROI a spazio scena originale.
|
# Ri-traslo coord da spazio crop ROI a spazio scena originale.
|
||||||
cx_out = cx_f + roi_offset[0]
|
cx_out = cx_f + roi_offset[0]
|
||||||
@@ -1116,6 +1122,16 @@ class LineShapeMatcher:
|
|||||||
score_f = float(score_f) * max(
|
score_f = float(score_f) * max(
|
||||||
0.0, 1.0 - scale_penalty * abs(var.scale - 1.0)
|
0.0, 1.0 - scale_penalty * abs(var.scale - 1.0)
|
||||||
)
|
)
|
||||||
|
# NMS post-refine: refine puo spostare il match di nms_radius;
|
||||||
|
# ricontrollo overlap su match gia accettati per evitare
|
||||||
|
# duplicati (stesso oggetto trovato da varianti angolari diverse).
|
||||||
|
dup = False
|
||||||
|
for k in kept:
|
||||||
|
if (k.cx - cx_out) ** 2 + (k.cy - cy_out) ** 2 < r2:
|
||||||
|
dup = True
|
||||||
|
break
|
||||||
|
if dup:
|
||||||
|
continue
|
||||||
kept.append(Match(
|
kept.append(Match(
|
||||||
cx=cx_out, cy=cy_out,
|
cx=cx_out, cy=cy_out,
|
||||||
angle_deg=ang_f,
|
angle_deg=ang_f,
|
||||||
|
|||||||
Reference in New Issue
Block a user