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); }
效果: