白话文讲计算机视觉-第七讲-GrabCut算法

大家好,我是小木,我又回来了,今天我们要讲一些算法,这些算法可以把物体和它的背景分离。这些算法最常用的有两种,一种叫做GrabCut算法,另外一种叫做分水岭算法。

我们今天不讲推导过程,原因是本人还没有看明白,所以第九节课给大家普及。这节课我只讲东西是怎么用的。

这节课先说一下GrabCut算法,这个算法是微软发明的一种算法,目的是我们用手指在手机屏幕上面画一个轮廓,然后剩余的部分就被当作背景干掉。微软能够发明出这么NB的东西,我只能说一句,美利坚人类的希望啊。像我们国家,某企鹅公司只会山寨,还没事儿和什么360、今日头条掐架。至于其它的嘛,啥也不是。人家领先你们最少40年,我们就不能往前面赶赶么,赶不上尽力了也行啊!

好了,废话不多说,我来BB一下这个东西是怎么用的吧!

首先我们有一张图:

 

额,又是小熊,为啥博主总是用它呢?很简单,我喜欢这个娃娃啊!我们想要把小熊后边的背景都给干掉,那么怎么办呢?

第一种方法叫做矩形框选法:

首先我们在这个图片上面把前景部分用矩形给圈起来:

 

接下来,我们假定在红色框框里面的东西一部分可能是前景、一部分可能是背景。而框框外面的物体全部都是背景。

我们下一步需要记录一下我们这个框框左上角和右下角的X,Y坐标。

下一步,我们建立一个掩模,其实就类似于PS中的蒙版。这个模版的大小和我们的图片一样大。这个模版我们假定里面开始时候都是0,指的是全都是背景。在计算完毕后,如果是背景,那么仍然为0,如果是前景,那么为1。如果可能是背景那么为2,可能是前景,那么为3。在OPENCV当中,每个数字代表如下:

GCD_BGD=0),背景;

GCD_FGD=1),前景;

GCD_PR_BGD=2),可能是背景;

GCD_PR_FGD=3),可能是前景。

接下来我们定义两个参数,叫做前景模型和背景模型,它们是一个矩阵,行数最大是1,列数最大是65=13*5,一般都是选择最大。

定义之后,我们把矩形框框坐标、前景、背景模型、已经蒙版都导入到GrabCut算法当中进行计算。在这个算法当中有一个参数叫做迭代次数,由我们自己来定,如果设定的小了,效果不好,如果设定大了,浪费时间,因为调整这个参数也比较重要。

我们在计算完成之后,我们会得到一个新的蒙版,这个蒙版里面包含了0~34种数据,而我们按照上述的方法进行计算,算法会判断哪些可能是前景,哪些可能是背景。而不会出现一定是前景或者一定是背景的情况,因此数字只能有2~3两个。因此,我们令蒙版中等于2的数据为0,也就是可能是背景的地方为0,然后令31,也就是前景的地方为1。接下来我们用原始的图片和蒙版做点对点乘积,也就是图片矩阵的每一个元素与蒙版中一一对应位置的元素相乘。然后我们就会把背景的地方全都转化为0,也就是黑色了。而前景部分乘以1,不会变。

接着把我们乘积得到的结果作为图片显示到窗口中就OK了。

运用OPENCV来进行背景分离,代码如下:

#导入类库

import numpy as np

import cv2

#画图类库,很好用,不用自己从头编写了

from matplotlib import pyplot as plt

 

#导入图像(小熊)

img = cv2.imread('D:/xiaomu/opencv7-1.png')

#建立一个和img图像一样大的蒙版

mask = np.zeros(img.shape[:2],np.uint8)

#画一个矩形框框,选出前景物体(小熊)

rect = (50,10,250,440)

#建立背景模型和前景模型,大小为1*65

bgdModel = np.zeros((1,65),np.float64)

fgdModel = np.zeros((1,65),np.float64)

 

#把上面的数据导入GrabCut算法中,进行计算,

#该方法参数为:1.原始图像 2.蒙版 3.矩形框框 4.背景模型 5.前景模型 6.迭代次数 7.方法选择(矩形框选法)

cv2.grabCut(img.copy(),mask,rect,bgdModel,fgdModel,10,cv2.GC_INIT_WITH_RECT)

 

#把2变为0,把3变为1

mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')

#将蒙版与原图做点对点乘积

img = img*mask2[:,:,np.newaxis]

 

#绘制出分离背景后的图像

plt.subplot(121), plt.imshow(img)

plt.title("grabcut"), plt.xticks([]), plt.yticks([])

#绘制出原图

plt.subplot(122), plt.imshow(cv2.cvtColor(cv2.imread('D:/xiaomu/opencv7-1.png'), cv2.COLOR_BGR2RGB))

plt.title("original"), plt.xticks([]), plt.yticks([])

#在窗口中显示图像

plt.show()

#保存图像

cv2.imwrite('D:/xiaomu/opencv7-2.png',img)

我们运行后的结果如下所示:

 

很显然,我们已经把背景全部都给干掉了。但是这个方法中我们发现了一个不好的地方也就是我们去掉背景后,还有残余的背景,那么我们怎么办呢?于是我们引入了第二种GrabCut的算法,叫做蒙版算法。

这种算法中,我们不是用矩形框框把某个前景物体圈起来了,而是我们画一个自定义的轮廓,接着我们上面的结果再次进行背景分离:

我们这次的图片是:

 

也就是我们上次矩形框框分离后的图图。

接着我们保存图片到本地磁盘,并用PS在矩形图片上面建立一个新图层。

在新图层中,我们用白色的刷子,把那些本来应该是前景,但是却没算成前景的地方给标记出来,然后把应该是背景的地方,但是却搞错的用黑色标记,剩下不改变的地方用灰色标记。

最后我们的,我们得到的蒙版为:

 

在蒙版中,我为了说明不同颜色的用途,所以我才随意画了一个白色的圈圈。

然后我们把这张图导入到OPENCV中作为蒙版,导入OPENCV中进行运算:

#导入类库

import numpy as np

import cv2

#画图类库,很好用,不用自己从头编写了

from matplotlib import pyplot as plt

 

#导入图像(小熊)

img = cv2.imread('D:/xiaomu/opencv7-1.png')

#建立一个和img图像一样大的蒙版

mask = np.zeros(img.shape[:2],np.uint8)

#画一个矩形框框,选出前景物体(小熊)

rect = (50,10,250,440)

#建立背景模型和前景模型,大小为1*65

bgdModel = np.zeros((1,65),np.float64)

fgdModel = np.zeros((1,65),np.float64)

 

#矩形框选法

#把上面的数据导入GrabCut算法中,进行计算,

#该方法参数为:1.原始图像 2.蒙版 3.矩形框框 4.背景模型 5.前景模型 6.迭代次数 7.方法选择(矩形框选法)

cv2.grabCut(img.copy(),mask,rect,bgdModel,fgdModel,10,cv2.GC_INIT_WITH_RECT)

#把2变为0,把3变为1

mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')

#将蒙版与原图做点对点乘积

img = img*mask2[:,:,np.newaxis]

 

#非矩形框,蒙版法再处理

img2 = cv2.imread('D:/xiaomu/opencv7-1.png')

#导入蒙版

img3 = cv2.imread('D:/xiaomu/opencv7-3.png',0)

#把蒙版中白色地方置为1,作为确定前景。黑色地方置为0,作为确定背景

mask[img3 == 0] = 0

mask[img3 == 255] = 1

 

#把上面的数据导入GrabCut算法中,进行计算,

#该方法参数为:1.原始图像 2.蒙版 3.矩形框框(无) 4.背景模型 5.前景模型 6.迭代次数 7.方法选择(蒙版法)

cv2.grabCut(img2,mask,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK)

 

#把2变为0,把3变为1

mask3 = np.where((mask==2)|(mask==0),0,1).astype('uint8')

#将蒙版与原图做点对点乘积

img2 = img2*mask3[:,:,np.newaxis]

 

#绘制出蒙版法处理后的图像

plt.subplot(121), plt.imshow(img2)

plt.title("grabcut-mask"), plt.xticks([]), plt.yticks([])

#绘制出矩形框法处理后的图像

plt.subplot(122), plt.imshow(img)

plt.title("grabcut-rect"), plt.xticks([]), plt.yticks([])

#在窗口中显示图像

plt.show()

 

cv2.waitKey()

cv2.destroyAllWindows()

我们运行后的结果如下所示:

 

我们可以看出,我们把白色的地方都变成了前景(前面的小圆),黑色的地方成背景(小熊与背景分开的更好了)

这样我们的第一种算法就讲解完成了。

第二种算法我们做的是分水岭算法,下节课我们继续讲解。

———————————————

如果对我的课程感兴趣的话,欢迎关注小木希望学园-微信公众号: 

mutianwei521

也可以扫描二维码哦!


猜你喜欢

转载自blog.csdn.net/u013631121/article/details/80549079