diff --git a/Test/bracket_tilted_01.png b/Test/bracket_tilted_01.png new file mode 100755 index 0000000..de2775a Binary files /dev/null and b/Test/bracket_tilted_01.png differ diff --git a/Test/bracket_tilted_02.png b/Test/bracket_tilted_02.png new file mode 100755 index 0000000..7aa1321 Binary files /dev/null and b/Test/bracket_tilted_02.png differ diff --git a/Test/bracket_tilted_03.png b/Test/bracket_tilted_03.png new file mode 100755 index 0000000..93f1d37 Binary files /dev/null and b/Test/bracket_tilted_03.png differ diff --git a/Test/bracket_tilted_04.png b/Test/bracket_tilted_04.png new file mode 100755 index 0000000..42ac95e Binary files /dev/null and b/Test/bracket_tilted_04.png differ diff --git a/Test/bracket_tilted_05.png b/Test/bracket_tilted_05.png new file mode 100755 index 0000000..880fab4 Binary files /dev/null and b/Test/bracket_tilted_05.png differ diff --git a/Test/bracket_tilted_06.png b/Test/bracket_tilted_06.png new file mode 100755 index 0000000..9562cda Binary files /dev/null and b/Test/bracket_tilted_06.png differ diff --git a/Test/bracket_tilted_07.png b/Test/bracket_tilted_07.png new file mode 100755 index 0000000..590916c Binary files /dev/null and b/Test/bracket_tilted_07.png differ diff --git a/Test/bracket_tilted_08.png b/Test/bracket_tilted_08.png new file mode 100755 index 0000000..0c364de Binary files /dev/null and b/Test/bracket_tilted_08.png differ diff --git a/Test/bracket_tilted_09.png b/Test/bracket_tilted_09.png new file mode 100755 index 0000000..cf584b9 Binary files /dev/null and b/Test/bracket_tilted_09.png differ diff --git a/Test/cap_illumination_01.png b/Test/cap_illumination_01.png new file mode 100755 index 0000000..66dff5e Binary files /dev/null and b/Test/cap_illumination_01.png differ diff --git a/Test/cap_illumination_05.png b/Test/cap_illumination_05.png new file mode 100755 index 0000000..02ff9c3 Binary files /dev/null and b/Test/cap_illumination_05.png differ diff --git a/Test/cap_illumination_07.png b/Test/cap_illumination_07.png new file mode 100755 index 0000000..b0c20da Binary files /dev/null and b/Test/cap_illumination_07.png differ diff --git a/Test/circle_plate_01.png b/Test/circle_plate_01.png new file mode 100755 index 0000000..89aa249 Binary files /dev/null and b/Test/circle_plate_01.png differ diff --git a/Test/circle_plate_02.png b/Test/circle_plate_02.png new file mode 100755 index 0000000..02bdc4a Binary files /dev/null and b/Test/circle_plate_02.png differ diff --git a/Test/circle_plate_03.png b/Test/circle_plate_03.png new file mode 100755 index 0000000..ccbf2bb Binary files /dev/null and b/Test/circle_plate_03.png differ diff --git a/Test/circle_plate_04.png b/Test/circle_plate_04.png new file mode 100755 index 0000000..7504325 Binary files /dev/null and b/Test/circle_plate_04.png differ diff --git a/Test/circle_plate_05.png b/Test/circle_plate_05.png new file mode 100755 index 0000000..27b4a24 Binary files /dev/null and b/Test/circle_plate_05.png differ diff --git a/Test/metal-part-distorted-01.png b/Test/metal-part-distorted-01.png new file mode 100755 index 0000000..b02887d Binary files /dev/null and b/Test/metal-part-distorted-01.png differ diff --git a/Test/metal-part-distorted-02.png b/Test/metal-part-distorted-02.png new file mode 100755 index 0000000..88013db Binary files /dev/null and b/Test/metal-part-distorted-02.png differ diff --git a/Test/metal-part-distorted-03.png b/Test/metal-part-distorted-03.png new file mode 100755 index 0000000..00e12ee Binary files /dev/null and b/Test/metal-part-distorted-03.png differ diff --git a/Test/metal-part-model-01.png b/Test/metal-part-model-01.png new file mode 100755 index 0000000..8443d3d Binary files /dev/null and b/Test/metal-part-model-01.png differ diff --git a/Test/metal-part-model-02.png b/Test/metal-part-model-02.png new file mode 100755 index 0000000..4f87784 Binary files /dev/null and b/Test/metal-part-model-02.png differ diff --git a/Test/metal-part-model-03.png b/Test/metal-part-model-03.png new file mode 100755 index 0000000..6ff0824 Binary files /dev/null and b/Test/metal-part-model-03.png differ diff --git a/Test/metal-parts-01.png b/Test/metal-parts-01.png new file mode 100755 index 0000000..2f24d96 Binary files /dev/null and b/Test/metal-parts-01.png differ diff --git a/Test/metal-parts-02.png b/Test/metal-parts-02.png new file mode 100755 index 0000000..43ff2b9 Binary files /dev/null and b/Test/metal-parts-02.png differ diff --git a/Test/metal-parts-03.png b/Test/metal-parts-03.png new file mode 100755 index 0000000..79443dd Binary files /dev/null and b/Test/metal-parts-03.png differ diff --git a/Test/metal-parts-04.png b/Test/metal-parts-04.png new file mode 100755 index 0000000..548e06c Binary files /dev/null and b/Test/metal-parts-04.png differ diff --git a/Test/metal-parts-05.png b/Test/metal-parts-05.png new file mode 100755 index 0000000..6e58d06 Binary files /dev/null and b/Test/metal-parts-05.png differ diff --git a/Test/metal-parts-06.png b/Test/metal-parts-06.png new file mode 100755 index 0000000..5902ef0 Binary files /dev/null and b/Test/metal-parts-06.png differ diff --git a/Test/metal-parts-07.png b/Test/metal-parts-07.png new file mode 100755 index 0000000..3c401bc Binary files /dev/null and b/Test/metal-parts-07.png differ diff --git a/Test/metal-parts-08.png b/Test/metal-parts-08.png new file mode 100755 index 0000000..7a1c7d2 Binary files /dev/null and b/Test/metal-parts-08.png differ diff --git a/Test/metal-parts-09.png b/Test/metal-parts-09.png new file mode 100755 index 0000000..04be5b0 Binary files /dev/null and b/Test/metal-parts-09.png differ diff --git a/Test/metal-parts-10.png b/Test/metal-parts-10.png new file mode 100755 index 0000000..a362daf Binary files /dev/null and b/Test/metal-parts-10.png differ diff --git a/Test/metal-parts-11.png b/Test/metal-parts-11.png new file mode 100755 index 0000000..079cc5c Binary files /dev/null and b/Test/metal-parts-11.png differ diff --git a/Test/metal-parts-12.png b/Test/metal-parts-12.png new file mode 100755 index 0000000..ed000cf Binary files /dev/null and b/Test/metal-parts-12.png differ diff --git a/Test/metal-parts-13.png b/Test/metal-parts-13.png new file mode 100755 index 0000000..03d0f3c Binary files /dev/null and b/Test/metal-parts-13.png differ diff --git a/Test/metal-parts-14.png b/Test/metal-parts-14.png new file mode 100755 index 0000000..ee097b8 Binary files /dev/null and b/Test/metal-parts-14.png differ diff --git a/Test/metal-parts-15.png b/Test/metal-parts-15.png new file mode 100755 index 0000000..5b7533f Binary files /dev/null and b/Test/metal-parts-15.png differ diff --git a/Test/work_sheet_01.png b/Test/work_sheet_01.png new file mode 100755 index 0000000..736db28 Binary files /dev/null and b/Test/work_sheet_01.png differ diff --git a/Test/work_sheet_02.png b/Test/work_sheet_02.png new file mode 100755 index 0000000..de0e1ae Binary files /dev/null and b/Test/work_sheet_02.png differ diff --git a/Test/work_sheet_03.png b/Test/work_sheet_03.png new file mode 100755 index 0000000..7a42b49 Binary files /dev/null and b/Test/work_sheet_03.png differ diff --git a/Test/work_sheet_04.png b/Test/work_sheet_04.png new file mode 100755 index 0000000..982f910 Binary files /dev/null and b/Test/work_sheet_04.png differ diff --git a/Test/work_sheet_05.png b/Test/work_sheet_05.png new file mode 100755 index 0000000..d5f35f3 Binary files /dev/null and b/Test/work_sheet_05.png differ diff --git a/pm2d/web/server.py b/pm2d/web/server.py index 9cc75a6..ae1148f 100644 --- a/pm2d/web/server.py +++ b/pm2d/web/server.py @@ -292,6 +292,25 @@ def index(): return HTMLResponse(html_path.read_text(encoding="utf-8")) +@app.get("/folder_image/{filename}") +def folder_image(filename: str, w: int = 120): + """Serve thumbnail PNG dell'immagine IMAGES_DIR (scalata a width w).""" + if "/" in filename or ".." in filename: + raise HTTPException(400, "nome non valido") + path = IMAGES_DIR / filename + if not path.is_file(): + raise HTTPException(404, "non trovato") + img = cv2.imread(str(path), cv2.IMREAD_COLOR) + if img is None: + raise HTTPException(400, "non leggibile") + h0, w0 = img.shape[:2] + if w0 > w: + sc = w / w0 + img = cv2.resize(img, (w, int(h0 * sc)), interpolation=cv2.INTER_AREA) + return Response(_encode_png(img), media_type="image/png", + headers={"Cache-Control": "public, max-age=3600"}) + + @app.get("/images") def list_images(): """Lista file immagine nella cartella configurata in IMAGES_DIR.""" diff --git a/pm2d/web/static/app.js b/pm2d/web/static/app.js index 7fa251b..186053d 100644 --- a/pm2d/web/static/app.js +++ b/pm2d/web/static/app.js @@ -80,18 +80,56 @@ async function fetchImagesList() { return await r.json(); } -function populateSelect(selectEl, files) { - selectEl.innerHTML = ""; - const opt0 = document.createElement("option"); - opt0.value = ""; opt0.textContent = "-- seleziona --"; - selectEl.appendChild(opt0); - for (const f of files) { - const o = document.createElement("option"); - o.value = f; o.textContent = f; - selectEl.appendChild(o); - } +function buildThumbPicker(pickerId, files, onSelect) { + const picker = document.getElementById(pickerId); + const current = picker.querySelector(".picker-current"); + const list = picker.querySelector(".picker-list"); + const text = current.querySelector(".picker-text"); + // Rimuovi eventuale vecchia thumbnail + const oldImg = current.querySelector("img"); + if (oldImg) oldImg.remove(); + list.innerHTML = ""; + + files.forEach((f) => { + const item = document.createElement("div"); + item.className = "picker-item"; + const img = document.createElement("img"); + img.src = `/folder_image/${encodeURIComponent(f)}?w=120`; + img.loading = "lazy"; + const name = document.createElement("span"); + name.className = "name"; name.textContent = f; + item.appendChild(img); item.appendChild(name); + item.addEventListener("click", () => { + // Aggiorna la visual del "current" + let thumb = current.querySelector("img"); + if (!thumb) { + thumb = document.createElement("img"); + current.insertBefore(thumb, text); + } + thumb.src = `/folder_image/${encodeURIComponent(f)}?w=80`; + text.textContent = f; + picker.classList.remove("open"); + onSelect(f); + }); + list.appendChild(item); + }); + + current.onclick = () => { + // Chiudi altri picker aperti + document.querySelectorAll(".thumb-picker.open") + .forEach((p) => { if (p !== picker) p.classList.remove("open"); }); + picker.classList.toggle("open"); + }; } +// Close picker on outside click +document.addEventListener("click", (e) => { + if (!e.target.closest(".thumb-picker")) { + document.querySelectorAll(".thumb-picker.open") + .forEach((p) => p.classList.remove("open")); + } +}); + function loadImage(src) { return new Promise((res, rej) => { const img = new Image(); @@ -310,19 +348,15 @@ function setStatus(s) { window.addEventListener("DOMContentLoaded", async () => { buildAdvancedForm(); setupROI(); - // Popola dropdown immagini da IMAGES_DIR + // Popola picker immagini da IMAGES_DIR (con thumbnail) const {files, dir} = await fetchImagesList(); - const selM = document.getElementById("sel-model"); - const selS = document.getElementById("sel-scene"); - populateSelect(selM, files); - populateSelect(selS, files); + buildThumbPicker("picker-model", files, onSelectModel); + buildThumbPicker("picker-scene", files, onSelectScene); if (files.length === 0) { setStatus(`Nessuna immagine in ${dir} (configura IMAGES_DIR in .env)`); } else { setStatus(`${files.length} immagini disponibili in ${dir}`); } - selM.addEventListener("change", (e) => onSelectModel(e.target.value)); - selS.addEventListener("change", (e) => onSelectScene(e.target.value)); document.getElementById("btn-match").addEventListener("click", doMatch); const slider = document.getElementById("p-min-score"); slider.addEventListener("input", (e) => { diff --git a/pm2d/web/static/index.html b/pm2d/web/static/index.html index 2ee6725..eb2f963 100644 --- a/pm2d/web/static/index.html +++ b/pm2d/web/static/index.html @@ -10,13 +10,21 @@

Pattern Matching 2D

- +
+
+ -- seleziona -- + +
+
+
- +
+
+ -- seleziona -- + +
+
+
Seleziona modello, disegna ROI, seleziona scena
diff --git a/pm2d/web/static/style.css b/pm2d/web/static/style.css index 7dba55d..6c02fd7 100644 --- a/pm2d/web/static/style.css +++ b/pm2d/web/static/style.css @@ -27,12 +27,39 @@ header h1 { } .btn-go:hover { background: #0d9c48; } .tb-label { color: #b0b0b0; font-size: 12px; margin-left: 8px; } -.tb-select { + +.thumb-picker { position: relative; display: inline-block; } +.picker-current { + display: flex; align-items: center; gap: 8px; background: #2a2a2a; color: #dcdcdc; border: 1px solid #444; - padding: 5px 8px; border-radius: 3px; font-size: 13px; - min-width: 160px; + padding: 4px 8px; border-radius: 3px; font-size: 13px; + cursor: pointer; min-width: 200px; min-height: 32px; } -.tb-select:focus { outline: 1px solid #00c8ff; } +.picker-current:hover { background: #353535; } +.picker-current img { + width: 36px; height: 36px; object-fit: contain; + background: #141414; border-radius: 2px; +} +.picker-text { flex: 1; white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; } +.caret { color: #888; font-size: 10px; } +.picker-list { + display: none; position: absolute; top: 100%; left: 0; + background: #232323; border: 1px solid #444; border-radius: 3px; + margin-top: 2px; z-index: 100; max-height: 360px; overflow-y: auto; + box-shadow: 0 4px 12px rgba(0,0,0,0.6); min-width: 280px; +} +.thumb-picker.open .picker-list { display: block; } +.picker-item { + display: flex; align-items: center; gap: 10px; + padding: 6px 10px; cursor: pointer; border-bottom: 1px solid #2a2a2a; +} +.picker-item:hover { background: #2e2e2e; } +.picker-item img { + width: 60px; height: 60px; object-fit: contain; + background: #141414; border-radius: 2px; +} +.picker-item .name { color: #dcdcdc; font-size: 13px; } #status { color: #00c8ff; margin-left: 12px; font-weight: 500; }