目标
• GrabCut 算法原理,使用 GrabCut 算法提取图像的前景
• 创建一个交互是程序完成前景提取
GrabCut 算法是由微软剑桥研究院的 Carsten_Rother, Vladimir_Kolmogorov和 Andrew_Blake 在文章《 GrabCut”: interactive foreground extraction using iterated graph cuts》中共同提出的。此算法在提取前景的操作过程中需要很少的人机交互,结果非常好。
从用户的角度来看它到底是如何工作的呢?开始时用户需要用一个矩形将前景区域框住(前景区域应该完全被包括在矩形框内部),然后算法进行迭代式分割直达达到最好结果。但是有时分割的结果不够理想,比如把前景当成了背景,或者把背景当成了前景。在这种情况下,就需要用户来进行修改了。用户
只需要在不理想的部位画一笔(点一下鼠标)就可以了。画一笔就等于在告诉计算机:“嗨,老兄,你把这里弄反了,下次迭代的时候记得改过来呀!”。然后,在下一轮迭代的时候你就会得到一个更好的结果了。
如下图所示。运动员和足球被蓝色矩形包围在一起。其中有我做的几个修正,白色画笔表明这里是前景,黑色画笔表明这里是背景。最后我得到了一个很好的结果。
现在我们进入 OpenCV 中的 grabcut 算法。 OpenCV 提供了函数:cv2.grabCut()。我们来先看看它的参数:
• img - 输入图像
• mask-掩模图像,用来确定那些区域是背景,前景,可能是前景/背景等。可以设置为:cv2.GC_BGD,cv2.GC_FGD,cv2.GC_PR_BGD,cv2.GC_PR_FGD,或者直接输入 0,1,2,3 也行。
• rect - 包含前景的矩形,格式为 (x,y,w,h)
• bdgModel, fgdModel - 算法内部使用的数组. 你只需要创建两个大小为 (1,65),数据类型为 np.float64 的数组。
• iterCount - 算法的迭代次数
• mode 可以设置为 cv2.GC_INIT_WITH_RECT 或 cv2.GC_INIT_WITH_MASK,也可以联合使用。这是用来确定我们进行修改的方式,矩形模式或者掩模模式
首先,我们来看使用矩形模式。加载图片,创建掩模图像,构建 bdgModel和 fgdModel.传入矩形参数。都是这么直接。让算法迭代 5 次。由于我们在使用矩形模式所以修改模式设置为 cv2.GC_INIT_WITH_RECT。运行grabcut。算法会修改掩模图像,在新的掩模图像中,所有的像素被分为四类:背景,前景,可能是背景/前景使用 4 个不同的标签标记(前面参数中提到过)。然后我们来修改掩模图像,所有的 0 像素和 1 像素都被归为 0(例如背景),所有的 1 像素和 3 像素都被归为 1(前景)。我们最终的掩模图像就这样准备好了。用它和输入图像相乘就得到了分割好的图像。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('image/meixi.png')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,450,290)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask ==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
plt.imshow(img),plt.colorbar(),plt.show()
结果图:
哎呀,梅西的头发被我们弄没了!让我们来帮他找回头发。所以我们要在那里画一笔(设置像素为 1,肯定是前景)。同时还有一些我们并不需要的草地。我们需要去除它们,我们再在这个区域画一笔(设置像素为 0,肯定是背景)。现在可以象前面提到的那样来修改掩模图像了。
实际上我是怎么做的呢?我们使用图像编辑软件打开输入图像,添加一个图层,使用笔刷工具在需要的地方使用白色绘制(比如头发,鞋子,球等);使用黑色笔刷在不需要的地方绘制(比如, logo,草地等)。然后将其他地方用灰色填充,保存成新的掩码图像。在 OpenCV 中导入这个掩模图像,根据新的
掩码图像对原来的掩模图像进行编辑。代码如下:
newmask = cv2.imread('newmask.jpg',0)
mask[newmask == 0] = 0
mask[newmask == 255] = 1
mask,bgdModel,fgdModel = cv2.grabCut(img, mask, None, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK)
mask = np.where((mask ==2)|(mask ==0),0,1).astype('uint8')
结果图:
参考:Opencv官方教程中文版(For Python)