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
Fill the holes in OpenCV
提问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 imfill
in MATLAB:
根据imfill
MATLAB 中的文档:
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 cvFloodFill
with 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)
回答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
这是文档
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);
回答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
这是一个示例结果
回答by rcv
Here's a quick and dirty approach:
这是一个快速而肮脏的方法:
- Perform canny on your input image so that the new binary image has 1's at the edges, and 0's otherwise
- 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)
- 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.
- Loop through the image and find any foreground pixels. Seed a floodfill on any you find.
- OR this new floodfilled image with your Canny image from step 1, and you're done.
- 对输入图像执行精明,以便新的二值图像边缘为 1,否则为 0
- 沿着边缘图像的一侧找到第一个 0,然后使用边缘图像作为蒙版在空白图像上的那个点开始用 1 进行填充。(我们希望在这里我们没有倒霉,并在屏幕一半的形状内部播种第一个填充)
- 这个新的充满洪水的图像是“背景”。这里任何具有 1 的像素都是背景,任何具有 0 的像素都是前景。
- 遍历图像并找到任何前景像素。在你找到的任何地方播种一个洪水填埋场。
- 或者使用步骤 1 中的 Canny 图像来填充这个新的填充图像,您就完成了。
回答by Tae-Sung Shin
Just an appendix for 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;
}
}
回答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()。