L’elaborazione delle immagini è un campo cruciale in molteplici discipline, dalle applicazioni mediche all’automazione industriale. OpenCV (Open Source Computer Vision Library) rappresenta un potente strumento in questo contesto, offrendo una vasta gamma di funzionalità per la manipolazione e l’analisi delle immagini. Tra le tecniche più utilizzate vi sono le trasformazioni morfologiche, che consentono di modellare la forma degli oggetti presenti in un’immagine.
[wpda_org_chart tree_id=37 theme_id=50]
Questo articolo si concentrerà sulle trasformazioni morfologiche in OpenCV utilizzando il linguaggio di programmazione Python. Esploreremo come queste operazioni possano essere impiegate per manipolare la forma degli oggetti in un’immagine, aprendo la porta a una vasta gamma di applicazioni pratiche.
Le Trasformazioni Morfologiche
Le trasformazioni morfologiche svolgono un ruolo cruciale nell’elaborazione delle immagini e nella visione artificiale. Esse consentono di affrontare sfide comuni come la rimozione del rumore, la separazione di oggetti connessi e la chiusura di buchi presenti nelle immagini. Comprendere queste tecniche è fondamentale per coloro che lavorano con dati visivi, poiché forniscono strumenti potenti per migliorare la qualità e l’utilità delle immagini elaborate.
Immagini Binarie e Scala di Grigi
Per comprendere appieno le trasformazioni morfologiche, è essenziale avere una solida comprensione delle immagini binarie e in scala di grigi. Un’immagine binaria è composta solo da pixel neri e bianchi, senza sfumature intermedie. Queste immagini sono spesso utilizzate per rappresentare informazioni di tipo on/off o presenza/assenza.
Le immagini in scala di grigi, d’altra parte, contengono livelli di intensità dei pixel che vanno da 0 (nero) a 255 (bianco), con sfumature di grigio intermedie. Queste immagini sono più realistiche e contengono una maggiore quantità di informazioni rispetto alle immagini binarie.
Kernel
Il kernel è una matrice quadrata o rettangolare di dimensioni definite che viene utilizzata nelle trasformazioni morfologiche. È posizionato sull’immagine e scansiona attraverso i pixel. I valori nel kernel influenzano la trasformazione del pixel corrispondente nell’immagine di output.
I valori del kernel sono spesso definiti come 0 e 1, dove 1 rappresenta l’area di interesse. Ad esempio, un kernel di dimensioni 3×3 potrebbe avere l’aspetto seguente:
1 1 1
1 1 1
1 1 1
Questo kernel uniforme è comunemente usato in operazioni come la dilatazione e l’erosione.
Implementazione Pratica
Prima di procedere con le trasformazioni morfologiche, è fondamentale caricare un’immagine e convertirla nel formato appropriato, in genere in scala di grigi. Questo passaggio preparatorio è essenziale per garantire che le trasformazioni possano essere applicate correttamente.
Partiamo da un’immagine come la seguente (la Mole Antonelliana di Torino) e che chiameremo Torino.jpg e che utilizzeremo per tutto l’articolo:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Carica un'immagine a colori
image = cv2.imread('Torino.jpg')
# Converti l'immagine in scala di grigi
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Visualizza l'immagine in scala di grigi
plt.imshow(gray_image, cmap='gray')
plt.title('Grayscale image')
plt.axis('off') # Nasconde gli assi
plt.show()
Eseguendo la conversione si otterrà la nuova immagine:
Con questa comprensione preliminare, possiamo procedere a esplorare le trasformazioni morfologiche di base, come l’erosione e la dilatazione, nelle sezioni successive.
L’Erosione
L’erosione è una delle trasformazioni morfologiche fondamentali utilizzate in OpenCV per manipolare la forma degli oggetti in un’immagine. Questa operazione comporta la riduzione delle regioni di interesse, eliminando i contorni e restringendo le caratteristiche degli oggetti presenti.
L’erosione avviene applicando un kernel all’immagine, posizionandolo su ciascun pixel e modificando il valore del pixel in base ai valori nel kernel. Se tutti i pixel coperti dal kernel sono “attivi” (con valore 1), il pixel centrale mantiene il suo valore; altrimenti, viene “eroso” (impostato a 0).
Il risultato è una riduzione della dimensione degli oggetti nell’immagine, rendendoli più sottili e separati.
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Carica un'immagine in scala di grigi
img = cv2.imread('Torino.jpg', 0)
# Definisci il kernel per l'erosione
kernel = np.ones((5, 5), np.uint8)
# Applica l'erosione all'immagine
erosion = cv2.erode(img, kernel, iterations=1)
# Visualizza l'immagine originale e quella sottoposta a erosione
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(img, cmap='gray')
plt.axis('off') # Nasconde gli assi
plt.title('Original Image')
plt.subplot(1, 2, 2)
plt.imshow(erosion, cmap='gray')
plt.axis('off') # Nasconde gli assi
plt.title('Image After Erosion')
plt.show()
Nel codice di esempio, l’immagine in scala di grigi viene sottoposta a un’operazione di erosione utilizzando un kernel quadrato di dimensioni 5×5. L’effetto dell’erosione è evidenziato visualizzando l’immagine prima e dopo l’applicazione dell’operazione.
L’erosione trova applicazione in diversi contesti, tra cui la rimozione del rumore, la separazione di oggetti connessi e la definizione di confini più precisi tra oggetti. Nei prossimi paragrafi, esploreremo applicazioni pratiche dell’erosione attraverso esempi specifici e ulteriori dettagli sull’implementazione.
La Dilatazione
La dilatazione è un’altra operazione fondamentale nelle trasformazioni morfologiche, spesso utilizzata in combinazione con l’erosione. La dilatazione agisce per espandere le regioni di interesse di un’immagine, aumentando la dimensione degli oggetti presenti.
Analogamente all’erosione, la dilatazione coinvolge l’applicazione di un kernel sull’immagine. Tuttavia, in questo caso, il valore del pixel centrale nell’immagine di output è determinato considerando i pixel coperti dal kernel. Se almeno uno di essi è “attivo” (con valore 1), il pixel centrale viene impostato a 1.
Il risultato è un aumento delle dimensioni degli oggetti, unione di regioni separate e chiusura di eventuali buchi presenti.
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Carica un'immagine in scala di grigi
img = cv2.imread('Torino.jpg', 0)
# Definisci il kernel per la dilatazione
kernel = np.ones((5, 5), np.uint8)
# Applica la dilatazione all'immagine
dilation = cv2.dilate(img, kernel, iterations=1)
# Visualizza l'immagine originale e quella sottoposta a dilatazione
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(img, cmap='gray')
plt.axis('off') # Nasconde gli assi
plt.title('Original Image')
plt.subplot(1, 2, 2)
plt.imshow(dilation, cmap='gray')
plt.axis('off') # Nasconde gli assi
plt.title('Image after Dilation')
plt.show()
Nel codice di esempio, l’immagine in scala di grigi viene dilatata utilizzando un kernel quadrato di dimensioni 5×5. La differenza tra l’immagine originale e quella sottoposta a dilatazione è evidenziata attraverso la visualizzazione.
La dilatazione trova ampio impiego in diverse situazioni, come il riempimento di buchi nelle immagini, la connessione di componenti separati e la rendizione più prominente degli oggetti. Nei prossimi paragrafi, esploreremo applicazioni pratiche della dilatazione con esempi specifici e dettagli implementativi.
L’Apertura e la Chiusura
Le operazioni di apertura e chiusura sono combinazioni di erosione e dilatazione che permettono di ottenere risultati più specifici nelle trasformazioni morfologiche.
L’apertura è una sequenza di erosione seguita da dilatazione. Questa operazione è utile per rimuovere il rumore nell’immagine, separare oggetti connessi e aprirli, nonché per mantenere le dimensioni degli oggetti originali.
La chiusura è l’operazione inversa, ovvero una sequenza di dilatazione seguita da erosione. Questa tecnica è efficace nel chiudere piccoli buchi nell’immagine, unire regioni di oggetti e mantenere la forma generale degli oggetti originali.
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Carica un'immagine in scala di grigi
img = cv2.imread('Torino.jpg', 0)
# Definisci il kernel per apertura e chiusura
kernel = np.ones((5, 5), np.uint8)
# Applica l'apertura
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# Applica la chiusura
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
# Visualizza l'immagine originale e quelle sottoposte ad apertura e chiusura
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(opening, cmap='gray')
plt.axis('off') # Nasconde gli assi
plt.title('Immage after Opening')
plt.subplot(1, 2, 2)
plt.imshow(closing, cmap='gray')
plt.axis('off') # Nasconde gli assi
plt.title('Immage after Closing')
plt.show()
Nel codice di esempio, l’apertura e la chiusura vengono applicate all’immagine in scala di grigi utilizzando un kernel quadrato di dimensioni 5×5. Osserva come queste operazioni influenzano la rimozione del rumore e la chiusura di eventuali buchi.
L’apertura e la chiusura sono particolarmente utili in contesti dove è necessario pulire l’immagine o mantenere determinate caratteristiche degli oggetti. Nelle prossime sezioni, esploreremo casi specifici in cui queste operazioni sono cruciali, fornendo dettagli implementativi e risultati visivi.