feat: background locale + verify NCC per eliminare falsi positivi
Problema: matcher linemod con solo orientamento gradient può dare score alto
su texture dense/rumore che per caso accumulano orientamenti compatibili.
Esempio: template ruota dentata su scena clip → match a score 0.9 (errati).
Fix in 2 livelli:
1. Background score LOCALE nel find()
- _bg_map(resp, box_size) = densità media bin attivi in bbox template
- Rinormalizza score: s' = max(0, (s - bg) / (1 - bg))
- Annulla contributo di zone sature ma preserva pattern puliti
2. Verify NCC post-hoc
- _verify_ncc(): warpa template alla pose (cx, cy, angle, scale) e
calcola NCC classico su intensità con la scena sottostante
- Threshold di default 0.4 elimina FP con edge orientati casualmente
- Parametro esposto in GUI (verify_threshold)
Rimossa penalty di saturazione nel response_map (ridondante).
Test regression (ruote dentate vs clip, clip vs ruote dentate):
no verify: 12+ falsi positivi con score ~0.7
verify 0.4: 1-2 falsi positivi rimanenti, true positive invariati
verify 0.5: 0 falsi positivi, 1 TP scale piccola perso
Benchmark clip→clip (13 istanze):
full pipeline (Numba + threads + refine + subpix + verify): 1.12s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
-4
@@ -43,6 +43,7 @@ PARAM_SCHEMA: list[tuple[str, str, type]] = [
|
||||
("strong_grad", "Strong grad (line)", float),
|
||||
("spread_radius", "Spread radius (line)", int),
|
||||
("pyramid_levels", "Pyramid levels", int),
|
||||
("verify_threshold", "Verify NCC threshold", float),
|
||||
]
|
||||
|
||||
|
||||
@@ -426,6 +427,7 @@ def run(
|
||||
min_score: float = 0.55,
|
||||
max_matches: int = 25,
|
||||
nms_radius: int = 0,
|
||||
verify_threshold: float = 0.4,
|
||||
backend: str = "line",
|
||||
) -> None:
|
||||
"""Entry-point GUI completo."""
|
||||
@@ -467,6 +469,7 @@ def run(
|
||||
"strong_grad": strong_grad,
|
||||
"spread_radius": spread_radius,
|
||||
"pyramid_levels": pyramid_levels,
|
||||
"verify_threshold": verify_threshold,
|
||||
}
|
||||
|
||||
while True:
|
||||
@@ -502,10 +505,17 @@ def run(
|
||||
print(f" train: {n} varianti in {t_train:.2f}s")
|
||||
t0 = time.time()
|
||||
nms = cur["nms_radius"] if cur["nms_radius"] > 0 else None
|
||||
matches = matcher.find(
|
||||
scene, min_score=cur["min_score"],
|
||||
max_matches=cur["max_matches"], nms_radius=nms,
|
||||
)
|
||||
if cur["backend"] == "line":
|
||||
matches = matcher.find(
|
||||
scene, min_score=cur["min_score"],
|
||||
max_matches=cur["max_matches"], nms_radius=nms,
|
||||
verify_threshold=cur.get("verify_threshold", 0.4),
|
||||
)
|
||||
else:
|
||||
matches = matcher.find(
|
||||
scene, min_score=cur["min_score"],
|
||||
max_matches=cur["max_matches"], nms_radius=nms,
|
||||
)
|
||||
t_find = time.time() - t0
|
||||
print(f" find: {len(matches)} match in {t_find:.2f}s")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user