第17篇:图像分割——从阈值到分水岭算法

系列导语:本文是"有趣的图像处理"系列第17篇。边缘检测找到的是"边界线",形态学操作整理的是"形状";而图像分割要解决的问题更根本:把图像划分为若干有意义的区域,每个区域内部的像素在某种属性(亮度、颜色、纹理)上具有一致性。本篇从最简单的全局阈值出发,逐步深入到自适应阈值和分水岭算法。

[← 第16篇:数学形态学——膨胀、腐蚀与开闭运算] | [第18篇:Perlin 噪声——自然感的数学来源 →] | [系列目录]


导语

图像分割是计算机视觉中最基础也最困难的问题之一。难在哪里?同一物体的不同部分亮度可能差异很大(光照不均);不同物体的像素值可能非常接近(颜色相似);边界往往模糊而不是一条清晰的线。

没有"万能"的分割算法——每种方法都有自己的假设前提:

  • 阈值方法:假设目标和背景的灰度值能被一个数值分开
  • 自适应阈值:假设局部区域内目标和背景可以被分开,但全局阈值不适用
  • 分水岭算法:把梯度图看作地形,用"淹水"模型找到分水岭线

本文将带你理解这三类方法的数学原理和设计动机。


一、全局阈值——最简单的分割

1.1 原理

最直接的分割思路:选一个灰度值 T T T,大于 T T T 的像素归为前景(白色),小于等于 T T T 的归为背景(黑色):

I out ( x , y ) = { 255 I ( x , y ) > T 0 I ( x , y ) ≤ T I_{\text{out}}(x,y) = \begin{cases} 255 & I(x,y) > T \\ 0 & I(x,y) \leq T \end{cases} Iout(x,y)={2550I(x,y)>TI(x,y)T

问题转化为:如何选择最优的 T T T

1.2 手动阈值的局限

固定阈值 T T T 在以下情况会失败:

  • 光照变化:同一物体在不同光照下灰度值不同
  • 不均匀背景:背景亮度随位置变化
  • 直方图没有明显双峰(目标与背景灰度分布重叠)

1.3 伪代码

函数 全局阈值(图像 I, 阈值 T):
    对每个像素 (x, y):
        若 I[y,x] > T:
            输出[y,x] = 255
        否则:
            输出[y,x] = 0
    返回 输出

在这里插入图片描述
左:原图 / 右:固定阈值(T=128)。亮于128的像素变为白色,暗于128的变为黑色,花朵高光和背景亮叶被归为前景,整体是简单的二值化结果。


二、Otsu 阈值——最优全局分割

2.1 问题形式化

Otsu(1979)把"最优阈值"定义为:使前景和背景两个类的类间方差最大的那个 T T T

类间方差越大,说明两类的分离程度越高,阈值选得越准。

2.2 推导

设灰度值为 v v v 的像素占总像素数的比例为 p ( v ) p(v) p(v)(即归一化直方图)。

对阈值 T T T,将所有像素分为两类:

  • 类1(背景):灰度 ≤ T \leq T T,概率 ω 0 = ∑ v = 0 T p ( v ) \omega_0 = \sum_{v=0}^{T} p(v) ω0=v=0Tp(v),均值 μ 0 \mu_0 μ0
  • 类2(前景):灰度 > T > T >T,概率 ω 1 = 1 − ω 0 \omega_1 = 1 - \omega_0 ω1=1ω0,均值 μ 1 \mu_1 μ1

类间方差

σ B 2 ( T ) = ω 0 ω 1 ( μ 0 − μ 1 ) 2 \sigma_B^2(T) = \omega_0 \omega_1 (\mu_0 - \mu_1)^2 σB2(T)=ω0ω1(μ0μ1)2

Otsu 最优阈值

T ∗ = arg ⁡ max ⁡ T σ B 2 ( T ) T^* = \arg\max_T \sigma_B^2(T) T=argTmaxσB2(T)

遍历所有可能的 T ∈ [ 0 , 255 ] T \in [0, 255] T[0,255],找使 σ B 2 \sigma_B^2 σB2 最大的那个。

2.3 伪代码

函数 Otsu阈值(图像 I):
    # 计算归一化直方图
    hist = 统计每个灰度值的像素数 / 总像素数
    
    best_T, best_var = 0, 0
    
    对 T ∈ [0, 255]:
        ω0 = sum(hist[0..T])
        ω1 = 1 - ω0
        若 ω0 == 0 或 ω1 == 0: 跳过
        
        μ0 = sum(v × hist[v] for v in [0..T]) / ω0
        μ1 = sum(v × hist[v] for v in [T+1..255]) / ω1
        
        σ² = ω0 × ω1 × (μ0 - μ1)²
        
        若 σ² > best_var:
            best_var = σ²
            best_T   = T
    
    返回 全局阈值(I, best_T)

计算复杂度:O(256),与图像大小无关,极快。

在这里插入图片描述
左:原图 / 右:Otsu 自动阈值。算法自动找到类间方差最大的阈值(约 T=85),花朵主体被较完整地提取为白色,背景暗部为黑色,无需手动指定阈值。


三、自适应阈值——局部分割

3.1 光照不均的问题

全局阈值(包括 Otsu)假设整张图有一个统一的最优 T T T。但当图像光照不均匀时,左边亮区的背景可能比右边暗区的前景更亮,导致一个全局 T T T 无法同时分割两个区域。

自适应阈值的思路:对每个像素,用其局部邻域计算一个局部阈值,而不是用全局值。

3.2 局部阈值计算

对每个像素 ( x , y ) (x,y) (x,y),在其 W × W W \times W W×W 邻域内计算局部统计量,作为该像素的阈值:

T ( x , y ) = μ local ( x , y ) − C T(x,y) = \mu_{\text{local}}(x,y) - C T(x,y)=μlocal(x,y)C

其中:

  • μ local \mu_{\text{local}} μlocal:局部均值(均值自适应)或局部高斯加权均值(高斯自适应)
  • C C C:偏移常数,补偿前景通常比背景更亮/更暗的先验

窗口大小 W W W 的选择:

  • W W W 太小:对噪声敏感,细节区域阈值不准
  • W W W 太大:失去局部性,退化为全局阈值

3.3 伪代码

函数 自适应阈值(图像 I, 窗口大小 W, 偏移 C):
    # 计算局部均值图(高斯加权)
    μ_local = 高斯模糊(I, σ = W/6)
    
    # 局部阈值 = 局部均值 - 偏移
    T_map = μ_local - C
    
    # 逐像素二值化
    对每个像素 (x, y):
        若 I[y,x] > T_map[y,x]:
            输出[y,x] = 255
        否则:
            输出[y,x] = 0
    
    返回 输出

在这里插入图片描述

左:原图 / 右:高斯自适应阈值(窗口=31,C=5)。局部明暗变化被自动补偿,花朵纹理和叶片细节均被提取,但背景中也出现了更多细节响应,这是局部阈值"过度细化"的典型表现。


四、分水岭算法——基于地形的分割

4.1 地形模型的直觉

把梯度幅值图想象成地形图:

  • 梯度大的地方(边缘)= 山脊
  • 梯度小的地方(均匀区域)= 山谷

从每个局部最低点开始向上注水,水位不断上升。当来自两个不同山谷的水即将汇合时,在汇合处筑一道"坝"——这道坝就是分割边界,即分水岭(Watershed)

4.2 标记分水岭(Marker-based Watershed)

原始分水岭对噪声极敏感(每个局部极小值都会成为一个区域,产生过分割)。实用的标记分水岭引入人工标记:

  1. 确定前景标记:已知肯定属于目标内部的点(如距离变换的局部最大值)
  2. 确定背景标记:已知肯定属于背景的点
  3. 未知区域:前景和背景标记的补集
  4. 注水:从标记点出发注水,水流到未知区域,以分水岭线为边界

4.3 基于距离变换的标记生成

函数 标记分水岭(图像 I):
    gray    = 灰度化(I)
    
    # 步骤1:获取粗略二值图
    T_otsu  = Otsu阈值(gray)
    binary  = T_otsu > 0
    
    # 步骤2:形态学开运算去噪
    opened  = 开运算(binary, 3×3核, 迭代2次)
    
    # 步骤3:确定背景(膨胀后的区域)
    sure_bg = 膨胀(opened, 3×3核, 迭代3次)
    
    # 步骤4:距离变换 + 阈值得前景标记
    dist    = 距离变换(opened)            # 每个前景像素到最近背景的距离
    sure_fg = dist > 0.5 × max(dist)     # 距离最大的点=目标中心
    
    # 步骤5:未知区域
    unknown = sure_bg - sure_fg
    
    # 步骤6:连通分量标记
    markers = 连通分量标记(sure_fg)      # 每个前景区域一个标签
    markers[unknown] = 0                  # 未知区域标为0
    
    # 步骤7:分水岭
    结果 = cv2.watershed(原图, markers)
    边界 = (结果 == -1)                   # -1 表示分水岭线
    
    返回 边界, 结果

距离变换的作用:距离变换给每个前景像素赋值"到最近背景的距离",目标中心的值最大,是稳定的前景标记。
在这里插入图片描述
左:原图 / 右:分水岭分割(彩色分区 + 原图半透明叠加)。不同颜色代表不同的分割区域,红线是分水岭边界,算法自动将图像划分为约 115 个连通区域。


五、三类方法的对比

方法 假设 优点 缺点 典型场景
固定阈值 前背景灰度值可分 极简单,极快 对光照敏感,需手动设 T 快速原型,受控场景
Otsu 直方图有双峰 自动确定 T,无需调参 光照不均时失败 文档二值化、工业检测
自适应阈值 局部区域可分 处理光照不均 对噪声敏感,参数多 扫描文档、自然图像
分水岭 梯度图类似地形 能分割粘连目标 过分割(需标记辅助) 细胞计数、医学图像

六、实现要点

6.1 前处理的重要性

分割前的高斯模糊几乎是必须的:

去噪 → 二值化/梯度计算 → 分割

特别是分水岭算法,对未去噪的梯度图会产生严重过分割。通常用 σ=1–2 的高斯模糊处理。

6.2 Otsu 的条件

Otsu 在以下情况效果好:

  • 直方图明显双峰(前景和背景灰度范围不重叠)
  • 前景和背景面积相当(各占约 30%–70%)

当前景面积很小(如图像中只有一个小目标)时,Otsu 可能失效——可以用 Triangle 法(OpenCV 的 THRESH_TRIANGLE)替代。

6.3 分水岭的过分割控制

减少过分割的方法:

  1. 更强的前处理:高斯模糊 + 形态学开运算
  2. 更严格的前景标记:只取距离变换中 top 20%–30% 的点
  3. 标记合并:相邻的小区域按颜色/纹理相似性合并(GrabCut 等方法)

结语

本文完成了形态学与图像分割两篇的最后一篇:

  • 全局阈值:最简分割, I > T I > T I>T 为前景,适合受控场景
  • Otsu 算法:最优化问题——最大化类间方差,自动确定 T T T,无需调参
  • 自适应阈值:逐像素计算局部均值作为阈值,处理光照不均
  • 分水岭算法:把梯度图视为地形,注水模型找分割边界;标记分水岭通过距离变换自动生成前景标记,控制过分割

至此,边缘检测+形态学+分割三组共五篇(14–17)构成了完整的图像结构分析体系。下一章进入程序化纹理生成,从分析图像转向创造图像。


系列文章导航

标签图像处理 图像分割 阈值 Otsu 自适应阈值 分水岭 距离变换 二值化 Python OpenCV


本文是"有趣的图像处理"系列的第17篇,所有算法基于 Python + OpenCV + NumPy 实现,完整代码见 python_filters/python_ps/Image Segmentation/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐