5.15. Operazioni morfologiche

Le operazioni morfologiche lavorano su immagini binarie – le maschere che escono dalla sogliatura e dal rilevamento dei bordi. Ogni operazione percorre lo stesso tipo di vicinato scorrevole usato dai filtri di levigatura, ma la domanda che pone in ogni posizione è sì/no: ogni pixel nel vicinato è acceso, è acceso almeno un pixel nel vicinato, com’è il pattern di acceso/spento? Le risposte fanno crescere le regioni, le restringono e ne ritagliano nuovamente i confini in modi che un filtro di media non può.

La morfologia è ciò che sta tra una maschera binaria iniziale – l’uscita della sogliatura, del rilevamento dei bordi o di qualche altro classificatore – e la maschera binaria pulita che il resto della pipeline può usare. Un’uscita grezza di sogliatura ha di solito pixel di rumore isolati sparsi tra le aree di vero primo piano, piccoli buchi perforati in regioni altrimenti solide, e confini frastagliati dove la soglia ha tagliato vicino al bordo di un oggetto. La morfologia rimuove questi difetti.

5.15.1. Le quattro operazioni classiche

Due operazioni primitive, e due loro composizioni, costituiscono il kit di strumenti morfologici:

dilate() fa crescere ogni regione di primo piano. La regola è: qualsiasi pixel che ha almeno un vicino di primo piano nella sua finestra (2 * size + 1) diventa primo piano. L’effetto visibile è che le regioni di primo piano si ingrandiscono di size pixel in ogni direzione, e i buchi al loro interno si restringono (o scompaiono) della stessa quantità.

erode() fa l’inverso. Qualsiasi pixel che non ha ogni vicino della sua finestra nel primo piano diventa sfondo. Le regioni di primo piano si rimpiccioliscono di size pixel in ogni direzione, i pixel di primo piano isolati (che non hanno vicini di primo piano) scompaiono completamente, e le piccole connessioni tra regioni più grandi vengono tagliate.

Una regione binaria di ingresso nel pannello più a sinistra: un quadrato pieno con un buco di un pixel al suo centro e tre pixel di rumore isolati intorno ad esso. Quattro pannelli a destra mostrano il risultato dell'applicazione di dilate, erode, open e close a quell'ingresso. Dilate fa crescere la regione, riempie il buco e fa crescere ogni pixel di rumore in un blocco; erode restringe la regione, fa crescere il buco e rimuove il rumore; open rimuove il rumore riproducendo esattamente la regione e il suo buco; close riempie il buco riproducendo esattamente il contorno della regione e i pixel di rumore.

Le quattro operazioni morfologiche classiche applicate a una regione binaria rumorosa. Erode restringe; dilate fa crescere; open è erode seguito da dilate (rimuove il rumore); close è dilate seguito da erode (riempie i buchi).

open() è erode seguito da dilate. L’immagine erosa ha avuto ogni pixel di rumore isolato rimosso, ma è stata anche rimpicciolita di size pixel in ogni direzione. Far seguire l’erode da un dilate della stessa dimensione ripristina le genuine regioni di primo piano ai loro confini originali approssimativi lasciando intanto sparito il rumore. La composizione è ciò che rende open l’operazione standard di «rimozione del rumore» nella morfologia classica: i pixel di primo piano isolati scompaiono, le regioni reali tornano illese.

close() è l’immagine speculare – dilate seguito da erode. Il dilate riempie i piccoli buchi all’interno delle regioni di primo piano e collega regioni separate da piccoli interstizi; l’erode restringe il risultato fino al suo confine esterno originale lasciando solido l’interno riempito. close è l’operazione standard di «riempimento dei piccoli interstizi».

binary_mask.open(1)       # remove single-pixel noise
binary_mask.close(2)      # fill small holes and gaps

Il parametro size ha lo stesso significato che nei filtri di luminosità: size=1 indica un vicinato 3 per 3, size=2 indica 5 per 5, e così via. Dimensioni maggiori significano una pulizia più aggressiva – e un costo per pixel più lungo.

5.15.2. Top-hat e black-hat

Due ulteriori composizioni vale la pena conoscere perché estraggono esattamente le caratteristiche che open e close rimuovono:

top_hat() restituisce la differenza tra l’immagine originale e la sua versione aperta – i pixel di primo piano che open avrebbe rimosso. Si tratta letteralmente di una maschera dei pixel di rumore, delle piccole regioni di primo piano isolate, delle sottili strutture di primo piano che l’operazione open non ha potuto preservare. Utile per estrarre piccole caratteristiche di primo piano quando quelle caratteristiche sono ciò che interessa all’applicazione, anziché rimuoverle.

black_hat() restituisce la differenza tra la versione chiusa dell’immagine e l’originale – i pixel di sfondo che close avrebbe riempito. Si tratta di una maschera dei piccoli buchi all’interno delle regioni di primo piano, degli stretti interstizi tra regioni che l’operazione close avrebbe colmato.

Entrambe sono raggiunte meno comunemente delle quattro operazioni di base, ma il pattern vale la pena ricordarlo – quando un’applicazione ha bisogno di estrarre caratteristiche piccole o sottili che il passaggio di pulizia standard rimuove, top-hat e black-hat sono il modo naturale per recuperarle.

5.15.3. Modalità soglia

Le quattro operazioni morfologiche di base accettano tutte una parola chiave intera threshold che addolcisce il test acceso/spento in ogni posizione. Senza di essa, le operazioni si comportano nel modo descritto sopra: erode() richiede che ogni vicino sia acceso, dilate() richiede almeno uno. Con threshold impostato, ogni operazione tollera quel numero di vicini che votano in senso opposto. Per erode, threshold è il numero di vicini di sfondo che un pixel può avere e sopravvivere comunque: threshold=4 mantiene qualsiasi pixel con almeno quattro degli otto vicini accesi (in una finestra 3 per 3 il pixel centrale ha otto vicini), quindi non erode in modo così aggressivo. Per dilate, threshold è il numero di vicini di primo piano in più che un pixel di sfondo deve avere prima di accendersi: threshold=2 richiede almeno tre vicini di primo piano invece di uno, quindi cresce in modo meno aggressivo.

La forma con soglia è utile per mettere a punto l’aggressività di un passaggio morfologico senza cambiare la dimensione della sua finestra, il che cambierebbe anche la scala delle caratteristiche su cui agisce. La maggior parte delle applicazioni si attiene al comportamento predefinito; la forma con soglia è lì per i casi in cui il valore predefinito è solo leggermente eccessivo o insufficiente.