标签 SIFT 下的文章

摘要

之前在SIFT算法中,有一个加速操作是使用图像金字塔,即不断对图像进行降采样。按照算法的思想表明:降采样后,标准差为$\sigma$的高斯模糊图像标准差会减半,得到标准差为$1/2\sigma$的高斯模糊图像

这里我不知道该如何证明....网上也没有相关资料,所以暂时采用数值解去验证这个说法。

实验过程

代码贴在最后,主要思路是比较两张图像:一张是先降采样一倍再用$\sigma$高斯模糊的图像;另一张是先使用$2\sigma$进行高斯模糊,再在模糊的图像上进行一倍降采样。

首先可视化这两张图,肉眼查看之间的差距,确实差距还是挺小的。此处$\sigma=30$(忽略窗口的值,那里标错了~)

image-20230709221257222

为了对比,这里把原图分别使用$\sigma$和$2\sigma$进行高斯模糊的结果也可视化了出来。这两张图就明显存在差异,这说明对高斯模糊过的图像降采样,确实会对其$sigma$产生影响

image-20230709221347242

image-20230709221357634

接着最早的两张图做差并画出来,可以看到形成了一个类似边缘检测的图像。这说明“先降采样再$\sigma$高斯模糊”跟“先$2\sigma$高斯模糊再降采样”这两个操作不完全等价。

为什么看上去是边缘检测图像?其实也很好理解,对于不同$\sigma$的高斯模糊图像相减,就是对高斯模糊图像微分,即DOG,DOG和LOG又只差一个常数倍,所以等效边缘检测了~

image-20230709221740842

接下来再探究“先$2\sigma$高斯模糊再降采样”得到的模糊图像的标准差到底是多少。最暴力的方式就是搜索,我们在降采样的图像上使用不同的$\sigma$进行遍历,画出delta的范数变化情况。最后我们发现:最接近的$\sigma_0$就是$\sigma$!

5d45f0cae88e5a5ae970dc07cc4d735

至此,我们知道上述的两个操作并不等价,但是它们足够接近。所以SIFT算法通过这种近似去做图像金字塔,大幅提高运算效率

代码

import cv2

Path = "C:\\Users\\Axuanz\\Desktop\\download.png"

if __name__ == "__main__":
    img = cv2.imread(Path,cv2.IMREAD_GRAYSCALE)

    sigma = 30

    ksize1 = sigma*6+1
    ksize2 = int(sigma/2*6+1)

    blur1 = cv2.GaussianBlur(img,ksize=(ksize1,ksize1),sigmaX=sigma)
    blur1 = cv2.pyrDown(blur1)

    downsample_img = cv2.pyrDown(img)
    blur2 = cv2.GaussianBlur(downsample_img,ksize=(ksize2,ksize2),sigmaX=sigma/2)

    blur3 = cv2.GaussianBlur(img,ksize=(ksize1,ksize1),sigmaX=sigma)
    blur4 = cv2.GaussianBlur(img,ksize=(ksize2,ksize2),sigmaX=sigma/2)

    delta = blur2 - blur1
    print(cv2.norm(delta))

    cv2.imshow("sigma=10",blur1)
    cv2.imshow("sigma=5",blur2)
    cv2.imshow("blur3",blur3)
    cv2.imshow("blur4",blur4)
    cv2.imshow("delta",delta)
    # cv2.waitKey()

###########################################
    sigma2 = 1
    norm_list = []
    while sigma2 <= sigma:
        _ksize = sigma2*6 + 1
        blur = cv2.GaussianBlur(downsample_img,ksize=(_ksize,_ksize),sigmaX = sigma2)
        delta = blur - blur1
        
        norm = cv2.norm(delta)
        print(f"sigma2 = {sigma2},delta norm ={norm}")

        norm_list.append(norm)
        sigma2 += 1
    
    import matplotlib.pyplot as plt
    x = list(range(1,sigma+1))
    print(x)
    print(norm_list)
    plt.plot(x,norm_list)
    plt.show()
###########################################

摘要

本节主要介绍尺度不变特征以及其经典代表——SIFT,网上关于如何计算SIFT特征的博客有很多,但是大多数博客都没有解释为什么这样找到的特征就是"尺度不变"的、为什么尺度和$\sigma$相关联等问题。因此这篇博客会对这些问题进行一些个人的补充,不一定对,欢迎留言~

尺度不变特征

在这里,我想把尺度理解成某个物体的大小。那么尺度不变特征就可以解释为:那些随着物体尺度变化而不会发生改变的特征。

具体来说,可以假设两张都有一个黑色实心圆的图片,图片大小保持一致,而图中的圆半径不一致。圆的尺度不同,那么尺度不变特征就可以理解成这两张图中共有的一些特征,这些特征不会随圆的改变而变化/消失。当然这里的圆也可以是复杂的建筑,如下图所示。

image-20230707194210236

之前提到的harris角点就明显不是一个尺度不变特征,当角点的大小放大到一定程度,只能检测到边,而检测不到角点,这说明角点特征不是尺度不变的。

回到刚刚圆的例子,我们希望能有一个特征,可以描述不同半径的圆。如果找到了,那么我们就可以允许识别任务中摄像头可以有一定的远近变化,这是非常有意义的,因为生活中的大部分识别任务不能100%保证摄像头与物体始终保持固定的距离。这就是尺度不变特征的意义。

拉普拉斯模板与圆形检测

之前介绍过高斯偏导模板,高斯一阶导模板可以检测边缘(边缘点的响应结果为极值),二阶导同样可以做到(边缘点的响应是0),我们称高斯二阶导模板为拉普拉斯模板

image-20230707194919919

拉普拉斯模板有个特点:对于特定宽度的信号,存在某个$\sigma$的拉普拉斯模板,两者卷积结果响应具有最大值,如下图所示。换句话说,对于某个宽度的信号,都可以找到一个$\sigma_0$对应的拉普拉斯模板,使两者在信号中间那个点的响应$f_0$最大,并且其他$\sigma$的拉普拉斯响应都小于$f_0$

image-20230707195110202

那么这个响应什么时候最大呢?可以证明:当信号宽度和拉普拉斯模板的0平面宽度一致时,响应可以取到最大值。带入拉普拉斯模板零平面方程可以得到$\sigma=r/\sqrt2$。

image-20230707195504518

至此,我们发现拉普拉斯模板可以检测圆形物体,具体方法是:对于同一张图像,我们使用不同的拉普拉斯模板去卷积($\sigma不断增大$),得到一系列卷积响应图。对于每个像素点,我们取出这些相应图中对应位置的响应,组成一个响应值序列。如果该序列存在极值,即满足响应从小到大再到小的趋势。那么我们可以认为在这个像素点可能是一个圆的圆心,圆的半径就是响应极值对应的拉普拉斯模板的$\sigma$乘以$\sqrt2$

尺度与sigma

以上说明还没有涉及尺度不变的问题,现在我们回到最开始提到的两张图像,两张图象都有一个圆,圆的半径不同。对于其中一张图像,我们通过上述方法可以检测出圆形的位置和大小。对于另外一张呢?同样可以!两者的区别只在于,我们通过一系列拉普拉斯模板去寻找响应值极值时,两张图片极值出现的像素点可能不同(圆心不同)以及极值对应的拉普拉斯模板$\sigma$不同(半径不同)。

但是这背后蕴藏了一个信息:对于圆这个物体,我们通过拉普拉斯响应可以在不同图像中都识别到它。并且这个圆大小并不影响我们的检测,无非是寻找到的$\sigma$不同而已。

那么这种拉普拉斯极值响应,是不是就是一种尺度不变的特征呢?答案我认为是对的,事实上这就是SIFT特征的理论基础。

还有一个小问题:我们刚刚发现拉普拉斯响应极值对应的$\sigma$和最终检测出来的圆形半径有关,圆形半径越大,$\sigma$越大,对应的拉普拉斯响应图越模糊。所以我们可以将圆形大小、$\sigma$、卷积结果的模糊程度联系起来。并且也多少能够解释网上的这种说法:不同$\sigma$的高斯模板卷积出来的图片对应不同的尺度空间,尺度原本应该描述大小上的关系(至少我的第一反应是),实际上却和图片的模糊程度相关。

SIFT特征

关于SIFT特征的求法,网上的资料已经非常丰富了。这里大概提一下重要的几个点。

改进一

第一个改进是使用DOG近似LOG,这是利用高斯模板之间的差分来近似拉普拉斯模板。可以证明的是

$$ normed \space LOG = \sigma \frac{ \partial G }{ \partial \sigma } $$

$$ G(x,y,k\sigma) - G(x,y,\sigma) \approx (k-1)\sigma^2 \nabla ^2G $$

那么我们就可以通过DOG(Difference of Gaussians)去近似LOG,可以节约计算量~

改进二

其次是不同$\sigma$的高斯响应,我们使用不同$\sigma$的高斯模板对于原图进行多次卷积操作。事实上,随着sigma不断增大,卷积核也越来越大,计算效率会不断降低。所以为了节约效率,SIFT有了第二个改进,通过上一次卷积的结果计算下一次卷积,这里用到了卷积的可加性,如下所示。

$$ \sigma_{op} = \sqrt{\sigma_{next}^2-\sigma_{curr}^2} $$

我们要得到一个$\sigma_{next}$的结果,只需要在$\sigma_{curr}$的基础上做一次$\sigma_{op}$的卷积就可以了。

改进三

第三个改进是使用多尺度的响应图,不同octave之间的图像大小相差一倍(使用上一个octave的倒数第三张图片缩放一倍后作为本octave的第一张图片,并且第一张图片不再做高斯模糊处理,为什么是倒数第三章可以参考其他博客,大概就是这种$\sigma$分布可以保证倒数第三张的$\sigma$刚好是$k\sigma$)。这里同样是考虑运算效率,小图上的卷积更快,并且降采样可以视为$sigma减半$,意味着不同octave之间可以使用相同的卷积模板,这也是opencv实现中的一个trick

image-20230707202438910

改进四

由于原始SIFT只能检测出圆形的特征区域,无法克服拍摄视角等问题。所以可以使用放射变换对圆进行微调。

image-20230707203911452

具体用到了之前harris角点提到的M矩阵,通过改变圆某个方向的半径,使M矩阵的两个特征值尽可能保持一致。具体方法就是,首先通过R矩阵确定圆形拍扁(拉长)的方向,在特征值较小的方向,将检测出来的圆进行压缩,直到椭圆区域内的M矩阵两个特征值一致

image-20230707204035170

改进五

对于提取出来的区域,我们可能会遇到视角旋转等问题。所以这里需要对提取出来的区域做一次归一化旋转。具体来说,就是统计区域内的各个像素点的梯度方向,画出统计直方图,根据直方图的主方向进行旋转,达成归一化的效果。因为只涉及梯度,所以这种方法对于光照、环境都是鲁棒的。

image-20230707204501881

image-20230707204320823

SIFT描述符

SIFT描述符的生成网上有很多资料,大致就是把SIFT区域分成4x4的子区域,每个子区域统计8个方向的梯度方向直方图,通过这八个实数来描述这个子区域,因此最终一个区域可以得到大小为128维的SIFT向量。这就是SIFT描述符了~

image-20230707204906140

总结

总的来说,SIFT特征理解起来还是比较抽象的,特别是尺度不变部分的描述。SIFT描述符也不止用在SIFT算法中,只要是对某个区域进行特征抽象都可以使用SIFT描述符的思想。