Calibrazione da una foto di scacchiera densa: distorsione e mm/px #84
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
COSA: l'acquisitore (scripts/camera_service.py, scripts/calibrazione.py) calibra da UNA foto di scacchiera densa che riempie tutto il campo visivo; l'operatore dà solo il lato dello scacco in mm. Si ricava la mappa di distorsione (visionsuite.calibration.DistortionMap, adatta alle telecentriche) e i mm/px; le immagini date dall'acquisitore escono raddrizzate.
PERCHÉ: Adriano 23/10 sera, fiera A&T Vicenza 27-29/10: banco TC (ottica telecentrica) con camera Itala; demo DXF compare buono/scarto (call Menoncin 23/10).
CRITERIO DI FATTO: foto sintetica di scacchiera distorta che riempie il FOV → griglia trovata senza dare cols/rows, mm/px entro 0,5%, immagine raddrizzata con scarto residuo < 0,3 px; grab di una sorgente calibrata esce raddrizzato; test verdi.
RIF: estende gitea:Adriano/TieMeasureFlow#83.
🔵 daitarn — preso in carico 2026-10-23T21:44:41Z
ramo:
lavoro/daitarn· capacità: build-windows, gpuANALISI (Opus, claude-opus-5-5) — gitea:Adriano/TieMeasureFlow#84
RAMO: lavoro/daitarn-tmf-calibdensa (da origin/lavoro/daitarn-tmf-dxfcal-worker
351739e), worktree C:/Users/Adriano/lavoro/TieMeasureFlow-calibdensaCOSA: l'acquisitore calibra da UNA foto di scacchiera densa che riempie tutto il FOV. Input operatore: solo square_mm. Uscita: mappa di distorsione + mm/px; i frame dell'acquisitore escono raddrizzati quando la sorgente è calibrata. Ottica telecentrica (banco TC Opto + camera Itala ITA32/ITA50 mono, 2064x1544 / 5 MP).
MODIFICHE
calibra_scacchiera_densa(pixels, square_mm):a. corner senza conoscere la griglia:
cv2.findChessboardCornersSBWithMeta(gray, (min_c, min_r), flags=CALIB_CB_LARGER | CALIB_CB_EXHAUSTIVE | CALIB_CB_ACCURACY | CALIB_CB_NORMALIZE_IMAGE)con pattern minimo piccolo (es. 5x5);metadà la dimensione reale della griglia trovata. Ordinare i punti in ordine di riga (n_rows x n_cols), righe dall'alto, colonne da sinistra. Se SBWithMeta non è disponibile nella versione di opencv del venv, dirlo nel rapporto e ripiegare su una ricerca di griglia propria (nonfindChessboardCornerscon dimensione fissa). Verificare la versione:uv run --python 3.13 --extra vision-worker --extra dev --with-editable vendor/visionsuite/packages/vs-camera python -c "import cv2; print(cv2.__version__, hasattr(cv2,'findChessboardCornersSBWithMeta'))".b. passo medio in px fra corner adiacenti →
grid_step = round(passo).c.
visionsuite.calibration.distortion_map.DistortionMap.from_grid(x, y, n_cols, n_rows, image_size=(w,h), grid_step, crop_domain=False, grid_step_measure=square_mm)→pixel_size= mm/px dell'immagine raddrizzata. Leggere tuttodistortion_map.pyprima (convenzioni map_col/map_row = cv2.remap, salvataggio/caricamento se esiste; se non esiste salvare map_col/map_row in .npz).d. qualità: residuo = distanza dei corner raddrizzati (rimappati con la mappa inversa o riproiettati) dalla griglia ideale; riportare
residuo_max_px,residuo_rms_px,n_corners,n_cols,n_rows,copertura_pct(area del quadrilatero dei corner esterni / area immagine).e. overlay PNG b64: corner trovati sull'immagine originale.
Archivio: oltre al JSON, salva la mappa per sorgente (<stesso nome>.npz), scrittura atomica come il JSON;leggi_mappa(sorgente)con cache in memoria invalidata da mtime;eliminatoglie anche la mappa.calibra_scacchiera(pixels, cols, rows, square_mm)resta (compatibilità C2), ma la pagina e il default usano la densa.POST /camera/calibrate:cols/rowsdiventano opzionali. Senza → densa (default). Con → vecchio metodo planare. Il frame per calibrare è SEMPRE grezzo (non raddrizzato). Campi di C1 restano tutti presenti (px_per_mm = 1/pixel_size, mm_per_px = pixel_size, rms_px = residuo_rms_px, n_corners, cols=n_cols, rows=n_rows, square_mm, image_width, image_height, scale_spread_pct calcolato come oggi o 0 se la mappa lo rende uniforme, calibrated_at) + nuovimetodo: "densa"|"planare",distorsione: true|false,residuo_max_px,copertura_pct.POST /camera/grab: se la sorgente ha una mappa salvata, il frame esce raddrizzato (cv2.remap, INTER_LINEAR). Nuovo campo json opzionalegrezzo: trueper avere il frame non corretto. Il raddrizzamento non deve rallentare il grab oltre ~50 ms su 5 MP: mappe float32 precalcolate (cv2.convertMapsa CV_16SC2 se serve) e cache.grezzo:trueno; C2 senza cols/rows → metodo densa; vecchio C2 con cols/rows → planare invariato; DELETE toglie anche .npz.TEST:
uv run --python 3.13 --extra vision-worker --extra dev --with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests→ tutti verdi.uv run --python 3.13 --extra vision --extra vision-worker --extra dev pytest src/vision/tests src/vision_worker/tests→ invariati (43).NON TOCCARE: src/** (backend, frontend, worker, vision), vendor/visionsuite (submodule: se serve una modifica lì, fermarsi e dirlo), packaging/**. I nomi delle rotte e dei campi esistenti di C1-C4.
🤖 goldrake — liberata nessun segno di daitarn da 6.0 h — 2026-10-24T03:44:58Z
Soluzione
ESITO: fatto (chiusa su richiesta di Adriano del 06/11/2026)
RIF: gitea:Adriano/TieMeasureFlow#84 (estende #83)
COSA: l'acquisitore calibra da una sola foto di scacchiera densa: l'operatore dà solo il lato dello scacco; mappa di distorsione e mm/px; i frame delle sorgenti calibrate escono raddrizzati.
VERIFICATO su origin/V3.0.0
8c361c1: scripts/calibrazione.py:377 calibra_scacchiera_densa(pixels, square_mm) → (esito, DistortionMap); test in scripts/tests/test_calibrazione.py, test_calibrazioni_archivio.py, test_calibrazioni_rotte.py, test_calibrazione_manuale.py.USO: in fiera dal 24-25/10 (ramo lavoro/daitarn-tmf-dxfcal, unito il 24/10; scheda AI-OS TieMeasureFlow, voce 24/10); le ricette della fiera portano la scala misurata (CalADP 89,63 px/mm sulla FIERA-DXF-COPIA-2).
NOTA: il segno di presa di daitarn era stato liberato da goldrake il 24/10 03:44 per mancanza di battito; il lavoro è stato completato e unito comunque.