第四章:特征提取方法——角点检测(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的流程大致分四步:

  1. 尺度空间极值检测——在不同尺度下找候选点
  2. 关键点定位——去掉低对比度和边缘响应点
  3. 方向分配——给每个关键点一个主方向
  4. 描述子生成——用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 知识体系图

下面这张图,帮你理清这三种方法的核心逻辑和适用场景:

特征提取方法核心逻辑 Harris角点 基于灰度变化 计算速度快 无尺度不变性 适合简单场景 棋盘格、角点检测 SIFT特征 尺度空间极值 128维描述子 精度最高 计算最慢 多视角拼接、高精度匹配 ORB特征 FAST + BRIEF升级 速度最快 有旋转不变性 尺度敏感 实时追踪、嵌入式 选型原则:速度优先选ORB,精度优先选SIFT,简单场景选Harris

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嘛,适合做预处理或辅助检测。


专注资料整理