OpenCV4学习笔记(56)——GrabCut图像分割算法

今天要整理记录的笔记是关于GrabCut图像分割的内容,GrabCut是基于图割(graph cut)实现的图像分割算法,它需要指定输入一个bounding box作为分割目标区域,根据目标区域中的像素,来实现对目标前景与背景的分割。注意GrabCut图像分割算法并不是把图像分成若干区域,而是注重于将我们需要的前景区域从整幅图像中提取出来,以达到前景、背景相互分割的目的。

而且由于GrabCut图像分割算法的分割速度比较快、效果比较好、同时支持交互操作,因此在很多图像分割或背景虚化的程序中都具有它的一席之地。

在维基百科中,关于GrabCut图像分割算法的介绍如下:

GrabCut是一种基于图形切割的图像分割方法。
该算法从要分段的对象周围的用户指定的边界框开始,使用高斯混合模型估计目标对象和背景的颜色分布。这用于在像素标签上构造Markov 随机字段,其能量函数更喜欢具有相同标签的连接区域,并运行基于图形剪切的优化以推断其值。由于此估计值可能比从边界框中获取的原始估计值更准确,因此重复这一两步过程,直到收敛。
用户可以通过指出错误分类的区域并重新运行优化来进一步更正估计值。该方法还会更正结果以保留边。
有几个开源实现可用,包括OpenCV(截至版本 2.1)。

通过维基百科的定义我们能了解到GrabCut图像分割算法的大致内容,那么对于GrabCut算法的主要步骤则可以总结为如下几步:
(1)在图片中选取(一个或者多个)包含目标的ROI矩形框,矩形框中即是我们所要提取出的目标对象;
(2)将图像中矩形框以外的区域都自动认为是属于背景区域,在掩膜mask中将背景区域的像素值置为0(GC_BGD);
(3)对于用户定义的ROI矩形区域,使用背景区域中的像素数据(如灰度值、颜色分布等等)来判断ROI区域中的前景区域和背景区域;
(4)使用高斯混合模型(GMM)来分别对背景和前景进行建模,并将未定义的像素标记为可能的前景或者可能的背景;
(5)图像中的每一个像素点都被看作通过虚拟边与周围像素点相连接,并且基于该像素点与周边像素点颜色上的相似性,来对其所具有的每一条虚拟边分别赋予一个属于前景或者背景的概率;
(6)通过对像素点的虚拟边具有的概率进行统计,从而判断该像素点属于哪一种区域,每一个像素点都会属于一个前景或背景;
(7)在每个像素点都完成连接后(可能是与背景或前景相连接),若像素点之间的虚拟边属于不同区域(即一条虚拟边连接着的两个像素点,其中一个像素点属于前景,另一个像素点属于背景),则会切断他们之间的虚拟边。对所有像素点进行类似操作后,就能将图像的前景和背景分割出来。

同时,可以通过迭代地运行GrabCut图像分割算法来提高图像分割的效果,当然了在提升效果的同时也会以牺牲时间为代价。

正如维基百科中所说,在OpenCV中已经封装好了实现GrabCut图像分割算法的APIgrabCut(),我们可以通过调用这个API来实现所需的功能。这个API的参数含义如下:
(1)参数img:输入的进行图像分割的8位三通道图像;
(2)参数mask:输入、输出的与参数img尺寸相同的8位单通道掩膜,输出的mask中像素点有4种不同的像素值,分别是:
0表示:GC_BGD 定义为明显的背景像素
1表示:GC_FGD 定义为明显的前景像素
2表示:GC_PR_BGD 定义为可能的背景像素
3表示:GC_PR_FGD 定义为可能的前景像素
(3)参数bgdModel:临时的背景高斯模型数组,Size(13x5,1),当处理同一张图像时不能对其进行修改;
(4)参数fgdModel:临时的前景高斯模型数组,Size(13x5,1),当处理同一张图像时不能对其进行修改;
(5)参数iterCount:GrapCut图像分割算法的迭代次数;
(6)参数mode:GrabCut图像分割算法的初始方式,有以下几种选择: GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut; GC_INIT_WITH_MASK( = 1),用掩码图像初始GrabCut;
GC_EVAL( = 2),执行分割,为默认值;

注意,当我们使用矩形框标记出目标所在区域时,应该设置参数mode=GC_INIT_WITH_RECT,当已经进行GrabCut算法分割输出前景的掩膜图像后,可以将掩膜图像再次作为GrabCut算法的输入mask来优化分割效果,此时的参数应该设置mode=GC_INIT_WITH_MASK

那么下面就来看一下代码演示:

	Mat image= imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\butterfly.jpg");
	//resize(image, image, Size(500, 700));
	imshow("image", image);

	Mat mask = Mat::zeros(image.size(), CV_8UC1);		
	Rect rect = selectROI("image", image, false);
	Mat bgdModel, fgdModel;		
	grabCut(image, mask, rect, bgdModel, fgdModel, 5, GC_INIT_WITH_RECT);

	Mat result = Mat::zeros(image.size(), CV_8UC3);
	for (int row = 0; row < result.rows; row++)
	{
		for (int col = 0; col < result.cols; col++)
		{
			//如果掩膜mask的某个位置上像素值为1或3,也就是明显前景和可能前景,就把原图像中该位置的像素值赋给结果图像
			if (mask.at<uchar>(row, col) == 1 || mask.at<uchar>(row, col) == 3)
			{
				result.at<Vec3b>(row, col) = image.at<Vec3b>(row, col);
			}
		}
	}
	imshow("result", result);

下面是用于演示的图像:
在这里插入图片描述
然后使用矩形框选取我们的目标区域:
在这里插入图片描述
随后使用GrabCut算法进行图像分割,实现目标前景与背景的分割:
在这里插入图片描述
这就是我们提取出来的蝴蝶的目标区域了。

下面换一张图像进行测试(图源网络):
在这里插入图片描述
这里用的是之前做简单证件照背景替换的图像,可以看到GrabCut图像分割算法同样能将人物的前景分割出来,也就是说也可以用来实现证件照的背景替换等功能,甚至鲁棒性还会更强一点。

好的那么本次笔记整理到此结束,下一次笔记就来整理一下如何使用GrabCut图像分割算法来实现图像背景替换和背景虚化的功能。

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

猜你喜欢

转载自blog.csdn.net/weixin_45224869/article/details/105848956