填补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中的漏洞[重复]的主要内容,如果未能解决你的问题,请参考以下文章