上一节我们学习了用如何用findContours函数对图像查找轮廓,以及用drawContours函数对查找的轮廓进行绘制,相信大家学习之后,对图像轮廓已经有了基本的认识,本节呢,我们对漫水填充算法(floodFill)进行详细的理解!
一、漫水填充函数详解
1、函数原型
int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, CV_OUT Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4);int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, CV_OUT Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4);
2、函数功能
用指定的颜色从种子点(参数中 seedPoint 为种子点)开始填充连通的元素;
在以下情况下,(x,y) 处的像素被视为重新绘制的区域:
(1)在灰度图像和浮动范围的情况下
![1a517245c2ed54f25c514b6e8dfc22d7.png](https://i-blog.csdnimg.cn/blog_migrate/6647f37d3cb3c4f0c443bc90d41b0784.jpeg)
在灰度图像和浮动范围的情况下
(2)在灰度图像和固定范围的情况下
![a1c4304f6df33ed287daf1efda662724.png](https://i-blog.csdnimg.cn/blog_migrate/4afede7b293abffddc65e70970b29b3f.jpeg)
在灰度图像和固定范围的情况下
(3)在彩色图像和浮动范围的情况下
![8b76e5aadab7db7ccb7022de9d800a13.png](https://i-blog.csdnimg.cn/blog_migrate/c6c2c848dae8c48c096285f7fdc2505a.jpeg)
在彩色图像和浮动范围的情况下
(4)在彩色图像和固定范围的情况下
![50140fbf7f6d91716d3eb8cbd79917bd.png](https://i-blog.csdnimg.cn/blog_migrate/56dee86aab1769993e9ee4e6be307e74.jpeg)
在彩色图像和固定范围的情况下
其中 src(x′,y′) 已知属于该元素的像素邻域之一的值 。也就是说,要添加到连接的元素中,像素的颜色/亮度应该足够接近于:
(1)在浮动范围内,已属于已连接元素的邻域之一的颜色/亮度;
(2)在固定范围内种子点的颜色/亮度。
使用这些函数标记具有指定颜色的连接要素,或构建掩码,然后提取轮廓,或将区域复制到其他图像,以此类推。
3、参数详解
- 第一个参数,InputOutputArray image,输入/输出图像,这个图像可以是单通道或者三通道图像,也可以是8位或者32位浮点图像;图像会被函数修改,如果在重载的函数中设置FLOODFILL_MASK_ONLY标志,图像不会被修改;
- 第二个参数,InputOutputArray mask,操作的掩码图像,一般为8位单通道图像,比图像(image)宽两个像素,高两个像素;由于这个mask输一个输入/输出参数,所以必须对这个参数进行初始化,即必须创建一个掩码图像;漫水填充不能跨越输入掩码中的非零像素;例如,边缘检测器输出可以用作掩码,以停止边缘填充;在输出时,对应于图像中填充像素的掩码中的像素设置为1或设置为在标志中指定的值;此外,该函数填充掩码的边框,以简化内部处理;因此,可以在函数的多次调用中使用相同的掩码,以确保填充的区域不重叠;
- 第三个参数,Point seedPoint,漫水填充的种子点;
- 第四个参数,Scalar newVal,重新绘制漫水填充区域所用的颜色;
- 第五个参数,CV_OUT Rect* rect = 0,函数将可选输出参数设置为重绘域的最小边界矩形;
- 第六个参数,Scalar loDiff = Scalar(),当前选定像素与其连通区中相邻像素中的一个像素,或者与加入该连通区的一个seedPoint像素,二者之间的最大下行差异值;
- 第七个参数,Scalar upDiff = Scalar(),当前选定像素与其连通区中相邻像素中的一个像素,或者与加入该连通区的一个seedPoint像素,二者之间的最大上行差异值;
- 第八个参数,int flags = 4,操作标记,前8位包含一个连通的值;默认值4意味着只考虑四个最近邻像素(共享边缘的像素);连通的值为8意味着将考虑8个最近的相邻像素(共享一个角的像素);接下来的8位(8-16)包含一个介于1到255之间的值,用来填充掩码(默认值是1);例如,4 |(255<8)将考虑4个最近的邻居,并填充值为255的掩码。以下附加选项占用较高的位,因此可能会进一步结合使用按位进行填充的连接和掩码填充值。
floodfill填充模式
1、FLOODFILL_FIXED_RANGE ,如果设置,则考虑当前像素与种子像素之间的差异;否则,考虑相邻像素之间的差异(即范围是浮动的)。
2、FLOODFILL_MASK_ONLY ,如果设置,函数不会更改图像(忽略newVal),并且只使用上面描述的标志位8-16中指定的值填充掩码;此选项仅在具有掩码参数的函数变体中才有意义。
二、综合实例
1、实验案例
#include #include #include #include #include using namespace cv;using namespace std;static void help(){ cout << "键盘: " "ESC - 退出程序" "c - 切换彩色与灰度图像" "m - 切换掩码图像" "r - 恢复为原始图像" "s - 不使用" "f - 绝对梯度" "g - 相对梯度" "4 - 4连通域" "8 - 8连通域" << endl;}// 图像Mat image0, image, gray, mask;// 漫水填充的模式int ffillMode = 1;int loDiff = 20, upDiff = 20;// 连通域int connectivity = 4;// 是否是彩色图像int isColor = true;// 是否使用掩码图像bool useMask = false;// 掩码值int newMaskVal = 255;static void onMouse( int event, int x, int y, int, void* ){ // 如果不是左键按下事件,则不处理 if( event != EVENT_LBUTTONDOWN ) return; // 获取鼠标点击位置的坐标 Point seed = Point(x,y); int lo = ffillMode == 0 ? 0 : loDiff; int up = ffillMode == 0 ? 0 : upDiff; // 判断使用哪种方式进行漫水填充 int flags = connectivity + (newMaskVal << 8) + (ffillMode == 1 ? FLOODFILL_FIXED_RANGE : 0); // 随机取值&255,使得值在0-255之间 int b = (unsigned)theRNG() & 255; int g = (unsigned)theRNG() & 255; int r = (unsigned)theRNG() & 255; // 图像的颜色,彩色还是灰色 Scalar newVal = isColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114); // 彩色图像还是灰度图像 Mat dst = isColor ? image : gray; int area; Rect ccomp; // 是否使用掩码图像 if( useMask ) { threshold(mask, mask, 1, 128, THRESH_BINARY); // 进行漫水填充 area = floodFill(dst, mask, seed, newVal, &ccomp, Scalar(lo, lo, lo), Scalar(up, up, up), flags); // 显示掩码图像 imshow( "mask