2012-03-25 12 views
43

ho scritto il seguente codice python molto semplice da trovare cerchi in un'immagine:scrittura robusta (colore e dimensione invariante) rilevamento cerchio con OpenCV (sulla base di Hough trasformare o altre caratteristiche)

import cv 
import numpy as np 

WAITKEY_DELAY_MS = 10 
STOP_KEY = 'q' 

cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE); 
cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE); 

key_pressed = False 
while key_pressed != STOP_KEY: 

    # grab image 
    orig = cv.LoadImage('circles3.jpg') 

    # create tmp images 
    grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1) 
    processed = cv.CreateImage(cv.GetSize(orig), 8, 1) 


    cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3) 

    cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY) 

    # do some processing on the grey scale image 
    cv.Erode(grey_scale, processed, None, 10) 
    cv.Dilate(processed, processed, None, 10) 
    cv.Canny(processed, processed, 5, 70, 3) 
    cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15) 

    storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3) 

    # these parameters need to be adjusted for every single image 
    HIGH = 50 
    LOW = 140 

    try: 
     # extract circles 
     cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, HIGH, LOW) 

     for i in range(0, len(np.asarray(storage))): 
      print "circle #%d" %i 
      Radius = int(np.asarray(storage)[i][0][2]) 
      x = int(np.asarray(storage)[i][0][0]) 
      y = int(np.asarray(storage)[i][0][1]) 
      center = (x, y) 

      # green dot on center and red circle around 
      cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0) 
      cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0) 

      cv.Circle(processed, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0) 
      cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0) 

    except: 
     print "nothing found" 
     pass 

    # show images 
    cv.ShowImage("image - press 'q' to quit", orig) 
    cv.ShowImage("post-process", processed) 

    cv_key = cv.WaitKey(WAITKEY_DELAY_MS) 
    key_pressed = chr(cv_key & 255) 

Come si può vedere dai seguenti due esempi, la 'qualità ritrovamento cerchio' varia parecchio:

CASE1:

input1 detection1 post-processed1

Case2:

input2 detection2 post-processed2

Case1 e Case2 sono fondamentalmente la stessa immagine, ma ancora l'algoritmo rileva diversi ambienti. Se presento all'algoritmo un'immagine con cerchi di dimensioni diverse, il rilevamento del cerchio potrebbe anche fallire completamente. Ciò è dovuto principalmente ai parametri HIGH e LOW che devono essere regolati individualmente per ogni nuova immagine.

Pertanto la mia domanda: quali sono le varie possibilità di rendere questo algoritmo più robusto? Dovrebbe essere di dimensioni e colori invariabili in modo che vengano rilevati cerchi diversi con colori diversi e di dimensioni diverse. Forse usare la trasformazione di Hough non è il modo migliore di fare le cose? Ci sono approcci migliori?

+0

Che aspetto ha l'immagine preelaborata? Cioè 'elaborato' prima della chiamata a' HoughCircles'. – Eric

+0

@Eric Ho aggiunto le immagini post-elaborate che vengono inserite in 'HoughCircles'. Spero possa aiutare. – memyself

+0

Grazie. Potresti descrivere anche per i tuoi due esempi qual è il tuo output previsto? Tutti i doodle o quelli specifici? – Eric

risposta

32

La seguente è basata sulla mia esperienza come ricercatore visione. Dalla tua domanda sembra che tu sia interessato a possibili algoritmi e metodi piuttosto che a un pezzo di codice funzionante. Per prima cosa do uno script Python veloce e sporco per le immagini di esempio e alcuni risultati dimostrano che potrebbe risolvere il tuo problema. Dopo averli tolti di mezzo, cerco di rispondere alle tue domande relative agli algoritmi di rilevamento robusti.

risultati rapidi

Alcune immagini di esempio (tutte le immagini a parte vostra vengono scaricati da flickr.com e sono concessi in licenza CC) con i cerchi rilevati (senza cambiare/messa a punto parametri, esattamente il seguente codice viene utilizzato per estrarre i cerchi in tutte le immagini): detected blobs in the sample image 1 detected blobs in the sample image 2 lots of circles blobs in the flickr image 1

codice (in base alla Blob rilevatore MSER) ​​

Ed ecco il codice:

import cv2 
import math 
import numpy as np 

d_red = cv2.cv.RGB(150, 55, 65) 
l_red = cv2.cv.RGB(250, 200, 200) 

orig = cv2.imread("c.jpg") 
img = orig.copy() 
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 

detector = cv2.FeatureDetector_create('MSER') 
fs = detector.detect(img2) 
fs.sort(key = lambda x: -x.size) 

def supress(x): 
     for f in fs: 
       distx = f.pt[0] - x.pt[0] 
       disty = f.pt[1] - x.pt[1] 
       dist = math.sqrt(distx*distx + disty*disty) 
       if (f.size > x.size) and (dist<f.size/2): 
         return True 

sfs = [x for x in fs if not supress(x)] 

for f in sfs: 
     cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), d_red, 2, cv2.CV_AA) 
     cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), l_red, 1, cv2.CV_AA) 

h, w = orig.shape[:2] 
vis = np.zeros((h, w*2+5), np.uint8) 
vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR) 
vis[:h, :w] = orig 
vis[:h, w+5:w*2+5] = img 

cv2.imshow("image", vis) 
cv2.imwrite("c_o.jpg", vis) 
cv2.waitKey() 
cv2.destroyAllWindows() 

Come si può vedere è basato sul rivelatore MSER blob. Il codice non pre-elaborazione dell'immagine a parte la semplice mappatura in scala di grigi. In questo modo si prevede che manchino quei deboli blob gialli nelle immagini.

Teoria

In breve: non ci dici quello che sai sul problema oltre a dare solo due immagini di esempio, senza descrizione di essi. Qui spiego perché, a mio modesto parere, è importante avere più informazioni sul problema prima di chiedere quali sono i metodi efficaci per attaccare il problema.

Torna alla domanda principale: qual è il metodo migliore per questo problema? Consideriamo questo come un problema di ricerca. Per semplificare la discussione assumiamo che cerchiamo cerchi con una dimensione/raggio determinati. Quindi, il problema si riduce alla ricerca dei centri.Ogni pixel è un centro candidato, quindi lo spazio di ricerca contiene tutti i pixel.

P = {p1, ..., pn} 
P: search space 
p1...pn: pixels 

Per risolvere questo problema ricerca altre due funzioni devono essere definite:

E(P) : enumerates the search space 
V(p) : checks whether the item/pixel has the desirable properties, the items passing the check are added to the output list 

Supponendo che la complessità dell'algoritmo non importa, la ricerca esaustiva o forza bruta può essere utilizzato in cui E prende ogni pixel e passa a V. Nelle applicazioni in tempo reale è importante ridurre lo spazio di ricerca e ottimizzare l'efficienza computazionale di V.

Ci stiamo avvicinando alla domanda principale. Come potremmo definire V, per essere più precisi quali proprietà dei candidati dovrebbero essere le misure e come dovrebbe risolvere il problema della dicotomia di dividerli in desiderabili e indesiderabili. L'approccio più comune è trovare alcune proprietà che possono essere utilizzate per definire semplici regole decisionali basate sulla misurazione delle proprietà. Questo è quello che stai facendo per tentativi ed errori. Stai programmando un classificatore imparando da esempi positivi e negativi. Questo perché i metodi che stai utilizzando non hanno idea di cosa vuoi fare. È necessario regolare/regolare i parametri della regola decisionale e/o preelaborare i dati in modo tale che la variazione delle proprietà (dei candidati desiderabili) utilizzata dal metodo per il problema della dicotomia sia ridotta. È possibile utilizzare un algoritmo di apprendimento automatico per trovare i valori dei parametri ottimali per un determinato set di esempi. Esiste tutta una serie di algoritmi di apprendimento dagli alberi decisionali alla programmazione genetica che puoi utilizzare per questo problema. È anche possibile utilizzare un algoritmo di apprendimento per trovare i valori dei parametri ottimali per diversi algoritmi di rilevamento cerchio e vedere quale fornisce una migliore precisione. Ciò comporta l'onere principale dell'algoritmo di apprendimento necessario per raccogliere immagini di esempio.

L'altro approccio per migliorare la robustezza che viene spesso trascurato è quello di utilizzare ulteriori informazioni prontamente disponibili. Se conosci il colore dei cerchi con uno sforzo extra virtualmente pari a zero, potresti migliorare significativamente la precisione del rilevatore. Se conoscessi la posizione dei cerchi sull'aereo e volevi rilevare i cerchi imaged, dovresti ricordare che la trasformazione tra questi due gruppi di posizioni è descritta da un'omografia 2D. E l'omografia può essere stimata usando solo quattro punti. Quindi potresti migliorare la robustezza per avere un metodo solido. Il valore della conoscenza specifica del dominio è spesso sottovalutato. Guardala in questo modo, nel primo approccio cerchiamo di approssimare alcune regole decisionali basate su un numero limitato di campioni. Nel secondo approccio conosciamo le regole decisionali e abbiamo solo bisogno di trovare un modo per utilizzarle in modo efficace in un algoritmo.

Sommario

In sintesi, ci sono due approcci per migliorare l'accuratezza/robustezza della soluzione:

  1. strumento basato su: trovare una più facile da usare l'algoritmo/con il minor numero di parametri/tweaking dell'algoritmo/automazione di questo processo utilizzando algoritmi di apprendimento automatico
  2. basato su informazioni: stai utilizzando tutte le informazioni prontamente disponibili? Nella domanda non menzionerai ciò che sai del problema.

Per queste due immagini che hai condiviso userei un blob detector non il metodo HT. Per la sottrazione dello sfondo, suggerirei di provare a stimare il colore dello sfondo, poiché nelle due immagini non varia, mentre il colore dei cerchi varia. E la maggior parte dell'area è spoglia.

+0

Thx per l'eccellente post. Puoi commentare perché hai deciso di cercare cerchi usando un rivelatore di blob piuttosto che HT? Vorresti invece usare HT se i cerchi fossero perfettamente circolari (cioè stampati su una pagina usando un computer), o potessero essere trasformati in cerchi perfetti (cioè usando l'omografia 2D)? – the911s

+0

La funzione cv2.cv.RGB() non è più disponibile nella nuova versione di opencv, qual è il nuovo nome di esso? – linuscl

+0

@linuscl nella nuova versione, puoi usare una tupla per i colori, 'd_red = (56, 55, 150)'. Nota che questo è nello spazio colore dell'immagine, BGR, non RGB, quindi scambiato i valori R e B. – fireant

6

La trasformazione di Hough utilizza un "modello" per trovare determinate funzioni in un'immagine (normalmente) rilevata dal bordo, come forse sapete. Nel caso di HoughCircles quel modello è un cerchio perfetto. Ciò significa che probabilmente non esiste una combinazione di parametri che consenta di rilevare i cerchi più irregolari ed ellittici nella tua immagine senza aumentare il numero di falsi positivi. D'altra parte, a causa del meccanismo di voto sottostante, un cerchio perfetto non chiuso o un cerchio perfetto con una "ammaccatura" potrebbero apparire in modo coerente. Quindi, a seconda dell'output atteso potresti voler o meno utilizzare questo metodo.

Detto questo, ci sono alcune cose che vedo che potrebbe aiutare il vostro cammino con questa funzione:

  1. HoughCircles chiamate Canny internamente, quindi credo che si può lasciare che chiamano fuori.
  2. param1 (che si chiama HIGH) viene in genere inizializzato attorno a un valore di 200. Viene utilizzato come parametro per la chiamata interna a Canny: cv.Canny(processed, cannied, HIGH, HIGH/2). Potrebbe essere utile eseguire lo Canny in questo modo per vedere come l'impostazione HIGH influisce sull'immagine con la trasformata di Hough.
  3. param2 (che si chiama LOW) viene in genere inizializzato attorno al valore 100. È la soglia di voto per gli accumulatori della trasformazione di Hough. Impostarlo più in alto significa più falsi negativi, più bassi falsi positivi. Credo che questo sia il primo con cui vuoi iniziare a trafficare.

Rif: http://docs.opencv.org/3.0-beta/modules/imgproc/doc/feature_detection.html#houghcircles

Aggiornamento Re: pieno cerchi: Dopo aver trovato le forme cerchio con la trasformata di Hough è possibile verificare se sono riempiti campionando il colore di confine e confrontandola con quella o più punti all'interno del cerchio presunto. In alternativa è possibile confrontare uno o più punti all'interno del cerchio presunto con un determinato colore di sfondo. Il cerchio viene riempito se il precedente confronto ha esito positivo, o nel caso del confronto alternativo se fallisce.

+1

se lascio 'Canny' fuori, quindi il risultato cambia drasticamente. – memyself

8

Guardando attraverso il codice, ho notato quanto segue:

  • la conversione in scala di grigi. Capisco perché lo stai facendo, ma ti rendi conto che stai lanciando informazioni di via dello lì. Come vedi nelle immagini "post processo", le tue cerchie gialle sono della stessa intensità dello sfondo, solo in un colore diverso.

  • Rilevamento dei bordi dopo la rimozione del rumore (erae/dilata). Questo non dovrebbe essere necessario; Canny dovrebbe prendersene cura.

  • Rilevamento bordo cann. I cerchi "aperti" hanno due bordi, un bordo interno ed uno esterno. Dal momento che sono abbastanza vicini, il filtro Canny gauss potrebbe aggiungerli. In caso contrario, avrai due bordi vicini tra loro. Cioè prima di Canny, hai cerchi aperti e pieni. Successivamente, hai rispettivamente 0/2 e 1 margine. Poiché Hough chiama nuovamente Canny, nel primo caso i due bordi potrebbero essere uniformati (a seconda della larghezza iniziale), motivo per cui l'algoritmo di Hough può trattare i cerchi aperti e quelli pieni uguali.

Quindi, la mia prima raccomandazione sarebbe quella di modificare la mappatura in scala di grigi. Non usare intensità, ma usa tonalità/saturazione/valore. Inoltre, usa un approccio differenziale: stai cercando i bordi. Quindi, calcolare una trasformazione HSV, smussare una copia e quindi fare la differenza tra la copia originale e quella lisciata. Questo ti darà dH, dS, dV valori (variazione locale in tonalità, saturazione, valore) per ogni punto. Piazza e aggiungi per ottenere un'immagine monodimensionale, con picchi vicino a tutti i bordi (interno ed esterno).

La mia seconda raccomandazione sarebbe la normalizzazione locale, ma non sono sicuro che sia addirittura necessario. L'idea è che non ti interessa particolarmente il valore esatto del segnale di bordo che hai ottenuto, dovrebbe essere davvero binario (edge ​​o no). Pertanto, è possibile normalizzare ciascun valore dividendo per una media locale (dove locale è nell'ordine di grandezza della dimensione del bordo).

2

Ok guardando le immagini. Io suggerisco di usare **Active Contours**

  • Active Contours La cosa buona di contorni attivi è che quasi si incastrano perfettamente nel qualsiasi forma. Be it squares o triangle e nel tuo caso sono i candidati perfetti.
  • Se sei in grado di estrarre il centro dei cerchi, è fantastico. I contorni attivi hanno sempre bisogno di un punto di partenza da cui possono crescere o rimpicciolirsi per adattarsi. Non è necessario che i centri siano sempre allineati al centro. Un piccolo offset sarà ancora ok.
  • E nel tuo caso, se lasci che i contorni crescano dal centro verso l'esterno, devono appoggiare i bordi del cerchio.
  • Si noti che i contorni attivi che si accrescono o si restringono utilizzano balloon energy che significa che è possibile impostare la direzione dei contorni, verso l'interno o verso l'esterno.
  • Probabilmente sarà necessario utilizzare l'immagine del gradiente in scala di grigi. Ma puoi provare anche a colori. Se funziona!
  • E se non si forniscono centri, inserire molti contorni attivi, farli crescere/ridurre. I contorni che si stabiliscono vengono mantenuti, quelli non fissati vengono gettati via. Questo è un approccio di forza bruta. Intensivo della CPU. Ma richiederà un lavoro più attento per essere sicuro di lasciare contorni corretti e buttare fuori quelli cattivi.

Spero in questo modo di risolvere il problema.

+0

Per qualche motivo, cvSnakes è stato rimosso da OpenCV. Puoi comunque collegarti alla libreria legacy. – Dave

+0

Di quale versione di OpenCV stai parlando? –

+0

2.qualcosa. È menzionato in altre domande su cvSnakes. – Dave

11

Ah, sì ... il vecchio colore/invarianti di dimensione per problema cerchi (AKA la trasformata di Hough è troppo specifico e non robusta) ...

In passato mi sono basato molto di più sulle funzioni di structural and shape analysis OpenCV anziché. È possibile ottenere una buona idea dalla cartella "samples" di ciò che è possibile, in particolare fitellipse.py e squares.py.

Per la spiegazione, presento una versione ibrida di questi esempi e basata sulla fonte originale. I contorni rilevati sono in verde e le ellissi montate in rosso.

enter image description here

Non è ancora lontani:

  • Le fasi di pre-elaborazione hanno bisogno di un po 'di tweaking per rilevare i cerchi più deboli.
  • Si potrebbe testare ulteriormente il contorno per determinare se si tratta di un cerchio o no ...

Buona fortuna!

import cv 
import numpy as np 

# grab image 
orig = cv.LoadImage('circles3.jpg') 

# create tmp images 
grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1) 
processed = cv.CreateImage(cv.GetSize(orig), 8, 1) 

cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3) 

cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY) 

# do some processing on the grey scale image 
cv.Erode(grey_scale, processed, None, 10) 
cv.Dilate(processed, processed, None, 10) 
cv.Canny(processed, processed, 5, 70, 3) 
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15) 

#storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3) 
storage = cv.CreateMemStorage(0) 

contours = cv.FindContours(processed, storage, cv.CV_RETR_EXTERNAL) 
# N.B. 'processed' image is modified by this! 

#contours = cv.ApproxPoly (contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) 
# If you wanted to reduce the number of points... 

cv.DrawContours (orig, contours, cv.RGB(0,255,0), cv.RGB(255,0,0), 2, 3, cv.CV_AA, (0, 0)) 

def contour_iterator(contour): 
    while contour: 
    yield contour 
    contour = contour.h_next() 

for c in contour_iterator(contours): 
    # Number of points must be more than or equal to 6 for cv.FitEllipse2 
    if len(c) >= 6: 
    # Copy the contour into an array of (x,y)s 
    PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2) 

    for (i, (x, y)) in enumerate(c): 
     PointArray2D32f[0, i] = (x, y) 

    # Fits ellipse to current contour. 
    (center, size, angle) = cv.FitEllipse2(PointArray2D32f) 

    # Convert ellipse data from float to integer representation. 
    center = (cv.Round(center[0]), cv.Round(center[1])) 
    size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5)) 

    # Draw ellipse 
    cv.Ellipse(orig, center, size, angle, 0, 360, cv.RGB(255,0,0), 2,cv.CV_AA, 0) 

# show images 
cv.ShowImage("image - press 'q' to quit", orig) 
#cv.ShowImage("post-process", processed) 
cv.WaitKey(-1) 

EDIT:

Solo un aggiornamento per dire che ritengo un tema importante per tutte queste risposte è che ci sono una miriade di ulteriori assunzioni e dei vincoli che possono essere applicati a quello che si cerca di riconoscere come circolare. La mia risposta non ha nulla a che fare con questo - né nella pre-elaborazione di basso livello né nel fitting geometrico di alto livello. Il fatto che molti dei cerchi non siano proprio così arrotondati a causa del modo in cui sono disegnati o delle trasformazioni non affini/proiettive dell'immagine, e con le altre proprietà nel modo in cui vengono renderizzati/catturati (colore, rumore, illuminazione, spigolo del bordo) - tutti i risultati in un numero qualsiasi di possibili cerchi candidati all'interno di una sola immagine.

Esistono tecniche molto più sofisticate. Ma ti costeranno. Personalmente mi piace l'idea di @fraxel di usare la soglia additiva. Questo è veloce, affidabile e ragionevolmente robusto. È quindi possibile testare ulteriormente i contorni finali (ad esempio utilizzare i momenti Hu) o i raccordi con un semplice test del rapporto dell'asse dell'ellisse - ad es. if ((min (size)/max (size))> 0.7).

Come sempre con Computer Vision c'è la tensione tra pragmatismo, principio e parsomonia. Come mi piace dire alla gente che pensa che il CV sia facile, non lo è - in effetti è notoriamente un problema AI complete. Il meglio che puoi spesso sperare al di fuori di questo è qualcosa che funziona la maggior parte del tempo.

27

Questo è un grande problema di modellazione. Ho le seguenti raccomandazioni/idee:

  1. Dividere l'immagine in RGB quindi elaborare.
  2. pre-elaborazione.
  3. Ricerca parametri dinamica.
  4. Aggiungere vincoli.
  5. 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: enter image description here

Come l'immagine preelaborata guarda prima che venga applicato il filtro canny (le diverse circonferenze di colore sono molto visibili): enter image description here

Rileva tutti tranne due (blob) seconda immagine: enter image description here

seconda immagine Altered (macchie sono cerchio-afied e ovale grande rese più circolare, migliorando così rilevamento), tutti rilevate: enter image description here

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). enter image description here

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) 
Problemi correlati