第 10 章 图像分割
学习目标:系统掌握把图像划分为有意义区域的所有经典方法——阈值、边缘、区域、聚类、图论、变分——及其在工业场景中的选择依据。 分割 (Segmentation) 是”从像素到语义”的桥梁,也是计算机视觉几乎所有高层任务的前置。
10.1 什么是图像分割?
10.1.1 形式化定义
定义 10.1:给定图像 (I),分割是把像素集 (P) 划分为不相交子集 ({R_1, \ldots, R_k}): [ \bigcup_{i=1}^{k} R_i = P, \quad R_i \cap R_j = \emptyset (i \neq j) ]
并满足:
- 每个 (R_i) 是连通的
- 同一 (R_i) 内的像素满足某种均匀性(灰度近似、纹理相似)
- 不同 (R_i) 间有显著差异
10.1.2 分割的三种抽象层次
| 层次 | 输出 | 示例 |
|---|---|---|
| 语义分割 (Semantic) | 每像素赋”类别” | 所有人 = 人 |
| 实例分割 (Instance) | 每像素赋”物体 ID” | 人 1、人 2、人 3 |
| 全景分割 (Panoptic) | 上两者融合 | 天空 + 人 1 + 人 2 |
10.1.3 分割方法的分类
分割方法├─ 基于阈值 (Threshold)├─ 基于边缘 (Edge-based)├─ 基于区域 (Region-based)├─ 基于聚类 (Clustering)├─ 基于图论 (Graph-based)├─ 基于变分 (Variational / Level Set)├─ 基于特征/模板└─ 基于深度学习 (CNN, Transformer)本章讲前 6 类经典方法;深度学习在第 12 章。
10.2 基于阈值的分割
10.2.1 全局阈值
最简单: [ g(x, y) = \begin{cases} 1 & f(x, y) > T \ 0 & f(x, y) \le T \end{cases} ]
关键:如何选 T?
10.2.2 基础算法(迭代)
1. 初始 T = mean(image)2. 用 T 分割成前景/背景3. T' = (mean_fg + mean_bg) / 24. 若 |T' - T| < ε 停止,否则 T = T',回到 2快速但对直方图依赖。
10.2.3 Otsu 方法(必会)
Nobuyuki Otsu 1979 的经典方法,基于最大化类间方差。
推导
设阈值 (T),前景 (C_1)(灰度 (\le T))、背景 (C_2)(> T)。
- 概率:(P_1(T) = \sum_{i=0}^{T} p_i),(P_2(T) = 1 - P_1)
- 均值:(m_1(T) = \frac{1}{P_1} \sum_{i=0}^{T} i p_i),(m_2 = \frac{1}{P_2} \sum_{i=T+1}^{L-1} i p_i)
- 全局均值:(m_G = P_1 m_1 + P_2 m_2)
类间方差: [ \sigma_B^2(T) = P_1 (m_1 - m_G)^2 + P_2 (m_2 - m_G)^2 = P_1 P_2 (m_1 - m_2)^2 ]
Otsu:选 (T^* = \arg\max_T \sigma_B^2(T))。
几何直觉
两类方差总和 = 组内方差 + 组间方差(ANOVA 方差分解)。
- 总方差固定(图像内禀属性)
- 最大化组间 = 最小化组内
- 组间大 = 两类均值距离大 → 最”分得开”
实现
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)适用条件
Otsu 假设双峰直方图(前景 + 背景两个明显聚集)。当直方图单峰(如光照均匀的文字),Otsu 也可给出合理结果。但直方图扁平(如噪声大、渐变光照),效果差。
10.2.4 多级 Otsu
推广到 (K) 类: [ \sigma_B^2 = \sum_{k=1}^{K} P_k (m_k - m_G)^2 ] 搜索 (K-1) 个阈值使上式最大。
复杂度指数级 (L^{K-1}),(K = 3, L = 256) 可枚举;更多类要动态规划 / GA。
10.2.5 基于噪声抗性的改进
Otsu 对噪声敏感。改进:
- 先高斯平滑再 Otsu
- 基于梯度加权:对梯度大的像素权重大(它们更可能在类边界)
10.2.6 自适应(局部)阈值
全局阈值对非均匀光照无能为力。
方法:每个像素的阈值由其局部邻域计算。 [ T(x, y) = \text{mean}(邻域) - C ] 或 [ T(x, y) = \text{Gaussian_weighted_mean}(邻域) - C ]
Niblack: [ T = m + k \sigma, \quad k \in (-0.5, 0) ]
Sauvola(文档处理): [ T = m \left[1 + k \left(\frac{\sigma}{R} - 1\right)\right] ]
adaptive = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blockSize=11, C=2)适合:OCR、车牌、扫描文档的预处理。
10.3 基于边缘的分割
10.3.1 边缘的物理本质
图像中灰度发生剧变的地方 → 对应三维场景中的:
- 物体与背景的交界
- 不同物体的交界
- 表面法线突变(角、棱)
- 不同材质的交界
- 阴影边界
理想边缘:阶跃 (step) 实际边缘:斜坡 (ramp) + 噪声
10.3.2 Canny 边缘检测(工业标准)
John Canny 1986 设计了满足三个最优性的边缘检测器:
- 良好检测:真边缘被检测出(低漏检)
- 精确定位:检测位置准(低偏差)
- 单响应:每个真边缘仅响应一次(不重影)
Canny 证明这三者下的最优核 ≈ 高斯函数的一阶导。
Canny 算法(5 步)
Step 1: 高斯平滑 I_s = G_σ * I
Step 2: 计算梯度 g_x = Sobel_x(I_s), g_y = Sobel_y(I_s) M = sqrt(g_x² + g_y²) θ = atan2(g_y, g_x)
Step 3: 非极大值抑制 (NMS) 沿梯度方向查看相邻两点 若 M(当前) 不是局部最大 → 置 0 → 把粗边缘变成 1 像素宽
Step 4: 双阈值 M > T_high : 强边缘 T_low < M < T_high : 弱边缘 M < T_low : 非边缘
Step 5: 滞后连接 保留强边缘 若弱边缘与强边缘连通 → 保留 否则 → 丢弃NMS 详解
对每像素 (p),根据 (\theta) 把梯度方向离散化为 4 个方向之一:
- 0° / 180°:水平
- 45° / 225°:对角
- 90° / 270°:垂直
- 135° / 315°:反对角
比较 (M(p)) 与沿该方向两侧邻居的幅值,只保留最大。
双阈值的作用
- 若只有单阈值:低了噪声多,高了边缘断
- 双阈值 + 滞后:强边缘作为”种子”,延伸到弱边缘 → 既降噪又保完整
经验:(T_{\text{high}} / T_{\text{low}} \approx 2-3)。
edges = cv2.Canny(gray, threshold1=50, threshold2=150)10.3.3 其他一阶检测器
回顾第 3 章:Sobel、Prewitt、Scharr。单独使用常不如 Canny。
10.3.4 LoG 与 Marr-Hildreth
Laplacian of Gaussian (LoG): [ \text{LoG}(x, y) = \nabla^2 G_\sigma = -\frac{1}{\pi \sigma^4} \left[1 - \frac{x^2 + y^2}{2\sigma^2}\right] e^{-(x^2+y^2)/(2\sigma^2)} ]
- 先高斯平滑再二阶导(合并为一个核更快)
- 边缘 = 过零点 (zero-crossing)
Marr-Hildreth:LoG + 过零点检测 = 边缘。
LoG 核形如”墨西哥帽”:
▁▁▁ (零轴) / \ / ▂ \ / ╱ ╲ \ / ╱ ╲ \─╱ ╲─DoG 近似:(\text{LoG} \approx G_{\sigma_1} - G_{\sigma_2})((\sigma_2 = 1.6 \sigma_1)),速度快。是 SIFT 的基础(第 11 章)。
10.3.5 从边缘到线段:Hough 变换
线检测
动机:边缘给出孤立的点,想把它们聚合成”整条直线”。
参数化:直线 (y = mx + c)?但垂直线 (m = \infty)。改用法向式: [ \rho = x \cos\theta + y \sin\theta ]
每个图像点 ((x, y)) 对应参数空间 ((\theta, \rho)) 的一条正弦曲线。
Hough 累加器:
for each edge pixel (x, y): for each θ in [0, π]: ρ = x cos θ + y sin θ accumulator[θ_bin, ρ_bin] += 1
找 accumulator 的局部极大 → 每个极大值对应一条检测到的直线lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=200)# 概率 Hough(返回端点)lines_p = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180, threshold=100, minLineLength=50, maxLineGap=10)圆检测
圆参数 ((a, b, r)) 三维累加器。OpenCV 的 HoughCircles 先检测圆心,再估半径。
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=30, param1=50, param2=30, minRadius=10, maxRadius=100)广义 Hough(任意形状)
用R-表存储模板形状相对参考点的偏移。可检测任何固定形状。
10.4 基于区域的分割
10.4.1 区域生长
算法:
1. 选 seed 点集 S2. 对每个 seed s,初始化区域 R = {s}3. 对 R 的 8-邻域像素 p: 若 p 满足相似准则 (|f(p) - f(s)| < T) → 加入 R4. 重复直到无新像素加入相似准则:
- 灰度差 < T
- 与区域均值差 < T(自适应)
- 纹理 / 颜色差
优点:生成连通区域,适合单物体分割。 缺点:seed 选择敏感、计算慢(每次扩展需遍历)。
10.4.2 区域分裂与合并 (Split & Merge)
四叉树分裂:
1. 把整幅图看作一个区域2. 若区域内方差 > T → 分成 4 个子区域3. 递归,直到每个叶子均匀合并:相邻叶子若满足均匀性 → 合并。
优点:自适应、多尺度。 缺点:边界仍是方块状。
10.4.3 分水岭 (Watershed)
直觉
把图像灰度当作地形高度:
- 低处是”盆地”
- 高处是”分水岭”
模拟洪水:
- 从每个局部极小点开始”注水”
- 水位上涨,水向四周蔓延
- 两个盆地的水相遇时 → 筑坝(标记为分割边界)
- 继续直到所有区域被覆盖
最终分割线就是这些筑坝位置。
灰度地形: 分水岭: ╱╲ ╱╲ ┃ ┃ ┃ ╱ ╲ ╱ ╲ ┃ ┃ ┃╱ ╲╱ ╲ ┃ ┣━━━┫ ┃ ┃ ┃直接用梯度作为地形
对原图求梯度,梯度作为高度 → 边界(高梯度处)成为分水岭。
过分割问题
图像中每个局部极小都会产生一个区域 → 典型图像有几百上千个 → 严重过分割。
Marker-based Watershed(解决方案)
思路:用户指定合理数量的 marker(前景 + 背景),只在 marker 处”注水”。
# 典型流程:金币分割img = cv2.imread('coins.jpg')gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)_, thresh = cv2.threshold(gray, 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 = cv2.distanceTransform(opening, cv2.DIST_L2, 5)_, sure_fg = cv2.threshold(dist, 0.7 * dist.max(), 255, 0)sure_fg = np.uint8(sure_fg)
# 未确定区域unknown = cv2.subtract(sure_bg, sure_fg)
# 标记_, markers = cv2.connectedComponents(sure_fg)markers += 1 # 背景从 1 开始(0 留给未知)markers[unknown == 255] = 0
# 分水岭markers = cv2.watershed(img, markers)img[markers == -1] = [0, 0, 255] # 边界标红经典应用:细胞计数、金币分割、粘连物体分离。
10.4.4 Mean-Shift 分割
思想:把每个像素 ((x, y, R, G, B)) 视为 5D 特征空间的点。对每个点做均值漂移聚类: [ \mathbf{m}(\mathbf{x}) = \frac{\sum_{\mathbf{x}_i \in B(\mathbf{x}, r)} \mathbf{x}_i}{|B(\mathbf{x}, r)|} - \mathbf{x} ] 每次向局部密度中心移动,直至收敛。收敛到同一模式的所有点 → 同一簇。
参数:
sp:空间带宽(像素位置尺度)sr:颜色带宽
smoothed = cv2.pyrMeanShiftFiltering(img, sp=20, sr=50)特点:
- 不需指定聚类数
- 保留边缘,视觉效果好
- 慢(5D 邻域搜索)
10.5 基于聚类的分割
10.5.1 K-Means
回忆机器学习经典算法:
1. 随机初始化 K 个中心2. 每点 → 最近中心(分配)3. 每中心 ← 所属点的均值(更新)4. 迭代到收敛在图像分割中:把每像素看作 3D (RGB) 或 5D (RGBXY) 点,聚类。
data = img.reshape(-1, 3).astype(np.float32)criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)K = 4_, labels, centers = cv2.kmeans(data, K, None, criteria, 10, cv2.KMEANS_PP_CENTERS)segmented = centers[labels.flatten()].reshape(img.shape).astype(np.uint8)优点:简单、快。 缺点:
- 需指定 K
- 非凸簇分不好
- 不保证空间连通
10.5.2 SLIC 超像素
Simple Linear Iterative Clustering (Achanta 2012)。
超像素 (Superpixel):一组空间邻近且颜色相似的像素 → 保留边缘的”大像素”。
算法:K-Means 在 5D (LAB + XY) 空间,空间距离约束在某个步长 S 内: [ D = \sqrt{\left(\frac{d_{\text{color}}}{m}\right)^2 + \left(\frac{d_{\text{spatial}}}{S}\right)^2} ]
from skimage.segmentation import slicsegments = slic(img, n_segments=500, compactness=10)用途:
- CNN 分割前的”降维”
- 交互分割的基本单元
- 降低后续算法复杂度(500 超像素 vs 500000 像素)
10.6 基于图论的分割
10.6.1 图的构造
把图像建模成图:
- 节点 (V) = 像素(或超像素)
- 边 (E) = 相邻像素之间的连接
- 权重 (w(i, j)) = 相似度(颜色、纹理)
[ w(i, j) = e^{-|F_i - F_j|^2 / \sigma^2} ]
分割 = 图划分。
10.6.2 Normalized Cut
Shi & Malik 2000 的里程碑。
定义”切”: [ \text{cut}(A, B) = \sum_{i \in A, j \in B} w(i, j) ] (两分区之间的总权重)
minimum cut 的问题:倾向切出孤立小区域(边少)。
归一化切 (Ncut): [ \text{Ncut}(A, B) = \frac{\text{cut}(A, B)}{\text{assoc}(A, V)} + \frac{\text{cut}(A, B)}{\text{assoc}(B, V)} ] 其中 (\text{assoc}(A, V) = \sum_{i \in A, j \in V} w(i, j))。
求解:NP-hard。松弛后变为广义特征值问题: [ (D - W) y = \lambda D y ] 取第 2 小特征值对应的特征向量,阈值化即分割。
递归:对每个子图继续 Ncut,形成树状分层分割。
10.6.3 Graph Cut / GrabCut
Boykov & Jolly 2001:把分割建模为最小能量问题: [ E(L) = \underbrace{\sum_i D_i(l_i)}{\text{data term}} + \lambda \underbrace{\sum{(i, j) \in N} V_{ij}(l_i, l_j)}_{\text{smoothness term}} ]
- (D_i(l_i)):像素 (i) 被标为 (l_i \in {0, 1}) 的代价(基于颜色模型)
- (V_{ij}):相邻像素标签不同的惩罚
最大流 / 最小割 算法求解全局最优。
GrabCut(Rother 2004):用户友好版本
用户画一个框(或涂几笔)标出前景 ROI:
- 框外 → 确定背景
- 框内 → 可能是前景
- 用 GMM 建前景/背景颜色模型
- Graph Cut 求解
- 用结果更新 GMM,迭代
mask = np.zeros(img.shape[:2], np.uint8)bgdModel = np.zeros((1, 65), np.float64)fgdModel = np.zeros((1, 65), np.float64)rect = (50, 50, 300, 400)cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')result = img * mask2[:, :, None]经典应用:PS”魔术棒”/“快速选择”、证件照背景替换。
10.7 基于变分的分割:Level Set
10.7.1 思路:把曲线演化为能量下降
主动轮廓 (Snake)(Kass 1988):曲线 (C),能量: [ E(C) = \underbrace{\int \alpha |C’|^2 + \beta |C”|^2}{\text{内能(平滑)}} + \underbrace{\int E{\text{image}}(C)}_{\text{外能(图像驱动)}} ]
外能常取负梯度幅值 (E_{\text{image}} = -|\nabla I|^2)(引导向边缘靠)。
梯度下降演化曲线到最小能量 → 分割。
10.7.2 Level Set (Osher & Sethian 1988)
问题:Snake 不能自然处理拓扑变化(曲线分裂、合并)。
思路:用隐式函数 (\phi(x, y)) 表示曲线: [ C = {(x, y) : \phi(x, y) = 0} ]
- (\phi > 0) 在内部
- (\phi < 0) 在外部
曲线演化转为 (\phi) 的演化: [ \frac{\partial \phi}{\partial t} = -F |\nabla \phi| ] (F) 是演化速度(依赖于图像)。
优势:
- 拓扑变化自然((\phi = 0) 可以自动断开或合并)
- 易推广到 3D
10.7.3 Chan-Vese 模型(2001)
不依赖边缘的变分分割: [ E(\phi) = \mu \int \delta(\phi) |\nabla \phi| + \lambda_1 \int (I - c_1)^2 H(\phi) + \lambda_2 \int (I - c_2)^2 (1 - H(\phi)) ]
- (c_1, c_2):前景 / 背景平均灰度
- 目的:把图像划分为两个平均灰度最均匀的区域
适合无明显边缘、但前景背景色调一致的场景(医学超声、显微镜)。
10.8 各方法对比与选型
| 方法 | 速度 | 准确性 | 是否需参数 | 适用场景 |
|---|---|---|---|---|
| 阈值 (Otsu) | 极快 | 中 | 自动 | 高对比度、背景简单 |
| Adaptive Thresh | 快 | 中 | 少 | 光照不均文档 |
| Canny + Hough | 快 | 高(线/圆) | 需调 | 几何检测 |
| 区域生长 | 中 | 高 | 需种子 | 已知种子位置 |
| 分水岭 | 中 | 高 | 需 marker | 粘连物体 |
| Mean-Shift | 慢 | 高 | 带宽 | 自动色块 |
| K-Means | 快 | 中 | K | 色调分组 |
| SLIC 超像素 | 中 | 高 | n_segments | 作为中间表示 |
| Ncut | 慢 | 极高 | 少 | 理论分析 |
| GrabCut | 中 | 极高 | 用户框 | 交互抠图 |
| Level Set | 慢 | 极高 | 多 | 医学成像 |
| CNN (Unet 等) | 快(GPU) | 最高 | 训练数据 | 通用场景(Ch 12) |
10.9 评估指标
10.9.1 像素级
[ \text{Pixel Acc} = \frac{\text{correct pixels}}{\text{total}} ]
问题:类别不平衡时不可靠。
10.9.2 IoU / Jaccard
[ \text{IoU} = \frac{|A \cap B|}{|A \cup B|} ]
mIoU:各类 IoU 平均。
10.9.3 Dice
[ \text{Dice} = \frac{2 |A \cap B|}{|A| + |B|} ]
与 IoU 等价:(\text{Dice} = 2 \text{IoU} / (1 + \text{IoU}))。
医学图像常用。
10.9.4 边界级
- Hausdorff 距离
- 轮廓 F-score
10.10 本章要点与面试考点
✅ 必须掌握
- 分割的严格定义与分类
- Otsu 公式及推导(类间方差)
- 自适应阈值的必要性
- Canny 五步(高斯、梯度、NMS、双阈值、滞后)
- Hough 变换的参数空间与累加器思想
- 分水岭过分割 + Marker-based 解决方案
- K-Means 在 RGB/LAB 空间分割
- GrabCut = GMM + Graph Cut
- 主要评估指标(IoU、Dice)
💡 高频面试题
Q1. Otsu 的最大化类间方差为什么等价于最小化类内方差?
答:方差分解 (\sigma_{\text{total}}^2 = \sigma_W^2 + \sigma_B^2)(within + between)。因为 (\sigma_{\text{total}}) 对图像固定,最大化 (\sigma_B) = 最小化 (\sigma_W)。直觉:两类均值拉开得越远,同时组内越紧凑,就越”分得清”。
Q2. 为什么 Canny 要用双阈值?
答:
- 单高阈值:强边缘清晰但容易断裂
- 单低阈值:边缘完整但噪声很多假边
- 双阈值 + 滞后:强边缘作种子,向连通的弱边缘延伸,兼顾降噪与完整
Q3. Hough 变换为什么用 ((\rho, \theta)) 而不是 ((k, b))?
答:(y = kx + b) 在垂直线时 (k = \infty),参数空间无法有限采样。法线式 (\rho = x \cos\theta + y \sin\theta) 中 (\theta \in [0, \pi))、(\rho) 有界,可以合理离散化累加器。
Q4. 分水岭为什么常过分割?如何解决?
答:每个局部极小点都会产生一个盆地。自然图像中几百个局部极小很常见 → 几百个区域。解决方法:
- 先平滑减少噪声极小
- Marker-based:只允许 marker 点作为种子
- 基于梯度后再平滑
Q5. GrabCut 的流程?
答:
- 用户画矩形框,框外标确定背景
- 对框内像素用 GMM(高斯混合模型)建颜色分布
- 建图(像素为节点,相邻连边,权重 = 颜色相似度)
- Graph Cut (min-cut / max-flow) 求最优分割
- 根据新标签更新 GMM,迭代
Q6. K-Means 分割和 Mean-Shift 分割的区别?
答:
- K-Means:参数 K;寻找全局最小代价;对初值敏感
- Mean-Shift:无需 K;局部密度上爬;自动聚类数;但慢
Q7. 为什么用 SLIC 超像素?
答:
- 降维:把 1000000 个像素变成 1000 个超像素,后续算法快 1000 倍
- 保边:超像素严格贴合物体边界
- 中间表示:许多高层分割算法(Ncut、CRF)本身对像素数敏感
Q8. 分割评价常用 IoU 而非像素准确率?
答:像素准确率在前景很小(如小肿瘤、小目标)时失真严重——“全预测为背景”可达到 99% 准确率但完全无效。IoU 只看交集与并集的比例,对前景大小不敏感,更反映真实分割质量。
10.11 延伸阅读
- Gonzalez, Digital Image Processing (4th), Ch. 10-11
- Szeliski, Computer Vision (2nd), Ch. 4-5
- Canny, “A Computational Approach to Edge Detection”, IEEE TPAMI 1986
- Otsu, “A Threshold Selection Method from Gray-Level Histograms”, 1979
- Shi & Malik, “Normalized Cuts and Image Segmentation”, IEEE TPAMI 2000
- Rother et al., “GrabCut: Interactive Foreground Extraction”, SIGGRAPH 2004
- Achanta et al., “SLIC Superpixels”, IEEE TPAMI 2012
下一章:分割告诉我们”像素属于谁”;特征提取要回答”它们代表什么”——如何把图像压缩成少量但强鲁棒的描述子,用于识别、匹配、检索?
如果這篇文章對你有幫助,歡迎分享給更多人!
部分資訊可能已經過時





















