feat: dedup varianti con feature-set identico post-quantizzazione
Hash byte-exact su (dx, dy, bin) ordinati + scale. Se due varianti post-rasterizzazione hanno lo stesso feature-set, ne tiene solo una. Tipico caso d'uso: template con simmetrie discrete (quadrati, croci, forme regolari) generano duplicati esatti per rotazioni multiple del periodo. Su quadrato 80x80 con angle_step=10 deg: 36 -> 27 varianti (~25% in meno di lavoro top-pruning). Approccio conservativo (byte-exact): zero rischio di rimuovere varianti distinte. Forme arrotondate (cerchi) o template asimmetrici non beneficiano ma non vengono compromessi. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -293,8 +293,42 @@ class LineShapeMatcher:
|
||||
kh=kh, kw=kw,
|
||||
cx_local=float(cx_local), cy_local=float(cy_local),
|
||||
))
|
||||
self._dedup_variants()
|
||||
return len(self.variants)
|
||||
|
||||
def _dedup_variants(self) -> int:
|
||||
"""Rimuove varianti con feature-set identico (post-quantizzazione).
|
||||
|
||||
Halcon-style: con angle range = (0, 360) e simmetrie del template,
|
||||
molte rotazioni producono lo stesso set quantizzato di feature.
|
||||
Es: quadrato a 0/90/180/270 deg → stesse features (modulo permutazione).
|
||||
Hash su feature ordinate (livello 0, full-res) elimina i duplicati.
|
||||
|
||||
Vantaggio: meno varianti = meno chiamate kernel JIT al top-level
|
||||
senza perdere copertura angolare effettiva. Per template asimmetrici
|
||||
non rimuove nulla.
|
||||
"""
|
||||
seen: dict[bytes, int] = {}
|
||||
kept: list[_Variant] = []
|
||||
removed = 0
|
||||
for var in self.variants:
|
||||
lvl0 = var.levels[0]
|
||||
order = np.lexsort((lvl0.bin, lvl0.dy, lvl0.dx))
|
||||
key = (
|
||||
lvl0.dx[order].tobytes()
|
||||
+ b"|" + lvl0.dy[order].tobytes()
|
||||
+ b"|" + lvl0.bin[order].tobytes()
|
||||
+ b"|" + str(round(var.scale, 4)).encode()
|
||||
)
|
||||
h = key # diretto, senza hash crypto (collision ok solo se identici)
|
||||
if h in seen:
|
||||
removed += 1
|
||||
continue
|
||||
seen[h] = len(kept)
|
||||
kept.append(var)
|
||||
self.variants = kept
|
||||
return removed
|
||||
|
||||
# --- Matching ------------------------------------------------------
|
||||
|
||||
def _response_map(self, gray: np.ndarray) -> np.ndarray:
|
||||
|
||||
Reference in New Issue
Block a user