分水岭算法:基于拓扑地貌的边界提取
核心原理
分水岭算法将图像视为拓扑地貌,灰度值代表海拔高度。通过模拟浸水过程:
- 局部极小值:对应集水盆(区域内部)。
- 分水岭线:集水盆之间的山脊(区域边界)。
- 淹没过程:从最低点开始注水,水位上升时在不同集水盆汇合处构建堤坝(分水岭)。
关键步骤
- 预处理:
- 梯度计算:使用Sobel、Canny等算子提取边缘(如
gradient = cv2.Laplacian(image, cv2.CV_64F)
)。 - 降噪:高斯模糊或形态学开运算去除微小噪声。
- 梯度计算:使用Sobel、Canny等算子提取边缘(如
- 标记控制:
- 前景标记:通过阈值分割或距离变换提取确定区域(如
cv2.distanceTransform
)。 - 背景标记:膨胀操作扩展背景区域。
- 前景标记:通过阈值分割或距离变换提取确定区域(如
- 分水岭变换:使用
cv2.watershed
函数,标记区域边界为-1
。
import cv2 # OpenCV库,用于图像处理
import numpy as np # NumPy库,用于数值计算
def watershed_segmentation(image):
"""
使用分水岭算法对输入图像进行分割,并标记边界
:param image: 输入的BGR彩色图像
:return: 标记了分水岭边界的图像(边界显示为红色)
"""
# 1. 转换为灰度图像
# 分水岭通常基于灰度图像的梯度计算,因此先将彩色图像转为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 2. 阈值分割(二值化)
# 使用Otsu算法自动确定阈值,并反转二值图像(背景为白色,前景为黑色)
# cv2.THRESH_BINARY_INV:反转二值化,使前景为白色(255),背景为黑色(0)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 3. 形态学开运算(去噪)
# 定义3x3的全1结构元素(核)
kernel = np.ones((3, 3), np.uint8)
# 开运算 = 先腐蚀后膨胀,用于去除小的噪声点(如孤立白点)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# 4. 确定背景区域
# 对开运算结果进行膨胀操作,扩展背景区域,确保背景完全覆盖噪声
sure_bg = cv2.dilate(opening, kernel, iterations=3)
# 5. 距离变换(提取确定的前景)
# 计算每个前景像素到最近背景像素的距离(欧式距离,L2范数)
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
# 对距离变换结果进行阈值化,提取确定的前景区域(距离较大的像素)
ret, sure_fg = cv2.threshold(dist_transform, 0.5 * dist_transform.max(), 255, 0)
# 将结果转换为8位无符号整数(0-255)
sure_fg = np.uint8(sure_fg)
# 6. 确定未知区域(边界区域)
# 未知区域 = 背景区域 - 确定的前景区域
unknown = cv2.subtract(sure_bg, sure_fg)
# 7. 标记连通区域
# 对确定的前景区域进行连通组件标记(每个连通区域分配一个唯一标签)
ret, markers = cv2.connectedComponents(sure_fg)
# 分水岭算法要求标记从1开始(0表示未知区域),因此对所有标记+1
markers = markers + 1
# 将未知区域(unknown)的标记设为0
markers[unknown == 255] = 0
# 8. 应用分水岭算法
# 输入原始图像和标记矩阵,分水岭算法会修改标记矩阵
# 边界区域的标记会被设为-1
markers = cv2.watershed(image, markers)
# 9. 标记边界(可视化)
# 将分水岭边界(markers == -1)在原图上标记为蓝色
image[markers == -1] = [255, 0, 0] # BGR格式的蓝色
return image
# 主程序
if __name__ == "__main__":
# 读取输入图像(确保路径正确)
image = cv2.imread('input.jpg')
# 调用分水岭分割函数
result = watershed_segmentation(image)
# 保存结果图像
cv2.imwrite('output.jpg', result)
分水岭算法主要是用于标记前景和背景的分界线,最终的处理结果如下: