第十二节、尺度不变特征(SIFT)
目录
上一节中,我们介绍了Harris角点检测。角点在图像旋转的情况下也可以检测到,但是如果减小(或者增加)图像的大小,可能会丢失图像的某些部分,甚至导致检测到的角点发生改变。这样的损失现象需要一种与图像比例无关的角点检测方法来解决。尺度不变特征变换(Scale-Invariant Feature Transform,SIFT)可以解决这个问题。我们使用一个变换来进行特征变换,并且该变换会对不同的图像尺度输出相同的结果。
到底什么是SIFT算法?通俗一点说,SIFT算法利用DoG(差分高斯)来提取关键点(或者说成特征点),DoG的思想是用不同的尺度空间因子(高斯正态分布的标准差)对图像进行平滑,然后比较平滑后图像的区别,差别大的像素就是特征明显的点,即可能是特征点。对得到的所有特征点,我们剔除一些不好的,SIFT算子会把剩下的每个特征点用一个128维的特征向量进行描述。
由上,易知,一幅图像经过SIFT算法后可以表示为一个128维的特征向量集。
SIFT具有一下特征:
- SIFT特征,对旋转、尺度缩放、亮度变化等保持不变性,对视角变换、仿射变化、噪声也保持一定程度的稳定性,是一种非常优秀的局部特征描述算法;
- 独特性好,信息量丰富,适用于海量特征库进行快速、准确的匹配;
- 多量性,即使是很少几个物体也可以产生大量的SIFT特征;
- 高速性,经优化的SIFT匹配算法甚至可以达到实时性的要求;
- 扩展性,可以很方便的与其他的特征向量进行联合。
一 使用DoG和SIFT进行特征提取和描述
1.1 示例
我们先用OpenCV库函数演示一下DoG和SIFT特征提取的效果,然后再来讲述一下SIFT的原理。
我们先来介绍一下Different of Gaussians(DoG),DoG是对同一图象使用不同高斯滤波器作差所得到的结果。DoG操作的最终结果会得到感兴趣的区域(关键点),这将通过SIFT来进行特征描述。
我们来看看如何通过SIFT得到充满角点和特征的图像:
# -*- coding: utf-8 -*- """ Created on Wed Aug 22 16:53:16 2018 @author: lenovo """ ''' SIFT算法 ''' import cv2 import numpy as np img = cv2.imread('./image/cali.bmp') img = cv2.resize(img,dsize=(600,400)) #转换为灰度图像 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #创建一个SIFT对象 sift = cv2.xfeatures2d.SIFT_create() #SIFT对象会使用DoG检测关键点,并且对每个关键点周围的区域计算特征向量。该函数返回关键点的信息和描述符 keypoints,descriptor = sift.detectAndCompute(gray,None) print(type(keypoints),len(keypoints),keypoints[0]) print(descriptor.shape) #在图像上绘制关键点 img = cv2.drawKeypoints(image=img,keypoints = keypoints,outImage=img,color=(255,0,255),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) #显示图像 cv2.imshow('sift_keypoints',img) cv2.waitKey(0) cv2.destroyAllWindows()
我们首先创建了一个SIFT对象,SIFT对象会使用DoG检测关键点,并且对每个关键点周围区域计算特征向量。detectAndCompute()函数会返回关键点信息(每一个元素都是一个对象,有兴趣的可以看一下OpenCV源码)和关键点的描述符。然后,我们在图像上绘制关键点,并显示出来。
1.2 cv2.drawKeypoints函数
上面我们用到了一个函数,下面来介绍一下:
1 | cv2.drawKeypoints(image,keypoints,outImage[,color[,flags]]) |
参数描述:
image:原始图像,可以使三通道或单通道图像;
keypoints:特征点集合list,向量内每一个元素是一个KeyPoint对象,包含了特征点的各种属性信息;
outImage:特征点绘制的画布图像,可以是原图像;
color:颜色设置,绘制的特征点的颜色信息,默认绘制的是随机彩色;
flags:特征点的绘制模式,其实就是设置特征点的哪些信息需要绘制,哪些不需要绘制,有以下几种模式可选:
- cv2.DRAW_MATCHES_FLAGS_DEFAULT:创建输出图像矩阵,使用现存的输出图像绘制匹配对和特征点,对每一个关键点只绘制中间点;
- cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG:不创建输出图像矩阵,而是在输出图像上绘制匹配对;
- cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:对每一个特征点绘制带大小和方向的关键点图形;
- cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制;
我们传入了DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS标志位,表明对图像每个关键点都绘制圆圈(大小)和方向。
二 SIFT特征检测的步骤
- 尺度空间的极值检测:搜索所有高斯尺度空间上的图像,通过高斯差分函数(DoG)来识别潜在的对尺度和选择不变的兴趣点。
- 关键点的定位和过滤:在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。
- 特征方向赋值:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
- 特征点描述:在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。
三 尺度空间
在一定的范围内,无论物体是大还是小,人眼都可以分辨出来。然而计算机要有相同的能力却不是那么简单的事情,在未知的场景中,计算机视觉并不能提供物体的尺度大小,其中的一个方法就是把物体不同尺寸下的图像都提供给机器,让机器能够对物体再不同的尺度下有一个统一的认知。在建立统一认知的过程中,要考虑的就是图像在不同的尺度下都存在的特征点。
3.1 多分辨率图像金字塔
在早期图像的多尺度通常使用图像金字塔表述形式。图像金字塔是同一图象在不同的分辨率下得到的一组结果,其生成过程一半包含两个步骤:
1、对原始图像进行平滑出来;
2、对处理后的图像进行降维采样(通常是水平、垂直方向的1/2);降维采样后得到一系列尺寸不断缩小的图像。显然,一个传统的金字塔中,每一层的图像是其上一层图像长、高的各一半。多分辨率的图像金字塔虽然生成简单,但其本质是降维采样,图像的局部特征则难以保持,也就是无法保持特性的尺度不变性。
3.2 高斯尺度空间
我们还可以通过图像的模糊程度来模拟人在距离物体远近时物体在视网膜上的成像过程,距离物体越近其尺寸越大图像也越清晰,这就是高斯尺度空间,使用不同的参数模糊图像(保持分辨率不变),是尺度空间的另一种表现形式。
我们知道图像和高斯函数进行卷积运算能够对图像进行模糊,使用不同的"高斯核"可得到不同模糊程度图像。
高斯核函数(二维高斯函数)的公式可以写成:
称为尺度空间因子,它是高斯正太分布的标准差,反映了图像被模糊的程度,其值越大图像越模糊,对应的尺度也就越大。
在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆,如图所示。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果。
一副图像其高斯尺度空间可由其和不同的高斯卷积得到:
代表着图像的高斯尺度空间。下图为图像取不同值得到的高斯尺度空间:
构建尺度空间的目的是为了检测出在不同的尺度下都存在的特征点,而检测特征点较好的算子是(高斯拉普拉斯,LoG,图像的二阶导数),是由2002年Mikolajczyk在详细的实验比较中发现的,同其它的特征提取函数,例如:梯度,Hessian或Harris角特征比较,能够产生最稳定的图像特征。
而Lindeberg早在1994年就发现高斯差分函数(Difference of Gaussian ,简称DOG算子)与尺度归一化的高斯拉普拉斯函数非常近似。使用LoG虽然能较好的检测到图像中的特征点,但是其运算量过大,通常可使用DoG(差分高斯,Difference of Gaussina)来近似计算LoG。其中和的关系可以从如下公式推导得到:
利用差分近似代替微分,则有:
因此有:
其中k-1是个常数,并不影响极值点位置的求取。高斯拉普拉斯和高斯差分的比较如下:
如图所示,红色曲线表示的是高斯差分算子,而蓝色曲线表示的是高斯拉普拉斯算子。Lowe使用更高效的高斯差分算子代替拉普拉斯算子进行极值检测,设为相邻两个高斯尺度空间的比例因子,则DoG的定义:
其中,是图像的高斯尺度空间。
从上式可以知道,将相邻的两个高斯空间的图像相减就得到了DoG的响应图像。为了得到DoG图像,先要构造高斯尺度空间,而高斯的尺度空间可以在图像金字塔降维采样的基础上加上高斯滤波得到,也就是对图像金字塔的每层图像使用不同的尺度参数进行高斯模糊,使每层金字塔有多张高斯模糊过的图像,然后我们把得到的同一尺寸大小的图像划分为一组。
易知,高斯金字塔有多组(如上图),每组又有多层。一组中的多个层之间的尺度是不一样的(也就是使用的高斯参数是不同的),相邻两层之间的尺度相差一个比例因子,如果每组有层,则。上一组图像的最低层图象是由下一组中尺度为的图像进行因子为2的降维采样得到的(高斯金字塔先底层建立)。高斯金字塔构建完成后,将相邻的高斯金字塔相减就得到了DoG金字塔。
高斯金字塔的组数一般是:
代表高斯金字塔的组数,,分别是图像的行和列。减去的系数可以在之间的任意值,和具体需要的金字塔的顶层图像的大小有关。
高斯模糊参数(尺度空间),可由下面关系得到:
其中为所在的组,为所在的层,为初始的尺度,为每组的层数。
在Lowe论文中,,,就是首先将原图像的长和宽各扩展一倍(为了保留原始图像信息,增加特征点数量)。
从上面可以得到同一组内相邻的图像尺度关系:
相邻组处于同一层之间的图像尺度关系:
下面是高斯尺度空间表示的一个例子:
3.3 高斯金字塔构建案例
以一张的图像为例,构建高斯金字塔步骤:(从0开始计数,倒立的金字塔)
1、金字塔的组数,,减去因子3,构建的金字塔组数为,取每组的层数为,相邻两个高斯尺度空间的比例因子;
2、构建第0组,将图像的宽和高都增加一倍,变成()。第0层,第1层,第2层;
3、构建第1组,对降维采样变成()。第0层,第1层,第2层;
4、......
5、构建第组,第层;
高斯金字塔构建成功后,将每一组相邻的两层相减就可以得到DoG金字塔。
四 DoG空间极值检测
为了寻找DoG金字塔尺度空间的极值点,每个像素点要和其图像域(相同大小的图像)和尺度域(相邻的尺度空间)的所有相邻点进行比较,当其大于(或者小于)所有相邻点时,该点就是极值点。如图所示,中间的检测点要和其所在图像的邻域8个像素点,以及其相邻的上下两层邻域18个像素点,共26个像素点进行比较。
从上面的描述中可以知道,不同组的图像大小不一样,因此每组图像的第一层和最后一层是无法进行比较取得极值的。为了满足尺度变换的连续性,在每一组图像继续使用高斯模糊生成3幅图像,高斯金字塔每组层图像,DoG金字塔的每组有层图像.
4.1 尺度变化的连续性
设,也就是每组有3层,则。如果按照之前的计算,也就是高斯金字塔每组有3层图像,DoG金字塔每组有2层图像,在DoG金字塔的第一组有两层尺度分别是,,,第二组有两层尺度分别是,,由于只有两项是无法比较取得极值的(只有左右两边都有值才能有极值),我们必须在高斯空间继续添加高斯模糊项,使得DoG空间尺度形成,,,,,这样就可以选择中间的三项,,。对应的下一组由上一组降维采样得到的三项是,,,其首项,刚好与上一组的最后一项尺度连接起来。所以需要在每一组图像继续使用高斯模糊生成3幅图像,高斯金字塔每组有层图像,DoG金字塔的每组有层图像。
下面是局部极值检测的结果:
五、删除不好的极值点(特征点)
到现在,我们已经得到了部分关键点,但是现在存在几个问题,通过比较检测得到的DoG的局部极值点是在离散的空间搜索得到的,由于离散空间是对连续空间采样得到的结果,因此在离散空间找到的极值点不一定是真正意义上的极值点,因此要设法将不满足条件的点剔除掉。可以通过尺度空间DoG函数进行曲线拟合寻找极值点,这一步的本质是去掉DoG局部曲率非常不对称的点,增强匹配稳定性、提高抗噪声能力,以达到精确定位关键点的目的。
利用已知的离散空间点插值得到的连续空间极值点的方法叫做子像素插值(Sub-pixelInterpolation)。
要剔除掉的不符合要求的点主要有两种:
- 低对比度的特征点;
- 不稳定的边缘响应点;
5.1 剔除低对比度的特征点
假设我们在尺度为的尺度图像上检测到了一个局部极值点,空间位置为,为了简单方便我们使用表示这个候选点,其极值点偏移量用表示,为了提高关键点的稳定性,需要对尺度空间DoG函数进行曲线拟合。利用DoG函数在尺度空间的泰勒展开式(拟合函数)为:
对求导并让方程等于零,可以得到极值点的偏移量为:
然后再把求得的带入到的泰勒展开式中:
其中,设对比度的阈值为,对比度为的绝对值,若,则该特征点保留,否则剔除掉。
下面是删除绝对值较小后的结果:
5.2 剔除不稳定的边缘响应点
有些极值点的位置是在图像的边缘位置的,因为图像的边缘点很难定位,同时也容易受到噪声的干扰,我们把这些点看做是不稳定的极值点,需要进行去除。
在边缘梯度的方向上主曲率值比较大,而沿着边缘方向则主曲率值较小。候选特征点的DoG函数的主曲率与Hessian矩阵的特征值成正比:
其中,,,是候选点邻域位置差分求得的。
为了避免求具体的值,可以使用特征值的比例.设为的最大特征值,为的最小特征值,则:
其中,为矩阵的迹,为矩阵的行列式.
设表示最大特征值与最小特征值的比值,则:
上式的结果与两个特征值的比例有关,和具体的大小无关,当两个特征值相等时其值最小,并且随着的增大而增大。因此为了检测主曲率是否在某个阈值下,只需检测:
如果上式成立,则剔除该特征点,否则保留。(Lowe论文中取)
我们知道边缘矩阵一个特征值很大,一个特征值很小,因此可以通过过滤较大的值达到过滤边缘的目的。具体可参见Harris角点检测算法第十一节、Harris角点检测原理。
下面是移除边缘响应后的结果:
注意:代码实现以及具体细节可以参看文章SIFT定位算法关键步骤的说明。
六 求取特征点的主方向
经过上面的步骤已经找到了在不同尺度下都存在的特征点,为了实现图像旋转不变性,需要给特征点的方向进行赋值。利用特征点邻域像素的梯度来确定其方向参数,再利用图像的梯度直方图求取关键点局部结构的稳定方向。
对于已经检测到的特征点,我们知道该特征点的尺度值,因此根据这一尺度值,也就可以得到这一尺度的高斯图像:
使用有限差分,计算以特征点为中心,为半径的区域图像的幅角和幅值,每个点的梯度的模以及方向可通过下面公式得到:
计算得到梯度方向后,就要使用直方图统计特征点邻域内像素对应的梯度方向和幅值。梯度方向的直方图的横轴是梯度方向的角度(梯度方向的范围是0到360度,直方图每36度一个柱共10个柱,或者每45度一个柱共8个柱),纵轴是梯度方向对应梯度幅值的累加,在直方图的峰值就是特征点的主方向。
在Lowe的论文还提到了使用高斯函数对直方图进行平滑以增强特征点近的邻域点对关键点方向的作用,并减少突变的影响。为了得到更精确的方向,通常还可以对离散的梯度直方图进行插值拟合。具体而言,关键点的方向可以由和主峰值最近的三个柱值通过抛物线插值得到。在梯度直方图中,当存在一个相当于主峰值80%能量的柱值时,则可以将这个方向认为是该特征点辅助方向。所以,一个特征点可能检测到多个方向(也可以理解为,一个特征点可能产生多个坐标、尺度相同,但是方向不同的特征点)。Lowe在论文中指出15%的关键点具有多方向,而且这些点对匹配的稳定性很关键。
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了