opencv 介绍 SIFT(Scale-Invariant Feature Transform)

本文详细介绍尺度不变特征变换(SIFT)算法的基本概念、原理及其在OpenCV中的应用。SIFT算法能在图像中查找关键点和描述符,具有旋转不变性和尺度不变性等特点。文章通过实例演示了如何使用Python和OpenCV实现SIFT算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标
• 学习 SIFT 算法的概念
• 学习在图像中查找 SIFT 关键点和描述符
原理
  在前面两节我们学习了一些角点检测技术,比如 Harris 等,它们具有旋转不变特性,即使图片发生了旋转,我们也能找到同样的角点,很明显即使图像发生旋转之后角点还是角点。那如果我们对图像进行缩放呢?角点可能就不再是角点了。以下图为例,在一副小图中使用一个小的窗口可以检测到一个角点,但是如果图像被放大,再使用同样的窗口就检测不到角点了。

这里写图片描述

  所以在 2004 年, D.Lowe 提出了一个新的算法:尺度不变特征变换(SIFT),这个算法可以帮助我们提取图像中的关键点并计算它们的描述符。
  SIFT 算法主要由四步构成。我们来逐步进行学习。
1、尺度空间极值检测
  从上图我们可以很明显的看出来在不同的尺度空间不能使用相同的窗口检测极值点。对小的角点要用小的窗口,对大的角点只能使用大的窗口。为了达到这个目的我们要使用尺度空间滤波器。(尺度空间滤波器可以使用一些列具有不同方差 σ 的高斯卷积核构成)。使用具有不同方差值 σ 的高斯拉普拉斯算子(LoG)对图像进行卷积, LoG 由于具有不同的方差值 σ 所以可以用来检测不同大小的斑点(当 LoG 的方差 σ 与斑点直径相等时能够使斑点完全平滑)。简单来说方差 σ 就是一个尺度变换因子。例如,上图中使用一个小方差 σ 的高斯卷积核是可以很好的检测出小的角点,而使用大方差 σ 的高斯卷积核时可以很好的检测除大的角点。所以我们可以在尺度空间和二维平面中检测到局部最大值,如(x, y, σ), 这表示在 σ 尺度中(x, y)点可能是一个关键点。(高斯方差的大小与窗口的大小存在一个倍数关系:窗口大小等于 6 倍方差加 1,所以方差的大小也决定了窗口大小)但是这个 LoG 的计算量非常大,所以 SIFT 算法使用高斯差分算子(DoG)来对 LoG 做近似。这里需要再解释一下图像金字塔,我们可以通过减少采样(如只取奇数行或奇数列)来构成一组图像尺寸(1, 0.5, 0.25 等)不同的金字塔,然后对这一组图像中的每一张图像使用具有不同方差 σ 的高斯卷积核构建出具有不同分辨率的图像金字塔(不同的尺度空间)。 DoG 就是这组 具有不同分辨率的图像金字塔中相邻的两层之间的差值。如下图所示:
这里写图片描述

  在 DoG 搞定之后,就可以在不同的尺度空间和 2D 平面中搜索局部最大值了。对于图像中的一个像素点而言,它需要与自己周围的 8 邻域,以及尺度空间中上下两层中的相邻的 18(2x9)个点相比。如果是局部最大值,它就可能是一个关键点。基本上来说关键点是图像在相应尺度空间中的最好代表。如
下图所示:
这里写图片描述

  该算法的作者在文章中给出了 SIFT 参数的经验值: octaves=4(通过降低采样从而减小图像尺寸,构成尺寸减小的图像金字塔(4 层)),尺度空间为 5,也就是每个尺寸使用 5 个不同方差的高斯核进行卷积,初始方差是 1.6,k 等于 根号2等。
2、关键点(极值点)定位
一旦找到关键点,我们就要对它们进行修正从而得到更准确的结果。作者使用尺度空间的泰勒级数展开来获得极值的准确位置,如果极值点的
灰度值小于阈值(0.03)就会被忽略掉。在 OpenCV 中这种阈值被称为contrastThreshold。DoG 算法对边界非常敏感,所以我们必须要把边界去除。前面我们讲的算法除了可以用于角点检测之外还可以用于检测边界。
  作者就是使用了同样的思路。作者使用 2x2 的 Hessian 矩阵计算主曲率。从 Harris 角点检
测的算法中,我们知道当一个特征值远远大于另外一个特征值时检测到的是边界。所以他们使用了一个简单的函数,如果比例高于阈值(OpenCV 中称为边界阈值),这个关键点就会被忽略。文章中给出的边界阈值为 10。所以低对比度的关键点和边界关键点都会被去除掉,剩下的就是我们感兴趣的关键点了。
3、为关键点(极值点)指定方向参数
  现在我们要为每一个关键点赋予一个反向参数,这样它才会具有旋转不变性。获取关键点(所在尺度空间)的邻域,然后计算这个区域的梯度级和方向。根据计算得到的结果创建一个含有 36 个bins(每 10 度一个 bin)的方向直方图。(使用当前尺度空间 σ 值的 1.5 倍为方差的圆形高斯窗口和梯度级做权重)。直方图中的峰值为主方向参数,如果其他的任何柱子的高度高于峰值的80% 被认为是辅方向。这就会在相同的尺度空间相同的位置构建除具有不同方向的关键点。这对于匹配的稳定性会有所帮助。
4、关键点描述符
  新的关键点描述符被创建了。选取与关键点周围一个 16x16 的邻域,把它分成 16 个 4x4 的小方块,为每个小方块创建一个具有 8 个 bin 的方向直方图。总共加起来有 128 个 bin。由此组成长为 128 的向量就构成了关键点描述符。除此之外还要进行几个测量以达到对光照变化,旋转等的稳定性。
5、关键点匹配
  下一步就可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取第一个图的某个关键点,通过遍历找到第二幅图像中的距离最近的那个关键点。但有些情况下,第二个距离最近的关键点与第一个距离最近的关键点靠的太近。这可能是由于噪声等引起的。此时要计算最近距离与第二近距离的比值。如果比值大于 0.8,就忽略掉。这会去除 90% 的错误匹配,同时只去除 5% 的正确匹配。如文章所说。
这就是 SIFT 算法的摘要。非常推荐你阅读原始文献,这会加深你对算法的理解。请记住这个算法是受专利保护的。所以这个算法包含在 OpenCV 中的收费模块中。
OpenCV 中的 SIFT
  现在让我们来看看 OpenCV 中关于 SIFT 的函数吧。让我们从关键点检测和绘制开始吧。首先我们要创建对象。我们可以使用不同的参数,这并不是必须的,关于参数的解释可以查看文档。

import cv2
import numpy as np 

img = cv2.imread('image/22.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT()
kp = sift.detect(gray,None)
img = cv2.drawKeypoints(gray, kp)
cv2.imwrite('sift_keypoint.jpg', img)

  函数 sift.detect() 可以在图像中找到关键点。如果你只想在图像中的一个区域搜索的话,也可以创建一个掩模图像作为参数使用。返回的关键点是一个带有很多不同属性的特殊结构体,这些属性中包含它的坐标(x, y),有意义的邻域大小,确定其方向的角度等。
  OpenCV 也提供了绘制关键点的函数: cv2.drawKeyPoints(),它可以在关键点的部位绘制一个小圆圈。如果你设置参数为 cv2.DRAW_MATCHES_FLAGS_DRAW_RI就会绘制代表关键点大小的圆圈甚至可以绘制除关键点的方向。
结果图:

这里写图片描述

  现在来计算关键点描述符, OpenCV 提供了两种方法。
1. 由于我们已经找到了关键点,我们可以使用函数 sift.compute() 来计算这些关键点的描述符。例如: kp; des = sift:compute(gray; kp)。
2. 如果还没有找到关键点,可以使用函数 sift.detectAndCompute()一步到位直接找到关键点并计算出其描述符。
这里我们来看看第二个方法:
这里 kp 是一个关键点列表。 des 是一个 Numpy 数组,其大小是关键点数目乘以 128。所以我们得到了关键点和描述符等。
参考:Opencv官方教程中文版(For Python)

<think>好的,我现在要处理用户关于SIFT算法的详细流程说明和C++实现代码的请求。首先,我需要回顾用户的历史对话,确保我理解他们的需求。用户之前询问了近似解求解方法,现在转向SIFT算法,说明他们可能在学习计算机视觉或图像处理相关的内容。用户提到是初学者,所以需要详细且易于理解的解释。 首先,我需要整理SIFT算法的详细流程。根据用户提供的引用[1]和[2],SIFT主要涉及尺度空间构建、关键点检测、方向分配、描述子生成等步骤。用户提供的引用提到高斯模糊的重要性,以及OpenCV中的接口函数。因此,在解释流程时,需要强调高斯金字塔的构建,以及如何通过不同尺度空间找到关键点。 接下来,用户需要C++的实现代码。考虑到他们可能使用的是OpenCV,我需要确保代码示例基于OpenCVSIFT类。用户之前的示例中提到了detect和compute函数,所以代码应该包括这些函数的调用,以及特征点绘制和描述子输出的部分。同时,需要指出OpenCV版本的要求,比如4.4.0之后SIFT被移到contrib模块,这可能会影响用户的配置,需要提醒他们注意。 用户是初学者,可能对某些术语不太了解,比如DoG、关键点方向直方图等,需要在解释时用简单的语言说明。例如,在构建高斯金字塔时,要解释为什么使用不同尺度的高斯核,以及如何通过降采样来构建不同的octave。在关键点检测部分,需要说明极值点的检测过程,以及如何去除边缘响应和低对比度的点,以确保关键点的稳定性。 另外,用户可能对方向分配和描述子生成的具体步骤感兴趣。这里需要解释如何计算梯度方向和幅值,构建方向直方图,从而确定关键点的主方向。在描述子部分,要说明如何将周围区域分成子块,生成直方图,最终形成128维的特征向量。这部分需要保持技术细节的准确性,同时避免过于复杂的数学公式,或者将公式用LaTeX格式正确呈现,符合用户之前的系统级指令要求。 在代码实现部分,需要确保示例代码的完整性和可运行性。包括头文件的引用、命名空间的使用、读取图像、创建SIFT检测器、检测关键点、计算描述子,以及结果的可视化和保存。此外,代码中的注释要清晰,帮助用户理解每一步的作用。例如,detect函数用于找到关键点,compute函数生成描述子,drawKeypoints用于可视化。 还需要注意用户可能的开发环境,比如OpenCV的安装和配置。由于SIFT在较新的OpenCV版本中属于contrib模块,用户可能需要额外安装opencv_contrib,并在编译时启用非免费模块。这一点在注意事项中需要明确说明,避免用户运行时出现错误。 最后,生成的相关问题需要与SIFT算法相关,帮助用户进一步深入学习。例如,关于高斯金字塔的作用、关键点方向分配的重要性、描述子生成的优势,以及SIFT的优化方法。这些问题应该覆盖算法的不同方面,引导用户探索更多细节。 总结来说,我需要确保回答结构清晰,分步骤解释SIFT流程,提供可运行的C++代码,并在适当的位置引用用户提供的参考文献,同时符合用户的中文回答要求和格式规范。要注意避免使用过多专业术语,保持解释的易懂性,同时技术细节准确无误。此外,检查LaTeX格式是否正确,行内公式用$...$,独立公式用$$...$$,并确保代码块语法正确。</think>### **SIFT算法详解与C++实现指南** --- #### **一、SIFT算法核心流程** SIFTScale-Invariant Feature Transform)算法通过**尺度空间特征提取**实现图像关键点的鲁棒检测与描述。其流程分为四个主要阶段: --- ##### **1. 尺度空间构建(Scale-Space Construction)** - **高斯金字塔生成** 通过不同尺度的高斯核卷积图像,构建多组(Octave)多层的尺度空间: $$ L(x,y,σ) = G(x,y,σ) * I(x,y) $$ 其中$G(x,y,σ) = \frac{1}{2πσ^2}e^{-\frac{x^2+y^2}{2σ^2}}$为高斯核,$σ$为尺度参数[^2]。 - **每组降采样**:每组最后一层图像降采样为原尺寸的1/2,生成下一组。 - **差分高斯金字塔(DoG)计算** 通过相邻高斯层相减得到DoG图像,用于关键点检测: $$ D(x,y,σ) = L(x,y,kσ) - L(x,y,σ) $$ *DoG响应在边缘和斑点区域显著*[^2]。 --- ##### **2. 关键点检测与精炼** - **极值点检测** 在DoG金字塔中,每个像素与其**26邻域**(相邻尺度与空间邻域)比较,若为极大/极小值则标记为候选关键点。 - **关键点精炼** - **低对比度剔除**:通过泰勒展开修正极值位置,若$|D(x)| < 0.03$(归一化阈值)则舍弃。 - **边缘响应剔除**:利用Hessian矩阵特征值比值消除边缘点(如比值大于10则剔除)[^2]。 --- ##### **3. 方向分配(Orientation Assignment)** - **梯度计算** 在关键点所在高斯尺度层,计算邻域像素的梯度幅值和方向: $$ m(x,y) = \sqrt{(L(x+1,y)-L(x-1,y))^2 + (L(x,y+1)-L(x,y-1))^2} \\ θ(x,y) = \arctan\left(\frac{L(x,y+1)-L(x,y-1)}{L(x+1,y)-L(x-1,y)}\right) $$ - **方向直方图** 将360°划分为36个区间(每10°一区间),统计梯度幅值加权直方图。 - **主方向选择**:直方图峰值对应方向作为关键点主方向。 - **多方向分配**:若存在次峰(>80%主峰幅值),则为同一位置生成多个关键点。 --- ##### **4. 描述子生成(Descriptor Generation)** - **旋转坐标系对齐** 将关键点邻域旋转至主方向,确保旋转不变性。 - **子区域划分** 将16×16邻域划分为4×4子块,每个子块计算8方向梯度直方图(共4×4×8=128维)。 - **归一化处理** 对128维向量进行归一化,增强光照不变性,并截断大于0.2的值以抑制大梯度影响。 --- #### **二、C++实现代码(基于OpenCV)** ```cpp #include <opencv2/opencv.hpp> #include <opencv2/xfeatures2d.hpp> // SIFT位于xfeatures2d模块 using namespace cv; using namespace cv::xfeatures2d; int main() { // 1. 读取图像 Mat img = imread("image.jpg", IMREAD_GRAYSCALE); if (img.empty()) { std::cerr << "图像加载失败!" << std::endl; return -1; } // 2. 创建SIFT检测器 Ptr<SIFT> sift = SIFT::create(); // OpenCV 4.4+需启用contrib模块 // 3. 检测关键点并计算描述子 std::vector<KeyPoint> keypoints; Mat descriptors; sift->detectAndCompute(img, noArray(), keypoints, descriptors); // 4. 可视化关键点 Mat img_keypoints; drawKeypoints(img, keypoints, img_keypoints, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS); // 5. 保存结果 imwrite("sift_keypoints.jpg", img_keypoints); FileStorage fs("descriptors.yml", FileStorage::WRITE); fs << "descriptors" << descriptors; fs.release(); return 0; } ``` --- #### **三、代码解析与注意事项** 1. **OpenCV配置** - OpenCV 4.4.0之后,SIFT被移至**opencv_contrib/xfeatures2d**模块,需编译时启用`-DOPENCV_ENABLE_NONFREE=ON`。 - 安装命令示例: ```bash pip install opencv-contrib-python==4.5.5.64 # Python版本 ``` 2. **关键函数说明** - `detectAndCompute()`:同时执行关键点检测与描述子计算。 - `KeyPoint`结构体:包含坐标(x,y)、尺度(size)、方向(angle)等属性。 - 描述子矩阵`descriptors`:每行为一个关键点的128维向量。 --- #### **四、SIFT算法特性分析** | **特性** | **实现方式** | **优势** | |------------------|---------------------------------------|------------------------------| | 尺度不变性 | 多尺度DoG极值检测 | 适应图像缩放 | | 旋转不变性 | 主方向对齐 | 抵抗旋转变换 | | 光照鲁棒性 | 梯度方向直方图归一化 | 对亮度变化不敏感 | | 抗噪声能力 | 高斯平滑预处理 | 抑制高频噪声干扰 | --- ### **相关问题** 1. 高斯金字塔中每组降采样的目的是什么?如何避免信息丢失[^2]? 2. 为何使用DoG而非直接比较高斯层差异进行极值检测? 3. SIFT描述子为何选择128维?更高维度是否一定更好? 4. 如何优化SIFT算法以提升实时性(如SURF、ORB的改进思路)[^1]? --- 通过结合多尺度分析与梯度统计特性,SIFT在目标识别、图像配准等领域仍具有重要价值[^1][^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值