2012-04-25 8 views
7

Sto eseguendo un rilevamento monete tramite JavaCV (wrapper OpenCV) ma ho un piccolo problema quando le monete sono collegate. Se provo a eroderli per separare queste monete, perdono la loro forma circolare e se provo a contare i pixel all'interno di ogni moneta ci possono essere problemi in modo che alcune monete possano essere incontrate come una più grande. Quello che voglio fare è innanzitutto rimodellarli e renderli come un cerchio (uguale al raggio di quella moneta) e poi contare i pixel al loro interno.Rimodellamento di monete rumorose in un modulo circolare

ecco la mia immagine thresholded: enter image description here

E qui è eroso immagine: enter image description here

Qualche suggerimento? O c'è un modo migliore per rompere i ponti tra le monete?

risposta

11

sembra simile ad un problema che ho ri cently ha dovuto separare colonie batteriche crescendo su piastre di agar. Ho eseguito una trasformazione a distanza sull'immagine con soglia (nel tuo caso dovrai invertirla). Poi trovato i picchi della mappa della distanza (calcolando la differenza tra una mappa della distanza dilatata e la mappa della distanza e trovando i valori zero). Quindi, ho assunto che ogni picco fosse il centro di un cerchio (moneta) e il valore del picco nella mappa della distanza fosse il raggio del cerchio.

Ecco il risultato della vostra immagine dopo questo gasdotto: distance transform works

Sono nuovo di OpenCV, e C++ in modo mio codice è probabilmente molto disordinato, ma l'ho fatto:

int main(int argc, char** argv){ 

    cv::Mat objects, distance,peaks,results; 
    std::vector<std::vector<cv::Point> > contours; 

    objects=cv::imread("CUfWj.jpg"); 
    objects.copyTo(results); 
    cv::cvtColor(objects, objects, CV_BGR2GRAY); 
    //THIS IS THE LINE TO BLUR THE IMAGE CF COMMENTS OF THIS POST 
    cv::blur(objects,objects,cv::Size(3,3)); 
    cv::threshold(objects,objects,125,255,cv::THRESH_BINARY_INV); 


    /*Applies a distance transform to "objects". 
    * The result is saved in "distance" */ 
    cv::distanceTransform(objects,distance,CV_DIST_L2,CV_DIST_MASK_5); 

    /* In order to find the local maxima, "distance" 
    * is subtracted from the result of the dilatation of 
    * "distance". All the peaks keep the save value */ 
    cv::dilate(distance,peaks,cv::Mat(),cv::Point(-1,-1),3); 
    cv::dilate(objects,objects,cv::Mat(),cv::Point(-1,-1),3); 

    /* Now all the peaks should be exactely 0*/ 
    peaks=peaks-distance; 

    /* And the non-peaks 255*/ 
    cv::threshold(peaks,peaks,0,255,cv::THRESH_BINARY); 
    peaks.convertTo(peaks,CV_8U); 

    /* Only the zero values of "peaks" that are non-zero 
    * in "objects" are the real peaks*/ 
    cv::bitwise_xor(peaks,objects,peaks); 

    /* The peaks that are distant from less than 
    * 2 pixels are merged by dilatation */ 
    cv::dilate(peaks,peaks,cv::Mat(),cv::Point(-1,-1),1); 

    /* In order to map the peaks, findContours() is used. 
    * The results are stored in "contours" */ 
    cv::findContours(peaks, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); 
    /* The next steps are applied only if, at least, 
    * one contour exists */ 
    cv::imwrite("CUfWj2.jpg",peaks); 
    if(contours.size()>0){ 

     /* Defines vectors to store the moments of the peaks, the center 
     * and the theoritical circles of the object of interest*/ 
     std::vector <cv::Moments> moms(contours.size()); 
     std::vector <cv::Point> centers(contours.size()); 
     std::vector<cv::Vec3f> circles(contours.size()); 
     float rad,x,y; 
     /* Caculates the moments of each peak and then the center of the peak 
     * which are approximatively the center of each objects of interest*/ 

     for(unsigned int i=0;i<contours.size();i++) { 
      moms[i]= cv::moments(contours[i]); 
      centers[i]= cv::Point(moms[i].m10/moms[i].m00,moms[i].m01/moms[i].m00); 
      x= (float) (centers[i].x); 
      y= (float) (centers[i].y); 
      if(x>0 && y>0){ 
       rad= (float) (distance.at<float>((int)y,(int)x)+1); 
       circles[i][0]= x; 
       circles[i][3]= y; 
       circles[i][2]= rad; 
       cv::circle(results,centers[i],rad+1,cv::Scalar(255, 0,0), 2, 4, 0); 
      } 
     } 
     cv::imwrite("CUfWj2.jpg",results); 
    } 

    return 1; 
} 
+1

Hai il miglior risultato btw .. Proverò questo su JavaCV stasera! –

+0

+1 Approccio interessante. – karlphillip

+1

** Tuttavia **, [l'immagine che ho ricevuto dal tuo codice] (http://imageshack.us/photo/my-images/526/58131003.jpg/) è leggermente diversa da quella che ci hai mostrato. – karlphillip

2

OpenCV ha una funzione chiamata HoughCircles() che può essere applicata al tuo caso, senza separare i diversi cerchi. Puoi chiamarlo da JavaCV? Se è così, farà quello che vuoi (rilevando e contando i cerchi), bypassando il tuo problema di separazione.

Il punto principale è rilevare i cerchi accuratamente senza separarli prima. Altri algoritmi (come la corrispondenza dei modelli possono essere utilizzati al posto della trasformazione Hough generalizzata, ma è necessario tenere conto delle diverse dimensioni delle monete:

+0

innanzitutto ho applicato la sfocatura gaussiana e quindi cvHoughCircles ma in alcune immagini non è possibile trovare i raggi e i centri per il cerchio corretti.E anche in alcune immagini trova cerchi errati - specialmente nel caso in cui le monete sono raggruppate e ce ne sono molte. –

+0

Probabilmente è meglio usare un filtro mediano invece del gaussiano, dato che la misura limiterà l'effetto sfocato (quindi migliorerà l'accuratezza del tuo algoritmo). Il problema principale tuttavia è che è molto difficile separare queste forme complesse dalla morfologia (bisogna ridurne a molto per riassumere). Se i cerchi di Hough non funzionano, puoi provare la corrispondenza dei modelli con cerchi di scale diverse, per rilevare direttamente i cerchi. – sansuiso

5

Non è necessario eseguire l'erosione, solo un buon set di parametri per cvHoughCircles() :

enter image description here

Il codice utilizzato per generare questa immagine è venuto dal mio post: Detecting Circles, con questi parametri:

CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, gray->height/12, 80, 26); 
+0

Ok, è bello, ma nel metodo Hough Circles in alcuni casi si possono disegnare 5 centesimi e 10 centesimi con lo stesso raggio che non è quello che voglio, voglio solo trovare i raggi per ogni moneta e trovare quanti soldi ci sono su quell'immagine secondo raggi .. –

+0

Il problema è che quando si esegue l'erosione, si perdono queste informazioni, come si è già osservato. Se non sei disposto a dedicare tempo a studiare come migliorare il tuo rilevamento usando Hough Circles, dovrò suggerirti di adottare un approccio completamente diverso usando [SURF] (http://achuwilson.wordpress.com/2011/08/ 05/object-detection-using-surf-in-opencv-part-1 /) o [haar] (http://achuwilson.wordpress.com/2011/02/13/object-detection-using-opencv-using- haartraining /) [training] (http://note.sonots.com/SciSoftware/haartraining.html) per rilevare le monete. – karlphillip

+0

Se hai solo a che fare con monete vicine l'una all'altra, questo è un grado di difficoltà che Hough Circles è in grado di gestire. Ma se hai a che fare con monete che si sovrappongono l'una all'altra, allora devi considerare uno degli altri approcci che ho appena suggerito. – karlphillip

1

Il L'approccio usuale per il riconoscimento degli oggetti basato sull'erosione consiste nell'etichettare le regioni continue nell'immagine erosa e quindi ricrearle finché non corrispondono alle regioni dell'immagine originale. I cerchi di Hough sono un'idea migliore nel tuo caso, però.

0

Dopo aver rilevato le monete unite, raccomando di applicare le operazioni morfologiche per classificare le aree come "sicuramente moneta" e "sicuramente non moneta", applicare una trasformazione a distanza, quindi eseguire lo spartiacque per determinare i confini. Questo scenario è in realtà l'esempio dimostrativo dell'algoritmo spartiacque in OpenCV - forse è stato creato in risposta a questa domanda.