From 2fe7a7a7fabec77b936103724369cddba2afe255 Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Wed, 13 May 2026 11:40:24 +0200 Subject: [PATCH] feat(find_model gui): toggle to overlay full SAM21 cluster (yellow) Adds a 4th checkbox 'Punti SAM21 (gialli)' that draws the full SAM21 segmentation (input to both the plane partition and Open3DModel) on the RGB result canvas. The backend now returns sam21_points_px and n_sam21 alongside the existing accepted/excluded splits. Useful as a sanity check: visualise what was actually segmented vs what the plane filter kept/rejected vs where the STL got placed. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/presa_mano/api/grasp_routes.py | 7 +++++++ web/app.js | 11 +++++++---- web/index.html | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/presa_mano/api/grasp_routes.py b/src/presa_mano/api/grasp_routes.py index ab9e1d8..ad2085f 100644 --- a/src/presa_mano/api/grasp_routes.py +++ b/src/presa_mano/api/grasp_routes.py @@ -306,6 +306,11 @@ def find_model(req: FindModelReq) -> dict[str, Any]: with sw.step("project_points"): accepted_px = project_camera_to_pixels(accepted_m, K, (W, H)) excluded_px = project_camera_to_pixels(excluded_m, K, (W, H)) + # Full SAM21 cluster (= input to the partition + Open3DModel search). + # Useful as a sanity-check overlay: shows everything segmented before + # any plane filtering. Equal to accepted ∪ excluded set-wise but kept + # as a separate field so the GUI can color it independently. + sam21_px = project_camera_to_pixels(seg.points_cv_m, K, (W, H)) with sw.step("encode_rgb"): rgb_b64 = base64.b64encode(seg.rgb_jpeg).decode("ascii") @@ -323,8 +328,10 @@ def find_model(req: FindModelReq) -> dict[str, Any]: "model_points_px": model_px.tolist(), "accepted_points_px": accepted_px.tolist(), "excluded_points_px": excluded_px.tolist(), + "sam21_points_px": sam21_px.tolist(), "n_accepted": int(accepted_m.shape[0]), "n_excluded": int(excluded_m.shape[0]), + "n_sam21": int(seg.points_cv_m.shape[0]), "plane_applied": bool(plane_info["plane_applied"]), "fit_ok": fit_ok, "fit_failed": fit_failed, diff --git a/web/app.js b/web/app.js index 24c42dd..c648ff5 100644 --- a/web/app.js +++ b/web/app.js @@ -540,7 +540,7 @@ $('findBtn').addEventListener('click', async () => { } }); -['findShowModel', 'findShowAccepted', 'findShowExcluded'].forEach(id => +['findShowModel', 'findShowAccepted', 'findShowExcluded', 'findShowSam21'].forEach(id => $(id).addEventListener('change', drawFindResult)); function drawFindResult() { @@ -552,9 +552,12 @@ function drawFindResult() { findCtx.fillStyle = color; for (const [u, v] of pts) findCtx.fillRect(u - r, v - r, r*2+1, r*2+1); }; - if ($('findShowExcluded').checked) dot(findResult.excluded_points_px, '#e74c3c', 1); - if ($('findShowAccepted').checked) dot(findResult.accepted_points_px, '#3ecf8e', 1); - if ($('findShowModel').checked) dot(findResult.model_points_px, '#4da3ff', 1); + // Draw order: SAM21 cluster (background) -> excluded -> accepted -> model + // (foreground), so the Open3DModel STL projection wins over everything. + if ($('findShowSam21').checked) dot(findResult.sam21_points_px || [], '#ffd83d', 1); + if ($('findShowExcluded').checked) dot(findResult.excluded_points_px, '#e74c3c', 1); + if ($('findShowAccepted').checked) dot(findResult.accepted_points_px, '#3ecf8e', 1); + if ($('findShowModel').checked) dot(findResult.model_points_px, '#4da3ff', 1); } function loadRecipesIfNeeded() { diff --git a/web/index.html b/web/index.html index f4a8b13..89aede0 100644 --- a/web/index.html +++ b/web/index.html @@ -268,6 +268,7 @@ +
—