【问题标题】:OpenCV Blob Detector has offset on found positionOpenCV Blob Detector 在找到的位置上有偏移
【发布时间】:2023-03-09 19:56:02
【问题描述】:

我已经尽可能地简化了这个,但仍然得到错误,所以我认为这是一个 OpenCV 错误......但试图确认我没有先做一些愚蠢的事情。

我有这张图片:

哪个 SimpleBlobDetector 被检测为

:

如您所见,有两个小斑点被正确检测到,但主要的大斑点在 x 方向偏移了大约 20 个像素,在 y 方向偏移了 10 个像素。我做错了什么?

显示此错误的示例应用程序:

#include "opencv2/opencv.hpp"

using namespace cv;

int main()
{
    cv::Mat im = imread("blob_error.png", cv::IMREAD_GRAYSCALE);

    // Set up SimpleBlobDetector parameters.
    cv::SimpleBlobDetector::Params params{};

    // Filter by color
    params.filterByColor = true;
    params.blobColor = 255;

    // Change grayscale search thresholds - since the input is a pre-binary-thresholded image, just use some values in the middle of the gray range
    // Note that simpleblobfinder does multiple passes at each threshold, exclusively, so the below is equivalent to a single pass at 128
    // The docs say the min is inclusive and the max is exclusive, but setting (128,129) yields zero detections
    params.minThreshold  = 127;
    params.maxThreshold  = 129;
    params.thresholdStep = 1;

    // Consider very-close blobs to be the same blob
    params.minDistBetweenBlobs = 5;

    // Filter by Area.  Always want to filter out tiny noise blobs
    params.filterByArea = true;
    params.minArea = 10;                                                              // Minimum size just to ignore noise
    params.maxArea = static_cast<float>(im.size().width * im.size().height) * 0.9f  ; // Let's say maximum size is 90% of the visible area.

    // Filter by Circularity (skipping)
    params.filterByCircularity = false;
    params.minCircularity = 0.1;
    params.maxCircularity = 1;

    // Filter by Convexity (skipping)
    params.filterByConvexity = false;
    params.minConvexity = 0;//.67;

    // Filter by Inertia (skipping)
    params.filterByInertia = false;
    params.minInertiaRatio = 0;//0.30;

    // Perform the detection
    cv::Ptr<cv::SimpleBlobDetector> detector = cv::SimpleBlobDetector::create(params);
    std::vector<cv::KeyPoint> keypoints;
    detector->detect( im, keypoints );

    cv::Mat im_with_keypoints;
    drawKeypoints(im, keypoints, im_with_keypoints, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    imshow("keypoints", im_with_keypoints);
    imwrite("blob_error_detected.png", im_with_keypoints);
    cvWaitKey(0);

    return 0;
}

我正在使用 OpenCV 3.4.2


编辑 -- 这是没有中心圆的图像:

这里是检测结果(没有圆形过滤)

【问题讨论】:

  • 我建议使用 findcontour 来更好地控制您想要实现的目标。
  • 我实际上使用了一系列检测机制,包括轮廓——但我担心这个奇怪的结果以及它为什么会给出偏移量。我想确保我没有遗漏任何东西,如果没有,请计划将其提交给 opencv 错误跟踪器。
  • 我不认为这是一个错误。请检查我的答案。

标签: c++ opencv computer-vision


【解决方案1】:

啊啊...想通了 - 非常感谢 @ziri 弹跳这个。

对于二值图像,使用以下参数:

params.minThreshold  = 128;
params.maxThreshold  = 129;
params.thresholdStep = 1;

params.minRepeatability = 1;

在我最初的评论中,我注意到将 min/max 设置为 128/129 不起作用,并假设有关 minThreshold 包含在内的文档是错误的,但它是正确的。失败的原因是,默认情况下,至少需要通过两次阈值处理才能返回 blob。

核心问题是 SimpleBlobDetector 不适用于二进制图像。正如我在问题中指出的那样,我最终没有使用它并直接使用轮廓,但想了解奇怪结果的原因。

关于那个奇怪的结果,目前没有足够的时间来确认,但是这里的 SimpleBlobDetectorImpl::detect 方法看起来像:https://github.com/opencv/opencv/blob/master/modules/features2d/src/blobdetector.cpp 合并了 blob 的中心位置,而不是大小。我怀疑这在灰度图像上比在二值图像上更有意义。

核心要点是,对于二进制图像,使用 minRepeatability = 1 并将阈值设置为 128/129/1,如上所述。 (否则不要使用 SimpleBlobDetector 并直接使用轮廓)。

【讨论】:

    【解决方案2】:

    BlobDetector 实际上是检测你拥有的最大斑点的质心,而不是圆形的!所以 Blobdetector 的结果是对的。

    如果您的目标是检测中心的圆形斑点,请设置:

    // Filter by Circularity
    params.filterByCircularity = true;
    params.minCircularity = 0.5;
    params.maxCircularity = 1;
    

    编辑:根据区域检测所有 blob(Blob 检测器)

    #include <opencv2\opencv.hpp>
    #include <opencv2\highgui\highgui.hpp>
    #include <opencv2\core\mat.hpp>
    #include <opencv2\imgproc.hpp>
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    
    int main()
    {
    cv::Mat im = imread("C:\\Users\\RaonOtics_Ziri\\Desktop\\vid\\test3.png", 
    cv::IMREAD_GRAYSCALE);
    
    // Set up SimpleBlobDetector parameters.
    cv::SimpleBlobDetector::Params params;
    
    params.filterByInertia = false;
    params.filterByConvexity = false;
    params.filterByCircularity = false;
    params.filterByColor = true;
    
    // Change thresholds
    params.minThreshold = 10;
    params.maxThreshold = 255;
    params.blobColor = 255;
    
    // Filter by Area
    params.filterByArea = true;
    params.minArea = 0;
    params.maxArea = 90000000;
    params.minDistBetweenBlobs = 0.0f;
    
    // Perform the detection
    cv::Ptr<cv::SimpleBlobDetector> detector = 
    cv::SimpleBlobDetector::create(params);
    std::vector<cv::KeyPoint> keypoints;
    detector->detect(im, keypoints);
    
    cv::Mat im_with_keypoints;
    drawKeypoints(im, keypoints, im_with_keypoints, cv::Scalar(0, 0, 255), 
    cv::DrawMatchesFlags::DEFAULT);
    
    imshow("keypoints", im_with_keypoints);
    
    imwrite("blob_error_detected.png", im_with_keypoints);
    cvWaitKey(0);
    
    return 0;
    

    }

    你会得到这个结果:

    并启用循环过滤(最小值 = 0.5 最大值 = 1):

    【讨论】:

    • 嗯,这很奇怪,虽然我不认为它检测到的大 blob 的大小等于较小 blob 的大小。为了测试,我移除了中心圆,并以您建议的 0.5-1.0 圆度范围再次运行。它只找到两个小圆圈。如果我禁用圆形过滤器,我会得到一个大的检测,但是如果源图像中不存在那个中心圆,我无法让它从我的原始结果返回较小的偏移圆。请查看我对新图像的编辑。
    • 要仅测试 blob 大小,您必须将所有其他参数设置为 false。正如您在此处看到的 (github.com/opencv/opencv/blob/master/modules/features2d/src/… ),类构造函数具有这些参数的默认值。
    • 所以,我确实在我的原始代码中禁用了其他参数,但为了确认,我用你的代码进行了测试。使用上面的代码,请尝试使用 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS 样式的关键点来查看我在说什么。我刚刚使用 RICH_KEYPOINTS 和修改后的图像(没有中心 blob)对其进行了测试,我看到了与我在上面的编辑中提到的相同的结果(对于主 blob)。顺便说一句,感谢您挖掘这个!
    猜你喜欢
    • 2012-11-16
    • 2011-05-06
    • 2018-03-05
    • 1970-01-01
    • 2012-10-13
    • 1970-01-01
    • 2018-05-26
    • 2018-09-07
    • 1970-01-01
    相关资源
    最近更新 更多