参考书籍:Computer Vision: Algorithms and Applications, Richard Szeliski
参考论文:
[1] Distinctive Image Features from Scale-Invariant Keypoints
Scale Invariant Feature Transfrom
SIFT提出了一种尺度无关的特征变换算法。包括了特征点的检测,特征描述符的设计,[1]还提出了自己的特征匹配和SIFT在物体识别上的应用。特征点和特征描述符的介绍可参考之前的文章。
传送门:特征点检测
SIFT特征提取的步骤
- 尺度空间极值检测 (Scale-space extrema detection)
- 角点(Keypoint)定位 (Keypoint localization)
- 角点梯度方向的计算 (Orientation assignment)
- 角点描述符 (Keypoint descriptor)
尺度空间极值检测(Scale-space extrema detaction)
SIFT算法首先通过求尺度空间的极值获取角点(keypoints)坐标和scale的粗略估计。
首先简单说一下尺度。直观上来说,图像提供了2维的空间信息。尺度(Scale)定义了另外一个维度,即与观测者的距离。离着越远,图像越小,图像的细节越模糊。
通常尺度空间可以用图像金字塔的形式描述。不同大小的图像构成了图像金字塔。每层图像为下面一层图像长宽分辨率的各一半(整图为原图尺寸的四分之一)。 通过某种函数对下层图像做处理,再下采样,生成上层图像。
如上图(左)为均值金字塔,对下层图像每四个像素求均值,获得相应的上层图像的值。上图(右)为高斯金字塔,对下层图像使用高斯函数滤波,再下采样获得上层图像的值。更多关于图像金字塔的内容,可参考Szeliski 3.5。
SIFT中的尺度空间定义为DOG金字塔。而DOG金子塔由高斯金字塔产生。
SIFT中的高斯金字塔
- SIFT首先将尺度空间分成了若干octave。
- 每个octave包含一组图像。这些图像的分辨率相同,但是采用不同的方差做高斯滤波。上面一层标准差为下面一层标准差的k倍。
- 不同的Octave的高斯滤波的标准差不同。Octave1从开始,Octave2从开始。
- 上层的ocatve的图像分辨率是下层的1/4,这就构成了图像金字塔结构。 由上图所示,第二个octave的第一幅图像,是第一个octave的最后一幅图像下采样生成。
- 假设每个octave有s个scale,为了满足方差的变化的连续性,可以令,或者写作。
从上面可以看到,只考虑高斯金字塔,每个octave应该至少有s+2幅图像( 加原图)。然而,考虑到后续DOG需要s scale个平面的极值,实际上需要s+3张图像。完整的SIFT尺度空间如下图。
SIFT中的尺度空间(DOG金字塔)
- 将每个octave内的image求差,构成了SIFT的DOG尺度空间(上图右)。
- 在尺度空间的每个平面寻找极值(极大,极小)。同平面8个邻居+上层平面9邻居+下层平面9邻居=26。
- 如果需要找S个scale的极值,就需要s+2幅DOG图像,相应的,Gaussian金字塔就需要S+3幅图像。
通过极值粗略定位了Keypoint的Scale和坐标。
几个实现细节,
- Lowe通过大量的实现,得出S=3,σ=1.6时,效果做好。
- 构建金字塔时,首先将原始图像尺寸扩大一倍,作为最底层。
番外:关于octave的翻译
octave是音乐中的音阶的意思,很多计算机视觉的教科书上直接将其翻译为"八度"。当初多少令我感到些困惑。因为金字塔中各种图像的数量和"8"没有任何关系。不过单纯从音阶的角度来理解,作者取名为octave是非常形象的。音阶中,每个八度中(哆啦咪发嗦啦西)音节的不同代表了方差的不同,而下一个八度又是承接了前一个八度的音阶,与作SIFT的金字塔非常相似。
角点定位(Keypoints localization)
上一步得到了角点尺度σ与坐标(x, y)是离散的,这一步通过差值获得亚像素(sub-pixel)的坐标和sub-scale。Lowe采用三维多项式的方法拟合求极值。
该问题可描述为,定义scale-space function D(x, y, σ),已知极值在离散点(xi, yi, σi)附近,求极值。
求解过程,将D(x, y, σ)二阶泰勒展开。得到以上公式。D为采样点(上一步得到的离散Keypoint)的值,为采样点梯度向量,为采样点的3*3 Hessian矩阵,x为采样点与极值点的offset。对x求导,并令导数为0,可得x
将上面两个公式联立,可以得到极值点的函数值。
代表了这个 Keypoint的对比度,如果低于0.03,认为属于低对比度点,排除此角点。
去除边缘效应(Edge)
为了进一步去除边缘上的点,或者靠近边缘上的点,SIFT采用了Hessian矩阵。
矩阵的特征值α,β分别代表了两个梯度方向上最大,最小的变化。关于该矩阵的详细内容及几何意义,在之前Harris角点里面有详细说明。这里响应函数与Harris定义的响应函数稍有不同,
令r=α/β,有α=rβ。r为最大最小特征值的比例,因此r越小,特征值越接近。为了进一步优化计算效率,与Harris算法类似,结合矩阵行列式和矩阵的迹,
,
SIFT选取 r = 10, 如果
那么排除这些点。
传送门: Harris角点 解释了自相关矩阵(海森矩阵)特征值的意义。
计算梯度方向
上一步获得了精确地(x, y, σ),这一步在该scale下(高斯金字塔)计算一定窗口内坐标的方向梯度的幅度m和角度θ。
创建梯度方向直方图,并选出主要方向(dominant orientation)。如果幅度超过峰值幅度的80%, 保留该方向。因此一个keypoint会关联多个方向。
角点描述符(Keypoint descriptor)
目前我们已知角点的坐标,尺度和方向,这一步需要设计描述符。特别的,如果一个角点有多个方向,那么需要为每一个方向单独创建一个描述符。
- 为每一个Keypoint选取16*16的窗口,按照该角点梯度方向旋转。如上图所示。
- 将此窗口划分为4*4个cell。
- 每一个cell内计算梯度直方图。横坐标为8个方向。
描述符中一共4*4 个方向直方图,因此SIFT特征 4*4*8 = 128。
代码
opencv实现了SIFT特征描述符,但是由于SIFT被申请了专利,所以需要把opencv_contrib编译进去,并且加上 OPENCV_ENABLE_NONFREE=ON 这个选项。
我在GITHUB上上传了一份代码,使用了opencv4.1.1的sift接口。在opencv4.1上可直接编译运行。
https://github.com/linusyue/sift.git
mkdir build && cd build
./sift ../data
显示结果如下:
另外,这份代码目前还包含了opensift的实现。Rob Hess的opensift基于opencv接口实现了Sift算法。不过在opencv4.1上接口不兼容。准备移植过来。代码仍在施工中。