9.10.5 EP09_05 🟠 Valutazione della Segmentazione: IoU e Dice Pixel per Pixel
Il Blocco 2 della sezione “Segmentazione Semantica con Architettura U-Net” definisce, in poche righe, la funzione iou_mascaras, utilizzata per misurare la qualità della baseline morfologica classica (smoothing + Otsu + apertura) e, più avanti, della stessa U-Net addestrata. Diversamente dall’IoU dell’EP09_04 — calcolato su bounding box (regioni rettangolari descritte da quattro numeri) —, l’IoU di segmentazione è calcolato pixel per pixel: ogni posizione dell’immagine viene confrontata individualmente tra la maschera predetta e la maschera di riferimento.
Ti è stato affidato il compito di generalizzare questa valutazione, implementando non solo l’IoU pixel per pixel, ma anche il coefficiente di Dice, un’altra metrica di sovrapposizione ampiamente utilizzata in segmentazione medica (inclusa nella funzione perda_dice, menzionata nello stesso blocco del capitolo come base della funzione di perdita utilizzata per addestrare la U-Net).
9.10.5.1 📋 Linee Guida di Implementazione
Input: Leggere le dimensioni \(H \times W\) delle maschere.
Maschera predetta: Leggere \(H\) righe con \(W\) valori interi (0 o 1) ciascuna — ad esempio, l’output di una U-Net dopo la sogliatura a \(0{,}5\) sulla sigmoide, come nel Blocco 4 del capitolo.
Maschera di riferimento: Leggere altre \(H\) righe con \(W\) valori interi (0 o 1) ciascuna — il ground truth.
Intersezione e unione: Considerando ogni pixel come appartenente all’oggetto quando il suo valore è diverso da zero, \[ \text{intersezione} = \sum_{i,j} \mathbb{1}[P_{ij}=1 \wedge R_{ij}=1], \qquad \text{unione} = \sum_{i,j} \mathbb{1}[P_{ij}=1 \vee R_{ij}=1]. \]
IoU pixel per pixel: \[ \text{IoU} = \frac{\text{intersezione}}{\text{unione}}. \]
Coefficiente di Dice: \[ \text{Dice} = \frac{2 \cdot \text{intersezione}}{|P| + |R|}, \] dove \(|P|\) e \(|R|\) sono il numero totale di pixel dell’oggetto in ciascuna maschera.
Convenzione per maschere vuote: se entrambe le maschere non hanno alcun pixel dell’oggetto (unione \(= 0\) e \(|P|+|R|=0\)), considera la corrispondenza banalmente perfetta: \(\text{IoU} = \text{Dice} = 1{,}0\).
Output: Due righe,
IoU: X.XXXXeDice: X.XXXX, ciascun valore con 4 cifre decimali.
9.10.5.2 📌 Vincoli Computazionali
- Qualsiasi valore non nullo conta come oggetto: tratta i valori diversi da \(0\) (non solo \(1\)) come appartenenti alla maschera, replicando il controllo
predita > 0usato iniou_mascarasnel capitolo. - Stesse dimensioni: le due maschere hanno sempre esattamente \(H \times W\) elementi.
- Convenzione del vuoto: applica la regola del punto 7 solo quando entrambe le maschere sono completamente vuote; se solo una è vuota, l’intersezione è \(0\) e l’IoU/Dice risultante sarà anch’esso \(0\).
9.10.5.3 🧠 Fondamento Teorico
| Elemento | Ruolo nella valutazione della segmentazione |
|---|---|
| IoU pixel per pixel | Generalizza la metrica dell’EP09_04 a regioni di forma arbitraria — non solo rettangoli — confrontando la maschera predetta e il riferimento posizione per posizione |
| Coefficiente di Dice | Metrica correlata all’IoU (sempre \(\text{Dice} \ge \text{IoU}\)), più sensibile a piccole intersezioni e ampiamente utilizzata come funzione di perdita in segmentazione (funzione perda_dice del capitolo) |
| Convenzione per maschere vuote | Evita la divisione per zero e riconosce che “nessun oggetto previsto, nessun oggetto reale” è, per definizione, un successo |
| Confronto classico vs. U-Net | Il capitolo usa esattamente questo tipo di metrica per giustificare, numericamente, perché la U-Net supera la baseline morfologica in scenari a basso contrasto |
9.10.5.4 🧩 Metodi di morph.py che possono aiutare
mm.readImg(h, w, dtype='uint8')— legge direttamente ogni maschera binaria \(h \times w\) dall’input standard (i valori \(0/1\) rientrano perfettamente nel tipo intero standard).- La stessa funzione
iou_mascaras, definita nel Blocco 2 della sezione U-Net del capitolo (non fa parte dimorph.py, ma del codice del capitolo), è l’ispirazione diretta di questo esercizio — vale la pena rileggere quelle poche righe prima di programmare. - Per un’estensione opzionale (non richiesta da questo EP),
mm.connectedComponentsomm.label0(visti nel contesto dell’analisi delle componenti connesse) permetterebbero di etichettare ogni nodulo individualmente e calcolare l’IoU per componente, invece che sull’intera maschera.
9.10.5.5 📦 Specifica di Input e Output (VPL)
Input:
- Riga 1: Interi \(H\) e \(W\).
- Prossime \(H\) righe: \(W\) valori interi (0 o 1) — maschera predetta.
- Prossime \(H\) righe: \(W\) valori interi (0 o 1) — maschera di riferimento.
Output:
- Riga 1:
IoU: X.XXXX. - Riga 2:
Dice: X.XXXX.
Considera una maschera predetta con un quadrato \(2\times2\) di pixel attivi e un riferimento spostato di una colonna, sovrapponendosi solo per metà dell’area:
Predetta Riferimento
0 0 0 0 0 0 0 0
0 1 1 0 0 0 1 1
0 1 1 0 0 0 1 1
0 0 0 0 0 0 0 0
Intersezione \(=2\) pixel, unione \(=6\) pixel (\(4+4-2\)), quindi \(\text{IoU}=2/6\approx0{,}3333\) e \(\text{Dice}=2\cdot2/(4+4)=0{,}5000\) — nota che il Dice è sempre uguale o maggiore dell’IoU per la stessa sovrapposizione.
9.10.5.6 📌 Esempi
| Input | Output | Osservazione |
|---|---|---|
| 4 4 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 |
IoU: 0.3333 Dice: 0.5000 |
Maschere \(4\times4\) con sovrapposizione parziale di 2 pixel. |
%%writefile EP09_05.py
# Codice PythonOverwriting EP09_05.py
TestSuite("EP09_05.py").run()✔️ EP09_05.cases esiste già in casos/
📋 4 caso/i caricato/i da casos/EP09_05.cases
🔍 Test di Python: EP09_05.py
⚠️ EP09_05.py: file vuoto (meno di 3 righe). Test saltati.