diff --git a/pm2d/auto_tune.py b/pm2d/auto_tune.py index 3c83b7b..4c715f3 100644 --- a/pm2d/auto_tune.py +++ b/pm2d/auto_tune.py @@ -152,14 +152,27 @@ def _cache_key(template_bgr: np.ndarray, mask: np.ndarray | None) -> str: return h.hexdigest() -def auto_tune(template_bgr: np.ndarray, mask: np.ndarray | None = None) -> dict: +def auto_tune( + template_bgr: np.ndarray, + mask: np.ndarray | None = None, + angle_tolerance_deg: float | None = None, + angle_center_deg: float = 0.0, +) -> dict: """Analizza template e ritorna dict parametri suggeriti. Chiavi compatibili con edit_params PARAM_SCHEMA. + angle_tolerance_deg: se != None, restringe angle_range a + (center - tol, center + tol). Usare quando l'orientamento del + pezzo e' noto a priori (feeder con guida, posizionamento + meccanico): training molto piu rapido (24x meno varianti per + tol=15° vs 360° pieno). + Risultato cachato in-memory (LRU): ri-chiamare con stessa ROI è O(1). """ ck = _cache_key(template_bgr, mask) + if angle_tolerance_deg is not None: + ck = f"{ck}|tol={angle_tolerance_deg}|c={angle_center_deg}" cached = _TUNE_CACHE.get(ck) if cached is not None: _TUNE_CACHE.move_to_end(ck) @@ -208,8 +221,13 @@ def auto_tune(template_bgr: np.ndarray, mask: np.ndarray | None = None) -> dict: # spread_radius proporzionale a risoluzione + pyramid (tolleranza ~1% dim) spread_radius = int(np.clip(max(3, min_side * 0.02), 3, 8)) - # angle range ridotto se simmetria rotazionale - angle_max = 360.0 / sym["order"] if sym["order"] > 1 else 360.0 + # angle range: priorita' a tolerance hint utente, poi simmetria rotazionale. + if angle_tolerance_deg is not None: + angle_min = float(angle_center_deg - angle_tolerance_deg) + angle_max = float(angle_center_deg + angle_tolerance_deg) + else: + angle_min = 0.0 + angle_max = 360.0 / sym["order"] if sym["order"] > 1 else 360.0 # min_score: se entropia orient alta → template distintivo → soglia alta ok # se entropia bassa → template ambiguo → soglia più permissiva @@ -228,7 +246,7 @@ def auto_tune(template_bgr: np.ndarray, mask: np.ndarray | None = None) -> dict: result = { "backend": "line", - "angle_min": 0.0, + "angle_min": angle_min, "angle_max": angle_max, "angle_step": angle_step, "scale_min": 1.0, diff --git a/pm2d/line_matcher.py b/pm2d/line_matcher.py index a5195df..a4e4a59 100644 --- a/pm2d/line_matcher.py +++ b/pm2d/line_matcher.py @@ -192,6 +192,26 @@ class LineShapeMatcher: np.array(picked_y, np.int32), np.array(picked_b, np.int8)) + def set_angle_range_around( + self, center_deg: float, tolerance_deg: float, + ) -> None: + """Restringe angle_range a (center - tol, center + tol). + + Comodo helper per scenari in cui l'orientamento del pezzo e' + noto a priori entro ±tolerance_deg (es. feeder vibrante con + guida meccanica). Riduce drasticamente le varianti generate + in train(): es. ±15° vs 360° = 24x meno varianti, training + e matching molto piu veloci. + + Esempio: + m.set_angle_range_around(0, 20) # cerca solo in [-20, +20] + m.train(template) + """ + self.angle_range_deg = ( + float(center_deg - tolerance_deg), + float(center_deg + tolerance_deg), + ) + def _scale_list(self) -> list[float]: s0, s1 = self.scale_range if s0 >= s1 or self.scale_step <= 0: