StupidGuy
StupidGuy

Reputation: 97

Detect Red and Green Circles

I want to detect red and green circles separately in the following image (and a few other similar images)

image

I'm using opencv and python.

I've tried using houghcircles but that wasn't of any help even after changing the params.

Any suggestion how to do this would really help a lot. I would appreciate if someone sends a code

Upvotes: 2

Views: 2232

Answers (1)

Nejc
Nejc

Reputation: 937

You mentioned in the comments that the circles will always have the same size. Let's take advantage of this fact. My code snippets are in C++ language but this should not be a problem because they are only here to show which OpenCV functions to use (and how) ...

TL; DR Do this:

  1. Create typical circle image - the template image.
  2. Use template matching to get all circle positions.
  3. Check the color of every circle.

Now, let's begin!

Step 1 - The template image

You need an image that shows the circle that is clearly separated from the background. You have two options (both are equally good):

  • make such an image yourself (computing it if you know the radius), or
  • simply take one image from the set you are working on and then crop one well-visible circle and save it as a separate image (that's what I did because it was a quicker option)

The circle can be of any color - it is only important that it is distinct from the background.

enter image description here

Step 2 - Template matching

Load the image and template image and convert them to HSV color space. Then split channels so that you will be able to only work with saturation channel:

using namespace std;
using namespace cv;

Mat im_rgb = imread("circles.jpg");
Mat tm_rgb = imread("template.jpg");

Mat im_hsv, tm_hsv;
cvtColor(im_rgb, im_hsv, CV_RGB2HSV);
cvtColor(tm_rgb, tm_hsv, CV_RGB2HSV);
vector<Mat> im_channels, tm_channels;
split(im_hsv, im_channels);
split(tm_hsv, tm_channels);

That's how circles and the template look now:

enter image description here enter image description here

Next, you have to obtain an image that will contain information about circle borders. Regardless of what you do to achieve that, you have to apply exactly the same operations on image and template saturation channels. I used sobel operator to get the job done. The code example only shows the operations I did on image saturation channel; the template saturation channel went through exactly the same procedure:

Mat im_f;
im_channels[1].convertTo(im_f, CV_32FC1);
GaussianBlur(im_f, im_f, Size(3, 3), 1, 1);
Mat sx, sy;
Sobel(im_f, sx, -1, 1, 0);
Sobel(im_f, sy, -1, 0, 1);

Mat image_input = abs(sx) + abs(sy);

That's how the circles on the obtained image and the template look like: enter image description here enter image description here

Now, perform template matching. I advise that you choose the type of template matching that computes normalized correlation coefficients:

Mat match_result;
matchTemplate(image_input, template_input, match_result, CV_TM_CCOEFF_NORMED);

This is the template matching result:

enter image description here

This image tells you how well the template correlates with the underlying image if you place the template at different positions on image. For example, the result value at pixel (0,0) corresponds to template placed at (0,0) on the input image.

When the template is placed in such a position that it matches well with the underlying image, the correlation coefficient is high. Use threshold method to discard everything except the peaks of signal (the values of template matching will lie inside [-1, 1] interval and you are only interested in values that are close to 1):

Mat thresholded;
threshold(match_result, thresholded, 0.8, 1.0, CV_THRESH_BINARY);

enter image description here

Next, determine the positions of template result maxima inside each isolated area. I recommend that you use thresholded image as a mask for this purpose. Only one maximum needs to be selected within each area.

These positions tell you where you have to place the template so that it matches best with the circles. I drew rectangles that start at these points and have the same width/height as template image:

enter image description here

Step 3: The color of the circle

Now you know where templates should be positioned so that they cover the circles nicely. But you still have to find out where the circle center is located on the template image. You can do this by computing center of mass of the template's saturation channel:

enter image description here

On the image, the circle centers are located at these points:

Point circ_center_on_image = template_position + circ_center_on_template;

Now you only have to check if the red color channel intensity at these points is larger that the green channel intensity. If yes, the circle is red, otherwise it is green:

enter image description here

Upvotes: 7

Related Questions