官方文档 – https://docs.opencv.org/3.4.0/d8/d83/tutorial_py_grabcut.html
最初用户在前景区域周围绘制一个矩形(前景区域应该完全在矩形中)。然后用迭代算法对其进行迭代以得到最好的结果。就完成了。但在某些情况下,分割并不会很好,比如,它可能会把一些前景区域作为背景,反之亦然。在这种情况下,用户需要进行精细的触控操作。只要在图片上画一些有错误的结果就可以了。笔画基本上是说“嘿,这个区域应该是前景,你标记它的背景,在下一次迭代中纠正它”或者它的背景是相反的。然后在下一次迭代中,你会得到更好的结果。
见下面的图片。第一个球员和足球是用蓝色的长方形围起来的。然后用白色的笔画(表示前景)和黑色的笔画(表示背景)来做最后的图。我们得到了一个很好的结果。
那么背景中会发生什么呢?
现在我们来看看OpenCV的grabcut算法。OpenCV有这个函数的函数,cv.grabCut()
。
mask, bgdModel, fgdModel = cv.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode])
我们将首先看它的参数:
cv.GC_BGD
,cv.GC_FGD
,cv.GC_PR_BGD
,cv.GC_PR_FGD
,或简单地将0,1,2,3传递给图像。np.float64
类型的0数组。cv.GC_INIT_WITH_RECT
或cv.GC_INIT_WITH_MASK
,或者组合起来决定我们是画矩形还是最后的触点。首先让我们看看矩形模式。我们加载图像,创建一个类似的蒙版图像。我们创建了fgdModel和bgdModel。我们给出了矩形的参数。一切都很简单。让算法运行5次迭代。mode应该是cv.GC_INIT_WITH_RECT
,因为我们使用的是矩形。然后运行grabcut。它修改了蒙版图像。在新的蒙版图像中,像素将被标记为四个标志,表示上面指定的背景/前景。 因此我们修改了这个蒙版,使所有的0像素和2像素都被设置为0(即背景),所有1像素和3像素都被设置为1(即前景像素)。现在我们的最后一个遮罩已经准备好了。只需将它与输入图像相乘,就可以得到分段图像。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg')
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)
cv.grabCut(img, mask, rect, bgdModel, fgdModel, 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.colorbar(), plt.show()
上图少了头发,所以我们会有1像素的修补(确定前景)。与此同时,有些地方的地面已经出现了我们不想要的画面,还有一些标志。我们需要移除它们。在那里我们提供了0像素的修补(当然是背景)。所以我们在之前的例子中修改了我们的结果蒙版。
我实际做的是,我在绘画应用程序中打开了输入图像,并在图像中添加了另一层。在油漆中使用笔刷工具,在新图层中,用白色标记一些丢失的前景(头发,鞋子,球等等),并且用黑色标记一些不需要的背景(标志,草地等)。然后将剩余的背景填充为灰色。然后在OpenCV中加载了这个蒙版图像,编辑了原始的蒙版图像,我们在新添加的蒙版图像中得到了相应的值。查看下面的代码:
# newmask是手动标记的蒙版图像
newmask = cv.imread('newmask.png',0)
# 标记为白色的地方(确定为前景),改变mask=1
# 标记为黑色的地方(确定为背景),改变mask=0
mask[newmask==0] = 0
mask[newmask==255] = 1
mask, bgdModel, fgdModel = cv.grabCut(img, mask, None, bgdModel, fgdModel, 5, cv.GC_INIT_WITH_MASK)
mask = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')
img = img * mask[:, :, np.newaxis]
plt.imshow(img), plt.colorbar(), plt.show()
在这里,你可以直接进入蒙版模式,而不是在矩形模式下初始化。用2像素或3像素(可能的背景/前景)在蒙版图像中标记矩形区域。然后用1像素标记我们的前景,就像我们在第二个例子中做的那样。然后直接将grabCut函数应用于蒙版模式。