win10+Python3.7.3+OpenCV3.4.1入门学习(十七 图像分割与提取)————17.4 交互式前景提取

Python版本是Python3.7.3,OpenCV版本OpenCV3.4.1,开发环境为PyCharm

17.4 交互式前景提取

经典的前景提取技术主要使用纹理(颜色)信息,如魔术棒工具,或根据边缘(对比度)信息,如智能剪刀等完成。2004年,微软研究院(剑桥)的Rother等人在论文GrabCut: Interactive Foreground Extraction Using Iterated Graph Cuts中提出了交互式前景提取技术。他们提出的算法,仅需要做很少的交互操作,就能够准确地提取出前景图像。
在开始提取前景时,先用一个矩形框指定前景区域所在的大致位置范围,然后不断迭代地分割,直到达到最好的效果。经过上述处理后,提取前景的效果可能并不理想,存在前景没有提取出来,或者将背景提取为前景的情况,此时需要用户干预提取过程。用户在原始图像的副本中(也可以是与原始图像大小相等的任意一幅图像),用白色标注要提取为前景的区域,用黑色标注要作为背景的区域。然后,将标注后的图像作为掩模,让算法继续迭代提取前景从而得到最终结果。
例如,对于下图的左图,先用矩形框将要提取的前景Lena框出来,再分别用白色和黑色对前景图像、背景图像进行标注。完成标注后,使用交互式前景提取算法,就会得到下图右图所示的结果图像。

在这里插入图片描述

PowerPoint 2016提供了“删除背景”功能。用户可以根据需要,在图像上标注出需要保留的部分和需要删除的部分,然后让PowerPoint帮助我们完成前景对象的提取。尝试在PowerPoint中删除图像背景,会帮助我们更好地理解交互式前景提取时模板的使用方式。
下面我们来看GrabCut算法的具体实施过程。
1.将前景所在的大致位置使用矩形框标注出来。值得注意的是,此时矩形框框出的仅仅是前景的大致位置,其中既包含前景又包含背景,所以该区域实际上是未确定区域。但是,该区域以外的区域被认为是“确定背景”。
2.根据矩形框外部的“确定背景”数据来区分矩形框区域内的前景和背景。
3.用高斯混合模型(Gaussians Mixture Model, GMM)对前景和背景建模。GMM会根据用户的输入学习并创建新的像素分布。对未分类的像素(可能是背景也可能是前景),根据其与已知分类像素(前景和背景)的关系进行分类。
4.根据像素分布情况生成一幅图,图中的节点就是各个像素点。除了像素点之外,还有两个节点:前景节点和背景节点。所有的前景像素都和前景节点相连,所有的背景像素都和背景节点相连。每个像素连接到前景节点或背景节点的边的权重由像素是前景或背景的概率来决定。
5.图中的每个像素除了与前景节点或背景节点相连外,彼此之间还存在着连接。两个像素连接的边的权重值由它们的相似性决定,两个像素的颜色越接近,边的权重值越大。
6.完成节点连接后,需要解决的问题变成了一幅连通的图。在该图上根据各自边的权重关系进行切割,将不同的点划分为前景节点和背景节点。
7.不断重复上述过程,直至分类收敛为止。
OpenCV的官网上有更详细的资料(http://www.cs.ru.ac.za/research/g02m1682/)
在OpenCV中,实现交互式前景提取的函数是cv2.grabCut(),其语法格式为:

mask, bgdModel, fgdModel =cv2.grabCut(img, mask, rect, bgdModel, fgdModel,
iterCount[, mode])

式中:
● img为输入图像,要求是8位3通道的。
● mask为掩模图像,要求是8位单通道的。该参数用于确定前景区域、背景区域和不确定区域,可以设置为4种形式。
● cv2.GC_BGD:表示确定背景,也可以用数值0表示。
● cv2.GC_FGD:表示确定前景,也可以用数值1表示。
● cv2.GC_PR_BGD:表示可能的背景,也可以用数值2表示。
● cv2.GC_PR_FGD:表示可能的前景,也可以用数值3表示。
在最后使用模板提取前景时,会将参数值0和2合并为背景(均当作0处理),将参数值1和3合并为前景(均当作1处理)。在通常情况下,我们可以使用白色笔刷和黑色笔刷在掩模图像上做标记,再通过转换将其中的白色像素设置为0,黑色像素设置为1。
● rect指包含前景对象的区域,该区域外的部分被认为是“确定背景”。因此,在选取时务必确保让前景包含在rect指定的范围内;否则,rect外的前景部分是不会被提取出来的。只有当参数mode的值被设置为矩形模式cv2.GC_INIT_WITH_RECT时,参数rect才有意义。其格式为(x, y, w, h),分别表示区域左上角像素的x轴和y轴坐标以及区域的宽度和高度。如果前景位于右下方,又不想判断原始图像的大小,对于w 和h可以直接用一个很大的值。使用掩模模式时,将该值设置为none即可。
● bgdModel为算法内部使用的数组,只需要创建大小为(1, 65)的numpy.float64数组。
● fgdModel为算法内部使用的数组,只需要创建大小为(1, 65)的numpy.float64数组。
● iterCount表示迭代的次数。
● mode表示迭代模式。其可能的值与含义如下表所示。

在这里插入图片描述

eg1:使用GrabCut算法提取图像的前景,并观察提取效果。
代码如下:

import numpy as np
import cv2
import matplotlib.pyplot as plt
o = cv2.imread('lenacolor.png')
orgb=cv2.cvtColor(o,cv2.COLOR_BGR2RGB)
mask = np.zeros(o.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,400,400)
cv2.grabCut(o,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
ogc = o*mask2[:,:,np.newaxis]
ogc=cv2.cvtColor(ogc,cv2.COLOR_BGR2RGB)
plt.subplot(121)
plt.imshow(orgb)
plt.axis('off')
plt.subplot(122)
plt.imshow(ogc)
plt.axis('off')
plt.show()

运行上述程序,得到的结果如下图所示。

在这里插入图片描述

可以看到,在不使用掩模(掩模值都设置为默认值0时),函数cv2.grabCut()的处理效果并不太好:提取图中左图的前景时,人物的帽子没有提取完整。对于有些图像,也有可能将背景错误地提取出来。
为了得到完整的前景对象,需要做一些改进。这里对原始图像进行标注,将需要保留的部分设置为白色,将需要删除的背景设置为黑色。以标记好的图像作为模板,使用函数cv2.grabCut()完成前景的提取。
这个过程主要包含以下步骤:
1.利用函数cv2.grabCut()在cv2.GC_INIT_WITH_RECT 模式下对图像进行初步的前景提取,得到初步提取的结果图像og。
2.使用Windows系统自带的笔刷工具,打开要提取前景的图像,比如lena。
3.使用白色笔刷在希望提取的前景区域做标记。
4.使用黑色笔刷在希望删除的背景区域做标记。
5.将当前设置好的lena图像另存为模板图像m0。
6.将模板图像m0中的白色值和黑色值映射到模板m中。将模板图像m0中的白色值(像素值为255)映射为模板图像m中的确定前景(像素值为1),将模板图像m0中的黑色值(像素值为0)映射为模板图像m中的确定背景(像素值为0)。
7.以模板图像m作为函数cv2.grabCut()的模板参数(mask),对图像og完成前景提取。
需要注意,在上述步骤中,使用画笔标记的模板图像m0不能直接作为模板(即参数mask)使用。函数cv2.grabCut()要求,参数mask的值必须是cv2.GC_BGD(确定背景)、cv2.GC_FGD(确定前景)、cv2.GC_PR_BGD(可能的背景)、cv2.GC_PR_FGD(可能的前景),或者是0、1、2、3之中的值。此时的模板图像m0中,存在着[0, 255]内的值,所以它的值不满足函数cv2.grabCut()的要求,无法作为参数mask直接使用。必须先将模板图像m0中的白色值和黑色值映射到模板m上,再将模板图像m作为函数cv2.grabCut()的模板参数。

eg2:在GrabCut算法中使用模板提取图像的前景,并观察提取的效果。
代码如下:

import numpy as np
import cv2
import matplotlib.pyplot as plt
o= cv2.imread('lenacolor.png')
orgb=cv2.cvtColor(o,cv2.COLOR_BGR2RGB)
mask = np.zeros(o.shape[:2],np.uint8)
bgd = np.zeros((1,65),np.float64)
fgd = np.zeros((1,65),np.float64)
rect = (50,50,400,500)
cv2.grabCut(o,mask,rect,bgd,fgd,5,cv2.GC_INIT_WITH_RECT)
mask2 = cv2.imread('mask.png',0)
mask2Show = cv2.imread('mask.png',-1)
m2rgb=cv2.cvtColor(mask2Show,cv2.COLOR_BGR2RGB)
mask[mask2 == 0] = 0
mask[mask2 == 255] = 1
mask, bgd, fgd = cv2.grabCut(o,mask,None,bgd,fgd,5,cv2.GC_INIT_WITH_MASK)
mask = np.where((mask==2)|(mask==0),0,1).astype('uint8')
ogc = o*mask[:,:,np.newaxis]
ogc=cv2.cvtColor(ogc,cv2.COLOR_BGR2RGB)
plt.subplot(121)
plt.imshow(m2rgb)
plt.axis('off')
plt.subplot(122)
plt.imshow(ogc)
plt.axis('off')
plt.show()

运行上述程序,得到的结果如下图所示。
在这里插入图片描述

在函数cv2.grabCut()的实际使用中,也可以不使用矩形初始化,直接使用模板模式。构造一个模板图像,其中:
● 使用像素值0标注确定背景。
● 使用像素值1标注确定前景。
● 使用像素值2标注可能的背景。
● 使用像素值3标注可能的前景。
构造完模板后,直接将该模板用于函数cv2.grabCut()处理原始图像,即可完成前景的提取。
一般情况下,自定义模板的步骤为:
1.先使用numpy.zeros构造一个内部像素值都是0(表示确定背景)的图像mask,以便在后续步骤中逐步对该模板图像进行细化。
2.使用mask[30:512, 50:400]=3,将模板图像中第30行到第512行,第50列到400列的区域划分为可能的前景(像素值为3,对应参数mask的含义为“可能的前景”)。
3.使用mask[50:300, 150:200]=1,将模板图像中第50行到第300行,第150列到第200列的区域划分为确定前景(像素值为1,对应参数mask的含义为“确定前景”)。
在此基础上,将mask作为自定义模板,使用GrabCut算法,完成前景的提取。

eg3:在GrabCut算法中直接使用自定义模板提取图像的前景,并观察提取效果。
代码如下:

import numpy as np
import cv2
import matplotlib.pyplot as plt
o= cv2.imread('lenacolor.png')
orgb=cv2.cvtColor(o,cv2.COLOR_BGR2RGB)
bgd = np.zeros((1,65),np.float64)
fgd = np.zeros((1,65),np.float64)
mask2 = np.zeros(o.shape[:2],np.uint8)
mask2[30:512,50:400]=3
mask2[50:300,150:200]=1
cv2.grabCut(o,mask2,None,bgd,fgd,5,cv2.GC_INIT_WITH_MASK)
mask2 = np.where((mask2==2)|(mask2==0),0,1).astype('uint8')
ogc = o*mask2[:,:,np.newaxis]
ogc=cv2.cvtColor(ogc,cv2.COLOR_BGR2RGB)
plt.subplot(121)
plt.imshow(orgb)
plt.axis('off')
plt.subplot(122)
plt.imshow(ogc)
plt.axis('off')
plt.show()

运行上述程序,得到的结果如下图所示。
在这里插入图片描述

为了方便大家理解,本例中构造的模板比较简单。如果构造更有针对性的模板,能够提取更为准确的前景对象。
需要注意的是,对于不同的图像,要构造不同的模板来划分它们的确定前景、确定背景、可能的前景与可能的背景。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值