diff --git a/pm2d/web/server.py b/pm2d/web/server.py index 2534697..1d49ecd 100644 --- a/pm2d/web/server.py +++ b/pm2d/web/server.py @@ -329,6 +329,49 @@ def index(): return HTMLResponse(html_path.read_text(encoding="utf-8")) +@app.post("/upload_to_folder") +async def upload_to_folder(file: UploadFile = File(...)): + """Salva file caricato nella cartella IMAGES_DIR. Ritorna lista aggiornata.""" + if not IMAGES_DIR.is_dir(): + raise HTTPException(500, f"IMAGES_DIR non esiste: {IMAGES_DIR}") + # Sanitizza nome file (no traversal) + name = Path(file.filename or "upload.png").name + if not name: + raise HTTPException(400, "nome file vuoto") + ext = Path(name).suffix.lower() + allowed = {".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff"} + if ext not in allowed: + raise HTTPException(400, f"estensione non supportata: {ext}") + # Leggi contenuto e valida come immagine + data = await file.read() + arr = np.frombuffer(data, dtype=np.uint8) + img = cv2.imdecode(arr, cv2.IMREAD_COLOR) + if img is None: + raise HTTPException(400, "file non è un'immagine valida") + # Evita overwrite: se esiste, aggiungi suffisso numerico + target = IMAGES_DIR / name + if target.exists(): + stem = target.stem; suffix = target.suffix + i = 1 + while True: + alt = IMAGES_DIR / f"{stem}_{i}{suffix}" + if not alt.exists(): + target = alt; break + i += 1 + # Scrivi su disco + with open(target, "wb") as f: + f.write(data) + # Ritorna lista aggiornata + return { + "saved_as": target.name, + "dir": str(IMAGES_DIR), + "files": sorted( + p.name for p in IMAGES_DIR.iterdir() + if p.is_file() and p.suffix.lower() in allowed + ), + } + + @app.get("/folder_image/{filename}") def folder_image(filename: str, w: int = 120): """Serve thumbnail PNG dell'immagine IMAGES_DIR (scalata a width w).""" diff --git a/pm2d/web/static/app.js b/pm2d/web/static/app.js index b719abd..7ad8063 100644 --- a/pm2d/web/static/app.js +++ b/pm2d/web/static/app.js @@ -82,6 +82,21 @@ async function fetchImagesList() { return await r.json(); } +async function uploadToFolder(file) { + const fd = new FormData(); + fd.append("file", file); + const r = await fetch("/upload_to_folder", { method: "POST", body: fd }); + if (!r.ok) throw new Error(await r.text()); + return await r.json(); +} + +async function refreshPickers() { + const {files, dir} = await fetchImagesList(); + buildThumbPicker("picker-model", files, onSelectModel); + buildThumbPicker("picker-scene", files, onSelectScene); + return {files, dir}; +} + function buildThumbPicker(pickerId, files, onSelect) { const picker = document.getElementById(pickerId); const current = picker.querySelector(".picker-current"); @@ -351,14 +366,28 @@ window.addEventListener("DOMContentLoaded", async () => { buildAdvancedForm(); setupROI(); // Popola picker immagini da IMAGES_DIR (con thumbnail) - const {files, dir} = await fetchImagesList(); - buildThumbPicker("picker-model", files, onSelectModel); - buildThumbPicker("picker-scene", files, onSelectScene); + const {files, dir} = await refreshPickers(); if (files.length === 0) { - setStatus(`Nessuna immagine in ${dir} (configura IMAGES_DIR in .env)`); + setStatus(`Nessuna immagine in ${dir} (carica file o configura IMAGES_DIR)`); } else { - setStatus(`${files.length} immagini disponibili in ${dir}`); + setStatus(`${files.length} immagini in ${dir}`); } + + // Upload file nella folder + const upEl = document.getElementById("file-upload"); + upEl.addEventListener("change", async (e) => { + const f = e.target.files[0]; + if (!f) return; + setStatus(`Caricamento ${f.name} nella cartella...`); + try { + const res = await uploadToFolder(f); + await refreshPickers(); + setStatus(`Salvato come ${res.saved_as} (${res.files.length} file totali)`); + } catch (err) { + setStatus(`Errore upload: ${err.message}`); + } + e.target.value = ""; // consente re-upload stesso file + }); 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 b47555a..aadd0e0 100644 --- a/pm2d/web/static/index.html +++ b/pm2d/web/static/index.html @@ -26,6 +26,10 @@
+ Seleziona modello, disegna ROI, seleziona scena