特征点检测与特征描述子
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
特征点检测与特征描述子
SIFT特征:
SIFT特征(Scale invariant feature transform)是一种局部特征检测的方法。
算法可以搜索出图像中的特征点,并且对特征点计算出一个128维的特征描述子以进行图像特征点匹配。
他具有尺度不变性,旋转不变性等优良性质,并且在一定程度上不受光照的影响。
原理介绍:
在介绍SIFT之前,先引入LoG (Laplacian of Gaussian)算子的
概念。
LoG算子实际就是在高斯滤波的基础上再求一个二阶导
(拉普拉斯算子)。
图像经过与LoG的卷积,得到的新矩阵,我
们通过寻找过0点就可以得到边缘角点等像素点。
并且使用归
一化的LoG算子可以得到尺度不变性(无论图像的尺度大小,
其极值点永远存在)。
之前曾经有人证明过,如果想要算子能够
产生稳定的图像特征,可以使用尺度归一化的LoG算子。
但是由于直接进行计算比较费时,所以SIFT通过
DOG(diference of Gaussian)来进行近似。
使用DOG来进行近似,需要构建高斯差分金字塔,在普通的图像金字塔基础上,在每个尺度的图像上使用标准差不同的高斯核做卷积。
之后,将相邻的图像相减得到最终的DOG结果,如下图所示。
在构造高斯图像金字塔时,需要以下几个参数。
O,图像降采样的次数,即有多少不同尺寸的图片;S,每个尺度的图片中,需要使用多少不同的高斯核进行卷积,σ,高斯核的标准差。
对于所有的DOG图,使用的高斯核的标准差满足下式:
下图更直观的表现了这三个参数之间的关系,并且具体O的数量与图像实际大小以及最小尺寸图像的大小有关。
而在实际计算当中,S的取值为3-5左右,并且由于我们需要得到高斯模糊后图像的差值,所以我们实际需要S+2张高斯模糊的图像,相邻的图片作差以得到S 张DOG图。
在得到DOG之后,需要寻找关键特征点。
待寻找特征是DOG图中在空间上的极值点。
这样对于每个像素,在他周围有8个像素点,并且和他同图片大小但是高斯核标准差不同的两个相邻的图片间,他们在空间上也有相邻关系。
这样每一个像素一共存在9*9+8一共26个相邻像素点。
如果这个像素点的DOG值为26个领域中的极大或极小值就认为这个像素是该图片大小下的一个特征点。
实际的特征点确定在此基础上还需要去掉一些不太好的极值点,并且有些特征点可能并不是恰好位于像素点上,而是处于两个像素点之间,所以还需要进一步的精确定位像素值。
具体做法此处略去,请参见论文及博客。
在确定了特征点的位置之后,需要进一步描述这些特征点。
描述特征点常常使用这一点附近像素梯度的直方图来描述这个特征点。
由于我们希望拥有旋转不变性。
在相同图片进行旋转之后,对于两幅图中相同的特征点,我们希望他们有几乎一样的特征描述子。
所以我们需要给每一个特征点设置一个主方向,而邻域的梯度也将会是相对这个主方向而言的。
为了获取这个主方向,我们在特征点邻域内获取梯度直方图,取直方图值最大的方向作为主方向。
在得到主方向之后,接下来就可以得到SIFT特征点的描述子。
在特征点的邻域内取16个像素点(在下图中以方格的形式呈现)。
之后,我们统计每个4*4的区域内像素的方向直方图(对于主方向而言的),每个直方图取8个bin。
这样每个关键点可以构造得到4*4*8一共128
维的特征描述子来描述这个关键点。
这样,在不同尺度的图像当中,每一个特征点都可以计算得到描述子。
至此,所有特征点的位置以及描述子计算完毕。
相关类与函数:
在OpenCV中可以使用多种方法计算SIFT特征。
/modules/nonfree/doc/feature_detection.html
class SIFT : public Feature2D
带参数的构造函数:
SIFT::SIFT(int nfeatures=0, int nOctaveLayers=3, double contrastThreshold=0.04, double edgeThreshold=10, double sigma=1.6)
nfeatures: 选取保留多少特征点。
默认为保留所有特征点。
nOctaveLayers: 图像金字塔的层数。
contrastThreshold: 用来去除不好的特征点的阈值。
阈值越高特征点越少。
edgeThreshold: 去除特征点边缘效应的阈值。
sigma: 在第0个octave的高斯核的标准差。
SIFT类重载运算符(),用于检测特征点。
void SIFT::operator()(InputArray img, InputArray mask, vector<KeyPoint>& keypoints, OutputArray descriptors, bool useProvidedKeypoints=false)
img: 待检测图像
mask: 可选参数,是否在某个区域内检测特征点
key_points: 检测得到的关键点。
descriptors: 特征点的特征描述子。
(注意为矩阵形式)
useProvidedKeypoints: false时将会检测特征点,true时会使用keypoints提供的特征点来计算描述子。
SURF特征:
SURF特征与SIFT特征原理类似,也是通过先寻找不同尺度图像中的关键点,然后计算这
些关键点的描述子。
在SIFT的基础上,SURF(Speed Up Robust Features)提高了特征提取的速度,并且也具有一定的尺度以及旋转不变性,并且受光照的影响更小。
原理介绍:
SIFT构造高斯图像金字塔以及使用DOG来寻找特征点,由于需要进行降采样以及多次高斯卷积,导致计算时间较长。
SURF使用Hessian矩阵来加快运算速度。
对于图像中的每个像素点我们可以计算Hessian矩阵,矩阵中的元素表示对图像进行高斯平滑后在x方向y方向的二阶导数(即高斯拉普拉斯算子)。
但是高斯拉普拉斯算子也较为费时间(所以SIFT才采用DOG去近似),在实际的计算中我们并不需要先进行高斯滤波再求二阶导数,而是使用一个近似于高斯拉普拉斯算子的滤波器与图像进行卷积,如下图所示。
左一图为y方向高斯拉普拉斯算子,我们使用左三图来进行近似;左二图为x方向的一阶导数与y方向一阶导数的乘积,我们使用左4图来进行近似。
类比于SIFT的图像金字塔,在SURF中我们也需要使用标准差不同的高斯核在不同的尺度上寻找特征点。
值得注意的一点是SIFT需要对图像进行降采样,而SURF却是通过改变滤波器的大小来构造尺度空间。
在得到不同尺度空间中每个像素的Hessian矩阵后,我们需要通过使用Hessian矩阵的行列式的值来找到特征点。
其中0.9是经验值,为了平衡行列式的值用。
之后,与SIFT相同,对于每个像素,我们需要查看他的行列式的值是否在空间中为极大值或极小值,以此来找到不同尺度上的特征点。
确定特征点之后,与SIFT相同,我们也需要计算每个特征点的特征描述子。
首先,在领域内计算梯度直方图确定主方向。
之后如下图所示,我们在特征点周围取一个正方形的框。
框的边长与该图所使用的滤波器大小有关。
将框分为16个区域,每个区域取25个像素点,分别统计他们在x方向和y方向(对于主方向而言)梯度之后以及梯度绝对值之和,这样每个区域可以得到一个四维的描述子,这样一个特征点一共拥有16*4维特征描述子。
相关类与函数:
与SIFT相同,使用NonFree包中的类。
Documentation链接同SIFT。
class SURF : public Feature2D
带参数的构造函数:
SURF::SURF(double hessianThreshold, int nOctaves=4, int nOctaveLayers=2, bool extended=true, bool upright=false )
hessianThreshold: Hessian矩阵行列式的阈值,低于阈值的极值点不会被作为关键点。
nOctaves: Octave的层数。
nOctaveLayers: 每个Octave中的层数。
extended: 是否使用扩展的特征。
upright: 是否启用主方向以旋转特征。
SURF类重载操作符()
void SURF::operator()(InputArray img, InputArray mask, vector<KeyPoint>& keypoints, OutputArray descriptors, bool useProvidedKeypoints=false)
img: 待检测图片
mask: 可选参数,可选择在某个区域内提取特征点
keypoints: 提取的特征点存放在vector中
descriptors: SURF特征描述子
useProvidedKeypoints: 是否使用keypoints中指定的关键点。
若为false,则会自动计算
keypoints,若为true,则会使用keypoints指定关键点计算描述子。
ORB特征
ORB( Oriented FAST and Rotated BRIEF ),是在BRIEF基础上解决了旋转不变性的一种快速的特征检测,特征点描述算法。
https:///sites/default/files/orb_final.pdf
原理介绍
首相,ORB算法使用FAST角点检测。
FAST角点特征的基本思想是通过找出像素中与周围像素亮度有明显差异的那些来作为关键点。
如下图所示,对于待检测的像素点p,它周围的
16个像素点当中,从6号到11号他们的灰度值与p的差异均超出了一个设定的阈值。
这样
我们就认定这样的店是角点。
由于我们希望判定角点,我们希望有连续的超过12个像素点均满足和p的灰度值的差异超过阈值。
并且我们可以优化算法,仅去判定1,5,9,13号像素点。
不过这样也会有一些问题:
1,当n < 12时非角点可能会被误认为角点。
2,角点并不是最优的,当前的设定默认了角点的分布,当分布不同时效果会变差。
3,前4号像素检测的结果会被丢弃
4,在临近的位置可能会检测到多个特征点
对于这四个问题,前三个可以使用机器学习的办法来解决。
由于我们希望可以知道如何去检测这16个点最为简便,所以这里使用了一个决策树的办法。
对于这个决策树,它的输入是一个待检测像素,以及它周围16个像素点的灰度值。
根据决策树,会依次选择使用哪一个像素点进行检测,直到得到这个像素是否为角点的信息。
具体的训练过程请参考论文。
为了解决问题4,同样需要使用非极大抑制的办法。
对于两个位置相邻的角点,我们用下式计算他们的角点响应值:
如果一个角点的V响应值比与他相邻的角点V值更小,我们就去掉他。
最后得到剩余的角点。
在FAST角点检测的基础上,为了使描述子有尺度不变性,还需要构建高斯图像金字塔,对图像进行高斯模糊并进行降采样。
此外,在实验中还发现FAST在检测图像中的边缘时V 响应也很高,所以在实际计算时,会先将FAST的阈值设置得较低,之后再使用Harris Corner Detector的响应函数对这些潜在可能的角点计算响应,并且取得值最高的N个角点来作为最终的特征点。
在寻找到特征描述子之后,需要计算ORB的特征描述子。
ORB的特征描述子使用改进的BRIEF算法,为BRIEF特征增加了旋转不变性这个特点。
BRIEF特征非常简单,具体来讲,对于一个特征点周围给定的一块区域,将这个区域内的像素进行编号。
随机取一定组的像素对并编号为1,2,3,4......n。
第一组中第一个像素大于第二个像素,则最终的BRIEF特征的第一位就为1,否则为0。
如此依次比较第二组,第三组等。
最终等到一个n-bit的特征向量。
但是基本的BRIEF特征并不具有旋转不变性。
相比于SIFT和SURF使用直方图寻找特征点的主方向,ORB通过计算“灰度中心”来确定主方向。
灰度中心是通过矩(moment)来计算。
通过下式可以计算中心:
而主方向可以通过下式进行计算:
对于BRIEF特征,原本我们将会抽取N组像素点对,在ORB中提出了BRIEF的一个变种
Steered BRIEF。
它将考虑之前计算的主旋转方向。
在不考虑主方向时抽取的像素点组成矩阵S。
在考虑主方向后,实际是在这个S矩阵前乘上了一个旋转矩阵R得到新的像素点坐标。
在BRIEF特征中,我们希望这些像素点对有较大的方差,这样这些点对才更有区分度。
为了保证这一特点,ORB提出了rBRIEF,rBRIEF的区别在于它通过穷举,在一个图像训练集上通过学习,选择了一种最有区分度的像素选择方式。
具体的训练过程请参见论文。
总得来说,ORB使用FAST得到特征点,用Harris Corner Detector进一步过滤特征点。
使用rBRIEF来计算特征点的特征描述子,构成256-bit的特征向量。
而SIFT和SURF的每一个特征向量为128*32bit或128*64bit浮点型数据。
这样节省了空间,并且和SIFT和SURF 一样,基本拥有尺度不变性,旋转不变性,并且在面对光照,模糊,噪声也有一定抗干扰性。
相关类与函数
class ORB : public Feature2D
ORB::ORB(int nfeatures=500, float scaleFactor=1.2f, int nlevels=8, int edgeThreshold=31, int firstLevel=0, int WTA_K=2, int scoreType=ORB::HARRIS_SCORE, int patchSize=31) nfeatures: 最多提取多少个特征
scaleFactor: 降采样率
nlevels: 图像金字塔的层数
edgeThreshold: 边缘保留的像素数量,和后面的patchSize有关,最好使用默认值firstLevel: 当前实现下应当为0
WTA_K:使用多少个patch来计算得到一位特征。
为2或4。
scoreType: 是否使用Harris Corner Detector来计算角点响应值。
可以为HARRIS_SCORE或者FAST_SCORE。
patchSize: 用于计算特征的patch大小。
void ORB::operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints, OutputArray descriptors, bool useProvidedKeypoints=false )
image: 输入图像
mask: 选择图像需要检测的部分,整张图片使用cv::noArray()
keypoints: 检测得到的特征点
descriptors: 描述特征点的描述子
useProvidedKeypoints: 是否使用给定的keypoints用于计算描述子,具体同SIFT和SURF。
BRISK特征
http://www.asl.ethz.ch/people/lestefan/personal/iccv2011.pdf
原理介绍
BRISK特征和之前的OBR特征均发布于2011年的ICCV,两个算法均利用了FAST角点检
测的思想,并且在FAST的基础上进行改进使得各自的特征点具有尺度不变性。
在构造图像金字塔上,BRISK对每张图以比率为2进行降采样。
在此基础之上,在两张相邻的图之间再进行降采样得到一个内部层
(intra-octave layer)。
内部层的降采
样比率为1.5。
首先,我们在每一
个尺寸的图像上(包括内部层)拥
TYPE_9_16做FAST角点检测,
得到的角点作为备选特征点。
之
后,我们会在这些角点上使用
FAST中的V响应在空间上做非极
大抑制。
比较新颖的一点在于
BRISK在寻找尺度空间和FAST
响应值时会使用插值的办法。
如
图中所示,在空间上寻找极值时,
会考虑与其相邻的两个内部层。
当确定了该店为尺度空间的极大
值时,我们可以得到三个响应值
为x轴坐标以及以尺度i为y坐
标。
这样我们可以用一个二次曲
线进行拟合以得到最终的尺度
值。
在得到尺度值之后,这个关
键点的位置会被重新插值计算得到。
这样我们就可以得到需要的特征点的位置。
接下来介绍BRISK计算特征描述子的方法。
在特征点周围,算法将会以右面的模式在领域内进行
采样。
在这个领域内取N个采样点,这样将会由
N*(N-1)/2组点对。
对于这些点对,可以分为长距
和短距两种。
然后对于每一组点对,我们可以计算
他们的局部梯度:
之后,为了获得旋转不变性,我们依然选哟寻找特
征点的主方向:使用所有的长距点对来求这个特征
点的主方向:
在得到这个梯度之后,我们可以得到这个梯度的方向从而确定主方向。
之后,与ORB相同,每一组点对代表一位特征。
BRISK不会使用所有点对,通过长短距种类的阈值以及采样模式最终将会得到一个512-bit的特征描述子。
相关类与函数
class BRISK : public Feature2D
BRISK::BRISK(int thresh=30, int octaves=3, float patternScale=1.0f)
thresh: FAST角点检测所使用的阈值
octaves: 图像金字塔的层数
patternScale: 领域采样区域的大小
下面这个构造函数用于希望自己确定采样模式,并且设置长短距的阈值
BRISK::BRISK(std::vector<float>& radiusList, std::vector<int>& numberList, float dMax=5.85f, float dMin=8.2f, std::vector<int> indexChange=std::vector<int>())
radiusList: 定义在关键点周围采样的半径
numberList: 定义在关键点周围采样的个数
dMax: 短距组的阈值
dMin: 长距组的阈值
indexChange: 重新安排采样点的顺序
void BRISK::operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints, OutputArray descriptors, bool useProvidedKeypoints=false )
image: 待检测的图像
mask: 需要检测的区域,整张图输入cv::noArray()
keypoints: 存储检测出的关键点
descriptors: 关键点的描述子
useProvidedKeypoints: 是否使用指定的keypoints,同前几个算法
FREAK特征
FREAD特征与之前的BRISK以及ORB类似。
在寻找特征点的过程中均使用FAST角点检测。
FREAK与BRISK不同点在于在计算特征描述子的时候,使用的采样模式较为不同。
大致思想是通过模拟生物视网膜结构来设计采样模式,并且在计算描述子的时候先计算粗略的特征,再更进一步计算更精细化的特征。
总得来说FREAK与BRISK基本一致,只是更换了采样模式,改进了描述子的计算方式。
相关类与函数
class FREAK : public DescriptorExtractor
FREAK::FREAK(bool orientationNormalized=true, bool scaleNormalized=true, float patternScale=22.0f, int nOctaves=4, const vector<int>& selectedPairs=vector<int>() ) orietationNormalized: 是否使用归一化的方向
scaleNormalized: 是否使用归一化的尺度
patternScale: 描述子计算使用的采样模式的尺度
nOctaves: 图像金字塔层数
selectedPairs: 用户可以自己指定点对的序号
BRIEF特征
BRIEF特征的原理在ORB中已经基本阐述。
主要思想为在特征点周围的一个区域内,进行随机采样得到一些像素点对,通过比较这些像素点对的灰度值来得到特征向量。
相关类与函数
Class BriefDescriptorExtractor : public DescriptorExtractor
BriefDescriptorExtractor(int bytes = 32)
Bytes: 特征描述子的长度,可以为16,32,64,对应特征维度为128,256,512。
若要进行计算,使用继承DescriptorExtractor类中的Compute函数。