fix: persistenza immagini su disco (sopravvive restart server)
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) <noreply@anthropic.com>
This commit is contained in:
+30
-16
@@ -9,17 +9,15 @@ Endpoint:
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import base64
|
import tempfile
|
||||||
import io
|
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from fastapi import FastAPI, File, HTTPException, UploadFile
|
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 fastapi.staticfiles import StaticFiles
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -31,20 +29,36 @@ WEB_DIR = Path(__file__).parent
|
|||||||
STATIC_DIR = WEB_DIR / "static"
|
STATIC_DIR = WEB_DIR / "static"
|
||||||
STATIC_DIR.mkdir(exist_ok=True)
|
STATIC_DIR.mkdir(exist_ok=True)
|
||||||
|
|
||||||
# In-memory image store: id → np.ndarray BGR
|
# Persistenza immagini su disco (sopravvive a restart server)
|
||||||
_IMAGES: dict[str, np.ndarray] = {}
|
CACHE_DIR = Path(tempfile.gettempdir()) / "pm2d_cache"
|
||||||
# Cache del last annotated: (image_id, matches_hash) → png bytes
|
CACHE_DIR.mkdir(exist_ok=True)
|
||||||
_ANNOT_CACHE: dict[tuple[str, int], bytes] = {}
|
|
||||||
|
|
||||||
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:
|
def _store_image(img: np.ndarray) -> str:
|
||||||
iid = uuid.uuid4().hex[:12]
|
iid = uuid.uuid4().hex[:12]
|
||||||
_IMAGES[iid] = img
|
cv2.imwrite(str(CACHE_DIR / f"{iid}.png"), img)
|
||||||
|
_IMG_CACHE[iid] = img
|
||||||
return iid
|
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:
|
def _encode_png(img: np.ndarray) -> bytes:
|
||||||
ok, buf = cv2.imencode(".png", img)
|
ok, buf = cv2.imencode(".png", img)
|
||||||
if not ok:
|
if not ok:
|
||||||
@@ -257,7 +271,7 @@ async def upload(file: UploadFile = File(...)):
|
|||||||
|
|
||||||
@app.get("/image/{iid}/raw")
|
@app.get("/image/{iid}/raw")
|
||||||
def image_raw(iid: str):
|
def image_raw(iid: str):
|
||||||
img = _IMAGES.get(iid)
|
img = _load_image(iid)
|
||||||
if img is None:
|
if img is None:
|
||||||
raise HTTPException(404, "Image not found")
|
raise HTTPException(404, "Image not found")
|
||||||
return Response(_encode_png(img), media_type="image/png")
|
return Response(_encode_png(img), media_type="image/png")
|
||||||
@@ -265,8 +279,8 @@ def image_raw(iid: str):
|
|||||||
|
|
||||||
@app.post("/match", response_model=MatchResp)
|
@app.post("/match", response_model=MatchResp)
|
||||||
def match(p: MatchParams):
|
def match(p: MatchParams):
|
||||||
model = _IMAGES.get(p.model_id)
|
model = _load_image(p.model_id)
|
||||||
scene = _IMAGES.get(p.scene_id)
|
scene = _load_image(p.scene_id)
|
||||||
if model is None or scene is None:
|
if model is None or scene is None:
|
||||||
raise HTTPException(404, "Immagini non trovate")
|
raise HTTPException(404, "Immagini non trovate")
|
||||||
x, y, w, h = p.roi
|
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,
|
Il server deriva i parametri tecnici (num_features, soglie gradiente,
|
||||||
piramide, ecc.) dall'analisi automatica della ROI.
|
piramide, ecc.) dall'analisi automatica della ROI.
|
||||||
"""
|
"""
|
||||||
model = _IMAGES.get(p.model_id)
|
model = _load_image(p.model_id)
|
||||||
scene = _IMAGES.get(p.scene_id)
|
scene = _load_image(p.scene_id)
|
||||||
if model is None or scene is None:
|
if model is None or scene is None:
|
||||||
raise HTTPException(404, "Immagini non trovate")
|
raise HTTPException(404, "Immagini non trovate")
|
||||||
x, y, w, h = p.roi
|
x, y, w, h = p.roi
|
||||||
@@ -364,7 +378,7 @@ def match_simple(p: SimpleMatchParams):
|
|||||||
|
|
||||||
@app.post("/auto_tune")
|
@app.post("/auto_tune")
|
||||||
def tune(p: TuneParams):
|
def tune(p: TuneParams):
|
||||||
model = _IMAGES.get(p.model_id)
|
model = _load_image(p.model_id)
|
||||||
if model is None:
|
if model is None:
|
||||||
raise HTTPException(404, "Immagine non trovata")
|
raise HTTPException(404, "Immagine non trovata")
|
||||||
x, y, w, h = p.roi
|
x, y, w, h = p.roi
|
||||||
|
|||||||
Reference in New Issue
Block a user