文章简介

本文将深入解析OpenCV中分水岭算法的原理与实践,从地形学类比到标记法改进,详细讲解算法流程、Mark图像创建、轮廓提取等关键步骤,并通过Python代码实现自动图像分割,对比漫水填充算法的差异,帮助读者掌握这一高效的图像区域分割技术。
在这里插入图片描述

一、分水岭算法的核心原理:地形学视角的图像分割

任何灰度图像都可以类比为一座地形地貌——像素的灰度值对应“海拔高度”:

  • 灰度0 → 地面(最低处);
  • 灰度最大值 → 山峰(最高处)。

算法的核心是模拟水流汇聚过程,将图像划分为不同“盆地”(区域):
请添加图片描述

1. 三类关键地形点

  • 局部最小值点:盆地的最低点,滴入的水会最终汇聚于此(可能是一个平面,所有点都是最小值)。
  • 盆地内点:水位上升时,水会流向最近的局部最小值点。
  • 分水岭线:两个盆地的交界点,水会等概率流向两个盆地(分割边界)。

2. 原始算法流程

  1. 找最小值:识别图像中所有局部最小值点(注水起点)。
  2. 注水过程:从最小值点开始“注水”,水位逐渐上升(对应梯度图的处理)。
  3. 建坝分割:当两个盆地的水即将交汇时,在交界线(分水岭)建坝。
  4. 结果输出:所有大坝构成分割边界,将图像划分为不同区域。
    请添加图片描述

二、OpenCV的改进:标记法解决过度分割问题

原始算法易受噪声影响,产生大量细小区域(过度分割)。OpenCV采用基于标记的分水岭算法:通过先验知识(手动或自动标记)指定“注水区域”,避免噪声点的干扰。

1. Mark图像的核心要求

  • 数据类型:必须是32位有符号整数(CV_32S),因为算法内部用-1表示分水岭线。
  • 标记规则
    • 未标记区域 → 0;
    • 不同目标区域 → 非零值(1-255),每个区域一个唯一值;
  • 边界处理:OpenCV会自动将图像最外圈设为-1(边界),因此手动标记时需避开最外圈。

2. Mark图像的创建方式

(1)手动标记:指定关键区域

例如,标记“背景”“塔”“树”三个区域:

import cv2
import numpy as np

# 读取图像
img = cv2.imread('122.png')
h, w = img.shape[:2]

# 创建Mark图像(初始为0)
mark = np.zeros((h, w), dtype=np.uint8)

# 标记背景(倒数第二圈,避免被算法设为边界)
cv2.rectangle(mark, (1, 1), (w-2, h-2), 255, 1)
# 标记塔(中心区域,值为64)
cv2.rectangle(mark, (w//2-10, h//2-10), (w//2+10, h//2+10), 64, 10)
# 标记树(特定位置,值为128)
cv2.rectangle(mark, (64, 284), (68, 300), 128, 5)

# 转换为32位有符号整数(算法要求)
mark = mark.astype(np.int32)

# 运行分水岭算法
cv2.watershed(img, mark)

# 查看结果(-1转为0,方便显示)
mark_8u = cv2.convertScaleAbs(mark)
cv2.imshow('Mark Result', mark_8u)
cv2.waitKey(0)

(2)自动标记:轮廓提取法

通过findContours提取图像轮廓,自动为每个轮廓分配唯一标记值(最常用的方法):

import cv2
import numpy as np
from random import randint

def random_color(value):
    """生成随机颜色(用于区域填充)"""
    value = value % 255
    return (randint(0, value), randint(0, value), randint(0, value))

def auto_watershed(image_path):
    # 1. 读取并预处理图像
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (3, 3), 2)  # 高斯模糊去噪
    edges = cv2.Canny(blur, 80, 140)         # Canny边缘检测

    # 2. 查找轮廓(所有轮廓,包括层级)
    contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # 3. 创建Mark图像(CV_32S)
    h, w = img.shape[:2]
    marks = np.zeros((h, w), dtype=np.int32)
    comp_count = 0  # 轮廓计数器

    # 为每个轮廓分配唯一标记值
    for i in range(len(contours)):
        cv2.drawContours(marks, contours, i, comp_count + 1, 1, hierarchy=hierarchy)
        comp_count += 1

    # 4. 运行分水岭算法
    cv2.watershed(img, marks)

    # 5. 结果可视化:颜色填充
    result = np.zeros_like(img)
    for i in range(h):
        for j in range(w):
            val = marks[i, j]
            if val == -1:
                result[i, j] = (255, 255, 255)  # 分水岭线→白色
            else:
                result[i, j] = random_color(val) # 区域→随机颜色

    # 6. 融合原始图像与结果(增强显示)
    fused = cv2.addWeighted(img, 0.4, result, 0.6, 0)

    # 显示结果
    cv2.imshow('Contours', edges)
    cv2.imshow('Marks', cv2.convertScaleAbs(marks))
    cv2.imshow('Segmentation', result)
    cv2.imshow('Fused Result', fused)
    cv2.waitKey(0)

# 测试
auto_watershed('122.png')

三、分水岭与漫水填充的核心区别

特征 分水岭算法 漫水填充
**种子点数量** 多个(手动/自动标记) 单个(仅种子点)
**分割逻辑** 区域生长+边界大坝 连通区域灰度差阈值
**适用场景** 复杂多区域分割 简单单区域填充

四、关键公式与算法细节

1. 像素高度值计算

在算法初始化阶段,需计算每个像素的“高度值”(用于队列排序),公式为:height(x,y)=min⁡(max⁡(∣R−RL∣,∣G−GL∣,∣B−BL∣),max⁡(∣R−RT∣,∣G−GT∣,∣B−BT∣),max⁡(∣R−RR∣,∣G−GR∣,∣B−BR∣),max⁡(∣R−RB∣,∣G−GB∣,∣B−BB∣)) \text{height}(x,y) = \min\left( \max(|\text{R}-\text{R}_L|, |\text{G}-\text{G}_L|, |\text{B}-\text{B}_L|), \max(|\text{R}-\text{R}_T|, |\text{G}-\text{G}_T|, |\text{B}-\text{B}_T|), \max(|\text{R}-\text{R}_R|, |\text{G}-\text{G}_R|, |\text{B}-\text{B}_R|), \max(|\text{R}-\text{R}_B|, |\text{G}-\text{G}_B|, |\text{B}-\text{B}_B|) \right) height(x,y)=min(max(RRL,GGL,BBL),max(RRT,GGT,BBT),max(RRR,GGR,BBR),max(RRB,GGB,BBB))其中,R/G/B\text{R/G/B}R/G/B 是当前像素的三通道值,RL/GL/BL\text{R}_L/\text{G}_L/\text{B}_LRL/GL/BL 是左侧像素的对应值(L/R/T/BL/R/T/BL/R/T/B 分别代表左/右/上/下)。

获取更多资料

我给大家整理了一套全网最全的人工智能学习资料(1.5T),包括:机器学习,深度学习,大模型,CV方向,NLP方向,kaggle大赛,实战项目、自动驾驶,AI就业等免费获取
请添加图片描述
请添加图片描述

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐