Capire e implementare il rilevamento di forme usando la trasformata di Hough con OpenCV e Python

Immagine caratteristica

Oggi impareremo come rilevare linee e cerchi in un’immagine, con l’aiuto di una tecnica chiamata trasformazione di Hough.

Cos’è lo spazio di Hough?

Prima di iniziare ad applicare la trasformata di Hough alle immagini, dobbiamo capire cos’è uno spazio di Hough, e lo impareremo con un esempio.

Spazio dei parametri

Quando lavoriamo con le immagini, possiamo immaginare che l’immagine sia una matrice 2d su alcune coordinate x e y, sotto la quale una linea potrebbe essere descritta come y = mx + b

Spazio dei parametri

Spazio dei parametri

Ma nello spazio dei parametri, che chiameremo spazio Hough, posso rappresentare quella stessa linea come m contro b invece, così la caratterizzazione di una linea sullo spazio immagine, sarà un singolo punto alla posizione m-b nello spazio Hough.

Spazio Hough

Spazio Hough

Ma abbiamo un problema però, con y = mx + b, non possiamo rappresentare una linea verticale, poiché la pendenza è infinita. Quindi abbiamo bisogno di una parametrizzazione migliore, coordinate polari (rho e theta).

Spazio Hough

  • rho: descrive la distanza della linea dall’origine
  • theta: descrive l’angolo lontano dall’orizzontale
Coordinate polari di linea

Coordinate polari di linea

Un’osservazione molto importante, però, è cosa succede quando prendiamo più punti intorno a una linea e li trasformiamo nel nostro spazio Hough.

Punti e linea nello spazio di Hough

Punti e linea nello spazio di Hough

Un singolo punto nello spazio immagine si traduce in una curva nello spazio di Hough, con la particolarità che i punti tra una linea nello spazio immagine saranno rappresentati da più curve con un singolo punto di contatto.

E questo sarà il nostro obiettivo, trovare i punti in cui un gruppo di curve si interseca.

Che cos’è la trasformazione di Hough?

La trasformazione di Hough è un metodo di estrazione di caratteristiche per rilevare forme semplici come cerchi, linee, ecc. in un’immagine.

La caratteristica “semplice” deriva dalla rappresentazione della forma in termini di parametri. Una forma “semplice” sarà rappresentata solo da pochi parametri, per esempio una linea può essere rappresentata dalla sua pendenza e intercetta, o un cerchio che può essere rappresentato da x, y e raggio.

Nel nostro esempio di linea, una trasformazione di Hough sarà responsabile dell’elaborazione dei punti sull’immagine e del calcolo dei valori nello spazio di Hough.

L’algoritmo per far avvenire la trasformazione e successivamente trovare le curve intersecanti è un po’ complicato, e quindi fuori dallo scopo di questo post. Tuttavia daremo un’occhiata a un’implementazione di questo algoritmo, che fa parte della libreria OpenCV.

Rilevare le linee usando OpenCV

In OpenCV, il rilevamento delle linee usando la trasformata di Hough è implementato nelle funzioni HoughLines e HoughLinesP (Probabilistic Hough Transform). Ci concentreremo su quest’ultima.

La funzione si aspetta i seguenti parametri:

  • image: immagine sorgente binaria a 8 bit, a singolo canale. L’immagine può essere modificata dalla funzione.
  • lines: Vettore di linee in uscita. Ogni linea è rappresentata da un vettore di 4 elementi (x_1, y_1, x_2, y_2) , dove (x_1,y_1) e (x_2, y_2) sono i punti finali di ogni segmento di linea rilevato.
  • rho: Risoluzione della distanza dell’accumulatore in pixel.
  • theta: Risoluzione angolare dell’accumulatore in radianti.
  • threshold: Parametro di soglia dell’accumulatore. Vengono restituite solo le linee che ottengono abbastanza voti
  • minLineLength: Lunghezza minima della linea. I segmenti di linea più corti vengono rifiutati.
  • maxLineGap: Spazio massimo consentito tra punti sulla stessa linea per collegarli.

Troppo complicato? è più facile con un esempio:

# Read image img = cv2.imread('lanes.jpg', cv2.IMREAD_COLOR)# Convert the image to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Find the edges in the image using canny detectoredges = cv2.Canny(gray, 50, 200)# Detect points that form a linelines = cv2.HoughLinesP(edges, 1, np.pi/180, max_slider, minLineLength=10, maxLineGap=250)# Draw lines on the imagefor line in lines: x1, y1, x2, y2 = line cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 3)# Show resultcv2.imshow("Result Image", img)

Ecco il risultato:

Esempio di rilevamento di linee

Esempio di rilevamento di linee

E’ molto importante che usiamo effettivamente un’immagine con solo bordo come parametro per la trasformata di Hough, altrimenti l’algoritmo non funzionerà come previsto.

Rilevare cerchi usando OpenCV

Il processo è simile a quello delle linee, con l’eccezione che questa volta useremo una funzione diversa dalla libreria OpenCV. Useremo ora HoughCircles, che accetta i seguenti parametri:

  • image: Immagine di input a 8 bit, a canale singolo, in scala di grigi.
  • circles: Vettore di uscita dei cerchi trovati. Ogni vettore è codificato come un vettore in virgola mobile a 3 elementi (x, y, raggio).
  • circle_storage: Nella funzione C questa è una memoria che conterrà la sequenza in uscita dei cerchi trovati.
  • method: Metodo di rilevamento da utilizzare. Attualmente, l’unico metodo implementato è CV_HOUGH_GRADIENT , che è fondamentalmente 21HT
  • dp: Rapporto inverso della risoluzione dell’accumulatore alla risoluzione dell’immagine. Per esempio, se dp=1 , l’accumulatore ha la stessa risoluzione dell’immagine in entrata. Se dp=2 , l’accumulatore ha la metà della larghezza e dell’altezza.
  • minDist: Distanza minima tra i centri dei cerchi rilevati. Se il parametro è troppo piccolo, possono essere rilevati falsamente più cerchi vicini oltre a uno vero. Se è troppo grande, alcuni cerchi potrebbero essere mancati.
  • param1: Primo parametro specifico del metodo. In caso di CV_HOUGH_GRADIENT , è la soglia più alta delle due passate al rilevatore di bordi Canny() (quella più bassa è due volte più piccola).
  • param2: Secondo parametro specifico del metodo. Nel caso di CV_HOUGH_GRADIENT , è la soglia dell’accumulatore per i centri dei cerchi nella fase di rilevamento. Più piccolo è, più falsi cerchi possono essere rilevati. I cerchi, corrispondenti ai valori più grandi dell’accumulatore, saranno restituiti per primi.
  • minRadius: Raggio minimo del cerchio.
  • maxRadius: Raggio massimo del cerchio.

Ricordate che i parametri devono essere diversi, poiché non possiamo descrivere un cerchio con la stessa parametrizzazione che abbiamo usato per le linee, e invece, dobbiamo usare un’equazione come (x - x0)^^2 + (y - y0)^^2 = r^^2.

E al codice:

# Read image as gray-scaleimg = cv2.imread('circles.png', cv2.IMREAD_COLOR)# Convert to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Blur the image to reduce noiseimg_blur = cv2.medianBlur(gray, 5)# Apply hough transform on the imagecircles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, img.shape/64, param1=200, param2=10, minRadius=5, maxRadius=30)# Draw detected circlesif circles is not None: circles = np.uint16(np.around(circles)) for i in circles: # Draw outer circle cv2.circle(img, (i, i), i, (0, 255, 0), 2) # Draw inner circle cv2.circle(img, (i, i), 2, (0, 0, 255), 3)

Nota che rispetto all’esempio precedente, qui non stiamo applicando nessuna funzione di rilevamento dei bordi. Questo perché la funzione HoughCircles ha il rilevamento canny incorporato.

E il risultato:

Esempio di rilevamento del cerchio

Esempio di rilevamento del cerchio

Conclusione

La trasformata di Hough è una tecnica eccellente per rilevare forme semplici nelle immagini e ha diverse applicazioni, che vanno dalle applicazioni mediche come i raggi X, l’analisi CT e MRI, alle auto a guida autonoma. Se siete interessati a saperne di più sullo spazio di Hough, vi consiglio di eseguire effettivamente il codice, provare diverse configurazioni da soli, e di controllare la documentazione di OpenCV per ulteriori informazioni.

Grazie per aver letto!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.