merge: fix UCS match + edge modello overlay

This commit is contained in:
2026-05-05 11:58:21 +02:00
+38 -12
View File
@@ -154,29 +154,55 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
bary_dx = float(np.mean(v0.levels[0].dx)) bary_dx = float(np.mean(v0.levels[0].dx))
bary_dy = float(np.mean(v0.levels[0].dy)) bary_dy = float(np.mean(v0.levels[0].dy))
# Lunghezza assi UCS: stessa formula dell'anteprima modello
# (0.15 * max lato template) scalata per m.scale → coerenza dimensionale.
if matcher is not None and matcher.template_size != (0, 0):
L_base = int(0.15 * max(matcher.template_size))
else:
L_base = 30
H_scene, W_scene = scene.shape[:2]
for i, m in enumerate(matches): for i, m in enumerate(matches):
# Proietta baricentro template alla pose del match # Proietta baricentro template alla pose del match.
# (delta-rotation rispetto alla variante a 0) # cv2.getRotationMatrix2D con angle positivo applica:
# new_x = cos*x + sin*y new_y = -sin*x + cos*y
# Visivamente in image y-down e' rotazione anti-clockwise.
ax = np.deg2rad(m.angle_deg) ax = np.deg2rad(m.angle_deg)
ca, sa = np.cos(ax), np.sin(ax) ca, sa = np.cos(ax), np.sin(ax)
bx_scene = m.cx + (bary_dx * ca + bary_dy * sa) * m.scale bx_scene = m.cx + (bary_dx * ca + bary_dy * sa) * m.scale
by_scene = m.cy + (-bary_dx * sa + bary_dy * ca) * m.scale by_scene = m.cy + (-bary_dx * sa + bary_dy * ca) * m.scale
cx, cy = int(round(bx_scene)), int(round(by_scene)) cx, cy = int(round(bx_scene)), int(round(by_scene))
# Lunghezza assi: 30% del lato bbox per essere visibile e scalato # Overlay edge del modello orientato (richiesta utente):
if m.bbox_poly is not None and len(m.bbox_poly) >= 2: # warpa template alla pose, applica hysteresis identica al matcher,
L = int(np.linalg.norm(m.bbox_poly[1] - m.bbox_poly[0]) * 0.4) # disegna pixel edge come overlay verde tenue.
if template_gray is not None and matcher is not None:
t = template_gray
th, tw = t.shape
cx_t = (tw - 1) / 2.0; cy_t = (th - 1) / 2.0
M = cv2.getRotationMatrix2D((cx_t, cy_t), m.angle_deg, m.scale)
M[0, 2] += m.cx - cx_t
M[1, 2] += m.cy - cy_t
warped_gray = cv2.warpAffine(
t, M, (W_scene, H_scene),
flags=cv2.INTER_LINEAR, borderValue=0)
mag, _ = matcher._gradient(warped_gray)
if matcher.weak_grad < matcher.strong_grad:
edge_mask = matcher._hysteresis_mask(mag)
else: else:
L = 40 edge_mask = mag >= matcher.strong_grad
L = max(20, L) if edge_mask.any():
# X axis (rosso) ruotato secondo angle del match edge_overlay = np.zeros_like(out)
x_end = (int(cx + L * np.cos(ax)), int(cy - L * np.sin(ax))) edge_overlay[edge_mask] = (0, 220, 0) # verde brillante
out = cv2.addWeighted(out, 1.0, edge_overlay, 0.6, 0)
L = max(20, int(L_base * m.scale))
# X axis = rotazione di (1, 0) con cv2 matrix → (cos, -sin)
x_end = (int(cx + L * ca), int(cy - L * sa))
# Y axis = rotazione di (0, 1) con cv2 matrix → (sin, cos)
# A m.angle_deg=0 deve puntare GIU' (image y-down convenzione modello)
y_end = (int(cx + L * sa), int(cy + L * ca))
cv2.arrowedLine(out, (cx, cy), x_end, cv2.arrowedLine(out, (cx, cy), x_end,
(0, 0, 255), 2, cv2.LINE_AA, tipLength=0.2) (0, 0, 255), 2, cv2.LINE_AA, tipLength=0.2)
cv2.putText(out, "X", (x_end[0] + 4, x_end[1] + 5), cv2.putText(out, "X", (x_end[0] + 4, x_end[1] + 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA) cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
# Y axis (verde) perpendicolare; +90° in image coords = giu' visivo
y_end = (int(cx + L * np.cos(ax + np.pi / 2)),
int(cy - L * np.sin(ax + np.pi / 2)))
cv2.arrowedLine(out, (cx, cy), y_end, cv2.arrowedLine(out, (cx, cy), y_end,
(0, 255, 0), 2, cv2.LINE_AA, tipLength=0.2) (0, 255, 0), 2, cv2.LINE_AA, tipLength=0.2)
cv2.putText(out, "Y", (y_end[0] + 4, y_end[1] + 12), cv2.putText(out, "Y", (y_end[0] + 4, y_end[1] + 12),