研究抠图算法有段时间,颇有所得 研究的算法包括 Grabcut 、Shared Sample Alpha Matting、robust matting 以及lazysnapping. 研究抠图算法主要是应用于移动端的交互式抠图,用户输入 前景像素、背景像素的Trimap图作为mask ,对原图进行分割。交互式抠图算法达到可以商用的标准主要有两点:1、精确的分割出前景像素与背景像素,2、分割后的主体或前景部分,边缘过渡效果良好,在新的背景上,视觉上没有违和感。

从实现上的难易程度、对用户的操作要求、以及算法的复杂度上考虑主要采用并实现了 Grabcut、lazysnapping算法。Shared Sample Alpha Matting在交互上用户的操作成本高一点效果一般,没有具体去实现只是看过 imageshop 一篇博客中实现的效果,大家有兴趣可以去下载看一看 链接地址:点击打开链接.考虑算法实现主要用在移动端,用户的交互感受是蛮重要的。我采用了Grabcut以及参考了lazysnapping 的思路,做了两种交互抠图实现方案。

其中,Grabcut opencv提供了实现API ,我也是直接用的OpenCV的 实现接口,总体效果还可以接受,Grabcut缺点是 对于前景 与背景像素相差不大的部分不能有效的分割,它采用图论理论,前景像素与背景像素建立无向图,利用图割maxflow-mincut理论 分割 前景像素 与 背景像素,grabcut论文中也有提及边缘处理的方法,但是没有给出具体的实现,以及找不到相关的资料,也就不了了之。对于前景与背景差异性较大情况,grabcut表现还是不错的,其实微软的办公软件 World 、PPT中都有删除背景功能,它们采用的就是Gabcut方法,首先用矩形框框选出主体,执行算法得到初分割图,然后再用画笔标记 做区域针对性分割。该算法效果就不展示了,感兴趣的可以利用opencv 实现一下。需要注意的一点是 每次算法的输入的是原图srcImage跟mask 图。下面对基于OpenCV 的Grabcut的实现简要说一下,它包括两个接口:第一个是输入参数是原图和矩形框,第二个接口输入参数是原图和mask图。

1、cv::grabCut(srcImg, mask, retangle, bgModle, fgModle, 5, cv::GC_INIT_WITH_RECT);

srcImg:原图,ratangle:矩形框,bgModle: 与原图大小一致Mat类型,fgModle:与原图大小一致 Mat类型, 5:高斯混合建模迭代次数,cv::GC_INIT_WITH_RECT:标志位。

重点说下mask参数:接口输出参数mask,mask代表初步分割的结果,有四种值:cv::GC_FGD(前景像素)、cv::GC_BGD(背景像素)、cv::GC_PR_FGD(可能前景像素)、GC_PR_BGD(可能背景像素),根据mask图可以得到分割后的二值图:

cv::Mat4b resultMaskToMatrix(int w, int h){  cv::Mat cvMat(h, w, CV_8UC4);  cvMat.setTo(0);   unsigned char *data = mask.data;  unsigned char *data2 = cvMat.data;  foreCount = 0;  int fgd = 0, bgd = 0, pfgd = 0, pbgd = 0;   for (int y = 0; y < h; y++){   for (int x = 0; x < w; x++){    int index = w * y + x;    int offset = 4 * (w * y + x);    if (data[index] == cv::GC_FGD){     //cvMat.at<cv::Vec4b>(cv::Point(x, y)) == cv::Vec4b(0, 0, 0, 255);     data2[offset] = 255;     data2[offset + 1] = 255;     data2[offset + 2] = 255;     data2[offset + 3] = 255;     fgd++;    }    else if (data[index] == cv::GC_BGD){     //cvMat.at<cv::Vec4b>(cv::Point(x, y)) == cv::Vec4b(255, 255, 255, 255);     data2[offset] = 0;     data2[offset + 1] = 0;     data2