交互式前景提取技术仅需要很少的交互操作,就可以准确地提取出前景图像。

读《"GrabCut" -- Interactive Foreground Extraction using Iterated Graph Cuts》

在PPT中,可以感受一下交互式前景提取。详情请见下面这篇文章:

提取过程

在开始提取前景时,先用一个矩形框指定前景区域所在的大致位置范围,然后不断迭代地分割,直到达到最好的效果。经过上述处理后,提取前景的效果可能并不理想,存在前景没有提取出来,或者将背景提取为前景的情况,此时需要用户干预提取过程。用户在原始图像的副本中(也可以是与原始图像大小相等的任意一幅图像),用白色标注要提取为前景的区域,用黑色标注要作为背景的区域。然后,将标注后的图像作为掩模,让算法继续迭代提取前景从而得到最终结果。

具体地:

1、将前景所在的大致位置使用矩形框标注出来。值得注意的是,此时矩形框框出的仅仅是前景的大致位置,其中既包含前景又包含背景,所以该区域实际上是未确定区域。但是,该区域以外的区域被认为是“确定背景”。

2、根据矩形框外部的“确定背景”数据来区分矩形框区域内的前景和背景。

3、用高斯混合模型(Gaussians Mixture Model.GMM)对前景和背景建模。GMM会根据用户的输入学习并创建新的像素分布。对未分类的像素(可能是背景也可能是前景),根据其与已知分类像素(前景和背景)的关系进行分类。

4、根据像素分布情况生成一幅图,图中的节点就是各个像素点。除了像素点之外,还有两个节点:前景节点和背景节点。所有的前景像素都和前景节点相连,所有的背景像素都和背景节点相连。每个像素连接到前景节点或背景节点的边的权重由像素是前景或背景的概率来决定。

5、图中的每个像素除了与前景节点或背景节点相连外,彼此之间还存在着连接。两个像素连接的边的权重值由它们的相似性决定,两个像素的颜色越接近,边的权重值越大。

6、完成节点连接后,需要解决的问题变成了一幅连通的图。在该图上根据各自边的权重关系进行切割,将不同的点划分为前景节点和背景节点。

7、不断重复上述过程,直至分类收敛为止。

函数语法

OpenCV中,实现交互式前景提取的函数是cv2.grabCut(),语法格式为:

mask, bgdModel, fgdModel = cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[,mode])
  • img:输入的图像,要求是8位3通道的;
  • mask:为掩模图像,要求8位单通道的。该参数用于确定前景区域、背景区域和不确定区域,可以设置四种形式:
    • cv2.GC_BGD:表示确定背景,也可以用0来表示
    • cv2.GC_FGD:表示确定前景,也可以用1来表示
    • cv2.GC_PR_BGD:表示可能的背景,也可以用数值2表示
    • cv2.GC_PR_FGD:表示可能的背景,也可以用数值2表示

在最后使用模板提取前景时,会将参数值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.floate64数组。

  • fgdModel为算法内部使用的数组,只需要创建大小为(1,65)的numpy.floate64数组。

  • iterCount表示迭代的次数。

  • mode表示迭代模式,其值如下表所示:

迭代模式

代码演示

例1:使用GrabCut算法提取图像的前景,并观察提取效果。

import cv2
import numpy as np
import matplotlib.pyplot as plt
o = cv2.imread(r'lean.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,500)

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()

提取效果

可以看出,不使用掩模的情况下(mask都设置为0),提取效果并不是很理想。为了得到理想的提取效果,需要做一些改进,即人工进行干预。可以对原始图像进行标注:将想要保留的部分设置为白色,将需要删除的背景设置为黑色。以标记好的图像作为模板,使用函数cv2.grabCut()完成前景的提取。

这个过程只要包括以下步骤:

windows自带的笔刷工具

需要注意,使用画笔标记的模板图像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,225]内的值,所以他的值不满足函数cv2.grabCut()的要求,无法作为参数mask直径使用。必须先将模板图像m0中的白色值和黑色值映射到模板m上,再将模板图像m作为函数cv2.grabCut()的模板参数。

例2:在GrabCut算法中使用模板提取图像的前景,并观察效果。

import cv2
import numpy as np
import matplotlib.pyplot as plt

o = cv2.imread(r'lean.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('lean0.png',0)
mask2Show = cv2.imread('lean0.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()

运行结果


素材:

lean

lean0


博主个人公众号
版权声明 ▶ 本网站名称:陶小桃Blog
▶ 本文链接:https://www.52txr.cn/2022/OpenCV56.html
▶ 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行核实删除。
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!

最后修改:2022 年 05 月 26 日
如果觉得我的文章对你有用,请随意赞赏