2012-04-25 9 views
7

Estoy haciendo una detección de monedas usando JavaCV (envoltorio OpenCV) pero tengo un pequeño problema cuando las monedas están conectadas. Si trato de erosionarlos para separar estas monedas, pierden su forma de círculo y si trato de contar los píxeles dentro de cada moneda, puede haber problemas para que algunas monedas puedan ser contadas como una más grande. Lo que quiero hacer primero es remodelarlos y hacerlos como un círculo (igual al radio de esa moneda) y luego contar los píxeles dentro de ellos.Remodelación de moneda ruidosa en forma de círculo

Aquí está mi imagen de umbral: enter image description here

Y aquí se erosiona la imagen: enter image description here

¿Alguna sugerencia? ¿O hay alguna forma mejor de romper los puentes entre las monedas?

Respuesta

11

Es similar a un problema que re Recientemente tuvo que separar las colonias bacterianas que crecían en placas de agar. Realicé una transformación de distancia en la imagen de umbral (en su caso, tendrá que invertirla). Luego se encontraron los picos del mapa de distancia (calculando la diferencia entre un mapa de distancia dilatado y el mapa de distancia y encontrando los valores cero). Luego, asumí que cada pico era el centro de un círculo (moneda) y el valor del pico en el mapa de distancia era el radio del círculo.

Este es el resultado de la imagen después de esta tubería: distance transform works

Soy nuevo en OpenCV, y C++ por lo que mi código es probablemente muy desordenado, pero lo hice:

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

Tienes el mejor resultado por cierto ... ¡Intentaré esto en JavaCV esta noche! –

+0

+1 enfoque interesante. – karlphillip

+1

** Sin embargo **, [la imagen que obtuve de su código] (http://imageshack.us/photo/my-images/526/58131003.jpg/) es un poco diferente de la que nos mostró. – karlphillip

2

OpenCV tiene una función llamada HoughCircles() que se puede aplicar a su caso, sin separar los diferentes círculos. ¿Puedes llamarlo desde JavaCV? Si es así, hará lo que quiera (detectar y contar círculos), evitando su problema de separación.

El punto principal es detectar los círculos con precisión sin separarlos primero. Otros algoritmos (como comparación de plantillas se pueden utilizar en lugar de generalizar la transformada de Hough, pero hay que tener en cuenta los diferentes tamaños de las monedas.

+0

En primer lugar, apliqué difuminado gaussiano y luego cvHoughCircles, pero en algunas imágenes no puede encontrar radios y centros correctos para el círculo.Y también en algunas imágenes detecta círculos incorrectos, especialmente en el caso de que las monedas estén agrupadas y hay muchas de ellas ... –

+0

Probablemente sea mejor usar un filtro mediano en lugar del gaussiano, ya que el ajuste limitará el efecto de desenfoque (por lo tanto, mejorará la precisión de su algoritmo). Sin embargo, el problema principal es que es muy difícil separar estas formas intrincadas por morfología (hay que erosionar mucho para resumir). Si los círculos de Hough no funcionaron, puedes probar la coincidencia de plantillas con círculos de diferentes escalas para detectar círculos directamente. – sansuiso

5

No es necesario para erosionar, sólo un buen conjunto de parametros para cvHoughCircles() :

enter image description here

El código utilizado para generar esta imagen vino de mi otro post: Detecting Circles, con estos parámetros:

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

Ok, eso está bien, pero en el método Hough Circles, en algunos casos se pueden dibujar 5 centavos y 10 centavos con el mismo radio que no es lo que quiero, solo quiero encontrar radios para cada moneda y encontrar cuánto dinero hay en esa imagen según radios .. –

+0

El problema es que cuando ejecuta la erosión, pierde esta información, como ya ha observado. Si no está dispuesto a perder tiempo investigando cómo mejorar su detección utilizando Hough Circles, tendré que sugerirle que tome un enfoque completamente diferente al utilizar [SURF] (http://achuwilson.wordpress.com/2011/08/ 05/object-detection-using-surf-in-opencv-part-1 /) or [haar] (http://achuwilson.wordpress.com/2011/02/13/object-detection-using-opencv-using- haartraining /) [entrenamiento] (http://note.sonots.com/SciSoftware/haartraining.html) para detectar monedas. – karlphillip

+0

Si solo tiene que ocuparse de las monedas cerca una de la otra, ese es un grado de dificultad que puede manejar Hough Circles. Pero si tiene que lidiar con monedas superpuestas entre sí, entonces debe considerar uno de los otros enfoques que acabo de sugerir. – karlphillip

1

El El enfoque habitual para el reconocimiento de objetos basado en la erosión es etiquetar regiones continuas en la imagen erosionada y luego volver a crecer hasta que coincidan con las regiones en la imagen original. Aunque los círculos de Hough son una mejor idea en tu caso.

0

Después de detectar las monedas unidas, recomiendo aplicar operaciones morfológicas para clasificar áreas como "definitivamente monedas" y "definitivamente no monedas", aplicar una transformación de distancia, luego ejecutar la cuenca para determinar los límites. Este escenario es en realidad el ejemplo de demostración del algoritmo de cuenca hidrográfica en OpenCV, tal vez fue creado en respuesta a esta pregunta.

Cuestiones relacionadas