Questo è un grande problema di modellazione. Ho le seguenti raccomandazioni/idee:
- Dividere l'immagine in RGB quindi elaborare.
- pre-elaborazione.
- Ricerca parametri dinamica.
- Aggiungere vincoli.
- Assicurati di cosa stai cercando di rilevare.
Più in dettaglio:
1: Come già sottolineato in altre risposte, convertendo direttamente al rigetti in mare in scala di grigi troppe informazioni - qualsiasi cerchi con una luminosità simile allo sfondo andranno persi. È molto meglio considerare i canali di colore in isolamento o in uno spazio cromatico diverso. Ci sono due modi per andare qui: eseguire HoughCircles
su ciascun canale pre-elaborato in isolamento, quindi combinare i risultati, o, elaborare i canali, quindi combinarli, quindi operare HoughCircles
. Nel mio tentativo di seguito, ho provato il secondo metodo, suddividendolo in canali RGB, elaborando e combinando. Fai attenzione a saturare l'immagine durante la combinazione, io uso cv.And
per evitare questo problema (in questa fase le mie cerchie sono sempre anelli/dischi neri su sfondo bianco).
2: la pre-elaborazione è piuttosto complicata, e spesso è meglio giocarla. Ho fatto uso di AdaptiveThreshold
che è un metodo di convoluzione molto potente che può migliorare i bordi di un'immagine fissando i pixel in base alla loro media locale (processi simili si verificano anche nella via iniziale del sistema visivo dei mammiferi). Questo è anche utile in quanto riduce un po 'di rumore. Ho usato dilate/erode
con un solo passaggio. E ho mantenuto gli altri parametri come li hai avuti. Sembra che l'utilizzo di Canny
prima del HoughCircles
aiuti molto nel trovare "cerchi pieni", quindi probabilmente è meglio tenerlo dentro. Questa pre-elaborazione è piuttosto pesante e può portare a falsi positivi con un po 'più "cerchi", ma nel nostro caso questo è forse desiderabile?
3: Come hai notato HoughCircles parametro param2
(il tuo parametro LOW
) deve essere regolata per ogni immagine al fine di ottenere una soluzione ottimale, infatti dal docs:
Il più piccolo si è , più cerchi falsi possono essere rilevati.
Il problema è che il punto debole sarà diverso per ogni immagine. Penso che l'approccio migliore qui sia quello di impostare una condizione e fare una ricerca attraverso diversi valori param2
fino a quando questa condizione non viene soddisfatta. Le tue immagini mostrano cerchi non sovrapposti, e quando param2
è troppo basso otteniamo in genere un sacco di cerchi sovrapposti. Quindi suggerisco cercando il:
numero massimo di non sovrapposizione, e circoli non confinate
Così continuiamo a chiamare HoughCircles con valori diversi di param2
fino a quando questo è soddisfatta. Lo faccio nel mio esempio qui sotto, semplicemente incrementando param2
fino a raggiungere il presupposto della soglia.Sarebbe molto più veloce (e abbastanza facile da fare) se si esegue una ricerca binaria per trovare quando viene soddisfatta, ma è necessario fare attenzione con la gestione delle eccezioni in quanto spesso spesso gli errori vengono visualizzati per valori dall'aspetto innocente di param2
(almeno in la mia installazione). Una condizione diversa a cui sarebbe molto utile confrontarsi sarebbe il numero di cerchi.
4: Ci sono altri vincoli che possiamo aggiungere al modello? Più cose possiamo dire al nostro modello, il compito più facile che possiamo fare per rilevare i cerchi. Ad esempio, sappiamo:
- Il numero di cerchi. - anche un limite superiore o inferiore è utile.
- Possibili colori dei cerchi, o dello sfondo, o di 'non-cerchi'.
- Le loro dimensioni.
- Dove possono essere in un'immagine.
5: Alcuni dei BLOB nelle immagini potrebbero essere definiti solo vagamente! Considera i due "blob non circolari" nella tua seconda immagine, il mio codice non li trova (buoni!), Ma ... se li faccio "photoshop" in modo che siano più circolari, il mio codice li può trovare ... Forse se vuoi rilevare cose che non sono cerchi, un approccio diverso come Tim Lukins
potrebbe essere migliore.
Problemi
Facendo pesante pre-elaborazione AdaptiveThresholding
e `Canny' non ci può essere un sacco di distorsione alle caratteristiche di un'immagine, che possono portare al rilevamento cerchio falso, o segnalazione di raggio errato. Ad esempio un grande disco solido dopo l'elaborazione può apparire un anello, quindi HughesCircles potrebbe trovare l'anello interno. Inoltre, anche i documenti notano che:
... in genere la funzione rileva bene i centri dei cerchi, tuttavia potrebbe non riuscire a trovare i raggi corretti.
Se avete bisogno di rilevamento raggi più accurata, suggerisco il seguente approccio (non implementato):
- sull'immagine originale, ray-trace dal centro riferito di circolo, in una croce in espansione (4 raggi: alto/basso/sinistra/destra)
- fare questo separatamente a ciascun canale RGB
- Combinare queste informazioni per ogni canale per ogni raggio in modo sensibile (cioè capovolgere, offset, scala, ecc come necessario)
.
- prendere l'averag e per i primi pochi pixel su ciascun raggio, utilizzare questo per rilevare dove si verifica una deviazione significativa sul raggio.
- Questi 4 punti sono stime di punti sulla circonferenza.
- Utilizzare queste quattro stime per determinare un raggio più preciso e una posizione centrale (!).
- Questo potrebbe essere generalizzato utilizzando un anello espandibile invece di quattro raggi.
Risultati
Il codice alla fine non abbastanza bene un bel po 'di tempo, questi esempi sono stati fatti con il codice come mostrato:
rileva tutti i cerchi in prima immagine:
Come l'immagine preelaborata guarda prima che venga applicato il filtro canny (le diverse circonferenze di colore sono molto visibili):
Rileva tutti tranne due (blob) seconda immagine:
seconda immagine Altered (macchie sono cerchio-afied e ovale grande rese più circolare, migliorando così rilevamento), tutti rilevate:
praticamente fa bene nel rilevare i centri in questo dipinto di Kandinsky (non riesco a trovare anelli concentrici a causa delle sue condizioni al contorno).
Codice:
import cv
import numpy as np
output = cv.LoadImage('case1.jpg')
orig = cv.LoadImage('case1.jpg')
# create tmp images
rrr=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
ggg=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
bbb=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
processed = cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
def channel_processing(channel):
pass
cv.AdaptiveThreshold(channel, channel, 255, adaptive_method=cv.CV_ADAPTIVE_THRESH_MEAN_C, thresholdType=cv.CV_THRESH_BINARY, blockSize=55, param1=7)
#mop up the dirt
cv.Dilate(channel, channel, None, 1)
cv.Erode(channel, channel, None, 1)
def inter_centre_distance(x1,y1,x2,y2):
return ((x1-x2)**2 + (y1-y2)**2)**0.5
def colliding_circles(circles):
for index1, circle1 in enumerate(circles):
for circle2 in circles[index1+1:]:
x1, y1, Radius1 = circle1[0]
x2, y2, Radius2 = circle2[0]
#collision or containment:
if inter_centre_distance(x1,y1,x2,y2) < Radius1 + Radius2:
return True
def find_circles(processed, storage, LOW):
try:
cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, LOW)#, 0, 100) great to add circle constraint sizes.
except:
LOW += 1
print 'try'
find_circles(processed, storage, LOW)
circles = np.asarray(storage)
print 'number of circles:', len(circles)
if colliding_circles(circles):
LOW += 1
storage = find_circles(processed, storage, LOW)
print 'c', LOW
return storage
def draw_circles(storage, output):
circles = np.asarray(storage)
print len(circles), 'circles found'
for circle in circles:
Radius, x, y = int(circle[0][2]), int(circle[0][0]), int(circle[0][1])
cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
#split image into RGB components
cv.Split(orig,rrr,ggg,bbb,None)
#process each component
channel_processing(rrr)
channel_processing(ggg)
channel_processing(bbb)
#combine images using logical 'And' to avoid saturation
cv.And(rrr, ggg, rrr)
cv.And(rrr, bbb, processed)
cv.ShowImage('before canny', processed)
# cv.SaveImage('case3_processed.jpg',processed)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.ShowImage('processed', processed)
#find circles, with parameter search
storage = find_circles(processed, storage, 100)
draw_circles(storage, output)
# show images
cv.ShowImage("original with circles", output)
cv.SaveImage('case1.jpg',output)
cv.WaitKey(0)
Che aspetto ha l'immagine preelaborata? Cioè 'elaborato' prima della chiamata a' HoughCircles'. – Eric
@Eric Ho aggiunto le immagini post-elaborate che vengono inserite in 'HoughCircles'. Spero possa aiutare. – memyself
Grazie. Potresti descrivere anche per i tuoi due esempi qual è il tuo output previsto? Tutti i doodle o quelli specifici? – Eric