0420c4a863
11 test senza dipendenza dalle immagini Test/ (non versionate): - precisione/recall su 7 pose GT (soglie 0.2-0.5 deg, 0.3-1.0 px, margine 3-4x sulle misure Fase 2) - unit: angle_list con estremi, clamp piramide, save/load roundtrip, no collisione cache scena, mask poligonale, find non addestrato Config ruff in pyproject (E702/E402 idiomi del codebase esclusi). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57 lines
2.2 KiB
Python
57 lines
2.2 KiB
Python
"""Test di non-regressione su precisione e recall (GT sintetica).
|
|
|
|
Soglie derivate dalle misure di Fase 2 (errore mediano ~0.05 deg /
|
|
~0.08 px) con margine 3-4x per assorbire rumore tra run/macchine.
|
|
Una regressione del refine (es. score saturo, minMaxLoc sul plateau)
|
|
riporterebbe gli errori a 2-4 deg / 4 px e fa fallire i test con
|
|
margine enorme.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from pm2d import LineShapeMatcher
|
|
from tests.conftest import GT_POSES, match_errors
|
|
|
|
|
|
def _find(template, scene, step, **kw):
|
|
m = LineShapeMatcher(angle_step_deg=step, num_features=96)
|
|
m.train(template)
|
|
return m.find(scene, min_score=0.5, max_matches=10, **kw)
|
|
|
|
|
|
def test_recall_and_precision_step5(template, scene):
|
|
matches = _find(template, scene, 5.0)
|
|
errs_a, errs_p, miss = match_errors(matches, GT_POSES)
|
|
assert miss == 0, f"{miss} pose GT non trovate"
|
|
assert float(np.median(errs_a)) < 0.2, f"err angolo mediano {np.median(errs_a):.3f} deg"
|
|
assert float(np.max(errs_a)) < 0.5, f"err angolo max {np.max(errs_a):.3f} deg"
|
|
assert float(np.median(errs_p)) < 0.3, f"err posizione mediano {np.median(errs_p):.3f} px"
|
|
assert float(np.max(errs_p)) < 1.0, f"err posizione max {np.max(errs_p):.3f} px"
|
|
|
|
|
|
def test_recall_and_precision_step2(template, scene):
|
|
# Step fine: storicamente il caso peggiore (plateau con piu' varianti
|
|
# dentro la tolleranza spread → scelta variante arbitraria).
|
|
matches = _find(template, scene, 2.0)
|
|
errs_a, errs_p, miss = match_errors(matches, GT_POSES)
|
|
assert miss == 0, f"{miss} pose GT non trovate"
|
|
assert float(np.median(errs_a)) < 0.2
|
|
assert float(np.max(errs_a)) < 0.5
|
|
assert float(np.median(errs_p)) < 0.3
|
|
|
|
|
|
def test_no_false_positives(template, scene):
|
|
# max_matches alto: non devono comparire match spuri oltre le 7 pose.
|
|
matches = _find(template, scene, 5.0)
|
|
assert len(matches) <= len(GT_POSES) + 1, (
|
|
f"{len(matches)} match per {len(GT_POSES)} oggetti reali"
|
|
)
|
|
|
|
|
|
def test_full_scan_path_equivalent(template, scene):
|
|
# Il path full-scan (propagate off) deve trovare le stesse pose.
|
|
matches = _find(template, scene, 5.0, pyramid_propagate=False)
|
|
_, _, miss = match_errors(matches, GT_POSES)
|
|
assert miss == 0
|