目录
1. 什么是分水岭算法?
分水岭算法(Watershed Algorithm) 是一种基于 拓扑形态学 的图像分割方法,适用于:
- 粘连目标的分割(如硬币、细胞、种子)。
- 边界模糊的目标分割(如医学图像、自然场景)。
核心思想
将灰度图像看作“地形图”:
- 亮度高的区域代表 山峰,亮度低的区域代表 山谷。
在山谷中注入水:
- 随着水面上升,不同的“水池”会相遇。
- 相遇处形成“坝” → 这条“坝”就是分割边界。
适用场景
✅ 目标粘连(如 硬币、细胞、果实、种子)。
✅ 目标边界模糊(如 医学图像、叶片分割)。
2. 分水岭算法的 OpenCV API
cv2.watershed(image, markers)
参数 | 说明 |
---|---|
image | 输入图像(3 通道 BGR)。 |
markers | 标记矩阵(单通道整数图像,前景/背景分区)。 |
返回 | 修改后的markers,其中 -1 代表分割边界。 |
其它 API:
API | 作用 |
---|---|
cv2.threshold() | 二值化图像(用于前景检测)。 |
cv2.distanceTransform() | 计算距离变换(用于前景提取)。 |
cv2.connectedComponents() | 计算连通区域(用于标记目标)。 |
cv2.morphologyEx() | 形态学操作(去噪、填充区域)。 |
3. OpenCV 代码实现(分水岭分割)
3.1 代码流程
- 图像预处理(灰度化 + 去噪)。
- 二值化(确定前景区域)。
- 形态学操作(去噪、增强前景)。
- 距离变换(计算前景区域的中心)。
- 标记前景、背景、未知区域。
- 应用分水岭算法,获取分割边界。
- 可视化结果。
3.2 代码实现
下面是使用 Python 和 OpenCV 库实现分水岭算法进行图像分割的示例代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
image = cv2.imread('D:\\resource\\filter\\coins.jpeg')
# 转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 进行高斯模糊以减少噪声
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 使用OTSU方法进行二值化
_, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 进行形态学开运算以去除小的噪声点
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)
_, 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)
# 标记连通区域
_, markers = cv2.connectedComponents(sure_fg)
# 所有标记加1,确保背景标记为1而不是0
markers = markers + 1
# 未知区域标记为0
markers[unknown == 255] = 0
# 使用分水岭算法进行分割
markers = cv2.watershed(image, markers)
image[markers == -1] = [0, 255, 0]
# 显示结果
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(markers, cmap='jet')
plt.title('Markers'), plt.xticks([]), plt.yticks([])
plt.show()
🔷代码说明:
- 读取图像并转换为灰度图像:使用cv2.imread读取图像,然后使用cv2.cvtColor将其转换为灰度图像。
- 高斯模糊和二值化:使用cv2.GaussianBlur进行高斯模糊以减少噪声,然后使用 Otsu's 方法进行二值化。
- 形态学开运算和膨胀操作:使用cv2.morphologyEx进行形态学开运算以去除小的噪声点,然后使用cv2.dilate进行膨胀操作得到背景区域。
- 距离变换和前景区域提取:使用cv2.distanceTransform进行距离变换,然后使用cv2.threshold提取前景区域。
- 找到未知区域:通过背景区域减去前景区域得到未知区域。
- 标记连通区域:使用cv2.connectedComponents标记连通区域,并将所有标记加 1,确保背景标记为 1 而不是 0。
- 使用分水岭算法进行分割:使用cv2.watershed进行分割,并将分割边界标记为 - 1。
- 显示结果:使用matplotlib显示分割后的图像和标记图。
🔷运行结果:
🔷注意事项:
分水岭算法容易受到噪声的影响,因此在进行分割之前需要进行适当的预处理,如高斯模糊和形态学操作。
4. 适用场景
应用场景 | 示例 |
---|---|
医学影像 | 细胞分割、病灶检测 |
工业检测 | 产品缺陷检测、物体计数 |
自然场景 | 叶片分割、果实分割 |
OCR 预处理 | 分割手写字符 |
5. 注意事项
前景必须清晰:
- 目标和背景 灰度差异明显,否则算法可能失败。
形态学处理:
- cv2.morphologyEx() 可以 去噪、增强前景。
- cv2.dilate() 确保背景区域足够大。
代码中 0.7 * dist_transform.max() 是经验值,可能需要调节:
- 过低会导致前景区域过小,目标可能丢失。
- 过高会导致分割过多,误分割目标。
适用于粘连目标:
- 粘连目标 → 分水岭算法更有效(如硬币、细胞)。
- 单一目标 → 其他方法更合适(如 Canny、K-means)。
6. 总结
分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。
分水岭算法适用于目标粘连、边界模糊的图像。
结合形态学操作、距离变换,可提升分割效果。