漫水填充是一种用特定的颜色填充连通区域(替换自动选中和种子点相连的区域的颜色),通过设置可连通像素的上下限以及连通方式来达到不同的填充效果的方法。
作用:①用来标记或分离图像的一部分(以便对其进行进一步处理或分析);
②用来输入图像获取掩码区域(掩码会加速处理过程,或只处理掩码指定的像素点,操作的结果总是连续的区域);
注:带掩模mask的floodFill函数中的掩模mask用于进一步控制哪些区域将被填充颜色。
作用:用我们指定的颜色从种子点开始填充一个连接域。连通性由像素值的接近程度来衡量。
int floodFill(
InputOutputArray image, //输入/输出1通道或3通道,8位或浮点图像
InputOutputArray mask, //操作掩模
Point seedPoint, //漫水填充算法的起始点
Scalar newVal, //像素点被染色的值,即在重绘区域像素的新值
Rect* rect=0, //用于设置floodFill函数将要重绘区域的最小边界矩形区域
Scalar loDiff=Scalar(),
//表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之负差的最大值
Scalar upDiff=Scalar(),
//表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之正差的最大值
int flags=4 //操作标识符
);
综合示例:
//---------------【综合示例:漫水填充】----------------
#include
#include
#include
#include
using namespace cv;
using namespace std;
//-----------------------【全局变量声明部分】----------------------
// 描述:全局变量声明
//-----------------------------------------------------------------
Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage;//定义原始图、目标图、灰度图、掩模图
int g_nFillMode = 1;//漫水填充的模式
int g_nLowDifference = 20, g_nUpDifference = 20;//负差最大值、正差最大值
int g_nConnectivity = 4;//表示floodFill函数标识符低八位的连通值
int g_bIsColor = true;//是否为彩色图的标识符布尔值
int g_bUseMask = false;//是否显示掩模窗口的布尔值
int g_nNewMaskVal = 255;//新的重新绘制的像素值
//-----------------------【onMouse()函数】----------------------
// 描述:鼠标消息onMouse回调函数
//-----------------------------------------------------------------
static void onMouse(int event,int x,int y,int , void*) {
//若鼠标左键没有按下,便返回
if (event != EVENT_LBUTTONDOWN)
return;
//---------------【1】调用floodFill函数之前的参数准备部分----------------
Point seed = Point(x,y);//漫水填充算法的起始点
int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;//空范围的漫水填充此值为0,否则设为全局的g_nLowDifference
int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;//空范围的漫水填充此值为0,否则设为全局的g_nUpDifference
int flags = g_nConnectivity + (g_nNewMaskVal << 8) + (g_nFillMode == 1 ? FLOODFILL_FIXED_RANGE : 0);
//标识符的0`7位为g_nConnectivity(控制算法的连通性取4或8),8`15位(用于指定填充掩码图像的值)为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL+FIXED_RANGE或者0
//随机生成bgr值
int b = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
int g = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
int r = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
Rect ccomp;//定义重绘区域的最小边界矩形区域
Scalar newVal = g_bIsColor ? Scalar(b,g,r):Scalar(r*0.299+g*0.587+b*0.114);
//在重绘制区域像素的新值,若是彩色图模式,取Scalar(b,g,r);若是灰度图模式,取Scalar(r*0.299+g*0.587+b*0.114);
Mat dst = g_bIsColor ? g_dstImage : g_grayImage;//目标图的赋值
int area;
//---------------【2】调用floodFill函数----------------
if (g_bUseMask) {
threshold(g_maskImage, g_maskImage, 1, 128, THRESH_BINARY);;
area = floodFill(dst,g_maskImage,seed,newVal,&ccomp,Scalar(LowDifference,LowDifference,LowDifference),Scalar(UpDifference,UpDifference,UpDifference),flags);
imshow("mask",g_maskImage);
}
else
{
area = floodFill(dst,seed,newVal,&ccomp,Scalar(LowDifference,LowDifference,LowDifference),Scalar(UpDifference,UpDifference,UpDifference),flags);
}
imshow("效果图",dst);
cout << area << "各像素被重绘\n";
}
//-----------------------【main()函数】----------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------
int main() {
//载入原图
g_srcImage = imread("juan.jpg ",1);
if (!g_srcImage.data) { printf("读取图片错误\n"); return false; }
g_srcImage.copyTo(g_dstImage);//复制原图到目标图
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);//转换三通道的image到灰度图
g_maskImage.create(g_srcImage.rows+2,g_srcImage.cols+2,CV_8UC1);//利用image0的尺寸来初始化掩模mask
namedWindow("效果图",WINDOW_AUTOSIZE);
createTrackbar("负差最大值","效果图",&g_nLowDifference,255,0);
createTrackbar("正差最大值","效果图",&g_nUpDifference,255,0);
//鼠标回调函数
setMouseCallback("效果图",onMouse,0);
//循环轮询按键
while (1) {
//先显示效果图
imshow("效果图",g_bIsColor?g_dstImage:g_grayImage);
//获取键盘按键
int c = waitKey(0);
//判断ESC是否按下,若按下便退出
if ((c&255)==27) {
cout << "程序退出。。。。。。\n";
break;
}
//根据按键的不同,进行各种操作
switch ((char)c)
{
//如果键盘'1'被按下,效果图在灰度图,彩色图之间转换
case '1':
if (g_bIsColor)//若原来为彩色,转为灰度图,并且将掩模mask所有元素设置为0
{
cout << "按键 '1' 被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n";
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
g_maskImage = Scalar::all(0);//将mask所有元素设置为0
g_bIsColor = false;//将标识符置为false,表示当前图像不为彩色,而是灰度
}
else //若原来为灰度图,便将原来的彩色image0再次复制给image,并且将掩模mask所有元素设置为0
{
cout << "按键 '1' 被按下,切换彩色/灰度模式,当前操作为将【灰度模式】切换为【彩色模式】\n";
g_srcImage.copyTo(g_dstImage);
g_maskImage = Scalar::all(0);//将mask所有元素设置为0
g_bIsColor = true;//将标识符置为true,表示当前图像为彩色
}
break;
//如果键盘'2'被按下,显示/隐藏掩模窗口
case '2':
if (g_bUseMask)
{
destroyWindow("mask");
g_bUseMask = false;
}
else
{
namedWindow("mask",0);
g_maskImage = Scalar::all(0);
imshow("mask", g_maskImage);
g_bUseMask = true;
}
break;
//如果键盘'3'被按下,恢复原始图像
case '3':
cout << "按键 '3' 被按下,恢复原始图像\n";
g_srcImage.copyTo(g_dstImage);
cvtColor(g_dstImage, g_grayImage, COLOR_BGR2GRAY);
g_maskImage = Scalar::all(0);//将mask所有元素设置为0
break;
//如果按键 '4' 被按下,使用空范围的漫水填充
case '4':
cout << "按键 '4' 被按下,使用空范围的漫水填充\n";
g_nFillMode = 0;
break;
//如果按键 '5' 被按下,使用渐变、固定范围的漫水填充
case '5':
cout << "按键 '5' 被按下,使用渐变、固定范围的漫水填充\n";
g_nFillMode = 1;
break;
//如果按键 '6' 被按下,使用渐变、浮动范围的漫水填充
case '6':
cout << "按键 '6' 被按下,使用渐变、浮动范围的漫水填充\n";
g_nFillMode = 2;
break;
//如果按键 '7' 被按下,操作标识符的低八位使用4位的连接模式
case '7':
cout << "按键 '7' 被按下,操作标识符的低八位使用4位的连接模式\n";
g_nConnectivity = 4;
break;
//如果按键 '8' 被按下,操作标识符的低八位使用8位的连接模式
case '8':
cout << "按键 '8' 被按下,操作标识符的低八位使用8位的连接模式\n";
g_nConnectivity = 8;
break;
}
}
return 0;
}
结果如图: