看的一篇博客,原文链接: http://blog.sina.com.cn/s/blog_1584387c90102x5fu.html
图像分割一直是图像处理中一项棘手的问题。图像分割算法从大的方面讲可以分为两类:
1 全自动图像分割:一般采用聚类算法来最大化前景与背景的差。
2 用户互动式图像分割:用户提供前景和背景的种子,然后对前景背景建立概率分布模型。
而GraphCut和GrabCut就是属于第二类图像分割算法。首先来说GraphCut。
GraphCut:
将图像中的每一个像素看成Graph中的一个Node,然后在Graph中增加两个Node,分别为F和B,F代表Foreground前景,而B代表Background背景,具体图如下:
然后每两个相邻像素点用一条边连起来,每一个像素点和F点用一条边连起来,每一个像素点和B点也用一条边连起来。然后一个切割就是将图像分成两部分,第一部分的像素点和F相连为前景,第二部分的像素点和B相连为背景。这样一个切割是通过优化以下的能量函数来实现的:
其中g(X)为Data项,由像素v属于前景和属于背景的概率决定。而h(v,u)为Smoothness项,由像素v和其邻接像素u的相似程度决定。最简单的前景背景概率计算就是基于图像的直方图,而衡量两个相邻像素点相似度的方法最简单的则用其灰度值的差来表示。通过上式可以看到,能量的最优化其实是将最有可能是前景的列为一组,最有可能是背景的列为一组,然后将灰度差最大的相邻像素切割开来,这样的分割为全局最优分割。
下图为一个具体例子,其中红线和绿线为用户提供的背景和前景的种子。
GrabCut:
GraphCut需要用户提供精确的前景背景的种子,而且当提供的种子无法覆盖所有分布时,必然会影响分割的准确度。为了解决这个问题,微软研究室提出了更加快捷高效的GrabCut分割算法。
GrabCut需要用户提供一个长方形,长方形包含前景,而长方形外是背景。GrabCut具体步骤如下:
1: 长方形外的Pixels作为背景Pixel,长方形内的Pixels作为前景Pixel,用着两组去Train背景GMM和前景 GMM(这里GMM指高斯混合模型)。
2: 用训练好的两个GMM来计算每一个像素属于背景和属于前景的概率,进而计算出能量函数E中的Data
项,能量函数中的Smoothness项的计算方法大致与GraphCut相同。
3:通过最优化能量函数得到图像的一个分割。
4:用3中的分割结果中的前景Pixels和背景Pixels去训练前景GMM和背景GMM.
5:重复2,3,4,直到分割结果收敛(不再有大的变化)。
由以上步骤可以看出,GrabCut是一个循环执行的算法,其循环的目的是为了EM(Expectation Maximization)。因为用户提供的长方形内也有部分背景像素,所以这样的种子是不完全正确的。好在GMM模型并不要求所有的训练数据正确,即使有一部分分类不正确,也可以通过EM步骤使得最终结果正确。而GrabCut正是利用了GMM的这一特性。值得注意的是,GMM有陷入局部最优的问题无法解决,所以GrabCut也有此问题。下面用图说明GrabCut分割结果,相比其他的分割算法,GrabCut的效果还是很不错的。
以下是GrabCut的OpenCV代码,有兴趣的可以试着运行一下:
#include "highgui.h"
#include "cv.h"
#include "features2d/features2d.hpp"
#include "opencv/cxcore.hpp"
#include "cvaux.h"
using namespace std;
using namespace cv;
void main()
{
cv::Mat bgModel, fgModel, mask;
//按照需要修改以下的长方形位置
cv::Rect rect;
rect.x = 20;
rect.y = 30;
rect.width = 100;
rect.height = 200;
//读入图像
Mat Img= imread(“C://1.jpg”);
//循环执行3次,这个可以自己设置
cv::grabCut(Img, mask, rect, bgModel, fgModel, 3, cv::GC_INIT_WITH_RECT);
cv::grabCut(Img, mask, rect, bgModel, fgModel, 3, cv::GC_INIT_WITH_RECT);
cv::compare(mask, cv::GC_PR_FGD, mask, cv::CMP_EQ);
imshow("mask", mask);
cvWaitKey(0);
}