我在OpenCV(canny边缘检测)中从边缘检测模块中提取了边缘图。我想要做的是填补边缘图中的孔。
我正在使用 C ++ 和 OpenCV 库。在OpenCV中有一个cvFloodFill()函数,它将用种子填充漏洞(其中一个位置开始泛滥)。但是,我试图在不知道种子的情况下填充所有内部空洞。(类似于MATLAB中的imfill())
Q1:如何查找所有种子,以便我可以应用'cvFloodFill()'? Q2:如何实现'imfill()'等效?
OpenCV中的新手,任何提示都表示赞赏。
答案 0 :(得分:34)
根据MATLAB中imfill
的文档:
BW2 = imfill(BW,'holes');
填充二进制图像
BW
中的空洞。 孔是一组背景像素,通过从图像边缘填充背景无法到达。
因此,要获得“孔”像素,请使用图像的左角像素作为种子调用cvFloodFill
。通过补充上一步中获得的图像来获得漏洞。
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)
答案 1 :(得分:12)
cvDrawContours函数有一个选项来填充您绘制的轮廓。
这是一个简短的例子 cvDrawContours(IplImage,轮廓,颜色,颜色,-1,CV_FILLED,8);
这是文档
我猜你很久以前就贴了这个,但我希望它可以帮助别人。
这是源代码(在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);
答案 2 :(得分: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;
}
诀窍在于cvDrawContours函数的参数设置: cvDrawContours(dst,contour,white,white,0,CV_FILLED);
openCV文档中的更多信息。
可能有一种方法可以直接将“dst”作为二进制图像,但我找不到如何将cvDrawContours函数与二进制值一起使用。
答案 3 :(得分:6)
我做了一个简单的函数,相当于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;
}
以下是一个示例结果
答案 4 :(得分:4)
这是一种快速而肮脏的方法:
答案 5 :(得分:3)
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;
}
}
答案 6 :(得分:2)
您是否尝试过Cannyied图像上的ContourFinding?
cvFindContours创建一种树,其中外部计数器是内部轮廓(“孔”)的父项。请参阅contours.py示例。从轮廓中你可以提取种子
答案 7 :(得分:2)
最近我也在找到解决这个问题的方法。我在这里实施了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;
}
希望这会有所帮助。
答案 8 :(得分:0)
如果你有边缘的点,你可以使用 fillConvexPoly()或fillPoly()(如果poly不凸)。
从边缘获取点的一种方法是 findContours() - &gt; approxPolyDP()强>