FROM: http://www.cnblogs.com/ronny/p/4028776.html

1. SIFT算法中一些符号的说明

I(x,y)表示原图像。

G(x,y,σ)表示高斯滤波器,其中G(x,y,σ)=12πσ2exp(−(x2+y2)/2σ2)。

L(x,y,σ)表示由一个高斯滤波器与原图像卷积而生成的图像,即L(x,y,σ)=G(x,y,σ)⊗I(x,y)。一系列的σi,则可以生成一系列的L(x,y,σi)图像,此时我们把这一系列的L(x,y,σ)图像称为原图像的一个尺度空间表示。关于尺度空间的知识可以参考:图像特征提取:尺度空间理论。

DOG表示高斯差分(Difference of Gaussians),也可以表示为D(x,y,σ),其中D(x,y,σ)=(G(x,y,kσ)–G(x,y,σ))⊗I(x,y)=L(x,y,kσ)–L(x,y,σ)。

上面特别值得注意的是尺度为σ的高斯差分图像由于尺度为kσ与尺度为σ的L图像生成的。k为两相邻尺度空间倍数的常数。

O:高斯金字塔的组数(Octave),其中值得注意的是在实际构建中,第一组的索引可以为0也可以为-1,这个在后面解释原理。

S:高斯金字塔每一组的层数。在实际最开始构建尺度空间图像,即L图像的时候,构建了S+3层,一定要把这个S+3与S区分开,为什么是S+3后面分析。

2. 构建高斯差分金字塔

2.1 第一组第一层图像的生成

很多初涉SIFT的都会被这个问题所困惑,这里要分两种情况:其一是把第一组的索引定为0;其二是把第一组的索引定为-1。

我们先考虑第一组索引为0的情况,我们知道第一组第一层的图像是由原图像与σo(一般设置为1.6)的高斯滤波器卷积生成,那么原图像是谁呢?是I(x,y)吗?不是!为了图像反走样的需要,通常假设输入图像是经过高斯平滑处理的,其值为σn=0.5,即半个像元。意思就是说我们采集到的图像I(x,y),已经被σ=σn=0.5的高斯滤波器平滑过了。所以我们不能直接对I(x,y)用σ0的高斯滤波器平滑,而应该用σ=σ20−σ2n−−−−−−−√的高斯滤波器去平滑I(x,y),即

FirstLayer(x,y)=I(x,y)⊗G(x,y,σ20−σ2n−−−−−−−√)

其中FirstLayer(x,y)表示整个尺度空间为第1组第1层的图像,σo一般取1.6,σn=0.5。

现在我们来考虑把第一组的索引定为-1的情况。那么首先第一个问题便是为什么要把索引定为-1。如果索引为0,如上面那种情况所示,整个尺度空间的第1组的第1层图像已经是由原图像模糊生成的了,那么也就是说已经丢失了细节信息,那么原图像我们完全没有利用上。基于这种考虑,我们先将图像放大2倍,这样原图像的细节就隐藏在了其中。由上面一种情况分析,我们已经知识了I(x,y)看成是已经被σn=0.5模糊过的图像,那么将I(x,y)放大2倍后得到Is(x,y),则可以看为是被2σn=1的高斯核模糊过的图像。那么由Is生成第1组第1层的图像用的高斯滤波器的σ=σ20−(2σn)2−−−−−−−−−−√。可以表示为。

FirstLayer(x,y)=Is(x,y)⊗G(x,y,σ20−(2σn)2−−−−−−−−−−√)

其中FirstLayer(x,y)表示整个尺度空间为第1组第1层的图像,Is(x,y)是由I(x,y)用双线性插值放大后的图像。σo一般取1.6,σn=0.5。

2.2 尺度空间生成了多少幅图像

我们知道S是我们最终构建出来的用来寻找特征点的高斯差分图像,而特征点的寻找需要查找的是空间局部极小值,即在某一层上查找局部极值点的时候需要用到上一层与下一层的高斯差分图像,所以如果我们需要查找S层的特征点,需要S+2层高斯差分图像,然后查找其中的第2层到第S+1层。

而每一个高斯差分图像G(x,y,σ)都需要两幅尺度空间的图像L(x,y,kσ)与L(x,y,σ)进行差分生成,这里假设S =3,则我们需要的高斯差分图像有S+2 = 5张,分别为G(x,y,σ),G(x,y,kσ),G(x,y,k2σ),G(x,y,k3σ),G(x,y,k4σ)。其中的G(x,y,kσ),G(x,y,k2σ),G(x,y,k3σ)这三张图像是我们用来查找局部极值点的图像。那么我们则需要S+3 = 6张尺度空间图像来生成上面那些高斯差分图像,它们分别为:L(x,y,σ),L(x,y,kσ),L(x,y,k2σ),L(x,y,k3σ),L(x,y,k4σ),L(x,y,k5σ)

从上面的分析,我们知道对于尺度空间来说,我们一共需要S+3层图像来构建出来S+2层高斯差分图像。所以,如果整个尺度空间一共有O组,每组有S+3层图像。共O*(S+3)张尺度图像,如果我们查找OpenCV中的SIFT源码,则很容易找到如下代码来说明问题:

pyr.resize(nOctaves*(nOctaveLayers + 3));

上面代码中的pyr代表了整个尺度空间的图像,nOctaves为组数,nOctaveLayers即为我们定义的S。

2.3 为什么是倒数第3张

相信你在看很多SIFT算法描述里都这样写着,取上一张的倒数第3张图像隔行采样后作为下一组的第一张图像。

答案是为了保证尺度空间的连续性,我们下面来仔细分析。

我们知道对于尺度空间第o组,第s层的图像,它的尺度为σ=σoko+s/S,其中,k=1/2,o∈[0,1,2,…,nOctave−1],s∈[0,1,2,…,S+2]。那么我们从第0组开始,看它各层的尺度。

第0组:σo→21/3σ0→22/3σ0→23/3σ0→24/3σ0→25/3σ0

第1组:2σo→2∗21/3σ0→2∗22/3σ0→2∗23/3σ0→2∗24/3σ0→2∗25/3σ0

我们只分析2组便可以看出,第1组的第0层图像恰好与第0组的倒数第三幅图像一致,尺度都为2σ0,所以我们不需要再根据原图来重新卷积生成每组的第0张图像,只需采用上一层的倒数第3张来降采样即可。

我们也可以继续分析,第0组尺度空间得到的高斯差分图像的尺度为:σo→21/3σ0→22/3σ0→23/3σ0→24/3σ0

而第1组尺度空间得到的高斯差分图像的尺度为:2σo→2∗21/3σ0→2∗22/3σ0→2∗23/3σ0→2∗24/3σ0

如果我们把它们的中间三项取出来拼在一起,则尺度为:21/3σ0→22/3σ0→23/3σ0→2∗21/3σ0→2∗22/3σ0→2∗23/3σ0,正好连续!!这一效果带来的直接的好处是在尺度空间的极值点确定过程中,我们不会漏掉任何一个尺度上的极值点,而是能够综合考虑量化的尺度因子。

2.4 用第i-1层的图像生成第i层的图像

值得注意的是,在SITF的源码里,尺度空间里的每一层的图像(除了第1层)都是由其前面一层的图像和一个相对sigma的高斯滤波器卷积生成,而不是由原图和对应尺度的高斯滤波器生成的,这一方面是因为我前面提到的不存在所谓意思上的“原图”,我们的输入图像I(x,y)已经是尺度为σ=0.5的图像了。另一方面是由于如果用原图计算,那么相邻两层之间相差的尺度实际上非常小,这样会造成在做高斯差分图像的时候,大部分值都趋近于0,以致于后面我们很难检测到特征点。

基于上面两点原因(个人认为原因1是最主要的,原因2只是根据实际尝试后的一个猜想,并无理论依据),所以对于每一组的第i+1层的图像,都是由第i层的图像和一个相对尺度的高斯滤波器卷积生成。

那么相对尺度如何计算呢,我们首先考虑第0组,它们的第i+1层图像与第i层图像之间的相对尺度为SigmaDiffi=(σ0ki+1)2–(σ0ki)2−−−−−−−−−−−−−−−√,为了保持尺度的连续性,后面的每一组都用这样一样相对尺度(SIFT实际代码里是这样做的)。这里有一个猜测,比如说尺度为2σ0的这一组,第i层与第i+1层之间的相对尺度计算的结果应该为(2σ0ki+1)2–(2σ0ki)2−−−−−−−−−−−−−−−−√=2⋅SigmaDiffi,可是代码里依然用SigmaDiffi是因为这一层被降维了。

sig[0] = sigma;
double k = pow(2., 1. / nOctaveLayers);
for (int i = 1; i < nOctaveLayers + 3; i++)
{double sig_prev = pow(k, (double)(i - 1))*sigma;double sig_total = sig_prev*k;sig[i] = std::sqrt(sig_total*sig_total - sig_prev*sig_prev);
}

3. 特征点的搜索

3.1 搜索策略

斑点的搜索是通过同一组内各DoG相邻层之间比较完成的。为了寻找尺度空间的极值点,每一个采样点要和它所有的相邻点进行比较,看其是否比它的图像域和尺度域的相邻点大或小。对于其中的任意一个检测点都要和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像位置空间都检测到极值点。也就是,比较是在一个3×3的立方体内进行。

搜索过程从每组的第二层开始,以第二层为当前层,对第二层的DoG图像中的每个点取一个3×3的立方体,立方体上下层为第一层与第三层。这样,搜索得到的极值点既有位置坐标(DoG的图像坐标),又有空间尺度坐标(层坐标)。当第二层搜索完成后,再以第三层作为当前层,其过程与第二层的搜索类似。当S=3时,每组里面要搜索3层。

3.2 子像元插值

上的的极值点的搜索是在离散空间中进行的,检测到的极值点并不是真正意义上的极值点。下图显示了一维信号离散空间得到的极值点与连续空间的极值点之间的差别。利用已知的离散空间点插值到连续空间极值点的方法叫子像元插值。

首先我们来看一个一维函数插值的例子。我们已经f(x)上几个点的函数值f(−1)=1,f(0)=6,f(1)=5,求f(x)在[−1,1]上的最大值。

如果我们只考虑离散的情况,那么只用简单比较一下,便知最大值为f(0)=6,下面我们用子像元插值法来考虑连续区间的上情况:

利用泰勒级数,可以将f(x)在f(0)附近展开为:

f(x)≈f(0)+f′(0)x+f′′(0)2x2

另外我们知道f(x)在x处的导数写成离散的形式为f′(x)=f(x+1)–f(x)2,二阶导数写成离散形式为f′′(x)=f(x+1)+f(x−1)−2f(x)。

所以,我们可以算出f(x)≈6+2x+−62x2=6+2x−3x2

求取函数f(x)的极大值和极大值所在的位置:

f′(x)=2−6x=0,   x^=13
f(x^)=6+2×13–3×(13)2=613

现在回到我们SIFT点检测中来,我们要考虑的是一个三维问题,假设我们在尺度为σ的尺度图像D(x,y)上检测到了一个局部极值点,空间位置为(x,y,σ),由上面的分析我们知道,它只是一个离散情况下的极值点,连续情况下,极值点可能落在了(x,y,σ)的附近,设其偏离了(x,y,σ)的坐标为(Δx,Δy,Δσ)。则对D(Δx,Δy,Δσ)可以表示为在点(x,y,σ)处的泰勒展开:

D(Δx,Δy,Δσ)=D(x,y,σ)+[∂Dx∂Dy∂Dσ]⎡⎣⎢ΔxΔyΔσ⎤⎦⎥+12[ΔxΔyΔσ]⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢∂2D∂x2∂2D∂y∂x∂2D∂σ∂x∂2D∂x∂y∂2D∂y2∂2D∂σ∂y∂2D∂x∂σ∂2D∂y∂σ∂2D∂σ2⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎡⎣⎢ΔxΔyΔσ⎤⎦⎥

可以将上式写成矢量形式如下:

D(x)=D+∂DT∂xΔx+12ΔxT∂2DT∂x2Δx

令上式的一阶导数等于0,可以求得Δx=−∂2D−1∂x2∂D(x)∂x

通过多次迭代(Lowe算法里最多迭代5次),得到最终候选点的精确位置与尺度x^,将其代入公式求得D(x^),求其绝对值得|D(x^)|。如果其绝对值低于阈值的将被删除。

Vec3f dD((img.at<sift_wt>(r, c + 1) - img.at<sift_wt>(r, c - 1))*deriv_scale,(img.at<sift_wt>(r + 1, c) - img.at<sift_wt>(r - 1, c))*deriv_scale,(next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c))*deriv_scale);
// dD为一阶差分矢量Df/Dx
float v2 = (float)img.at<sift_wt>(r, c) * 2;
float dxx = (img.at<sift_wt>(r, c + 1) + img.at<sift_wt>(r, c - 1) - v2)*second_deriv_scale;
float dyy = (img.at<sift_wt>(r + 1, c) + img.at<sift_wt>(r - 1, c) - v2)*second_deriv_scale;
float dss = (next.at<sift_wt>(r, c) + prev.at<sift_wt>(r, c) - v2)*second_deriv_scale;
float dxy = (img.at<sift_wt>(r + 1, c + 1) - img.at<sift_wt>(r + 1, c - 1) -img.at<sift_wt>(r - 1, c + 1) + img.at<sift_wt>(r - 1, c - 1))*cross_deriv_scale;
float dxs = (next.at<sift_wt>(r, c + 1) - next.at<sift_wt>(r, c - 1) -prev.at<sift_wt>(r, c + 1) + prev.at<sift_wt>(r, c - 1))*cross_deriv_scale;
float dys = (next.at<sift_wt>(r + 1, c) - next.at<sift_wt>(r - 1, c) -prev.at<sift_wt>(r + 1, c) + prev.at<sift_wt>(r - 1, c))*cross_deriv_scale;Matx33f H(dxx, dxy, dxs,dxy, dyy, dys,dxs, dys, dss);
// dD + Hx = 0  -->  x = H^-1 * (-dD)
Vec3f X = H.solve(dD, DECOMP_LU);

3.3 删除边缘效应

为了得到稳定的特征点,只是删除DoG响应值低的点是不够的。由于DoG对图像中的边缘有比较强的响应值,而一旦特征点落在图像的边缘上,这些点就是不稳定的点。一方面图像边缘上的点是很难定位的,具有定位歧义性;另一方面这样的点很容易受到噪声的干扰而变得不稳定。

一个平坦的DoG响应峰值往往在横跨边缘的地方有较大的主曲率,而在垂直边缘的方向有较小的主曲率。而主曲率可以通过2×2的Hessian矩阵H求出:

H(x,y)=[Dxx(x,y)Dxy(x,y)Dxy(x,y)Dyy(x,y)]

上式中,D值可以通过求取邻近点像素的差分得到。H的特征值与D的主曲率成正比例。我们可以避免求取具体的特征值,因为我们只关心特征值的比例。令α=λmax为最大的特征值,β=λmin为最小的特征值,那么,我们通过H矩阵直迹计算它们的和,通过H矩阵的行列式计算它们的乘积:

Tr(H)=Dxx+Dyy=α+β
Det(H)=DxxDyy−(Dxy)2=αβ

如果γ为最大特征值与最小特征值之间的比例,那么α=γβ,这样便有

Tr(H)2Det(H)=(α+β)2αβ=(γ+1)2γ

上式的结果只与两个特征值的比例有关,而与具体特征值无关。当两个特征值相等时,(γ+1)2γ的值最小,随着γ的增加,(γ+1)2γ的值也增加。所以要想检查主曲率的比例小于某一阈值γ,只要检查下式是否成立:

Tr(H)2Det(H)<(γ+1)2γ

Lowe在论文中给出的γ=10。也就是说对于主曲率比值大于10的特征点将被删除。

float t = dD.dot(Matx31f(xc, xr, xi));
//D(\bar{x}) = D + 1/2*dD*\bar{x}
contr = img.at<sift_wt>(r, c)*img_scale + t * 0.5f; // 插值得到的极值点的值
if (std::abs(contr) * nOctaveLayers < contrastThreshold)return false;
// principal curvatures are computed using the trace and det of Hessian
float v2 = img.at<sift_wt>(r, c)*2.f;
float dxx = (img.at<sift_wt>(r, c + 1) + img.at<sift_wt>(r, c - 1) - v2)*second_deriv_scale;
float dyy = (img.at<sift_wt>(r + 1, c) + img.at<sift_wt>(r - 1, c) - v2)*second_deriv_scale;
float dxy = (img.at<sift_wt>(r + 1, c + 1) - img.at<sift_wt>(r + 1, c - 1) -img.at<sift_wt>(r - 1, c + 1) + img.at<sift_wt>(r - 1, c - 1)) * cross_deriv_scale;
float tr = dxx + dyy;
float det = dxx * dyy - dxy * dxy;if (det <= 0 || tr*tr*edgeThreshold >= (edgeThreshold + 1)*(edgeThreshold + 1)*det)
return false;

SIFT定位算法关键步骤的说明相关推荐

  1. 图像特征提取:SIFT定位算法关键步骤的说明

    1. SIFT算法中一些符号的说明 I(x,y)表示原图像. G(x,y,σ)表示高斯滤波器,其中G(x,y,σ)=12πσ2exp(−(x2+y2)/2σ2). L(x,y,σ)表示由一个高斯滤波器 ...

  2. 图像迁移风格保存模型_CV之NS:图像风格迁移(Neural Style 图像风格变换)算法简介、关键步骤配图、案例应用...

    CV之NS:图像风格迁移(Neural Style 图像风格变换)算法简介.过程思路.关键步骤配图.案例应用之详细攻略 目录 图像风格迁移算法简介 图像风格迁移算法过程思路 1.VGG对比NS 图像风 ...

  3. ML之GB:GB算法相关论文、相关思路、关键步骤、代码实现、配图集合、案例应用之详细攻略

    ML之GB:GB算法相关论文.相关思路.关键步骤.代码实现.配图集合.案例应用之详细攻略 目录 GB算法相关文献.论文 GB算法关键步骤 GB算法代码实现 GB案例应用 1.GB用于回归 2.GB用于 ...

  4. ML之Clustering之H-clustering:Hierarchical clustering算法相关论文、主要思路、关键步骤、代码实现等相关配图之详细攻略

    ML之Clustering之H-clustering:Hierarchical clustering算法相关论文.主要思路.关键步骤.代码实现等相关配图之详细攻略 目录 H-clustering算法相 ...

  5. ML之HMM:HMM算法相关论文、关键步骤、测试代码配图集合

    ML之HMM:HMM算法相关论文.算法过程.关键步骤.测试代码配图集合 目录 HMM算法相关论文 HMM算法算法过程 HMM算法关键步骤 HMM算法测试代码 HMM算法相关论文 部分内容因为版本升级丢 ...

  6. CV之NS:图像风格迁移(Neural Style 图像风格变换)算法简介、过程思路、关键步骤配图、案例应用之详细攻略

    CV之NS:图像风格迁移(Neural Style 图像风格变换)算法简介.过程思路.关键步骤配图.案例应用之详细攻略 目录 图像风格迁移算法简介 图像风格迁移算法过程思路 1.VGG对比NS 图像风 ...

  7. rssi室内定位算法原理_一种基于RSSI测距的室内定位方法与流程

    本发明涉及室内定位领域,尤其涉及一种基于RSSI测距的室内定位方法. 背景技术: : 室内无线定位,是指利用无线网络和定位终端提供待测节点位置.速度和方向等相关信息的服务.对于一个定位算法而言,评价其 ...

  8. ML之Clustering之普聚类算法:普聚类算法的相关论文、主要思路、关键步骤、代码实现等相关配图之详细攻略

    ML之Clustering之普聚类算法:普聚类算法的相关论文.主要思路.关键步骤.代码实现等相关配图之详细攻略 目录 普聚类算法的相关论文 普聚类算法的主要思路 普聚类算法的关键步骤 普聚类算法的代码 ...

  9. ML之回归预测:机器学习中的各种Regression回归算法、关键步骤配图

    ML之回归预测:机器学习中的各种Regression回归算法.关键步骤配图 目录 机器学习中的各种回归算法 1.回归算法代码 2.各种回归算法 3.各种回归算法大PK 机器学习中的各种回归算法 1.回 ...

最新文章

  1. [2018.12.9]BZOJ2153 设计铁路
  2. HashMap在java并发中如何发生死循环
  3. 《0bug-C/C++商用工程之道》节选00--内存管理的基本要求
  4. 单元测试JUnit 4 (一)——keeps the bar green to keeps the code clean
  5. 中国历史上唯一没有贪污的王朝
  6. 启动docker容器报错 driver failed programming external connectivity on endpoint
  7. Ansible 详细用法部署安装
  8. wireshark-抓包极简使用教程
  9. avs php,linux 安装AdultVideoScript (AVS)全教程
  10. 非线性优化:Ax=b求解的几种算法
  11. 图画日记怎么画_画画日记(通用10篇)
  12. Pandas数据分析第2部分
  13. rar压缩包解密在线网站,rar压缩包有密码如何解开?
  14. 【蓝桥杯】单片机教程
  15. 【数据结构与算法】试卷 1(含答案)
  16. 计算机概论在线阅读,计算机科学概论(Python版)
  17. java 幽灵引用_全面解析Java中的GC与幽灵引用
  18. wps在线编辑梳理(此处整理了对接过后容易出错的地方)
  19. int类型数组和bool类型数组互相转换
  20. OMG!程序猿小哥是如何做到基金收益率高达26.03%?

热门文章

  1. leetcode算法题--删除回文子序列
  2. Day18 (二)反射
  3. Web安全实践(2)基于http的web架构剖析
  4. 区块链技术指2.1 区块链技术
  5. 基于 Asio 的 C++ 网络编程
  6. IOS之--UI进阶--多控制器管理第一天
  7. [题解]第十一届北航程序设计竞赛预赛——L.偶回文串
  8. Matplotlib for Python Developers
  9. 解决SQL Server 2000 错误15023:当前数据库中已存在用户或角色
  10. Stack(栈)和Heap(堆)的区别