本文架构
- SIFT简介
- SIFT算法的实质
- SIFT算法的特点
- SIFT算法可以解决哪些问题
- SIFT算法的实现步骤
- 尺度空间的获取–高斯模糊(必备知识)
- SIFT算法实现的第一步–尺度空间极值检测
- SIFT算法实现的第二步–关键点定位
- SIFT算法实现的第三步–关键点方向定位(方向确定)
- SIFT算法实现的第四步–关键点描述
- SIFT的缺点
- SIFT实现代码
SIFT简介
- SIFT(Scale-invariant feature transform)是一种电脑视觉的算法,用来侦测与描述影像中的局部性特征,并在空间尺度中寻找极值点,提取出特征在图像中的位置、尺度、以及旋转不变量。
- SIFT是基于物体上的一些局部外观的兴趣点进行检测与影像的大小和旋转无关,同时对于光线、噪声、微视角改变的容忍度也相当高,基于这些特点,很容易辨识图像中的物体而且少有误认。
- SIFT对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。(带着口罩进行人脸识别)
- SIFT应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。
SIFT算法的实质
- SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。
- SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等
SIFT算法的特点
- 稳定性:SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度、视角变化、仿射变换、噪声变化保持不变性
- 独特性:信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配
- 多量性:即使少数的几个物体也可以产生大量的SIFT特征向量
- 高速性:经优化的SIFT匹配算法甚至可以达到实时的要求
- 可扩展性:可以很方便的与其他形式的特征向量进行联合。(比如对位置的限制、对于大小的限制)
SIFT算法可以解决那些问题
- 目标的旋转、缩放、平移(RST)
- 图像仿射/投影变换(视点viewpoint)
- 光照影响(illumination)
- 目标遮挡(occlusion)
- 杂物场景(clutter)
- 噪声
对于以上这些问题,虽然SIFI可以解决,但还是需要考虑外界的干扰因素的(影响识别的准确率)
SIFT算法的实现步骤
- 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。
- 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
- 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
- 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。
尺度空间的获取–高斯模糊
SIFT算法是在不同的尺度空间上查找关键点,而尺度空间的获取需要使用高斯模糊来实现。
可能你会疑惑,什么是尺度空间呢?( ´・ω・)ノ(._.`)
尺度空间的大体意思是:多个尺度下观察目标,然后加以综合的分析和理解(可能还有点抽象)
举个例子:比如放学去食堂,此时食堂就是你的观察目标,在你离食堂越近的时候,你看食堂可能会更清晰(这就是不同尺度下观察食堂),也就是说不同的尺度空间对于目标的观察是不同的(有点扯远了,哈哈)(到后面会详细的讲解什么是尺度空间~ 先了解个大概)
那什么又是高斯模糊呢?
高斯模糊可以理解为一种图像的滤波器,使用正态分布来计算模糊模板(记住就行哈),然后使用这个模板与原图进行卷积运算,从而达到模糊图像的目的(为什么SIFT要模糊图像,我稍后解答)
当然我们还需要了解N维空间的正态分布方程(方便理解~):G(r)=1(2πδ2)Ne−r22δ2G(r) = \frac{1}{(\sqrt {2πδ^2})^N}e^{-\frac{r^2}{2δ^2}}G(r)=(2πδ2)N1e−2δ2r2
- δδδ是正态分布的标准差,δδδ的值越大,图像越是模糊(平滑)
- rrr是模糊半径,就是指模板元素到模板中心的距离(有点抽象哈)
公式有点抽象,我们来举个例子。
有一个二维模板,他的大小是m*n,那么这个模板上的元素(x,y)对应的高斯计算公式是:G(x,y)=1(2πδ2)Ne−(x−m2)2+(y−n2)22δ2G(x,y) = \frac{1}{(\sqrt {2πδ^2})^N}e^{-\frac{(\frac{x-m}{2})^2+(\frac{y-n}{2})^2}{2δ^2}}G(x,y)=(2πδ2)N1e−2δ2(2x−m)2+(2y−n)2
公式这回应该是了解如何使用了(记住公式就得了,咱不搞数学哈(^_−)☆)
有了公式,咱们再搞个可视化看看:
从图像中,我们不难看出在二维空间中,曲面的等高线是从中心开始呈正态分布的同心圆。(这个应该好理解)
我们在回归的卷积的问题,如果一个分布不为零的像素组成的卷积矩阵与原图形做变换,那么得到的结果会是什么呢?
首先,我们回归到卷积的原理上,如果进行卷积,每个像素的值都是周围相邻像素值得加权平均吧(此处无异议)
那么,原图像的像素的值就有了最大的高斯分布值,也就有了最大的权重
当然那些距离原图像像素远的像素值,权重也就是小的(可以理解吧)
那么以上的这波操作,会比其他类型的均衡模糊滤波器更高的保留边缘效果。
(读到这里,你可能会猜想一些事情,模糊图像和SIFT有什么关系呢(✪ω✪),好像有答案啦~)
从理论上来说,如果图形中的每个点的分布都不为零,那可要计算整个图片了!!!!
不过,在实际应用中,计算高斯函数的离散近似的时候,往往将距离大于3δ3δ3δ的像素(距离太远)都看作是不起作用的,通俗的说,就是不用计算(比如上面的那个图像,咱们掐尖计算即可)
通常,图像处理程序只需要计算(6δ+1)∗(6δ+1)(6δ+1)*(6δ+1)(6δ+1)∗(6δ+1)的矩阵就可以保证相关像素的影响(科学家研究的,记住就行啦~)
根据δδδ的值,我们可以计算高斯模板矩阵的大小(6δ+1)∗(6δ+1)(6δ+1)*(6δ+1)(6δ+1)∗(6δ+1),使用G(x,y)=1(2πδ2)Ne−(x−m2)2+(y−n2)22δ2G(x,y) = \frac{1}{(\sqrt {2πδ^2})^N}e^{-\frac{(\frac{x-m}{2})^2+(\frac{y-n}{2})^2}{2δ^2}}G(x,y)=(2πδ2)N1e−2δ2(2x−m)2+(2y−n)2公式,我们可以计算高斯模板矩阵的值,并与原图像做卷积,这样就可以获得了原图像的平滑(高斯模糊)图像
为了确保模板矩阵中的元素在[0, 1]之间,需要将模板进行归一化处理,即可得5*5的高斯模板
卷积示意图:
我们再来看看效果图~~
通过上面的图片,再次确认二维高斯模板实现了模糊图像的目的(。-_-。)
图b和图c 就是因为模板矩阵的关系,从而造成了边缘图像缺失(模糊了~)
δδδ的值越大,缺失的像素也就越多,丢弃模板会造成黑边(图d)
当然,我们也不能忘记一点,δδδ的值越大,咱的计算量也是成几何倍的增长!!!
怎么优化这个计算呢?我们再次引入高斯函数的可分离性~
高斯函数的可分离性是指:
用二维矩阵变换得到的效果也可以等同于在水平和竖直方向各做一次高斯矩阵变换
用公式来解释的话,就是由原来的O(m∗n∗M∗N)O(m*n*M*N)O(m∗n∗M∗N)转换成了O(n∗M∗N)+O(n∗M∗N)O(n*M*N)+O(n*M*N)O(n∗M∗N)+O(n∗M∗N),时间复杂度成几何的形式下降了~
【这里的m、n是高斯矩阵的维数,M、N是二维图像的维数】
两次一维的高斯卷积将消除二维的高斯矩阵所产生的边缘,可以参考下面的卷积示意图
分离高斯模糊的效果图(δ=2.0δ = 2.0δ=2.0)
看到图像也不赖吧,既可以达到降低算法复杂度、又达到了预期的效果,堪称完美~~
(高斯模糊处理图片的方式就先说到这里,不知道小伙伴有没有明白~)
SIFT算法实现的第一步–尺度空间极值检测
尺度空间是使用高斯金字塔表示的(有点抽象,后面会详细的介绍高斯金字塔,这里先了解着)
尺度空间的基本思想是啥呢?
在图像信息处理模型中引入一个被视为尺度的参数,通过连续变化尺度参数,就可以获得多尺度下的尺度空间序列。
我们再对这些序列进行尺度空间主轮廓的提取,并以这个主轮廓作为一种特征向量,实现边缘、角点检测、以及不同分辨率的特征提取等。(额,这里的主轮廓暂且就理解为目标图像的轮廓吧~~)
尺度空间的方法将传统的单尺度图形信息处理技术 纳入 尺度不断变化的动态分析框架中,这样容易获取图像的本质特征了。
尺度空间中各尺度图像的模糊程度逐渐变大的过程,可以理解为模拟人在距离目标由近到远时目标落在视网膜上形成的过程(可以回忆上面所讲的食堂的例子哦~)
(敲黑板、重点来了~)
尺度空间满足视觉不变性。啥又是视觉不变性呢?
当我们用眼睛观察物体的时候
一方面,当物体所处背景的光照条件变化时,视网膜感知的图形的亮度水平和对比度是不同的,所以我们想要实验成功,那么就减少一部分的影响,因此我们需要尺度空间算子对图像的分析不受图像的灰度水平和对比度的变化影响,也就是要满足灰度不变性和对比度的不变性。
另一方面,相对于某一个固定的坐标系,当观察者和物体之间的相对位置发生变化时,视网膜所感知的图像的位置、大小、角度、和形状是不同的,所以呢,我们还需要尺度空间算子对图像的分析和图像的位置、大小、角度以及仿射变换无关,也就是要满足:平移不变性、尺度不变性、欧几里德不变性以及反射不变性等。(晕,好多不变性~)
说到这里可能你会对尺度空间有了大体的了解,那么尺度空间该如何表示呢?
一个图形的尺度空间可以被定义为L(x,y,δ)L(x, y, δ )L(x,y,δ)
L(x,y,δ)=G(x,y,δ)∗I(x,y)L(x, y, δ ) = G(x, y, δ ) * I(x, y)L(x,y,δ)=G(x,y,δ)∗I(x,y)
- G(x,y,δ)G(x, y, δ )G(x,y,δ)是变化尺度的高斯函数
- I(x,y)I(x, y)I(x,y)是原图像
从公式上不难看出,一个尺度空间的定义就是变化尺度的高斯函数与原图像的卷积(“ * ” 表示卷积)
G(x,y,δ)=12πδ2e−(x−m2)2+(y−n2)22δ2G(x,y,δ) = \frac{1}{2πδ^2}e^{-\frac{(\frac{x-m}{2})^2+(\frac{y-n}{2})^2}{2δ^2}}G(x,y,δ)=2πδ21e−2δ2(2x−m)2+(2y−n)2
- m、n表示高斯模板的维度(6δ+1)∗(6δ+1)(6δ+1)*(6δ+1)(6δ+1)∗(6δ+1)
- (x , y) 表示图形的像素位置
- δδδ 是尺度空间因子,值越小表示图像被平滑(模糊处理)的越少,相应的尺度也就越小
大尺度对应与图形的概貌特征、小尺度对应于图形的细节特征 (可以理解吧( ̄▽ ̄)~*)
了解了尺度空间以及其表示法,现在的你可能还为什么是高斯金字塔这个问题感到困惑。
高斯金字塔的构建分为两部分:
- 对图像做不同尺度的高斯模糊
- 对图像做降阶采样(隔点采样)
((ಥ_ಥ) 好抽象,不急,咱们慢慢来)
图像的金字塔模型是指:将原始图像不断的降阶采样,得到一系列大小不一的图像,由大到小,从下到上构成的塔状模型。原图像为金字塔的第一层,每次降阶采样所得到的的新图像为金子塔的一层(每层一张图像),每个金字塔一共n层,金字塔的层数根据图像的原始大小和塔尖图像的大小共同决定。(可能有点抽象,不过下面的图像就可以很好的解释了~)
最底层就是原图,其上的图片就是降阶采样处理的图片,降阶采样处理的计算公式为:n=log2min(M,N)−tn = log_2^{ min(M, N) }-tn=log2min(M,N)−t ,其中t∈[0,log2min(M,N))t∈[0, log_2^{ min(M, N) })t∈[0,log2min(M,N))
M、N为原图像的大小,t为塔顶图像的最小维数的对数值。
举个好理解的例子:一个512∗512512*512512∗512的图像,第零层为原图,第一层就是:256∗256256*256256∗256,以此类推(如下表[以0开始])
图像大小 | 512 | 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
金字塔层数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
(有的小伙伴会疑问,29=5122^9=51229=512 也就是9层,为什么这里是10层呢?本文后面会解答~~)
通过图表我们不难理解,但是在数学里,连续函数总是比离散函数要方便一些
所以,我们让尺度体现出其连续性,便在降阶采样的基础上加上了高斯滤波,将每一层的一张图像使用不同的参数进行高斯模糊,这样原本一层一张图片的高斯金字塔变成了一层为一组图像的高斯金字塔!(上图就是这个意思哦~)
降阶采样时,高斯金字塔上一组图像的初始图像是由前一组的倒数第三张图片隔点采样得到的(为什么是倒数第三张,记住就可以啦,【个人感觉和卷积计算有关(¬_¬)】)
估计现在的你已经明白了什么是高斯金子塔,现在我引进一个术词:高斯差分金字塔
有的小伙伴肯定问高斯差分金字塔是个啥?
慢慢来,我们先要了解什么是高斯差分函数。
高斯差分函数,简称DOG算子,与尺度归一化的高斯拉普拉斯函数非常近(有点抽象,我们可以数学解释一下)
首先:
高斯差分函数:G(x,y,kδ)−G(x,y,δ)G(x, y, kδ) - G(x, y, δ)G(x,y,kδ)−G(x,y,δ)
尺度归一化的高斯拉普拉斯函数是:δ2▽2Gδ ^2▽^2Gδ2▽2G
对δ进行求偏导得:∂G∂δ=δ▽2G\frac{∂G}{∂δ} = δ▽^2G∂δ∂G=δ▽2G
利用差分近似代替微分得:δ▽2G=∂G∂δ≈G(x,y,kδ)−G(x,y,δ)kδ−δδ▽^2G = \frac{∂G}{∂δ} ≈\frac{G(x, y, kδ) - G(x, y, δ)}{ kδ - δ}δ▽2G=∂δ∂G≈kδ−δG(x,y,kδ)−G(x,y,δ)
整理一下公式:G(x,y,kδ)−G(x,y,δ)≈(k−1)δ2▽2GG(x, y, kδ) - G(x, y, δ) ≈ (k-1)δ ^2▽^2GG(x,y,kδ)−G(x,y,δ)≈(k−1)δ2▽2G
k−1k-1k−1是常数项,不影响极值点位置的求取~
上图来比较高斯拉普拉斯和高斯差分的区别~
图片中:红色的曲线代表的是高斯差分算子,蓝色的表示高斯拉普拉斯算子
可能现在你有些明白了,为什么突然冒出个高斯差分金字塔,主要的目的就是优化算法~~
使用高效的高斯差分算子来代替拉普拉斯算子进行检测公式为:
D(x,y,δ)=(G(x,y,kδ)−G(x,y,δ))∗I(x,y)=L(x,y,kδ)−L(x,y,δ)D(x,y,δ) = (G(x, y, kδ )- G(x, y, δ ))* I(x, y) = L(x, y, kδ) - L(x, y, δ)D(x,y,δ)=(G(x,y,kδ)−G(x,y,δ))∗I(x,y)=L(x,y,kδ)−L(x,y,δ)(相当于两个尺度空间做差,有木有|ू・ω・` ))
在实际的计算时,使用高斯金子塔每组中相邻上下两层图像相减,得到高斯差分图像(有点抽象,上图~)
这个就是高斯金子塔的组成喽~
说道这里,有没有小伙伴有疑问,不论是高斯金字塔还是高斯差分金字塔,每一组到底有多少张图像嘞??
首先,我们回忆一下,在降阶采样的时候,我们是不是要从每组的倒数第三张图片进行采样。所以每组至少应该有三张图像,比如每组检测S个尺度的极值点,那么每组需要S+3层图像,对于高斯差分金字塔呢,由于高斯差分金字塔是高斯金子塔相邻的两层相减得到,所以高斯差分金字塔每组需要S+2层图像。(这个是我的个人理解,原理大概也是这个意思(本文后面也会提及),小伙伴可以琢磨一下~~)
好了,好了,到这里尺度空间也了解了,高斯差分金字塔也了解了,那咱们就开始回归正题了,实现空间极值点的检测,也就是关键点的初步探查~
关键点是由DOG空间的布局极值点(DOG函数的极值点)组成的
关键点的初步探查是通过同一组内各DOG相邻两层图像之间比较完成的(毕竟比较才能出最大值嘛~)
为了寻找DOG函数的极值点,每个像素点要和它所有的相邻点进行比较,看其是否比它的图像域和尺度域的相邻点大或者小(有点小抽象,上图)
比如上面这个例子,中间的检测点和它同尺度的8个相邻点上上下对应的18(9*2)个点,共26个点进行比较,以确保在尺度空间和二维图像空间都检测到极值点。
由于要在相邻尺度进行比较,高斯金子塔只能在中间两层进行两个尺度的极值点检测,其他的尺度只能在不同的组中进行。为了能检测S个尺度的极值点,则高斯差分金字塔每组需要S+2层图像,相对于高斯金字塔每组需要S+3层图像,所以计算时S范围在3-5(这段话细品~~)
当然这样产生的极值点并不全是稳定的特征点,因为某些极值点响应较弱,并且DOG算子会产生较强的边缘检测响应。。。
到这里SIFT算法实现的第一步就完事了,我们开始第二步之旅~
SIFT算法实现的第二步–关键点定位
通过第一步的了解,我们明白使用高斯差分金字塔产生的极值有两个问题需要优化
- 极值点响应小
- 会产生较强的边缘检测响应
通过第二步,我们需要进行优化
我们需要三维二次函数来精确的确定关键点的位置和尺度,同时也要去除低对比度的关键点和不稳定的边缘相应点,以增强匹配稳定性、提高考抗噪声能力。(有点复杂,抽象,我们慢慢分析~~)
首先是关键点(极值点)的精确定位
相对于连续空间的极值点来讲,离散空间的极值点并不能说明这个点是真正的极值点(期间的差别,如下图)
注:利用已知的离散空间极值点插值得到的连续空间极值点的方法叫做子像素插值
现在可能大家对极值点的精准检测有了一定的了解(至少要明白离散函数要变成连续函数 -_-||)
那么科学家们又是如何实现关键点的稳定性的呢?原理其实就是曲线拟合
GOD函数在尺度空间的泰勒展开式(拟合函数)为:D(X)=D+∂DT∂XX+12XT∂2D∂X2XD(X)=D+\frac{∂D^T}{∂X}X + \frac{1}{2}X^T\frac{∂^2D}{∂X^2}XD(X)=D+∂X∂DTX+21XT∂X2∂2DX,其中X=(x,y,δ)TX=(x, y, δ)^TX=(x,y,δ)T。
对其进行求导并让方程等于零,可以得到极值点的偏移量为:X^=−∂2D−1∂X2∂D∂X\hat{X} = - \frac{∂^2D^{-1}}{∂X^2}\frac{∂D}{∂X}X^=−∂X2∂2D−1∂X∂D
对应极值点,方程的值为:D(X^)=D+12∂DT∂XX^D(\hat{X}) = D + \frac{1}{2}\frac{∂D^T}{∂X}\hat{X}D(X^)=D+21∂X∂DTX^
其中,X^=(x,y,δ)T\hat{X} = (x, y, δ)^TX^=(x,y,δ)T代表相对查值中心的偏移量,当它在任一维度(xxx或yyy或δδδ)
上的偏移量大于0.5时,也就意味着插值中心已经偏移到它的临近店上了,所以必须改变当前关键点的位置。
同时在新的位置上反复插值直到收敛,如果超出所设定的迭代次数或者超出图像边界的范围,这样的点我们应该删除。~(这里应该好理解)
如果你读到这里,你可能会对关键点的定位原理有了一些的了解,当然我们还需要解决边缘检测响应的问题
一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率,而在垂直边缘的方向有较小的曲率
DOG算子会产生较强的边缘响应,需要提出不需要的边缘响应点。
首先我们需要获取主曲率,我们通过Hessian矩阵进行求解:H=[DxxDxyDxyDyy]H = \begin{bmatrix} D_{xx} & D_{xy} \\ D_xy & D_yy \\ \end{bmatrix}H=[DxxDxyDxyDyy]
HHH的特征值α、βα、βα、β代表x和y方向的梯度
Tr(H)=Dxx+Dyy=α+βTr(H)=D_{xx}+D_{yy}=α+βTr(H)=Dxx+Dyy=α+β
Det(H)=DxxDyy−(Dxy)2=αβDet(H)=D_{xx}D_{yy} - (D_{xy})^2 = αβDet(H)=DxxDyy−(Dxy)2=αβ
假设ααα是较大的特征值,而βββ是较小的特征值,令α=rβα=rβα=rβ,则:Tr(H)2Det(H)=(α+β)2αβ=(rβ+β)2rβ2=(r+1)2r\frac{Tr(H)^2}{Det(H)} = \frac{(α+β)^2}{αβ} = \frac{(rβ+β)^2}{rβ^2} = \frac{(r+1)^2}{r}Det(H)Tr(H)2=αβ(α+β)2=rβ2(rβ+β)2=r(r+1)2
D 的主曲率和H的特征值成正比,假设ααα是最大的特征值,而βββ是最小的特征值,则公式(r+1)2r\frac{(r+1)^2}{r}r(r+1)2的值在两个特征值相等时最小,随之增大而增大
这也可以说明,两个特征值的比值越大,即在某一个方向的梯度值越大,而在另一个方向的梯度值越小,边缘就是这种情况。所以为了剔除边缘响应点,我们需要让该比值小于一定的阈值。(不难理解吧~)
为了检测主曲率是否在某阈值r下,只需要检测:Tr(H)2Det(H)=(r+1)2r\frac{Tr(H)^2}{Det(H)} = \frac{(r+1)^2}{r}Det(H)Tr(H)2=r(r+1)2,如果该式成立则将关键点保留、否则将其剔除。
从图像不难看出,的确有效果哈((σ゚∀゚)σ…:*☆哎哟不错哦)
至此,SIFT算法实现的第二步–关键点定位的原理也就说到这了,其实主要解决的就是那第一步中的两个方面的问题,小伙伴,你明白了么?(^_−)☆ 我们即将开启第三步。
SIFT算法实现的第三步–关键点方向定位
为了使描述符具有旋转不变性,所以我们需要利用图像的局部特征为每一个关键点分配一个方向
使用图像的梯度方法求取局部结构的稳定方向
对于在DOG金字塔中检测出的关键点,采集其所在高斯金字塔图像3δ3δ3δ邻域窗口内像素的梯度和方向分布特征。梯度的模值和方向如下:
模值:m(x,y)=(L(x+1,y)−L(x−1,y))2+(L(x,y+1)−L(x,y−1))2m(x, y) = \sqrt{(L(x+1, y) - L(x-1,y))^2+(L(x,y+1)-L(x,y-1))^2} \quadm(x,y)=(L(x+1,y)−L(x−1,y))2+(L(x,y+1)−L(x,y−1))2
方向:θ(x,y)=tan−1L(x,y+1)−L(x,y−1)L(x+1,y)−L(x−1,y)θ(x,y)=tan^{-1}\frac{L(x,y+1)-L(x,y-1)}{L(x+1,y)-L(x-1,y)}θ(x,y)=tan−1L(x+1,y)−L(x−1,y)L(x,y+1)−L(x,y−1)
(有点像计算空间向量,有木有(๑°艸°๑))
在完成关键点的梯度计算后,使用直方图统计邻域内像素的梯度和方向,
梯度直方图将360度方向范围分为36个柱(bins)其中每个柱子10度,直方图的峰值代表了关键字的主方向。
方向直方图的峰值则代表了该特征点处邻域梯度的方向
以直方图最大值作为该关键点的主方向。
为了增强匹配的鲁棒性(强壮性),只保留了大于主方向峰值80%的方向作为该关键点的辅方向
因此,对于同一梯度值的多个峰值的关键点位置,在相同位置和尺度将会有多个关键点被创建但方向不同。
仅有15%的关键点被赋予多个方向,但可以明显的提高关键点匹配的稳定性。
其实我们在实际的编程实现的时候,就是把该关键点复制成多份关键点,并将方向值分别赋给这些复制后的关键点,并且,离散的梯度方向直方图要进行插值拟合处理,来求得更精确的方向角度值
实现结果如下:
至此,将检测出的含有位置、尺度和方向的关键点即是该图像的SIFT特征点。(你get到了么(・ω・)ノ)
SIFT算法实现的第四步–关键点描述
通过以上步骤,对于每一个关键点,拥有三个信息:位置、尺度以及方向。
接下来就是为每个关键点建立一个描述子,用一组向量将这个关键点描述出来,使其不随各种变化而改变,比如光照变化、视角变化等等
这个描述子不但包括关键点,也包含关键点周围对其有贡献的像素点,并且描述符应该有较高的独特性,以便于提高特征点正确匹配的概率。
描述符使用在关键点尺度空间内44的窗口中计算的8个方向的梯度信息,共44*8=128维向量表征。
步骤如下:
- 确定计算描述子所需的图像区域
- 将坐标轴旋转为关键点的方向,以确保旋转不变性
- 将邻域内的采样点分配到对应的子区间内,将子区域内的梯度值分配到8个方向上,并计算权值
- 插值计算每个种子点八个方向的梯度
- 如上统计的448=128个梯度信息即为该关键点的特征向量
- 描述向量门限
- 按特征点的尺度对特征描述向量进行排序
(1)确定计算描述子所需的图像区域
特征描述子与特征点所在的尺度有关,因此对梯度的求取应在特征点的高斯图像上进行
将关键点附近的邻域划分为d∗dd*dd∗d个子区域,每个区域作为一个种子点,每个种子点有8个方向
每个子区域的大小与关键点方向分配时相同,即每个区域有3δ3δ3δ子像素,为每个子区域分配边长为3δ3δ3δ的矩形区域进行采样。
考虑到实际计算时,需要采用双线性插值,所需图像窗口边长为3δ∗(d+1)3δ*(d+1)3δ∗(d+1),在考虑到旋转因素,实际计算所需的图像区域半径为:
raditus=3δ∗2∗(d+1)2raditus = \frac{3δ*\sqrt{2}*(d+1)}{2}raditus=23δ∗2∗(d+1)计算结果四舍五入取整。
旋转引起的邻域半径变化,如图:
(2)将坐标轴旋转为关键点的方向,以确保旋转不变性
(3)将邻域内的采样点分配到对应的子区间内,将子区域内的梯度值分配到8个方向上,并计算权值
旋转后的采样坐标在半径为radius的圆内被分配到d∗dd*dd∗d的子区域中,计算影响因子区域的采样的梯度和方向,分配到8个方向上。
旋转后的采样点(xt,yt)(x^t,y^t)(xt,yt)落在子区域的下标为:
(xttytt)=13δ(xtyt)+d2\begin{pmatrix} x^{tt} \\ y^{tt} \\ \end{pmatrix} = \frac{1}{3δ}\begin{pmatrix} x^{t} \\ y^{t} \\ \end{pmatrix}+\frac{d}{2}(xttytt)=3δ1(xtyt)+2d
子区域的像素的梯度大小最好按δ=0.5dδ =0.5dδ=0.5d的高斯加权计算,也就是:w=m(a+x,b+y)∗e−(x′)2+(y′)22∗(0.5d)2w = m(a+x,b+y)*e^{-\frac{(x')^2+(y')^2}{2*(0.5d)^2}}w=m(a+x,b+y)∗e−2∗(0.5d)2(x′)2+(y′)2
其中,a、b为关键字在高斯金字塔图像的位置坐标。
(4)插值计算每个种子点八个方向的梯度
如图,所得采样点在子区域中的下标 (图中蓝色窗口内红色点)线性插值,计算其对每个种子点的贡献。如图中的红色点,落在第0行和第1行之间,对这两行都有贡献。对第0行第3列种子点的贡献因子为dr,对第1行第3列的贡献因子为1-dr,同理,对邻近两列的贡献因子为dc和1-dc,对邻近两个方向的贡献因子为do和1-do。则最终累加在每个方向上的梯度大小。
(5)如上统计的448=128个梯度信息即为该关键点的特征向量
特征向量形成后,为了除去光照变化的影响,需要对他们进行归一化处理
对于图像灰度值整体漂移,图像各点的梯度是邻域梯度像素相减得到的,所以也能去除。
(6)描述向量门限
非线性光照,相机饱和度变化对造成某些方向的梯度值过大
而对方向的影响微弱
因此设置门限值(也就是向量归一化)
截断较大的梯度值,然后在进行一次归一化处理,来提高特征的鉴别性。
(7)按特征点的尺度对特征描述向量进行排序
- 把特征点的主方向旋转到x轴的方向,并标记旋转后的特征点的坐标
- 使用(xttytt)=13δ(xtyt)+d2\begin{pmatrix} x^{tt} \\ y^{tt} \\ \end{pmatrix} = \frac{1}{3δ}\begin{pmatrix} x^{t} \\ y^{t} \\ \end{pmatrix}+\frac{d}{2}(xttytt)=3δ1(xtyt)+2d计算旋转后的坐标
- 统计特征点在直方图中的位置,计算权值(公式为:weight=w∗drk∗(1−dr)1−k∗dcm∗(1−dc)1−m∗don∗(1−do)1−nweight = w*dr^k*(1-dr)^{1-k}*dc^m*(1-dc)^{1-m}*do^n*(1-do)^{1-n}weight=w∗drk∗(1−dr)1−k∗dcm∗(1−dc)1−m∗don∗(1−do)1−n)
- 当所有的点都统计完,这个描述符就完成啦~
至此,SIFI算法的特征检测原理及实现的步骤原理都结束了,小伙伴,你明白了么?(σ゚∀゚)σ…:*☆
SIFT的缺点
SIFT在图像的不变特征提取方面拥有无与伦比的优势,但并不完美,仍然存在
- 实时性不高
- 有时特征点较少
- 对边缘光滑的目标无法准确提取特征点
- 对模糊的图像和边缘平滑的图像,检测出的特征点过少,对圆更是无能为力
对于这些问题,当然还有改进算法,比较有名的就是:SURF和CSIFT,对于这两个算法我们下次再聊一聊~。
(END)
实现代码
# -*- coding: utf-8 -*-
"""
第一步:进行高斯模糊,获得不同模糊度的图片
第二步:进行直接的降采样,获得多分辨的图片
第三步:将高斯模糊后的图片进行相减操作获得差分金字塔,使用DOC公式
第四步:对差分金字塔进行上下金字塔的比较,获得极值点
第五步:使用泰勒展开式,获得精确的极值点
第六步:使用herrian公式,通过特征向量变化,比较λ1和λ2的大小,用于消除边界点
第七步:使用sobel算子,计算特征点的梯度大小和梯度方向
第八步:统计特征点临近位置的梯度方向,做出直方图,求出特征点的主要方向
第九步:对应于特征的主要方向,进行旋转,保持特征点大小的方向不变性
第十步:对领域的特征点进行统计,按照4*4的数目,每个区域生成8个方向,即每个方向的出现的次数作为特征,一般使用16个区域,即16*8= 128个特征
shift特征点:用来进行侦查和描述图片的特征,它在空间尺度中寻找极值点,并提取位置,尺度(梯度大小),旋转不变量(方向)
"""
import cv2
import numpy as np
img = cv2.imread('../data/home.jpg') # 读取图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换成灰度值
sift = cv2.xfeatures2d.SIFT_create() # sift为实例化的sift函数
kp = sift.detect(gray, None) # 找出图像中的关键点
"""
kp是关键点,包含的信息如下:
angle:角度,表示关键点的方向,为了保证方向不变形,SIFT算法通过对关键点周围邻域进行梯度运算,求得该点方向。-1为初值。
class_id:当要对图片进行分类时,我们可以用class_id对每个特征点进行区分,未设定时为-1,需要靠自己设定
octave:代表是从金字塔哪一层提取的得到的数据。
pt:关键点点的坐标
response:响应程度,代表该点强壮大小,更确切的说,是该点角点的程度。
size:该点直径的大小
"""
img = cv2.drawKeypoints(gray, kp, img) # 画出图像中的关键点
kp, dst = sift.detectAndCompute(gray,None) # 计算关键点对应的sift特征向量(kp表示输入的关键点,dst表示输出的sift特征向量,通常是128维的)
cv2.imwrite('sift_keypoints.jpg', img)
cv2.imshow('sift_keypoints.jpg', img)
cv2.waitKey(0)
参考于:《Distinctive Image Features from Scale-Invariant Keypoints》