【问题标题】:Filling holes inside a binary object填充二进制对象内的孔
【发布时间】:2012-05-06 03:51:39
【问题描述】:

我在填充黑色硬币内的白洞时遇到问题,因此我只能使用填充黑色硬币的 0-255 二进制图像。我使用中值滤波器来完成它,但在这种情况下,硬币之间的连接桥会增长并且经过几次侵蚀后无法识别它们......所以我需要一个简单的opencv中类似floodFill的方法

这是我的带孔图片:

编辑: 类似 Floodfill 的函数必须填充大组件中的孔,而不提示 X、Y 坐标作为种子...

编辑:我尝试使用 cvDrawContours 函数,但我没有在较大的轮廓内填充轮廓。

这是我的代码:

        CvMemStorage mem = cvCreateMemStorage(0);
        CvSeq contours = new CvSeq();
        CvSeq ptr = new CvSeq();
        int sizeofCvContour = Loader.sizeof(CvContour.class);

        cvThreshold(gray, gray, 150, 255, CV_THRESH_BINARY_INV);

        int numOfContours = cvFindContours(gray, mem, contours, sizeofCvContour, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
        System.out.println("The num of contours: "+numOfContours); //prints 87, ok

        Random rand = new Random();
        for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
            Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
            CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
            cvDrawContours(gray, ptr, color, color, -1, CV_FILLED, 8);
        }
        CanvasFrame canvas6  = new CanvasFrame("drawContours");
        canvas6.showImage(gray);

结果:(你可以看到每个硬币里面都有黑洞)

【问题讨论】:

    标签: image-processing opencv javacv


    【解决方案1】:

    有两种方法可以做到这一点:

    1) 轮廓填充:

    首先反转图像,在图像中找到轮廓,用黑色填充并反转回来。

    des = cv2.bitwise_not(gray)
    contour,hier = cv2.findContours(des,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
    
    for cnt in contour:
        cv2.drawContours(des,[cnt],0,255,-1)
    
    gray = cv2.bitwise_not(des)
    

    生成的图像:

    2) 图片打开:

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
    res = cv2.morphologyEx(gray,cv2.MORPH_OPEN,kernel)
    

    生成的图像如下:

    你可以看到,这两种情况没有太大区别。

    NB:gray - 灰度图像,所有代码都在OpenCV-Python中

    【讨论】:

    • 好的,我有一个参数问题我应该使用:cvDrawContours(gray, ptr, color, color, 0, CV_FILLED, 8);以 0 作为 max_level 参数。嗯,现在它充满了成功:)
    • 尝试设置更大的 StructuringElement,例如 15,15。这可能会改善您的“开放”程序
    • 很酷的解决方案 - 谢谢!对我来说,图像打开方法的执行时间约为 6 毫秒,轮廓填充方法的执行时间约为 3.5 毫秒。处理使用 cv2.VideoCapture(0) 和我的 macbook pro 拍摄的视频。有什么方法可以使用 numpy 加快速度,还是 opencv 已经在使用?
    • @user391339:我认为在这方面没有什么可以提高它的速度,因为它使用现成的 opencv 函数,并且已经优化到最好。哦..请等一下!!!
    • @user391339:再想一想,我认为使用cv2.RETR_ExTERNAL 而不是cv2.RETR_CCOMP 可能会有一点改进。我不确定,你必须检查一下。但是,它仍然没有多大用处。相反,您可以检查代码中其他需要更多时间的内容并尝试对其进行优化。顺便说一句,它只有 3.5ms。为什么还需要提高它的速度?
    【解决方案2】:

    我想,一个简单的扩张和侵蚀可以很好地缩小差距。我想也许这就是你要找的。​​p>

    更稳健的解决方案是对整个图像进行边缘检测,然后对圆形进行霍夫变换。一个快速的谷歌显示有各种语言的代码示例,用于使用霍夫变换来检测圆圈的大小不变,所以希望这会给你一些东西。

    使用霍夫变换的好处是该算法实际上会为您提供每个圆的大小和位置的估计值,因此您可以根据该模型重建理想的图像。重叠也应该非常稳健,尤其是考虑到这里输入图像的质量(即更少担心误报,因此可以降低结果阈值)。

    【讨论】:

      【解决方案3】:

      您可能正在寻找Fillhole transformation,这是一种形态图像重建的应用程序。

      这种转换将填补您硬币上的孔,尽管代价是同时填充相邻硬币组之间的所有孔。其他发帖者建议的霍夫空间或基于开放的解决方案可能会给您更好的高级识别结果。

      【讨论】:

      • 但对于 opencv 不适用于 matlab
      • @lbstr:页面主要讲解理论。我不能把 OpenCV 代码给你。
      • 链接已失效。
      • @Reunanen Link 在撰写本文时仍在运行。该链接指向 MATLAB 图像处理工具箱中的官方 imfill 函数。只要 MATLAB 存在,它就应该是可靠的。
      • @rayryeng-ReinstateMonica 它可以运行,因为 Cris Luengo 在我发表评论后修复了它:stackoverflow.com/posts/10318004/revisions
      【解决方案4】:

      如果有人正在寻找 cpp 实现 -

                  std::vector<std::vector<cv::Point> > contours_vector;
      
                  cv::findContours(input_image, contours_vector, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
      
                  cv::Mat contourImage(input_image.size(), CV_8UC1, cv::Scalar(0));
                  for ( ushort contour_index = 0; contour_index < contours_vector.size(); contour_index++) {
                      cv::drawContours(contourImage, contours_vector, contour_index, cv::Scalar(255), -1);
                  }
      
                  cv::imshow("con", contourImage);
                  cv::waitKey(0);
      

      【讨论】:

        【解决方案5】:

        尝试使用cvFindContours() 函数。您可以使用它来查找连接的组件。使用正确的参数,此函数会返回一个包含每个连接组件的轮廓的列表。

        找到代表孔的轮廓。然后使用cvDrawContours() 用前景色填充所选轮廓,从而关闭孔。

        【讨论】:

          【解决方案6】:

          我认为如果物体被触摸或拥挤,使用轮廓和数学形态学开口会出现一些问题。 相反,找到并测试了以下简单的解决方案。它工作得很好,不仅适用于这张图片,还适用于任何其他图片。

          这是http://blogs.mathworks.com/steve/2008/08/05/filling-small-holes/中看到的步骤(优化)

          I: 输入图像

          1. filled_I = floodfill(I). // fill every hole in the image.
          2. inverted_I = invert(I)`.   
          3. holes_I = filled_I AND inverted_I. // finds all holes 
          4. cc_list = connectedcomponent(holes_I) // list of all connected component in holes_I.
          5. holes_I = remove(cc_list,holes_I, smallholes_threshold_size) // remove all holes from holes_I having size > smallholes_threshold_size.
          6. out_I = I OR holes_I. // fill only the small holes.
          

          简而言之,算法就是找到所有的洞,去掉大的,然后只在原始图像上写小的。

          【讨论】:

            【解决方案7】:

            我一直在互联网上寻找合适的 imfill 函数(如 Matlab 中的函数),但在 C 中使用 OpenCV。经过一番研究,我终于想出了一个解决方案:

            IplImage* imfill(IplImage* src)
            {
                CvScalar white = CV_RGB( 255, 255, 255 );
            
                IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3);
                CvMemStorage* storage = cvCreateMemStorage(0);
                CvSeq* contour = 0;
            
                cvFindContours(src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
                cvZero( dst );
            
                for( ; contour != 0; contour = contour->h_next )
                {
                    cvDrawContours( dst, contour, white, white, 0, CV_FILLED);
                }
            
                IplImage* bin_imgFilled = cvCreateImage(cvGetSize(src), 8, 1);
                cvInRangeS(dst, white, white, bin_imgFilled);
            
                return bin_imgFilled;
            }
            

            为此:Original Binary Image

            结果是:Final Binary Image

            诀窍在于 cvDrawContours 函数的参数设置: cvDrawContours(dst,contour,white,white,0,CV_FILLED);

            • dst = 目标图像
            • 轮廓 = 指向第一个轮廓的指针
            • 白色 = 用于填充轮廓的颜色
            • 0 = 绘制轮廓的最大级别。如果为 0,则仅绘制轮廓
            • CV_FILLED = 绘制轮廓线的粗细。如果为负数(例如 =CV_FILLED),则绘制轮廓内部。

            openCV 文档中的更多信息。

            可能有一种方法可以直接将“dst”作为二进制图像获取,但我找不到如何将 cvDrawContours 函数与二进制值一起使用。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-08-11
              • 2014-08-03
              • 2014-04-14
              • 1970-01-01
              • 1970-01-01
              • 2019-05-19
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多