Find overlapping/complex circles with OpenCV

前端 未结 1 829
孤独总比滥情好
孤独总比滥情好 2021-02-09 20:04

I want to compute the red circles radius (fig 2). I have troubles finding these circles using HoughCircles from OpenCV. As you can see in fig. 2 I can only find the little circl

相关标签:
1条回答
  • 2021-02-09 20:24

    You already know the smaller circles in the image(which you have drawn in black).

    • Prepare a mask image using these circles so the areas having smaller circles will have non-zero pixels. We'll call it mask:

    enter image description here

    • In the original image, fill these circle areas in a dark color(say black). This will result in an image like your fig 2. We'll call it filled
    • Threshold the filled image to obtain the dark areas. We'll call it binary. You can use Otsu thresholding for this. Result will look something like this:

    enter image description here

    • Take the distance transform of this binary image. Use an accurate distance estimation method for this. We'll call this dist. It'll look something like this. The colored one is just a heat map for more clarity:

    enter image description here enter image description here

    • Use the mask to obtain the peak regions from dist. The max value of each such region should give you the radius of the larger circle. You can also do some processing on these regions to arrive at a more reasonable value for radius rather than just picking up the max.
    • For selecting the regions, you can either find the contours of the mask and then extract that region from dist image, or, since you already know the smaller circles from applying hough-circle transform, prepare a mask from each of those circles and extract that region from dist image. I'm not sure if you can calculate max or other stats by giving a mask. Max will definitely work because the rest of the pixels are 0. You might be able calculate the stats of the region if you extract those pixels to another array.

    Figures below show such mask and the extracted region from dist. For this I get a max around 29 which is consistent with the radius of that circle. Note that the images are not to scale.

    mask for a circle, extracted region from dist

    enter image description here enter image description here

    Here's the code (I'm not using hough-circles transform):

        Mat im = imread(INPUT_FOLDER_PATH + string("ex1.jpg"));
    
        Mat gray;
        cvtColor(im, gray, CV_BGR2GRAY);
    
        Mat bw;
        threshold(gray, bw, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);
        // filtering smaller circles: not using hough-circles transform here. 
        // you can replace this part with you hough-circles code.
        vector<int> circles;
        vector<vector<Point>> contours;
        vector<Vec4i> hierarchy;
        findContours(bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
        for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
        {
            Rect rect = boundingRect(contours[idx]);
            if (abs(1.0 - ((double)rect.width/rect.height) < .1))
            {
                Mat mask = Mat::zeros(im.rows, im.cols, CV_8U);
                drawContours(mask, contours, idx, Scalar(255, 255, 255), -1);
                double area = sum(mask).val[0]/255;
                double rad = (rect.width + rect.height)/4.0;
                double circArea = CV_PI*rad*rad;
                double dif = abs(1.0 - area/circArea);
                if (dif < .5 && rad < 50 && rad > 30)   // restrict the radius
                {
                    circles.push_back(idx); // store smaller circle contours
                    drawContours(gray, contours, idx, Scalar(0, 0, 0), -1); // fill circles
                }
            }
        }
    
        threshold(gray, bw, 0, 255, CV_THRESH_BINARY_INV|CV_THRESH_OTSU);
    
        Mat dist, distColor, color;
        distanceTransform(bw, dist, CV_DIST_L2, 5);
        double max;
        Point maxLoc;
        minMaxLoc(dist, NULL, &max);
        dist.convertTo(distColor, CV_8U, 255.0/max);
        applyColorMap(distColor, color, COLORMAP_JET);
        imshow("", color);
        waitKey();
    
        // extract dist region corresponding to each smaller circle and find max
        for(int idx = 0; idx < (int)circles.size(); idx++)
        {
            Mat masked;
            Mat mask = Mat::zeros(im.rows, im.cols, CV_8U);
            drawContours(mask, contours, circles[idx], Scalar(255, 255, 255), -1);
            dist.copyTo(masked, mask);
            minMaxLoc(masked, NULL, &max, NULL, &maxLoc);
            circle(im, maxLoc, 4, Scalar(0, 255, 0), -1);
            circle(im, maxLoc, (int)max, Scalar(0, 0, 255), 2);
            cout << "rad: " << max << endl;
        }
        imshow("", im);
        waitKey();
    

    Results(scaled):

    enter image description here enter image description here

    Hope this helps.

    0 讨论(0)
提交回复
热议问题