查看全课大纲(第 22 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
GrabCut
小象实战讲义 · 基于深度学习的计算机视觉
在上一节中,我们学习了基于图论的 Graph Cuts 分割方法,它需要用户提供前景和背景的种子点。本节我们将介绍一种更强大的交互式分割算法——GrabCut。它仅需用户在目标外围画一个矩形框,算法便能自动迭代优化,实现精准的前景提取。GrabCut 是 Graph Cuts 的进阶版本,通过引入高斯混合模型(GMM)来更精细地建模前景和背景的颜色分布,从而在复杂场景下也能获得出色的分割效果。
💡 核心导读
- GrabCut 与 Graph Cuts 的关系:GrabCut 是 Graph Cuts 的迭代优化版本,核心思想一致,但建模更精细。
- 高斯混合模型(GMM):用于更准确地描述前景和背景的颜色分布,避免单一高斯模型的局限性。
- K-means 聚类算法:作为 GMM 参数估计的简化工具,在 GrabCut 的迭代过程中用于更新颜色模型。
- 迭代优化流程:GrabCut 通过交替执行“估计 GMM 参数”和“执行 Graph Cut 分割”两个步骤,使能量函数不断下降,分割结果越来越精确。
GrabCut 的基本思想
GrabCut 是一种基于图论的交互式图像分割方法。与 Graph Cuts 需要用户精确标注前景和背景种子点不同,GrabCut 对用户交互的要求更低:用户只需在目标物体外围画一个矩形框,将目标大致框住即可。
import numpy as np
import cv2
# 模拟一个简单的图像(8x8,3通道),中间区域为前景(红色),周围为背景(绿色)
# 我们用这个矩阵来示意用户框选的位置
image_sim = np.zeros((8, 8, 3), dtype=np.uint8)
# 背景设为绿色
image_sim[:, :, 1] = 200
# 在中心区域(3:6, 3:6)模拟前景(红色)
image_sim[3:6, 3:6, 0] = 255
image_sim[3:6, 3:6, 1] = 0
image_sim[3:6, 3:6, 2] = 0
# 用户框选的矩形坐标 (x, y, width, height),这里框住了中心区域
rect = (2, 2, 4, 4) # 从(2,2)开始,宽高为4的矩形
print("模拟图像中心区域为红色前景,周围为绿色背景。")
print(f"用户框选的矩形区域为: {rect}")
print("GrabCut 将以此矩形为初始信息进行分割。")模拟图像中心区域为红色前景,周围为绿色背景。
用户框选的矩形区域为: (2, 2, 4, 4)
GrabCut 将以此矩形为初始信息进行分割。算法会假定矩形框内部的像素可能是前景,而矩形框外部的像素一定是背景。基于这个初始的、不完美的“猜测”,GrabCut 开始其迭代优化过程。如果用户对初步结果不满意,还可以通过额外标注(如指定某些像素一定属于前景或背景)来引导算法,获得更完美的分割效果。
其强大的分割能力,源于它将 Graph Cuts 的能量最小化框架与更复杂的颜色模型相结合。
理论基础:高斯混合模型与 K-means
高斯混合模型
在 Graph Cuts 中,我们使用简单的灰度或颜色直方图来建模前景和背景。但在颜色分布复杂的情况下(例如前景物体本身包含多种颜色,或者前景与背景颜色相似),这种简单模型就显得力不从心。
GrabCut 采用高斯混合模型来为前景和背景分别建立颜色模型。GMM 认为,一个复杂的颜色分布不是由单个高斯分布产生的,而是由多个高斯分布叠加混合而成。
import numpy as np
# 假设我们有一组二维数据点,它实际上来自两个不同的高斯分布(两类)
np.random.seed(42)
# 第一类:中心在(2,2),协方差较小
class1 = np.random.multivariate_normal([2, 2], [[0.5, 0], [0, 0.5]], 50)
# 第二类:中心在(8,8),协方差较大
class2 = np.random.multivariate_normal([8, 8], [[1.5, 0.5], [0.5, 1.5]], 50)
data = np.vstack([class1, class2])
# 如果错误地用一个高斯分布去拟合所有数据
mean_single = np.mean(data, axis=0)
cov_single = np.cov(data.T)
print("单一高斯模型参数:")
print(f"均值: {mean_single}")
print(f"协方差矩阵:\n{cov_single}")
print("\n可以看到,单一模型无法捕捉到数据中存在两个聚集中心的事实。")单一高斯模型参数:
均值: [4.95023845 5.07178386]
协方差矩阵:
[[10.199475 9.75136159]
[ 9.75136159 10.71881598]]
可以看到,单一模型无法捕捉到数据中存在两个聚集中心的事实。而 GMM 则用多个高斯分量来拟合。在 GrabCut 中,通常为前景和背景各指定 个高斯分量(论文中常取 )。对于图像中的任何一个像素,其颜色要么来源于前景 GMM 的某一个高斯分量,要么来源于背景 GMM 的某一个高斯分量。
K-means 聚类算法
为了估计 GMM 的参数(每个高斯分量的均值、协方差和权重),我们可以使用期望最大化(EM)算法。在 GrabCut 的讲解和实现中,常用一个更直观的简化版算法——K-means 聚类来帮助理解颜色的“聚类”过程。
K-means 的目标是将数据点划分为 个簇,使得每个点到其所属簇中心的距离平方和最小。其算法流程如下:
- 初始化:随机选择 个点作为初始聚类中心。
- 分配步骤:计算每个数据点到 个聚类中心的距离,将其分配给距离最近的簇。
- 更新步骤:重新计算每个簇中所有点的均值,作为新的聚类中心。
- 迭代:重复步骤 2 和 3,直到满足终止条件(如聚类中心不再变化、达到最大迭代次数等)。
import numpy as np
# 一个简化的 K-means 演示(固定种子保证可重复)
np.random.seed(123)
# 生成更简单的演示数据:两类明显分离的点
simple_data = np.vstack([
np.random.normal(loc=[1, 1], scale=0.3, size=(20, 2)),
np.random.normal(loc=[4, 4], scale=0.3, size=(20, 2))
])
# 1. 初始化:随机选两个点作为中心(这里我们故意选两个距离较远的点,优化初始化)
# 在实际完全随机中,可能选到两个很近的点,导致效果不好。
centers = np.array([[0.5, 0.5], [4.5, 4.5]]) # 模拟一个较好的初始状态
print(f"初始聚类中心:\n{centers}")
# 执行一轮 K-means (分配 + 更新)
# 2. 分配步骤
distances = np.linalg.norm(simple_data[:, np.newaxis, :] - centers[np.newaxis, :, :], axis=2)
labels = np.argmin(distances, axis=1)
# 3. 更新步骤
new_centers = np.array([simple_data[labels == i].mean(axis=0) for i in range(2)])
print(f"经过一轮分配和更新后,新的聚类中心:\n{new_centers}")
# 查看分配结果
print(f"\n数据点被分配到簇 0 的数量: {np.sum(labels == 0)}")
print(f"数据点被分配到簇 1 的数量: {np.sum(labels == 1)}")初始聚类中心:
[[0.5 0.5]
[4.5 4.5]]
经过一轮分配和更新后,新的聚类中心:
[[0.9756223 0.9743495 ]
[4.05177694 4.01745622]]
数据点被分配到簇 0 的数量: 20
数据点被分配到簇 1 的数量: 20关于 K-means,有几个常见问题:
- K值怎么定? 主要依赖经验。可以尝试不同的 K 值,选择那个能使聚类结果最符合业务直觉或数据特性的值。在 GrabCut 中,K 通常固定为 5。
- 初始中心怎么选? 完全随机选择可能导致结果不稳定或陷入局部最优。一种优化方法是选择彼此距离最远的点作为初始中心。
- 算法会收敛吗? 是的,K-means 算法可以证明是收敛的(误差平方和函数单调递减),但通常收敛到的是局部最优,因此初始中心的选择会影响最终结果。
在 GrabCut 的课程讲解中,K-means 被用来对当前估计为前景或背景的像素颜色进行聚类,从而更新前景和背景 GMM 的颜色聚类中心(即各个高斯分量的均值);完整的 GMM 参数估计通常由 EM 算法完成。
GrabCut 算法流程详解
GrabCut 的核心是一个迭代优化过程,它交替执行以下两个步骤:
- 颜色模型估计:根据当前的分割结果(每个像素属于前景或背景的标签),使用 K-means 聚类分别更新前景和背景的 GMM 颜色模型参数。
- 图割分割:基于更新后的 GMM 模型,为每个像素计算其属于前景或背景的似然概率,并构建新的图,然后执行一次 Graph Cut,得到新的、更优的分割标签。
这个过程反复进行,每一次迭代都使得整体的能量函数值下降,分割边界也越来越准确。
让我们结合一个具体的例子来理解迭代过程:
- 初始化:用户画框。框外像素被标记为“确定背景”。框内像素被标记为“可能前景”。
- 初始颜色模型:此时,“可能前景”的像素集合中,既包含了真实前景的颜色(如骆驼的棕色),也包含了框内背景的颜色(如草地的绿色)。因此,为“前景”建立的 GMM 实际上是一个混杂的模型。而为“背景”建立的 GMM 则只基于框外确定的绿色/黑色像素,相对纯净。
- 第一次迭代:
- 执行 Graph Cut:基于上述混杂的 GMM,算法进行第一次分割。能量函数中的“区域项”会倾向于将绿色的像素划归背景(因为背景 GMM 中绿色分量概率高),将棕色的像素划归前景。同时,“边界项”会惩罚在颜色相似区域出现的不连续分割。这次分割会得到一个比初始框更精确的、二值化的前景/背景标签图。
- 更新 GMM:根据新的标签图,重新对属于前景的像素颜色进行 K-means 聚类(K=5),更新前景 GMM 的参数。此时,前景的聚类中心会更集中在骆驼的棕色、黄色系上。同样,更新背景 GMM。
- 后续迭代:重复步骤 3。随着迭代进行,前景 GMM 越来越能代表目标的真实颜色,背景 GMM 也越来越纯净。两者差异越来越大,导致每个像素属于前景或背景的概率变得更加“泾渭分明”(例如从 0.6 vs 0.4 变为 0.95 vs 0.05)。最终,算法收敛,得到精确的分割边界。
import numpy as np
# 用一个极度简化的数值例子说明 GrabCut 迭代中“概率”变得“泾渭分明”的思想
# 假设一个像素的颜色属于前景/背景的初始概率得分(0~1,越大越可能属于该类)
# 这里不是真实计算,仅为示意趋势。
np.random.seed(1)
# 模拟一个真实属于前景的像素,在迭代过程中其“属于前景的得分”变化
# 得分越高,表示越可能是前景
score_foreground = np.array([0.55, 0.70, 0.85, 0.95]) # 迭代中逐渐升高
score_background = 1 - score_foreground # 迭代中逐渐降低
print("模拟一个真实前景像素在 GrabCut 迭代中的归属概率变化:")
for i in range(len(score_foreground)):
print(f"迭代 {i+1}: P(前景)={score_foreground[i]:.2f}, P(背景)={score_background[i]:.2f}")
print("\n可以看到,随着迭代优化,算法对该像素的分类越来越确信。")模拟一个真实前景像素在 GrabCut 迭代中的归属概率变化:
迭代 1: P(前景)=0.55, P(背景)=0.45
迭代 2: P(前景)=0.70, P(背景)=0.30
迭代 3: P(前景)=0.85, P(背景)=0.15
迭代 4: P(前景)=0.95, P(背景)=0.05
可以看到,随着迭代优化,算法对该像素的分类越来越确信。📝 动手练一练
- 概念辨析:GrabCut 和 Graph Cuts 都基于图割的最小化能量框架。请问 GrabCut 在哪些方面对 Graph Cuts 进行了主要的改进和增强?
👉 点击查看参考答案
主要改进有两点:
- 交互方式简化:Graph Cuts 需要用户提供前景和背景的种子点,而 GrabCut 只需要一个包围目标的外接矩形,交互成本更低。
- 颜色模型增强:Graph Cuts 通常使用颜色直方图等简单模型。GrabCut 为前景和背景分别引入高斯混合模型,能够更精细地刻画复杂的颜色分布,特别是当前景或背景内部包含多种颜色时,建模能力更强,从而提升了分割精度,尤其是在颜色相似的边界处。
- 流程理解:在 GrabCut 的迭代算法中,每一步都包含“估计 GMM 参数”和“执行 Graph Cut”两个子步骤。请问这两个子步骤之间是如何相互依赖、相互促进的?
👉 点击查看参考答案
这是一个“鸡生蛋,蛋生鸡”的迭代优化过程,形成闭环:
- Graph Cut 依赖 GMM:执行 Graph Cut 需要计算能量函数中的“区域项”,即像素属于前景或背景的惩罚成本。这个成本直接由当前的前景和背景 GMM 模型计算得出。GMM 模型越好,区域项越准确,Graph Cut 的分割结果也越好。
- GMM 依赖 Graph Cut:更新 GMM 模型参数(如均值、协方差)需要知道哪些像素属于前景,哪些属于背景。这正是上一步 Graph Cut 输出的分割标签所提供的。分割标签越准确,用来训练前景和背景 GMM 的像素集合就越纯净,从而学习到的 GMM 模型也越精确。
- 两者交替进行,每次迭代都用更准的 GMM 得到更准的分割,再用更准的分割训练出更准的 GMM,直至收敛。
本章小结
本节我们深入学习了 GrabCut 这一经典的交互式图像分割算法。它继承了 Graph Cuts 基于能量最小化的图割框架,并通过引入高斯混合模型来精细化颜色建模,同时大幅降低了用户交互的难度。其核心在于一个迭代优化过程,通过 K-means 聚类与 Graph Cut 的交替执行,不断优化前景和背景模型,最终收敛到精确的分割边界。
GrabCut 代表了深度学习时代之前,传统计算机视觉在交互式分割领域的一个高峰。它巧妙地将图论、概率模型和迭代优化结合在一起,其思想至今仍有启发意义。
📋 行动清单
- 理解 GrabCut 仅需一个外接矩形作为交互输入的核心便利性。
- 掌握高斯混合模型相比单一颜色直方图在建模复杂颜色分布时的优势。
- 梳理 GrabCut “估计GMM -> 执行Graph Cut -> 更新分割 -> 再估计GMM”的迭代优化流程,理解两个步骤间的相互依赖关系。
—— 小象教研组