python OpenCV学习笔记(三十):利用迭代图割算法进行交互式前景提取

官方文档 – https://docs.opencv.org/3.4.0/d8/d83/tutorial_py_grabcut.html


最初用户在前景区域周围绘制一个矩形(前景区域应该完全在矩形中)。然后用迭代算法对其进行迭代以得到最好的结果。就完成了。但在某些情况下,分割并不会很好,比如,它可能会把一些前景区域作为背景,反之亦然。在这种情况下,用户需要进行精细的触控操作。只要在图片上画一些有错误的结果就可以了。笔画基本上是说“嘿,这个区域应该是前景,你标记它的背景,在下一次迭代中纠正它”或者它的背景是相反的。然后在下一次迭代中,你会得到更好的结果。

见下面的图片。第一个球员和足球是用蓝色的长方形围起来的。然后用白色的笔画(表示前景)和黑色的笔画(表示背景)来做最后的图。我们得到了一个很好的结果。

python OpenCV学习笔记(三十):利用迭代图割算法进行交互式前景提取_第1张图片

那么背景中会发生什么呢?

  • 用户输入的矩形。这个矩形外的所有内容都将被作为确定的背景(这是在您的矩形应该包含所有对象之前提到的原因)。矩形内的所有东西都是未知的。类似地,任何指定前景和背景的用户输入都被认为是硬标签,这意味着它们在这个过程中不会改变。
  • 计算机对我们提供的数据做了初步的标记。它将前景和背景像素(或硬标签)标记为标签。
  • 现在使用高斯混合模型(GMM)来模拟前景和背景。
  • 根据我们提供的数据,GMM会学习并创建新的像素分布。也就是说,未知的像素被标记为可能的前景或可能的背景,这取决于它与其他硬标签的像素在颜色统计方面的关系(它就像集群一样)。
  • 图是由这个像素分布构建的。图中的节点是像素。添加了两个节点,源节点和汇聚节点。每个前景像素都连接到源节点,每个背景像素都连接到汇聚节点。
  • 将像素连接到源节点/端节点的边的权重是由像素是前景/背景的概率来定义的。像素之间的权重是由边缘信息或像素相似性定义的。如果像素颜色相差很大,它们之间的边缘将得到较低的权重。
  • 然后用一种mincut算法对图像进行分段。它将图形分割为两个分离的源节点和汇聚节点,并具有最小的成本函数。成本函数是所有被切割的边的权值的和。在切割之后,连接到源节点的所有像素都将成为前景,而连接到汇节点的节点则成为背景。
  • 这个过程一直持续到分类收敛为止。

python OpenCV学习笔记(三十):利用迭代图割算法进行交互式前景提取_第2张图片

Demo

现在我们来看看OpenCV的grabcut算法。OpenCV有这个函数的函数,cv.grabCut()
mask, bgdModel, fgdModel = cv.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode])
我们将首先看它的参数:

  • img:输入图像
  • mask :它是一个蒙版图像,我们指定哪些区域是背景,前景或可能的背景/前景等。它是由下面的标志,cv.GC_BGDcv.GC_FGDcv.GC_PR_BGDcv.GC_PR_FGD,或简单地将0,1,2,3传递给图像。
  • rect :它是一个矩形的坐标,其中包含了前景对象的格式(x,y,w,h)
  • bdgModel, fgdModel :这些是算法内部使用的数组。你只需要创建两个大小为(1,65)的np.float64类型的0数组。
  • iterCount :算法应该运行的迭代次数。
  • mode :它应该是cv.GC_INIT_WITH_RECTcv.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()

python OpenCV学习笔记(三十):利用迭代图割算法进行交互式前景提取_第3张图片

上图少了头发,所以我们会有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()

python OpenCV学习笔记(三十):利用迭代图割算法进行交互式前景提取_第4张图片

在这里,你可以直接进入蒙版模式,而不是在矩形模式下初始化。用2像素或3像素(可能的背景/前景)在蒙版图像中标记矩形区域。然后用1像素标记我们的前景,就像我们在第二个例子中做的那样。然后直接将grabCut函数应用于蒙版模式。

你可能感兴趣的:(OpenCV)