填补OpenCV中的漏洞[重复]

Posted

技术标签:

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

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

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

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

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

【问题讨论】:

【参考方案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】:

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

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

【讨论】:

以上是关于填补OpenCV中的漏洞[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在opencv中平滑图像中插入孔?

Java中的C ++(OpenCV)[重复]

opencv中的重复轮廓

Windows 10中的Opencv导入问题[重复]

OpenCV暴严重安全漏洞

OpenCV 曝出两个严重漏洞