C++ 填补 OpenCV 中的漏洞

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1716274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:59:24  来源:igfitidea点击:

Fill the holes in OpenCV

c++matlabimage-processingopencvflood-fill

提问by Lily

I have an edge map extracted from edge detection module in OpenCV(canny edge detection). What I want to do is to fill the holes in the edge map.

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

I am using C++, and OpenCVlibraries. In OpenCV there is a cvFloodFill()function, and it will fill the holes with a seed (with one of the location to start flooding). However, I am trying to fill all the interior holes without knowing the seeds.(similar to imfill()in MATLAB)

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

Q1:how to find all the seeds, so that I could apply 'cvFloodFill()'?
Q2:how to implement a 'imfill()' equivalent?

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

Newbie in OpenCV, and any hint is appreciated.

OpenCV 新手,任何提示都值得赞赏。

回答by Amro

According to the documentation of imfillin MATLAB:

根据imfillMATLAB 中的文档:

BW2 = imfill(BW,'holes');

fills holes in the binary image BW. A hole is a set of background pixels that cannot be reached by filling in the background from the edge of the image.

BW2 = imfill(BW,'holes');

填充二值图像中的空洞BW。空洞是一组无法通过从图像边缘填充背景而达到的背景像素。

Therefore to get the "holes" pixels, make a call to cvFloodFillwith the left corner pixel of the image as a seed. You get the holes by complementing the image obtained in the previous step.

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

MATLAB Example:

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)

screenshot1screenshot2

截图1截图2

回答by NotNamedDwayne

the cvDrawContours function has an option to fill the contours that you have drawn.

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

Here is a short example cvDrawContours( IplImage, contours, color, color, -1, CV_FILLED, 8 );

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

Here is the documentation

这是文档

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

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

I guess you posted this a long time ago, but I hope it helps someone.

我猜你很久以前就发布了这个,但我希望它可以帮助某人。

This is the source code (in C#):

这是源代码(在 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);

Result

结果

回答by Jeremy.S

I've been looking around the internet to find a proper imfillfunction (as the one in Matlab) but working in C++ with OpenCV. After some reaserches, I finally came up with a solution :

我一直在互联网上寻找合适的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;
}

For this: Original Binary Image

为此:原始二进制图像

Result is: Final Binary Image

结果是:最终的二进制图像

The trick is in the parameters setting of the cvDrawContours function: cvDrawContours( dst, contour, white, white, 0, CV_FILLED);

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

  • dst = destination image
  • contour = pointer to the first contour
  • white = color used to fill the contour
  • 0 = Maximal level for drawn contours. If 0, only contour is drawn
  • CV_FILLED = Thickness of lines the contours are drawn with. If it is negative (For example, =CV_FILLED), the contour interiors are drawn.
  • dst = 目标图像
  • 轮廓=指向第一个轮廓的指针
  • 白色 = 用于填充轮廓的颜色
  • 0 = 绘制轮廓的最高级别。如果为 0,则仅绘制轮廓
  • CV_FILLED = 绘制轮廓线的粗细。如果它是负数(例如,=CV_FILLED),则绘制轮廓内部。

More info in the openCV documentation.

openCV 文档中的更多信息。

There is probably a way to get "dst" directly as a binary image but I couldn't find how to use the cvDrawContours function with binary values.

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

回答by Pedro Batista

I made a simple function that is equivalent to matlab's imfill('holes'). I've not tested it for many cases, but it has worked so far. I'm using it on edge images but it accepts any kind of binary image, like from a thresholding operation.

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

A hole is no more than a set of pixels that cannot be "reached" when background is filled, so,

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

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;
}

Here is an example result

这是一个示例结果

enter image description here

在此处输入图片说明

回答by rcv

Here's a quick and dirty approach:

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

  1. Perform canny on your input image so that the new binary image has 1's at the edges, and 0's otherwise
  2. Find the first 0 along a side of your edge image, and initiate a floodfill with 1's at that point on a blank image using your edge image as the mask. (We're hoping here that we didn't get unlucky and seed this first fill on the inside of a shape that is half-off the screen)
  3. This new floodfilled image is the 'background'. Any pixel here that has a 1 is the background, and any pixel that has a 0 is the foreground.
  4. Loop through the image and find any foreground pixels. Seed a floodfill on any you find.
  5. OR this new floodfilled image with your Canny image from step 1, and you're done.
  1. 对输入图像执行精明,以便新的二值图像边缘为 1,否则为 0
  2. 沿着边缘图像的一侧找到第一个 0,然后使用边缘图像作为蒙版在空白图像上的那个点开始用 1 进行填充。(我们希望在这里我们没有倒霉,并在屏幕一半的形状内部播种第一个填充)
  3. 这个新的充满洪水的图像是“背景”。这里任何具有 1 的像素都是背景,任何具有 0 的像素都是前景。
  4. 遍历图像并找到任何前景像素。在你找到的任何地方播种一个洪水填埋场。
  5. 或者使用步骤 1 中的 Canny 图像来填充这个新的填充图像,您就完成了。

回答by Tae-Sung Shin

Just an appendix for Amro's answer.

只是Amro 答案的附录

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;
    }
}

回答by dnul

Have you tried ContourFinding over the Cannyied Image?

您是否在 Cannyied 图像上尝试过 ContourFinding?

cvFindContours creates sort of a tree in which the outer countours are parents to the inner contours ('holes'). See contours.py sample. From the contours you could extract seeds

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

回答by Bill Xia

Recently I'am also finding the solution to this problem. Here I implemented Amro's idea as follows:

最近我也在寻找这个问题的解决方案。在这里我实现了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;
} 

Hope this will be helpful.

希望这会有所帮助。

回答by Rui Marques

If you have the points from the edges you can use fillConvexPoly() or fillPoly()(if poly not convex).

如果您有边缘的点,您可以使用fillConvexPoly() 或 fillPoly()(如果多边形不是凸面)。

One way to get the points from edges is to do findContours() -> approxPolyDP().

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