做微信视频的网站,seo人才招聘,创意100图片欣赏,综合网站模板第17章#xff1a;图像分割提取一、用分水岭算法实现图像分割提取#xff1a;1. 算法原理#xff1a;2. 相关函数介绍#xff1a;(1) 形态学函数回顾#xff1a;(2) 距离变换函数distanceTransform#xff1a;(3) 确定未知区域#xff1a;(4) 函数connectedComponents对…
第17章图像分割提取一、用分水岭算法实现图像分割提取1. 算法原理2. 相关函数介绍(1) 形态学函数回顾(2) 距离变换函数distanceTransform(3) 确定未知区域(4) 函数connectedComponents对象标注(5) 函数cv2.watershed()3. 分水岭算法图像分割实例二、交互式前景提取在图像处理的过程中我们经常需要将前景对象从图像中提取出来。例如在视频监控中观测到的是固定背景下的视频内容而我们对背景本身并无兴趣感兴趣的是背景中出现的车辆、行人或者其他对象。我们希望将这些对象从视频中提取出来而忽略那些没有对象进入背景的视频内容。
一、用分水岭算法实现图像分割提取
图像分割是图像处理过程中一种非常重要的操作。分水岭算法将图像形象地比喻为地理学上的地形表面实现图像分割该算法非常有效。
1. 算法原理
任何一幅灰度图像都可以被看作是地理学上的地形表面灰度值高的区域可以被看成是山峰灰度值低的区域可以被看成是山谷。如下图所示其中左图是原始图像右图是其对应的“地形表面”。 如果我们向每一个山谷中“灌注”不同颜色的水(这里采用了OpenCV官网的表述冈萨雷斯将灌注表述为在山谷中打洞然后让水穿过洞以均匀的速率上升)。那么随着水位不断地升高不同山谷的水就会汇集到一起。在这个过程中为了防止不同山谷的水交汇我们需要在水流可能汇合的地方构建堤坝。该过程将图像分成两个不同的集合集水盆地和分水岭线。我们构建的堤坝就是分水岭线也即对原始图像的分割。这就是分水岭算法。
下图中左图是原始图像右图是使用分水岭算法得到的图像分割结果。在CMM的网站上不仅提供了该示例图像还提供了动画演示效果有兴趣的读者可以去网站上看看。 但是由于噪声等因素的影响采用上述基础分水岭算法经常会得到过度分割的结果。过度分割会将图像划分为一个个稠密的独立小块让分割失去了意义。下图展示了过度分割的图像。其中左图是电泳现象的图像右图是过度分割的结果图像可以看到过度分割现象非常严重。 为了改善图像分割效果人们提出了基于掩模的改进的分水岭算法。改进的分水岭算法允许用户将他认为是同一个分割区域的部分标注出来(被标注的部分就称为掩模)。这样分水岭算法在处理时就会将标注的部分处理为同一个分割区域。
在下图中左图是原始图像我们对其做了标注处理其中被标注为深色的三个小色块表示在使用掩模分水岭算法时这些部分所包含的颜色都会被分割在同一个区域内。使用掩模分水岭算法得到的分割结果如图中的右图所示。 采用改进的分水岭算法对图中左侧的电泳图像进行掩模处理得到右侧的分割结果。可以看出分割结果得到明显的改进。 2. 相关函数介绍
在OpenCV中可以使用函数cv2.watershed()实现分水岭算法。在具体的实现过程中还需要借助于形态学函数、距离变换函数 cv2.distanceTransform()、cv2.connectedComponents()来完成图像分割。下面对分水岭算法中用到的函数进行简单的说明。
使用分水岭算法之前需要对图像进行一些预处理下面就是图像预处理的步骤。也就是图像分割可以使用分水岭算法来进行使用分水岭算法需要对图像进行预处理。OpenCV中的watershed函数实现的分水岭算法是基于“标记”的分割算法用于解决传统的分水岭算法过度分割的问题。
(1) 形态学函数回顾
在使用分水岭算法对图像进行分割前需要对图像进行简单的形态学处理。先回顾一下形态学里的基本操作。 开运算 开运算是先腐蚀、后膨胀的操作开运算能够去除图像内的噪声。例如在下图中先对左图进行腐蚀操作会得到中间的图像再对中间的图像进行膨胀操作会得到右侧的图像。对图像进行开运算能够去除图像内的噪声。在用分水岭算法处理图像前要先使用开运算去除图像内的噪声以避免噪声对图像分割可能造成的干扰。 获取图像边界 通过形态学操作和减法运算能够获取图像的边界。例如在下图中左图是原始图像中间的图是对其进行腐蚀而得到的图像对二者进行减法运算就会得到右侧的图像。通过观察可知右图是左图的边界。 通过以上分析可知使用形态学操作和减法运算能够获取图像的边界信息。但是形态学操作仅适用于比较简单的图像。如果图像内的前景对象存在连接的情况使用形态学操作就无法准确获取各个子图像的边界了。
(2) 距离变换函数distanceTransform
当图像内的各个子图没有连接时可以直接使用形态学的腐蚀操作确定前景对象但是如果图像内的子图连接在一起时就很难确定前景对象了。此时借助于距离变换函数cv2.distanceTransform()可以方便地将前景对象提取出来。
距离变换函数cv2.distanceTransform()计算二值图像内所有点到最近背景点的距离(即图像内非零值像素点到最近的零值像素点的距离)。当然如果像素点本身的值为0则这个距离也为0。
距离变换函数cv2.distanceTransform()的计算结果反映了图像内各个像素与背景(值为0的像素点)的距离关系。通常情况下
如果前景对象的中心(质心)距离值为0的像素点距离较远会得到一个较大的值。如果前景对象的边缘距离值为0的像素点较近会得到一个较小的值。
如果对上述计算结果进行阈值化就可以得到图像内前景对象的中心、骨架等信息。距离变换函数cv2.distanceTransform()可以用于计算对象的中心还能细化轮廓、获取图像前景等有多种功能。
距离变换函数cv2.distanceTransform()的语法格式为 dstcv2.distanceTransform(src,distanceType,maskSize[,dstType]]) src是8位单通道的二值图像。 distanceType为距离类型参数其具体值和含义如表所示。 maskSize为掩模的尺寸其可能的值如表所示。需要注意当 distanceTypecv2.DIST_L1或 cv2.DIST_C时maskSize强制为3(因为设置为3和设置为5及更大值没有什么区别)。 dstType为目标图像的类型默认值为CV_32F。 dst返回值表示计算得到的目标图像可以是8位或32位浮点数尺寸和src相同。
示例用距离变换函数 cv2.distanceTransform()计算一幅图像的确定前景并观察效果
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(img.jpg)
# img cv2.imread(../sugar.tiff)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 阈值处理
rst, thresh cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)# 开运算
kernel np.ones((3, 3), np.uint8)
opening cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations2)# 图像距离计算
dist_transform cv2.distanceTransform(opening, cv2.DIST_L2, 5)# 获取前景对象的中心
rst, front cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)plt.subplot(161)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(162)
plt.imshow(gray_img, cmapgray)
plt.title(gray_img)
plt.axis(off)plt.subplot(163)
plt.imshow(thresh, cmapgray)
plt.title(thresh)
plt.axis(off)plt.subplot(164)
plt.imshow(opening, cmapgray)
plt.title(opening)
plt.axis(off)plt.subplot(165)
plt.imshow(dist_transform, cmapgray)
plt.title(dist_transform)
plt.axis(off)plt.subplot(166)
plt.imshow(front, cmapgray)
plt.title(front)
plt.axis(off)plt.show()(3) 确定未知区域
使用形态学的膨胀操作能够将图像内的前景“膨胀放大”。当图像内的前景被放大后背景就会被“压缩”所以此时得到的背景信息一定小于实际背景的不包含前景的“确定背景”。以下为了方便说明将确定背景称为B。
距离变换函数cv2.distanceTransform()能够获取图像的“中心”得到“确定前景”。为了方便说明将确定前景称为F。
图像中有了确定前景F和确定背景B剩下区域的就是未知区域UN了。这部分区域正是分水岭算法要进一步明确的区域
针对一幅图像O通过以下关系能够得到未知区域UN
未知区域UN图像O-确定背景B-确定前景F
对上述表达式进行整理可以得到
未知区域UN(图像O-确定背景B)-确定前景F
上式中的“图像O-确定背景B”可以通过对图像进行形态学的膨胀操作得到。前景对象膨胀 图像o - 确定背景B
示例标注一幅图像的确定前景确定背景及未知区域
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(img.jpg)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 阈值分割
rst, thresh cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
kernel np.ones((3, 3), dtypenp.uint8)
# 开运算
opening cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations2)
# 膨胀
bg cv2.dilate(opening, kernel, iterations3)# 距离计算
dist_tansform cv2.distanceTransform(opening, cv2.DIST_L2, 5)
rst, fore cv2.threshold(dist_tansform, 0.7 * dist_tansform.max(), 255, 0)
fore np.uint8(fore)
un cv2.subtract(bg, fore)plt.subplot(221)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(222)
plt.imshow(bg)
plt.title(bg)
plt.axis(off)plt.subplot(223)
plt.imshow(fore)
plt.title(fore)
plt.axis(off)plt.subplot(224)
plt.imshow(un)
plt.title(un)
plt.axis(off)plt.show()注意的是在图右上角的图像bg中
前景的一个个小圆是“原始图像-确定背景”部分而不是“确定背景”。其背景图像才是“确定背景”。
(4) 函数connectedComponents对象标注
明确了确定前景后就可以对确定前景图像进行标注了。在 OpenCV 中可以使用函数cv2.connectedComponents()进行标注。该函数会将背景标注为0将其他的对象使用从1开始的正整数标注。
函数cv2.connectedComponents()的语法格式为
retval,labelscv2.connectedComponents(image) image为8位单通道的待标注图像。retval为返回的标注的数量。labels为标注的结果图像。
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(img.jpg)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)rst, thresh cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
kernel np.ones((3, 3), dtypenp.uint8)
opening cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations2)sure_bg cv2.dilate(opening, kernel, iterations3)
dist_transform cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, fore cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0)
fore np.uint8(fore)# 标注前景对象
rst, markers cv2.connectedComponents(fore)
print(markers)plt.subplot(131)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(132)
plt.imshow(fore)
plt.title(fore)
plt.axis(off)plt.subplot(133)
plt.imshow(markers)
plt.title(markers)
plt.axis(off)plt.show()左图是原始图像中间的是经过距离变换后得到的前景图像的中心点图像fore。右图是对前景图像的中心点图像进行标注后的结果图像markers。
可以看到前景图像的中心点被做了不同的标注
函数 cv2.connectedComponents()在标注图像时会将背景标注为0将其他的对象用从1开始的正整数标注。具体的对应关系为
数值0代表背景区域。从数值1开始的值代表不同的前景区域。
在分水岭算法中标注值0代表未知区域。所以我们要对函数cv2.connectedComponents()标注的结果进行调整将标注的结果都加上数值1。经过上述处理后在标注结果中
数值1代表背景区域。从数值2开始的值代表不同的前景区域。
为了能够使用分水岭算法还需要对原始图像内的未知区域进行标注将已经计算出来的未知区域标注为0即可。
ret,markerscv2.connectedComponents(fore)
markersmarkers1
markers[未知区域]0示例对cv2.connectedComponents()标注结果进行修正
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(img.jpg)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)rst, thresh cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
kernel np.ones((3, 3), dtypenp.uint8)
opening cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations2)sure_bg cv2.dilate(opening, kernel, iterations3)
dist_transform cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, fore cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
fore np.uint8(fore)
rst, markers cv2.connectedComponents(fore)# 修正标注的前景对象
fore_adv fore.copy()
unknown cv2.subtract(sure_bg, fore_adv)
ret2, markers2 cv2.connectedComponents(fore_adv)
markers2 1
markers2[unknown 255] 0plt.subplot(141)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(142)
plt.imshow(fore)
plt.title(fore)
plt.axis(off)plt.subplot(143)
plt.imshow(markers)
plt.title(markers)
plt.axis(off)plt.subplot(144)
plt.imshow(markers2)
plt.title(markers2)
plt.axis(off)plt.show()markers图是对一幅图像使用函数cv2.connectedComponents()直接标注后的结果。markers2图是修正后的标注结果。
对比左右图可以看出右图在前景图像的边缘(未知区域)进行了标注使得每一个确定前景都有一个黑色的边缘这个边缘是被标注的未知区域。
(5) 函数cv2.watershed()
OpenCV中的watershed函数实现的分水岭算法是基于“标记”的分割算法用于解决传统的分水岭算法过度分割的问题。
完成上述图像预处理后就可以使用分水岭算法对预处理结果图像进行分割了。在 OpenCV 中实现分水岭算法的函数是cv2.watershed()其语法格式为 markerscv2.watershed(image,markers) image是输入图像必须是8位三通道的图像。 markers是32位单通道的标注结果它应该和image具有相等大小。 在对图像使用cv2.watershed()函数处理之前必须先对图像进行预处理用正数大致勾画出图像中的期望分割区域。每一个分割的区域会被标注为1、2、3等。对于尚未确定的区域需要将它们标注为0。我们可以将标注区域理解为进行分水岭算法分割的“种子”区域。 在markers中每一个像素要么被设置为初期的“种子值”要么被设置为“-1”表示边界。 算法会根据markers传入的轮廓作为种子(也就是所谓的注水点)对图像上其他的像素点根据分水岭算法规则进行判断并对每个像素点的区域归属进行划定直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为“-1”以做区分。
3. 分水岭算法图像分割实例
使用分水岭算法进行图像分割时基本的步骤为
通过形态学开运算对原始图像O去噪。通过腐蚀操作获取“确定背景B”。需要注意这里得到“原始图像-确定背景”即可。利用距离变换函数cv2.distanceTransform()对原始图像进行运算并对其进行阈值处理得到“确定前景F”。计算未知区域UN(UNO –B-F)。利用函数cv2.connectedComponents()对原始图像O进行标注。对函数cv2.connectedComponents()的标注结果进行修正。使用分水岭函数完成对图像的分割。
1-6是图像预处理只要是将图像中的未知区域标记为0已知区域标注为1、2、3…也就是标记种子区域。第7步是根据标注使用分水岭算法对图像进行分割。
示例
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(img.jpg)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
new_img rgb_img.copy()rst, thresh cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
kernel np.ones((3, 3), np.uint8)
opening cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations2)
sure_bg cv2.dilate(opening, kernel, iterations3)
dist_transform cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, sure_fg cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
sure_fg np.uint8(sure_fg)
unknown cv2.subtract(sure_bg, sure_fg)
ret, markers cv2.connectedComponents(sure_fg)
markers 1
markers[unknown 255] 0
markers cv2.watershed(new_img, markers)
new_img[markers -1] [0, 255, 0]plt.subplot(121)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(122)
plt.imshow(new_img)
plt.title(rst)
plt.axis(off)plt.show()二、交互式前景提取
经典的前景提取技术主要使用纹理(颜色)信息如魔术棒工具或根据边缘(对比度)信息如智能剪刀等完成。2004年微软研究院(剑桥)的Rother等人在论文GrabCut:Interactive Foreground Extraction Using Iterated Graph Cuts中提出了交互式前景提取技术。他们提出的算法仅需要做很少的交互操作就能够准确地提取出前景图像。
在开始提取前景时先用一个矩形框指定前景区域所在的大致位置范围然后不断迭代地分割直到达到最好的效果。 经过上述处理后提取前景的效果可能并不理想存在前景没有提取出来或者将背景提取为前景的情况此时需要用户干预提取过程。用户在原始图像的副本中(也可以是与原始图像大小相等的任意一幅图像)用白色标注要提取为前景的区域用黑色标注要作为背景的区域。然后将标注后的图像作为掩模让算法继续迭代提取前景从而得到最终结果。
例如对于下图的左图先用矩形框将要提取的前景 Lena 框出来再分别用白色和黑色对前景图像、背景图像进行标注。完成标注后使用交互式前景提取算法就会得到右图所示的结果图像。 下面我们来看GrabCut算法的具体实施过程。 将前景所在的大致位置使用矩形框标注出来。值得注意的是此时矩形框框出的仅仅是前景的大致位置其中既包含前景又包含背景所以该区域实际上是未确定区域。但是该区域以外的区域被认为是“确定背景”。 根据矩形框外部的“确定背景”数据来区分矩形框区域内的前景和背景。 用高斯混合模型(Gaussians Mixture Model,GMM)对前景和背景建模。GMM会根据用户的输入学习并创建新的像素分布。对未分类的像素(可能是背景也可能是前景)根据其与已知分类像素(前景和背景)的关系进行分类。 根据像素分布情况生成一幅图图中的节点就是各个像素点。除了像素点之外还有两个节点前景节点和背景节点。所有的前景像素都和前景节点相连所有的背景像素都和背景节点相连。每个像素连接到前景节点或背景节点的边的权重由像素是前景或背景的概率来决定。 图中的每个像素除了与前景节点或背景节点相连外彼此之间还存在着连接。两个像素连接的边的权重值由它们的相似性决定两个像素的颜色越接近边的权重值越大。 完成节点连接后需要解决的问题变成了一幅连通的图。在该图上根据各自边的权重关系进行切割将不同的点划分为前景节点和背景节点。 不断重复上述过程直至分类收敛为止。 OpenCV 的官网上有更详细的资料(http://www.cs.ru.ac.za/research/g02m1682/)读者有兴趣的话可以进一步学习。
在OpenCV中实现交互式前景提取的函数是cv2.grabCut()其语法格式为 mask,bgdModel,fgdModelcv2.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表示。 注意mask不仅是做为参数的使用的掩膜图像同时也是同时也是grabCut函数处理完后的结果掩膜图像我们会根据这个结果掩膜图像提取前景对象。 在最后使用模板提取前景时会将参数值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表示迭代模式。其可能的值与含义如表所示。 函数的返回值为mask,bgdModel,fgdModel
示例1使用GrabCut 算法提取图像的前景并观察提取效果。
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(../lena512color.tiff)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
mask np.zeros(img.shape[:2], dtypenp.uint8)
bgd_model np.zeros((1, 65), dtypenp.float64)
fgd_model np.zeros((1, 65), dtypenp.float64)
rect (50, 50, 500, 500)# 函数的返回值为mask,bgdModel,fgdModel
cv2.grabCut(img, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)
print(mask)
mask2 np.where((mask 2) | (mask 0), 0, 1).astype(uint8)rst img * mask2[:, :, np.newaxis]
rst cv2.cvtColor(rst, cv2.COLOR_BGR2RGB)plt.subplot(121)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(122)
plt.imshow(rst)
plt.title(rst)
plt.axis(off)plt.show() 可以看到在不使用掩模(掩模值都设置为默认值0时)函数 cv2.grabCut()的处理效果并不太好提取左图的前景时人物的帽子没有提取完整。对于有些图像也有可能将背景错误地提取出来。
为了得到完整的前景对象需要做一些改进。这里对原始图像进行标注**将需要保留的部分设置为白色将需要删除的背景设置为黑色。**以标记好的图像作为模板使用函数cv2.grabCut()完成前景的提取。 这个过程主要包含以下步骤
利用函数 cv2.grabCut()在 cv2.GC_INIT_WITH_RECT 模式下对图像进行初步的前景提取得到初步提取的结果图像og。主要是为了得到初步的mask。使用Windows系统自带的笔刷工具打开要提取前景的图像比如lena。使用白色笔刷在希望提取的前景区域做标记。使用黑色笔刷在希望删除的背景区域做标记。将当前设置好的lena图像另存为模板图像m0。将模板图像m0中的白色值和黑色值映射到模板m中。将模板图像m0中的白色值(像素值为255)映射为模板图像m中的确定前景(像素值为1)将模板图像m0中的黑色值(像素值为0)映射为模板图像m中的确定背景(像素值为0)。以模板图像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()的模板参数。
import cv2
import numpy as np
import matplotlib.pyplot as pltimg cv2.imread(lena512color.tiff)
rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 对前景对象进行初步提取获取初步mask
mask np.zeros(img.shape[:2], dtypenp.uint8)
bgd np.zeros((1, 65), dtypenp.float64)
fgd np.zeros((1, 65), dtypenp.float64)
rect (50, 50, 500, 500)
cv2.grabCut(img, mask, rect, bgd, fgd, 5, cv2.GC_INIT_WITH_RECT)# 读取模板根据模板设置得到的初始mask
mask2 cv2.imread(m.tiff)
rgb_mask2 cv2.cvtColor(mask2, cv2.COLOR_BGR2RGB)
gray_mask2 cv2.cvtColor(mask2, cv2.COLOR_BGR2GRAY)
mask[gray_mask2 0] 0
mask[gray_mask2 255] 1# 根据修改后的mask再次进行前景对象提取
cv2.grabCut(img, mask, None, bgd, fgd, 5, cv2.GC_INIT_WITH_MASK)
mask np.where((mask 2) | (mask 0), 0, 1).astype(uint8)# 根据得到的mask提取前景对象
new_img rgb_img.copy()
rst new_img * mask[:, :, np.newaxis]plt.subplot(131)
plt.imshow(rgb_img)
plt.title(img)
plt.axis(off)plt.subplot(132)
plt.imshow(rgb_mask2)
plt.title(m)
plt.axis(off)plt.subplot(133)
plt.imshow(rst)
plt.title(rst)
plt.axis(off)plt.show()