From e1a1b956fd5a7077986e37c60403f19276700c65 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Fri, 24 Apr 2026 10:00:54 +0200 Subject: [PATCH] fix: persistenza immagini su disco (sopravvive restart server) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: _IMAGES era dict in-memory, restart server → browser con id cached riceveva 404 'Immagini non trovate'. Fix: scrittura PNG in /tmp/pm2d_cache/{id}.png al upload, _load_image() prova cache memory prima di leggere disco. Rimossa funzione _store_image duplicata. Co-Authored-By: Claude Opus 4.7 (1M context) --- pm2d/web/server.py | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/pm2d/web/server.py b/pm2d/web/server.py index 817c452..cc1ddf7 100644 --- a/pm2d/web/server.py +++ b/pm2d/web/server.py @@ -9,17 +9,15 @@ Endpoint: """ from __future__ import annotations -import base64 -import io +import tempfile import time import uuid from pathlib import Path -from typing import Any import cv2 import numpy as np from fastapi import FastAPI, File, HTTPException, UploadFile -from fastapi.responses import FileResponse, HTMLResponse, Response, StreamingResponse +from fastapi.responses import HTMLResponse, Response from fastapi.staticfiles import StaticFiles from pydantic import BaseModel @@ -31,20 +29,36 @@ WEB_DIR = Path(__file__).parent STATIC_DIR = WEB_DIR / "static" STATIC_DIR.mkdir(exist_ok=True) -# In-memory image store: id → np.ndarray BGR -_IMAGES: dict[str, np.ndarray] = {} -# Cache del last annotated: (image_id, matches_hash) → png bytes -_ANNOT_CACHE: dict[tuple[str, int], bytes] = {} +# Persistenza immagini su disco (sopravvive a restart server) +CACHE_DIR = Path(tempfile.gettempdir()) / "pm2d_cache" +CACHE_DIR.mkdir(exist_ok=True) -app = FastAPI(title="PM2D Webapp", version="1.0.0") +# Cache in-memory (soft, ricaricata da disco se mancante) +_IMG_CACHE: dict[str, np.ndarray] = {} def _store_image(img: np.ndarray) -> str: iid = uuid.uuid4().hex[:12] - _IMAGES[iid] = img + cv2.imwrite(str(CACHE_DIR / f"{iid}.png"), img) + _IMG_CACHE[iid] = img return iid +def _load_image(iid: str) -> np.ndarray | None: + cached = _IMG_CACHE.get(iid) + if cached is not None: + return cached + p = CACHE_DIR / f"{iid}.png" + if not p.exists(): + return None + img = cv2.imread(str(p)) + if img is not None: + _IMG_CACHE[iid] = img + return img + +app = FastAPI(title="PM2D Webapp", version="1.0.0") + + def _encode_png(img: np.ndarray) -> bytes: ok, buf = cv2.imencode(".png", img) if not ok: @@ -257,7 +271,7 @@ async def upload(file: UploadFile = File(...)): @app.get("/image/{iid}/raw") def image_raw(iid: str): - img = _IMAGES.get(iid) + img = _load_image(iid) if img is None: raise HTTPException(404, "Image not found") return Response(_encode_png(img), media_type="image/png") @@ -265,8 +279,8 @@ def image_raw(iid: str): @app.post("/match", response_model=MatchResp) def match(p: MatchParams): - model = _IMAGES.get(p.model_id) - scene = _IMAGES.get(p.scene_id) + model = _load_image(p.model_id) + scene = _load_image(p.scene_id) if model is None or scene is None: raise HTTPException(404, "Immagini non trovate") x, y, w, h = p.roi @@ -317,8 +331,8 @@ def match_simple(p: SimpleMatchParams): Il server deriva i parametri tecnici (num_features, soglie gradiente, piramide, ecc.) dall'analisi automatica della ROI. """ - model = _IMAGES.get(p.model_id) - scene = _IMAGES.get(p.scene_id) + model = _load_image(p.model_id) + scene = _load_image(p.scene_id) if model is None or scene is None: raise HTTPException(404, "Immagini non trovate") x, y, w, h = p.roi @@ -364,7 +378,7 @@ def match_simple(p: SimpleMatchParams): @app.post("/auto_tune") def tune(p: TuneParams): - model = _IMAGES.get(p.model_id) + model = _load_image(p.model_id) if model is None: raise HTTPException(404, "Immagine non trovata") x, y, w, h = p.roi