计算机视觉(opencv)实战二十——SIFT提取图像特征
🔑 SIFT(尺度不变特征变换)详解与实践
1. SIFT 简介
SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)是 David Lowe 在 1999 年提出的一种局部特征提取算法,它能够在图像中检测到一系列稳定的“关键点”,并为每个关键点生成一段具有独特性的特征描述符。
SIFT 的最大优点是对尺度、旋转、亮度变化和一定程度的仿射变换具有不变性,因此在目标识别、图像拼接、三维重建等任务中非常有用。
2. SIFT 算法原理
SIFT 主要分为 四个步骤:
2.1 尺度空间极值检测
目标:在不同尺度下找到潜在的关键点。
方法:
构建高斯金字塔:对原始图像多次高斯模糊,下采样,得到不同分辨率的图像。
构建高斯差分金字塔(DoG):对相邻的高斯模糊图像做差分,得到 DoG 图像。
在 DoG 图像中寻找局部极值:每个像素与其 3×3 邻域和相邻尺度层的像素比较,如果是最大或最小值,就认为是关键点候选。
2.2 关键点精确定位
对候选关键点使用泰勒展开精确拟合,剔除对比度过低或位于边缘的点,保留稳定的关键点。
2.3 方向分配
在关键点邻域内计算梯度方向直方图,为每个关键点分配一个或多个主方向。
这样可以实现旋转不变性。
2.4 关键点描述符生成
以关键点为中心,选取一定区域,计算梯度幅值和方向。
将区域划分为 4×4 个小块,每个小块计算 8 个方向的梯度直方图,共 4×4×8=128 维特征向量。
对向量进行归一化,增强对光照变化的鲁棒性。
总结:SIFT 特征点是局部、稳定且可重复检测的点,每个点有一个 128 维描述符,适合做特征匹配。
3. SIFT 代码解析
图片准备:
以下代码展示了如何用 OpenCV 检测图像中的 SIFT 特征点,并可视化结果。
import cv2
import numpy as np# 1. 读取图像并转为灰度
man = cv2.imread('face1.jpg') # 读入彩色图像
man_gray = cv2.cvtColor(man, cv2.COLOR_BGR2GRAY) # 转为灰度,SIFT 对灰度图操作# 2. 创建 SIFT 特征检测器
sift = cv2.SIFT_create() # 也可写成 cv2.xfeatures2d.SIFT_create()# 3. 检测关键点
kp = sift.detect(man_gray)
# kp 是一个列表,每个元素是 cv2.KeyPoint 对象,包含关键点信息
# 常用属性:
# kp.pt:关键点坐标 (x, y)
# kp.size:关键点的尺度大小
# kp.angle:关键点的方向
# kp.response:关键点的响应值
# kp.octave:关键点所在金字塔层级# 4. 绘制关键点
man_sift = cv2.drawKeypoints(man, kp, outImage=None,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)
cv2.imshow('sift', man_sift)
cv2.waitKey(0)# 5. 计算描述符
kp, des = sift.compute(man, kp)
print(np.array(kp).shape, des.shape)
# np.array(kp).shape 显示关键点数量
# des.shape 表示描述符的维度,一般是 (关键点数量, 128)
运行结果:
4. 运行结果与说明
可视化效果:你会看到图像上绘制了许多带方向的小圆圈,这些就是检测到的关键点,每个箭头方向表示主方向。
输出形状:
np.array(kp).shape
:关键点个数,例如(245,)
表示一共检测到 245 个关键点。des.shape
:描述符矩阵,例如(245, 128)
,说明有 245 个关键点,每个用 128 维向量描述。
展示 描述符计算 和 两张不同角度图的匹配效果
import cv2 import numpy as np# 1. 读取两张图像(可以用同一张图的不同视角或同一场景的两张图) img1 = cv2.imread('face1.jpg') # 查询图像 img2 = cv2.rotate(img1, cv2.IMREAD_GRAYSCALE) # 目标图像# 转为灰度图 gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# 2. 创建 SIFT 检测器 sift = cv2.SIFT_create()# 3. 检测关键点并计算描述符 kp1, des1 = sift.detectAndCompute(gray1, None) kp2, des2 = sift.detectAndCompute(gray2, None)print(f"图1关键点数:{len(kp1)}, 描述符形状:{des1.shape}") print(f"图2关键点数:{len(kp2)}, 描述符形状:{des2.shape}")# 4. 绘制关键点 img1_kp = cv2.drawKeypoints(img1, kp1, None,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) img2_kp = cv2.drawKeypoints(img2, kp2, None,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)cv2.imshow('Image 1 KeyPoints', img1_kp) cv2.imshow('Image 2 KeyPoints', img2_kp) cv2.waitKey(0)# 5. 特征匹配(使用BF匹配器 + KNN筛选) bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False) matches = bf.knnMatch(des1, des2, k=2) # KNN 匹配,每个点找两个最相近的匹配# 6. Lowe比率测试,筛选优质匹配 good_matches = [] for m, n in matches:if m.distance < 0.75 * n.distance:good_matches.append(m)print(f"匹配到的特征点数:{len(good_matches)}")# 7. 绘制匹配结果 match_img = cv2.drawMatches(img1, kp1, img2, kp2,good_matches, None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)cv2.imshow("SIFT Feature Matching", match_img) cv2.waitKey(0) cv2.destroyAllWindows()
结果:
5. 应用场景
图像拼接:匹配两张图的 SIFT 特征,计算单应矩阵完成拼接。
目标识别:提取特征后可用于训练分类器,识别目标物体。
三维重建:利用多视图中的特征点匹配,恢复场景结构。
✅ 总结:
SIFT 是一种鲁棒、稳定的局部特征描述子,对缩放、旋转、光照变化都不敏感。通过 cv2.SIFT_create()
+ detect()
+ compute()
可以轻松获得关键点和描述符,后续可用于图像匹配和目标识别。