openCV中实现了背景分割算法——grabCut()和漫水填充算法——floodFill();
其中GrabCut算法是调用仅需要确认前景和背景输入,该算法就可以完成前景和背景的相对最优的分割;该算法利用了图像中的纹理信息和边界反差信息,来进行分割,和分水岭算法比较类似,但是速度挺慢的,结果好于分水岭;
floodFill漫水填充算法比较常见,图画中的填充色用的就是这个算法;原理也比较简单就是遍历封闭区域内的像素点并置换为指定色为止;也可以用来做阈值分割;
下面是grabCut()的实例;
1、分组函数的方法:
//背景分割算法1
/****************GrabCut mask方法*******************/
void MyShowImage(Mat Image,const string winName)
{
imshow( winName, Image );
}
void getBinMask( const Mat& comMask, Mat& binMask )
{
binMask.create( comMask.size(), CV_8UC1 );
binMask = comMask & 1;
}
void imgGrabCut()//背景分割算法1
{
Mat image = imread("D:/ImageTest/qq.jpg" );
const string winName = "image";
MyShowImage(image,winName);
/***********************************/
Mat bg;Mat fg;
Rect rect = Rect(47,48,408,464);
Mat mask,res;
mask.create( image.size(), CV_8UC1);
grabCut( image, mask, rect, bg, fg, 1, 0 );
Mat binMask;
getBinMask( mask, binMask );
image.copyTo( res, binMask );
MyShowImage(res,winName);
/***********************************/
cvWaitKey(0);
}
效果:
2、单函数方法:
void imgGrabCut2()//背景分割算法2
{
// 矩形外的像素是背景
Rect rectGrab=Rect(47,48,408,464);
// 打开另一幅图像
cv::Mat image= cv::imread("D:/ImageTest/qq.jpg");
Mat temp=image.clone();
rectangle( temp,rectGrab,Scalar(0, 0, 255), 2, 8);
imshow("src",temp);
Mat result;
Mat bgModel,fgModel; //临时变量,函数需要
grabCut(image, //输入图像
result, //分段结果
rectGrab, // 包含前景的矩形
bgModel,fgModel, // 前景、背景
1, // 迭代次数
cv::GC_INIT_WITH_RECT); // 用矩形
//比较函数保留值为GC_PR_FGD的像素
compare(result,//输入图像1
GC_PR_FGD,//输入图像2,或者像素级,或者具体像素
result,//输出图像
cv::CMP_EQ);//操作类型
// enum { CMP_EQ=0, //相等
// CMP_GT=1, //大于
// CMP_GE=2, //大于等于
// CMP_LT=3, //小于
// CMP_LE=4, //小于等于
// CMP_NE=5 }; //不相等
// enum GrabCutClasses {
// GC_BGD = 0, //!< an obvious background pixels
// GC_FGD = 1, //!< an obvious foreground (object) pixel
// GC_PR_BGD = 2, //!< a possible background pixel
// GC_PR_FGD = 3 //!< a possible foreground pixel
// };
// 产生输出图像
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
//背景值为 GC_BGD=0,作为掩码
image.copyTo(foreground,result);
imshow("result", foreground);
waitKey(0);
}
效果:
漫水填充法实例:
void imgFloodFill()//满水填充法
{
Mat src = imread("D:/ImageTest/222.JPG");
Rect rect;
imshow("src", src);
floodFill(src, //输入图像
Point(20,20), //拾取点
Scalar(255, 0, 0), //填充颜色
&rect, //重绘最小矩形,默认值是0;
Scalar(30, 30, 30), //负差,拾取点像素rgb与当前像素rgb做差,差值大于这个
Scalar(12, 12, 12)); //正差,当前像素rgb与拾取点像素rgb做差,差值小于这个
// int floodFill( InputOutputArray image,
// InputOutputArray mask,
// Point seedPoint,
// Scalar newVal,
// CV_OUT Rect* rect=0,
// Scalar loDiff = Scalar(),
// Scalar upDiff = Scalar(),
// int flags = 4 );
// 函数 cvFloodFill 用指定颜色,从种子点开始填充一个连通域。连通性由象素值的接近程度来衡量。
// 在点 (x, y) 的象素被认为是属于重新绘制的区域,如果:
// src(x',y')-lo_diff<=src(x,y)<=src(x',y')+up_diff, 灰度图像,浮动范围
// src(seed.x,seed.y)-lo<=src(x,y)<=src(seed.x,seed.y)+up_diff, 灰度图像,固定范围
// src(x',y')r-lo_diffr<=src(x,y)r<=src(x',y')r+up_diffr 和
// src(x',y')g-lo_diffg<=src(x,y)g<=src(x',y')g+up_diffg 和
// src(x',y')b-lo_diffb<=src(x,y)b<=src(x',y')b+up_diffb, 彩色图像,浮动范围
// src(seed.x,seed.y)r-lo_diffr<=src(x,y)r<=src(seed.x,seed.y)r+up_diffr 和
// src(seed.x,seed.y)g-lo_diffg<=src(x,y)g<=src(seed.x,seed.y)g+up_diffg 和
// src(seed.x,seed.y)b-lo_diffb<=src(x,y)b<=src(seed.x,seed.y)b+up_diffb, 彩色图像,固定范围
// 其中 src(x',y') 是象素邻域点的值。也就是说,为了被加入到连通域中,一个象素的彩色/亮度应该足够接近于:
// 它的邻域象素的彩色/亮度值,当该邻域点已经被认为属于浮动范围情况下的连通域。
// 固定范围情况下的种子点的彩色/亮度值
imshow("result", src);
waitKey(0);
}
效果: