SIFT是由UBC(university of British Column)的教授David Lowe 于1999年提出, 并在2004年得以完善的一种检测图像关键点(key points , 或者称为图像的interest points(兴趣点) ), 并对关键点提取其局部尺度不变特征的描绘子, 采用这个描绘子进行用于对两幅相关的图像进行匹配(matching)。 目前, SIFT可以说是所有图像局部特征描述特征子 中最火的一个了。 关于SIFT的描述详见David Lowe与2004年发表的论文<<Distinctive image features from scale-invariant key points>>, International journal of Computer Vision 2004.。 David Lowe 也一直是我比较佩服的计算机视觉领域的大牛之一。 下图是David Lowe的照片:
目前, SIFT已经申请了专利。 专利持有人是UCB大学。 这里简要介绍一下SIFT的原理和实现细节。
任何一个好的图像特征描绘子, 应该满足如下几点要求:
(1)Distinctive features(so that we can correctly matched againsta large database of featuresfrom many images)。
(2)invariance to image scale and rotation. 也就是说, 当图像发生了尺度变化(缩小, 放大等等), 或者是图像发生旋转的时候, 我们能够保证我们提取到的特征是不变的, 没有任何的特征描述上的变化。 这就是SIFT的主要特征, 这也是为什么这个特征的名字叫做scale invariant features 吧。
(3) Robustness to Affine distoration, change in 3D viewpoint, Addition of noise, change in illumination。 也就是说, 我们的特征不会因为图像中场景中多了噪声, 发生了仿射失真, 或者亮度改变, 或者我们拍摄图片的时候发生了视角的变化等等等因素而变化。 我们的特征对这些影响保持鲁棒性。
例如上面的两幅图像, 对同一个物体(车辆), 从不同的角度拍摄得到的两幅图像, 虽然有occlusion, other objects, clutter, rotation等等, 但是当我们计算图像对应位置的SIFT features的时候, 二者对应的特征basically the same。 所以, 只要是相同的object, 我们就可以用SIFT匹配。
满足上面的几个特点的这样一个特征很难找到。 然而David Lowe 找到了, 这就是SIFT, 这也是SIFT产生的Motivation吧。 SIFT特征的优点由如下几种:
(1) Locality:特征是局部的, 所以对于遮挡(occusion)和复杂的背景(clutter)的鲁棒性非常好, very rebust.
(2) Distinctiveness: 特征具有独一无二性。 也就是说 individual features can be matched to a large database of objects.
(3)Quantity: 即使是一个很小的物体(objects), 也能够产生大量的SIFT features.。
(4)Efficiency: SIFT接近于实时的性能, 效率很高。
在这里, 可能会遇到的问题:
Q1: 什么是图像的局部特征描绘子。
前面说了SIFT是图像的局部特征描绘子。 英文就是Local features。 与之相对应的是全局特征(global features)。 给定一幅图像, 我们计算出整幅图像的直方图(histogram), 那么这个直方图就是这幅图像的全局特征描绘子(global feature), 如果采用滑动窗口的办法, 例如窗口的大小为4x4等, 然后计算窗口的所有像素的均值, 将这个均值作为中心像素的邻域描述, 那么这个就是局部特征。 类似的, SIFT计算关键点的SIFT特征的时候, 就用到了关键点周围的邻域(neighbourhood)的信息统计, 所以SIFT就是局部特征描绘子。
Q2: detector(检测子)和descriptor (描绘子)的区别是什么
首先, detector(检测子)告诉我们我们感兴趣的位置在哪里, descriptor 的作用是我们如何去描述这个感兴趣的位置(特征)。 一个是关于where, 一个是关于how 的问题。 举个例子, 光检测边缘的检测子(detector)就有几十种了, 例如Canny detector, Laplacian 啊 等等。 这些检测子只是告诉你边缘发生在图像的那些位置。 描述子(desriptor)是关于我们如何去描述的, 层次比detector高。 如果我们能够正确的描述一个问题, 那么我们基本上就成功一半了。 对于SIFT, 第一步是关于detector, 也就是检测出关键点(感兴趣点), 我们是找到这些感兴趣点在哪里? 这是我们的第一步, 第二是关于descriptor, 也就是描述我们所找到的感兴趣点。 这就是SIFT features.。 这样我们就可以利用这些SIFT features 去做计算机视觉中比较advanced tasks 了。
下面介绍一下SIFT找到(提取)图像中的Key points, 并且使用key Point descriptor 去描述这些关键点的步骤。
整个过程主要分为四个步骤。
(1)尺度空间峰值选择(Scale space peak selection), 这一步的目的是在尺度空间中选择选择潜在的满足尺度不变性和旋转不变性的关键点(或者称为兴趣点)。
(2)关键点定位(key point localization): 这一步的目的就是精确定位出特征关键点的位置, 涉及到剔除伪关键点。
(3)方向分配(orientation assignment): 这一步的目的就是基于关键点的局部梯度方向,给每个相应的关键点分配方向(Assign orientation to key points)。
(4) 关键点描述(Key Point descriptor): 此步骤的目的就是对于每个关键点, 用一个高维度(high dimensional vector, 在下面的例子中是128 维的向量)的向量去描述每个关键点。
下面详细介绍上面的四个步骤。
(1)尺度空间峰值选择
首先, 给定一幅图像, 要想使用SIFT提取出潜在的感兴趣点, 我们的第一步就是构造图像的尺度空间(Scale space)。我们采用的办法就是采用Laplacian 0f Gaussian(LoG) 去构建图像的尺度空间。 构造完成之后, 我们通过选择LoG 尺度空间上所有的的local maxima, 作为我们的interest point(兴趣点),这也是stable features, 这样我们就完成了第一步。
具体解释如下:
给定一幅图像:
我们要提取出这幅图像的兴趣点, 第一步是不同variance(方差)的Gaussian filter (离散域是Gaussian kernel: 高斯核)进行滤波, 例如我们选择
其中, I(x, y )表示原图像, G是高斯核。 由于在尺度空间中, G是三维的函数:
我们通过选择不同的标准差(注意是标准差, 不是方差), 从而得到与原图像对应的许多幅不同的尺度(这里尺度代表标准差)高斯滤波器平滑滤波(blur)后的图像。
再比如, 我们记t 为高斯函数的方差(), 此时我们的高斯核函数公式为:
对于下面的图像, 不同的方差的高斯核函数作用于这幅图像得到如下对应的几幅图像(参见下图):
不难发现, 随着方差t的变大, 滤波后的图像平滑的效果越来越明显, 相应的, 图像也就损失了越来越多的细节。 为了从直观上给出解释, 下面我们画出了一维Gaussian 图像的曲线图:
不难看出, 随着方差的减小, 越来越尖, 平滑效果不明显, 随着方差增大, 高斯函数越来越flat, 平滑效果越来越明显。 离散的Gaussian kernel 如下:
言归正传, 对于图像:
我们使用一系列的不同方差的高斯滤波函数对其滤波平滑后的图像, 然后使用对所有得到的图像采用Laplacian operator, 得到相对应的对应的图像。
然而, 上述的做法分了两步, 也就是先平滑处理, 后采用Laplacian operator, 事实上, 我们可以一步到位, 对于原图像, 直接采用LoG (Laplacian of Gaussian)算子即可完成上面的操作。 具体的推导原理如下(参见冈萨雷斯数字图像处理书英文版738页):
所以LoG算子定义如下:
其中:
这样, 我们对于图像, 使用一系列的方差的LoG算子得到如下:
我们于是得到右边三维的空间(其中x表示行, y 表示列, 表示不同的高斯标准方差(标准差平方, 立方, 。。。))等等得到的图像。 上面的几幅图像都是在同一尺度的。 也就是说没有下采样。NOTE: 只有统一尺度(这里的尺度scale的意思是size)的图像, 才有相互比较的意义。
例如, 上面同一尺度(size)得到了三幅图像。 为了利用这个三维空间中找到图像的中的兴趣点, 我们需要找到这个尺度的3x3邻域的局部极值(由于这个尺度共有3幅图像, 所以共需要比较绿色区域中心点附近3x3 x 3 -1(即26个像素), 看看这个这个中心点像素的的值是否大于周围的26个像素。 如果是, 我们就说这个中心点是局部极值。 这个点就是我们的兴趣点。 一次类推, 找到所有的极值点(兴趣点)。
题外话:
LoG可以用作“blob” detector, 如下, 对于不同大小的blob, 运用不同大小的LoG(即不同方差)(也即采用scale space 用于检测不同大小的blob)出现极值的情况, 不难看出, 对于一定大小的圆, 只有一定方差的LoG才能检测出极值的位置。
再比如, 对于下图:
不同的sigma的对应的尺度空间中进行blob 检测的结果如下:
如果我们利用上面的原始图像得到scale space, 对于每一个像素, 求出尺度空间中的局部极值, 我们最终会得到下图中检测到的blob的情况:
上图中不同大小的红色圆圈表示不同的尺度空间(即不同sigma)中的极值(极值所位于的sigma的位置)。 相同大小的圆圈表示在相同的极值位于相同的sigma对应的图像中找到的。
言归正传, 回到SIFT中, 下面构建尺度空间。
为了得到尺度不变特征(scale invariant), 我们必须在所有的尺度空间中检测出相应的兴趣点。 如果使用LoG去构建(build)尺度空间, 求二阶导数会导致计算量很大。
为了解决这个问题, 我们采用DoG去近似逼近LoG, 具体推导如下:
通常情况下, 我们选择:
关于为啥通常会选择者两个值, 后面将会给出解释。
构造尺度空间具体如下图:
对应的Gaussian的方差具体取值情况如下图所示:
对于上面的一幅图像, 我们需要注意如下几点:
(1)对于每一个octave, 共有5幅经过不同的标准差的Gaussian 滤波器过滤后的图像。 对于每一个octave的从下向上的图像, 作用于上一幅图像滤波器的标准差是与其相邻下一幅的滤波器标准差的k 倍。 不同的ocatve的最底层, 使用的滤波器上一octave的标准差是下一octave最底层的标准差的k^2倍。你可能会问问什么? 原因是上一octave的图像是下一层图像行列个下采样2 every other row and column, 所以分辨率变小了, 所以滤波器的标准差也变成了原来的2倍(也就是k^2)。
(2)同一octave对应着相同的尺度(size 的图像), 求出相邻图像的差, 也就是求出DoG, 得到了对应的尺度空间(在这里, 每一个octave对应着4幅DoG图像)。
当我们取值:
不难计算出不同octave, 不同的Gaussian滤波器对应的标准差如下表:
Q: 每一个octave究竟需要多少个scale呢(也就是几幅不同标准差的Gaussian滤波后的图像)呢?
要回答这个问题, 需要一幅实验得到的图, David Lowe 经过试验测试, 给出了如下图:
有上面的图像, 可以看到重复率最好的位置是每个octave的scale数目为3的时候, 然而在超过5以后,就保持flat, 所以选择5 是一个比较好的折中。
Q2 sigma的初试值应该选为多少合适呢?
之前我们已经给出了sigma的一般取值是1.6. 这并不是凭空想象出来的。 这个值也是David Lowe 经过测试得到的最好的结果。 David 经过测试得到如下表:
由上表不难看出, 重复率最好的位置是当sigma取值为1.6的时候。
这样, 我们就构建出了尺度空间, 接下来, 我们需要根据我们得到的尺度空间得到每一个octave所有的局部极值(峰值检测), 这个检测到的局部峰值就是我们的兴趣点。
详见下图(前面有介绍过, 这里不再赘述)。
至此为止, 上面完成了SIFT算法的第一步。 下面介绍第二步。
(2)关键点定位(key point localization): 这一步的目的就是精确定位出特征关键点的位置。
在上一步中, 我们求出了图像中所有的局部极值点。 我们选取其位置。
如上图。 但是并不是所有的极值点都是关键点。 而且由于对于一幅图像, 我们可能找到很多的极值点, 所以我们需要剔除掉一些不是关键点的点(可能是边缘的点, 因为DoG对于边缘有很强的响应)。
剔去这是伪关键点可以使用两大步。
1.initial outlier rejection
这一步可以说是粗剔去。 主要剔去的是低对比度的候选关键点(candidates) 和poorly localized candidates along an edge。
为了找到剔除判别标准, 我们下面对尺度空间(DoG)进行taylor series展开:
注意上面的x 是一个三维向量。 对D求导得到的是一个矩阵。
对上面的Taylor再次求导, 令其等于0, 得到的就是极值的准确的位置, 如下表示:
当然会有很多的极值点, 求出来的都是准确的兴趣点的位置。
下面给出我们剔除非关键点的标准:
th 是判别阈值。 凡是大于th的兴趣点, 我们都保留下来, 凡是小于th 的兴趣点我们都剔除。
例如下例, 我们未剔除之前, 共有832个关键点, 使用th = 0.03 的规则之后, 最后剔除剩下了729个关键点:
Further outlier rejection
上面剔除的方法只是比较粗糙的剔除。 下面我们采用边缘响应的方式更进一步的剔除关键点:
这里剔除关键点所基于的原理是DoG算子对于边缘具有很强的响应。
现在我们将DoG多尺度图像想想成一个surface。 我们接下来计算DoG surfec 的主曲率(principle curvatures)。 由于沿着边缘的方向(along the edge) 其中的一个主曲率的值会很小, 垂直边缘的方向(across the edge)主曲率会很高。 这一点和PCA是有点相似的, PCA关注数据的变化的两个方向。
所以我们接下来计算DoG surface D的Hessian matrix(其中是D的特征值) :
接下来, 我们按照如下方式剔除非关键点。
不难看出, 当r = 1 的时候, 上式取得最小值, 为4, 而且右边的式子会随着r的增大而增大。
所以我们规定, 凡是使得r > 10的点, 我们都将该点视为outlier(即不是关键点), 我们将这些点剔除。
效果见下图, 不难看出经过这一步, 关键点的数目有729 降为536个:
至此, 我们算是完成了第二步, 接下来, 我们开始SIFT的第三步: Orientation Assignment
(3)给关键点方向分配
这是SIFT的第三步。 目的是实现SIFT特征的旋转不变性(orientation invariance)。
我们所做的就是, 对于在某个尺度平滑后的图像L, 我们计算出L在每个关键点出的中心导数, 进而计算出每个关键点(x, y)处的magnitude 和direction 信息。此时每个关键带你所有的信息有: 位置, 方向, 所处的尺度。
计算公式如下:
为了确定关键点的方向信息, 我们采用如下方法:
对于每一个关键点, 我们选取其周围16 x 16 的窗口(neighbourhood)(这个窗口很大), 我们将这个窗口内的所有像素(共256个)的梯度量化到36个bin 的直方图中。
然后统计。 不难看出每个bin的方向范围是10度(360/36 = 10°/bin)。
值得注意的是, 我们的灰度直方图并不是简单的对方向个数进行统计, 而是weighted direction histogram in a neighbourhood of a key point (36bins)。
对于每一个像素点, 这些方向的weights 是该像素点的gradient magnitudes.。 举个例子, 如果某个像素点的梯度方向是15°, 在该像素点的梯度是12, 那么我们就在10 °- 19°这个bin中统计的时候加上12., 而不是简单的加上1了。
由于16 x 16 是很大的窗口, 简便起见, 我们下面以一个4 x 4 的窗口说明最终得到的方向图像:
不难知道, 上面的方向的长短表示这个方向的权重(也就是这个像素点梯度的magnitude)。 我们对上述图像进行统计, 得到如下的统计直方图:
接下来, 我们选择直方图中的峰值(peak)作为这个关键点的方向。
注意, 有的是偶, 我们也会引入additional key points, 因为在这里也出现了local peaks(是Max peaks 的0.8), 这个额外的关键点与本关键点的位置相同, 但是关键点的方向却不同, 如下图:
至此, 第三步已经完成了, 下面介绍第四步,关键点描述子。
(1)关键点描述
完成了上面的SIFT的三大步, 接下来我们就需要找到一个关键点的局部图像描绘子了(Local Image Descriptors at key points)。
其中, 一个可能的描绘子就是将关键点邻域附近的灰度样本存储起来, 以便描述这个关键点点。 然而如果这样做的话, 效果会很差, 因为这个描绘子对于灰度光变化非常敏感等等
所以我们选择的是使用梯度方向直方图来描述这个关键点(gradient orientation histograms)。 这是一个关键点的robust representation。
因为梯度比较稳定, 记录的是周围的变坏。
具体的, 我们的做法如下:
计算出 每个关键点周围16x 16的neighbourhood周围相对的orientation和magnitude。 然后将这16 x 16 的window 分成4 x 4 的块。 所以每个关键点的这个neighbourhood 就总共有16个blocks.。 对于每一个blocks, 我们统计一个8bins的 weighted histogram。 权重是magnitude 和这个关键点所处的spatial Gaussian。
然后, 我们将这16个histogram (因为有16个块) concatenate , 从而组成一个long vector of 128 dimensions(即16 x 8 = 128)。
为了简便分析, 下面假如去关键点附近的8 x 8 的neighbourhood。 将其分成4 x 4 的block, 这样就有2 x 2 (即4个)blocks.
此时描述这样的一个关键点的的向量的维数为4 x 8 = 32 维.
Q: 为什么选择将16 x 16 的窗口分成了size4 x 4的block共16个blocks 呢??
首先这样划分也不是David的主观臆断的, 而是经过试验测试的, 这样划分blocks 的实验效果好, 具体的如下:
从上图中, 可以看出4 x 4 的block的效果比较好。
至此我们已经完成了第三步。 我们使用向量描绘出了所有的关键点。 为了更近一步的提高SIFT的性能, 我们可以将这个向量归一化为单位向量, 从而实现
SIFT特征的illumination invariance, 以及对于affine changes 的invariance。
对于非线性的亮度变化, 我们将我们的单位向向量中的每一个元素限制在最大为0.2, 一旦有超过0.2的, 我们就而移除这个较大的梯度, 重新对单位向量归一化。
至此, 我们完成了所有SIFT的工作, 我们得到了SIFT特征描绘子(也就是所有的关键点都用了128维的特征向量表示了)。 接下来, 我们所做的就是关键点匹配了(Key Point Matching )。
根据SIFT进行match
生成了A、B两幅图的描述子,(分别是k1*128维和k2*128维),就将两图中各个scale(所有scale)的描述子进行匹配,匹配上128维即可表示两个特征点match上了。
匹配的准则就是找最近邻。 也就是说给定一幅图像的一个关键点的128维的向量描述, 从另一幅图像中的关键点中找到欧氏距离最小的那个关键点, 表示这两个关键点完成了匹配。
高效的最近邻匹配(efficient Nearest neighbour matching):
look at ratio of distance between best and 2nd best match (一般取为0.8)。
结果如下:
也会有ambiguities: