如果撇开论文看,目前网上质量比较高的两篇介绍SIFT的博文应该是:http://blog.csdn.net/zddblog/article/details/7521424和 http://blog.csdn.net/abcjennifer/article/details/7639681 总的来说这两篇博文应该算是每个步骤都作了详细的解释,但是有些细节的地方不知道为什么没有仔细的讲,也许是我看的不仔细=-= 为了整理下自己的实现,这里看了对比两篇博文和Lowe原文把几个重要步骤的细节描述下
1 . 建立尺度空间
这里在我看来主要有两个坑:
1) 在期望建立包含S个连续尺度的octave是,需要生成S+3个高斯图,这里的原因两位博主讲的都很有道理,首先,因为DOG需要两幅高斯图才能生成所以如果需要S+2幅DOG则需要S+3幅高斯图,然后,在找极值的时候需要同时对比高一层的尺度和低一层的尺度,所以一个八度内的最高层和最低层是没办法按照这个要求生成极值的,因此,想要生成S层Extrema的分布,则需要S+2层DOG ; 然后为了使所有Octave上的所有Scale Level都能连续起来,sigma前系数的K 的值是2^(1/s) , 这样前一个Octave : oct的最高的包含Extrema的层的尺度系数是2^(oct) * sigma*2^(s/s) , 下一个Octave: oct+1 的最低包含Extrema的Scale层尺度系数是2^(oct+1)*sigma*2^(1/s)=2^(oct)*sigma*2^((s+1)/s) 这样前一层的Octave和后一层的octave就以2^(1/s)的step连续了
图1 .这里注意图中红圈圈出的部分,都是以同一个step连续递增的
2) 这个坑感觉两位博主都没有说清楚 , 不同Octave建立的时候,高一层的八度的建立基础并不是直接在前一层Octave的基础上降采样图片
其实,在所有的Octave建立的过程中,始终用的是同一张图片,不做任何降采样,但是由于表征高斯尺度的sigma系数的变大,高斯核的直径kSize也在随之线性变大, 两者之间的关系opencv上的定义是 sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8 => kSize=(sigma-0.5)/0.15+1 也有说kSize=3sigma的, 总之是线性变化的;
根据原文,高一层的Octave是前一层的Octave的采样基础上隔点采样的, 这样得到的高斯图像尺寸就会宽高各为原来一半 , 然后这里还有一个坑, 因为是隔点采样,所以高斯核也是根据一定的step apply到采样窗口的, 那么最后apply到采样窗口内的所有高斯系数要重新作一次归一化
2. 特征点提取
特征点提取有很多可以说的 , local extrema 基于26个neighbor (上面9个 , 中间8个,下面9个)很浅显,不谈 ;
然后是低对比度点过滤和边缘点过滤,首先讲低对比度过滤,原文的意思是,虽然离散的在各个尺度中初步的找到了一些高频的点,但是这些点 1) 可能受到噪声干扰,不太可靠 2) 因为离散采样的关系,本身位置不太准,真正的极值点可能是在两点之间, 然后把两点之间的offset 作为变量带入了 taylor expansion的估算,同时,令 offset=0 以估算接近真正极值点的位置;然后,关键来了,在这个点的数值的绝对值小于0.03的情况下,把这个点剔除, 这里也要注明下, 因为DOG是近似等价于归一化的LOG的,所以这里的数值range是[0,1] 。 实话说这里找真正空间分布太麻烦了, 我偷了个懒, 直接把所有0.03的点都去除了
再讲边缘过滤,所谓边缘过滤这里的做法其实基本接近于Harris-Detect找角点,也就是在得到所有点的梯度后,保留所有方向上梯度变化都大的点,具体的原理可以去查Harris-Detect , 结论基本是找到对应的两个特征值的比值比较接近的一组特征向量(。。。感觉不是人话,具体还是看Harris Detector , 这里不展开) 原文也给出了一个方便计算的公式 Tr(H) = D xx + D yy = α + β,Det(H) = D xx D yy − (D xy ) 2 = αβ. 所求的比值ratio 是 Tr(H)^2 / Det(H) 假如ratio小于threshold ,那么这个就是一个合理的特征点,否则就是边缘点,用来作特征点并不理想 , 这里原文里threshold是用(r+1)^2/r 定义的, Lowe表示他们的r定义为10
到这里为止,所有的特征点也找齐了
3. 生成描述子
先不概括描述子的数据结构,直接讲描述子的生成过程 ,
SIFT的目的是 尺度不敏感,旋转不敏感 的前提下找到特征点,然后匹配起来, 现在尺度不敏感已经靠尺度空间生成的金字塔做到了,旋转不敏感就要靠之后的描述子实现了
1) 在当前尺度对应sigma的1.5 sigma大小的高斯窗口内统计每个点的方向和大小,然后形成histgram , 方向计算方式如下
θ(x, y) = tan −1 ((L(x, y + 1) − L(x, y − 1))/(L(x + 1, y) − L(x − 1, y)))
m(x,y) = sqrt((L(x + 1, y) − L(x − 1, y)) 2 + (L(x, y + 1) − L(x, y − 1)) 2)
m是梯度大小,θ 是梯度方向( 其实应该也可以用sobel ..) 注: 这里的L 也就是采样图像是当前尺度对应的高斯图像 ,对应关系见图1 。
注2: 这里的大小按是需要加权的,加权系数是1.5sigma的高斯核,
生成了histgram后,选取最大的方向作为main orientation , 然后可以选一个达到main orientation 80%的一个方向作为辅助 , 每一个被选出来的方向都可以生成一个特征点的描述子,
2 ) 现在特征点的描述子有了当前图像的主要方向的描述,我们需要做到方向不敏感,那么我们需要把neigbor window按照这个方向旋转一下,那么,所有的关于这个特征点附近窗口的统计都是基于main orientation 进行偏移的,这样就没有方向问题了; 那么问题来了, 如果要作匹配的话,基于灰度的匹配,像NCC SSDA 这种,都是对尺度很敏感的,这里显然不能采用,SIFT怎么作匹配呢 ? Lowe原文提到,根据对于人脑皮层(=.=) 的研究,人脑对于纹理的方向更加敏感,而对于纹理的location 没那么敏感,所以SIFT对于特征点的邻域作了一个模糊的梯度幅值和方向统计: 先是从对应的高斯图(就是最开始建立尺度空间的时候第一次生成的S+3的那几组)选相邻8x8(原文的栗子举的是8x8 , 但是Lowe同时也表示他们实验的时候用的是16x16 , 这个计算量爆炸....坑爹阿) 的点 ,然后把8x8的点每个都旋转一下:
注意,这里算出来的位置可能不是整数,不能直接取整,需要加权的平坦到预设的周围点 ,打个比方, x‘=1.6 ,y’=1.3 ,m=1 , 那么 平摊下来x=1 y=1的点会被平摊到(1-0.6)*(1-0.3)*m=0.28 , x=2 , y=1的点会被摊到0.6*(1-0.3)*m=0.56 其他类推(。。。我这里理解对吗?? 应该是这样吧=。=) 这样得到的一个领域窗口内每个离散点的梯度方向和大小,这组数据还要做一次加权,加权系数还是高斯核,这个高斯核大小是什么? 按照原文的说法是 sigma =0.5 d , 这里d是指领域窗口的直径,这里也就是8 (也就是说Lowe的实验用的是16=。=)
注:为什么用0.5 作系数 ? 如果kSize=3 sigma 那么 , 这个高斯核的直径大小kSize=1.5d , 刚刚好大于sqrt(2) d , 为什么会对sqrt(2)敏感 , 因为之前做了一次旋转,而旋转的最大偏差发生的情况应该是45度 ,135度 等 , 这个时候x'=sqrt(2)*x/2 - sqrt(2)*y/2 y'=sqrt(2)*x/2 + sqrt(2)*y/2 ,简单的说就是原来的对角线对应现在的邻域窗口的直径 正好1.414倍, 所以这个0.5sigma应该足够 cover 整个参与采样的邻域窗口
在得到旋转后邻域内每个点加权后的梯度大小和幅值后,在把他们根据方向把对应幅值累积到一个种子点内,每个种子点规定8个方向,种子点大小是4x4
图2. 基于邻域的梯度统计
以上,每个特征点的描述子就生成完了, 所以我们总结下,所谓描述子就是特征点附件的种子点每个方向上梯度幅值按照高斯加权后的累计的值 ,说简单点,就是特征点邻域内的种子点的各个方向的向量大小 , 所谓的128维向量得这样理解: 一个16x16的邻域窗口按照4x4的种子点分割,会有4x4个种子点,每个种子点有8个方向,所以描述一个种子点需要8个向量 ,4x4x8 =128 (什么?你问我图里的? 图里的是2x2x8=32 , 和128没关系=。= ,128 是Lowe的实验采用描述子) ,
之前的步骤中还漏了一步,128维的vector生成后还需要对这个vector做一次归一化,用来消除光照变化的影响, vector=(H0,H2,...H127) normalized vector=(H0/DO,H1/DO ... H127/DO) ,DO=sqrt(||H1||^2+||H2||^2+...||H127||^2) , 这个归一化的vector最后就作为每一个feature point的描述子了
3. 描述子的匹配
描述子的匹配简单的说用的是欧几里德距离,比方说 一个向量是 (a1,b1) 另一个向量是(a2,b2) 他们的欧几里德举例就是sqrt((a1-a2)^2+(b1-b2)^2)
这里是用一副图像里的一个特征点来算出这个点和另一幅图像素有的特征点描述子之间的欧几里德距离,其中欧几里德举例最小的一组点,就是候选的相似点,为什么候选? 因为可能两幅图完全没关系,所以匹配不起来阿=。= SIFT怎么解决这种问题的, Lowe在计算欧几里德举例的时候还保留了第二小的欧几里德距离E(1),E(0)/E(1) 小于某个阈值的时候,可以认为这个匹配是有效的匹配,为什么这么说? 因为不太可能出现两个完全一样的特征点,但是可能出现相似的噪声 (大家意会=。=)
对于阈值的选取,原文给了一组数据图,总结下来就是0.4-0.6之间比较合适,看大家项目实际应用情况自行选值吧
到这里,主要的SIFT几个步骤都讲完了,在完成这些匹配后,再用RANSAC 剔除一些错误的匹配,就能得到比较好的匹配对了