Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c46a6ca9b | |||
| d335f866a3 | |||
| 88f80a2cad |
+9
-23
@@ -1042,11 +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)
|
||||||
# Precisione angolare: tol piu' stretto + piu' iterazioni golden.
|
# Precisione angolare: 10 iter golden con tol 0.05 deg.
|
||||||
# 16 iter golden coprono ~1e-3 della search_radius -> precisione
|
# Compromesso speed/accuracy: il parabolic fit aggiuntivo era
|
||||||
# ~0.005 deg per step=10 deg.
|
# instabile su score map non-smooth (template simmetrici producono
|
||||||
tol = 0.02
|
# multipli local max ravvicinati che lo facevano divergere).
|
||||||
for _ in range(16):
|
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]:
|
||||||
@@ -1063,22 +1064,6 @@ class LineShapeMatcher:
|
|||||||
x1 = x2; s1 = s2; cx1 = cx2; cy1 = cy2
|
x1 = x2; s1 = s2; cx1 = cx2; cy1 = cy2
|
||||||
x2 = a_lo + _GOLDEN * (a_hi - a_lo)
|
x2 = a_lo + _GOLDEN * (a_hi - a_lo)
|
||||||
s2, cx2, cy2 = _score_at_angle(x2)
|
s2, cx2, cy2 = _score_at_angle(x2)
|
||||||
# Parabolic fit finale sui 3 punti vicini al best per refinement
|
|
||||||
# sub-step ulteriore (precisione <0.01 deg quando lo score map e'
|
|
||||||
# smooth attorno al picco).
|
|
||||||
best_off = best[0] - angle_deg
|
|
||||||
delta = max(0.05, abs(a_hi - a_lo) * 0.5)
|
|
||||||
sm, _, _ = _score_at_angle(best_off - delta)
|
|
||||||
sp, _, _ = _score_at_angle(best_off + delta)
|
|
||||||
# Vertice parabola su 3 punti (sm, sb, sp) a (-delta, 0, +delta)
|
|
||||||
denom = sm - 2 * best[1] + sp
|
|
||||||
if abs(denom) > 1e-9:
|
|
||||||
shift = 0.5 * (sm - sp) / denom * delta
|
|
||||||
shift = max(-delta, min(delta, shift))
|
|
||||||
new_off = best_off + shift
|
|
||||||
sn, cxn, cyn = _score_at_angle(new_off)
|
|
||||||
if sn >= best[1]:
|
|
||||||
return (angle_deg + new_off, sn, cxn, cyn)
|
|
||||||
return best
|
return best
|
||||||
|
|
||||||
def _get_view_template(
|
def _get_view_template(
|
||||||
@@ -1320,8 +1305,9 @@ class LineShapeMatcher:
|
|||||||
if t is None:
|
if t is None:
|
||||||
return 1.0
|
return 1.0
|
||||||
h, w = t.shape
|
h, w = t.shape
|
||||||
cx_t = (w - 1) / 2.0
|
# Coerente con training (center = diag / 2.0, no -1)
|
||||||
cy_t = (h - 1) / 2.0
|
cx_t = w / 2.0
|
||||||
|
cy_t = h / 2.0
|
||||||
|
|
||||||
# Bounding box del template ruotato/scalato attorno a (cx, cy)
|
# Bounding box del template ruotato/scalato attorno a (cx, cy)
|
||||||
diag = int(np.ceil(np.hypot(w, h) * scale)) + 8
|
diag = int(np.ceil(np.hypot(w, h) * scale)) + 8
|
||||||
|
|||||||
+12
-6
@@ -168,7 +168,10 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
|
|||||||
if template_gray is not None and matcher is not None:
|
if template_gray is not None and matcher is not None:
|
||||||
t = template_gray
|
t = template_gray
|
||||||
th, tw = t.shape
|
th, tw = t.shape
|
||||||
cx_t = (tw - 1) / 2.0; cy_t = (th - 1) / 2.0
|
# Centro template coerente col training: in train si usa
|
||||||
|
# `center = (diag / 2.0, diag / 2.0)` (no -1). Usare (tw-1)/2
|
||||||
|
# introduceva uno shift di 0.5px per template di lato pari.
|
||||||
|
cx_t = tw / 2.0; cy_t = th / 2.0
|
||||||
M = cv2.getRotationMatrix2D((cx_t, cy_t), m.angle_deg, m.scale)
|
M = cv2.getRotationMatrix2D((cx_t, cy_t), m.angle_deg, m.scale)
|
||||||
M[0, 2] += m.cx - cx_t
|
M[0, 2] += m.cx - cx_t
|
||||||
M[1, 2] += m.cy - cy_t
|
M[1, 2] += m.cy - cy_t
|
||||||
@@ -181,10 +184,11 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
|
|||||||
warped_mask = cv2.warpAffine(
|
warped_mask = cv2.warpAffine(
|
||||||
mask_src, M, (W_scene, H_scene),
|
mask_src, M, (W_scene, H_scene),
|
||||||
flags=cv2.INTER_NEAREST, borderValue=0)
|
flags=cv2.INTER_NEAREST, borderValue=0)
|
||||||
# Erode di spread_radius per scartare la fascia di transizione
|
# Erode minimo (3x3) per togliere SOLO artefatti border-padding
|
||||||
# bordo che produce gradient spurio
|
# (~1px di bordo nero da warpAffine borderValue=0). Erode piu'
|
||||||
er_k = max(3, 2 * matcher.spread_radius + 1)
|
# grande spostava visualmente l'edge verso l'interno e creava
|
||||||
kernel_er = np.ones((er_k, er_k), np.uint8)
|
# apparente "traslazione fissa" rispetto al bordo del pezzo.
|
||||||
|
kernel_er = np.ones((3, 3), np.uint8)
|
||||||
warped_mask = cv2.erode(warped_mask, kernel_er)
|
warped_mask = cv2.erode(warped_mask, kernel_er)
|
||||||
mag, _ = matcher._gradient(warped_gray)
|
mag, _ = matcher._gradient(warped_gray)
|
||||||
if matcher.weak_grad < matcher.strong_grad:
|
if matcher.weak_grad < matcher.strong_grad:
|
||||||
@@ -194,7 +198,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)
|
||||||
|
|||||||
Reference in New Issue
Block a user