diff --git a/pm2d/line_matcher.py b/pm2d/line_matcher.py index e5f212a..72cf49b 100644 --- a/pm2d/line_matcher.py +++ b/pm2d/line_matcher.py @@ -239,6 +239,8 @@ class LineShapeMatcher: self._train_mask = mask_full.copy() self.variants.clear() + # Invalida cache feature di refine: il template e cambiato. + self._refine_feat_cache = {} for s in self._scale_list(): sw = max(16, int(round(w * s))) sh = max(16, int(round(h * s))) @@ -433,17 +435,36 @@ class LineShapeMatcher: H, W = spread0.shape margin = 3 + # Cache template features per angolo (chiave: int(round(ang*20)) = + # bucket di 0.05°). Golden-search ricontratto puo richiedere lo + # stesso bucket piu volte; evita re-warp+gradient+extract (costoso). + # Cache a livello matcher per riusare tra chiamate find() su scene + # diverse: la rotazione del template non dipende dalla scena. + if not hasattr(self, '_refine_feat_cache'): + self._refine_feat_cache = {} + feat_cache = self._refine_feat_cache + cache_scale_key = round(scale * 1000) + def _score_at_angle(off: float) -> tuple[float, float, float]: """Ritorna (score, best_cx, best_cy) per angolo = angle_deg + off.""" ang = angle_deg + off - M = cv2.getRotationMatrix2D(center, ang, 1.0) - gray_r = cv2.warpAffine(gray_p, M, (diag, diag), - flags=cv2.INTER_LINEAR, - borderMode=cv2.BORDER_REPLICATE) - mask_r = cv2.warpAffine(mask_p, M, (diag, diag), - flags=cv2.INTER_NEAREST, borderValue=0) - mag, bins = self._gradient(gray_r) - fx, fy, fb = self._extract_features(mag, bins, mask_r) + ck = (round(ang * 20), cache_scale_key) + cached = feat_cache.get(ck) + if cached is not None: + fx, fy, fb = cached + else: + M = cv2.getRotationMatrix2D(center, ang, 1.0) + gray_r = cv2.warpAffine(gray_p, M, (diag, diag), + flags=cv2.INTER_LINEAR, + borderMode=cv2.BORDER_REPLICATE) + mask_r = cv2.warpAffine(mask_p, M, (diag, diag), + flags=cv2.INTER_NEAREST, borderValue=0) + mag, bins = self._gradient(gray_r) + fx, fy, fb = self._extract_features(mag, bins, mask_r) + # LRU semplice: limita cache a ~256 angoli (8 angoli * 32 candidati) + if len(feat_cache) > 256: + feat_cache.pop(next(iter(feat_cache))) + feat_cache[ck] = (fx, fy, fb) if len(fx) < 8: return (0.0, cx, cy) dx = (fx - center[0]).astype(np.int32)