diff --git a/pm2d/line_matcher.py b/pm2d/line_matcher.py index b9da553..b0291ad 100644 --- a/pm2d/line_matcher.py +++ b/pm2d/line_matcher.py @@ -796,7 +796,15 @@ class LineShapeMatcher: scn = scn_crop[valid].astype(np.float32) tm = tpl - tpl.mean() sm = scn - scn.mean() - denom = np.sqrt((tm * tm).sum() * (sm * sm).sum()) + 1e-9 + # Std minimo: se template o scena patch sono quasi uniformi + # (es. zona di sfondo bianco/nero), NCC e instabile e da false + # high-correlation. Halcon-style: scarta match. + tpl_var = float((tm * tm).sum()) + scn_var = float((sm * sm).sum()) + n_pix = float(valid.sum()) + if tpl_var < 1e-3 * n_pix or scn_var < 1e-3 * n_pix: + return 0.0 + denom = np.sqrt(tpl_var * scn_var) + 1e-9 return float((tm * sm).sum() / denom) def find( @@ -1175,6 +1183,22 @@ class LineShapeMatcher: poly = _oriented_bbox_polygon( cx_out, cy_out, tw * var.scale, th * var.scale, ang_f, ) + # Reject match con bbox che sfora pesantemente la scena: + # spesso indica match spurio (centro derivato male o scala + # incoerente). Tollera 25% out-of-bounds, sopra rigetta. + H_scn, W_scn = gray_full.shape + poly_area = float(cv2.contourArea(poly)) + if poly_area > 0: + # Clip poly alla scena: intersezione con rettangolo (0,0,W,H) + scene_rect = np.array([ + [0, 0], [W_scn, 0], [W_scn, H_scn], [0, H_scn], + ], dtype=np.float32) + inter, _ = cv2.intersectConvexConvex( + poly.astype(np.float32), scene_rect, + ) + inside_ratio = float(inter) / poly_area + if inside_ratio < 0.75: + continue # Penalità scala opzionale: score degrada con distanza da 1.0 if scale_penalty > 0.0 and var.scale != 1.0: score_f = float(score_f) * max( diff --git a/pm2d/web/server.py b/pm2d/web/server.py index 1d49ecd..3d5b092 100644 --- a/pm2d/web/server.py +++ b/pm2d/web/server.py @@ -249,9 +249,9 @@ PRECISION_ANGLE_STEP = { # Un operatore sceglie il livello di rigore, non un numero astratto. FILTRO_FP_MAP = { "off": 0.0, # disabilitato: mantieni tutti i match shape-based - "leggero": 0.20, # tollera variazioni intensità/illuminazione forti - "medio": 0.35, # default bilanciato (consigliato) - "forte": 0.50, # scarta match con intensità molto diversa dal template + "leggero": 0.30, # tollera variazioni intensità/illuminazione forti + "medio": 0.50, # default bilanciato (consigliato) + "forte": 0.70, # scarta match con intensità molto diversa dal template }