赞
踩
接着,来看一个关于Floodfill的简单的调用范例:
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include
#include
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
Mat src = imread("1.jpg");
imshow("【原始图】",src);
Rect ccomp;
floodFill(src, Point(50,300), Scalar(155, 255,55), &ccomp, Scalar(20, 20, 20),Scalar(20, 20, 20));
imshow("【效果图】",src);
waitKey(0);
return 0;
}
运行截图,原始图:
效果图:
五、floodFill函数在OpenCV中的实现源代码
这个部分贴出OpenCV中本文相关函数的源码实现细节,来给想了解实现细节的小伙伴们参考。浅墨暂时不在源码的细节上挖深作详细注释。
5.1 OpenCV2.X中两个版本的floodFill函数源码
第一个,不带mask版本的floodFill:
int cv::floodFill( InputOutputArray _image,Point seedPoint,
Scalar newVal, Rect* rect,
Scalar loDiff, ScalarupDiff, int flags )
{
CvConnectedComp ccomp;
CvMat c_image = _image.getMat();
cvFloodFill(&c_image, seedPoint, newVal, loDiff, upDiff, &ccomp,flags, 0);
if( rect )
*rect = ccomp.rect;
return cvRound(ccomp.area);
}
第二个,带mask版本的floodFill:
int cv::floodFill( InputOutputArray _image,InputOutputArray _mask,
Point seedPoint, ScalarnewVal, Rect* rect,
Scalar loDiff, ScalarupDiff, int flags )
{
CvConnectedComp ccomp;
CvMat c_image = _image.getMat(), c_mask = _mask.getMat();
cvFloodFill(&c_image, seedPoint, newVal,loDiff, upDiff, &ccomp, flags, c_mask.data.ptr ? &c_mask : 0);
if( rect )
*rect = ccomp.rect;
return cvRound(ccomp.area);
}
我们依然可以发现,其内部实现是基于OpenCV 1.X旧版的cvFloodFill函数,我们再来看看其旧版函数的源码。
5.2 OpenCV2.X中cvFloodFill()函数的实现源码
CV_IMPL void
cvFloodFill( CvArr* arr, CvPointseed_point,
CvScalar newVal, CvScalar lo_diff, CvScalar up_diff,
CvConnectedComp* comp, int flags, CvArr* maskarr )
{
cv::Ptr tempMask;
std::vector buffer;
if( comp )
memset( comp, 0, sizeof(*comp) );
int i, type, depth, cn, is_simple;
int buffer_size, connectivity = flags & 255;
union {
uchar b[4];
int i[4];
float f[4];
double _[4];
}nv_buf;
nv_buf._[0] = nv_buf._[1] = nv_buf._[2] = nv_buf._[3] = 0;
struct { cv::Vec3b b; cv::Vec3i i; cv::Vec3f f; } ld_buf, ud_buf;
CvMat stub, *img = cvGetMat(arr, &stub);
CvMat maskstub, *mask = (CvMat*)maskarr;
CvSize size;
type = CV_MAT_TYPE( img->type );
depth = CV_MAT_DEPTH(type);
cn = CV_MAT_CN(type);
if( connectivity == 0 )
connectivity = 4;
else if( connectivity != 4 && connectivity != 8 )
CV_Error( CV_StsBadFlag, "Connectivity must be 4, 0(=4) or 8");
is_simple = mask == 0 && (flags & CV_FLOODFILL_MASK_ONLY) ==0;
for( i = 0; i < cn; i++ )
{
if( lo_diff.val[i] < 0 || up_diff.val[i] < 0 )
CV_Error( CV_StsBadArg, "lo_diff and up_diff must benon-negative" );
is_simple &= fabs(lo_diff.val[i]) < DBL_EPSILON &&fabs(up_diff.val[i]) < DBL_EPSILON;
}
size = cvGetMatSize( img );
if( (unsigned)seed_point.x >= (unsigned)size.width ||
(unsigned)seed_point.y >=(unsigned)size.height )
CV_Error( CV_StsOutOfRange, "Seed point is outside of image");
cvScalarToRawData( &newVal, &nv_buf, type, 0 );
buffer_size = MAX( size.width, size.height ) * 2;
buffer.resize( buffer_size );
if( is_simple )
{
int elem_size = CV_ELEM_SIZE(type);
const uchar* seed_ptr = img->data.ptr + img->step*seed_point.y +elem_size*seed_point.x;
for(i = 0; i < elem_size; i++)
if (seed_ptr[i] != nv_buf.b[i])
break;
if (i != elem_size)
{
if( type == CV_8UC1 )
icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,nv_buf.b[0],
comp, flags, &buffer);
else if( type == CV_8UC3 )
icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,cv::Vec3b(nv_buf.b),
comp, flags,&buffer);
else if( type == CV_32SC1 )
icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,nv_buf.i[0],
comp, flags,&buffer);
else if( type == CV_32FC1 )
icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,nv_buf.f[0],
comp, flags,&buffer);
else if( type == CV_32SC3 )
icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,cv::Vec3i(nv_buf.i),
comp, flags, &buffer);
else if( type == CV_32FC3 )
icvFloodFill_CnIR(img->data.ptr, img->step, size, seed_point,cv::Vec3f(nv_buf.f),
comp, flags,&buffer);
else
CV_Error(CV_StsUnsupportedFormat, "" );
return;
}
}
if( !mask )
{
/* created mask will be 8-byte aligned */
tempMask = cvCreateMat( size.height + 2, (size.width + 9) & -8,CV_8UC1 );
mask = tempMask;
}
else
{
mask = cvGetMat( mask, &maskstub );
if( !CV_IS_MASK_ARR( mask ))
CV_Error( CV_StsBadMask, "" );
if( mask->width != size.width + 2 || mask->height != size.height +2 )
CV_Error( CV_StsUnmatchedSizes, "mask must be 2 pixel wider "
"and 2pixel taller than filled image" );
}
int width = tempMask ? mask->step : size.width + 2;
uchar* mask_row = mask->data.ptr + mask->step;
memset(mask_row - mask->step, 1, width );
for( i = 1; i <= size.height; i++, mask_row += mask->step )
{
if( tempMask )
memset( mask_row, 0, width );
mask_row[0] = mask_row[size.width+1] = (uchar)1;
}
memset( mask_row, 1, width );
if( depth == CV_8U )
for( i = 0; i < cn; i++ )
{
int t = cvFloor(lo_diff.val[i]);
ld_buf.b[i] = CV_CAST_8U(t);
t = cvFloor(up_diff.val[i]);
ud_buf.b[i] = CV_CAST_8U(t);
}
else if( depth == CV_32S )
for( i = 0; i < cn; i++ )
{
int t = cvFloor(lo_diff.val[i]);
ld_buf.i[i] = t;
t = cvFloor(up_diff.val[i]);
ud_buf.i[i] = t;
}
else if( depth == CV_32F )
for( i = 0; i < cn; i++ )
{
ld_buf.f[i] = (float)lo_diff.val[i];
ud_buf.f[i] = (float)up_diff.val[i];
}
else
CV_Error( CV_StsUnsupportedFormat, "" );
if( type == CV_8UC1 )
icvFloodFillGrad_CnIR(
img->data.ptr,img->step, mask->data.ptr, mask->step,
size, seed_point,nv_buf.b[0],
Diff8uC1(ld_buf.b[0],ud_buf.b[0]),
comp, flags,&buffer);
else if( type == CV_8UC3 )
icvFloodFillGrad_CnIR<:vec3b cv::vec3i diff8uc3>(
img->data.ptr,img->step, mask->data.ptr, mask->step,
size, seed_point,cv::Vec3b(nv_buf.b),
Diff8uC3(ld_buf.b, ud_buf.b),
comp, flags,&buffer);
else if( type == CV_32SC1 )
icvFloodFillGrad_CnIR(
img->data.ptr,img->step, mask->data.ptr, mask->step,
size, seed_point,nv_buf.i[0],
Diff32sC1(ld_buf.i[0], ud_buf.i[0]),
comp, flags, &buffer);
else if( type == CV_32SC3 )
icvFloodFillGrad_CnIR<:vec3i cv::vec3i diff32sc3>(
img->data.ptr,img->step, mask->data.ptr, mask->step,
size, seed_point,cv::Vec3i(nv_buf.i),
Diff32sC3(ld_buf.i, ud_buf.i),
comp, flags,&buffer);
else if( type == CV_32FC1 )
icvFloodFillGrad_CnIR(
img->data.ptr,img->step, mask->data.ptr, mask->step,
size, seed_point,nv_buf.f[0],
Diff32fC1(ld_buf.f[0], ud_buf.f[0]),
comp, flags,&buffer);
else if( type == CV_32FC3 )
icvFloodFillGrad_CnIR<:vec3f cv::vec3f diff32fc3>(
img->data.ptr,img->step, mask->data.ptr, mask->step,
size, seed_point,cv::Vec3f(nv_buf.f),
Diff32fC3(ld_buf.f,ud_buf.f),
comp, flags,&buffer);
else
CV_Error(CV_StsUnsupportedFormat, "");
}
四、关于SetMouseCallback函数
因为下面示例程序中有用到SetMouseCallback函数,我们在这里讲一讲。SetMouseCallback函数为指定的窗口设置鼠标回调函数。
C++: void setMouseCallback(conststring& winname, MouseCallback onMouse, void* userdata=0 )
第一个参数,const string&类型的winname,为窗口的名字。第二个参数,MouseCallback类型的onMouse,指定窗口里每次鼠标时间发生的时候,被调用的函数指针。这个函数的原型应该为voidFoo(int event, int x, int y, int flags, void* param);其中event是 CV_EVENT_*变量之一, x和y是鼠标指针在图像坐标系的坐标(不是窗口坐标系), flags是CV_EVENT_FLAG的组合, param是用户定义的传递到cvSetMouseCallback函数调用的参数。第三个参数,void*类型的userdata,用户定义的传递到回调函数的参数,有默认值0。
五、综合示例部分
本次的综合示例为OpenCV文档中自带的一个程序。浅墨将其做了适当的修改并详细注释,放出来供大家消化理解。
操作说明如下:
可以看到,此程序着不少的按键功能。而我们拿着鼠标对窗口中的图形一顿狂点,就可以得到类似PhotoShop中魔棒的效果,当然,就这短短的两百来行代码写出来的东西,体验是比不上PS的魔棒工具的。
废话不多说,程序详细注释的源码如下:
//-----------------------------------【程序说明】----------------------------------------------
//程序名称::《【OpenCV入门教程之十五】水漫金山:OpenCV漫水填充算法(Floodfill)》 博文配套源码
//开发所用IDE版本:Visual Studio 2010
// 开发所用OpenCV版本:2.4.9
//2014年6月3日 Created by 浅墨
//浅墨的微博:@浅墨_毛星云 http://weibo.com/1723155442/profile?topnav=1&wvr=5&user=1
//浅墨的知乎:http://www.zhihu.com/people/mao-xing-yun
//浅墨的豆瓣:http://www.douban.com/people/53426472/
//----------------------------------------------------------------------------------------------
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#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;//是否为彩色图的标识符布尔值
bool g_bUseMask = false;//是否显示掩膜窗口的布尔值
int g_nNewMaskVal = 255;//新的重新绘制的像素值
//-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
//输出一些帮助信息
printf("
欢迎来到漫水填充示例程序~
");
printf( "
按键操作说明:
"
"鼠标点击图中区域- 进行漫水填充操作
"
"键盘按键【ESC】- 退出程序
"
"键盘按键【1】- 切换彩色图/灰度图模式
"
"键盘按键【2】- 显示/隐藏掩膜窗口
"
"键盘按键【3】- 恢复原始图像
"
"键盘按键【4】- 使用空范围的漫水填充
"
"键盘按键【5】- 使用渐变、固定范围的漫水填充
"
"键盘按键【6】- 使用渐变、浮动范围的漫水填充
"
"键盘按键【7】- 操作标志符的低八位使用4位的连接模式
"
"键盘按键【8】- 操作标志符的低八位使用8位的连接模式
"
"
by浅墨
"
);
}
//-----------------------------------【onMouse( )函数】--------------------------------------
// 描述:鼠标消息onMouse回调函数
//---------------------------------------------------------------------------------------------
static void onMouse( int event, int x, int y, int, void* )
{
// 若鼠标左键没有按下,便返回
if( event != CV_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 ? CV_FLOODFILL_FIXED_RANGE : 0);//标识符的0~7位为g_nConnectivity,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, CV_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 << " 个像素被重绘
";
}
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
//改变console字体颜色
system("color 2F");
//载入原图
g_srcImage = imread("1.jpg", 1);
if( !g_srcImage.data ) { printf("Oh,no,读取图片image0错误~!
"); return false; }
//显示帮助文字
ShowHelpText();
g_srcImage.copyTo(g_dstImage);//拷贝源图到目标图
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);//转换三通道的image0到灰度图
g_maskImage.create(g_srcImage.rows+2, g_srcImage.cols+2, CV_8UC1);//利用image0的尺寸来初始化掩膜mask
namedWindow( "效果图",CV_WINDOW_AUTOSIZE );
//创建Trackbar
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 << "程序退出...........
";
break;
}
//根据按键的不同,进行各种操作
switch( (char)c )
{
//如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
case '1':
if( g_bIsColor )//若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
{
cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】
";
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”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】
";
g_srcImage.copyTo(g_dstImage);
g_maskImage = Scalar::all(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”被按下,恢复原始图像
";
g_srcImage.copyTo(g_dstImage);
cvtColor(g_dstImage, g_grayImage, COLOR_BGR2GRAY);
g_maskImage = Scalar::all(0);
break;
//如果键盘按键“4”被按下,使用空范围的漫水填充
case '4':
cout << "按键“4”被按下,使用空范围的漫水填充
";
g_nFillMode = 0;
break;
//如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
case '5':
cout << "按键“5”被按下,使用渐变、固定范围的漫水填充
";
g_nFillMode = 1;
break;
//如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
case '6':
cout << "按键“6”被按下,使用渐变、浮动范围的漫水填充
";
g_nFillMode = 2;
break;
//如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
case '7':
cout << "按键“7”被按下,操作标志符的低八位使用4位的连接模式
";
g_nConnectivity = 4;
break;
//如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
case '8':
cout << "按键“8”被按下,操作标志符的低八位使用8位的连接模式
";
g_nConnectivity = 8;
break;
}
}
return 0;
}
一些运行截图,首先是运行后的原始图:
点鼠标啊点鼠标:
滑滚动条啊滑滚动条:
地球已经阻止不了我们的鼠标了,点出来的图,已经有点恐怖。。。。。看妹子的手。。。。
如果鼠标点到妹子脸上的话。。。。呃,更多惊悚的图,浅墨不放出了,免得。。。。。
随着我们鼠标的点击,程序会记下我们的操作:
接着看一张灰度图模式的漫水填充效果和掩码图:
再来一张彩色窗户:
程序功能还是很多的,有鼠标操作,键盘8个按键的操作,还可以调滚动条:
好了,更多的功能我们就不在这里示范了,大家下载了程序自己回去玩吧。
本篇文章的配套源代码请点击这里下载:
【浅墨OpenCV入门教程之十五】配套源代码下载
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。