sift、surf、orb 特征提取——三维重建

参考文章:[sift、surf、orb 特征提取及最优特征点匹配]
最近刚刚入门三维重建,在SFM算法的代码中注意到了feature_type( feature extraction method)这个参数,想细致了解一下这几种特征提取算法,在这里做一下笔记,文章中删除线 的标记都是一些我存疑的点,还请大家评论区不吝赐教。

SIFT

简介

SIFT(Scale-Invariant Feature Transform)——尺度不变特征变换,是一种计算机视觉的特征提取算法,用来侦测与描述图像中的局部特征。

实质上,它是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向 。SIFT所查找到的关键点是一些十分突出、不会因光照、仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

特征提取步骤

1.尺度空间的极值检测
尺度空间指一个变化尺度σ的二维高斯函数G(x,y,σ)与原图像I(x,y)卷积(即高斯模糊)后形成的空间:
在这里插入图片描述
L = G*I相当于对图片进行了滤波操作,不同的σ对图片进行的滤波程度不同

在这里插入图片描述
经过不同程度滤波后的图片进行减法运算,得到DOG图
在这里插入图片描述

尺度不变特征应该既是空间域上又是尺度域上的局部极值。极值检测的大致原理是根据不同尺度下的高斯模糊化图像差异(Difference of Gaussians,DoG)寻找局部极值,这个点不仅要在图像域上是最大值,还要在尺度域上为最大值,寻找方法如图示,这些找到的极值所对应的点被称为关键点或特征点。
在这里插入图片描述
2.关键点定位
在不同尺寸空间下可能找出过多的关键点,有些关键点可能相对不易辨识易受噪声干扰。该步借由关键点附近像素的信息、关键点的尺寸、关键点的主曲率来定位各个关键点,借此消除位于边上或是易受噪声干扰的关键点。
在这里插入图片描述
讲真。。。下面这张图说的有些看不懂,可能是我有些图像处理的基础知识没掌握
在这里插入图片描述
(r + 1)2/r的值在两个特征值相等的时候最小,随着r的增大而增大,因此,为了检测主曲率是否在某域值r下,只需检测:
在这里插入图片描述
if (α+β)/ αβ> (r+1)2/r, throw it out. 在Lowe的文章中,取r=10。
3. 方向定位
为了使描述符具有旋转不变性,需要利用图像的局部特征为给每一个关键点分配一个基准方向。通过计算关键点局部邻域的方向直方图,寻找直方图中最大值的方向作为关键点的主方向。
上一步中确定了每幅图中的特征点,为每个特征点计算一个方向,依照这个方向做进一步的计算, 利用关键点邻域像素的梯度方向分布特性为每个关键点指定方向参数,使算子具备旋转不变性。
在这里插入图片描述
在实际计算时,我们在以关键点为中心的邻域窗口内采样,并用直方图统计邻域像素的梯度方向。梯度直方图的范围是0~360度,其中每45度一个柱,总共8个柱, 或者每10度一个柱,总共36个柱。Lowe论文中还提到要使用高斯函数对直方图进行平滑,减少突变的影响。直方图的峰值则代表了该关键点处邻域梯度的主方向,即作为该关键点的方向。

4. 关键点描述子
找到关键点的位置、尺寸并赋予关键点方向后,将可确保其移动、缩放、旋转的不变性。此外还需要为关键点建立一个描述子向量,使其在不同光线与视角下皆能保持其不变性。SIFT描述子是关键点邻域高斯图像梯度统计结果的一种表示,见下图。通过对关键点周围图像区域分块,计算块内梯度直方图,生成具有独特性的向量,这个向量是该区域图像信息的一种抽象,具有唯一性。Lowe在原论文中建议描述子使用在关键点尺度空间内44的窗口中计算的8个方向的梯度信息,共44*8=128维向量表征。(opencv中实现的也是128维)
在这里插入图片描述
直方图中的峰值就是主方向,其他的达到最大值80%的方向可作为辅助方向
在这里插入图片描述该步中将建立所有scale中特征点的描述子(128维)。

通过对关键点周围图像区域分块,计算块内梯度直方图,生成具有独特性的向量,这个向量是该区域图像信息的一种抽象,具有唯一性。
在这里插入图片描述
首先将坐标轴旋转为关键点的方向,以确保旋转不变性。以关键点为中心取8×8的窗口。(有个问题,为什么这样就能保证旋转不变性了)
在这里插入图片描述这样就可以对每个feature形成一个4x4x8=128维的描述子,每一维都可以表示4*4个格子中一个的scale. 将这个向量归一化之后,就进一步去除了光照的影响
在这里插入图片描述
在这里插入图片描述
具体可以参考这篇博客:https://www.cnblogs.com/liuchaogege/p/5155739.html***

Code

特征提取

def sift(filename):
    img = cv2.imread(filename) # 读取文件
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转化为灰度图
    sift = cv2.xfeatures2d_SIFT.create()
    keyPoint, descriptor = sift.detectAndCompute(img, None) # 特征提取得到关键点以及对应的描述符(特征向量)
    return img,keyPoint, descriptor

这里解释一下为什么要进行转化为灰度图?
识别物体,最关键的因素是梯度(SIFT/HOG),梯度意味着边缘,这是最本质的部分,而计算梯度,自然就用到灰度图像了,可以把灰度理解为图像的强度。
颜色,易受光照影响,难以提供关键信息,故将图像进行灰度化,同时也可以加快特征提取的速度。

特征匹配
BruteForce匹配FLANN匹配是opencv二维特征点匹配常见的两种办法,对应BFMatcher(BruteForceMatcher)和FlannBasedMatcher。
二者的区别在于BFMatcher总是尝试所有可能的匹配,从而使得它总能够找到最佳匹配,这也是Brute Force(暴力法)的原始含义。而FlannBasedMatcher中FLANN的含义是Fast Library forApproximate Nearest Neighbors,从字面意思可知它是一种近似法,算法更快但是找到的是最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。当然也可以通过调整FlannBasedMatcher的参数来提高匹配的精度或者提高算法速度,但是相应地算法速度或者算法精度会受到影响。

本文是进行最优特征点匹配,因此选用BruteForce Matcher。

def match(filename1, filename2):
    img1, kp1, des1 = sift(filename1)
    img2, kp2, des2 = sift(filename2)
    bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)  # sift的normType应该使用NORM_L2或者NORM_L1
    matches = bf.match(des1, des2)
    matches = sorted(matches, key=lambda x: x.distance)
    knnMatches = bf.knnMatch(des1, des2, k=1)  # drawMatchesKnn
        # 过滤
    for m in matches:
        for n in matches:
            if(m != n and m.distance >= n.distance*0.75):
                matches.remove(m)
                break
    img = cv2.drawMatches(img1, kp1, img2, kp2, matches[:50], img2, flags=2)
    cv2.imshow("matches", img)
    cv2.waitKey()
    cv2.destroyAllWindows()
def main():
    for i in range(3):
        match('./pic/wechat1.jpg', './pic/wechat2.png')
if __name__ == '__main__':
    main()

SURF

简介

SURF(Speeded Up Robust Features, 加速稳健特征) 是一种稳健的图像识别和描述算法。它是SIFT的高效变种,也是提取尺度不变特征,算法步骤与SIFT算法大致相同,但采用的方法不一样,要比SIFT算法更高效(正如其名),实时性更高。SURF使用海森(Hesseian)矩阵的行列式值作特征点检测并用积分图加速运算;SURF 的描述子基于 2D 离散小波变换响应并且有效地利用了积分图。

特征提取步骤

1.特征点检测
SURF使用Hessian矩阵来检测特征点,该矩阵是x,y方向的二阶导数矩阵,可测量一个函数的局部曲率,其行列式值代表像素点周围的变化量,特征点需取行列式值的极值点。用方型滤波器取代SIFT中的高斯滤波器,利用积分图(计算位于滤波器方型的四个角落值)大幅提高运算速度
构建Hessian矩阵的目的是为了生成图像稳定的边缘点(突变点),跟Canny、拉普拉斯边缘检测的作用类似,为特征提取做准备。构建Hessian矩阵的过程对应着SIFT算法中的DoG过程。
2.特征点定位
与SIFT类似,通过特征点邻近信息插补来定位特征点。
3. 方向定位
通过计算特征点周围像素点x,y方向的哈尔小波变换,并将x,y方向的变换值在xy平面某一角度区间内相加组成一个向量,在所有的向量当中最长的(即x、y分量最大的)即为此特征点的方向。
在这里插入图片描述
具体来说,采用的是统计特征点圆形邻域内的Harr小波特征,即在特征点的圆形邻域内,统计60度扇形内所有点的水平、垂直Harr小波特征总和,然后扇形以0.2弧度大小的间隔进行旋转并再次统计该区域内Harr小波特征值之后,最后将值最大的那个扇形的方向作为该特征点的主方向,该过程示意图如上。

4. 特征描述子
选定了特征点的方向后,其周围相素点需要以此方向为基准来建立描述子。此时以5x5个像素点为一个子区域,取特征点周围20x20个像素点的范围共16个子区域,计算子区域内的x、y方向(此时以平行特征点方向为x、垂直特征点方向为y)的哈尔小波转换总和Σdx、ΣdyΣdx、Σdy与其向量长度总和Σ|dx|、Σ|dy|Σ|dx|、Σ|dy|共四个量值,共可产生一个64维的描述子。描述子这一块,上个sift算法那里就没看懂…

具体可以参考这篇博客:
https://www.cnblogs.com/zyly/p/9531907.html

Code

特征提取

def surf(filename):
    img = cv2.imread(filename) # 读取文件
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转化为灰度图
    sift = cv2.xfeatures2d_SURF.create()
    keyPoint, descriptor = sift.detectAndCompute(img, None) # 特征提取得到关键点以及对应的描述符(特征向量)
    return img, keyPoint, descriptor

特征匹配
同样选用选用BruteForce Matcher,代码不变。

ORB

简介

ORB(Oriented FAST and Rotated BRIEF)该特征检测算法是在著名的FAST特征检测和BRIEF特征描述子的基础上提出来的,其运行时间远远优于SIFT和SURF,可应用于实时性特征检测。ORB特征检测具有尺度和旋转不变性,对于噪声及其透视变换也具有不变性,良好的性能是的利用ORB在进行特征描述时的应用场景十分广泛。ORB特征检测主要分为以下两个步骤:(1)方向FAST特征点检测(2)BRIEF特征描述。

特征提取算法

1.FAST特征点检测
2.BRIEF特征描述子

Code

特征提取

def orb(filename):
    img = cv2.imread(filename) # 读取文件
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转化为灰度图
    sift = cv2.ORB_create()
    keyPoint, descriptor = sift.detectAndCompute(img, None) # 特征提取得到关键点以及对应的描述符(特征向量)
    return img, keyPoint, descriptor

特征匹配
同样选用选用BruteForce Matcher,代码不变。

效果展示

特征提取

比较一下提取的结果看看

def compare(filename):
    imgs = []
    keyPoint = []
    descriptor = []
    img, keyPoint_temp, descriptor_temp = sift(filename)
    keyPoint.append(keyPoint_temp)
    descriptor.append(descriptor_temp)
    imgs.append(img)
    img, keyPoint_temp, descriptor_temp = surf(filename)
    keyPoint.append(keyPoint_temp)
    descriptor.append(descriptor_temp)
    imgs.append(img)
    img, keyPoint_temp, descriptor_temp = orb(filename)
    keyPoint.append(keyPoint_temp)
    descriptor.append(descriptor_temp)
    imgs.append(img)
    return imgs, keyPoint, descriptor

def main():
    method = ['sift','surf','orb']
    imgs, kp, des = compare('./pic/doraemon1.jpg')
    for i in range(3):
        img = cv2.drawKeypoints(imgs[i], kp[i], None)
        cv2.imshow(method[i], img)
        cv2.waitKey()
        cv2.destroyAllWindows()
    print("sift len of des: %d, size of des: %d" % (len(des[0]), len(des[0][0])))
    print("surf len of des: %d, size of des: %d" % (len(des[1]), len(des[1][0])))
    print("orb len of des: %d, size of des: %d" % (len(des[2]), len(des[2][0])))

下图是提取的结果,从左到右分别是原图、sift、surf、orb
在这里插入图片描述

sift len of des: 458, size of des: 128
surf len of des: 1785, size of des: 64
orb len of des: 500, size of des: 32

可以看出:
sift虽然提取的特征点最少,但是效果最好
sift提取的特征点维度是128维,surf是64维,orb是32维。

特征匹配

接下来看一下上面的代码运行的结果。从上到底依次是原图、sift、surf、orb

在这里插入图片描述

sift size of kp: 59, after filtering: 20
surf size of kp: 197, after filtering: 35
orb size of kp: 390, after filtering: 47

从输出的结果来看,orb的效果最好。感兴趣的话还可以用其他图片看看效果

总结

(不保对) 基于特征的匹配分为特征点提取和匹配两个步骤,本篇主要针对特征点提取三种方法进行比较,分别是SIFT,SURF以及ORB三种方法,这三种方法在OpenCV里面都已实现。SURF基本就是SIFT的全面升级版,有 SURF基本就不用考虑SIFT,而ORB的强点在于计算时间,以下具体比较:
SURF SIFT ORB三种特征检测算法比较
(建议综合这篇文章评论来看。。。)

  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值