a5e8472e88
/camera/calibrate non salva piu' (calcolo_id in memoria, ultimi 8); nuove rotte GET/POST/DELETE /camera/calibrazioni e PUT /camera/calibrazione-attiva (409 se la risoluzione non combacia col frame della sorgente); DELETE /camera/calibration ora scollega. Test: rotte nuove e test esistenti aggiornati (save=true -> salva con nome). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jbf7qKuxsnAVjxthRy7dnB
423 lines
19 KiB
Python
423 lines
19 KiB
Python
"""Test della calibrazione dell'acquisitore (#83): logica pura e rotte di camera_service.
|
|
|
|
Uso: dalla radice, `uv run --python 3.13 --extra vision-worker --extra dev
|
|
--with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests -q`
|
|
(o il venv di VisionSuite come per test_camera_service.py).
|
|
"""
|
|
import base64
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import cv2
|
|
import numpy as np
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
SCRIPTS = Path(__file__).resolve().parents[1]
|
|
MODULO = SCRIPTS / "camera_service.py"
|
|
|
|
|
|
def _carica():
|
|
spec = importlib.util.spec_from_file_location("camera_service", MODULO)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
def una_scacchiera(square_px: int = 40, nx: int = 10, ny: int = 7, margin: int = 60) -> np.ndarray:
|
|
"""nx x ny quadrati => (nx-1) x (ny-1) angoli interni, su fondo bianco."""
|
|
img = np.full((ny * square_px + 2 * margin, nx * square_px + 2 * margin), 255, np.uint8)
|
|
for j in range(ny):
|
|
for i in range(nx):
|
|
if (i + j) % 2 == 0:
|
|
y, x = margin + j * square_px, margin + i * square_px
|
|
img[y:y + square_px, x:x + square_px] = 0
|
|
return img
|
|
|
|
|
|
def una_scacchiera_densa(w: int = 640, h: int = 480, square_px: int = 40, k: float = 1e-8) -> np.ndarray:
|
|
"""#84: scacchiera che riempie tutto il campo, con distorsione a barilotto (k sui px dal centro)."""
|
|
m = 3 * square_px # la scacchiera continua oltre il campo: nessun bordo finto nell'immagine
|
|
yy, xx = np.mgrid[0:h + 2 * m, 0:w + 2 * m]
|
|
piana = (((xx // square_px) + (yy // square_px)) % 2 * 255).astype(np.uint8)
|
|
cx, cy = w / 2.0, h / 2.0
|
|
dx, dy = (xx[m:m + h, m:m + w] - m - cx).astype(np.float32), (yy[m:m + h, m:m + w] - m - cy).astype(np.float32)
|
|
f = 1 + k * (dx * dx + dy * dy)
|
|
img = cv2.remap(piana, cx + m + dx * f, cy + m + dy * f, cv2.INTER_LINEAR)
|
|
return cv2.GaussianBlur(img, (3, 3), 0.8) # bordi non perfettamente netti, come in camera
|
|
|
|
|
|
def residuo_reticolo(griglia: np.ndarray) -> float:
|
|
"""Residuo massimo (px) della griglia (nr,nc,2) da un reticolo affine regolare."""
|
|
nr, nc, _ = griglia.shape
|
|
ii, jj = np.mgrid[0:nr, 0:nc]
|
|
A = np.stack([np.ones(nr * nc), ii.ravel(), jj.ravel()], axis=1)
|
|
P = griglia.reshape(-1, 2)
|
|
coef, *_ = np.linalg.lstsq(A, P, rcond=None)
|
|
return float(np.hypot(*(A @ coef - P).T).max())
|
|
|
|
|
|
def _png(img: np.ndarray) -> bytes:
|
|
ok, buf = cv2.imencode(".png", img)
|
|
assert ok
|
|
return buf.tobytes()
|
|
|
|
|
|
@pytest.fixture
|
|
def cartella(tmp_path):
|
|
# la sorgente camera:folder fa il loop su una scacchiera: il grab la trova
|
|
cartella = tmp_path / "immagini"
|
|
cartella.mkdir()
|
|
cv2.imwrite(str(cartella / "scacchiera.png"), una_scacchiera())
|
|
return cartella
|
|
|
|
|
|
@pytest.fixture
|
|
def client(cartella, tmp_path):
|
|
mod = _carica()
|
|
with TestClient(mod.create_app(cartella, config_dir=tmp_path / "config")) as c:
|
|
c.config = tmp_path / "config"
|
|
yield c
|
|
|
|
|
|
CAMPI = {"cols": "9", "rows": "6", "square_mm": "2"}
|
|
CHIAVI_C1 = {"px_per_mm", "mm_per_px", "rms_px", "n_corners", "cols", "rows",
|
|
"square_mm", "image_width", "image_height", "scale_spread_pct", "calibrated_at"}
|
|
# #84: campi in piu' per metodo planare (C2 con cols/rows) e per la densa
|
|
CHIAVI_PLANARE = CHIAVI_C1 | {"metodo", "distorsione", "origine"} # #91: + origine ("camera:X" | "file:Y")
|
|
CHIAVI_DENSA = CHIAVI_PLANARE | {"residuo_rms_px", "residuo_max_px", "copertura_pct"}
|
|
SORGENTE = "camera:folder"
|
|
# #91: GET /camera/calibration = voce di libreria + "nome" + "source" (la sorgente che la usa)
|
|
CHIAVI_ATTIVA = {"source", "nome"}
|
|
|
|
|
|
def salva_con_nome(client, calcolo: dict, nome: str, **extra):
|
|
"""#91: POST /camera/calibrazioni sul calcolo_id di una risposta di /camera/calibrate."""
|
|
return client.post("/camera/calibrazioni", json={"calcolo_id": calcolo["calcolo_id"], "nome": nome, **extra})
|
|
|
|
|
|
def carica_su(client, sorgente: str, nome: str):
|
|
"""#91: PUT /camera/calibrazione-attiva."""
|
|
return client.put("/camera/calibrazione-attiva", json={"source": sorgente, "nome": nome})
|
|
|
|
|
|
@pytest.fixture
|
|
def cartella_densa(tmp_path):
|
|
# #84: la sorgente camera:folder fa il loop su una scacchiera densa distorta
|
|
cartella = tmp_path / "immagini_dense"
|
|
cartella.mkdir()
|
|
cv2.imwrite(str(cartella / "densa.png"), una_scacchiera_densa(k=1e-7))
|
|
return cartella
|
|
|
|
|
|
@pytest.fixture
|
|
def client_denso(cartella_densa, tmp_path):
|
|
mod = _carica()
|
|
with TestClient(mod.create_app(cartella_densa, config_dir=tmp_path / "config_densa")) as c:
|
|
c.config = tmp_path / "config_densa"
|
|
yield c
|
|
|
|
|
|
NON_IMMAGINE = b"questo non e una immagine"
|
|
|
|
|
|
# ---- logica pura
|
|
|
|
def test_scacchiera_sintetica_da_la_scala():
|
|
_carica() # mette scripts/ in sys.path
|
|
import calibrazione
|
|
r = calibrazione.calibra_scacchiera(una_scacchiera(), 9, 6, 2.0)
|
|
assert r["px_per_mm"] == pytest.approx(20.0, rel=0.005)
|
|
assert r["mm_per_px"] == pytest.approx(0.05, rel=0.005)
|
|
assert r["rms_px"] < 0.5
|
|
assert r["n_corners"] == 54
|
|
assert (r["image_width"], r["image_height"]) == (520, 400)
|
|
assert r["scale_spread_pct"] < 1.0
|
|
assert base64.b64decode(r["overlay_png"])[:8] == b"\x89PNG\r\n\x1a\n"
|
|
|
|
|
|
def test_immagine_bianca_solleva():
|
|
_carica()
|
|
import calibrazione
|
|
with pytest.raises(calibrazione.CalibrazioneError, match="non trovata"):
|
|
calibrazione.calibra_scacchiera(np.full((300, 300), 255, np.uint8), 9, 6, 2.0)
|
|
|
|
|
|
@pytest.mark.parametrize("cols,rows,quadrato", [(1, 6, 2.0), (9, 6, 0.0), (9, 6, -1.0)])
|
|
def test_parametri_invalidi_sollevano(cols, rows, quadrato):
|
|
_carica()
|
|
import calibrazione
|
|
with pytest.raises(calibrazione.CalibrazioneError):
|
|
calibrazione.calibra_scacchiera(una_scacchiera(), cols, rows, quadrato)
|
|
|
|
|
|
# ---- rotte
|
|
|
|
def test_c1_non_calibrata_404(client):
|
|
r = client.get("/camera/calibration", params={"source": "camera:folder"})
|
|
assert r.status_code == 404
|
|
assert r.json()["detail"] == "sorgente non calibrata"
|
|
|
|
|
|
def test_c1_senza_source_400(client):
|
|
assert client.get("/camera/calibration").status_code == 400
|
|
|
|
|
|
def test_c2_immagine_caricata_senza_salvare(client):
|
|
r = client.post("/camera/calibrate", data={"source": "camera:folder", **CAMPI},
|
|
files={"image": ("s.png", _png(una_scacchiera()), "image/png")})
|
|
assert r.status_code == 200, r.text
|
|
corpo = r.json()
|
|
assert CHIAVI_PLANARE | {"overlay_png", "calcolo_id"} == set(corpo) # #91: niente "saved", niente "source"
|
|
assert corpo["metodo"] == "planare" and corpo["distorsione"] is False # #84: con cols/rows nulla cambia
|
|
assert corpo["origine"] == "file:s.png"
|
|
assert corpo["px_per_mm"] == pytest.approx(20.0, rel=0.005)
|
|
# il calcolo non salva mai
|
|
assert client.get("/camera/calibration", params={"source": "camera:folder"}).status_code == 404
|
|
assert not client.config.exists() or not list(client.config.iterdir())
|
|
|
|
|
|
def test_c2_save_true_viene_ignorato(client):
|
|
# #91: il vecchio campo save non salva piu' nulla
|
|
r = client.post("/camera/calibrate", data={"source": "camera:folder", "save": "true", **CAMPI})
|
|
assert r.status_code == 200, r.text
|
|
assert client.get("/camera/calibration", params={"source": "camera:folder"}).status_code == 404
|
|
assert client.get("/camera/calibrazioni").json()["calibrazioni"] == []
|
|
assert not client.config.exists() or not list(client.config.iterdir())
|
|
|
|
|
|
def test_c2_c1_c3_ciclo_con_grab_dalla_sorgente(client):
|
|
# nessun file: grab da camera:folder (la scacchiera in cartella)
|
|
r = client.post("/camera/calibrate", data={"source": "camera:folder", **CAMPI})
|
|
assert r.status_code == 200, r.text
|
|
assert r.json()["origine"] == "camera:folder"
|
|
assert salva_con_nome(client, r.json(), "Banco").status_code == 201 # #91: si salva con nome, dopo
|
|
|
|
# salvata ma non ancora caricata sulla sorgente
|
|
assert client.get("/camera/calibration", params={"source": "camera:folder"}).status_code == 404
|
|
assert carica_su(client, "camera:folder", "Banco").status_code == 200
|
|
|
|
r1 = client.get("/camera/calibration", params={"source": "camera:folder"})
|
|
assert r1.status_code == 200
|
|
assert set(r1.json()) == CHIAVI_PLANARE | CHIAVI_ATTIVA # nessun overlay nel file salvato
|
|
assert r1.json()["px_per_mm"] == pytest.approx(20.0, rel=0.005)
|
|
assert r1.json()["source"] == "camera:folder" and r1.json()["nome"] == "Banco"
|
|
file_salvati = list((client.config / "calibrazioni").glob("*.json"))
|
|
assert len(file_salvati) == 1
|
|
assert json.loads(file_salvati[0].read_text(encoding="utf-8"))["cols"] == 9
|
|
|
|
# DELETE /camera/calibration scollega: la libreria resta
|
|
assert client.delete("/camera/calibration", params={"source": "camera:folder"}).status_code == 204
|
|
assert client.get("/camera/calibration", params={"source": "camera:folder"}).status_code == 404
|
|
assert len(list((client.config / "calibrazioni").glob("*.json"))) == 1
|
|
# scollegare di nuovo e' idempotente
|
|
assert client.delete("/camera/calibration", params={"source": "camera:folder"}).status_code == 204
|
|
|
|
|
|
def test_c2_scacchiera_non_trovata_422(client):
|
|
bianca = np.full((300, 300), 255, np.uint8)
|
|
r = client.post("/camera/calibrate", data={"source": "camera:folder", **CAMPI},
|
|
files={"image": ("b.png", _png(bianca), "image/png")})
|
|
assert r.status_code == 422
|
|
assert "non trovata" in r.json()["detail"]
|
|
|
|
|
|
def test_c2_parametri_invalidi_422(client):
|
|
r = client.post("/camera/calibrate",
|
|
data={"source": "camera:folder", "cols": "1", "rows": "6", "square_mm": "2"},
|
|
files={"image": ("s.png", _png(una_scacchiera()), "image/png")})
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_c2_immagine_illeggibile_422(client):
|
|
r = client.post("/camera/calibrate", data={"source": "camera:folder", **CAMPI},
|
|
files={"image": ("x.png", NON_IMMAGINE, "image/png")})
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_c2_errori_camera_come_grab(client):
|
|
r = client.post("/camera/calibrate", data={"source": "camera:non-esiste", **CAMPI})
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_due_nomi_simili_hanno_file_diversi(client):
|
|
# #91: i nomi si ripuliscono come prima (hash sul nome originale): due nomi diversi non si pestano
|
|
r = client.post("/camera/calibrate", data={"source": "camera:folder", **CAMPI},
|
|
files={"image": ("s.png", _png(una_scacchiera()), "image/png")})
|
|
assert r.status_code == 200
|
|
for nome in ("banco a b", "banco a_b"):
|
|
assert salva_con_nome(client, r.json(), nome).status_code == 201
|
|
assert len(list((client.config / "calibrazioni").glob("*.json"))) == 2
|
|
|
|
|
|
def test_c4_pagina_html(client):
|
|
r = client.get("/calibrazione")
|
|
assert r.status_code == 200
|
|
assert r.headers["content-type"].startswith("text/html")
|
|
assert "Acquisisci e calibra" in r.text
|
|
assert "Lato dello scacco" in r.text and 'id="cols"' not in r.text # #84: un solo campo
|
|
assert "http://" not in r.text and "https://" not in r.text # offline, niente CDN
|
|
|
|
|
|
# ---- #84: calibrazione densa (logica pura)
|
|
|
|
def test_densa_trova_la_griglia_da_sola_e_la_scala():
|
|
_carica()
|
|
import calibrazione
|
|
r, mappa = calibrazione.calibra_scacchiera_densa(una_scacchiera_densa(k=1e-8), 2.0)
|
|
assert (r["cols"], r["rows"]) == (15, 11) # 640x480 con quadrati da 40 px
|
|
assert r["n_corners"] == 15 * 11
|
|
assert r["mm_per_px"] == pytest.approx(2.0 / 40, rel=0.005)
|
|
assert r["px_per_mm"] == pytest.approx(20.0, rel=0.005)
|
|
assert r["metodo"] == "densa" and r["distorsione"] is True
|
|
assert r["residuo_max_px"] < 0.3 and r["residuo_rms_px"] <= r["residuo_max_px"]
|
|
assert 60 < r["copertura_pct"] <= 100
|
|
assert (r["image_width"], r["image_height"]) == (640, 480)
|
|
assert base64.b64decode(r["overlay_png"])[:8] == b"\x89PNG\r\n\x1a\n"
|
|
assert mappa.shape == (480, 640) and mappa.pixel_size == pytest.approx(0.05, rel=0.005)
|
|
|
|
|
|
def test_densa_raddrizza_su_griglia_regolare():
|
|
_carica()
|
|
import calibrazione
|
|
img = una_scacchiera_densa(k=1e-7) # distorsione marcata: ai bordi ~5 %
|
|
r, mappa = calibrazione.calibra_scacchiera_densa(img, 2.0)
|
|
assert (r["cols"], r["rows"]) == (15, 11)
|
|
# prima la griglia e' curva, dopo e' un reticolo regolare
|
|
assert residuo_reticolo(calibrazione._trova_griglia(img)) > 1.0
|
|
pronta = calibrazione._prepara_mappa(mappa)
|
|
dritta = calibrazione.raddrizza(img, pronta)
|
|
assert dritta.shape == img.shape
|
|
assert residuo_reticolo(calibrazione._trova_griglia(dritta)) < 0.3
|
|
|
|
|
|
@pytest.mark.parametrize("cosa", ["bianca", "scacchiera_piccola"])
|
|
def test_densa_senza_griglia_solleva(cosa):
|
|
_carica()
|
|
import calibrazione
|
|
img = (np.full((300, 300), 255, np.uint8) if cosa == "bianca"
|
|
else una_scacchiera_densa(80, 60, 20)) # 3x2 angoli: meno di 3x3
|
|
with pytest.raises(calibrazione.CalibrazioneError, match="non trovata"):
|
|
calibrazione.calibra_scacchiera_densa(img, 2.0)
|
|
|
|
|
|
@pytest.mark.parametrize("quadrato", [0.0, -1.0, float("nan")])
|
|
def test_densa_quadrato_invalido_solleva(quadrato):
|
|
_carica()
|
|
import calibrazione
|
|
with pytest.raises(calibrazione.CalibrazioneError):
|
|
calibrazione.calibra_scacchiera_densa(una_scacchiera_densa(), quadrato)
|
|
|
|
|
|
def test_archivio_mappa_cache_e_invalidazione(tmp_path):
|
|
_carica()
|
|
import calibrazione
|
|
_, mappa = calibrazione.calibra_scacchiera_densa(una_scacchiera_densa(), 2.0)
|
|
arch = calibrazione.Archivio(tmp_path)
|
|
assert arch.leggi_mappa("s") is None
|
|
arch.salva_nome("cal", {"a": 1}, mappa) # #91: salvataggio con nome + attivazione sulla sorgente
|
|
arch.attiva("s", "cal")
|
|
prima = arch.leggi_mappa("s")
|
|
assert prima is not None and arch.leggi_mappa("s") is prima # cache in memoria
|
|
assert not list(tmp_path.rglob("*.tmp*")) # niente temporanei rimasti
|
|
arch.salva_nome("cal", {"a": 2}, mappa, sovrascrivi=True) # file riscritto -> cache invalidata
|
|
assert arch.leggi_mappa("s") is not prima
|
|
arch.salva_nome("cal", {"a": 3}, sovrascrivi=True) # salvataggio senza mappa (planare): la vecchia sparisce
|
|
assert arch.leggi_mappa("s") is None and not list(tmp_path.rglob("*.npz"))
|
|
|
|
|
|
# ---- #84: rotte con la calibrazione densa
|
|
|
|
def _grigio(r) -> np.ndarray:
|
|
return cv2.imdecode(np.frombuffer(r.content, np.uint8), cv2.IMREAD_GRAYSCALE)
|
|
|
|
|
|
def test_c2_senza_cols_rows_e_densa(client_denso):
|
|
r = client_denso.post("/camera/calibrate", data={"source": SORGENTE, "square_mm": "2"})
|
|
assert r.status_code == 200, r.text
|
|
corpo = r.json()
|
|
assert CHIAVI_DENSA | {"overlay_png", "calcolo_id"} == set(corpo)
|
|
assert corpo["metodo"] == "densa" and corpo["distorsione"] is True
|
|
assert (corpo["cols"], corpo["rows"]) == (15, 11)
|
|
assert corpo["px_per_mm"] == pytest.approx(1.0 / corpo["mm_per_px"])
|
|
assert not client_denso.config.exists() or not list(client_denso.config.iterdir())
|
|
|
|
|
|
def test_c2_solo_cols_o_solo_rows_422(client_denso):
|
|
r = client_denso.post("/camera/calibrate", data={"source": SORGENTE, "square_mm": "2", "cols": "9"})
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_grab_raddrizzato_se_calibrata_e_grezzo_a_richiesta(client_denso, cartella_densa):
|
|
originale = cv2.imread(str(cartella_densa / "densa.png"), cv2.IMREAD_GRAYSCALE)
|
|
prima = client_denso.post("/camera/grab", json={"source": SORGENTE})
|
|
assert prima.headers["x-raddrizzato"] == "no" # non calibrata: frame com'e'
|
|
assert np.array_equal(_grigio(prima), originale)
|
|
|
|
r = client_denso.post("/camera/calibrate", data={"source": SORGENTE, "square_mm": "2"})
|
|
assert r.status_code == 200, r.text
|
|
assert salva_con_nome(client_denso, r.json(), "Densa").status_code == 201 # #91: salvata con nome
|
|
libreria = client_denso.config / "calibrazioni"
|
|
json_salvati, mappe = list(libreria.glob("*.json")), list(libreria.glob("*.npz"))
|
|
assert len(json_salvati) == 1 and len(mappe) == 1
|
|
assert mappe[0].stem == json_salvati[0].stem # stesso nome del JSON
|
|
assert client_denso.post("/camera/grab", json={"source": SORGENTE}).headers["x-raddrizzato"] == "no" # salvata != attiva
|
|
assert carica_su(client_denso, SORGENTE, "Densa").status_code == 200
|
|
saved = client_denso.get("/camera/calibration", params={"source": SORGENTE}).json()
|
|
assert set(saved) == CHIAVI_DENSA | CHIAVI_ATTIVA and saved["metodo"] == "densa"
|
|
|
|
dopo = client_denso.post("/camera/grab", json={"source": SORGENTE})
|
|
assert dopo.status_code == 200 and dopo.headers["x-raddrizzato"] == "si"
|
|
import calibrazione
|
|
dritta = _grigio(dopo)
|
|
assert dritta.shape == originale.shape
|
|
assert not np.array_equal(dritta, originale)
|
|
assert residuo_reticolo(calibrazione._trova_griglia(dritta)) < 0.3 # griglia regolare
|
|
|
|
grezzo = client_denso.post("/camera/grab", json={"source": SORGENTE, "grezzo": True})
|
|
assert grezzo.headers["x-raddrizzato"] == "no"
|
|
assert np.array_equal(_grigio(grezzo), originale)
|
|
|
|
# la calibrazione parte sempre dal frame grezzo: ricalibrare dalla sorgente da' lo stesso mm/px
|
|
di_nuovo = client_denso.post("/camera/calibrate", data={"source": SORGENTE, "square_mm": "2"})
|
|
assert di_nuovo.json()["mm_per_px"] == pytest.approx(r.json()["mm_per_px"])
|
|
assert di_nuovo.json()["residuo_max_px"] < 0.3
|
|
|
|
|
|
def test_grab_grezzo_non_booleano_400(client_denso):
|
|
r = client_denso.post("/camera/grab", json={"source": SORGENTE, "grezzo": "si"})
|
|
assert r.status_code == 400
|
|
|
|
|
|
def test_scollega_lascia_la_libreria_ed_elimina_toglie_la_mappa(client_denso):
|
|
r = client_denso.post("/camera/calibrate", data={"source": SORGENTE, "square_mm": "2"})
|
|
salva_con_nome(client_denso, r.json(), "Densa")
|
|
carica_su(client_denso, SORGENTE, "Densa")
|
|
libreria = client_denso.config / "calibrazioni"
|
|
assert list(libreria.glob("*.npz"))
|
|
# scollegare: il frame torna com'e', ma la voce (e la mappa) restano in libreria
|
|
assert client_denso.delete("/camera/calibration", params={"source": SORGENTE}).status_code == 204
|
|
assert client_denso.post("/camera/grab", json={"source": SORGENTE}).headers["x-raddrizzato"] == "no"
|
|
assert list(libreria.glob("*.npz")) and list(libreria.glob("*.json"))
|
|
# eliminare la voce toglie JSON e mappa
|
|
carica_su(client_denso, SORGENTE, "Densa")
|
|
assert client_denso.delete("/camera/calibrazioni", params={"nome": "Densa"}).status_code == 204
|
|
assert not list(libreria.glob("*.npz")) and not list(libreria.glob("*.json"))
|
|
assert client_denso.post("/camera/grab", json={"source": SORGENTE}).headers["x-raddrizzato"] == "no"
|
|
|
|
|
|
def test_ricalibrare_planare_toglie_la_mappa_densa(client_denso):
|
|
densa = client_denso.post("/camera/calibrate", data={"source": SORGENTE, "square_mm": "2"})
|
|
salva_con_nome(client_denso, densa.json(), "Cal")
|
|
carica_su(client_denso, SORGENTE, "Cal")
|
|
libreria = client_denso.config / "calibrazioni"
|
|
assert list(libreria.glob("*.npz"))
|
|
planare = client_denso.post("/camera/calibrate", data={"source": SORGENTE, **CAMPI},
|
|
files={"image": ("s.png", _png(una_scacchiera()), "image/png")})
|
|
assert planare.status_code == 200 and planare.json()["metodo"] == "planare"
|
|
assert salva_con_nome(client_denso, planare.json(), "Cal").status_code == 409 # nome gia' preso
|
|
assert salva_con_nome(client_denso, planare.json(), "Cal", sovrascrivi=True).status_code == 201
|
|
assert not list(libreria.glob("*.npz")) # niente mappa vecchia che raddrizza a vuoto
|
|
assert client_denso.post("/camera/grab", json={"source": SORGENTE}).headers["x-raddrizzato"] == "no"
|