【问题标题】:Find overlapping/complex circles with OpenCV使用 OpenCV 查找重叠/复杂的圆
【发布时间】:2014-10-29 07:06:18
【问题描述】:

我想计算红色圆圈的半径(图 2)。我很难使用来自 OpenCV 的 HoughCircles 找到这些圆圈。如图所示。 2 我只能找到中心的小圆圈,使用 HoughCircles 显示为黑色。

原创图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 和 accumulator 玩游戏,但没有结果。真实图像大 5 倍。这里a link 例如阈值后的 1。

谢谢

【问题讨论】:

  • 你能展示你的 HoughCircles 代码吗?对于 HoughCircles afaik 来说,重叠/半圆等通常不是问题。
  • 谢谢 Micka 我刚刚添加了一些代码
  • 您可以将 maskedImage 保存到文件 (imwrite("maskedImage.jpg", maskedImage)) 并发布链接吗?
  • 我们需要maskedImagethreshold(maskedImage, maskedImage, thresh, 255, THRESH_BINARY_INV | THRESH_OTSU); 之后而不是之前:)
  • 你能修复链接吗?

标签: opencv image-processing


【解决方案1】:

你已经知道图像中的小圆圈(你用黑色画的)。

  • 使用这些圆圈准备蒙版图像,这样具有较小圆圈的区域将具有非零像素。我们称之为面具

  • 在原始图像中,用深色(比如黑色)填充这些圆形区域。这将产生如图 2 所示的图像。我们将其称为 filled
  • 填充图像设置阈值以获得暗区。我们称之为二进制。您可以为此使用 Otsu 阈值。结果将如下所示:

  • 对此二进制图像进行距离变换。为此使用准确的距离估计方法。我们称之为 dist。它看起来像这样。彩色的只是为了更清晰的热图:

  • 使用 ma​​skdist 获取峰值区域。每个此类区域的最大值应该为您提供较大圆的半径。您还可以对这些区域进行一些处理,以获得更合理的半径值,而不仅仅是获取最大值。
  • 要选择区域,您可以找到 ma​​sk 的轮廓,然后从 dist 图像中提取该区域,或者,因为您已经知道应用 hough-circle 变换,从每个圆圈中准备一个掩码,并从 dist 图像中提取该区域。我不确定您是否可以通过提供掩码来计算最大值或其他统计数据。 Max 肯定会起作用,因为其余像素为 0。如果将这些像素提取到另一个数组中,您也许可以计算该区域的统计数据。

下图显示了这样的掩码和从 dist 中提取的区域。为此,我得到一个大约 29 的最大值,这与该圆的半径一致。请注意,图片未按比例缩放。

圆的掩码,从 dist

中提取的区域

这是代码(我没有使用霍夫圆变换):

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

结果(缩放):

希望这会有所帮助。

【讨论】:

  • 谢谢,这是个好主意。我使用 distanceTransform 获得了相同的结果。 (顺便说一下,你如何为距离图着色 applyColorMap 似乎对我不起作用)。但是我看不到如何区分圆半径并有效地计算这些最大值?
  • 在 OpenCV 中,距离变换结果将是浮点类型。尝试将其缩放到 0-255 范围并转换为 uint8 类型。然后 applyColorMap 将起作用。至于获得最大值,请参阅我编辑的帖子,看看我是否在那里回答了您的问题。
  • 感谢您的宝贵回答。我得到了地图颜色,但我很难理解如何使用蒙版图像和小圆圈中心提取区域。你能详细说明这部分吗?
  • 最后我加了两个数字。使用您已经必须为每个这样的圆圈生成一个掩码的较小的霍夫圆。第一个图像就是这样的掩码,第二个图像是从距离变换图像中提取的相应区域。然后,您可以找到第二张图像的最大值,即较大圆的半径。您可以对其他圈子重复此操作。
  • 中心不会给出半径,除非它与最大距离重叠。因此,最好考虑中心附近的社区。我这个解决方案,邻居是较小的磁盘。
猜你喜欢
  • 2021-10-10
  • 2016-07-23
  • 2020-12-24
  • 2015-01-12
  • 2021-07-19
  • 1970-01-01
  • 2020-01-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多