【opencv学习之三十九】背景分割和漫水填充

时间:2021-09-02 16:31:05

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);
}

效果:

【opencv学习之三十九】背景分割和漫水填充

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);
}

效果:

【opencv学习之三十九】背景分割和漫水填充

漫水填充法实例:

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);
}

效果:

【opencv学习之三十九】背景分割和漫水填充