diff --git a/pm2d/line_matcher.py b/pm2d/line_matcher.py index e5f212a..3133243 100644 --- a/pm2d/line_matcher.py +++ b/pm2d/line_matcher.py @@ -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: