Compare commits

..

5 Commits

Author SHA1 Message Date
Adriano d335f866a3 merge: refine veloce + UCS Y visibile 2026-05-05 12:38:47 +02:00
Adriano 88f80a2cad fix: refine angolo piu' veloce + edge overlay ciano (no clash con asse Y)
Bug visibili dallo screenshot:
1. Rallentamento sostanziale: il fix precedente aggiungeva 16 iter golden
   (era 8) + 3 chiamate parabolic fit = ~19 _score_at_angle vs 11 prima.
2. Asse Y dell'UCS invisibile sul match: edge overlay era verde brillante
   (0,220,0) e si sovrapponeva esattamente al verde dell'asse Y dell'UCS.
3. Angolo non corretto: il parabolic fit finale era instabile su template
   simmetrici (multiple local max ravvicinati lo facevano divergere fuori
   dal vero picco trovato dal golden).

Fix:
- _refine_angle: 10 iter golden con tol 0.05 (compromesso tra precisione
  e velocita'). Rimosso parabolic fit finale instabile. search_radius
  resta a step pieno (utile per recuperare estremi del bin).
- Edge overlay color: ciano (BGR 255,200,0) invece di verde brillante.
  L'asse Y verde dell'UCS ora ben visibile sopra l'overlay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:38:47 +02:00
Adriano d52d0d0489 merge: precisione rotazione + default Nessuna 2026-05-05 12:32:17 +02:00
Adriano 9451a418a6 fix: precisione rotazione +UI simmetria default Nessuna
Precisione rotazione:
- _refine_angle: tol 0.1 -> 0.02 deg, 8 -> 16 iter golden-section
- search_radius default = step pieno (era step/2): copre il caso peggiore
  in cui il picco vero e' all'estremo del bin angolare grezzo
- Aggiunto parabolic fit finale sui 3 punti vicini al best (precisione
  <0.01 deg quando lo score map e' smooth attorno al picco)

Default UI:
- Simmetria "Nessuna" come default (era "Invariante" che limitava
  matching a una singola pose - confondente per l'operatore tipico).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:32:17 +02:00
Adriano 2c9160e4be merge: perf profile/bench/prune 2026-05-05 12:25:15 +02:00
3 changed files with 15 additions and 6 deletions
+11 -4
View File
@@ -954,7 +954,7 @@ class LineShapeMatcher:
# variante e' quantizzato a multipli di angle_step (5 deg default). # variante e' quantizzato a multipli di angle_step (5 deg default).
# Refine angolare e' essenziale per orientamento sub-step. # Refine angolare e' essenziale per orientamento sub-step.
if search_radius is None: if search_radius is None:
search_radius = self._effective_angle_step() / 2.0 search_radius = self._effective_angle_step()
h, w = template_gray.shape h, w = template_gray.shape
sw = max(16, int(round(w * scale))) sw = max(16, int(round(w * scale)))
@@ -1042,8 +1042,12 @@ class LineShapeMatcher:
# Score all'origine come riferimento (ang offset 0) # Score all'origine come riferimento (ang offset 0)
s0, cx0_s, cy0_s = _score_at_angle(0.0) s0, cx0_s, cy0_s = _score_at_angle(0.0)
best = (angle_deg, s0, cx0_s, cy0_s) best = (angle_deg, s0, cx0_s, cy0_s)
tol = 0.1 # gradi # Precisione angolare: 10 iter golden con tol 0.05 deg.
for _ in range(8): # Compromesso speed/accuracy: il parabolic fit aggiuntivo era
# instabile su score map non-smooth (template simmetrici producono
# multipli local max ravvicinati che lo facevano divergere).
tol = 0.05
for _ in range(10):
if s1 > best[1]: if s1 > best[1]:
best = (angle_deg + x1, s1, cx1, cy1) best = (angle_deg + x1, s1, cx1, cy1)
if s2 > best[1]: if s2 > best[1]:
@@ -1825,7 +1829,10 @@ class LineShapeMatcher:
ang_f, score_f, cx_f, cy_f = self._refine_angle( ang_f, score_f, cx_f, cy_f = self._refine_angle(
spread0, bit_active_full, self.template_gray, cx_f, cy_f, spread0, bit_active_full, self.template_gray, cx_f, cy_f,
var.angle_deg, var.scale, mask_full, var.angle_deg, var.scale, mask_full,
search_radius=self._effective_angle_step() / 2.0, # Search radius esteso allo step pieno (era step/2):
# copre il caso peggiore in cui il picco vero e' all'estremo
# del bin angolare della variante grezza.
search_radius=self._effective_angle_step(),
original_score=score, original_score=score,
) )
# Halcon SubPixel='least_squares_high': refinement iterativo # Halcon SubPixel='least_squares_high': refinement iterativo
+3 -1
View File
@@ -194,7 +194,9 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
edge_mask = edge_mask & (warped_mask > 0) edge_mask = edge_mask & (warped_mask > 0)
if edge_mask.any(): if edge_mask.any():
edge_overlay = np.zeros_like(out) edge_overlay = np.zeros_like(out)
edge_overlay[edge_mask] = (0, 220, 0) # verde brillante # Ciano (cambiato da verde): non collide col verde dell'asse
# Y dell'UCS che altrimenti scompariva nell'overlay edge.
edge_overlay[edge_mask] = (255, 200, 0) # ciano (BGR)
out = cv2.addWeighted(out, 1.0, edge_overlay, 0.6, 0) out = cv2.addWeighted(out, 1.0, edge_overlay, 0.6, 0)
L = max(20, int(L_base * m.scale)) L = max(20, int(L_base * m.scale))
# X axis = rotazione di (1, 0) con cv2 matrix → (cos, -sin) # X axis = rotazione di (1, 0) con cv2 matrix → (cos, -sin)
+1 -1
View File
@@ -102,8 +102,8 @@
<div class="field"> <div class="field">
<label>Simmetria</label> <label>Simmetria</label>
<select id="p-simmetria"> <select id="p-simmetria">
<option value="nessuna" selected>Nessuna (0..360°)</option>
<option value="invariante">Invariante (cerchi — no rotazione)</option> <option value="invariante">Invariante (cerchi — no rotazione)</option>
<option value="nessuna">Nessuna (0..360°)</option>
<option value="bilaterale">Bilaterale (speculare 180°)</option> <option value="bilaterale">Bilaterale (speculare 180°)</option>
<option value="rot_3">Rotazionale 3× (120°)</option> <option value="rot_3">Rotazionale 3× (120°)</option>
<option value="rot_4">Rotazionale 4× (90°)</option> <option value="rot_4">Rotazionale 4× (90°)</option>