第四章:特征提取方法——角点检测(Harris)、SIFT特征、ORB特征
各位同学,今天我们来聊聊特征提取。这是视觉追踪里最核心的一步,没有之一。
你想想看,我们要追踪一个目标,总得先知道“目标长什么样”吧?特征提取,就是帮我们把图像里最有代表性的“点”或“区域”找出来。我个人习惯把特征点比作人的“面部特征”——鼻子、眼睛、嘴角。这些点稳定、独特,换个角度还能认出来。
好,咱们直接进入正题。今天讲三个经典方法:Harris角点、SIFT、ORB。
4.1 Harris角点检测——经典中的经典
Harris角点检测,说白了就是找图像里“灰度变化剧烈”的点。什么叫剧烈?你想象一下,一个十字路口,你往哪个方向走,周围的景色都在变。这就是角点。
数学上,Harris用一个矩阵M来描述局部图像的结构。这个矩阵的特征值,决定了这个点是角点、边缘还是平坦区域。
核心思想:如果某个像素点,往任何方向移动一小步,灰度值都变化很大,那它就是角点。
我记得刚入行时,用Harris做棋盘格角点检测。当时有个坑——阈值设得太低,结果满屏都是“角点”,根本没法用。后来我学乖了,一般先设一个较高的阈值,再根据实际场景微调。
Harris的优点是计算快、原理简单。缺点也很明显:对尺度变化敏感。说白了,你把图像放大一倍,原来的角点可能就找不到了。
避坑指南:我曾经在一个项目中,用Harris检测远距离的小目标,结果角点全丢了。后来换成SIFT才解决。所以,如果你的目标尺度变化大,Harris可能不是最佳选择。
4.2 SIFT特征——尺度不变性的王者
SIFT(Scale-Invariant Feature Transform),尺度不变特征变换。这个名字已经告诉你它的核心优势了——不管目标放大还是缩小,它都能稳定提取特征。
SIFT的流程大致分四步:
- 尺度空间极值检测——在不同尺度下找候选点
- 关键点定位——去掉低对比度和边缘响应点
- 方向分配——给每个关键点一个主方向
- 描述子生成——用128维向量描述特征点
嗯,这里要注意:SIFT的128维描述子,虽然信息丰富,但计算量也大。我做过一个实时追踪项目,用SIFT在640×480的图像上提取特征,每帧要花50毫秒。对于30fps的要求来说,这显然不行。
性能警告:SIFT是有专利的,商用需要授权。另外,它的计算速度在嵌入式设备上可能是个瓶颈。如果你做手机端或无人机端的视觉追踪,建议慎重考虑。
但话说回来,SIFT的匹配精度确实高。我记得有一次做多视角拼接,用SIFT匹配的特征点,误差控制在1个像素以内。这种精度,Harris和ORB都很难达到。
4.3 ORB特征——速度与精度的平衡
ORB(Oriented FAST and Rotated BRIEF),这个名字有点绕。简单说,它是FAST角点检测 + BRIEF描述子的升级版。
ORB的优势就一个字:快。我实测过,同样一张图,ORB比SIFT快一个数量级。在树莓派上跑,ORB都能轻松达到实时。
ORB做了两个关键改进:
- 方向性——给FAST角点加上了主方向,解决了旋转不变性问题
- 旋转不变性——用rBRIEF描述子,对旋转不敏感
但ORB也有短板。它对尺度变化比较敏感。你想想看,FAST本身就不具备尺度不变性,ORB虽然加了方向,但尺度问题没解决。
我的建议:如果你的场景是固定视角、目标尺度变化不大,ORB是首选。又快又准。但如果目标忽远忽近,还是老老实实用SIFT吧。
4.4 三种方法对比
为了让你看得更清楚,我整理了一张对比表:
| 特性 | Harris | SIFT | ORB |
|---|---|---|---|
| 尺度不变性 | 无 | 强 | 弱 |
| 旋转不变性 | 弱 | 强 | 强 |
| 计算速度 | 快 | 慢 | 非常快 |
| 匹配精度 | 一般 | 高 | 较高 |
| 适用场景 | 简单场景、实时性要求高 | 高精度、多尺度变化 | 实时追踪、嵌入式设备 |
4.5 知识体系图
下面这张图,帮你理清这三种方法的核心逻辑和适用场景:
4.6 代码示例:OpenCV实现
最后,给你一段实际可跑的代码。我用OpenCV实现了三种方法的特征提取和匹配:
import cv2
import numpy as np
# 读取图像
img1 = cv2.imread('target.jpg', 0)
img2 = cv2.imread('scene.jpg', 0)
# 1. Harris角点
gray = np.float32(img1)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
img1_harris = img1.copy()
img1_harris[dst > 0.01 * dst.max()] = [0, 0, 255]
# 2. SIFT特征
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append(m)
img_sift = cv2.drawMatches(img1, kp1, img2, kp2, good[:20], None)
# 3. ORB特征
orb = cv2.ORB_create()
kp1_orb, des1_orb = orb.detectAndCompute(img1, None)
kp2_orb, des2_orb = orb.detectAndCompute(img2, None)
bf_orb = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches_orb = bf_orb.match(des1_orb, des2_orb)
matches_orb = sorted(matches_orb, key=lambda x: x.distance)
img_orb = cv2.drawMatches(img1, kp1_orb, img2, kp2_orb, matches_orb[:20], None)
cv2.imshow('Harris', img1_harris)
cv2.imshow('SIFT Matches', img_sift)
cv2.imshow('ORB Matches', img_orb)
cv2.waitKey(0)
小技巧:用ORB时,记得用NORM_HAMMING距离,别用L2距离。这是BRIEF描述子的特性决定的。我第一次用ORB时踩过这个坑,匹配结果惨不忍睹。
好了,特征提取这部分就讲到这里。三种方法各有千秋,选哪个取决于你的实际场景。我个人建议:先试ORB,不行再换SIFT。Harris嘛,适合做预处理或辅助检测。