13

I've been playing around with OpenCV and with alot of trial and error have managed to learn how to detect circles (coins) in a photo. Everything is working great, except when I place coins directly next to each other (as seen below, ignore the fact that the 2nd image is upside down).

Original Photo Contours Found

It seems because the coins are so close together cvFindContours think they are the same object. My question is how can I separate these contours into their separate objects, or get a list of contours that are already separated.

The parameters I used for cvFindContours are:

cvFindContours( img, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0) );

Any help or advice would be greatly appreciated.

karlphillip
  • 87,606
  • 33
  • 227
  • 395
Grinneh
  • 823
  • 1
  • 8
  • 13
  • 3
    From your "after" image, you could use a modified hough transform (http://opencv.willowgarage.com/documentation/STRAWMAN/cpp/feature_detection.html#cv-houghcircles) to detect circles in your image, it should give you reasonable results – etarion May 18 '11 at 11:55

2 Answers2

13

This is not great, but it shows how to get there:

IplImage* src = cvLoadImage(argv[1], CV_LOAD_IMAGE_UNCHANGED);
IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); 
cvCvtColor(src, gray, CV_BGR2GRAY);
cvSmooth(gray, gray, CV_GAUSSIAN, 7, 7); 

IplImage* cc_img = cvCreateImage(cvGetSize(gray), gray->depth, 3); 
cvSetZero(cc_img);
CvScalar(ext_color);

cvCanny(gray, gray, 10, 30, 3); 

CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, src->height/6, 100, 50);
cvCvtColor(gray, src, CV_GRAY2BGR);
for (size_t i = 0; i < circles->total; i++)
{   
     // round the floats to an int
     float* p = (float*)cvGetSeqElem(circles, i); 
     cv::Point center(cvRound(p[0]), cvRound(p[1]));
     int radius = cvRound(p[2]);

     // draw the circle center
     //cvCircle(cc_img, center, 3, CV_RGB(0,255,0), -1, 8, 0 );

     // draw the circle outline
     cvCircle(cc_img, center, radius+1, CV_RGB(0,0,255), 2, 8, 0 );

     //printf("x: %d y: %d r: %d\n", center.x, center.y, radius);
}   

CvMemStorage *mem;
mem = cvCreateMemStorage(0);
CvSeq *contours = 0;
cvCvtColor(cc_img, gray, CV_BGR2GRAY);
// Use either this:
int n = cvFindContours(gray, mem, &contours, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
// Or this:
//int n = cvFindContours(gray, mem, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));

for (; contours != 0; contours = contours->h_next)
{
    ext_color = CV_RGB( rand()&255, rand()&255, rand()&255 ); //randomly coloring different contours
    cvDrawContours(cc_img, contours, ext_color, CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0));
}

cvSaveImage("out.png", cc_img);

enter image description here

karlphillip
  • 87,606
  • 33
  • 227
  • 395
  • Thanks for your reply, what I failed to mention in the first post is that preserving the size of each coin was important, as the project was meant to determine what value each coin was based on its size. I'll see if I can tweak it somehow for accuracy. Thanks again – Grinneh Aug 04 '11 at 05:12
3

You could try thresholding (cvThreshold) the image and then erode (cvErode) the resulting binary image to separate the coins. Then find the contours of the eroded image.

Karl Voigtland
  • 7,227
  • 30
  • 29
  • 1
    Thanks for the suggestion, but I found to erode the image to the point where I could separate the contours resulted in the coins not looking like circles anymore. Losing a little bit of circle accuracy is ok, but overall I want to preserve it as much as possible – Grinneh May 18 '11 at 21:07
  • Yes, this way is also suggested in OpenCV book for separating cells on a microscope image . – Valentin Heinitz May 29 '12 at 12:18