【问题标题】:Fill the holes in OpenCV [duplicate]填补OpenCV中的漏洞[重复]
【发布时间】:2010-12-15 12:50:05
【问题描述】:

我有一个从OpenCV 中的边缘检测模块中提取的边缘图(canny 边缘检测)。我想要做的是填补边缘图中的洞。

我正在使用 C++OpenCV 库。在 OpenCV 中有一个 cvFloodFill() 函数,它会用种子填充孔洞(其中一个位置开始泛滥)。但是,我试图在不知道种子的情况下填充所有内部孔。(类似于 MATLAB 中的imfill()

Q1:如何找到所有种子,以便我可以应用'cvFloodFill()'?
Q2:如何实现'imfill() ' 等价的?

OpenCV 新手,欢迎提供任何提示。

【问题讨论】:

    标签: c++ matlab image-processing opencv flood-fill


    【解决方案1】:

    根据imfill在MATLAB中的文档:

    BW2 = imfill(BW,'holes');
    

    填充二进制图像BW 中的漏洞。 洞是一组背景像素,无法通过从图像边缘填充背景来达到。

    因此,要获取“孔”像素,请调用cvFloodFill,并将图像的左角像素作为种子。您可以通过补充上一步中获得的图像来获得孔。

    MATLAB 示例:

    BW = im2bw( imread('coins.png') );
    subplot(121), imshow(BW)
    
    % used here as if it was cvFloodFill
    holes = imfill(BW, [1 1]);    % [1 1] is the starting location point
    
    BW(~holes) = 1;               % fill holes
    subplot(122), imshow(BW)
    

    【讨论】:

    • 太棒了,阿姆罗!谢谢!!!
    • 非常好。我试图弄清楚如何在不依赖imfill 中的holes 标志的情况下做到这一点。我想在 OpenCV Python 上执行此操作,您已经向我展示了方法。谢谢阿姆罗!
    【解决方案2】:

    cvDrawContours 函数有一个选项可以填充您绘制的轮廓。

    这是一个简短的例子 cvDrawContours(IplImage,contours,color,color,-1,CV_FILLED,8);

    这是文档

    http://opencv.willowgarage.com/documentation/drawing_functions.html?highlight=cvdrawcontours#cvDrawContours

    我猜你很久以前就发过这个了,但我希望它对某人有所帮助。

    这是源代码(C#):

            Image<Gray, byte> image = new Image<Gray, byte>(@"D:\final.bmp");
            CvInvoke.cvShowImage("image 1", image);
    
            var contours = image.FindContours();
            while (contours != null)
            {
                CvInvoke.cvDrawContours(image, contours, new Gray(255).MCvScalar, new Gray (255).MCvScalar, 0, -1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, new DPoint(0, 0));
                contours = contours.HNext;
            }
            CvInvoke.cvShowImage("image 2", image);
    

    【讨论】:

      【解决方案3】:

      我一直在互联网上寻找合适的 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 函数与二进制值一起使用。

      【讨论】:

      • 确切地说,这不是使用 C++ API,而是使用旧的 C 接口。除此之外,这与NotNamedDwayne 在他的回答中显示的相同
      • @Amro :你说得对,我编辑了我的答案。谢谢!
      【解决方案4】:

      我做了一个简单的函数,相当于matlab的imfill('holes')。我没有在很多情况下测试过它,但到目前为止它已经奏效了。我在边缘图像上使用它,但它接受任何类型的二进制图像,例如阈值操作。

      一个洞只不过是填充背景时无法“到达”的一组像素,所以,

      void fillEdgeImage(cv::Mat edgesIn, cv::Mat& filledEdgesOut) const
      {
          cv::Mat edgesNeg = edgesIn.clone();
      
          cv::floodFill(edgesNeg, cv::Point(0,0), CV_RGB(255,255,255));
          bitwise_not(edgesNeg, edgesNeg);
          filledEdgesOut = (edgesNeg | edgesIn);
      
          return;
      }
      

      这是一个示例结果

      【讨论】:

        【解决方案5】:

        这是一种快速而肮脏的方法:

        1. 对输入图像执行 canny,使新的二值图像边缘为 1,否则为 0
        2. 在边缘图像的一侧找到第一个 0,然后使用边缘图像作为蒙版在空白图像上的该点处使用 1 启动填充。 (我们希望在这里我们没有走运,并将第一次填充播种在屏幕一半的形状内部)
        3. 这个新的填充图像是“背景”。此处任何像素为 1 的都是背景,任何像素为 0 的都是前景。
        4. 遍历图像并找到任何前景像素。在你找到的任何东西上播种填充物。
        5. 或者使用您在第 1 步中的 Canny 图像使用这个新的填充图像,您就完成了。

        【讨论】:

          【解决方案6】:

          只是Amro's answer.的附录

          void cvFillHoles(cv::Mat &input)
          {
              //assume input is uint8 B & W (0 or 1)
              //this function imitates imfill(image,'hole')
              cv::Mat holes=input.clone();
              cv::floodFill(holes,cv::Point2i(0,0),cv::Scalar(1));
              for(int i=0;i<input.rows*input.cols;i++)
              {
                  if(holes.data[i]==0)
                      input.data[i]=1;
              }
          }
          

          【讨论】:

          • 可以从 255 中减去以补充 CV_8U 二值图像,而不是循环,相当于:holes = 255 -holes
          【解决方案7】:

          您是否尝试过通过 Cannyied Image 进行 ContourFinding?

          cvFindContours 创建了一种树,其中外部计数是内部轮廓(“孔”)的父级。请参阅 contours.py 示例。从轮廓中你可以提取种子

          【讨论】:

          • 我正在尝试这个,它不起作用:FindContours 识别出许多边缘急剧转弯的假轮廓,因此您无法准确地标记它们。
          【解决方案8】:

          最近我也在寻找解决这个问题的方法。这里我实现Amro的思路如下:

          #include <iostream>
          using namespace std;
          #include <cv.h>
          #include <cxcore.h>
          #include <highgui.h>
          using namespace cv;
          
          int main()
          {
              IplImage *im = cvLoadImage("coin.png",CV_LOAD_IMAGE_ANYDEPTH);
              IplImage *hole = cvCreateImage(cvSize(im->width,im->height),8,1);
              cvShowImage("Original",im);
          
              cvCopyImage(im,hole);
              cvFloodFill(hole,cvPoint(0,0),cvScalar(255));
              cvShowImage("Hole",hole);
              cvSaveImage("hole.png",hole);
          
              cvNot(hole,hole);
              cvAdd(im,hole,im);
              cvShowImage("FillHole",im);
              cvSaveImage("fillHole.png",im);
          
              cvWaitKey(0);
              system("pause");
              return 0;
          } 
          

          希望这会有所帮助。

          【讨论】:

          • 不幸的是,这也使用了即将被弃用的 OpenCV 的 C API。不要再使用它了,除非你有一些非常特定的理由这样做。
          【解决方案9】:

          如果你有来自边缘的点,你可以使用 fillConvexPoly() 或 fillPoly()(如果 poly 不是凸的)。

          从边缘获取点的一种方法是执行 findContours() -> approxPolyDP()

          【讨论】:

            猜你喜欢
            • 2011-11-04
            • 1970-01-01
            • 1970-01-01
            • 2016-10-24
            • 1970-01-01
            • 1970-01-01
            • 2015-05-03
            • 1970-01-01
            • 2021-06-03
            相关资源
            最近更新 更多