【问题标题】:Contours opencv : How to eliminate small contours in a binary imageContours opencv:如何消除二值图像中的小轮廓
【发布时间】:2012-05-01 14:21:49
【问题描述】:

我目前正在从事图像处理项目。我将 Opencv2.3.1 与 VC++ 一起使用。 我编写了这样的代码,输入图像被过滤为仅蓝色并转换为二进制图像。二进制图像有一些我不想要的小物体。我想消除那些小物体,所以我使用 openCV 的cvFindContours() 方法来检测二进制图像中的轮廓。但问题是我无法消除图像输出中的小物体。我用了cvContourArea()函数,但是不能正常工作..,erode函数也不能正常工作。

所以请有人帮我解决这个问题..

我得到的二值图像

我想要获得的结果/输出图像:

【问题讨论】:

    标签: c++ c visual-c++ opencv image-processing


    【解决方案1】:

    好的,我相信您的问题可以通过 OpenCV 最近引入的the bounding box demo 解决。

    您可能已经注意到,您感兴趣的对象应该位于图片中最大的矩形内。幸运的是,这段代码不是很复杂,我相信你可以通过调查和试验来弄清楚。

    【讨论】:

    • 万一,你有密码吗?因为链接失效了
    • @mrid 更新了死链接。
    【解决方案2】:

    这是我消除小轮廓的解决方案。 基本思想是检查每个轮廓的长度/面积,然后从矢量容器中删除较小的轮廓。

    通常你会得到这样的轮廓

    Mat canny_output; //example from OpenCV Tutorial
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    Canny(src_img, canny_output, thresh, thresh*2, 3);//with or without, explained later.
    findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0));
    

    使用 Canny() 预处理,您将获得轮廓段,但是每个段都与边界像素一起存储为闭合环。遇到这种情况,可以检查一下长度,把小的删掉就好了

    for (vector<vector<Point> >::iterator it = contours.begin(); it!=contours.end(); )
    {
        if (it->size()<contour_length_threshold)
            it=contours.erase(it);
        else
            ++it;
    }
    

    如果没有 Canny() 预处理,您将获得对象的轮廓。 类似的,你也可以使用面积来定义一个阈值来消除小物体,如 OpenCV 教程所示

    vector<Point> contour = contours[i];
    double area0 = contourArea(contour);
    

    这个contourArea()是非零像素的个数

    【讨论】:

      【解决方案3】:

      您确定按小轮廓区域过滤不起作用吗?它一直对我有用。我们可以看看你的代码吗?

      另外,正如 sue-ling 所提到的,最好同时使用侵蚀和扩张来大致保留面积。要删除小的噪声位,请先使用 erode,要填充孔,请先使用 dilate。

      另外,如果您还不知道 cv* 函数的新 C++ 版本(documentation 代表 findContours),您可能想查看它们。在我看来,它们更容易使用。

      【讨论】:

      • 感谢您的建议,但我不知道如何编写代码来查找已检测到的每个斑点/轮廓的区域。我编写的代码在以下链接中。你能更正它并回复吗? :textuploader.com/?p=6&id=YwRl.
      • 哦!我只是忘记了,cvutility 的头文件和 cpp 文件可以在这些链接中找到:textuploader.com/?p=6&id=Dtnvjtextuploader.com/?p=6&id=TSlRQ请将它们包含在源目录中并包含路径。
      • 我对C api不太熟悉,但看起来你只是在计算第一个轮廓的面积。您需要遍历轮廓并仅绘制面积 > 某个阈值的轮廓。如果你只是想找到一个物体,你应该画出面积最大的轮廓。
      【解决方案4】:

      根据前后图像判断,您需要确定所有白色区域或斑点的面积,然后应用阈值面积值。这将消除所有小于该值的区域,只留下第二张图像中看到的大的白色区域。使用 cvFindContours 函数后,尝试使用 0 阶矩。这将返回图像中斑点的区域。此链接可能有助于实现我刚刚描述的内容。 http://www.aishack.in/2010/07/tracking-colored-objects-in-opencv/

      【讨论】:

      • 感谢您的建议,但我不知道如何编写代码来查找已检测到的每个斑点/轮廓的区域。
      • 感谢您的建议,但我不知道如何编写代码来查找已检测到的每个斑点/轮廓的区域。我编写的代码在以下链接中。你能更正它并回复吗? :textuploader.com/?p=6&id=YwRl.
      • 哦!我只是忘记了,cvutility 的头文件和 cpp 文件可以在这些链接中找到:textuploader.com/?p=6&id=Dtnvjtextuploader.com/?p=6&id=TSlRQ请将它们包含在源目录中并包含路径。
      • 链接已失效。
      【解决方案5】:

      我相信您可以使用 erode 和 dilate 等形态运算符(阅读更多 here

      您需要使用接近右侧圆半径(您要消除的那个)的内核大小执行腐蚀。 然后使用相同的内核进行膨胀以填补侵蚀步骤产生的空白。

      仅供参考,腐蚀之后使用相同的内核进行膨胀称为开环。

      代码会是这样的

      int erosion_size = 30; // adjust with you application
      Mat erode_element = getStructuringElement( MORPH_ELLIPSE,
                               Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                               Point( erosion_size, erosion_size ) );
      erode( binary_img, binary_img, erode_element );
      dilate( binary_img, binary_img, erode_element );
      

      【讨论】:

        【解决方案6】:

        这不是一种快速的方法,但在某些情况下可能很有用。 OpencCV 3.0 中有一个新功能——connectedComponentsWithStats。有了它,我们可以获得连接组件的面积并消除不必要的。所以我们可以很容易地去除带孔的圆,与实心圆具有相同的边界框。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-11
          • 2017-11-06
          • 2014-08-23
          • 2016-09-25
          • 2018-01-18
          • 1970-01-01
          • 1970-01-01
          • 2011-12-13
          相关资源
          最近更新 更多