SIFT算法原理详解及代码实现(笔记)

一、SIFT算法概述:
SIFT(Scale Invariant Feature Transform)全称尺度不变特征变换,SIFT算子是把图像中检测到的特征点用一个128维的特征向量进行描述,因此一幅图像经过SIFT算法后表示为一个128维的特征向量集,该特征向量集具有对图像缩放,平移,旋转不变的特征,对于光照、仿射和投影变换也有一定的不变性,是一种非常优秀的局部特征描述算法。
SIFT算法步骤分为:
(1).尺度空间极点检测
(2).关键点精确定位
(3).确定关键点方向
(4).生成特征向量
二、尺度空间极点检测
2.1 尺度空间
特征点的检测就是寻找特征点的位置和尺度,需要位置的原因显而易见,而需要尺度的原因则是因为真实世界中的物体只有在一定尺度下才有意义。我们寻找的特征点就是要找到在连续的尺度空间下位置不发生改变的点。

构建尺度空间的目的就是找到在尺度变化中具有不变性的位置,可以使用连续的尺度变化,即在尺度空间中所有可能的尺度变化中找到稳定的特征点,通过这种方式找到的极点可以保证在图像缩放和旋转变化中具有不变性。

经过前人证明,尺度空间内核是高斯函数。因此假设I(x,y)是原始图像,G(x,y,σ)是尺度空间可变的高斯函数,则一个图像的尺度空间可以定义为:在这里插入图片描述
其中,∗表示的是卷积运算,σ表示尺度空间的大小,σ越大则表示越模糊,表示图像的概貌,σ越小则表示越清晰,表示图像的细节。高斯函数G(x,y,σ)定义为:
在这里插入图片描述
经过一系列的尺度空间变换和二倍的下采样,最终得到高斯金字塔。
需要注意的是公式1中的图像I(x,y)具有无限的分辨率,也就是说他的尺度σ=0,即I(x,y)=L(x,y,0)。也就是说公式1得到的尺度空间图像L(x,y,σ)是由尺度尺度空间为0的图像L(x,y,0)生成的,但是现实生活中是不存在尺度空间为0,即具有无限分辨率的图像的。在Lowe的论文中,他们给定原图一个很小的尺度空间,为0.5。因此由一个小尺度空间图像L(x,y,σ1)生成一个大的尺度空间图像L(x,y,σ2)的过程为:
在这里插入图片描述在这里插入图片描述
2.2 高斯差分(DOG)
高斯差分可以在尺度空间中找到稳定不变的极值点,高斯差分(DOG)函数D(x,y,σ)定义为:
在这里插入图片描述
其中kσ和σ是连续的两个图像的平滑尺度,所得到的差分图像再高斯差分金字塔中。

为何选择高斯差分函数:

  1. 计算简单,因为L(x,y,σ)是一定需要计算的,而D(x,y,σ)只需要执行减法。
  2. 高斯拉普拉斯算子LoG(Laplacian of Gaussian),即图像的二阶导数,能够在不同的尺度下检测到图像的斑点特征,从而检测到图像中尺度变化下的位置不动点,但是LoG的运算效率不高。而DoG是LoG的近似。DoG和LoG的关系如下述所示:
    在这里插入图片描述
    故而:
    在这里插入图片描述
    而σ2∇2G正是尺度归一化算子的表达形式。在所有的尺度中k−1是一个常数,当k趋近于1的时候误差趋近于0,但实际上这种误差对于极值的位置检测并没有什么影响
  3. 通过前人的实验证明LoG提取的特征稳定性最强。

2.3 高斯金字塔与高斯差分金字塔
高斯金字塔和高斯差分金字塔如下图所示:
在这里插入图片描述
这里的几个参数定义如下:
4. 金字塔的组数(number of octaves):大多数情况下为4,但是实际上这个值与图像的大小有关,我在网上查到的资料大多数为⌊log2(min(M,N))⌋−3或者⌊log2(min(M,N))⌋−2,具体实现看效果确定吧。
5. 每层的组数:S1=s+3这里s为极值检测需要的层数,一般取3到5。
6. 参数: 在这里插入图片描述,σ0=1.6

高斯金字塔的生成过程:
设我们输入的图像的尺度为0.5(Lowe论文中设定),由该图像进行高斯模糊得到第0组的第0层图像作为基准图像,设它的尺度为σ0,即Lowe论文中的1.6,我们称σ0为基准层尺度,由第0层生成第1层图像的尺度为kσ0,第1层生成第2层的尺度为k2σ0,依次类推。 那么第0组中的图像的尺度为:
σ=krσ0,r=0,1,…,s+2
当第0组构建完以后,再构建第1组,第1组中的第0层图像是根据第0组的倒数第三张图像进行隔点采样得到的。由上面公式我们可以知道,第0组的倒数第三层的尺度为在这里插入图片描述,而在这里插入图片描述因此其尺度为2σ0,所以第1组的第0张图像的尺度依然为2σ0。
但是第1组中的图像是第0组中的图像经过隔点采样后得到的,因此相对于输入图像分辨率来说,其尺度为2σ0,而对于第1组中的图像分辨率来说,其尺度为σ0。因此我们称σ0为基准层尺度。
上述总结规律如下:
第o组中的第r个图像相对于输入图像的尺度为:
在这里插入图片描述
而DoG金字塔的生成过程就比较简单了,就是由高斯金字塔相邻的两层相减得到DoG金字塔中的一层,然后依次得到。由高斯金字塔中每组有s+3层,所以高斯差分金字塔中每组有s+2层。

金字塔中每一组(octave)中的每层(scale)的平滑尺度都不同,下一组的第一层都是由上一层的倒数第三张的图像隔点降采样得到的。

这样做的目的是使DoG满足尺度连续性。

2.4 极值点的选定
如上图的最右方所示,只有当前点与其周围24个点值相比,如果是最大值或者最小值则该点为极值点,否则不是。这种比较计算量比较小,因为大多数的点在比较的前几步就已经被pass掉了 。

三、特征点的精确定位
计算机中存储的图像数据是离散的,而我们之前找到的极值点也就是离散空间中的极值点,但是离散空间中的极值点并不是真实的连续空间中的极值点。所以需要对DoG空间进行拟合处理,以找到极值点的精确位置和尺度。另外,我们还需要去除那些在边缘位置的极值点,以提高关键点的稳定性。
3.1、位置的插值
下图是二维函数离散空间得到的极值点与连续空间极值点的差别:
在这里插入图片描述
利用已知的离散空间点插值得到的连续空间极值点的方法叫做子像素插值(Sub-pixel Interpolation).对尺度空间DOG函数进行曲线拟合(子像素插值),利用DOG函数在尺度空间的泰勒展开式:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
公式推导如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
极值点的偏移量的求解如下:
在这里插入图片描述
3.2、去除边缘响应

一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率,而在垂直边缘的方向有较小的主曲率。 DOG 算子会产生较强的边缘响应,需要剔除不稳定的边缘响应点。获取特征点处的Hessian 矩阵,主曲率通过一个2x2 的Hessian 矩阵H 求出:
在这里插入图片描述
四、特征点方向分配
对上面提取的每个关键点,围绕该点选择一个窗口(圆形区域),窗口内各采样点的梯度方向构成一个方向直方图,根据直方图的峰值确定关键点的方向。关键点的尺度用来选择哪个高斯滤波图像参与计算,还用来决定窗口的大小——为了保证不同尺度下的同一关键点的方向都包含相同的信息量,那么窗口的大小必然不一样:同一个原始图像,尺度越大,窗口应该越大;反之,如果窗口大小不变,尺度越大的图像,该窗口内的信息越少.
在这里插入图片描述
做一个梯度方向的直方图,范围是0~360度,其中每10度一个柱,总共36个柱。每个采样点按照其梯度方向θ(x,y)加权统计到直方图,权值为幅度m(x,y )和贡献因子的乘积。贡献因子是采样点到关键点(窗口中心)距离的量度,距离越大,贡献因子越小.直方图的峰值代表了该关键点处邻域梯度的主方向.
在这里插入图片描述
Lowe指出,直方图的峰值确定以后,任何大于峰值80%的方向(柱)创建一个具有该方向的关键点,因此,对于多峰值(幅值大小接近)的情形,在同一位置和尺度就会产生多个具有不同方向的关键点。虽然这样的点只占15%,但是它们却能显著地提高匹配的稳定性。用每个峰值和左右两个幅值拟合二次曲线,以定位峰值的实际位置(抛物线的最高点)。峰值方向的精度高于10度。
在这里插入图片描述
所以,关键点的方向分配步骤如下:
在这里插入图片描述
五、描述符的生成
SIFT 描述子是关键点领域高斯图像梯度统计结果的一种表示。通过对关键点周围图像区域分块,计算块内梯度直方图,生成具有独特性的向量,这个向量是该区域图像信息的一种抽象,具有唯一性。 Lowe 建议描述子使用在关键点尺度空间内4×4的窗口中计算的8 个方向的梯度信息,共4×4×8 =128维向量表征.
1、确定计算描述子所需的图像区域
在这里插入图片描述
计算结果四舍五入取整。
2、将坐标轴旋转为关键点的方向,以确保旋转不变性
在这里插入图片描述
旋转后领域内采样点的新坐标为:
在这里插入图片描述
3、将领域内的采样点分配到对应的子区域内,将子区域内的梯度值分配到8 个方向上,计算其权值
在这里插入图片描述
在这里插入图片描述
4、插值计算每个种子点八个方向的梯度
在这里插入图片描述
如上图所示,将所得采样点在子区域中的下标(x’’,y’’)(图中蓝色窗口内红色点)线性插值,计算其对每个种子点的贡献。如图中的红色点,落在第0 行和第1 行之间,对这两行都有贡献。对第0 行第3 列种子点的贡献因子为dr,对第1 行第3 列的贡献因子为1-dr,同理,对邻近两列的贡献因子为dc 和1-dc,对邻近两个方向的贡献因子为do 和1-do。则最终累加在每个方向上的梯度大小为:
在这里插入图片描述
5、描述符向量元素门限化

即把方向直方图每个方向上梯度幅值限制在一定门限值一下(门限一般取0.2)

6、描述符向量元素归一化

特征向量形成后,为了去除光照变化的影响,需要对它们进行归一化处理,对于图像灰度值整体漂移,图像各点的梯度是邻域像素相减得到,所以也能去除。
在这里插入图片描述
六、匹配

采用关键点描述子的欧式距离来作为两幅图像的关键点的相似性度量。
在这里插入图片描述
七、SIFT算法的缺陷

(1)SIFT在求主方向阶段太过于依赖局部区域像素的梯度方向,有可能使得找到的主方向不准确,后面的特征向量提取以及匹配都严重依赖于主方向,即使不大偏差角度也可以造成后面特征匹配的放大误差,从而匹配不成功;
(2)图像金字塔的层取得不足够紧密也会使得尺度有误差,后面的特征向量提取同样依赖相应的尺度,发明者在这个问题上的折中解决方法是取适量的层然后进行插值。
(3)我们知道同样的景物在不同的照片中可能出现不同的形状、大小、角度、亮度,甚至扭曲;计算机视觉的知识表明通过光学镜头获取的图像,对于平面形状的两个物体它们之间可以建立射影对应,对于像人脸这种曲面物体在不同角度距离不同相机参数下获取的两幅图像,它们之间不是一个线性对应关系,就是说我们即使获得两张图像中的脸上若干匹配好的点对,还是无法从中推导出其他点的对应。
https://blog.csdn.net/CXP2205455256/article/details/41747325
https://blog.csdn.net/lhanchao/article/details/52345845#commentsedit

SIFT代码实现:

#include<opencv2/opencv.hpp>
#include<iostream>
#include<vector>
#include<opencv2/xfeatures2d.hpp>


using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;

Mat src, gray_src;
const char* output_title = "SIFT";
int main(int argc, char**argv) {
	src = imread("D:/picture/opencv/images/home.jpg");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	int numFeatures = 400;
	Ptr<SIFT>detector = SIFT::create(numFeatures);
	vector<KeyPoint>keypoints;
	detector->detect(src, keypoints, Mat());
	printf("Total KeyPoints:%d\n", keypoints.size());

	Mat keypoint_img;
	drawKeypoints(src, keypoints, keypoint_img, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
	
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow(output_title, keypoint_img);
	waitKey(0);
	return 0;
}

在这里插入图片描述

  • 29
    点赞
  • 271
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 关于在GitHub上找到SIFT算法的C语言实现代码,以下是一个可能的回答: 在GitHub上,有许多不同的SIFT算法实现代码,可以根据个人的需求和偏好进行选择。 一种常用的SIFT算法实现是VLFeat库(https://github.com/vlfeat/vlfeat)。VLFeat是一个流行的计算机视觉库,提供了各种计算机视觉算法的高效实现。它包含了SIFT特征提取和匹配的C语言实现代码,可以用于图像特征提取和对图像进行匹配和物体检测。VLFeat库还提供了用于处理图像和特征向量的其他功能和实用工具。 在GitHub上,还有其他实现SIFT算法的库和代码,如OpenCV库(https://github.com/opencv/opencv)。OpenCV是一个功能强大的计算机视觉和机器学习库,提供了SIFT算法的C++实现,同时还提供了与其他编程语言的接口。通过OpenCV,你可以使用C++代码实现SIFT特征提取和匹配,并集成到你的项目中。 除了库之外,你也可以在GitHub上找到一些独立的SIFT算法实现。只需在GitHub的搜索栏中输入"SIFT"或者相关关键词,就会显示与SIFT算法相关的代码库和项目。你可以筛选展示的结果,根据项目的活跃程度、代码质量和其他用户的评价来选择适合自己的实现代码。 总的来说,GitHub上有许多实现SIFT算法的C语言代码,可以根据个人需求选择合适的库或者项目来使用。无论是VLFeat库还是OpenCV库,或者其他独立的实现代码,它们都为开发者提供了实现SIFT算法的工具和资源。 ### 回答2: SIFT(Scale-invariant feature transform,尺度不变特征变换)是一种计算机视觉算法,用于在图像中检测和描述局部特征。如要在GitHub上找到用C语言实现SIFT算法代码,可以按照以下步骤进行: 1. 打开GitHub网站 (https://github.com)并登录账号。 2. 在搜索框中输入关键词"SIFT C",点击搜索按钮。 3. 在搜索结果中选择与SIFT算法相关的代码库。可以通过阅读项目的描述、README文件和代码示例来判断是否是我们需要的算法实现。 4. 确保选择的代码库使用C语言作为主要编程语言,以确保我们找到了C语言实现代码。 5. 查看代码库的目录结构,确认是否包含实现SIFT算法的C代码文件。 6. 点击代码文件进行查看,并阅读代码注释和实现逻辑,以确保代码是可理解和可维护的。 7. 如果代码符合要求,下载或克隆整个代码库到本地计算机中,以便进一步进行使用和调试。 需要注意的是,在GitHub上搜索到的代码库可能包含不同版本和不同作者的实现,因此要根据自己的需求选择适合的版本和质量较高的实现。 希望以上回答能帮助您在GitHub上找到合适的SIFT算法C实现代码。 ### 回答3: 在Github上,有许多关于SIFT(尺度不变特征变换)算法的C语言实现代码SIFT是一种计算机视觉算法,可以用于图像特征提取和图像匹配。以下是一个大致描述SIFT算法C语言实现的步骤: 1. 尺度空间构建:使用高斯差分金字塔来模拟图像的尺度空间结构。这个过程可以对输入图像进行多次高斯模糊操作,生成不同尺度的图像。 2. 极值点检测:通过比较像素值与其周围像素值,找出图像中的极值点。这些极值点通常表示图像的显著特征。 3. 关键点定位:对于极值点,使用插值的方法将其精确定位在图像上。这一步骤还会排除掉边缘响应和低对比度的点。 4. 方向分配:对于每个关键点,使用局部图像梯度的方向来确定其主方向。这个方向将用于后续的特征描述。 5. 特征描述:在每个关键点周围的区域内,通过计算局部图像的梯度直方图来描述特征。这一步骤将生成一个具有128维的特征向量。 以上是一个简单描述的SIFT算法的C语言实现步骤。在Github上可以找到许多不同的SIFT算法实现,包括开源库和个人项目。你可以搜索"SIFT C code"或者是具体的项目名称来查找,并根据自己的需求选择适合的代码实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值