白话文讲计算机视觉-第八讲-分水岭算法


这节课小木给大家来介绍一下啥叫分水岭算法。书上写着,算法叫做分水岭是因为它里面有水的概念。我们把图像的低密度区域想象成山谷,图像高密度区域我们想象成山峰。接下来我们往山谷里面注水,然后这些水在山峰之间开始汇聚,等一开始汇聚,我们就用一个水坝给阻挡了,阻挡的东西就叫做分割线。我们最后把所有水坝挑出来,作为图像分割的依据。具体的推导过程我下几节课会给大家讲解,这节课还是一样,教的是怎么用。

首先,我们导入一张图片:

 

1

还是小熊。如果大家觉得烦了的话,不要着急,因为我们讲到人脸识别的时候,我会换成本人的帅照的,哈哈哈哈哈哈!

接下来我们把图片转换为灰度图:

 

2

然后我们利用大津算法把灰度图二值化处理。这里简单说一下啥叫大津算法。它有一个优化函数,然后我们从1这个数字开始,把大于1的分为一组,小于等于1的分为另一组。我们把所有组数据带入优化函数中,得到一个数据。然后我们把1+1=2,然后把大于2的分为1组,其余的分为另一组,再算得到一个优化数据。最后我们会得到254组优化数据,我们看一眼哪个值最优就选哪个值作为二值化的阈值。

 

3

我们二值化之后,我们进行一下先开后闭运算来去除噪声。

 

4

之后,我们膨胀操作,提取部分的背景区域

 

5

背景提取之后,我们还有前景区域。我们运用距离变换法莱提取。在我们腐蚀膨胀后的图3中,越是远离背景区域的边界点越可能是前景。我们应用一个阈值来进行选择,一般选取CV_DIST_L2,也就是欧氏距离(直角坐标点之间的距离),还有一个参数是蒙版选择,数值为3,也就是一个,也就是一个3*3的蒙版。具体的算法细节流程我在这里面不介绍,等下次课统一介绍,以免大家看着懵B。我们把腐蚀膨胀得到的图片,也就是图3,带入到距离变换算法中,计算出可能的前景。

  

6

在前后景的计算完毕后,我们看图5和图6会发现,背景和前景有好多重合的部分,那么我们需要去除重合的部分。方法是用背景-前景。就是把背景的矩阵里面,既在背景中,又在前景中的地方,也就是它们的交集作为未知的区域。这样背景中的重合部分就被去掉了。最后区分出来的背景和前景如下图所示。

 

7

因为前景部分有些是相连的有些是不相连的,所以我们设定刚才我们说的水坝,把所有的前景图片给分割开来,并且将前景的值设定为-1

接着我们把背景值设定为1,最后把它们的交集设定为0

设定之后,我们就开始注水了。往前景注水,也就是为-1的地方注水。水满了的区域就是我们的前景了。

代码如下:

#导入类库

import numpy as np

import cv2

from matplotlib import pyplot as plt

 

#导入图片

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

#转换为灰度图

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#二值化处理

ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

# 去除噪声

kernel = np.ones((5,5))

opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel)

opening=cv2.morphologyEx(opening,cv2.MORPH_CLOSE,kernel)

 

#提取部分背景区域

sure_bg = cv2.dilate(opening,kernel,iterations=5)

 

#提取部分前景区域

dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,3)

ret, sure_fg = cv2.threshold(dist_transform,0.1*dist_transform.max(),255,0)

cv2.imshow("orgin",sure_fg)

 

#找到前景背景的交集

sure_fg = np.uint8(sure_fg)

unknown = cv2.subtract(sure_bg,sure_fg)

 

#画水坝

ret, markers = cv2.connectedComponents(sure_fg)

#cv2.imshow("orgin",markers)

 

#令背景为1

markers = markers+1

 

#令未知区域为0

markers[unknown==255] = 0

 

#注水

markers = cv2.watershed(img,markers)

img[markers == -1] = [255,0,0]

 

#显示图像

plt.imshow(img)

plt.show()

 

#等待按键退出

cv2.waitKey()

cv2.destroyAllWindows()

结果如下所示:


被红色线圈包裹的地方就是前景,我们这张图用分水岭算法的话,需要调整的参数太多了,很难实现,所以用GRABCUT算法才是解决问题的最好算法。


下一节我们讲讲这两种方法是如何实现的。

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

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

mutianwei521

也可以扫描二维码哦!


猜你喜欢

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