Introduzione
Ecco come promesso, un altro articolo della lunga serie di articoli riguardanti il mondo di OpenCV e della programmazione con Python. In un precedente articolo abbiamo già visto il concetto di edge detection, in questo articolo analizzeremo insieme un particolare algoritmo chiamato Canny Edge Detection.
Se vuoi sapere di più, questo è l’articolo precedente:
Canny Edge Detection
Nella panoramica degli algoritmi sviluppati per la edge detection, il Canny Edge Detection è abbastanza popolare e deve il suo nome proprio a chi lo ha sviluppato, John F. Canny.
Questo algoritmo ha una serie di caratteristiche interessanti, infatti è un algoritmo a più stadi
- Noise Reduction
- Ricerca del gradiente di intensità
- Non-Maximum soppression
- Hysteresis Thresholding
Il primo stadio richiede la rimozione del rumore di fondo dell’immagine (Noise Reduction), dato che la edge detection può essere molto influenzata dalla sua presenza. Quindi si utilizza un filtro Gaussiano 5×5 per la rimozione (riduzione) del rumore di fondo.
Poi il secondo stadio coinvolge la ricerca del gradiente di intensità dell’immagine (Intensity Gradient). L’immagine viene filtrata con un kernel Sobel sia in direzione verticale che orizzontale ottenendo così le derivate prime nelle due direzioni (Gx e Gy). Da queste due immagini, si trova poi l’edge gradient G.
e la direzione del gradiente per ciascun pixel
,
La direzione del gradiente è sempre perpendicolare agli edge. Viene arrotondato ad 1 per i quattro angoli che rappresentano le due direzioni diagonali, e quella verticale ed orizzontale.
Lo stadio successivo è la Non-maximum Suppression, in cui alcuni pixel indesiderati vengono rimossi in modo che non potranno essere confusi come edge. Per fare ciò, viene analizzata l’intera immagine, controllando se ciascun pixel è un massimo locale nella direzione del gradiente relativamente alla sua area.
Se osserviamo la figura sopra, vediamo l’analisi del punto A. Questo punto si trova sull’edge considerato nella direzione verticale. Sappiamo che la direzione del gradiente è perpendicolare all’edge. Quindi analizziamo i punti B e C che si trovano sulla direzione del gradiente. A questo punto, il punto A viene analizzato insieme ai punti B e C per vedere se forma un massimo locale. Se lo forma, allora viene considerato nel prossimo stadio, altrimenti viene rimosso, ponendolo a valore zero.
Infine l’ultimo stadio è l’applicazione di una soglia di isteresi (Hysteresis Thresholding). In questo stadio finale si decide veramente quali edge sono realmente tali e quali non lo sono affatto. Per questa operazione è necessario stabilire due valori di soglia, uno minimo minVal, e uno massimo maxVal. Qualsiasi edge con un gradiente di intensità maggiore di maxVal sarà sicuramente un edge, e quelli con un valore minore di min Val non lo saranno affatto, e verranno scartati. Per tutti gli altri edge che si trovassero nel range tra questi due valori di soglia vengono sottoposti ad una ulteriore analisi, stabilendo se siano tali attraverso la loro connettività. Se essi sono connessi ad un edge di quelli già accertati allora viene anch’esso considerato come tale, altrimenti scartato.
Prerequisiti prima di cominciare a programmare
Per eseguire il codice presente in questo articolo sono necessari alcuni prerequisiti. Per prima cosa è necessario far partire l’ambiente virtuale su cui avete compilato ed installato la libreria OpenCV.
source ~/.profile workon cv (cv) $
Se non aveste ancora installato OpenCV, seguite la procedura di installazione descritta in questo articolo.
Inoltre in questo esempio per migliorare la visualizzazione di più immagini contemporaneamente, sfrutteremo la libreria grafica matplotlib. Se ancora non l’avete installata sul vostro ambiente virtuale potete farlo scrivendo
pip --no-cache-dir install matplotlib
poi installate il pacchetto TKinter necessario per il corretto funzionamento di matplotlib.
sudo apt-get install python-tk
Faremo qualche prova anche su un’immagine a colori e ricca di bordi e di gradienti di colore. Scaricate l’immagine seguente (se non l’avete già fatto negli esempi precedenti) e salvatela come logos.jpg.
Programmiamo con Python l’algoritmo Canny Edge Detection
In OpenCV, questo tipo di edge detection è già stata implementata e si attiva richiamando la funzione cv2.Canny().
cv2.Canny(image, minVal, maxVal)
Questa funzione accetta tre argomenti. Il primo argomento è proprio l’immagine da sottoporre ad analisi, il secondo ed il terzo argomento sono i valori di minVal e maxVal rispettivamente, di cui abbiamo parlato in precedenza.
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('logos.jpg',0) edges = cv2.Canny(img,100,110) plt.subplot(2,1,1),plt.imshow(img,cmap = 'gray') plt.title('Original Image'), plt.xticks([]), plt.yticks([]) plt.subplot(2,1,2),plt.imshow(edges,cmap = 'gray') plt.title('Canny Edge Detection'), plt.xticks([]), plt.yticks([]) plt.show()
Se eseguiamo il codice otterremo i risultati seguenti.
Altre prove sulle immagini
Passiamo ora ad effettuare l’analisi su una fotografia ricca di particolari e contorni. A tale proposito ho scelto gli ingranaggi di un orologio.
Utilizzerò questa immagine per vedere i bordi selezionati a seconda dei valori di thresholding minVal e maxVal. Ecco i risultati ottenuti.
Questi sono i bordi selezionati con i valori standard minVal = 100, maxVal = 200. Come possiamo vedere i contorni del meccanismo sono stati ben individuati.
Passando ad un threshold troppo altro, parecchi particolari vengono persi, e molti contorni diventano approssimativi. minVal = 300 maxVal = 350
Vediamo nel caso opposto, un threshold troppo basso fa passare troppi falsi contorni e l’immagine si arricchisce di troppi particolari inutili. minVal = 30 maxVal = 35.
Conclusione
Come hai visto in questo articolo, l’applicazione dell’algoritmo Canny Edge Detection è davvero molto semplice grazie proprio alla libreria OpenCV. In altri articoli della stessa serie, potrai approfondire ancora di più l’argomento della edge detection.