acquisitore: calibrazione manuale con mm/px impostato a mano (Refs #95)
POST /camera/calibrazioni/manuale salva una voce senza mappa (metodo manuale); anteprima/calibrata senza mappa risponde il frame grezzo (X-Raddrizzato: no); scheda «Calibrazione manuale» nella tab Calibrazione. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -268,6 +268,22 @@ PAGINA = r"""<!doctype html>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- #95: calibrazione manuale, mm per pixel impostato a mano (nessuna scacchiera) -->
|
||||
<section class="scheda" id="sezioneManuale">
|
||||
<h2>Calibrazione manuale</h2>
|
||||
<p class="nota" style="margin-top:0">Senza scacchiera: si scrive a mano quanti millimetri vale un pixel. Vale solo come scala, nessuna correzione dell'ottica.</p>
|
||||
<div class="riga" style="margin-top:8px">
|
||||
<div><label for="mmPx">mm per pixel</label><input id="mmPx" type="text" inputmode="decimal" size="10" autocomplete="off"></div>
|
||||
<div class="nota" id="pxMmCalc" style="margin:0 0 8px">= - px/mm</div>
|
||||
</div>
|
||||
<div class="riga" style="margin-top:6px">
|
||||
<div><label for="nomeManuale">Nome</label><input id="nomeManuale" type="text" maxlength="60" size="22" autocomplete="off"></div>
|
||||
<button id="salvaManuale">Salva</button>
|
||||
</div>
|
||||
<div id="notaManuale" class="nota"></div>
|
||||
<div id="msgManuale" class="messaggio"></div>
|
||||
</section>
|
||||
|
||||
<section class="scheda" id="sezioneSalvate">
|
||||
<h2>Calibrazioni salvate</h2>
|
||||
<div id="stato" class="stato">-</div>
|
||||
@@ -286,6 +302,7 @@ var ultimo = null; // ultimo calcolo di calibrazione, per «Salva»
|
||||
|
||||
// numero all'italiana (virgola decimale); cifre = decimali fissi, omesso = fino a 4
|
||||
function fmt(v, cifre) {
|
||||
if (v === null || v === undefined || v === "") return "-"; // #95: campi null (rms_px, square_mm della manuale) = «-», non 0
|
||||
var n = Number(v);
|
||||
if (!isFinite(n)) return "-";
|
||||
var o = cifre === undefined ? { maximumFractionDigits: 4 } : { minimumFractionDigits: cifre, maximumFractionDigits: cifre };
|
||||
@@ -494,14 +511,14 @@ function mostraInTabCal(url) {
|
||||
}
|
||||
|
||||
// #92: riquadro «Calibrata»: l'immagine raddrizzata oppure il motivo per cui non c'e' (404/409 del servizio)
|
||||
function mostraCalibrata(blob, nome) {
|
||||
function mostraCalibrata(blob, nome, soloScala) {
|
||||
var vecchio = A.urlCal;
|
||||
A.urlCal = URL.createObjectURL(blob);
|
||||
$("immagineCal").src = A.urlCal;
|
||||
if (vecchio) URL.revokeObjectURL(vecchio);
|
||||
$("corniceCal").hidden = false;
|
||||
$("vuotoCal").hidden = true;
|
||||
$("nomeCalibrata").textContent = nome ? "«" + nome + "»" : "";
|
||||
$("nomeCalibrata").textContent = (nome ? "«" + nome + "»" : "") + (soloScala ? " — solo scala, nessuna correzione ottica" : ""); // #95: senza mappa il frame e' grezzo
|
||||
}
|
||||
function messaggioCalibrata(testo) {
|
||||
$("corniceCal").hidden = true;
|
||||
@@ -544,7 +561,7 @@ async function aggiornaCalibrata(lato) {
|
||||
try { nome = decodeURIComponent(r.headers.get("X-Calibrazione") || ""); } catch (e) { /* intestazione non decodificabile: nessun nome */ }
|
||||
var blob = await r.blob();
|
||||
if (n !== A.seqCal) return;
|
||||
mostraCalibrata(blob, nome);
|
||||
mostraCalibrata(blob, nome, r.headers.get("X-Raddrizzato") === "no"); // #95
|
||||
} catch (e) {
|
||||
if (n === A.seqCal) messaggioCalibrata("Servizio non raggiungibile.");
|
||||
}
|
||||
@@ -962,6 +979,11 @@ $("tabCal").onclick = function () { scegliTab("cal", true); };
|
||||
// ---- Calibrazione (logica di #83 e #84; #87: lato precompilato, data italiana, conferma in pagina)
|
||||
|
||||
function righe(d) {
|
||||
if (d.metodo === "manuale") return [ // #95: solo scala impostata a mano, niente griglia, residui o lato dello scacco
|
||||
["Scala", fmt(d.px_per_mm, 4) + " px/mm (" + fmt(d.mm_per_px, 5) + " mm/px)"],
|
||||
["Metodo", "manuale (mm/px impostato a mano, nessuna correzione della distorsione)"],
|
||||
["Immagine", fmt(d.image_width, 0) + " x " + fmt(d.image_height, 0) + " px"]
|
||||
];
|
||||
return [
|
||||
["Scala", fmt(d.px_per_mm, 4) + " px/mm (" + fmt(d.mm_per_px, 5) + " mm/px)"],
|
||||
["Metodo", d.metodo !== "densa" ? "planare (senza correzione della distorsione)" : "densa (immagine raddrizzata)"],
|
||||
@@ -1018,6 +1040,7 @@ async function aggiornaStato() {
|
||||
var stato = $("stato");
|
||||
annullaConferma();
|
||||
$("calSorgente").textContent = sorgente ? $("sorgente").options[$("sorgente").selectedIndex].textContent : "-"; // #91
|
||||
statoManuale(); // #95: «Salva» della calibrazione manuale spento senza sorgente
|
||||
if (!sorgente) { stato.textContent = "-"; $("statoSorgente").textContent = "-"; caricaCalibrazioni(); return; }
|
||||
try {
|
||||
var r = await fetch("/camera/calibration?source=" + encodeURIComponent(sorgente));
|
||||
@@ -1043,7 +1066,7 @@ async function aggiornaStato() {
|
||||
stato.appendChild(t);
|
||||
stato.appendChild(quando);
|
||||
$("statoSorgente").textContent = "calibrata: " + (d.nome || "(senza nome)") + " · " + fmt(d.px_per_mm, 2) + " px/mm"; // #92
|
||||
if (d.square_mm) $("quadrato").value = fmtCasella(d.square_mm); // #87: parte dal lato gia' usato
|
||||
if (Number(d.square_mm) > 0) $("quadrato").value = fmtCasella(d.square_mm); // #87: parte dal lato gia' usato
|
||||
}
|
||||
} catch (e) {
|
||||
stato.textContent = "Servizio non raggiungibile.";
|
||||
@@ -1114,7 +1137,7 @@ function disegnaElenco() {
|
||||
riga2.textContent = [
|
||||
c.image_width && c.image_height ? fmt(c.image_width, 0) + "×" + fmt(c.image_height, 0) : "-",
|
||||
fmt(c.px_per_mm, 3) + " px/mm",
|
||||
c.metodo === "densa" ? "densa" : "planare",
|
||||
c.metodo === "densa" || c.metodo === "manuale" ? c.metodo : "planare", // #95: «manuale» mostrato come tale
|
||||
c.calibrated_at ? dataItaliana(c.calibrated_at) : "-",
|
||||
descriviOrigine(c.origine)
|
||||
].join(" · ");
|
||||
@@ -1312,6 +1335,60 @@ async function salvaNome() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- #95: calibrazione manuale (mm/px a mano)
|
||||
var M = { nome: null, timer: null }; // secondo clic su «Sovrascrivi» entro 4 s
|
||||
function statoManuale() {
|
||||
var s = $("sorgente").value;
|
||||
$("salvaManuale").disabled = !s;
|
||||
$("notaManuale").textContent = s ? "" : "Scegliere una sorgente nella tab Acquisizione: serve per registrare la risoluzione dell'immagine.";
|
||||
}
|
||||
function aggiornaManuale() {
|
||||
var v = leggiNumero($("mmPx").value);
|
||||
$("pxMmCalc").textContent = v > 0 ? "= " + fmt(1 / v, 4) + " px/mm" : "= - px/mm";
|
||||
}
|
||||
function annullaSovrascriviManuale() {
|
||||
clearTimeout(M.timer);
|
||||
M.timer = null;
|
||||
M.nome = null;
|
||||
$("salvaManuale").textContent = "Salva";
|
||||
}
|
||||
async function salvaManuale() {
|
||||
var sorgente = $("sorgente").value;
|
||||
if (!sorgente) { dire("msgManuale", "Scegliere una sorgente nella tab Acquisizione.", "errore"); return; }
|
||||
var mm = leggiNumero($("mmPx").value);
|
||||
if (!(mm >= 0.00001 && mm <= 100)) { dire("msgManuale", "mm/px deve essere un numero positivo (da 0,00001 a 100).", "errore"); return; }
|
||||
var nome = $("nomeManuale").value.trim();
|
||||
if (!nome) { dire("msgManuale", "Scrivere un nome per la calibrazione.", "errore"); return; }
|
||||
if (nome.length > 60) { dire("msgManuale", "Il nome può avere al massimo 60 caratteri.", "errore"); return; }
|
||||
var sovrascrivi = M.timer !== null && M.nome === nome.toLowerCase(); // secondo clic sullo stesso nome
|
||||
annullaSovrascriviManuale();
|
||||
$("salvaManuale").disabled = true;
|
||||
try {
|
||||
var r = await fetch("/camera/calibrazioni/manuale", {
|
||||
method: "POST", headers: JSON_H,
|
||||
body: JSON.stringify({ source: sorgente, nome: nome, mm_per_px: mm, sovrascrivi: sovrascrivi }) // al servizio sempre il numero, mai il testo con la virgola
|
||||
});
|
||||
if (r.status === 409) {
|
||||
M.nome = nome.toLowerCase();
|
||||
$("salvaManuale").textContent = "Sovrascrivi «" + nome + "»? Clic ancora";
|
||||
M.timer = setTimeout(annullaSovrascriviManuale, 4000);
|
||||
dire("msgManuale", await dettaglio(r), "errore");
|
||||
return;
|
||||
}
|
||||
if (!r.ok) { dire("msgManuale", await dettaglio(r), "errore"); return; }
|
||||
dire("msgManuale", "Calibrazione «" + nome + "» salvata: per usarla «Carica» o CONFERMA.", "ok");
|
||||
caricaCalibrazioni();
|
||||
} catch (e) {
|
||||
dire("msgManuale", "Servizio non raggiungibile.", "errore");
|
||||
} finally {
|
||||
statoManuale();
|
||||
}
|
||||
}
|
||||
$("mmPx").oninput = aggiornaManuale;
|
||||
$("salvaManuale").onclick = salvaManuale;
|
||||
$("nomeManuale").onkeydown = function (e) { if (e.key === "Enter") salvaManuale(); };
|
||||
statoManuale();
|
||||
|
||||
$("acquisisci").onclick = calcola;
|
||||
$("salva").onclick = salvaNome;
|
||||
$("nomeCal").onkeydown = function (e) { if (e.key === "Enter") salvaNome(); };
|
||||
|
||||
Reference in New Issue
Block a user