特征提取与描述:SIFT、SURF、ORB、HOG、LBP等经典特征
聊到特征提取,这几乎是每个做视觉的工程师都绕不开的坎。我记得刚入行那会儿,面试官问我“你用过哪些特征?”我张口就是SIFT、SURF,结果被追问了一句“那你说说它们本质区别是什么?”——嗯,当时确实有点懵。今天咱们就把这几个经典特征掰开揉碎了讲清楚。
一、SIFT:尺度不变特征变换
SIFT可以说是特征提取界的“老大哥”了。它最牛的地方在于,不管你把图片放大缩小、旋转、还是改变光照,它都能找到对应的特征点。说白了,就是鲁棒性极强。
核心思想:在尺度空间中寻找极值点,然后给每个点分配一个128维的描述子。
关键步骤:
- 构建高斯差分金字塔(DoG)
- 在DoG空间检测局部极值点
- 精确定位关键点(去除低对比度和边缘响应点)
- 为关键点分配主方向
- 生成128维描述子
我在项目中遇到过一个问题:用SIFT做图像拼接,结果两张图明明有重叠区域,但特征点匹配数量就是不够。后来发现是图像分辨率太大,默认的尺度参数不够细。我建议你实际用的时候,把nOctaveLayers调大一点,效果会好很多。
避坑指南:我曾经在嵌入式设备上跑SIFT,结果帧率只有0.5fps。SIFT的计算量确实大,不适合实时场景。如果你追求速度,建议往下看ORB。
二、SURF:加速版SIFT
SURF全称是Speeded-Up Robust Features,说白了就是SIFT的“加速版”。它用Hessian矩阵代替了DoG,用积分图加速了计算过程。
| 对比项 | SIFT | SURF |
|---|---|---|
| 尺度空间构建 | 高斯金字塔 | Hessian矩阵+积分图 |
| 特征点检测 | DoG极值 | Hessian行列式极值 |
| 描述子维度 | 128维 | 64维(默认) |
| 速度 | 慢 | 快3-5倍 |
我个人习惯在需要实时性但又不想牺牲太多精度时,优先考虑SURF。比如做AR场景的标记物识别,SURF就挺合适。不过要注意,SURF是有专利的,商用的话得小心。
三、ORB:又快又免费
ORB是OpenCV Labs推出的,结合了FAST角点检测和BRIEF描述子。它最大的优势就是——快,而且没有专利限制。
为什么快?因为FAST检测只需要比较像素亮度,BRIEF描述子用的是二进制字符串,匹配时用汉明距离,位运算一下就搞定了。
// OpenCV中ORB的使用示例
cv::Ptr<cv::ORB> orb = cv::ORB::create(500); // 500个特征点
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
orb->detectAndCompute(image, cv::Mat(), keypoints, descriptors);
// 匹配时用汉明距离
cv::BFMatcher matcher(cv::NORM_HAMMING);
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
你想想看,SIFT匹配一次可能要几十毫秒,ORB可能1毫秒就搞定了。我在做手机端的实时SLAM时,用的就是ORB。不过它也有缺点——对旋转和尺度变化比较敏感,尤其是大角度旋转时,匹配质量会下降。
小技巧:如果你用ORB做特征匹配,建议先做一下图像金字塔,或者用cv::ORB::create()时设置nlevels参数,这样能稍微缓解尺度问题。
四、HOG:行人检测的利器
HOG(方向梯度直方图)跟前面几个不太一样。它不是找特征点,而是提取整个图像块的特征。说白了,就是统计图像局部区域的梯度方向分布。
核心思路:
- 将图像分成小的cell(比如8x8像素)
- 计算每个cell的梯度方向和幅值
- 统计梯度方向直方图
- 将多个cell组成block,做归一化
- 串联所有block的特征,得到最终描述子
我记得当年做行人检测,HOG+SVM是标配。OpenCV里有个cv::HOGDescriptor,直接就能用。不过要注意,HOG对光照变化比较敏感,我建议你在预处理时做一下直方图均衡化。
实际经验:HOG的cell大小和block大小对效果影响很大。我一般用8x8的cell,2x2的block,步长8像素。如果你检测的目标比较小,可以试试4x4的cell。
五、LBP:纹理特征的经典
LBP(局部二值模式)是纹理分析的好手。它的思想很简单:比较中心像素和周围像素的大小关系,生成一个二进制编码。
基本LBP计算:
// 假设3x3邻域
// 中心像素值为c,周围8个像素值为p0-p7
// 如果pi >= c,则对应位为1,否则为0
// 最终得到一个8位二进制数
// 例如:
// 中心值: 85
// 周围: [90, 80, 70, 95, 100, 75, 65, 88]
// 比较后: [1, 0, 0, 1, 1, 0, 0, 1]
// 二进制: 10011001 = 153
LBP的优点是计算极快,而且对单调灰度变化不敏感。我在做人脸识别时,经常用LBP提取纹理特征,配合直方图做分类。不过它也有局限——对噪声比较敏感,而且旋转不变性不好。
避坑指南:我曾经直接用原始LBP做纹理分类,结果准确率只有60%多。后来换成圆形邻域和旋转不变模式(LBPROT),准确率提到了85%。所以,别偷懒,该升级的版本还是要升级。
六、如何选择?
说了这么多,到底该用哪个?我个人的经验是这样的:
- 追求精度、不差算力:选SIFT,尤其是做三维重建、图像拼接
- 需要实时、精度尚可:选SURF,适合AR、视觉里程计
- 极致速度、移动端:选ORB,SLAM、特征匹配首选
- 目标检测、行人识别:选HOG,配合SVM效果稳定
- 纹理分析、人脸识别:选LBP,简单高效
当然,现在深度学习火了之后,很多场景直接用CNN提取特征了。但经典方法的价值在于——它们让你理解“特征”到底是什么。你想想看,如果连SIFT的尺度空间都不懂,你怎么理解CNN里的感受野和池化?
嗯,今天就聊到这儿。下一章咱们会讲特征匹配和几何验证,到时候再细聊。