diff --git a/pm2d/gui.py b/pm2d/gui.py index 2e32e57..d3a7d91 100644 --- a/pm2d/gui.py +++ b/pm2d/gui.py @@ -12,7 +12,6 @@ Tutta la logica algoritmica vive in pm2d.matcher.EdgeShapeMatcher. from __future__ import annotations -import sys from pathlib import Path from tkinter import Tk, filedialog import tkinter as tk diff --git a/pm2d/line_matcher.py b/pm2d/line_matcher.py index 543e0c6..5ac9d85 100644 --- a/pm2d/line_matcher.py +++ b/pm2d/line_matcher.py @@ -38,7 +38,6 @@ _GOLDEN = (math.sqrt(5.0) - 1.0) / 2.0 # ≈ 0.618 from pm2d._jit_kernels import ( score_by_shift as _jit_score_by_shift, - score_bitmap as _jit_score_bitmap, score_bitmap_rescored as _jit_score_bitmap_rescored, score_bitmap_rescored_window as _jit_score_bitmap_rescored_window, score_bitmap_greedy as _jit_score_bitmap_greedy, @@ -326,8 +325,6 @@ class LineShapeMatcher: n_vars = len(self.variants) n_levels = len(self.variants[0].levels) var_meta = np.zeros((n_vars, 6), dtype=np.float32) # ang, scale, kh, kw, cxl, cyl - all_dx, all_dy, all_bin, all_offsets = [], [], [], [] - offset = 0 all_offsets_per_level = [[] for _ in range(n_levels)] all_dx_per_level = [[] for _ in range(n_levels)] all_dy_per_level = [[] for _ in range(n_levels)] @@ -1483,12 +1480,9 @@ class LineShapeMatcher: if nms_radius is None: nms_radius = max(8, min(self.template_size) // 2) # Pruning adattivo allo step angolare: con step piccolo (<= 3 deg) - # ci sono molte varianti vicine, gli score top-level sono ravvicinati - # e top_thresh*0.5 e' troppo aggressivo: scarta varianti valide che - # sarebbero state riprese al full-res. Stessa cosa per - # coarse_angle_factor (skip 1 ogni 2): con step fine non e' utile. - # Risultato osservato: precisione "veloce" 10° dava risultati - # migliori di "preciso" 2° proprio perche evitava il pruning. + # ci sono molte varianti vicine e gli score top-level sono + # ravvicinati: top_thresh*0.5 e' troppo aggressivo, scarta varianti + # valide che sarebbero state riprese al full-res. # Il path windowed (pyramid_propagate) assume che il picco # top-level localizzi la posizione entro il margine finestra. # Su template ALLUNGATI (es. lama 40x280, o ROI parziale lungo un @@ -1503,10 +1497,25 @@ class LineShapeMatcher: pyramid_propagate = False eff_step = self._effective_angle_step() top_factor = self.top_score_factor - cf_eff = max(1, coarse_angle_factor) if eff_step <= 3.0: top_factor = max(top_factor, 0.7) - cf_eff = 1 + # Coarse step angolare AUTO al top-level (Halcon-style): al livello + # top le feature distano R/2^top dal centro, quindi lo spread + # (raggio in px, costante per livello) tollera una rotazione + # ~atan(spread / (max_side_top/2)) — molto piu' ampia dello step + # richiesto a full-res. Si valuta al top 1 variante ogni cf_eff; + # le intermedie vengono riprese dall'espansione ai vicini. + # Es: template 160 px, 3 livelli, step 2° → tolleranza top ~11° + # → cf 6 → top-pruning ~6x piu' veloce a parita' di recall. + if self.template_size != (0, 0): + max_side_top = max(self.template_size) / (2 ** top) + else: + max_side_top = 64.0 + step_top_tol = math.degrees( + math.atan2(float(self.spread_radius), max(8.0, max_side_top / 2.0)) + ) + cf_auto = int(np.clip(round(step_top_tol / max(eff_step, 1e-6)), 1, 8)) + cf_eff = max(1, coarse_angle_factor, cf_auto) top_thresh = min_score * top_factor diag["top_thresh_used"] = float(top_thresh) @@ -1562,7 +1571,6 @@ class LineShapeMatcher: dtype=bool, ) if scene_bins.any(): - n_scene_active = int(scene_bins.sum()) # Soglia: variante deve avere >= 50% delle sue feature in bin # presenti nella scena. Sotto = score certamente < 0.5. pruned_idx_list = []