首页 文章

使用OpenCV查找重叠/复杂的圆圈

提问于
浏览
5

我想计算红色圆圈半径(图2) . 我使用OpenCV的HoughCircles找到这些圈子很麻烦 . 正如你在图中看到的那样 . 2我只能在中心找到使用HoughCircles以黑色显示的小圆圈 .

原始
pic1
图2.
red

因为我知道红色圆圈的中心(与红色圆圈的中心相同),有没有办法简单地计算红色圆圈的半径?

是否也可以在更复杂的图像上使用通用方法计算圆的半径,例如:

example 2

编辑:在获得图2后,我的代码中有趣的部分:

threshold(maskedImage, maskedImage, thresh, 255, THRESH_BINARY_INV | THRESH_OTSU);
    std::vector<Vec3f> circles;
// Canny(maskedImage, maskedImage, thresh, thresh * 2, 3);

HoughCircles(maskedImage, circles, CV_HOUGH_GRADIENT, 1, src_gray.rows / 4, cannyThreshold, accumulatorThreshold, 0, 0);

Mat display = src_display.clone();
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]);
    // circle center
    circle(display, center, 3, Scalar(0, 255, 0), -1, 8, 0);
    // circle outline
    circle(display, center, radius, Scalar(0, 0, 255), 3, 8, 0);
}

我试图使用cannyThreshold和累加器播放没有结果 . 真实图像是5倍大 . 这里a link例如在阈值之后1 .

谢谢

1 回答

  • 5

    您已经知道图像中较小的圆圈(以黑色绘制) .

    • 使用这些圆准备掩模图像,使具有较小圆的区域具有非零像素 . 我们称之为 mask

    enter image description here

    • 在原始图像中,将这些圆形区域填充为深色(例如黑色) . 这将产生类似于图2的图像 . 我们称之为 filled

    • 阈值 filled 图像以获取暗区 . 我们称之为 binary . 您可以使用Otsu阈值处理 . 结果将如下所示:

    enter image description here

    • 对此 binary 图像进行距离变换 . 为此使用精确的距离估计方法 . 我们称之为 dist . 它看起来像这样 . 有色的只是一张热图,更清晰:

    enter image description here

    enter image description here

    • 使用 maskdist 获取峰区域 . 每个此类区域的最大值应该为您提供较大圆的半径 . 您还可以对这些区域进行一些处理,以获得更合理的半径值,而不是仅仅获取最大值 .

    • 为了选择区域,您可以找到 mask 的轮廓,然后从 dist 图像中提取该区域,或者,因为您已经知道应用霍夫圆变换的较小圆圈,所以从每个圆圈准备一个蒙版并提取来自 dist 图片的那个区域 . 我不确定你是否可以通过给出面具来计算最大或其他数据 . Max肯定会工作,因为其余的像素都是0.如果将这些像素提取到另一个数组,您可以计算区域的统计数据 .

    下图显示了这样的掩模和 dist 中提取的区域 . 为此,我获得了大约29的最大值,这与该圆的半径一致 . 请注意,图像不按比例 .

    一个圆圈的掩模,从 dist 提取的区域

    enter image description here

    enter image description here

    这是代码(我不使用hough-circles变换):

    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();
    

    结果(缩放):

    enter image description here

    enter image description here

    希望这可以帮助 .

相关问题