之前小白为各位小伙伴带来了SIFT特征点中的图像金字塔和特征点的位置与方向。本次小白为各位小伙伴们带来SIFT的最后一讲——特征点描述符。
特征点描述符
通过以上步骤,对于每一个关键点,拥有三个信息:位置、尺度以及方向。接下来就是为每个关键点建立一个描述符,使其不随各种变化而改变,比如光照变化、视角变化等等。并且描述符应该有较高的独特性,以便于提高特征点正确匹配的概率。
4.1、特征的生成过程
4.1.1、确定计算描述子所需的区域
将关键点附近的区域划分为d*d(Lowe建议d=4)个子区域,每个子区域作为一个种子点,每个种子点有8个方向。考虑到实际计算时,需要采用三线性插值,所需图像窗口边长为3x3xσ_oct x(d+1) 。在考虑到旋转因素(方便下一步将坐标轴旋转到关键点的方向),如下图6.1所示,实际计算所需的图像区域半径为:

4.1.2、坐标轴旋转至主方向
将坐标轴旋转为关键点的方向,以确保旋转不变性。

4.1.3、梯度直方图的生成
将邻域内的采样点分配到对应的子区域内,将子区域内的梯度值分配到8个方向上,计算其权值。

4.1.4、三线性插值
插值计算每个种子点八个方向的梯度。

采样点在子区域中的下标(x'',y'') (图中蓝色窗口内红色点)线性插值,计算其对每个种子点的贡献。如图中的红色点,落在第0行和第1行之间,对这两行都有贡献。对第0行第3列种子点的贡献因子为dr,对第1行第3列的贡献因子为1-dr,同理,对邻近两列的贡献因子为dc和1-dc,对邻近两个方向的贡献因子为do和1-do。则最终累加在每个方向上的梯度大小为:

其中k,m,n为0(像素点超出了对要插值区间的四个邻近子区间所在范围)或为1(像素点处在对要插值区间的四个邻近子区间之一所在范围)。
4.1.5、特征描述子
如上统计的4*4*8=128个梯度信息即为该关键点的特征向量。
特征向量形成后,为了去除光照变化的影响,需要对它们进行归一化处理,对于图像灰度值整体漂移,图像各点的梯度是邻域像素相减得到,所以也能去除。得到的描述子向量为H=(h1,h2,.......,h128),归一化后的特征向量为L=(L1,L2,......,L128),则

4.1.6、描述子的门限化
非线性光照,相机饱和度变化对造成某些方向的梯度值过大,而对方向的影响微弱。因此设置门限值(向量归一化后,一般取0.2)截断较大的梯度值(大于0.2的则就令它等于0.2,小于0.2的则保持不变)。然后再进行一次归一化处理,提高特征的鉴别性。
4.2、描述子相关分析
用一组图来概括描述子的生成过程
4.2.1、描述子生成总括

4.2.3、描述子三线性插值源码分析
static void interp_hist_entry(double*** hist, double rbin, double cbin,
double obin, double mag, int d, int n)
{
double d_r, d_c, d_o, v_r, v_c, v_o;
double** row, *h;
int r0, c0, o0, rb, cb, ob, r, c, o;
r0 = cvFloor(rbin);//向下取整
c0 = cvFloor(cbin);
o0 = cvFloor(obin);
d_r = rbin - r0;//小数余项
d_c = cbin - c0;
d_o = obin - o0;
for (r = 0; r <= 1; r++)//沿着行方向不超过1个单位长度
{
rb = r0 + r;
if (rb >= 0 && rb < d)//如果此刻还在真正的描述子区间内
{
v_r = mag * ((r == 0) ? 1.0 - d_r : d_r);//d_r = rbin - r0;
row = hist[rb];
for (c = 0; c <= 1; c++)//沿着行方向不超过1个单位长度
{
cb = c0 + c;
if (cb >= 0 && cb < d)
{
v_c = v_r * ((c == 0) ? 1.0 - d_c : d_c);
h = row[cb];
for (o = 0; o <= 1; o++)//沿着直方图方向不超过1个单位角度
{
ob = (o0 + o) % n;//n=8,8个小柱子
v_o = v_c * ((o == 0) ? 1.0 - d_o : d_o);
h[ob] += v_o;
}
}
}
}通过上面的1至4个大步骤就可以完成SIFT算法对图像特征点的提取。至此SIFT算法完结。图像特征提取是图像匹配的基础,经过此算法提取出来的特征点用于后续的图像特征匹配和特征识别中。