摘要 :本文深入讲解计算机视觉中两大核心算法------Harris 角点检测 与 SIFT 特征提取,结合完整代码与可视化图示,帮助你从原理到实践全面掌握图像特征检测技术。
目录
- 什么是角点?
- [Harris 角点检测](#Harris 角点检测)
- 原理详解
- 参数说明
- 代码实战
- [SIFT 特征提取](#SIFT 特征提取)
- 原理详解
- 高斯差分金字塔
- 关键点描述符
- 代码实战
- 完整代码
- 总结对比
一、什么是角点?
在图像处理中,角点(Corner) 是指图像中局部区域与周围区域在各方向上都有较大灰度变化 的像素点。

根据灰度变化方向,图像区域分为三类:
| 类型 | 特征 | 说明 |
|---|---|---|
| 平坦区域 | 各方向变化均小 | 无明显特征,难以区分 |
| 边缘区域 | 仅单一方向变化大 | 只能确定垂直边缘方向 |
| 角点区域 | 所有方向变化均大 | 位置唯一确定,可重复检测 ✅ |
角点是图像匹配、目标跟踪、三维重建等任务的基础特征,因为它具有旋转不变性,且在不同视角下容易被重复检测到。
二、Harris 角点检测
2.1 原理详解
Harris 角点检测(1988年由 Chris Harris 提出)通过构造结构张量矩阵 M 来描述局部图像的梯度分布:
M = Σ w(x,y) * [Ix² IxIy]
[IxIy Iy² ]
其中:
- Ix、Iy:像素点在 x、y 方向的梯度(由 Sobel 算子计算)
- w(x,y):高斯窗口权重函数
角点响应函数 R:
R = det(M) - k * trace(M)²
= λ1*λ2 - k*(λ1+λ2)²
det(M) = λ1 * λ2(两个特征值之积)trace(M) = λ1 + λ2(两个特征值之和)k为经验参数,通常取 0.04 ~ 0.06

R 值判定规则:
R >> 0 → 角点(两个特征值均大)
R << 0 → 边缘(一大一小特征值)
|R| ≈ 0 → 平坦区域(两个特征值均小)
2.2 计算流程

整个流程可概括为:
- 将彩色图像转为灰度图 并转换为
float32 - 使用 Sobel 算子计算 x、y 方向梯度 Ix、Iy
- 在窗口内求和,构建结构张量矩阵 M
- 计算每个像素的角点响应值 R
- 对 R 进行阈值筛选,标记角点位置
2.3 API 参数说明
python
dst = cv2.cornerHarris(src, blockSize, ksize, k)
| 参数 | 含义 | 典型值 |
|---|---|---|
src |
输入灰度图(float32) | --- |
blockSize |
邻域窗口大小(越大越宏观) | 2 ~ 5 |
ksize |
Sobel 卷积核大小(奇数) | 3、5、7 |
k |
Harris 自由参数 | 0.04 ~ 0.06 |
dst |
返回角点响应图(float32,与原图同尺寸) | --- |
调参技巧 :
blockSize增大会检测到更宏观的角点;k值越大,对角点的判定越严格(检测到的角点越少但更精确)。
2.4 代码实战
python
import cv2
import numpy as np
# 读取图像并转为灰度
img = cv2.imread("img.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray) # Harris 要求 float32 输入
# 执行 Harris 角点检测
# blockSize=4: 邻域窗口4×4
# ksize=3: Sobel核大小
# k=0.04: 经验参数
dst = cv2.cornerHarris(gray, 4, 3, 0.04)
# 阈值标记:响应值 > 5% 的最大值,标记为红色
img[dst > 0.05 * dst.max()] = [0, 0, 255]
cv2.imshow("Harris角点检测结果", img)
cv2.waitKey(0)
关键点解析:
np.float32(gray):Harris 函数要求输入必须是float32类型dst > 0.05 * dst.max():通过响应值的相对阈值筛选角点,0.05 是经验值,越小检测的角点越多- 满足阈值的像素被标记为
[0, 0, 255](红色,BGR格式)
对于上方的棋盘格图像,Harris 算法能精准定位所有格角交叉点(即真正的角点),而忽略直线边缘区域。
三、SIFT 特征提取
3.1 什么是 SIFT?
SIFT(Scale-Invariant Feature Transform,尺度不变特征变换) 由 David Lowe 于 2004 年提出,是计算机视觉史上最经典的特征提取算法之一。
核心优势:
| 特性 | 说明 |
|---|---|
| 尺度不变性 | 对图像缩放不敏感 |
| 旋转不变性 | 对图像旋转不敏感 |
| 光照鲁棒性 | 对光照变化有一定适应性 |
| 128维描述符 | 丰富的局部纹理信息,适合特征匹配 |
3.2 高斯差分金字塔(DoG)
SIFT 通过构建多尺度高斯差分金字塔来检测尺度空间中的极值点:

构建步骤:
-
建立高斯金字塔:对原图施加不同 sigma 的高斯模糊,得到多个尺度层
-
相邻层相减:同一组内相邻层差分,得到 DoG(Difference of Gaussian)图
-
极值检测:在 DoG 空间中,与26个邻居(3×3×3空间)比较,保留极大/极小值点
-
精确定位:用泰勒展开精细化关键点位置,并去除低对比度点和边缘响应点
DoG(x, y, σ) = G(x, y, kσ) * I(x, y) - G(x, y, σ) * I(x, y)
3.3 关键点方向分配与描述符

方向分配:
- 在关键点邻域内统计梯度方向直方图(36个方向bin)
- 直方图峰值方向作为关键点的主方向(实现旋转不变性)
128维描述符生成:
- 以关键点为中心取 16×16 的邻域
- 划分为 4×4 个子块,每块统计 8个方向的梯度直方图
- 拼接得到 4 × 4 × 8 = 128维特征向量
3.4 代码实战
python
import cv2
import numpy as np
# 读取图像
man = cv2.imread("img.png")
man_gray = cv2.cvtColor(man, cv2.COLOR_BGR2GRAY)
# 创建 SIFT 对象
sift = cv2.SIFT_create()
# 第一步:检测关键点
kp = sift.detect(man_gray, None)
# 关键点属性说明:
# kp.pt -> 关键点坐标 (x, y)
# kp.size -> 关键点尺度(邻域大小)
# kp.angle -> 关键点主方向(0~360度)
# kp.response -> 关键点响应强度
# kp.octave -> 所在金字塔层级
# 第二步:绘制关键点(含尺度和方向信息)
man_sift = cv2.drawKeypoints(
man_gray, kp, None,
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
# DRAW_RICH_KEYPOINTS:绘制圆圈+方向箭头,展示尺度和方向
)
cv2.imshow("SIFT关键点检测", man_sift)
cv2.waitKey(0)
# 第三步:计算描述符
kp, des = sift.compute(man, kp)
print(f"关键点数量: {np.array(kp).shape}")
print(f"描述符形状: {des.shape}") # (N, 128) N个关键点,每个128维

输出解读:
关键点数量: (N,) # N 为检测到的关键点总数
描述符形状: (N, 128) # 每个关键点对应一个128维向量
detectvscomputevsdetectAndCompute
detect():只检测关键点位置,不计算描述符(速度快)compute():对已知关键点计算描述符detectAndCompute():一步完成关键点检测+描述符计算(推荐,效率更高)
四、完整代码
以下是本文涉及的完整代码,来自实际项目
D:/pythonProject/机器视觉/onpencv高阶操作/角点检测.py
python
'''-------------------角点检测----------------------'''
"""
角点是图像中局部区域与周围区域有较大灰度变化的点或像素
dst = cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])
参数说明:
src : 输入图像,必须是单通道灰度图(float32 类型)
blockSize : 角点检测时邻域窗口大小,窗口越大检测的角点越宏观
ksize : Sobel 算子的卷积核大小(必须是奇数:3、5、7)
k : Harris 角点检测的自由参数,经验值范围:0.04 ~ 0.06
dst : 输出角点响应图,float32 类型,与原图同尺寸
"""
import cv2
import numpy as np
img = cv2.imread("img.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 4, 3, 0.04)
# 阈值标记角点:响应值大于最大值的5%的像素点标记为红色
img[dst > 0.05 * dst.max()] = [0, 0, 255]
cv2.imshow("dst", img)
cv2.waitKey(0)
'''-----------------特征提取 SIFT------------------'''
# cv2.SIFT_create() 创建 SIFT 特征提取对象
# sift.detect(img) 在图像中查找关键点
# sift.compute(img, kp) 计算关键点的描述符
man = cv2.imread("img.png")
man_gray = cv2.cvtColor(man, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create()
# 检测关键点
kp = sift.detect(man_gray, None)
# 关键点属性:
# kp.pt 关键点的 (x, y) 坐标
# kp.size 关键点的大小(尺度)
# kp.angle 关键点的方向
# kp.response 关键点的响应值
# kp.octave 关键点所在的金字塔层级
# 绘制关键点(含尺度圆和方向)
man_sift = cv2.drawKeypoints(
man_gray, kp, None,
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)
cv2.imshow("man_sift", man_sift)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 计算描述符(用于后续特征匹配)
kp, des = sift.compute(man, kp)
print(np.array(kp).shape, des.shape)
# 输出:关键点数量 和 (N, 128) 的描述符矩阵

五、总结对比

Harris vs SIFT 横向对比
| 维度 | Harris 角点检测 | SIFT 特征提取 |
|---|---|---|
| 目标 | 检测角点位置 | 检测+描述特征点 |
| 尺度不变 | ❌ 否 | ✅ 是 |
| 旋转不变 | 部分支持 | ✅ 是 |
| 输出 | 响应值图(float32) | 关键点 + 128维描述符 |
| 计算速度 | 快 | 较慢 |
| 适用场景 | 快速角点定位 | 图像匹配、目标识别 |
| OpenCV API | cv2.cornerHarris() |
cv2.SIFT_create() |
使用建议
- 实时性要求高:优先选 Harris 或 ORB(Harris 的升级版,速度更快)
- 精度要求高(图像匹配/拼接):推荐 SIFT 或 SURF
- 嵌入式/移动端:使用 ORB(Binary 描述符,轻量高效)
参考资料
- OpenCV 官方文档 - Feature Detection
- Harris, C., & Stephens, M. (1988). A Combined Corner and Edge Detector.
- Lowe, D.G. (2004). Distinctive Image Features from Scale-Invariant Keypoints. IJCV.
作者 :计算机视觉学习者
环境 :Python 3.x + OpenCV 4.x
代码:来源于机器视觉实践项目