18

I am trying to make a program which opens an image, scans it for circles/round shapes and returns the coordinates so that I can use the cv.Circle function to draw circles over the circle detected.

My question is: How do I get the coordinates/radii of the circles detected in an image using cv.HoughCircles() ?

Using this page, I found out how to detect the circles (which took me a lot of time to find out since I don't understand terms like threshold and the OpenCV documentation for Python is really poor, almost none). Unfortunately, on that page it didn't show how to extract the information of each circle detected from the CvMat created. How do I extract that information/is there some other way(eg. with MemoryStorage() ) ?

This is my code so far:

import cv, opencv

def main():


    im = cv.LoadImageM("Proba.jpg")

    gray = cv.CreateImage(cv.GetSize(im), 8, 1)
    edges = cv.CreateImage(cv.GetSize(im), 8, 1)

    cv.CvtColor(im, gray, cv.CV_BGR2GRAY)
    cv.Canny(gray, edges, 50, 200, 3)
    cv.Smooth(gray, gray, cv.CV_GAUSSIAN, 9, 9)

    storage = cv.CreateMat(im.rows, 1, cv.CV_32FC3)

    cv.HoughCircles(edges, storage, cv.CV_HOUGH_GRADIENT, 2, gray.height/4, 200, 100)
    # Now, supposing it found circles, how do I extract the information?
    print storage.r



if __name__ == '__main__':
    main()

Also, what value do the last two parameters of HoughCircles need to have in order for me to detect really small circles (like 3mm on the screen) ?

Thank you all for your time and effort trying to help me!

The image I'm working with is this:enter image description here

Community
  • 1
  • 1
Calin
  • 413
  • 2
  • 7
  • 16
  • Could you post a sample image so that I can test my code? – Blender Mar 20 '11 at 14:14
  • 1
    related: [Finding number of colored shapes from picture using Python](http://stackoverflow.com/questions/5298884/finding-number-of-colored-shapes-from-picture-using-python) – jfs Mar 20 '11 at 15:23

3 Answers3

4

The last two parameters are what seem to be passed to cv.Canny(), which implies that cv.Canny() is called from within cv.HoughCircles(). I'm not too sure about that.

As for the sizes, it seems like the next two parameters (after 200, 100) default to 0, which might mean that all sizes are detected.

From the C++ example's source, I can also guess that you don't need to do a Canny edge detection:

#include <cv.h>
#include <highgui.h>
#include <math.h>

using namespace cv;

int main(int argc, char** argv)
{
    Mat img, gray;
    if( argc != 2 && !(img=imread(argv[1], 1)).data)
        return -1;
    cvtColor(img, gray, CV_BGR2GRAY);
    // smooth it, otherwise a lot of false circles may be detected
    GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
    vector<Vec3f> circles;
    HoughCircles(gray, circles, CV_HOUGH_GRADIENT,
                 2, gray->rows/4, 200, 100 );
    for( size_t i = 0; i < circles.size(); i++ )
    {
         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
         int radius = cvRound(circles[i][2]);
         // draw the circle center
         circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
         // draw the circle outline
         circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }
    namedWindow( "circles", 1 );
    imshow( "circles", img );
    return 0;
}

You're trying to convert this C++ code into Python, I assume?

for( size_t i = 0; i < circles.size(); i++ )
{
     Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
     int radius = cvRound(circles[i][2]);
     // draw the circle center
     circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
     // draw the circle outline
     circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
}

As far as I can tell, CvMat objects are iterable, just like a list:

for circle in storage:
  radius = circle[2]
  center = (circle[0], circle[1])

  cv.Circle(im, center, radius, (0, 0, 255), 3, 8, 0)

I don't have any test images, so don't take my word that this works. Your complete code would might be:

import cv

def main():
  im = cv.LoadImage('Proba.jpg')
  gray = cv.CreateImage(cv.GetSize(im), 8, 1)
  edges = cv.CreateImage(cv.GetSize(im), 8, 1)

  cv.CvtColor(im, gray, cv.CV_BGR2GRAY)
  #cv.Canny(gray, edges, 20, 55, 3)

  storage = cv.CreateMat(im.width, 1, cv.CV_32FC3)
  cv.HoughCircles(edges, storage, cv.CV_HOUGH_GRADIENT, 5, 25, 200, 10)

  for i in xrange(storage.width - 1):
    radius = storage[i, 2]
    center = (storage[i, 0], storage[i, 1])

    print (radius, center)

    cv.Circle(im, center, radius, (0, 0, 255), 3, 8, 0)

  cv.NamedWindow('Circles')
  cv.ShowImage('Circles', im)
  cv.WaitKey(0)

if __name__ == '__main__':
  main()
Blender
  • 289,723
  • 53
  • 439
  • 496
  • Unfortunately, CvMat doesn't seem to be iterable, atleast not in Python: TypeError: 'cv.cvmat' object is not iterable – Calin Mar 20 '11 at 14:34
  • Could you post your test image? I can debug code a lot faster if I can actually run it ;) You don't have image-sharing privileges, so if you would like, upload it to an image-sharing site and post a link. I'll edit your question so that it shows up as an image. – Blender Mar 20 '11 at 14:40
  • `storage` has no `columns` attribute (do you mean `rows` here?). `circle` is undefined. – jfs Mar 20 '11 at 15:26
  • I think the right attribute is cols, but it doesn't work with it either. – Calin Mar 20 '11 at 15:29
  • I'm playing with the code right now. For some reason, `cv.HoughCircles()` doesn't detect anything. It just runs with no errors, but no modifications to the original image... – Blender Mar 20 '11 at 15:49
  • Hmm, this is interesting, maybe it's because of the parameters passed. You have NO idea how thankful I am to you for trying to help me. I never expected anybody to go so far with providing an answer. I am really grateful! :) – Calin Mar 20 '11 at 15:58
  • I tried playing with the parameters. Not too many people use OpenCV and Python (I do!), so I couldn't find many examples using `cv.HoughCircles()`... – Blender Mar 20 '11 at 16:08
2

Have a look at my answer to this question for some working source code (it's C but I used a C++ compiler cause it's more lenient).

First, I cropped your image (to get something convenient to work with) and applied a threshold to your image to separate the foreground from the background:

enter image description here

Then I directly applied the source code to the thresholded image. Here is the text output:

center x: 330 y: 507 A: 13 B: 4
center x: 78 y: 507 A: 22 B: 4
center x: 270 y: 503 A: 8 B: 8
center x: 222 y: 493 A: 21 B: 17
center x: 140 y: 484 A: 17 B: 18
center x: 394 y: 478 A: 17 B: 15
center x: 311 y: 468 A: 8 B: 8
center x: 107 y: 472 A: 12 B: 12
center x: 7 y: 472 A: 6 B: 19
center x: 337 y: 442 A: 10 B: 9
center x: 98 y: 432 A: 10 B: 10
center x: 357 y: 421 A: 7 B: 7
center x: 488 y: 429 A: 22 B: 23
center x: 411 y: 400 A: 13 B: 12
center x: 42 y: 400 A: 11 B: 12
center x: 365 y: 391 A: 14 B: 13
center x: 141 y: 396 A: 19 B: 19
center x: 9 y: 379 A: 8 B: 18
center x: 192 y: 365 A: 10 B: 9
center x: 347 y: 340 A: 20 B: 20
center x: 8 y: 305 A: 7 B: 13
center x: 95 y: 308 A: 23 B: 24
center x: 318 y: 297 A: 15 B: 15
center x: 159 y: 285 A: 10 B: 10
center x: 412 y: 291 A: 26 B: 27
center x: 504 y: 278 A: 6 B: 16
center x: 233 y: 277 A: 20 B: 20
center x: 459 y: 256 A: 15 B: 15
center x: 7 y: 239 A: 6 B: 9
center x: 377 y: 239 A: 14 B: 14
center x: 197 y: 228 A: 12 B: 12
center x: 302 y: 237 A: 12 B: 22
center x: 98 y: 224 A: 24 B: 23
center x: 265 y: 203 A: 18 B: 18
center x: 359 y: 202 A: 22 B: 22
center x: 149 y: 201 A: 20 B: 21
center x: 219 y: 169 A: 7 B: 9
center x: 458 y: 172 A: 20 B: 20
center x: 497 y: 157 A: 13 B: 21
center x: 151 y: 125 A: 18 B: 17
center x: 39 y: 109 A: 9 B: 10
center x: 81 y: 116 A: 20 B: 19
center x: 249 y: 104 A: 14 B: 13
center x: 429 y: 76 A: 23 B: 24
center x: 493 y: 33 A: 11 B: 10
center x: 334 y: 26 A: 12 B: 14

And here is the output image:

enter image description here

The main problem is that circles that have merged together have not been detected at all. The code was originally written for detecting filled ellipses only, so you can probably deal with this issue by tweaking the code.

Community
  • 1
  • 1
mpenkov
  • 21,621
  • 10
  • 84
  • 126
0

A similar solution in python. Originally I tried to run a contour detection described here, but it doesn't worked well. So first some thresholding was necessary. The code for threshold is here:

    fimg = misc.imread("boubles.jpg")
    gimg = color.colorconv.rgb2grey(fimg)
    vimg = []
    for l in gimg:
        l2 = sign(l - 0.50) / 2 + 0.5
        vimg.append(l2)

    img = array(vimg)
    imshow(img)

With this I get an image like this:

thresholded image

And after edge detection described in the link above I got this:

find contours

If you check the code, you will find that's really easy to count the objects. The only problem is, that some of the bubbles are counted twice. And I guess the thresholding function can be improved as well. But I suggest to use skimage it's easy to use and has good samples on their web page.

mrbo
  • 3,067
  • 1
  • 18
  • 7