Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8029a1e12b | |||
| d37833076e | |||
| e1ed9206a3 | |||
| e84ae199ac | |||
| 5f0c4542d3 | |||
| 29c034fb05 | |||
| 6fb1efcab8 |
+79
-114
@@ -78,6 +78,7 @@ def _matcher_cache_key(roi: np.ndarray, tech: dict) -> str:
|
|||||||
h.update(roi.tobytes())
|
h.update(roi.tobytes())
|
||||||
# Solo parametri che influenzano il training
|
# Solo parametri che influenzano il training
|
||||||
relevant = ("num_features", "weak_grad", "strong_grad",
|
relevant = ("num_features", "weak_grad", "strong_grad",
|
||||||
|
"min_feature_spacing",
|
||||||
"angle_min", "angle_max", "angle_step",
|
"angle_min", "angle_max", "angle_step",
|
||||||
"scale_min", "scale_max", "scale_step",
|
"scale_min", "scale_max", "scale_step",
|
||||||
"spread_radius", "pyramid_levels")
|
"spread_radius", "pyramid_levels")
|
||||||
@@ -133,33 +134,34 @@ def _encode_png(img: np.ndarray) -> bytes:
|
|||||||
def _draw_matches(scene: np.ndarray, matches: list[Match],
|
def _draw_matches(scene: np.ndarray, matches: list[Match],
|
||||||
template_gray: np.ndarray | None,
|
template_gray: np.ndarray | None,
|
||||||
matcher: "LineShapeMatcher | None" = None) -> np.ndarray:
|
matcher: "LineShapeMatcher | None" = None) -> np.ndarray:
|
||||||
"""Disegna match annotati sulla scena.
|
"""Disegna SOLO UCS (richiesta utente) per ogni match trovato.
|
||||||
|
|
||||||
Se matcher e' passato, usa la stessa pipeline di edge filtering
|
UCS = sistema di coordinate (X rosso, Y verde) posizionato sul
|
||||||
(hysteresis weak/strong_grad) e selezione feature usata in training,
|
baricentro feature del modello, ruotato secondo l'angolo del match.
|
||||||
cosi' l'overlay nel match riflette ESATTAMENTE quello che l'utente
|
Niente edge, niente cerchietti feature, niente bbox: i match sulla
|
||||||
ha visto nel preview "Anteprima edge". Inoltre disegna UCS
|
scena reale devono essere puliti, gli edge filtrati si vedono solo
|
||||||
(asse X rosso, Y verde) sul centro pose del match.
|
nell'anteprima modello.
|
||||||
|
|
||||||
Senza matcher: fallback Canny (legacy).
|
|
||||||
"""
|
"""
|
||||||
out = scene.copy()
|
out = scene.copy()
|
||||||
H, W = scene.shape[:2]
|
# Lunghezza assi UCS: stessa formula dell'anteprima modello
|
||||||
palette = [
|
# (0.15 * max lato template) scalata per m.scale → coerenza dimensionale.
|
||||||
(0, 255, 0), (0, 200, 255), (255, 100, 100), (255, 200, 0),
|
if matcher is not None and matcher.template_size != (0, 0):
|
||||||
(200, 0, 255), (100, 255, 200), (255, 0, 0), (0, 255, 255),
|
L_base = int(0.15 * max(matcher.template_size))
|
||||||
]
|
else:
|
||||||
bin_colors = [
|
L_base = 30
|
||||||
(255, 0, 0), (255, 128, 0), (255, 255, 0), (0, 255, 0),
|
H_scene, W_scene = scene.shape[:2]
|
||||||
(0, 255, 255), (0, 128, 255), (0, 0, 255), (255, 0, 255),
|
|
||||||
(255, 100, 100), (255, 180, 100), (255, 230, 100), (180, 255, 100),
|
|
||||||
(100, 255, 200), (100, 180, 255), (180, 100, 255), (255, 100, 200),
|
|
||||||
]
|
|
||||||
for i, m in enumerate(matches):
|
for i, m in enumerate(matches):
|
||||||
color = palette[i % len(palette)]
|
# UCS posizionato esattamente sul CENTRO POSE del match (m.cx, m.cy):
|
||||||
# Posizione UCS: baricentro feature warpate (default = cx, cy se non disponibile).
|
# equivale al centro template traslato alla scena, ruotato con
|
||||||
# Mantiene coerenza con anteprima modello che mostra UCS sul baricentro.
|
# m.angle_deg. Coerente con UCS dell'anteprima modello che ora
|
||||||
ucs_x, ucs_y = float(m.cx), float(m.cy)
|
# e' anche sul centro ROI (vedi preview_edges).
|
||||||
|
ax = np.deg2rad(m.angle_deg)
|
||||||
|
ca, sa = np.cos(ax), np.sin(ax)
|
||||||
|
cx, cy = int(round(m.cx)), int(round(m.cy))
|
||||||
|
# Overlay edge del modello orientato (richiesta utente):
|
||||||
|
# warpa template alla pose, applica hysteresis identica al matcher,
|
||||||
|
# disegna pixel edge come overlay verde tenue.
|
||||||
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
|
||||||
@@ -167,92 +169,28 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
|
|||||||
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
|
||||||
# Background edge filtrati: warpa template + hysteresis
|
|
||||||
warped_gray = cv2.warpAffine(
|
warped_gray = cv2.warpAffine(
|
||||||
t, M, (W, H), flags=cv2.INTER_LINEAR, borderValue=0)
|
t, M, (W_scene, H_scene),
|
||||||
|
flags=cv2.INTER_LINEAR, borderValue=0)
|
||||||
mag, _ = matcher._gradient(warped_gray)
|
mag, _ = matcher._gradient(warped_gray)
|
||||||
if matcher.weak_grad < matcher.strong_grad:
|
if matcher.weak_grad < matcher.strong_grad:
|
||||||
edge_mask = matcher._hysteresis_mask(mag)
|
edge_mask = matcher._hysteresis_mask(mag)
|
||||||
else:
|
else:
|
||||||
edge_mask = mag >= matcher.strong_grad
|
edge_mask = mag >= matcher.strong_grad
|
||||||
if edge_mask.any():
|
if edge_mask.any():
|
||||||
bg_overlay = np.zeros_like(out)
|
edge_overlay = np.zeros_like(out)
|
||||||
dark = tuple(int(c * 0.35) for c in color)
|
edge_overlay[edge_mask] = (0, 220, 0) # verde brillante
|
||||||
bg_overlay[edge_mask] = dark
|
out = cv2.addWeighted(out, 1.0, edge_overlay, 0.6, 0)
|
||||||
out = cv2.addWeighted(out, 1.0, bg_overlay, 0.7, 0)
|
L = max(20, int(L_base * m.scale))
|
||||||
# Feature reali del matcher: usa quelle pre-computate della
|
# X axis = rotazione di (1, 0) con cv2 matrix → (cos, -sin)
|
||||||
# variante che ha generato il match. Stesse identiche feature
|
x_end = (int(cx + L * ca), int(cy - L * sa))
|
||||||
# mostrate nell'anteprima modello (ruotate/scalate alla pose).
|
# Y axis = rotazione di (0, 1) con cv2 matrix → (sin, cos)
|
||||||
vi = getattr(m, "variant_idx", -1)
|
# A m.angle_deg=0 deve puntare GIU' (image y-down convenzione modello)
|
||||||
fx_scene = fy_scene = fb_arr = None
|
y_end = (int(cx + L * sa), int(cy + L * ca))
|
||||||
if 0 <= vi < len(matcher.variants):
|
|
||||||
lvl0 = matcher.variants[vi].levels[0]
|
|
||||||
# dx/dy sono offsets relativi al CENTRO del template warpato
|
|
||||||
# nelle coordinate del kernel template (gia' pre-ruotate
|
|
||||||
# all'angolo della variante grezza). Per coerenza con la
|
|
||||||
# pose finale m.angle_deg (post-refine), ri-rotazione del
|
|
||||||
# delta angolare (m.angle_deg - var.angle_deg).
|
|
||||||
var = matcher.variants[vi]
|
|
||||||
dang = np.deg2rad(m.angle_deg - var.angle_deg)
|
|
||||||
ca, sa = np.cos(dang), np.sin(dang)
|
|
||||||
dxr = lvl0.dx * ca + lvl0.dy * sa
|
|
||||||
dyr = -lvl0.dx * sa + lvl0.dy * ca
|
|
||||||
fx_scene = m.cx + dxr
|
|
||||||
fy_scene = m.cy + dyr
|
|
||||||
fb_arr = lvl0.bin
|
|
||||||
else:
|
|
||||||
# Fallback: estrai feature dal warpato (perde precisione)
|
|
||||||
_, bins_w = matcher._gradient(warped_gray)
|
|
||||||
fx, fy, fb = matcher._extract_features(mag, bins_w, None)
|
|
||||||
fx_scene = fx.astype(np.float32)
|
|
||||||
fy_scene = fy.astype(np.float32)
|
|
||||||
fb_arr = fb
|
|
||||||
# Disegna feature
|
|
||||||
for k in range(len(fx_scene)):
|
|
||||||
px = int(round(float(fx_scene[k])))
|
|
||||||
py = int(round(float(fy_scene[k])))
|
|
||||||
if 0 <= px < W and 0 <= py < H:
|
|
||||||
bcol = bin_colors[int(fb_arr[k]) % len(bin_colors)]
|
|
||||||
cv2.circle(out, (px, py), 2, bcol, -1, cv2.LINE_AA)
|
|
||||||
# UCS sul baricentro feature (in scene coords)
|
|
||||||
if len(fx_scene) > 0:
|
|
||||||
ucs_x = float(np.mean(fx_scene))
|
|
||||||
ucs_y = float(np.mean(fy_scene))
|
|
||||||
elif template_gray is not None:
|
|
||||||
# Senza matcher: legacy Canny
|
|
||||||
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
|
|
||||||
edge = cv2.Canny(t, 50, 150)
|
|
||||||
warped = cv2.warpAffine(edge, M, (W, H),
|
|
||||||
flags=cv2.INTER_NEAREST, borderValue=0)
|
|
||||||
mask = warped > 0
|
|
||||||
if mask.any():
|
|
||||||
overlay = np.zeros_like(out)
|
|
||||||
overlay[mask] = color
|
|
||||||
out[mask] = (0.3 * out[mask] + 0.7 * overlay[mask]).astype(np.uint8)
|
|
||||||
# bbox poly e linea-marker rimossi (richiesta utente "togli la ROI"):
|
|
||||||
# UCS + edge filtrati gia' identificano pose e orientamento.
|
|
||||||
cx, cy = int(round(ucs_x)), int(round(ucs_y))
|
|
||||||
# UCS sul centro pose match (richiesta utente: come nell'anteprima
|
|
||||||
# modello). Asse X rosso destra, Y verde basso (image y-down).
|
|
||||||
# Lunghezza derivata dalla diagonale bbox per scala-invariante.
|
|
||||||
L = int(np.linalg.norm(m.bbox_poly[1] - m.bbox_poly[0])) // 2
|
|
||||||
if L < 10:
|
|
||||||
L = 30 # fallback se bbox degenere
|
|
||||||
ax = np.deg2rad(m.angle_deg)
|
|
||||||
# X axis ruotato (rosso)
|
|
||||||
x_end = (int(cx + L * np.cos(ax)), int(cy - L * np.sin(ax)))
|
|
||||||
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 perpendicolare (verde, +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),
|
||||||
@@ -260,9 +198,6 @@ def _draw_matches(scene: np.ndarray, matches: list[Match],
|
|||||||
# Origine UCS: cerchio bianco con bordo nero
|
# Origine UCS: cerchio bianco con bordo nero
|
||||||
cv2.circle(out, (cx, cy), 4, (0, 0, 0), -1, cv2.LINE_AA)
|
cv2.circle(out, (cx, cy), 4, (0, 0, 0), -1, cv2.LINE_AA)
|
||||||
cv2.circle(out, (cx, cy), 3, (255, 255, 255), -1, cv2.LINE_AA)
|
cv2.circle(out, (cx, cy), 3, (255, 255, 255), -1, cv2.LINE_AA)
|
||||||
label = f"#{i+1} {m.angle_deg:.0f}d s={m.scale:.2f} {m.score:.2f}"
|
|
||||||
cv2.putText(out, label, (cx + 12, cy - 12),
|
|
||||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2, cv2.LINE_AA)
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@@ -365,6 +300,15 @@ class SimpleMatchParams(BaseModel):
|
|||||||
penalita_scala: float = 0.0 # 0 = score shape invariante, >0 = penalizza scala != 1
|
penalita_scala: float = 0.0 # 0 = score shape invariante, >0 = penalizza scala != 1
|
||||||
min_score: float = 0.65
|
min_score: float = 0.65
|
||||||
max_matches: int = 25
|
max_matches: int = 25
|
||||||
|
# --- Override edge da pannello "Anteprima edge" (None = auto_tune) ---
|
||||||
|
# Quando settati, sovrascrivono i valori derivati da auto_tune e
|
||||||
|
# vengono usati identici sia nel training del matcher sia nel find.
|
||||||
|
# Salvati nella ricetta cosi' la stessa pulizia rumore e' replicata
|
||||||
|
# quando la ricetta viene caricata.
|
||||||
|
edge_weak_grad: float | None = None
|
||||||
|
edge_strong_grad: float | None = None
|
||||||
|
edge_num_features: int | None = None
|
||||||
|
edge_min_feature_spacing: int | None = None
|
||||||
# --- Halcon-mode flags (default off = backward compat) ---
|
# --- Halcon-mode flags (default off = backward compat) ---
|
||||||
# Init-time (richiede ri-train se cambiato)
|
# Init-time (richiede ri-train se cambiato)
|
||||||
use_polarity: bool = False # F: 16 bin orientation mod 2pi
|
use_polarity: bool = False # F: 16 bin orientation mod 2pi
|
||||||
@@ -413,10 +357,24 @@ def _simple_to_technical(
|
|||||||
smin, smax, sstep = SCALE_PRESETS.get(p.scala, (1.0, 1.0, 0.1))
|
smin, smax, sstep = SCALE_PRESETS.get(p.scala, (1.0, 1.0, 0.1))
|
||||||
ang_step = PRECISION_ANGLE_STEP.get(p.precisione, 5.0)
|
ang_step = PRECISION_ANGLE_STEP.get(p.precisione, 5.0)
|
||||||
|
|
||||||
|
# Override edge dal pannello "Anteprima edge" se utente li ha settati.
|
||||||
|
# Questi sostituiscono i valori auto_tune nel training del matcher,
|
||||||
|
# garantendo che la selezione edge identica a quella del preview
|
||||||
|
# venga usata sia in training sia in find.
|
||||||
|
weak_g = (p.edge_weak_grad if p.edge_weak_grad is not None
|
||||||
|
else tune["weak_grad"])
|
||||||
|
strong_g = (p.edge_strong_grad if p.edge_strong_grad is not None
|
||||||
|
else tune["strong_grad"])
|
||||||
|
n_feat = (p.edge_num_features if p.edge_num_features is not None
|
||||||
|
else nf)
|
||||||
|
min_sp = (p.edge_min_feature_spacing if p.edge_min_feature_spacing is not None
|
||||||
|
else 3)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"num_features": nf,
|
"num_features": n_feat,
|
||||||
"weak_grad": tune["weak_grad"],
|
"weak_grad": weak_g,
|
||||||
"strong_grad": tune["strong_grad"],
|
"strong_grad": strong_g,
|
||||||
|
"min_feature_spacing": min_sp,
|
||||||
"spread_radius": spread,
|
"spread_radius": spread,
|
||||||
"pyramid_levels": pyr,
|
"pyramid_levels": pyr,
|
||||||
"angle_min": 0.0,
|
"angle_min": 0.0,
|
||||||
@@ -652,6 +610,7 @@ def match_simple(p: SimpleMatchParams):
|
|||||||
scale_range=(tech["scale_min"], tech["scale_max"]),
|
scale_range=(tech["scale_min"], tech["scale_max"]),
|
||||||
scale_step=tech["scale_step"],
|
scale_step=tech["scale_step"],
|
||||||
spread_radius=tech["spread_radius"],
|
spread_radius=tech["spread_radius"],
|
||||||
|
min_feature_spacing=tech.get("min_feature_spacing", 3),
|
||||||
pyramid_levels=tech["pyramid_levels"],
|
pyramid_levels=tech["pyramid_levels"],
|
||||||
use_polarity=p.use_polarity,
|
use_polarity=p.use_polarity,
|
||||||
use_gpu=p.use_gpu,
|
use_gpu=p.use_gpu,
|
||||||
@@ -721,6 +680,11 @@ class SaveRecipeParams(BaseModel):
|
|||||||
precisione: str = "normale"
|
precisione: str = "normale"
|
||||||
use_polarity: bool = False
|
use_polarity: bool = False
|
||||||
use_gpu: bool = False
|
use_gpu: bool = False
|
||||||
|
# Override edge dal pannello "Anteprima edge" (None = auto_tune)
|
||||||
|
edge_weak_grad: float | None = None
|
||||||
|
edge_strong_grad: float | None = None
|
||||||
|
edge_num_features: int | None = None
|
||||||
|
edge_min_feature_spacing: int | None = None
|
||||||
name: str # nome file ricetta (no path)
|
name: str # nome file ricetta (no path)
|
||||||
|
|
||||||
|
|
||||||
@@ -787,26 +751,23 @@ def preview_edges(p: EdgePreviewParams):
|
|||||||
b = int(fb[i])
|
b = int(fb[i])
|
||||||
col = bin_colors[b % len(bin_colors)]
|
col = bin_colors[b % len(bin_colors)]
|
||||||
cv2.circle(out, (int(fx[i]), int(fy[i])), 2, col, -1, cv2.LINE_AA)
|
cv2.circle(out, (int(fx[i]), int(fy[i])), 2, col, -1, cv2.LINE_AA)
|
||||||
# UCS sul baricentro feature (richiesta utente): assi X rosso, Y verde
|
# UCS sul CENTRO ROI (coerente con _draw_matches che usa centro pose).
|
||||||
bary_cx = bary_cy = None
|
# In questo modo l'UCS visualizzato nel modello = UCS del match (modulo
|
||||||
if len(fx) > 0:
|
# rotazione/traslazione data dalla pose del pezzo trovato).
|
||||||
bary_cx = float(np.mean(fx))
|
rh, rw = roi_img.shape[:2]
|
||||||
bary_cy = float(np.mean(fy))
|
bx, by = (rw - 1) // 2, (rh - 1) // 2
|
||||||
bx, by = int(round(bary_cx)), int(round(bary_cy))
|
axis_len = max(20, int(0.15 * max(rw, rh)))
|
||||||
axis_len = max(20, int(0.15 * max(out.shape[:2])))
|
|
||||||
# X axis (rosso, verso destra)
|
|
||||||
cv2.arrowedLine(out, (bx, by), (bx + axis_len, by),
|
cv2.arrowedLine(out, (bx, by), (bx + axis_len, by),
|
||||||
(0, 0, 255), 2, cv2.LINE_AA, tipLength=0.2)
|
(0, 0, 255), 2, cv2.LINE_AA, tipLength=0.2)
|
||||||
cv2.putText(out, "X", (bx + axis_len + 4, by + 5),
|
cv2.putText(out, "X", (bx + axis_len + 4, by + 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, verso il basso = convenzione image y-down)
|
|
||||||
cv2.arrowedLine(out, (bx, by), (bx, by + axis_len),
|
cv2.arrowedLine(out, (bx, by), (bx, by + axis_len),
|
||||||
(0, 255, 0), 2, cv2.LINE_AA, tipLength=0.2)
|
(0, 255, 0), 2, cv2.LINE_AA, tipLength=0.2)
|
||||||
cv2.putText(out, "Y", (bx + 4, by + axis_len + 12),
|
cv2.putText(out, "Y", (bx + 4, by + axis_len + 12),
|
||||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
|
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
|
||||||
# Origine: cerchio bianco con bordo nero
|
|
||||||
cv2.circle(out, (bx, by), 4, (0, 0, 0), -1, cv2.LINE_AA)
|
cv2.circle(out, (bx, by), 4, (0, 0, 0), -1, cv2.LINE_AA)
|
||||||
cv2.circle(out, (bx, by), 3, (255, 255, 255), -1, cv2.LINE_AA)
|
cv2.circle(out, (bx, by), 3, (255, 255, 255), -1, cv2.LINE_AA)
|
||||||
|
bary_cx, bary_cy = float(bx), float(by)
|
||||||
img_id = _store_image(out)
|
img_id = _store_image(out)
|
||||||
n_edge_strong = int((mag >= m.strong_grad).sum())
|
n_edge_strong = int((mag >= m.strong_grad).sum())
|
||||||
n_edge_total = int(edge_mask.sum() / 255)
|
n_edge_total = int(edge_mask.sum() / 255)
|
||||||
@@ -838,6 +799,10 @@ def save_recipe(p: SaveRecipeParams):
|
|||||||
tipo=p.tipo, simmetria=p.simmetria, scala=p.scala,
|
tipo=p.tipo, simmetria=p.simmetria, scala=p.scala,
|
||||||
precisione=p.precisione,
|
precisione=p.precisione,
|
||||||
use_polarity=p.use_polarity, use_gpu=p.use_gpu,
|
use_polarity=p.use_polarity, use_gpu=p.use_gpu,
|
||||||
|
edge_weak_grad=p.edge_weak_grad,
|
||||||
|
edge_strong_grad=p.edge_strong_grad,
|
||||||
|
edge_num_features=p.edge_num_features,
|
||||||
|
edge_min_feature_spacing=p.edge_min_feature_spacing,
|
||||||
)
|
)
|
||||||
tech = _simple_to_technical(sp, roi_img)
|
tech = _simple_to_technical(sp, roi_img)
|
||||||
m = LineShapeMatcher(
|
m = LineShapeMatcher(
|
||||||
|
|||||||
@@ -53,10 +53,34 @@ function readUserParams() {
|
|||||||
document.getElementById("p-penalita-scala").value),
|
document.getElementById("p-penalita-scala").value),
|
||||||
min_score: parseFloat(document.getElementById("p-min-score").value),
|
min_score: parseFloat(document.getElementById("p-min-score").value),
|
||||||
max_matches: parseInt(document.getElementById("p-max-matches").value, 10),
|
max_matches: parseInt(document.getElementById("p-max-matches").value, 10),
|
||||||
|
...readEdgeOverrides(),
|
||||||
...readHalconFlags(),
|
...readHalconFlags(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readEdgeOverrides() {
|
||||||
|
// Override edge dal pannello "Anteprima edge". Settati = utente li ha
|
||||||
|
// toccati (anche se uguali al default attuale). Vengono propagati a
|
||||||
|
// _simple_to_technical e usati identici sia in training sia in find.
|
||||||
|
// Inoltre salvati nella ricetta cosi' si replicano al load.
|
||||||
|
const _v = (id, parser) => {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (!el) return null;
|
||||||
|
const v = parser(el.value);
|
||||||
|
return Number.isFinite(v) ? v : null;
|
||||||
|
};
|
||||||
|
// Sempre passa i valori correnti degli slider: e' la richiesta utente
|
||||||
|
// che i param di pulizia rumore vengano usati anche nel find/ricetta.
|
||||||
|
const polCb = document.getElementById("hc-use-polarity");
|
||||||
|
return {
|
||||||
|
edge_weak_grad: _v("ep-weak", parseFloat),
|
||||||
|
edge_strong_grad: _v("ep-strong", parseFloat),
|
||||||
|
edge_num_features: _v("ep-nf", parseInt),
|
||||||
|
edge_min_feature_spacing: _v("ep-sp", parseInt),
|
||||||
|
use_polarity: polCb?.checked || document.getElementById("ep-pol")?.checked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function readHalconFlags() {
|
function readHalconFlags() {
|
||||||
// Halcon-mode toggle: tutti i flag default-off, esposti via "Modalità Halcon"
|
// Halcon-mode toggle: tutti i flag default-off, esposti via "Modalità Halcon"
|
||||||
const $cb = (id) => document.getElementById(id)?.checked ?? false;
|
const $cb = (id) => document.getElementById(id)?.checked ?? false;
|
||||||
@@ -716,6 +740,10 @@ async function saveRecipe() {
|
|||||||
precisione: user.precisione,
|
precisione: user.precisione,
|
||||||
use_polarity: user.use_polarity,
|
use_polarity: user.use_polarity,
|
||||||
use_gpu: user.use_gpu,
|
use_gpu: user.use_gpu,
|
||||||
|
edge_weak_grad: user.edge_weak_grad,
|
||||||
|
edge_strong_grad: user.edge_strong_grad,
|
||||||
|
edge_num_features: user.edge_num_features,
|
||||||
|
edge_min_feature_spacing: user.edge_min_feature_spacing,
|
||||||
name: name,
|
name: name,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user