OpenCV学习(20) grabcut分割算法

15 篇文章 0 订阅

http://www.cnblogs.com/mikewolf2002/p/3330390.html

 在OpenCV中,实现了grabcut分割算法,该算法可以方便的分割出前景图像,操作简单,而且分割的效果很好。算法的原理参见papaer:“GrabCut” — Interactive Foreground Extraction using Iterated Graph Cuts

比如下面的一副图,我们只要选定一个四边形框,把框中的图像作为grabcut的一个输入参数,表示该框中的像素可能属于前景,但框外的部分一定属于背景

imageimage

然后调用grabcut函数,就可以分割出城堡来。具体代码如下:

// 打开另一幅图像
cv::Mat image= cv::imread("../tower.jpg");
if (!image.data)
    {
    cout<<"不能打开图像!"<<endl;
    return 0; 
    }

// 矩形外的像素是背景 
cv::Rect rectangle(50,70,image.cols-150,image.rows-180);

cv::Mat result;
//两个临时矩阵变量,作为算法的中间变量使用,不用care
cv::Mat bgModel,fgModel; 
double tt = cv::getTickCount();
// GrabCut 分段
cv::grabCut(image,    //输入图像
    result,   //分段结果
    rectangle,// 包含前景的矩形 
    bgModel,fgModel, // 前景、背景
    1,        // 迭代次数
    cv::GC_INIT_WITH_RECT); // 用矩形
tt = cv::getTickCount() - tt;
printf("算法执行执行时间:%g ms\n", tt/cv::getTickFrequency()*1000);
// 得到可能是前景的像素
//比较函数保留值为GC_PR_FGD的像素
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
// 产生输出图像
cv::Mat foreground(image.size(),CV_8UC3,cv::Scalar(255,255,255));
//背景值为 GC_BGD=0,作为掩码
image.copyTo(foreground,result); 

grabCut函数的第一个参数为我们要处理的图像,本程序中就是image,图像的类型必须为:CV_8UC3

第二个参数是mask图像,它的大小和image一样,但是它的格式为CV_8UC1,只能是单通道的,grabcut算法的结果就保存在该图像中。

前面的代码中,我们并没有对mask图像(result)进行初始化设置,因为第6个参数为cv::GC_INIT_WITH_RECT,它表示算法会根据rectangle的范围,来生成一个初始化的mask图像。

cv::grabCut(image,    //输入图像 
    result,   //分段结果 
    rectangle, // 包含前景的矩形 
    bgModel,fgModel, // 前景、背景 
    1,        // 迭代次数 
    cv::GC_INIT_WITH_RECT); // 用矩形
 

mask图像的值只能为下面下面4个值(PR,probably表示可能的):

GC_BGD    = 0,  //背景

GC_FGD    = 1,  //前景 
GC_PR_BGD = 2,  //可能背景

GC_PR_FGD = 3   //可能前景 

根据rectangle生成的mask图像规则为:四边形外面的部分一定是背景,所以在mask图中对应的像素值为GC_BGD,而四边形内部的的值可能为前景,所以对应的像素值为GC_PR_FGD。所以我们程序中使用mask图像应该如下图所示。


image

如果第7个参数为GC_INIT_WITH_MASK,这时第三个参数rectangle没有使用,我们必须在调用grabcut函数之前,手工设置mask图像(变量result),如果我们把result设置成上图所示的灰度图。那个调用函数

cv::grabCut(image1,    //输入图像 
    result1,   //分段结果 
    rectangle, // 包含前景的矩形 
    bgModel,fgModel, // 前景、背景 
    1,        // 迭代次数 
    cv::GC_INIT_WITH_MASK); // 用矩形

可以得到同样的结果。 
可以参考下面的代码:

cv::Mat result1= cv::Mat(image1.rows, image1.cols,CV_8UC1, cv::Scalar(cv::GC_BGD));
//注意给子矩阵赋值的方法
cv::Mat roi(result1, cv::Rect(50,70,result1.cols-150,result.rows-180));
roi = cv::Scalar(cv::GC_PR_FGD);
tt = cv::getTickCount();
// GrabCut 分段
cv::grabCut(image1,    //输入图像
    result1,   //分段结果
    rectangle,// 包含前景的矩形 
    bgModel,fgModel, // 前景、背景
    1,        // 迭代次数
    cv::GC_INIT_WITH_MASK); // 用矩形
tt = cv::getTickCount() - tt;
printf("算法执行执行时间:%g ms\n", tt/cv::getTickFrequency()*1000);

// 得到可能是前景的像素
//比较函数保留值为GC_PR_FGD的像素
cv::compare(result1,cv::GC_PR_FGD,result,cv::CMP_EQ);
// 产生输出图像
cv::Mat foreground1(image1.size(),CV_8UC3,cv::Scalar(255,255,255));
//背景值为 GC_BGD=0,作为掩码
image.copyTo(foreground1,result1); 

第3个参数是rectangle的大小位置,如果第7个参数为GC_INIT_WITH_MASK,则该参数没有作用。

第4,5个参数是两个算法在执行过程中使用临时矩阵变量,不用care它们的内容。

第6个参数是迭代次数,迭代越多,效果越好,但划时间也越长。

第7个参数是操作模式,通常情况下为GC_INIT_WITH_RECT和GC_INIT_WITH_MASK。

从上面的图中,我们可以看到,grabcut算法的效果很好,但是花的时间也很长,上面图像在我的笔记本上需要4.4秒。

image

程序源代码:工程FirstOpenCV13

分类:  OpenCV

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值