Acquisizione: immagine da file come sorgente, calibrazione sempre su frame grezzo, vista grezza e calibrata affiancate #92

Closed
opened 2026-10-24 14:22:36 +00:00 by Adriano · 1 comment
Owner

ANALISI (passo 1, claude-opus-5-5)
Correzione del disegno di gitea:Adriano/TieMeasureFlow#91 chiesta da Adriano: immagine da file come sorgente di acquisizione, calibrazione sempre dalla sorgente e su frame grezzo, vista grezza e calibrata affiancate, sorgente in cima.

DUE PIANI A FILE DISGIUNTI (contratto sotto, identico in testa ai due prompt)
A servizio: scripts/camera_service.py + test in scripts/tests (nuovo test_immagini_affiancate.py; aggiornare i test esistenti solo se cambia l'elenco sorgenti, es. camera:file).
TEST A: upload ok/estensione no/file illeggibile (niente su disco); sources con immagini e senza camera:file; grab/anteprima/calibrate su immagine caricata; DELETE; anteprima tiene il frame grezzo; anteprima/calibrata 404 senza frame, 409 senza attiva, 409 risoluzione diversa, 200 con attiva densa (X-Raddrizzato si, X-Calibrazione) senza nuova lettura (sorgente finta che conta i grab).
B pagina: scripts/calibrazione_pagina.py + scripts/tests/test_pagina_tab.py (aggiornare: niente origFile/origSorgente, niente input file nella calibrazione, presenza di /camera/immagini, /camera/anteprima/calibrata, casella mostra calibrata, sezione sorgente prima dell'anteprima).
NON TOCCARE: scripts/calibrazione.py, src/, vendor/, packaging/.
Comando test: uv run --python 3.13 --extra vision-worker --extra dev --with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests -q
Prova d'insieme (passo 3, Opus): TMF da sorgente su daitarn, Playwright 1400 e 400 px: carica calibration_135_1.bmp come sorgente, calcola, salva, carica, affiancata grezza/calibrata; Itala 601020 con calibrazione di risoluzione diversa -> riquadro calibrata con messaggio, grezza viva.

CONTRATTO #92 (identico per i due piani; repo TieMeasureFlow, base ramo lavoro/daitarn-tmf-dxfcal 8eb80e2, che contiene #91)
Correzione di Adriano 24/10/2026 al disegno di #91: l'immagine da file e' una SORGENTE DI ACQUISIZIONE, non un'opzione della calibrazione. La calibrazione riceve SEMPRE l'immagine dalla sorgente selezionata nella tab Acquisizione, e sempre GREZZA (non calibrata). Nella tab Acquisizione si vede l'immagine grezza e, con un flag, accanto la stessa immagine calibrata, una a fianco all'altra. La sezione Sorgente sta in cima alla tab Acquisizione.

SERVIZIO (scripts/camera_service.py; scripts/calibrazione.py non cambia)

  1. Immagini caricate: cartella <config_dir>/immagini/ (creata al bisogno).
    POST /camera/immagini (multipart, campo image) -> salva col nome del file ripulito (caratteri sicuri come Archivio._file, estensione fra quelle di visionsuite.camera.sources._ESTENSIONI; se il nome c'e' gia' lo sostituisce) -> 201 {"source": "", "label": "immagine — ", "kind": "image"}.
    400 senza file o estensione non ammessa; 422 file non leggibile come immagine (cv2.imdecode None) -> in questo caso non resta nulla su disco.
    DELETE /camera/immagini?nome= -> 204; 404 se non c'e'. Chiude la sorgente se aperta.
  2. GET /camera/sources: camere fisiche + camera:folder + le immagini caricate (kind "image", name = nome file, label "immagine — "), in quest'ordine. camera:file (replay, mai configurato in TMF) NON compare piu'.
  3. Apertura: _apri usa open_source(nome, <config_dir>/immagini, config=...) cosi' un nome di file si apre come sorgente immagine (visionsuite.camera.sources._apri_immagine). /camera/grab, /camera/anteprima, /camera/calibrate funzionano anche sulle immagini caricate. Una stazione puo' usare come camera_source il nome di un'immagine caricata.
  4. POST /camera/anteprima: invariato nella forma; inoltre tiene in memoria, per sorgente, l'ULTIMO frame pieno GREZZO letto (prima di raddrizzare), sotto il lock gia' usato.
  5. POST /camera/anteprima/calibrata json {source, lato_max=1280 (320..4096)} -> NON legge un frame nuovo: raddrizza con la mappa della calibrazione attiva l'ultimo frame grezzo tenuto da (4) e risponde JPEG q85 ridotto come /camera/anteprima, header X-Raddrizzato: si, X-Larghezza, X-Altezza (frame pieno raddrizzato), X-Calibrazione: (percent-encoded UTF-8, urllib.parse.quote).
    404 «nessun frame: avviare l'anteprima o scattare» se non c'e' un frame tenuto; 409 «nessuna calibrazione attiva su questa sorgente»; 409 «la calibrazione «X» e' per immagini W×H, il frame e' w×h» se raddrizza restituisce None o la risoluzione non combacia; calibrazione planare senza mappa -> 409 «la calibrazione «X» non ha una mappa di correzione (planare)».
  6. POST /camera/calibrate: la pagina manda SOLO source (+ square_mm). Il frame e' sempre grezzo (gia' cosi': _grab non raddrizza). Il campo image resta accettato per compatibilita' ma la pagina non lo usa. origine = il nome della sorgente (anche per un'immagine caricata, es. "calibration_135_1.bmp").
    Tutto il resto di #91 (archivio unico, nomi, attive, PUT /camera/calibrazione-attiva col controllo risoluzione, GET /camera/calibration per il backend) resta invariato.

PAGINA (scripts/calibrazione_pagina.py, costante PAGINA, offline, niente alert/confirm/prompt)

  • Tab «Acquisizione» | «Calibrazione» come in #91.
  • Tab Acquisizione, dall'alto:
    a) Sorgente (larghezza piena, in cima): select «Camera o immagine» (camere, camera da cartella, immagini caricate); pulsante «Carica immagine da file...» (input file nascosto) che fa POST /camera/immagini, ricarica l'elenco e seleziona l'immagine caricata; «Rimuovi immagine» visibile solo se la sorgente selezionata e' un'immagine caricata (doppio clic, DELETE /camera/immagini). Stato: «calibrata: · <px/mm> px/mm» o «non calibrata».
    b) Barra: Avvia anteprima / Ferma / Scatta; casella «Mostra immagine calibrata»; casella «Mirino centrale». La vecchia casella «Immagine grezza» sparisce.
    c) Due riquadri affiancati (in colonna sotto i ~900 px): «Immagine» = sempre GREZZA (POST /camera/anteprima con grezzo:true); «Calibrata» visibile solo con la casella attiva = POST /camera/anteprima/calibrata subito dopo ogni anteprima grezza riuscita (stesso frame). Se la calibrata risponde 404/409 il riquadro mostra il messaggio al posto dell'immagine e l'anteprima grezza continua. Mirino su entrambi.
    d) Indicatori (nitidezza, saturati, livello medio, frame pieno) dal frame grezzo.
    e) Parametri camera come oggi (#87).
  • Tab Calibrazione: niente radio, niente file. Riga «Sorgente: — immagine grezza dalla tab Acquisizione». Lato scacco, «Calcola» (POST /camera/calibrate con source), risultato, nome + Salva (409 -> sovrascrivi con secondo clic), elenco a schede «Calibrazioni salvate» con Carica / Scollega / Elimina come in #91. Mentre la tab Calibrazione e' aperta il ciclo anteprima si sospende; tornando su Acquisizione riprende se era attivo. Durante «Calcola» niente richieste di anteprima in volo.
  • Dopo Carica/Scollega: stato sorgente e riquadro calibrata aggiornati.
ANALISI (passo 1, claude-opus-5-5) Correzione del disegno di gitea:Adriano/TieMeasureFlow#91 chiesta da Adriano: immagine da file come sorgente di acquisizione, calibrazione sempre dalla sorgente e su frame grezzo, vista grezza e calibrata affiancate, sorgente in cima. DUE PIANI A FILE DISGIUNTI (contratto sotto, identico in testa ai due prompt) A servizio: scripts/camera_service.py + test in scripts/tests (nuovo test_immagini_affiancate.py; aggiornare i test esistenti solo se cambia l'elenco sorgenti, es. camera:file). TEST A: upload ok/estensione no/file illeggibile (niente su disco); sources con immagini e senza camera:file; grab/anteprima/calibrate su immagine caricata; DELETE; anteprima tiene il frame grezzo; anteprima/calibrata 404 senza frame, 409 senza attiva, 409 risoluzione diversa, 200 con attiva densa (X-Raddrizzato si, X-Calibrazione) senza nuova lettura (sorgente finta che conta i grab). B pagina: scripts/calibrazione_pagina.py + scripts/tests/test_pagina_tab.py (aggiornare: niente origFile/origSorgente, niente input file nella calibrazione, presenza di /camera/immagini, /camera/anteprima/calibrata, casella mostra calibrata, sezione sorgente prima dell'anteprima). NON TOCCARE: scripts/calibrazione.py, src/, vendor/, packaging/. Comando test: uv run --python 3.13 --extra vision-worker --extra dev --with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests -q Prova d'insieme (passo 3, Opus): TMF da sorgente su daitarn, Playwright 1400 e 400 px: carica calibration_135_1.bmp come sorgente, calcola, salva, carica, affiancata grezza/calibrata; Itala 601020 con calibrazione di risoluzione diversa -> riquadro calibrata con messaggio, grezza viva. CONTRATTO #92 (identico per i due piani; repo TieMeasureFlow, base ramo lavoro/daitarn-tmf-dxfcal 8eb80e2, che contiene #91) Correzione di Adriano 24/10/2026 al disegno di #91: l'immagine da file e' una SORGENTE DI ACQUISIZIONE, non un'opzione della calibrazione. La calibrazione riceve SEMPRE l'immagine dalla sorgente selezionata nella tab Acquisizione, e sempre GREZZA (non calibrata). Nella tab Acquisizione si vede l'immagine grezza e, con un flag, accanto la stessa immagine calibrata, una a fianco all'altra. La sezione Sorgente sta in cima alla tab Acquisizione. SERVIZIO (scripts/camera_service.py; scripts/calibrazione.py non cambia) 1. Immagini caricate: cartella <config_dir>/immagini/ (creata al bisogno). POST /camera/immagini (multipart, campo image) -> salva col nome del file ripulito (caratteri sicuri come Archivio._file, estensione fra quelle di visionsuite.camera.sources._ESTENSIONI; se il nome c'e' gia' lo sostituisce) -> 201 {"source": "<nome file>", "label": "immagine — <nome file>", "kind": "image"}. 400 senza file o estensione non ammessa; 422 file non leggibile come immagine (cv2.imdecode None) -> in questo caso non resta nulla su disco. DELETE /camera/immagini?nome=<nome file> -> 204; 404 se non c'e'. Chiude la sorgente se aperta. 2. GET /camera/sources: camere fisiche + camera:folder + le immagini caricate (kind "image", name = nome file, label "immagine — <nome>"), in quest'ordine. camera:file (replay, mai configurato in TMF) NON compare piu'. 3. Apertura: _apri usa open_source(nome, <config_dir>/immagini, config=...) cosi' un nome di file si apre come sorgente immagine (visionsuite.camera.sources._apri_immagine). /camera/grab, /camera/anteprima, /camera/calibrate funzionano anche sulle immagini caricate. Una stazione puo' usare come camera_source il nome di un'immagine caricata. 4. POST /camera/anteprima: invariato nella forma; inoltre tiene in memoria, per sorgente, l'ULTIMO frame pieno GREZZO letto (prima di raddrizzare), sotto il lock gia' usato. 5. POST /camera/anteprima/calibrata json {source, lato_max=1280 (320..4096)} -> NON legge un frame nuovo: raddrizza con la mappa della calibrazione attiva l'ultimo frame grezzo tenuto da (4) e risponde JPEG q85 ridotto come /camera/anteprima, header X-Raddrizzato: si, X-Larghezza, X-Altezza (frame pieno raddrizzato), X-Calibrazione: <nome> (percent-encoded UTF-8, urllib.parse.quote). 404 «nessun frame: avviare l'anteprima o scattare» se non c'e' un frame tenuto; 409 «nessuna calibrazione attiva su questa sorgente»; 409 «la calibrazione «X» e' per immagini W×H, il frame e' w×h» se raddrizza restituisce None o la risoluzione non combacia; calibrazione planare senza mappa -> 409 «la calibrazione «X» non ha una mappa di correzione (planare)». 6. POST /camera/calibrate: la pagina manda SOLO source (+ square_mm). Il frame e' sempre grezzo (gia' cosi': _grab non raddrizza). Il campo image resta accettato per compatibilita' ma la pagina non lo usa. origine = il nome della sorgente (anche per un'immagine caricata, es. "calibration_135_1.bmp"). Tutto il resto di #91 (archivio unico, nomi, attive, PUT /camera/calibrazione-attiva col controllo risoluzione, GET /camera/calibration per il backend) resta invariato. PAGINA (scripts/calibrazione_pagina.py, costante PAGINA, offline, niente alert/confirm/prompt) - Tab «Acquisizione» | «Calibrazione» come in #91. - Tab Acquisizione, dall'alto: a) Sorgente (larghezza piena, in cima): select «Camera o immagine» (camere, camera da cartella, immagini caricate); pulsante «Carica immagine da file...» (input file nascosto) che fa POST /camera/immagini, ricarica l'elenco e seleziona l'immagine caricata; «Rimuovi immagine» visibile solo se la sorgente selezionata e' un'immagine caricata (doppio clic, DELETE /camera/immagini). Stato: «calibrata: <nome> · <px/mm> px/mm» o «non calibrata». b) Barra: Avvia anteprima / Ferma / Scatta; casella «Mostra immagine calibrata»; casella «Mirino centrale». La vecchia casella «Immagine grezza» sparisce. c) Due riquadri affiancati (in colonna sotto i ~900 px): «Immagine» = sempre GREZZA (POST /camera/anteprima con grezzo:true); «Calibrata» visibile solo con la casella attiva = POST /camera/anteprima/calibrata subito dopo ogni anteprima grezza riuscita (stesso frame). Se la calibrata risponde 404/409 il riquadro mostra il messaggio al posto dell'immagine e l'anteprima grezza continua. Mirino su entrambi. d) Indicatori (nitidezza, saturati, livello medio, frame pieno) dal frame grezzo. e) Parametri camera come oggi (#87). - Tab Calibrazione: niente radio, niente file. Riga «Sorgente: <etichetta> — immagine grezza dalla tab Acquisizione». Lato scacco, «Calcola» (POST /camera/calibrate con source), risultato, nome + Salva (409 -> sovrascrivi con secondo clic), elenco a schede «Calibrazioni salvate» con Carica / Scollega / Elimina come in #91. Mentre la tab Calibrazione e' aperta il ciclo anteprima si sospende; tornando su Acquisizione riprende se era attivo. Durante «Calcola» niente richieste di anteprima in volo. - Dopo Carica/Scollega: stato sorgente e riquadro calibrata aggiornati.
Adriano added the sviluppo label 2026-10-24 14:22:36 +00:00
Author
Owner

Soluzione

ESITO: fatto
RIF: gitea:Adriano/TieMeasureFlow#92, ramo lavoro/daitarn-tmf-dxfcal
COSA: immagine da file = sorgente di acquisizione; calibrazione sempre sul grezzo della sorgente; grezza e calibrata affiancate (casella); Sorgente in cima alla tab Acquisizione.
FILE: scripts/camera_service.py — POST/DELETE /camera/immagini (cartella acquisitore/immagini), immagini in /camera/sources, camera:file tolta, ultimo grezzo per sorgente, POST /camera/anteprima/calibrata (404/409), cache camere 60 s + ricerca all'avvio, ?aggiorna=true (632fd51, 9c45a51)
FILE: scripts/calibrazione_pagina.py — Sorgente in cima con Carica/Rimuovi immagine e Aggiorna, riquadri affiancati, tab Calibrazione senza file, calibrata chiesta solo con calibrazione attiva e non dopo un 409 (f504c86, 9c45a51)
PROVA: pytest scripts/tests → 151 passed; curl /camera/sources → 2 ms (prima 10 s); Playwright Edge su TMF da sorgente (Itala 601020 + calibration_135_1.bmp): upload→sorgente, calcolo/salva/carica, affiancate, Itala senza calibrazione 47 grezze e 0 calibrate in 5 s, tab Calibrazione sospende (0 in 3 s), 400 px senza scorrimento.
MANCA: merge su V3.0.0 con #83-#85; build e installazione sul PC fiera.

**Soluzione** ESITO: fatto RIF: gitea:Adriano/TieMeasureFlow#92, ramo lavoro/daitarn-tmf-dxfcal COSA: immagine da file = sorgente di acquisizione; calibrazione sempre sul grezzo della sorgente; grezza e calibrata affiancate (casella); Sorgente in cima alla tab Acquisizione. FILE: scripts/camera_service.py — POST/DELETE /camera/immagini (cartella acquisitore/immagini), immagini in /camera/sources, camera:file tolta, ultimo grezzo per sorgente, POST /camera/anteprima/calibrata (404/409), cache camere 60 s + ricerca all'avvio, ?aggiorna=true (632fd51, 9c45a51) FILE: scripts/calibrazione_pagina.py — Sorgente in cima con Carica/Rimuovi immagine e Aggiorna, riquadri affiancati, tab Calibrazione senza file, calibrata chiesta solo con calibrazione attiva e non dopo un 409 (f504c86, 9c45a51) PROVA: pytest scripts/tests → 151 passed; curl /camera/sources → 2 ms (prima 10 s); Playwright Edge su TMF da sorgente (Itala 601020 + calibration_135_1.bmp): upload→sorgente, calcolo/salva/carica, affiancate, Itala senza calibrazione 47 grezze e 0 calibrate in 5 s, tab Calibrazione sospende (0 in 3 s), 400 px senza scorrimento. MANCA: merge su V3.0.0 con #83-#85; build e installazione sul PC fiera.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/TieMeasureFlow#92