在这一章当中
GrabCut
算法来提取图像中的前景GrabCut 算法由英国剑桥微软研究院的 Carsten Rother、Vladimir Kolmogorov 和 Andrew Blake 设计。在他们的论文“GrabCut”中:使用迭代图切割的交互式前景提取。需要一种算法以最少的用户交互进行前景提取,结果是 GrabCut。
从用户的角度来看它是如何工作的?最初用户在前景区域周围绘制一个矩形(前景区域应该完全在矩形内)。然后算法对其进行迭代分割以获得最佳结果。完毕。但是在某些情况下,分割不会很好。例如,它可能将某些前景区域标记为背景,反之亦然。在这种情况下,用户需要进行精细的修饰。只需在存在一些错误结果的图像上进行一些描边即可。Strokes 基本上是说 “嘿,这个区域应该是前景,你把它标记为背景,在下一次迭代中纠正它” 或者它的相反背景。然后在下一次迭代中,将获得更好的结果。
见下图。第一位球员和足球被封闭在一个蓝色矩形中。然后用 白色笔触(表示前景)和黑色笔触(表示背景) 进行一些最终修饰,最终得到了一个不错的结果。
这背后会发生什么?
mincut
算法对图进行分割。它以最小的代价函数将图切割成两个分离的源节点和汇节点。成本函数是被切割的边的所有权重的总和。剪切后,所有连接到源节点的像素成为前景,连接到接收节点的像素成为背景。现在使用 OpenCV 实现grabcut算法。OpenCV 有函数cv2.grabCut()
。我们将首先看到它的参数:
mask, bgdModel, fgdModel = cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode] )
- 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
或组合,这决定了是绘制矩形还是最终的修饰笔触。
首先,看看矩形模式( rectangular mode)。加载图像,然后创建一个类似的蒙版图像。创建fgdModel
和bgdModel
。并给出矩形参数。这一切都是直截了当的。让算法迭代运行 5 次。模式应该是cv2.GC_INIT_WITH_RECT
,这是因为使用的是矩形。然后运行grabcut
。它修改蒙版图像。在新的蒙版图像中,像素将被标记为四个标志,表示上面指定的背景/前景。所以修改掩码,使得所有 0 像素和 2 像素都置为 0(即背景),所有 1 像素和 3 像素均置为 1(即前景像素)。现在最终的mask准备好了。只需将其与输入图像相乘即可得到分割图像。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('messi2.jpg')
mask = np.zeros(img.shape[:2], np.uint8)
cv2.imwrite('dd.jpg', mask)
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 像素的修饰(当然是背景)。因此,正如现在所说的那样,修改了之前案例中的结果掩码。
实际做的是,在绘画应用程序中打开输入图像并为图像添加了另一个图层。在油漆中使用画笔工具,在这个新图层上用白色标记错过的前景(头发、鞋子、球等)和用黑色标记不需要的背景(如标志、地面等)。然后用灰色填充剩余的背景。 然后在 OpenCV 中加载该蒙版图像,编辑我们获得的原始蒙版图像,并在新添加的蒙版图像中使用相应的值。检查下面的代码:
# newmask is the mask image by manually labelled
newmask = cv2.imread('messi-new-mask.jpg', 0)
# wherever it is marked white (sure foreground), change mask=1
# wherever it is marked black (sure background), change mask=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')
img = img*mask[:,:,np.newaxis]
plt.subplot(121)
plt.imshow(mask)
plt.subplot(122)
plt.imshow(img)
plt.colorbar()
plt.show()
在这里,可以直接进入掩码模式,而不是在 rect 模式下初始化。只需用 2 像素或 3 像素(可能的背景/前景)标记蒙版图像中的矩形区域。然后像我们在第二个示例中所做的那样用 1 像素标记我们的sure_foreground
。然后直接应用带有mask模式的grabCut
函数。