OpenCV 中的分水岭算法是一种基于拓扑理论的图像分割方法,它把图像的灰度值看作地形高度,通过“浸水”过程形成分水岭(边界)。下面我会详细介绍它的原理、传统方法的问题,以及最常用的标记控制分水岭算法的完整实现。
代码示例如下:
#分水岭分割图像
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1. 读取图像,转为灰度并去噪
# img = cv2.imread('./images/coins.jpg')
img = cv2.imread('./images/water_coins.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5)
# 2. 二值化(Otsu 自动阈值)
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 3. 去除小噪声(可选)
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2)
# 4. 确定背景(膨胀后的区域)
sure_bg = cv2.dilate(opening, kernel, iterations=3)
# 5. 确定前景(距离变换 + 阈值)
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)
# 6. 找到未知区域(背景 - 前景)
unknown = cv2.subtract(sure_bg, sure_fg)
# 7. 为前景创建标记(连通组件编号,背景为 0,物体从 1 开始)
ret, markers = cv2.connectedComponents(sure_fg)
# 所有标记加 1,让背景从 1 开始(而不是 0)
markers = markers + 1
# 未知区域标记为 0
markers[unknown == 255] = 0
# 8. 应用分水岭算法(图像必须为彩色,OpenCV 要求 3 通道)
markers = cv2.watershed(img, markers)
# 9. 在原图上绘制边界(红色)
img[markers == -1] = [0, 0, 255]
# 显示结果
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(markers, cmap='jet')
plt.title('Markers'), plt.xticks([]), plt.yticks([])
plt.show()
全部评论