feat: interfaccia web HTML (FastAPI + canvas JS)

Sostituisce GUI cv2/tkinter con webapp standalone:

Server (pm2d/web/server.py):
- FastAPI + uvicorn
- Endpoint: GET /, POST /upload, POST /match, POST /auto_tune,
  GET /image/{id}/raw
- In-memory image store (uuid-based)
- Rendering annotated server-side via opencv (overlay bbox + edges
  template warpati)

Frontend (pm2d/web/static/):
- index.html: layout 3 colonne (MODELLO | SCENA | PARAMETRI) + footer
  legenda
- style.css: tema dark, CSS grid responsive
- app.js: canvas HTML5 per visualizzazione scalata fit,
  ROI selection con drag mouse, form parametri live,
  MATCH button, Auto-tune button

Parametri modificabili INLINE (niente dialog separata).
Enter su qualsiasi campo triggera MATCH.
Legenda match in fondo con pallino colorato + dati.

main.py ora lancia il server webapp. Deprecato ingresso GUI cv2
(pm2d/gui.py resta importable per backward compat).

Test: /match su rings_and_nuts: 3/3 ruote in 1.14s (train 0.36s + find 0.77s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 09:06:25 +02:00
parent 4ddda1ec62
commit fd7585acc5
8 changed files with 1234 additions and 20 deletions
+58
View File
@@ -0,0 +1,58 @@
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>Pattern Matching 2D</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header>
<h1>Pattern Matching 2D</h1>
<div class="toolbar">
<label class="btn">📂 Modello
<input type="file" id="file-model" accept="image/*" hidden>
</label>
<label class="btn">📂 Scena
<input type="file" id="file-scene" accept="image/*" hidden>
</label>
<button class="btn" id="btn-tune">Auto-tune</button>
<button class="btn btn-go" id="btn-match">▶ MATCH</button>
<span id="status">Carica modello + disegna ROI + carica scena</span>
</div>
</header>
<main>
<section class="col" id="col-model">
<h2>MODELLO</h2>
<div class="canvas-wrap">
<canvas id="c-model" width="380" height="420"></canvas>
</div>
<div id="roi-info">ROI: (nessuna)</div>
</section>
<section class="col" id="col-scene">
<h2>SCENA</h2>
<div class="canvas-wrap">
<canvas id="c-scene" width="820" height="620"></canvas>
</div>
</section>
<section class="col" id="col-params">
<h2>PARAMETRI</h2>
<div id="params-form"></div>
<h2 style="margin-top:16px">TEMPI</h2>
<div class="kv"><span>train:</span><span id="t-train">-</span></div>
<div class="kv"><span>find:</span><span id="t-find">-</span></div>
<div class="kv"><span>varianti:</span><span id="t-var">-</span></div>
<div class="kv"><span>match:</span><span id="t-match">-</span></div>
</section>
</main>
<footer>
<h2>LEGENDA</h2>
<div id="legend"></div>
</footer>
<script src="/static/app.js"></script>
</body>
</html>