【翻译:OpenCV-Python教程】用GrabCut算法进行交互式的前景提取

⚠️由于自己的拖延症,3.4.3翻到一半,OpenCV发布了4.0.0了正式版,所以接下来是按照4.0.0翻译的。

⚠️除了版本之外,其他还是照旧,Interactive Foreground Extraction using GrabCut Algorithm,原文

目标

在本章

  • 我们会看到GrabCut(译者注:它是graph cut算法的改进)算法来提取图像中的前景
  • 我们将为此创建一个交互式的应用程序

理论

GrabCut算法的设计者是来自微软英国剑桥研究院的Carsten Rother,Vladimir Kolmogorov 和 Andrew Blake,在他们的论文 "GrabCut": interactive foreground extraction using iterated graph cuts 中提出来的。该算法需要最小的人工交互来做前景提取,这个算法被称为 GrabCut。

从用户的角度来看,该算法是如何工作的呢?最初用户在前景区域周围绘制一个矩形(该矩形需要完全框住所有的前景区域) 。然后算法对其进行迭代分割,得到最佳结果。但在某些情况下,分割的不是那么理想,比如说,它可能把一些前景区域标成了背景,或者反过来。如果发生了这样的情况,用户需要进行仔细的修正。只要在有错误结果的地方“划一下”就行了。“划一下”基本上的意思就是说,*"嘿,这个区域应该是前景,你标记它为背景,在下一次迭代中更正它。"* 或者对背景来说,反过来。然后再下一次迭代中,你就会得到更好的结果。

看下面的图像。第一张图中,球员和足球被圈在一个蓝色的长方形里。然后用白色笔画(表示前景)和黑色笔画(表示背景)进行一些最终的修正。我们得到了一个很好的结果。

grabcut_output1.jpg

那么到底对背景做了什么操作呢?

  • 用户输入了一个矩形。所有在此矩形之外的区域都被确信为背景(这就是我们之前提到的,你必须用此矩形框住所有前景的原因)。而在矩形内部的区域,都被认为是未知的。类似地,任何指定前台和后台的用户输入都被认为是硬标签,这意味着它们不会在后续迭代的过程中改变。
  • 计算机根据我们给出的数据,做一个初始的标记。它标记出前景和背景像素(或者硬标记)。
  • 现在利用高斯混合模型(GMM)对前景和背景进行建模。
  • 根据我们给出的数据,GMM学习并且创建一个新的像素分布。也就是说,根据未知像素与其他硬标记像素的颜色统计关系(就像聚类一样),将未知像素标记为可能的前景或可能的背景。
  • 由这个像素分布构建一个图像。图像中的节点是像素。再添加另外两个节点,源节点终节点。每个前景像素连接到源节点,每个背景像素连接到汇聚节点。
  • 连接像素点到 源节点/终节点 的权重值,被设置成该像素点属于 前景/背景的概率。像素间的权重由边缘信息或像素相似性来定义。如果像素颜色差异较大,则它们之间的边缘就会被给出一个较低的权重。
  • 然后采用最小切割(mincut)算法对图像进行分割。它用最小损失函数将图分割成两部分,源节点和终节点。顺势函数是所有被切割边的权值之和。在分割之后,切割后,所有连接到源节点的像素都变成了前景,连接到汇聚节点的像素都变成了背景。
  • 这个过程将继续迭代下去,直到分类收敛为止。:

如下图所示:(感谢提供图像:http://www.cs.ru.ac.za/research/g02m1682/)

grabcut_scheme.jpg

示例

现在我们用OpenCV来做一个grabcut算法,OpenCV有这个函数,cv.grabCut()来完成这个工作。我们先来看看它的参数吧:

  • img - 输入图像
  • mask - 这是个遮罩图层,我们在这个图层上指定出哪里是背景区域,前景区域,或者可能的前景/背景等等。它由以下这些标志组成,cv.GC_BGDcv.GC_FGDcv.GC_PR_BGDcv.GC_PR_FGD,或者简单的传入0,1,2,3给这个图层。
  • rect - 它表示一个矩形以及矩形所在的坐标,该矩形框住了所有前景,它的格式为(x,y,w,h)。
  • bdgModel, fgdModel - 这些是算法内部使用的数组。你只要创建两个 np.float64 类型的,大小为(1,65)的,全部是0的数组就好了。
  • iterCount - 算法应该迭代的次数。
  • mode - 它应该是cv.GC_INIT_WITH_RECT或者cv.GC_INIT_WITH_MASK或者组合,它决定了我们是否要画一个矩形,或者做出最终的修正。

首先我们来看矩形模式。我们加载图像,创建一个相似的遮罩图层。我们创建 fgdModelbgdModel.。给出矩形的参数。一切都很直接简单,现在让算法迭代运行五次。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()

看以下结果:

grabcut_rect.jpg

糟了,梅西的头发不见了。梅西没有头发还有人喜欢吗?我们得把他的头发给弄回来。所以我们得用1像素(确信是前景)来仔细的来修正一下了。同时,某些地面也被划进来了,这些使我们不需要的,还有些logo的部分也是一样。我们的把他们去掉。这里我们给了一些0像素来修正(确认是背景)。就像我们现在说的那样,我们修正了结果遮罩层,应用在之前情况下。

我实际做的是,我在一个画图软件中打开输入图像并向图像添加另一个图层。使用笔刷工具在新增的这一图层里,我用白色标记出那些丢失的前景(头发,鞋子,球等等),用黑色标记出那些我们不需要的背景(比如logo,地面等等)。然后用灰色填充剩余的背景。再将该遮罩图层加载到OpenCV中,用新添加的遮罩图层中相应的值来编辑我们之前得到的原始遮罩图层。请看下面的代码:

# newmask is the mask image I manually labelled
newmask = cv.imread('newmask.png',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 = 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()

看以下结果:

grabcut_mask.jpg

就是这样了。现在我们不在矩形模式下初始化,你可以直接跳到遮罩图层模式。只需要在图像中用2像素或者3像素标记矩形区域(可能的背景/前景)。然后在第二个例子中,用1像素标记我们确信的前景。然后直接使用遮罩图层模式直接应用grabCut函数就可以了。

额外资源

练习

  • OpenCV 例子中包含了一个示例grabcut.py,它是一个应用了grabcut的交互式个工具,查看它。同时也看看这个视频 youtube video (译者注:youtube,翻墙吧!)了解怎么使用它。
  • 现在,您可以将其制作为交互式示例,通过鼠标画矩形以及划出修正,创建一个拖动条来调节这些修正等等。

上篇:【翻译:OpenCV-Python教程】用分水岭算法分割图像

下篇:【翻译:OpenCV-Python教程】理解特征

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值