SLIC 超像素算法
一、超像素是什么
超像素把图像从像素网格重新组织成有意义的局部区域,每个区域由颜色相近、位置相邻的一堆像素组成。SLIC = S imple L inear I terative Clustering,直译「简单线性迭代聚类」。
和下采样的区别:下采样丢像素、损失分辨率;超像素保留全部像素,只是换一种组织方式,物体边界保留在区域边界上。
二、工作原理
2.1 初始化聚类中心
在图像上均匀撒 K 个种子点(用户指定的期望超像素数),种子间距:
S = √(N / K) 其中 N = 图像总像素数
撒完种子还有一步容易漏掉:把每个种子挪到自己 3×3 邻域内梯度最小的位置。种子要是正好压在边缘上,第一轮聚类就会被边缘两侧的像素来回拉扯。
2.2 搜索范围限制
每个像素只和它周围 2S × 2S 窗口内的聚类中心比较,而不是全图 K 个中心都比一遍。
2S
┌─────────────────────┐
│ │
│ · 聚类中心 │
│ 搜索区域 │
│ │
└─────────────────────┘
窗口内最多碰到 4 个左右的中心,总比较次数和像素数成正比,这就是 SLIC 复杂度 O(N) 的来源。也正因为只跟附近中心比较,远处的同色像素不会聚到同一个超像素------空间局部性是这么来的。
但注意,这一步并不保证单个超像素内部连通。细缝、噪声会在区域里切出「飞地」,连通性是迭代结束后靠后处理强制的,见 2.4 第 4 步。
2.3 距离度量
同时考虑颜色距离(CIELAB 空间)和空间距离(xy 坐标):
d_color = √((l₁ - l₂)² + (a₁ - a₂)² + (b₁ - b₂)²)
d_space = √((x₁ - x₂)² + (y₁ - y₂)²)
归一化后加权求和:
D = √((d_color / m)² + (d_space / S)²)
| 参数 | 作用 |
|---|---|
| m | 控制超像素的紧凑程度。m 越大 → 结果越接近方形网格;m 越小 → 更贴合物体边界 |
| S | 搜索范围,由 K 自动决定 |
2.4 迭代优化
1. 每个像素在 2S×2S 邻域内分配到 D 最小的聚类中心
2. 更新聚类中心:取区域内像素的平均 Lab 和 xy
3. 重复 1-2,通常迭代 10 次左右(收敛很快,再多次数收益不大)
4. 强制连通性后处理:按标签做连通域检查,
不连通的小碎片(实现里常用阈值 N/K/4)重新分配给邻近超像素
第 4 步顺带解决了空区域问题------迭代后个别中心可能一个像素都没分到。
三、输出格式
| 输出 | 说明 |
|---|---|
| labels | 整数矩阵,尺寸同原图,值范围 0 ~ K-1。每个像素的值表示它属于哪个超像素 |
| 属性列表 | 每个超像素的平均颜色、像素坐标列表、面积、质心等可计算属性 |
关于 K 值:K 不是精确值,只是用户指定的期望目标。初始撒 K 个种子,迭代后过小的区域被合并,最终实际超像素数约 0.9K ~ K。
四、超像素 vs 聚类(如 k-means)
SLIC 可以当成局部化的 k-means,加了两条约束:特征里混入空间坐标、搜索范围限制在 2S×2S 邻域。展开对比:
| 普通聚类(k-means) | 超像素(SLIC) | |
|---|---|---|
| 目标 | 全局数据分组 | 生成局部、紧凑、边界贴合的区域 |
| 特征 | 只考虑颜色/特征 | 颜色 + 空间坐标 |
| 空间连通性 | 不保证,同一类可能分散在图中各处 | 聚类阶段同样不保证,靠后处理强制 |
| 搜索范围 | 全图 | 限制在 2S×2S 邻域 |
| 复杂度 | O(N × K × 迭代次数) | O(N × 迭代次数) |
| 结果形式 | 离散的类别标签 | 分块区域,贴合物体边界 |
示例对比:
原图:左上角蓝天,右下角蓝天
k-means:左上角蓝色像素 + 右下角蓝色像素 → 同一类
SLIC:左上角蓝色像素 → 超像素A,右下角蓝色像素 → 超像素B(搜索范围限制,天然分开)
五、适用场景
| 场景 | 说明 |
|---|---|
| 语义分割预处理 | 在超像素上跑分类器(随机森林、k-NN),而非逐像素。样本量从百万级降到百级 |
| 视频跟踪/目标分割 | 以超像素为节点构建图模型,跟踪超像素而非像素 |
| 深度学习的替代输入 | 超像素特征作为网络输入,减少计算量 |
| 图像抠图/编辑 | 用户点击超像素而非像素选取区域,交互更高效 |
| 深度估计/立体匹配 | 以超像素为匹配基元,比逐像素鲁棒 |
排第一的「语义分割预处理」是超像素最经典的用法,也是「快 + 降噪」收益最直观的地方:
逐像素:
200 万像素 × 每个像素独立预测
→ 孤立噪点导致结果散乱(胡椒盐噪声)
超像素:
SLIC 聚合成 200 个区域
每个区域已是相邻像素的平均/聚合 → 天然去噪
分类器只处理 200 个样本,少了 4 个数量级
→ 结果平滑,边界整齐
六、超像素分类完整流程
把 SLIC + 分类器 + CRF 三个环节串起来,讲清「用超像素做语义分割」从头到尾怎么走。
6.1 整体流程
训练阶段:
模板图 → SLIC → 超像素标签图
→ 人工给【每个】超像素打标签(关键,不是只选几个)
→ 每个超像素算一组特征
→ (特征, 标签) 喂给分类器(SVM / 随机森林 / MLP)
预测阶段:
新图 → SLIC → 超像素
→ 每个超像素算同样的一组特征
→ 分类器预测 → 每个超像素一个类别概率
→(可选)CRF 后处理 → 平滑结果
→ 类别映射回像素 → 分割图
6.2 三个常见误区
误区一:只选几个超像素当训练样本
错。监督学习需要每个训练样本都有标签,没标签的超像素模型学不到。要给所有(或足够多)超像素打标签,像填色游戏。
误区二:只用 RGB 均值当特征
太弱。RGB 对光照敏感(见第七节)。常用更强的特征向量:
- Lab 均值(SLIC 本身就在 Lab 空间,比 RGB 稳)
- 颜色直方图(每通道分 bin)
- 纹理特征:LBP、Gabor、梯度直方图(HOG)
- 形状/统计:面积、紧凑度、方差
误区三:特征里用绝对坐标
新图物体位置变了 → 直接翻车。分类特征别用绝对质心坐标 (x,y),除非物体位置固定(见第九节)。
6.3 为什么加 CRF(可选后处理)
分类器对每个超像素独立预测,可能出现「一块区域里冒出几个不同标签的孤岛」。CRF 接在分类器后面,根据「颜色相近的邻居该一致」做空间平滑,消除孤岛,同时保留颜色跳变的真实边界。
细节另见 CRF 条件随机场,这里只讲它在流程里的位置。
七、对光照的敏感性
SLIC 的距离度量包含颜色距离(Lab 空间),光源变化会拉偏颜色值,导致:
- 同区域的像素颜色差异变大 → 本该属于同一物体的区域被分裂为多个超像素
- 边界偏移,不再严格对齐物体边缘
补救方式:
- 归一化处理:对每张图做标准化(减去均值除以标准差),抵消光照波动
- 调大 m:注意方向别搞反------m 在分母上,调大 m 才是压低颜色项权重、让超像素更依赖空间位置;调小 m 反而更贴颜色边界、对光照更敏感
- 改用光照不变特征:如 LBP 纹理、梯度直方图替代原始 RGB/Lab
- 工业场景直接换方法:光源固定时,差影法比 SLIC 更稳定
八、C++ 实现
OpenCV(推荐,最方便)
cpp
#include <opencv2/ximgproc.hpp>
cv::Mat image = cv::imread("image.jpg");
// 注意参数语义:createSuperpixelSLIC(image, 算法类型, region_size, ruler)
// region_size 就是正文里的 S(超像素近似边长),不是超像素数 K!
// 想要约 K 个超像素,按 S = √(N/K) 反推:500×500 图想要 ~200 个 → region_size = 35
// ruler 就是紧凑度 m
cv::Ptr<cv::ximgproc::SuperpixelSLIC> slic =
cv::ximgproc::createSuperpixelSLIC(image, cv::ximgproc::SLIC, 35, 10.0f);
slic->iterate(10);
cv::Mat labels, contour_mask;
slic->getLabels(labels); // 每像素的超像素编号
slic->getLabelContourMask(contour_mask); // 超像素边界 mask
int num_superpixels = slic->getNumberOfSuperpixels();
两个容易踩的坑:
createSuperpixelSLIC(image, SLIC, 200)看起来像「要 200 个超像素」,实际 200 是 region_size------500×500 的图跑出来只有 ~6 个大块- 取边界用
getLabelContourMask,OpenCV 里没有getLabelContours这个函数
算法类型除了 SLIC 还有 SLICO(自适应 m,OpenCV 默认)和 MSLIC(取中值更新中心,对噪声更稳)。
性能参考(500×500 图)
| 参数 | OpenCV 耗时 | 说明 |
|---|---|---|
| K=1000, 迭代10次 | 15~40 ms | 默认配置 |
| K=1000, 迭代5次 | 8~20 ms | 减少迭代 |
| K=200 | 10~25 ms | 减少超像素数 |
- 现代桌面 CPU 约 20ms,ARM 嵌入式约 50~100ms
- 纹理丰富的图迭代收敛慢,略慢一些
- OpenCV 预编译版自带 SIMD 优化,运行时按 CPU 能力选指令集路径
VLFeat(轻量 C 库)
也提供 SLIC 实现,适合嵌入式环境。
九、不适合的场景
- PCB 元件错位检测:元件位置偏移会改变超像素图,不能靠「比较超像素」来检测缺陷
- 高精度工业测量:超像素边界不是亚像素精度
- 物体位置不固定的场景:全局分类时质心坐标特征会失效
- 光照频繁变化的环境:除非先做归一化(详见第七节)