Acquisizione: specchiatura orizzontale e verticale per sorgente, memorizzata #110

Closed
opened 2026-10-28 09:32:00 +00:00 by Adriano · 1 comment
Owner

RICHIESTA: Adriano 28/10 (fiera A&T): «nell'acquisizione bisogna mettere flip orizzontale, verticale che poi sia memorizzato».

COSA: due caselle nella pagina Acquisizione (scripts/calibrazione_pagina.py, servita da scripts/camera_service.py su :8765/acquisizione): «Specchia orizzontale», «Specchia verticale». Valgono PER SORGENTE, si salvano subito su disco al cambio, si ricaricano al riavvio del servizio. La specchiatura si applica in software (cv2.flip) a OGNI frame letto dalla sorgente, PRIMA di raddrizzatura/calibrazione: anteprima, /camera/grab (quindi misure e wizard DXF del TMF, che passano dal grab), calibrazione da camera, copia bloccata. Ambo le caselle = rotazione 180°.

PERCHÉ SOFTWARE e non ReverseX/ReverseY GenICam: vale per ogni sorgente (GenTL, OpenCV, camera:folder, immagini caricate) e non dipende dai nodi del producer Itala.

RAMO: lavoro/daitarn-tmf-flip da lavoro/daitarn-tmf-dxfcal b2e3867 (è il codice sul PC fiera DESKTOP-RABEHJJ).

MODIFICHE:

  1. scripts/camera_service.py, create_app():
    • stato orientamenti: dict {sorgente: {"orizzontale": bool, "verticale": bool}} caricato all'avvio da /orientamento.json (archivio.cartella / "orientamento.json"); file assente o rotto → {} con _log.warning, mai eccezione.
    • helper _specchia(nome, frame): legge l'orientamento della sorgente sotto lock; orizzontale+verticale → cv2.flip(frame, -1); solo orizzontale → cv2.flip(frame, 1); solo verticale → cv2.flip(frame, 0); nessuno → frame invariato (stesso oggetto, niente copia).
    • applicarlo nei DUE soli punti che leggono dalla sorgente: _grab (riga ~320: lambda s: _specchia(nome, s.grab(timeout))) e leggi in camera_anteprima (riga ~547: specchiare PRIMA di grezzi[nome] = f.copy(), così /camera/blocca, /camera/anteprima/calibrata e calibrate bloccata ricevono il frame già specchiato). NON specchiare l'immagine caricata a mano in /camera/calibrate (campo image, origine file:).
    • GET /camera/orientamento?source=S → 200 {source, orizzontale, verticale} (default false/false); 400 se manca source.
    • PUT /camera/orientamento json {source, orizzontale: bool, verticale: bool} → 200 come GET + "avviso": stringa o null. 400 se source mancante o i due campi non bool. Scrittura atomica (tmp + os.replace) di tutto il dict; tutti false → la sorgente si toglie dal file. Dopo il cambio: with lock: grezzi.pop(nome, None); bloccati.pop(nome, None) (frame vecchi con l'orientamento di prima).
    • avviso: se archivio.leggi_mappa(nome) non è None e l'orientamento è cambiato → "la calibrazione attiva «<archivio.attiva_di(nome)>» corregge la distorsione con l'orientamento di prima: va rifatta". Mappa assente (manuale/planare: solo scala) → avviso null.
    • docstring di testa: aggiungere le due rotte nel blocco «Acquisizione (#87)» e la riga del file orientamento.json accanto a quella delle calibrazioni.
  2. scripts/calibrazione_pagina.py, sezione «Parametri camera» (riga ~220): sotto #parametri, una riga con le due caselle (id specchiaO, specchiaV) e un <div class="nota" id="msgSpecchia">. JS: caricaOrientamento() chiamata in cambioSorgente() accanto a caricaParametri() e al primo caricamento della sorgente; onchange di ciascuna casella → PUT, in caso di errore rimettere la casella com'era e mostrare dettaglio(r); se c'è avviso mostrarlo in msgSpecchia (classe di errore/avviso esistente); se l'anteprima è attiva il prossimo frame esce già specchiato (niente da fare), se ferma chiamare unaAnteprima(A.lato) una volta per far vedere l'effetto. Seguire lo stile esistente (var, function, fetch, $, dire).

TEST (scripts/tests/test_camera_orientamento.py, nuovo, stesso schema di test_camera_acquisizione.py: modulo caricato con importlib, TestClient, sorgente finta da cartella con un'immagine asimmetrica nota, es. 4x3 con un pixel acceso in alto a sinistra):

  • GET senza file → false/false.
  • PUT orizzontale → /camera/grab PNG ha il pixel in alto a destra; verticale → in basso a sinistra; entrambi → in basso a destra; tutti false → originale.
  • /camera/anteprima con lato_max grande (niente riduzione) → stesso effetto; X-Larghezza/X-Altezza invariati.
  • persistenza: PUT, poi create_app NUOVA sulla stessa config-dir → GET restituisce il valore e il grab è specchiato.
  • file orientamento.json rotto → create_app parte, GET false/false.
  • PUT con campo non bool → 400; senza source → 400.
  • dopo PUT, POST /camera/blocca → 404 (frame tenuti scartati).
  • avviso: con mappa attiva (usare come gli altri test calibrazione densa, oppure monkeypatch archivio.leggi_mappa → array) PUT che cambia → avviso non null; senza mappa → null.
  • test_pagina_acquisizione.py: la pagina contiene specchiaO, specchiaV e chiama /camera/orientamento.
    Comando: uv run --python 3.13 --extra vision-worker --extra dev --with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests -q → tutto verde, nessun test esistente toccato.

NON TOCCARE: vendor/visionsuite (submodule), src/ backend/frontend TMF, scripts/calibrazione.py, il formato di attive.json e dei settaggi, il comportamento di /camera/calibrate con image caricata, gli header esistenti delle risposte.

FATTO QUANDO: test verdi; sul PC fiera, caselle attivate → anteprima e grab specchiati, riavvio TMF → caselle e specchiatura restano.

RICHIESTA: Adriano 28/10 (fiera A&T): «nell'acquisizione bisogna mettere flip orizzontale, verticale che poi sia memorizzato». COSA: due caselle nella pagina Acquisizione (scripts/calibrazione_pagina.py, servita da scripts/camera_service.py su :8765/acquisizione): «Specchia orizzontale», «Specchia verticale». Valgono PER SORGENTE, si salvano subito su disco al cambio, si ricaricano al riavvio del servizio. La specchiatura si applica in software (cv2.flip) a OGNI frame letto dalla sorgente, PRIMA di raddrizzatura/calibrazione: anteprima, /camera/grab (quindi misure e wizard DXF del TMF, che passano dal grab), calibrazione da camera, copia bloccata. Ambo le caselle = rotazione 180°. PERCHÉ SOFTWARE e non ReverseX/ReverseY GenICam: vale per ogni sorgente (GenTL, OpenCV, camera:folder, immagini caricate) e non dipende dai nodi del producer Itala. RAMO: lavoro/daitarn-tmf-flip da lavoro/daitarn-tmf-dxfcal b2e3867 (è il codice sul PC fiera DESKTOP-RABEHJJ). MODIFICHE: 1. scripts/camera_service.py, create_app(): - stato `orientamenti: dict` {sorgente: {"orizzontale": bool, "verticale": bool}} caricato all'avvio da <config-dir>/orientamento.json (archivio.cartella / "orientamento.json"); file assente o rotto → {} con _log.warning, mai eccezione. - helper `_specchia(nome, frame)`: legge l'orientamento della sorgente sotto `lock`; orizzontale+verticale → cv2.flip(frame, -1); solo orizzontale → cv2.flip(frame, 1); solo verticale → cv2.flip(frame, 0); nessuno → frame invariato (stesso oggetto, niente copia). - applicarlo nei DUE soli punti che leggono dalla sorgente: `_grab` (riga ~320: `lambda s: _specchia(nome, s.grab(timeout))`) e `leggi` in camera_anteprima (riga ~547: specchiare PRIMA di `grezzi[nome] = f.copy()`, così /camera/blocca, /camera/anteprima/calibrata e calibrate bloccata ricevono il frame già specchiato). NON specchiare l'immagine caricata a mano in /camera/calibrate (campo `image`, origine file:). - GET /camera/orientamento?source=S → 200 {source, orizzontale, verticale} (default false/false); 400 se manca source. - PUT /camera/orientamento json {source, orizzontale: bool, verticale: bool} → 200 come GET + "avviso": stringa o null. 400 se source mancante o i due campi non bool. Scrittura atomica (tmp + os.replace) di tutto il dict; tutti false → la sorgente si toglie dal file. Dopo il cambio: `with lock: grezzi.pop(nome, None); bloccati.pop(nome, None)` (frame vecchi con l'orientamento di prima). - avviso: se `archivio.leggi_mappa(nome)` non è None e l'orientamento è cambiato → "la calibrazione attiva «<archivio.attiva_di(nome)>» corregge la distorsione con l'orientamento di prima: va rifatta". Mappa assente (manuale/planare: solo scala) → avviso null. - docstring di testa: aggiungere le due rotte nel blocco «Acquisizione (#87)» e la riga del file orientamento.json accanto a quella delle calibrazioni. 2. scripts/calibrazione_pagina.py, sezione «Parametri camera» (riga ~220): sotto `#parametri`, una riga con le due caselle (id `specchiaO`, `specchiaV`) e un `<div class="nota" id="msgSpecchia">`. JS: `caricaOrientamento()` chiamata in `cambioSorgente()` accanto a `caricaParametri()` e al primo caricamento della sorgente; onchange di ciascuna casella → PUT, in caso di errore rimettere la casella com'era e mostrare `dettaglio(r)`; se c'è `avviso` mostrarlo in msgSpecchia (classe di errore/avviso esistente); se l'anteprima è attiva il prossimo frame esce già specchiato (niente da fare), se ferma chiamare `unaAnteprima(A.lato)` una volta per far vedere l'effetto. Seguire lo stile esistente (var, function, fetch, `$`, `dire`). TEST (scripts/tests/test_camera_orientamento.py, nuovo, stesso schema di test_camera_acquisizione.py: modulo caricato con importlib, TestClient, sorgente finta da cartella con un'immagine asimmetrica nota, es. 4x3 con un pixel acceso in alto a sinistra): - GET senza file → false/false. - PUT orizzontale → /camera/grab PNG ha il pixel in alto a destra; verticale → in basso a sinistra; entrambi → in basso a destra; tutti false → originale. - /camera/anteprima con lato_max grande (niente riduzione) → stesso effetto; X-Larghezza/X-Altezza invariati. - persistenza: PUT, poi create_app NUOVA sulla stessa config-dir → GET restituisce il valore e il grab è specchiato. - file orientamento.json rotto → create_app parte, GET false/false. - PUT con campo non bool → 400; senza source → 400. - dopo PUT, POST /camera/blocca → 404 (frame tenuti scartati). - avviso: con mappa attiva (usare come gli altri test calibrazione densa, oppure monkeypatch archivio.leggi_mappa → array) PUT che cambia → avviso non null; senza mappa → null. - test_pagina_acquisizione.py: la pagina contiene specchiaO, specchiaV e chiama /camera/orientamento. Comando: uv run --python 3.13 --extra vision-worker --extra dev --with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests -q → tutto verde, nessun test esistente toccato. NON TOCCARE: vendor/visionsuite (submodule), src/ backend/frontend TMF, scripts/calibrazione.py, il formato di attive.json e dei settaggi, il comportamento di /camera/calibrate con `image` caricata, gli header esistenti delle risposte. FATTO QUANDO: test verdi; sul PC fiera, caselle attivate → anteprima e grab specchiati, riavvio TMF → caselle e specchiatura restano.
Adriano added the sviluppo label 2026-10-28 09:32:00 +00:00
Author
Owner

Soluzione

ESITO: fatto
COSA: specchiatura orizzontale/verticale per sorgente in Acquisizione, memorizzata in /acquisitore/orientamento.json, applicata in anteprima e grab.
FILE: scripts/camera_service.py — _carica_orientamenti, _specchia, GET/PUT /camera/orientamento (scrittura atomica, ripristino in memoria se il salvataggio fallisce, avviso se esiste una mappa di calibrazione) (2c2d601, 9111eb2)
FILE: scripts/calibrazione_pagina.py — caselle specchiaO/specchiaV, caricaOrientamento/salvaOrientamento (155abfd)
FILE: scripts/tests/test_camera_orientamento.py (15 test), scripts/tests/test_pagina_acquisizione.py
RAMO: lavoro/daitarn-tmf-dxfcal @ 34c251a (pushato)
PROVA: pytest scripts/tests (file camera) → 90 passed; browser locale camera:folder → salva/azzera senza errori console
DEPLOY: DESKTOP-RABEHJJ (PC fiera A&T) 28/10/2026, hash file verificati, porte 8000/8100/8765/8780 su in 30 s, orientamento.json = {}

**Soluzione** ESITO: fatto COSA: specchiatura orizzontale/verticale per sorgente in Acquisizione, memorizzata in <config-dir>/acquisitore/orientamento.json, applicata in anteprima e grab. FILE: scripts/camera_service.py — _carica_orientamenti, _specchia, GET/PUT /camera/orientamento (scrittura atomica, ripristino in memoria se il salvataggio fallisce, avviso se esiste una mappa di calibrazione) (2c2d601, 9111eb2) FILE: scripts/calibrazione_pagina.py — caselle specchiaO/specchiaV, caricaOrientamento/salvaOrientamento (155abfd) FILE: scripts/tests/test_camera_orientamento.py (15 test), scripts/tests/test_pagina_acquisizione.py RAMO: lavoro/daitarn-tmf-dxfcal @ 34c251a (pushato) PROVA: pytest scripts/tests (file camera) → 90 passed; browser locale camera:folder → salva/azzera senza errori console DEPLOY: DESKTOP-RABEHJJ (PC fiera A&T) 28/10/2026, hash file verificati, porte 8000/8100/8765/8780 su in 30 s, orientamento.json = {}
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/TieMeasureFlow#110