sift算法matlab代码详解

编程实验

72人已加入

描述

  sift算法解析

  SIFT综述

  尺度不变特征转换(Scale-invariant feature transform或SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe在1999年所发表,2004年完善总结。

  其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。

  此算法有其专利,专利拥有者为英属哥伦比亚大学。

  局部影像特征的描述与侦测可以帮助辨识物体,SIFT 特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、些微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用 SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。SIFT特征的信息量大,适合在海量数据库中快速准确匹配。

  SIFT算法的特点有:

  1、SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;

  2、独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;

  3、 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;

  4、 高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;

  5、可扩展性,可以很方便的与其他形式的特征向量进行联合。

  SIFT算法可以解决的问题:

  目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。而SIFT算法在一定程度上可解决:

  1、目标的旋转、缩放、平移(RST)

  2、图像仿射/投影变换(视点viewpoint)

  3、光照影响(illumination)

  4、目标遮挡(occlusion)

  5、杂物场景(clutter)

  6、噪声

  SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

  SIFT的缺点

  SIFT在图像的不变特征提取方面拥有无与伦比的优势,但并不完美,仍然存在:

  1、实时性不高。

  2、有时特征点较少。

  3、对边缘光滑的目标无法准确提取特征点。

  sift算法matlab代码的实现

  1、构建尺度空间

  定理1 对图像做 σ=σ1 的高斯平滑,再做一次 σ=σ2 的高斯平滑,等效于对原图像做一次matlab的高斯平滑。

  1.1、构建高斯金字塔

  高斯卷积核是实现尺度变换的唯一线性核(Koenderink, 1984; Lindeberg, 1994)。

  一幅图像的尺度空间被定义为对其做可变尺度的高斯卷积:

  matlab

  对于给定的彩色图像,转化为灰度图像,用不同大小的σ做高斯平滑(按照 3σ 准则,高斯核矩阵的大小设为 (6σ+1)⋅(6σ+1) ,并保证行和列为奇数),再此基础上将图像降采样得到不同大小的组(octave),每组若干图像(interval)。详细描述如下:

  为了得到更多的特征点,将图像扩大为原来的两倍。假设原图像已有 σ=0.5 的高斯平滑,而我们需要第一个octave的第一张图像的 σ=1.6 ,按照定理1,我们要对扩大两倍的图像做一次高斯平滑,matlab

  上一个octave的图像的长度和宽度分别是下一个octave的图像的两倍。因此图像组数(octaves)可由图像大小决定,将其设为 log2(min(height,width)) − 2 ,这样将使顶层octave图像的长度和宽度最小值在8像素左右。

  设第m个octave的第n张图像相对于原始图像的参数σ为 sigma(m,n),则sigma(1,1)=σ0=1.6。每个octave有s+1张图像(即intervals),这样得到的高斯差分金字塔(DoG)每个octave将有s张图像,我们设s为3。为了满足在不同octave间尺度的连续性,并使 sigma(m,n)= 2⋅sigma(m−1,n),按照定理1,则:

  matlab

  matlab

  如上图所示,在第一个octave中尺度为k3⋅σ0的“最后”一张图像进行下采样得到第二个octave的第一张图像,尺度仍为k3⋅σ0=2⋅σ0。

  但实际上我们需要做出更多不同尺度的高斯平滑图像,这是因为在后续高斯差分金字塔的极值检测中,需要前后两级尺度都存在图像。如图中红框所示,高斯差分金字塔中每个octave有s幅图像,则需要高斯金字塔中每个octave包含s+3幅图像。其中第s+1幅图像用作下一个octave第一幅图像的降采样。

  具体实现中并未对单幅图像多次进行高斯平滑,而是由上一幅图像进行高斯平滑得到下一幅图像并迭代之,按照定理1计算σ。

  1.2 构建高斯差分金字塔

  对两幅高斯金字塔的图像作差。

  1.3 检测极值点

  matlab

  如上图,与前后两幅图像及自身的共26个邻域像素点比较灰度值检测极值。

  2、关键点精确定位

  检测到的极值点是离散的,通过三元二次函数拟合来精确确定关键点的位置和尺度,达到亚像素精度。以某关键点为中心的尺度空间函数 D(x,y,intvl) 的二次泰勒展开式为:

  matlab

  其中等号右边第一个D为某关键点处的灰度值, X=(x,y,intvl)T 是以此点为中心的偏移量,由于 D(X) 是离散的,其导数用差分法求得。令 D(X) 导数为零,得到精确极值位置的偏移量为:

  matlab

  在任意一个维度大于0.5,说明极值点精确位置距离另一个点更近,应该将关键点定位于更近的那个位置。定位到新点后再进行相同操作,若迭代5次位置仍不收敛,则不认为此点为关键点。设定图像边缘img_border,若关键点落在图像边缘区域(以img_border为宽度的矩形外框)也不认为此点为关键点。

  2.1 去除低反差(low contrast)的点精确极值点处函数值:

  matlab

  同样不认为此点是极值点。在此过程中保存极值点的数据ddata,为特征的构建做准备。计算出σ_octv,即位于一个相同的octave内的尺度,某个octave内第n张图像的 σ_octv=σ0⋅kintvl−1 ,此处intvl为精确定位后的intvl。

  2.2 消除边缘响应

  高斯差分函数有较强的边缘响应,对于比较像边缘的点应该去除掉。这样的点的特征为在某个方向有较大主曲率,而在垂直的方向主曲率很小。

  设r为大主曲率与小主曲率的比值,H为关键点处的Hessian矩阵,则有(具体推导可见Lowe的论文):

  matlab

  其中rt为一阈值,设为10说明此处r较小,认为此关键点不位于边缘,否则则去除该点。

  3、方向指定

  根据关键点的局部特性为每个关键点指定一个方向,可以具备旋转不变性。关键点局部特性在检测到关键点的高斯差分金字塔图像临近的高斯金字塔图像中计算。在关键点3σ邻域窗口计算梯度和方向分布,计算方式如下:

  matlab

  此处的x正方向向右,y正方向向上。其中L为关键点在上述精确定位后所处尺度的灰度值,m(x,y)为梯度的幅值,θ(x,y)为关键点处梯度方向的弧度(范围为(−π,π])。将360度的方向划分为36个区域(bins),第一个区域的范围是[35π36,37π36),按逆时针方向依次划分。对m(x,y)按 σ=1.5σ_octv 的高斯分布,在 3σ=3⋅1.5σ_octv 的邻域窗口加权计算,得到36个方向的直方图。然后对直方图进行两次平滑处理,即按0.25,0.5,0.25的大小对每3个连续的bin加权两次。

  直方图最大值的方向代表该关键点的主方向,对于其他峰值,若大于或等于主方向值的80%,则再分配一个方向。所以对于一个关键点,可能会有多个对应的方向,将带有方向的关键点定义为feature,则一个关键点可能对应多个feature。由于第一个octave是双倍大小的图像,feature的坐标和尺度应转换到原始图像所在的octave处理。最后用抛物插值精确定位feature的方向。

  对于x为-1,0,1,y为l,c,r的三个点来说,抛物插值得到极值点的x为:

  matlab

  上一步已得到具有主方向的关键点,即feature,下一步是对feature的邻域进行采样,形成对该局部图像的描述,然后可用某种度量方法对描述进行匹配。

  Lowe提出的sift描述子是一个 4×4×8=128 维的向量。描述子的数学形式可定义为 h(x,y,θ) ,其中的x,y代表 4×4=16 个图像区域的位置,θ即梯度方向,只能取8个值。h(x,y,θ)的值就是在(x,y)代表的图像区域计算得到的在θ方向的梯度大小。

  4.1、描述子采样区域

  这16个图像区域中的每一个区域均为 3σ_octv 像素,因此16个区域的半边长为 4×3σ_octv/2 ,考虑到后续操作需要三线性插值,采样区域半边长设为 (4+1)×3σ_octv/2 ,又由于旋转操作,这个值需要乘以2√,得到 radius=(4+1)× 2√×3σ_octv/2 。

  如下图所示,图中 m=3, Bp=4,σ=σ_octv 。

  matlab

  4.2、旋转至主方向

  为了使描述符具有旋转不变性,将坐标轴旋转至关键点主方向。设i,j分别为采样点相对关键点的行偏移量和列偏移量,i = -radius:radius,j = -radius:radius,关键点左上角i和j均为负数。关键点主方向为θ,范围是(−π,π]。

  定理2 在右手平面直角坐标系中,向量(x,y)逆时针旋转θ,得到的向量(x’,y’)为:

  matlab

  在左图以关键点为中心建立右手平面直角坐标系o-uv,u的正方向与左图x方向相同,v的正方向与左图y方向相反。左图中x=(1,0),y=(0,-1),将x,y代入定理2的公式,得到右图中 x′=(cosθ,sinθ), y′=(sinθ,−cosθ) 。其中θ为左图坐标系旋转到右图坐标系的角度,在上图中为一负数。设图像中有一点按左图的x,y可表示为 j⋅x+i⋅y ,按右图中的x′,y′可表示为 j′⋅x′+i′⋅y′ ,则有:

  matlab

  得到新的行、列偏移量后,将 3σ_octv 设为单位长度,并将中心转移至最左上角区域中心,得到新的坐标r_bin和c_bin。对梯度方向弧度值减去主方向弧度,并设 2π8 为一个单位,得到o_bin。

  采样点的梯度幅值按照 σ=0.5⋅4⋅3σ_octv (即16个区域边长的一半)的高斯函数加权:

  matlab

  其中a,b为关键点在高斯金字塔图像中的位置坐标。

  4.3、三线性插值

  上述过程中构造了一个三维的bin空间,如4.1中右图所示,维度包括r_bin,c_bin和o_bin。注意最上层格子和最底层格子是相连的,因为0度等于360度。所有带有三维坐标的梯度幅值都将分配到三维格子里。

  为了减少一个梯度幅值从一个格子漂移(shift)到另一个格子引起的描述子突变,需要对梯度值做三线性插值。也就是根据三维坐标计算距离周围格子的距离,按距离的倒数计算权重,将梯度幅值按权重分配到临近的格子里。

  matlab

  某点在三维bin空间的坐标为(r_bin,c_bin,o_bin),求出r=⌊r_bin⌋, c=⌊c_bin⌋, o=⌊o_bin⌋, dr=r_bin−r, dc=c_bin−c, do=o_bin−o,它的梯度幅值最多可能分配到周围的8个格子中。计算公式如下:

  matlab

  1−k其中i,j,k均可取0或1,weightedValue下标加1的目的是使下标从1开始。

  为简化计算,可改为:

  matlab

  4.4、生成描述子

  将上述直方图数组按顺序排列可转换为一个128维的向量。

  为了减少光照变化的影响,对该向量进行归一化处理。非线性光照变化仍可能导致梯度幅值的较大变化,然而影响梯度方向的可能性较小。因此对于超过阈值0.2的梯度幅值设为0.2,然后再进行一次归一化。最后将描述子按照对应高斯金字塔图像的尺度大小排序。

  5、匹配

  描述子向量已经归一化,所以可直接用向量之间的夹角进行匹配,相当于球面距离。图像A 的描述子匹配图像B最近的两个描述子点积之比小于0.6,则认为匹配成功。

      6、总结

  6.1、性能优化

  因为用的是Matlab,所以不注重性能。然而又不得不注重性能,因为第一次跑通程序的时候跑了一晚上都没跑完一半!也就是一张图片的描述子都没算完。后来发现是因为在运行次数最多的for循环(描述子计算中的梯度计算)里用到了cell数组。把对这个cell数组的查询操作提到两重循环前以后,这个程序好像跑了半个小时左右跑出结果了。然而还是太慢,于是我又用Matlab的计时分析工具分析了程序最耗时的地方:

  1)把cell数组的查询尽可能减少

  2)充分利用Matlab的向量操作

  3)一些没用的参数给去掉了(如计算梯度时的三个返回值合并到了一个二维数组)

  4)一个三维数组折叠成了一维的(hist)

  程序里用了很多全局变量,是因为我把函数分成了文件而不是放在一个文件,为了节省点内存(以及方便)只能这么做(虽然据说Matlab在不改变变量的情况下函数传值等于引用,然而我并不清楚究竟是怎样的)。把for循环换成parfor的时候提示,parfor里似乎不推荐用全局变量,而且实际运行的时候全局变量似乎也会影响性能,于是我把全局变量复制成了局部的再放进parfor里。

  我还发现一个奇葩的问题,在运行次数最多的计算梯度的函数里用zeros(1,2)创建一个数组竟然也耗时非常多,改成[0 0]就好了。

  经过这些修改后,在开启parallel pool的情况下运行时间缩短到了7分钟左右。(然而Lowe的C语言版本只要十几秒

  6.2 运行结果

  

  这次作业老师给的是两张768x1024的图片,分别检测到5288和4798个特征点,最后匹配了906对点。用Lowe的siftDemoV4跑出来的结果是1252对匹配。

  这个程序的参数基本都是参照opensift,但最后的匹配用的是Lowe的方案。Lowe的实现毕竟不太一样,运行的结果和opensift有一些差异。以下是匹配siftDemoV4.zip里的scene.pgm和book.pgm的结果:

  matlab

  sw-sift和opensift的区别主要是在高斯平滑和匹配算法上。opensift的高斯平滑用的是OpenCV的CVSmooth函数,匹配用的是欧式距离(而且把描述子乘以512从double类型转成了int)。和opensift相比,sw-sift检测到的特征点数量很接近,但是匹配数量较少,所以可改进的地方主要是匹配算法(然而我不想改了==)。另外,我发现高斯平滑的核矩阵大小对结果有很大影响,根据3σ准则它的宽度应该是 (6σ+1)⋅(6σ+1) ,然而有人设成 (3σ+1)⋅(3σ+1) 却取得了更多特征点,因此调整这个参数再用其它参数限制错误数量或许可以得到更好的结果。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分