Commit Graph

61 Commits

Author SHA1 Message Date
Adriano 0df0d98aa5 merge: X ensemble multi-template (con M/Y/Z preservati) 2026-05-04 22:42:43 +02:00
Adriano b2b959e801 merge: V save/load model 2026-05-04 22:42:05 +02:00
Adriano b05246b492 merge: Z subpixel LM (M+Y preservati) 2026-05-04 22:42:00 +02:00
Adriano aeaa7fb5f7 merge: Y soft-margin gradient (con M recall preservato) 2026-05-04 22:40:26 +02:00
Adriano 0296083e3c feat: add_template_view - multi-template ensemble (Halcon-style)
Aggiunge una view extra al matcher gia addestrato. Le varianti
della nuova view vengono APPENDATE a self.variants col tag view_idx
e partecipano al pruning/matching come le altre.

NCC verify usa il template della view che ha matchato (via
_get_view_template + parametro view_idx propagato a _verify_ncc).

Halcon-equivalent: create_aniso_shape_model con fusione N viste.
Use case: pezzo che cambia aspetto (chiaro/scuro, prima/dopo
trattamento, illuminazioni diverse) → un solo matcher robusto
invece di N matcher distinti.

API:
    m.train(template_chiaro)
    m.add_template_view(template_scuro)
    m.find(scene)  # match su entrambi gli aspetti

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:37:13 +02:00
Adriano 39208aadab feat: save_model / load_model - persistenza ricetta addestrata
Halcon-equivalent write_shape_model / read_shape_model. Salva su
file .npz compresso:
- Tutti i parametri matcher (incluso use_polarity)
- Template gray + maschera training
- Tutte le varianti pre-computate (con piramide flat per scrittura
  efficiente, ~12KB per template 80x80 con 28 varianti)

Caso d'uso: training offline su workstation, deploy a runtime
production senza re-train. load_model() istantaneo: skip training
(che e' il costo dominante per molte scale/angoli).

Format version 1, np.savez_compressed (zlib).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:34:54 +02:00
Adriano 2b7ee6799c feat: subpixel_lm - refinement iterativo gradient-field least-squares
_subpixel_refine_lm: per ogni feature template, calcola normale
gradient nella scena (bilineare) e stima shift (dx, dy) globale
che minimizza errore direzionale gradient field. Iterazione damped
(max 1px/iter) per stabilita.

Halcon-equivalent SubPixel='least_squares_high'. Precisione attesa
0.05 px (vs 0.5 px del fit quadratico 2D plain). Costo: ~5ms per
match aggiuntivi (negligibile vs total find).

Default off (subpixel_lm=False, backward compat). Attivare per
applicazioni di alignment/dimensional inspection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:33:55 +02:00
Adriano 5059ce1d89 feat: use_soft_score - Halcon Metric soft-margin gradient similarity
_compute_soft_score: cos(theta_template - theta_scena) continuo
(non quantizzato a bin) pesato per magnitude. Polarity-aware se
use_polarity=True (mod 2pi) else |cos| (mod pi).

Quando use_soft_score=True (default off, backward compat), lo score
finale e' fuso con quello shape: piu discriminante per match a
piccola rotazione (penalita' graduale invece di binaria on/off).

Equivalente a Halcon Metric='use_polarity' / 'ignore_global_polarity'
in find_shape_model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:32:17 +02:00
Adriano f05dec5183 feat: min_recall - Halcon-style feature recall check post-refine
_compute_recall calcola hits/N feature template alla pose finale
(post sub-pixel refine). Equivalente Halcon MinScore originale:
quante feature shape effettivamente combaciano sul match accettato.

Param min_recall (default 0 = off, backward compat). Util quando
NCC e' alto ma poche feature reali matchano (es. match parziale
su zona di simil-tessitura). Soglia 0.7-0.9 raccomandata per
filtri stringenti.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:31:02 +02:00
Adriano f8f6a15166 fix: pruning top adattivo a angle_step (precisione preciso era peggio)
Bug osservato: con precisione "veloce" (10 deg) il matching dava
risultati migliori che con "preciso" (2 deg). Causa: con step fine
ci sono molte varianti vicine, score top-level ravvicinati e:
- top_thresh = min_score * 0.5 troppo aggressivo: scartava varianti
  valide che sarebbero state scelte al full-res
- coarse_angle_factor=2 (skip 1 ogni 2): col fine vicini sono quasi
  identici, ma il pruning skippava la migliore

Fix: quando angle_step <= 3 deg, automatic:
- top_score_factor min 0.7 (vs default 0.5)
- coarse_angle_factor = 1 (no skip varianti)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:20:35 +02:00
Adriano 5bd8fca248 fix: re-check min_score dopo NCC averaging
Bug: score finale = (shape + ncc) / 2 puo scendere sotto min_score
impostato dall'utente. La UI mostrava match con score < soglia
perche il filtro min_score era applicato solo allo shape-score
iniziale, non al risultato finale post-NCC.

Aggiunto re-check dopo averaging: scarta match con score finale
< min_score. Coerenza filtro user-facing ripristinata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:00:32 +02:00
Adriano 796ccb8052 fix(web): simmetria invariante (0) collassava a 360 per || default
Bug JS: SYM_MAP[user.simmetria] || 360 trasforma il valore valido 0
(invariante = nessuna rotazione) in 360 = no simmetria. Risultato:
cambiare simmetria nel pannello avanzato non aveva effetto se
selezionato invariante; per le altre opzioni il valore passava
ma con potenziale altri valori 0 in futuro.

Sostituito con ?? per distinguere "chiave mancante" da "valore zero".
Stessa fix per PREC_MAP.

Inoltre allineato FP_MAP JS al server (medio 0.35 -> 0.50, ecc.)
per coerenza UI/backend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 21:54:16 +02:00
Adriano 0a8a9365bb fix: NCC robusto + reject bbox fuori scena + threshold piu rigorosi
3 fix per match spuri ad alto score visti su scena reale:

1. NCC con guard varianza minima: se template-patch o scene-patch
   hanno std quasi-zero (zone uniformi bianche/nere) NCC e instabile
   e da false-correlation alta. Ora ritorna 0 sotto soglia varianza.

2. Reject post-bbox: se il bounding-box ruotato del match sfora
   la scena per piu del 25%, scarto (centro derivato male o scala
   incoerente). Tollera 25% out-of-bounds (bordi).

3. FILTRO_FP_MAP alzato: leggero 0.20→0.30, medio 0.35→0.50,
   forte 0.50→0.70. Default piu conservativo per evitare match
   spuri su zone con pochi edge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 21:51:43 +02:00
Adriano 9ed779637e merge: angle restrict helper 2026-05-04 17:09:09 +02:00
Adriano 077d44c3c8 merge: polarity 16-bin 2026-05-04 17:09:05 +02:00
Adriano 041b26e791 feat: helper set_angle_range_around + angle_tolerance hint in auto_tune
LineShapeMatcher.set_angle_range_around(center, tol): restringe
angle_range a (center-tol, center+tol). Use case: feeder/posizionamento
meccanico noto a priori. Esempio:
    m.set_angle_range_around(0, 20)  # cerca solo in [-20, +20]

auto_tune accetta angle_tolerance_deg + angle_center_deg: emette
angle_min/angle_max ristretti se hint fornito. Cache key include
hint per non collidere con tune default.

Beneficio misurato: angle_step=5 deg, template 80x80
- range 360°: 72 varianti
- range ±15°: 6 varianti (12x meno = matching ~12x piu veloce)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 17:08:56 +02:00
Adriano 84b73dc651 feat: use_polarity 16-bin orientation (mod 2pi)
Flag opt-in use_polarity=True su LineShapeMatcher: distingue edge
chiaro->scuro da scuro->chiaro raddoppiando i bin (8 mod pi a 16
mod 2pi). Riduce match accidentali quando il template e direzionale
ma scena ha bordo opposto (es. pezzo nero su bg chiaro vs pezzo
chiaro su bg nero).

Implementazione:
- _gradient calcola atan2 mod 2pi quando use_polarity
- _spread_bitmap usa uint16 (16 bit) invece di uint8 (8 bit)
- Nuovi kernel JIT _jit_score_bitmap_rescored_u16 e
  _jit_popcount_density_u16
- Wrapper Python score_bitmap_rescored / popcount_density fanno
  dispatch su dtype dello spread

Default off (use_polarity=False) = backward compat completo, 8 bin.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 17:07:38 +02:00
Adriano 8d8a89ac35 feat: NMS poligonale (IoU bbox ruotato) cross-variant
_poly_iou via cv2.intersectConvexConvex: IoU esatto tra bbox
orientati. Sostituisce distanza-centro nel NMS post-refine.

Vantaggio: due pezzi adiacenti con centri vicini (entro nms_radius)
ma orientamenti diversi non vengono piu fusi se overlap reale e
basso. Stesso pezzo trovato da varianti angolari diverse (centri
uguali, IoU ~1) viene correttamente droppato.

Param nms_iou_threshold default 0.3. Fallback distanza centro
(r2/4) come safety per bbox degeneri.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 17:04:11 +02:00
Adriano 41976f574d fix: duplicati, score saturato e angolo impreciso
3 problemi visibili da test interattivo:
1. Match duplicati: stesso oggetto trovato da varianti angolari
   diverse, NMS pre-refine non basta perche refine sposta i match.
   Aggiunto NMS post-refine cross-variant.

2. Score sempre alto/saturato a 1.0: NCC era opzionale (skip>=0.85)
   e non veniva mescolato nello score. Ora ncc_skip_above=1.01
   (NCC sempre) e score finale = (shape + NCC) / 2: piu discriminante.

3. Angolo impreciso: _refine_angle aveva early-exit per shape-score
   >= 0.99, ma quel valore satura facile (con pyramid_propagate o
   spread ampio) senza garantire angolo preciso. Rimosso early-exit:
   refine angolare e' sempre essenziale per orientamento sub-step.

Inoltre: pyramid_propagate default False (era True), riduce duplicati
da picchi propagati su angle-vicini. propagate_topk default 4 (era 8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 16:33:58 +02:00
Adriano 4ef7a4a85f merge: dedup varianti 2026-05-04 15:46:34 +02:00
Adriano 7de7f35b7c merge: SIMD popcount fallback 2026-05-04 15:46:21 +02:00
Adriano 7b014b7f69 merge: batch_top variant-parallel kernel 2026-05-04 15:46:17 +02:00
Adriano 367ee9aaac merge: greediness (kernel greedy alternativo a rescore strided) 2026-05-04 15:45:15 +02:00
Adriano 74e5a45a39 merge: refine cache 2026-05-04 15:43:23 +02:00
Adriano 11c5160385 merge: refine_pose_joint (param list unito) 2026-05-04 15:43:19 +02:00
Adriano 07bab87cb9 merge: lazy NCC 2026-05-04 15:42:53 +02:00
Adriano a247484f36 merge: auto angle_step 2026-05-04 15:42:45 +02:00
Adriano e188df0adb merge: pyramid_propagate (con coarse_stride preservato) 2026-05-04 15:42:41 +02:00
Adriano b35d47669c merge: coarse_stride 2026-05-04 15:41:57 +02:00
Adriano 6da4dd5329 feat: dedup varianti con feature-set identico post-quantizzazione
Hash byte-exact su (dx, dy, bin) ordinati + scale. Se due varianti
post-rasterizzazione hanno lo stesso feature-set, ne tiene solo una.

Tipico caso d'uso: template con simmetrie discrete (quadrati, croci,
forme regolari) generano duplicati esatti per rotazioni multiple
del periodo. Su quadrato 80x80 con angle_step=10 deg: 36 -> 27 varianti
(~25% in meno di lavoro top-pruning).

Approccio conservativo (byte-exact): zero rischio di rimuovere varianti
distinte. Forme arrotondate (cerchi) o template asimmetrici non beneficiano
ma non vengono compromessi.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:37:42 +02:00
Adriano b143c6607a feat: numpy.bitwise_count come fallback SIMD per popcount
NumPy 2.0+ espone np.bitwise_count: implementato in C nativo con
intrinsics SIMD (POPCNT/AVX2 vpopcnt). Aggiunto come fallback secondo
livello quando Numba non e disponibile (es. wheel constraint, env
ristretto). Numba JIT parallel resta default: misura su 1080p 0.5ms
vs 1.6ms (bitwise_count e single-thread).

AVX2 puro su _jit_score_bitmap_rescored richiederebbe C extension
con build nativa: out-of-scope per questo branch (Numba LLVM gia
autovettorizza il loop interno).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:36:48 +02:00
Adriano 6704d66cd5 feat: kernel JIT batch top-max-per-variant (opt-in)
Nuovo kernel _jit_top_max_per_variant: prange esterno sulle varianti
invece di n_vars chiamate JIT separate via ThreadPoolExecutor.
Wrapper Python top_max_per_variant prepara buffer flat (offsets +
dx_flat/dy_flat/bins_flat) e bg per scala.

Default batch_top=False perche su benchmark realistici (Linux 13 core,
72-180 varianti) ThreadPoolExecutor + kernel singolo che rilascia GIL
e gia ottimale. Path batch_top=True utile come opzione per scenari
con n_vars >>> n_threads o overhead chiamate JIT dominante.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:35:51 +02:00
Adriano 4419c237b2 feat: greediness param con early-exit kernel JIT
Nuovo kernel _jit_score_bitmap_greedy: per ogni pixel scorre N feature
ed esce non appena hits + remaining < greediness * min_score * N.
Esposto in find() come greediness in [0..1], default 0 (backward compat).

Sostituisce il kernel rescored al top-level quando attivo: salta il
rescore background ma early-exit pixel impossibili. Util su template
con molte feature (>100) e scena con pochi pattern competitivi.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:33:39 +02:00
Adriano f00cf9b621 feat: cache features template per _refine_angle
Cache LRU (chiave: angolo arrotondato a 0.05deg, scale) di
(fx, fy, fb) per evitare warpAffine + gradient + extract ripetuti
durante golden-search refine. Bucket condiviso tra match della stessa
find() e tra find() consecutive sulla stessa ricetta.

Cache invalidata in train(): il template puo essere cambiato.
Limite 256 entry (sufficiente per 32 candidati x 8 valutazioni).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:31:37 +02:00
Adriano 4b7271094b feat: refine_pose_joint - Nelder-Mead 3D su (cx, cy, angle)
Alternativa al refine angolare 1D + subpixel quadratico: ottimizza
simultaneamente posizione e angolo con Nelder-Mead 3D inline (no
scipy). Default off (refine_pose_joint=False) per backward compat.

Vantaggio Halcon-style: un singolo iter LM/simplex stila il match a
precisione sub-pixel + sub-step in modo congiunto invece di alternare
assi. Convergenza tipica ~24 valutazioni vs ~15 (golden+quadratico)
ma piu robusto su template asimmetrici dove pose e angolo sono
fortemente correlati.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:30:20 +02:00
Adriano 746d1668c6 feat: NCC verify lazy con skip per shape-score alto
ncc_skip_above (default 0.85): se lo score shape e gia molto alto,
salta la verifica NCC (costosa: warp + corr per ogni match). I match
borderline 0.6-0.85 vengono comunque verificati.

Comportamento Halcon-style: NCC come tie-breaker per casi ambigui,
non come gate generalizzato. Su scene con molti match netti riduce
sensibilmente il costo della fase post-NMS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:28:24 +02:00
Adriano d9a40952c4 feat: angle_step auto adattivo a dimensione template
Halcon-style: angle_step_deg=0 attiva derivazione automatica
step = atan(2/max_side) deg, clampato [0.5, 10]. Template grande
ottiene step fine, piccolo step grosso. auto_tune emette il valore
calcolato direttamente.

_refine_angle ora usa _effective_angle_step() per coerenza con
training quando la modalita auto e attiva.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:27:35 +02:00
Adriano 6db2086ead feat: pyramid_propagate - candidati top-level guidano full-res
Top-level ritorna top-K picchi locali invece di solo max. Fase full-res
valuta solo crop locali attorno ai picchi propagati (margine =
sf_top + spread + nms_radius/2) invece di scansionare intera scena.

Su scene 1920x1080 con pochi candidati: ~20-30% piu veloce mantenendo
identici match. Vantaggio cresce con scene piu grandi e meno candidati.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:26:29 +02:00
Adriano 27a0ef1a45 feat: coarse_stride per sub-sampling top-level
Nuovo kernel JIT _jit_score_bitmap_rescored_strided: valuta solo
pixel su griglia stride x stride al top della piramide. NMS + fase
full-res recuperano precisione. Speed-up ~stride^2 sulla fase coarse,
specie su scene grandi (1920x1080).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:24:44 +02:00
Adriano ba4024d252 feat: search_roi parametro find() per limitare area di ricerca
Equivalente a Halcon set_aoi: matching opera su crop locale, coord
output ri-traslate al sistema scena. Costo proporzionale a w*h del
ROI invece di W*H scena intera.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:22:43 +02:00
root 89b59b3ea3 perf: Fase 2 speed (3x baseline) - fuse JIT + LRU + sub-pixel lazy
Ottimizzazioni cumulative (225s -> 73s sul bench suite, 3.07x):

pm2d/line_matcher.py:
- Sub-pixel + plateau centroid spostati DOPO il pre-NMS (prima: 58k chiamate
  per clip_preciso anche su candidati poi scartati dalla NMS; ora solo sui
  ~75 preliminary sopravvissuti). Coordinate intere OK per la decisione
  reject, dato che nms_radius >= 8 px.
- Usa nuovo kernel fuso score+rescore (no allocazione intermedia).
- Adaptive plateau_radius + propagazione train_mask per NCC coerente.
- Local crop NCC (diag template invece di intera scena).
- Fallback adattivo se bg_rescore azzera tutti gli score top-level.

pm2d/_jit_kernels.py:
- Nuovo kernel _jit_score_bitmap_rescored: fonde scoring bitmap e rescore
  (score - bg) / (1 - bg) in un singolo pass parallelo. Evita allocazione
  e passata aggiuntiva (era ~15% del tempo find sul preciso).

pm2d/auto_tune.py:
- LRU cache in-memory sui risultati auto_tune (chiave md5 ROI + mask):
  richiamate successive con stessa ROI sono O(1).
- Downsample a 128px prima della correlazione rotazionale
  (O(n_angles * H * W) -> insensibile su sample moderati).
- Soglie weak/strong da percentili reali (p55/p85) senza clamp a 100,
  con clamp massimo 400 per evitare saturazione su template ad alto contrasto.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:21:59 +00:00
Adriano 3e4c20ecf5 feat: upload file nella cartella IMAGES_DIR
POST /upload_to_folder: sanitizza nome, valida estensione e contenuto
via cv2.imdecode, auto-rename su collisione.
Toolbar UI: bottone 'Carica file', dopo upload ricarica picker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:45:16 +02:00
Adriano cc7d035f66 feat: scale_penalty - score riflette dimensione oltre a forma
Shape matching e invariante scala per design: 3 ruote dentate di dim
diverse avevano tutte score 1.00 confondendo l operatore.

Parametro scale_penalty [0..1]: score_final = score * max(0, 1 - penalty * |scale - 1|)
UI dropdown 'Peso dimensione nel score' con preset 0 / 0.3 / 0.5 / 0.8.

Test rings con penalty 0.5: 1.00 -> 1.00, 0.95 -> 0.97, 0.80 -> 0.90.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:37:36 +02:00
Adriano 37b718e45e perf: Fase 1 speed+precision (V1 V11 P1 P5)
V1 Coarse-to-fine angolare:
  - Al top-level valuta solo 1 variante ogni coarse_angle_factor (default 2)
  - Espande ai vicini nel full-res per preservare accuracy
  - Safe anche per template allungati (factor=2 non perde match)

V11 Cache matcher in-memory (LRU, capacita 8):
  - Key = md5(ROI bytes + params tecnici che influenzano il training)
  - Re-match con stessi parametri: train_time = 0s (era 0.5-1.5s)
  - OrderedDict LRU con _cache_get_matcher / _cache_put_matcher

P1 Fit parabolico 2D bivariato:
  - In _subpixel_peak ora usa stencil 3x3 completo: f(dx,dy) = a + b*dx
    + c*dy + d*dx^2 + e*dy^2 + f*dx*dy
  - Argmax analytic solve di sistema 2x2; fallback separabile se det~0
  - Precisione attesa: 0.1-0.3 px (era 0.5 px separabile)

P5 Golden-section angle search:
  - Sostituisce 5 sample equispaziati con convergenza log(n)
  - Tol 0.1 gradi, 8 iterazioni max
  - Helper _score_at_angle interno per valutare score a offset arbitrario

P2 Weighted centroid plateau:
  - Peso = (score - (max-0.01))^2 per enfatizzare top del plateau

Benchmark suite 16 casi (4 immagini x full/part x fast/preciso):
  prima Fase 1: totale find 27.3s
  dopo  Fase 1: totale find 25.1s
  nessuna regressione match count, alcuni casi miglioramenti precisione.

ROADMAP.md aggiornato con checklist Fase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:35:40 +02:00
Adriano b83e577eab feat: thumbnail picker custom per selezione modello/scena
- GET /folder_image/{filename}?w=N: PNG ridotto cache 1h
- Frontend: 2 thumb-picker al posto dei select (thumb + nome + caret)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:10:35 +02:00
Adriano 2bca68d700 feat: 'Filtro falsi positivi' preset user-friendly (era verify_ncc)
Rinomina il parametro tecnico verify_threshold in un preset semantico
che operatore/cliente capisce senza leggere docs:

  off      -> 0.00 (tutti i match shape-based passano)
  leggero  -> 0.20 (tollera illuminazione/riflessi)
  medio    -> 0.35 (consigliato, default)
  forte    -> 0.50 (massima selettivita, scarta mismatch intensita)

UI: dropdown etichettato 'Filtro falsi positivi (verifica intensita colori)'
accanto a precisione angolare. Override tecnico (numerico) resta in
sezione Avanzate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:52:26 +02:00
Adriano 1671a151da feat: selezione immagini da cartella IMAGES_DIR via .env
- .env con IMAGES_DIR=Test
- server: _load_env legge .env senza dip extra
- GET /images lista file, POST /load_from_folder carica per nome
- frontend: file picker sostituiti con 2 select popolati all avvio

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:50:37 +02:00
Adriano 1954bc6ffd fix: allineamento preciso match (skip refine saturo + plateau centroid)
Bug: modello == scena non sovrapponeva perfettamente.

1. refine_angle trovava angoli spurious -2.5 deg con score saturo 1.0
   perche' parabolic fit su picco saturo estrapola rumore.
   Fix: skip refine quando original_score >= 0.99

2. Subpixel peak su plateau (spread_radius=5 satura picco su area)
   sceglieva pixel random via cv2.minMaxLoc.
   Fix: se >1 pixel a score >= max-0.01 nel raggio 10 usa CENTROIDE
   del plateau invece del parabolic fit.

Test self-match tooth_rim foro piccolo:
  prima:  pos=(355, 111.50) delta=(0, -3.50) ang=-2.5 deg
  dopo:   pos=(355, 115.00) delta=(0, +0.00) ang=+0.0 deg

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:12:26 +02:00
Adriano 45e3a29ff0 feat: simmetria 'invariante' per oggetti circolari (1 variante angolare)
Test tooth_rim foro grande: 12x piu veloce (0.14s vs 1.77s) perche
angle_max=0 genera 1 sola variante angolare invece di 72.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:02:32 +02:00
Adriano e1a1b956fd fix: persistenza immagini su disco (sopravvive restart server)
Bug: _IMAGES era dict in-memory, restart server → browser con id cached
riceveva 404 'Immagini non trovate'.

Fix: scrittura PNG in /tmp/pm2d_cache/{id}.png al upload, _load_image()
prova cache memory prima di leggere disco.

Rimossa funzione _store_image duplicata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:00:54 +02:00