0

I want to detect an archery target by its colors and made it that far:

Original image and analyzed colors:

I'm getting the circles using color range on a RGB image. As you can see, there are also many other blobs on the images which I want to get rid of.

I tried to compare all shapes in all three images with each other to find the best match by matchShape, but it doesn't result in those three circles I want.

Any ideas how I can find out which contours/shapes/objects on the image are actually the three circles?

The input image might have an angle/perspective, so I can't use hough to find circles.

What I need as a result is a contour of the three circles (outer and inner contour), as I need to do further processing with that data.

This is actually a follow-up of that question: find archery target in image of different perspectives


As it is a more specific question, I created a new one. I'm new here


A follow-up to Miki's answer. One of the results looks like this: Contour of blue circle and fitting ellipse The contour in the binary image and the resulting ellipse in the original image are different.

I would still like to solve that problem. I'm thinking of an algorithm that goes along the contour of the binary image and wherever the contour is broken the algorithm keeps going using the last known radius until it finds the next pixel of the contour. Is there such an algorithm? Or maybe a method to stretch the fitting ellipse at specific points until all parts of the contour are covered?

Community
  • 1
  • 1
Normalo
  • 387
  • 4
  • 12
  • you could try to extract contours and use contour moments (e.g. matchShapes function http://docs.opencv.org/3.1.0/d5/d45/tutorial_py_contours_more_functions.html) to compare each contour in the image with each other. Hopefully you'll observe good results for your target cotours and bad results for the background. – Micka Apr 18 '16 at 10:50
  • @Micka As mentioned in the question, I tried that already. Or do you mean something else? – Normalo Apr 18 '16 at 11:16

2 Answers2

0

You can look a Circle Hough Transform algorithm to find all circular objects in Red, Green and Blue channels and then match them.

You can find implementation here or use OpenCV realization.

Artem
  • 1,535
  • 10
  • 13
  • I guess I can't use circle detection because the input image might be in different angles which even doesn't allow me to look for ellipses. – Normalo Apr 18 '16 at 11:18
  • perspectively distorted circles ARE ellipses! – Micka Apr 18 '16 at 11:29
0

Finding directly the ellipses in that image can be quite tricky. You can, however, have a look here for a few info and references to code.


In this case, it's much easier to segment the 3 color: blue, red and yellow, find the outer contours, and fit an ellipse to them.

So, on this input image:

enter image description here

So, you first convert your image to HSV, and then apply some thresholds to recover the masks. Using morphological close operations will get rid of some holes, and link nearby blobs.

blue mask:

enter image description here

red mask:

enter image description here

yellow mask:

enter image description here

Then, you can retrieve the external contours from this masks, and keep only the largest (in case you find some smaller blob not belonging to the target).

Now you just need to fit an ellipse to these contours:

enter image description here

Please note that I tried also on the image in your other question. Blue target is deformed and so it's not an ellipse, so fitting an ellipse is not a good choice here:

enter image description here

In this case may be better to use the convex hull of the contours, which will be more robust than the contour itself, if the mask is not perfect (code is given below):

enter image description here

Code:

#include <opencv2/opencv.hpp>
#include <vector>
#include <string>

using namespace std;
using namespace cv;

int main()
{
    // Load image
    Mat3b img = imread("path_to_image");

    // Convert to hsv
    Mat3b hsv;
    cvtColor(img, hsv, COLOR_BGR2HSV);

    // Find masks for different colors
    Mat1b blue_mask;
    inRange(hsv, Scalar(90, 150, 150), Scalar(110, 255, 255), blue_mask);

    Mat1b red_mask;
    inRange(hsv, Scalar(160, 150, 100), Scalar(180, 255, 255), red_mask);

    Mat1b yellow_mask;
    inRange(hsv, Scalar(20, 150, 100), Scalar(30, 255, 255), yellow_mask);

    // Apply morphological close
    Mat1b kernel = getStructuringElement(MORPH_ELLIPSE, Size(11,11));
    morphologyEx(blue_mask, blue_mask, MORPH_CLOSE, kernel);
    morphologyEx(red_mask, red_mask, MORPH_CLOSE, kernel);
    morphologyEx(yellow_mask, yellow_mask, MORPH_CLOSE, kernel);

    // Find largest blob and draw it
    vector<Point> blue_contour, red_contour, yellow_contour;
    {
        vector<vector<Point>> contours;
        findContours(blue_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        blue_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
            return contourArea(lhs) < contourArea(rhs); });
    }
    {
        vector<vector<Point>> contours;
        findContours(red_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        red_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
            return contourArea(lhs) < contourArea(rhs); });
    }
    {
        vector<vector<Point>> contours;
        findContours(yellow_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        yellow_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
            return contourArea(lhs) < contourArea(rhs); });
    }

    // Fit ellipse
    RotatedRect blue_ellipse = fitEllipse(blue_contour);
    RotatedRect red_ellipse = fitEllipse(red_contour);
    RotatedRect yellow_ellipse = fitEllipse(yellow_contour);

    // Draw ellipses
    ellipse(img, blue_ellipse, Scalar(255, 0, 0), 3);
    ellipse(img, red_ellipse, Scalar(0, 0, 255), 3);
    ellipse(img, yellow_ellipse, Scalar(0, 255, 255), 3);

    imshow("Result", img);
    waitKey();

    return 0;
}

Code for convex hull:

// Get convex hulls
vector<Point> blue_hull, red_hull, yellow_hull;
convexHull(blue_contour, blue_hull);
convexHull(red_contour, red_hull);
convexHull(yellow_contour, yellow_hull);

// Draw convex hulls
drawContours(img, vector < vector<Point> > {blue_hull}, 0, Scalar(255,0,0), 3);
drawContours(img, vector < vector<Point> > {red_hull}, 0, Scalar(0, 0, 255), 3);
drawContours(img, vector < vector<Point> > {yellow_hull}, 0, Scalar(0, 255, 255), 3);
Community
  • 1
  • 1
Miki
  • 40,887
  • 13
  • 123
  • 202
  • Amazing solution! I got the same results doing this with Java. I would still like to solve the problem with the last image, where fitting an ellipse doesn't cover the whole shape of the circle. I'm thinking of an algorithm that goes along the contour of the binary image and wherever the contour is broken the algorithm keeps going using the last known radius until it finds the next pixel of the contour. Is there such an algorithm? Or maybe a method to stretch the fitting ellipse at specific points until all parts of the contour are covered? – Normalo Apr 19 '16 at 01:34