OpenCV学习笔记-图像分割之GrabCut

一、原理
GrabCut是graph cut的改进版,是迭代的graph cut。该算法利用了图像中的纹理(颜色)信息和边界(反差)信息,只要小量的用户交互操作即可得到比较好的分割效果。

计算原理参考: 点击打开链接, 点击打开链接
在整个过程发什么了什么呢?

1、用户输入一个矩形。矩形外的所有区域肯定是背景。矩形框内的东西是未知的。同样用户确定前景和背景的任何操作都不会被程序改变。
2、计算机会对我们的输入图像做一个初始化标记。它会标记前景和背景像素。

3、使用一个高斯混合模型(GMM)对前景和背景建模
4、根据我们的输入,GMM会学习并创建新的像素分布。对那些分类未知的像素(可能是前景也可能是背景),可以根据他们与已知分类(如背景)的像素关系来进行分类(就想在做聚类操作)。

5、这样就会根据像素的分布创建一幅图。图中的节点就是像素点。除了像素点做节点之外还有两个节点:Source_node和Sink_node。所有的前景像素都和Source_node相连。所有的背景像素都和sink_node相连。
6、将像素连接到Source_node/end_node的边的权重由他们属于同一类的概率决定。两个像素之间的权重由边的信息或者两个像素的相似性来决定。如果两个像素的颜色有很大的不同,那么它们之间的边的权重就会很小。

7、使用mincut算法对上面的图进行分割。它会根据最低成本方程将图像分为Source_node和Sink_node。成本方程就是被剪掉的所有边的权重之和。在裁剪之后,所有连接到Source_node的像素被认为是前景,所有连接到Sink_node的像素被认为是背景。
8、继续这个过程知道分类收敛。

OpenCV学习笔记-图像分割之GrabCut_第1张图片
演示

OpenCV提供了函数cv.grabCut()。
grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode=None)
img: 输入图像,必须是8位3通道图像,在处理过程中不会被修改

mask: 掩码图像,用来确定哪些区域是背景,前景,可能是背景,可能是前景等。
            GCD_BGD (=0), 背景; GCD_FGD (=1),前景;GCD_PR_BGD (=2),可能是背景;GCD_PR_FGD(=3),可能是前景。
            如果没有手工标记GCD_BGD 或者GCD_FGD,那么结果只会有 GCD_PR_BGDGCD_PR_FGD

rect: 包含前景的矩形,格式为(x, y, w, h)

bdgModel,fgdModel: 算法内部使用的数组,只需要创建两个大小为(1,65),数据类型为np.float64的数组

iterCount: 算法迭代的次数

mode: 用来指示grabCut函数进行什么操作:
            cv.GC_INIT_WITH_RECT (=0),用矩形窗初始化GrabCut;
            cv.GC_INIT_WITH_MASK (=1),用掩码图像初始化GrabCut。

具体代码:
img = cv.imread('img/meixi.jpg')
newmask = cv.imread('img/meixi_2.png', 0)

cv.imshow('meixi_1', newmask)
cv.imshow('image', img)
#转成rgb格式
b,g,r=cv.split(img)
img[:,:,0]=r
img[:,:,2]=b

mask = np.zeros(img.shape[:2], np.uint8)

bgdModel = np.zeros((1, 65), np.float64)
fgbModel = np.zeros((1, 65), np.float64)

rect = (80, 20, 230, 270)
cv.grabCut(img, mask, rect, bgdModel, fgbModel, 5, cv.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')
img = img * mask2[:,:,np.newaxis]
plt.imshow(img)
plt.show()
值得提一下,OpenCV读取的图像是BGR格式 ,而matplatlab支持的是RGB格式的图像,所以为了正常显示,必须转换一下。

OpenCV学习笔记-图像分割之GrabCut_第2张图片

手臂内的背景还没有消除,所以我们来修改一下掩模图像。打开PS,添加一个图层,用笔刷在需要的地方用白色绘制(如头发,鞋子,球);使用黑色笔刷在不需要的地方绘制。然后将其他地方用灰色填充,保存成新的掩码图像。在OpenCV中导入这个掩码图像,根据新的掩码图像对原来的掩码图像进行编辑。

newmask = cv.imread('img/meixi_2.png', 0)
cv.imshow('meixi_1', newmask)
mask[newmask == 0] = 0
mask[newmask == 255] = 1
mask, bgdModel, fgbModel = cv.grabCut(img, mask, None, bgdModel, fgbModel, 5, cv.GC_INIT_WITH_MASK)
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')
img = img * mask2[:,:,np.newaxis]
plt.imshow(img)
plt.show()

效果:

OpenCV学习笔记-图像分割之GrabCut_第3张图片

手臂内的背景没有了,不过头发和足球还是没显示出来,我也找不到原因,如果有人实现了,请帮忙告诉我怎么做。
这个是新增的掩码图像

OpenCV学习笔记-图像分割之GrabCut_第4张图片

打开

你可能感兴趣的:(#,OpenCV-Python,python,#,OpenCV学习笔记)