diff --git a/pm2d/web/server.py b/pm2d/web/server.py index 7cf3fcc..4a29468 100644 --- a/pm2d/web/server.py +++ b/pm2d/web/server.py @@ -217,6 +217,7 @@ class MatchResp(BaseModel): find_time: float num_variants: int annotated_id: str + diag: dict | None = None # CC: diagnostica pipeline (drop reasons) class TuneParams(BaseModel): @@ -521,6 +522,7 @@ def match(p: MatchParams): ) for m_ in matches], train_time=t_train, find_time=t_find, num_variants=n, annotated_id=ann_id, + diag=m.get_last_diag() if hasattr(m, "get_last_diag") else None, ) @@ -596,6 +598,7 @@ def match_simple(p: SimpleMatchParams): ) for mt in matches], train_time=t_train, find_time=t_find, num_variants=n, annotated_id=ann_id, + diag=m.get_last_diag() if hasattr(m, "get_last_diag") else None, ) @@ -769,6 +772,7 @@ def match_recipe(p: RecipeMatchParams): ) for mt in matches], train_time=0.0, find_time=t_find, num_variants=len(m.variants), annotated_id=ann_id, + diag=m.get_last_diag() if hasattr(m, "get_last_diag") else None, ) diff --git a/pm2d/web/static/app.js b/pm2d/web/static/app.js index b4bd931..49c76d9 100644 --- a/pm2d/web/static/app.js +++ b/pm2d/web/static/app.js @@ -336,6 +336,7 @@ async function doMatchRecipe() { document.getElementById("t-find").textContent = `${data.find_time.toFixed(2)}s`; document.getElementById("t-var").textContent = data.num_variants; document.getElementById("t-match").textContent = data.matches.length; + renderDiag(data.diag, data.matches.length); setStatus(`${data.matches.length} match trovati (ricetta ${state.active_recipe})`); } @@ -409,6 +410,7 @@ async function doMatch() { document.getElementById("t-find").textContent = `${data.find_time.toFixed(2)}s`; document.getElementById("t-var").textContent = data.num_variants; document.getElementById("t-match").textContent = data.matches.length; + renderDiag(data.diag, data.matches.length); setStatus(`${data.matches.length} match trovati${hasAdv ? " (avanzato)" : ""}`); } @@ -436,6 +438,61 @@ function setStatus(s) { } // ---------- Init ---------- +// ---------- CC: Diagnostica match ---------- +function renderDiag(diag, n_matches) { + const el = document.getElementById("diag-content"); + if (!diag) { + el.innerHTML = 'Diagnostica non disponibile'; + return; + } + const dropTotal = (diag.drop_ncc_low || 0) + (diag.drop_min_score_post_avg || 0) + + (diag.drop_recall_low || 0) + (diag.drop_bbox_out_of_scene || 0) + + (diag.drop_nms_iou || 0); + // Hint contestuali se 0 match + let hint = ""; + if (n_matches === 0) { + if (diag.n_after_pre_nms === 0) { + hint = `
⚠ Nessun candidato sopra soglia. + Prova: ↓ min_score o ↓ top_thresh (currently ${diag.top_thresh_used.toFixed(2)})
`; + } else if (diag.drop_ncc_low > 0 && dropTotal === diag.drop_ncc_low) { + hint = `
⚠ ${diag.drop_ncc_low} candidati droppati da NCC. + Prova: ↓ verify_threshold (filtro_fp più leggero)
`; + } else if (diag.drop_min_score_post_avg > 0) { + hint = `
⚠ ${diag.drop_min_score_post_avg} match sotto min_score post-NCC. + Prova: ↓ min_score
`; + } else if (diag.drop_recall_low > 0) { + hint = `
⚠ ${diag.drop_recall_low} match con recall < ${diag.min_recall_used}. + Prova: ↓ min_recall
`; + } else if (diag.drop_bbox_out_of_scene > 0) { + hint = `
⚠ ${diag.drop_bbox_out_of_scene} match con bbox fuori scena. + Centro derivato male: aumenta min_score o restringi search_roi
`; + } + } + const flags = []; + if (diag.use_polarity) flags.push("polarity"); + if (diag.use_soft_score) flags.push("soft"); + if (diag.subpixel_lm) flags.push("subpix-LM"); + el.innerHTML = ` +
Pipeline pruning:
+
varianti: ${diag.n_variants_total} → top_eval=${diag.n_variants_top_evaluated} + → top_pass=${diag.n_variants_top_passed} → full_eval=${diag.n_variants_full_evaluated}
+
Candidati: raw=${diag.n_raw_candidates} + → pre_nms=${diag.n_after_pre_nms} → final=${diag.n_final}
+
Drop reasons: NCC=${diag.drop_ncc_low}, score=${diag.drop_min_score_post_avg}, + recall=${diag.drop_recall_low}, bbox=${diag.drop_bbox_out_of_scene}, NMS=${diag.drop_nms_iou}
+
Soglie: top=${diag.top_thresh_used.toFixed(2)}, + min_score=${diag.min_score_used.toFixed(2)}, + NCC=${diag.verify_threshold_used.toFixed(2)}, + recall=${diag.min_recall_used.toFixed(2)}
+ ${flags.length ? `
Flag attivi: ${flags.join(", ")}
` : ""} + ${hint} + `; + // Auto-apri pannello se 0 match (segnala problema) + if (n_matches === 0) { + document.getElementById("diag-panel").open = true; + } +} + // ---------- Auto-tune (Halcon-style) ---------- async function doAutoTune() { if (!state.model || !state.roi) { diff --git a/pm2d/web/static/index.html b/pm2d/web/static/index.html index c73f00d..4d014ef 100644 --- a/pm2d/web/static/index.html +++ b/pm2d/web/static/index.html @@ -214,6 +214,16 @@
find:-
varianti:-
match:-
+ +
+ 🔍 Diagnostica (CC) +
+ Esegui un MATCH per vedere la diagnostica +
+