OpenCV 的分水岭图像分割算法
认识分水岭算法,并在 opencv 使用该算法
什么是分水岭算法?
- 任何一副灰度图都可以看作是拓扑平面,灰度值高的区域被看成山峰,灰度值低的区域看作山谷
- 向每个山谷灌不同颜色的水,随着水位的升高,不同山谷的水就会相遇,为了防止不同山谷的水汇合,在水汇合的地方构筑堤坝
- 不停地灌水,不停地构筑堤坝,直至所有山峰被水淹没,我们构筑的堤坝就是对图像的分割
如何解决分水岭算法的过分割现象?
- 使用传统的分水岭算法时,由于噪声的存在,图像上存在很多很小的局部极值点,会产生过分割的现象
- 对图像进行高斯平滑操作,抹除很多小的最小值,这些小分区就会合并
- 采用基于标记 (mark) 的图像分水岭算法,即不从最小值开始增长,可以将相对较高的灰度值像素作为起始点(需要用户手动标记),从标记处开始进行淹没,则很多小区域都会被合并为一个区域这也是 OpenCV 所采用的方法
基于 markers 的分水岭算法原理?
- 手动标记后得到确定的前景、确定的背景、不确定区域,通过确定的前景及背景的水涨过程,逐渐去掉不确定区域,实现前景和背景的分割
- 确定的前景、确定的背景、不确定区域存储在和原图大小一致的二值图 (markers) 上,即用大于 0 标记确定为前景或对象区域;(2) 用另一种颜色(大于 0)标记确定为背景或非对象区域;(3) 用 0 标记不确定任何内容的区域
- 运行分水岭算法,对象的边界将具有 - 1 的值
基于 markers 的分水岭算法步骤?
- 首先构造分水岭算法需要 markers,然后使用分水岭算法进行分割
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36# ---------------------1.图像预处理-----------------------------
# 转为灰度图
gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 自适应二值化
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# ---------------------2.构造种子-----------------------------
# 开运算
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations =2)
# 膨胀前景,剩下是确定的背景
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# 缩小前景,得到确定的前景
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
dist_output = cv2.normalize(dist_transform, 0, 1.0, cv2.NORM_MINMAX) #归一化
ret, sure_fg = cv2.threshold(dist_output,0.7*dist_output.max(),255,0)
# 确定的背景-确定的前景,剩下部分是未知区域
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
show_images([sure_bg,sure_fg,unknown],closize=3)
# 连通区域标记
ret, markers = cv2.connectedComponents(sure_fg)
#cv2.imwrite("markers.png",markers)
# markers所有值+1,确保值从1开始
markers = markers+1
# 使用0值标记背景区域
markers[unknown==255]=0
# ---------------------3.使用分水岭算法-----------------------------
markers = cv2.watershed(image,markers)
OpenCV 分水岭算法的缺点?
- 标记一张图片后,无法将用法用于所有的图片
- 图片必须有背景,并且有标注
- 图片内所有连通的前景必须有标注,否则分水岭算法按背景处理