一、ORB算法
1.算法简介
ORB 是 Oriented Fast and Rotated Brief 的简称,可以用来对图像中的关键点快速创建特征向量,这些特征向量可以用来识别图像中的对象。
其中,Fast 和 Brief 分别是特征检测算法和向量创建算法。ORB 首先会从图像中查找特殊区域,称为关键点。关键点即图像中突出的小区域,比如角点,比如它们具有像素值急剧的从浅色变为深色的特征。然后 ORB 会为每个关键点计算相应的特征向量。ORB 算法创建的特征向量只包含 1 和 0,称为二元特征向量。1 和 0 的顺序会根据特定关键点和其周围的像素区域而变化。该向量表示关键点周围的强度模式,因此多个特征向量可以用来识别更大的区域,甚至图像中的特定对象。
ORB 的特点是速度非常快,而且在一定程度上不受噪点和图像变换的影响,例如旋转和缩放变换等。
2.2.FAST寻找特征点
ORB 特征检测的第一步是查找图像中的关键点,这一步骤中使用的是 FAST 算法。
FAST 是 Features from Accelerated Segments Test 的简称,可以快速选择关键点,算法步骤如下:
(1)确定选定特征点的阈值参数 h 的数值。
(2)对于图像上的任意一个像素点 p 而言,FAST 比较以点 p 为圆心的圆圈范围中的 16 个像素,如果圈圈上灰度值小于 lp - h ( lp 即 p 点的灰度值)或灰度值大于 lp + h 的像素共计有 8 个以上,则将像素 p 选作关键点。
FAST 如此高效的原因是,仅将 p 与圆圈中的 4 个等距像素相比。这种方法已经证明和比较 16 个周围像素的效果相同。如果至少有一对连续像素的灰度高于 lp + h 或低于 lp - h ,则将 p 选作关键点。这种优化使得在整个图像中搜索关键点的时间缩短了四倍。
由 FAST 确定特征点的算法步骤可以看出, FAST 确定的关键特征点位于灰度有快速变化的区域,此类区域通常确定了某种边缘。下图中绿色的点为 FAST 确定的图像特征点,图中猫的眼睛轮廓、鼻头着色区域即为图像中的边缘性区域。边缘定义了猫的界限,以及脸部区域的界限,因此这些关键点使我们能够识别这只猫,而不是图像中的任何其他对象或背景。
3.BRIEF 算法生成图像特征描述符
BRIEF 是 Binary Robust Independent Elementary Features 的简称,它的作用是根据一组关键点创建二进制特征向量,又称为二进制特征描述符,是仅包含 1 和 0 的特征向量。在 BRIEF 中 每个关键点由一个二进制特征向量描述,该向量一般为 128-512 位的字符串,其中仅包含 1 和 0。
BRIEF 算法生成二进制的特征描述符最大优点在于能非常高效地存储在内存中,并且可以快速计算,且使 BRIEF 能够在计算资源非常有限的设备(例如智能手机)上运行。
BRIEF算法生成特征描述符的具体步骤如下:
(1)首先利用高斯核对给定图像进行平滑处理,以防描述符对高频噪点过于敏感。
(2)然后对于给定关键点(例如下图猫爪上的绿点),以该关键点为中心的高斯分布中抽取一个像素,下文将该点称作关键点的一号点(下图中的蓝点),标准差为 σ。
(3)以一号点为中心的高斯分布中抽取一个像素,下文将该点称作关键点的二号点(下图中的黄点),标准差为 σ/2【这么取是因为经验表明这种选择提高了特征匹配率】。
(4)为关键点构建二进制特征描述符,方法是比较(2)和(3)得到的一号点和二号点的灰度值。如果一号点比二号点亮,则为描述符中的相应位分配值 1,否则分配值 0。
(5)然后针对同一关键点选择新的一号点和二号点,比较它们的灰度并为特征向量中的下个位分配 1 或 0。(即跳转到(2)重复循环)
(6)对于设定的生成不同维度的具体程序,BRIEF算法会重复(2)~(4)对应次数,产生指定长度的特征描述符。并对每一特征点重复以上算法。
4.满足缩放旋转不变性的改进
BRIEF 算法可以生成二进制特征描述符,但并不满足图像的缩放旋转不变性,故不能直接用于要求较高的图像匹配中,为克服这一缺点,将 BRIEF 作下述改进。
(1)缩放不变性
为使特征满足缩放不变性, BRIEF 算法构建图像金字塔。
图像金字塔是单个图像的多尺度表示法,由一系列原始图像的不同分辨率版本组成。金字塔的每个级别都由上个级别的图像下采样版本组成。下采样是指图像分辨率被降低,比如图像按照 1/2 比例下采样。因此一开始的 4x4 正方形区域现在变成 2x2 正方形。图像的下采样包含更少的像素,并且以 1/2 的比例降低大小。
上图是一个包含 5 个级别的图形金字塔示例,在每个级别图像都以 1/2 的比例下采样。
ORB 创建好图像金字塔后,会使用 FAST 算法从每个级别不同大小的图像中快速找到关键点。因为金字塔的每个级别由原始图像的更小版本组成,因此原始图像中的任何对象在金字塔的每个级别也会降低大小。
通过确定每个级别的关键点 ORB 能够有效发现不同尺寸的对象的关键点,这样的话 ORB 实现了部分缩放不变性。
(2)旋转不变性
为满足特征的旋转不变性,ORB 为每个关键点分配一个方向,该方向取决于该关键点周围的灰度是如何变化的。
ORB 首先选择金字塔Level 0 中的图像,计算该图像关键点的方向。
首先计算以该关键点为中心的指定大小方框中的强度形心。强度形心可以看做给定区域中的平均像素灰度的位置。计算强度形心后,通过画一条从关键点到强度形心的向量,获得该关键点的方向。
如上图所示。这个关键点为左下方向,因为这个区域的亮度朝着这个方向增强。
为金字塔级别 0 的图像中的每个关键点分配方向后,ORB 现在为所有其他金字塔级别的图像重复相同流程。需要注意的是,在每个图像金字塔级别,关键点周围用于确认方向的区域大小并没有缩减,因此相同区域在每个金字塔级别覆盖的图像区域将更大,导致关键点的大小各不相同。
(3)rBRIEF
ORB 现在使用修改后的 BRIEF 版本创建特征向量,这个修改后的 BRIEF 版本称为 rBRIEF,即 Rotation-Aware BRIEF。无论对象的方向如何,它都可以为关键点创建相同的向量,使得 ORB 算法具有旋转不变性,意味着它可以在朝着任何角度旋转的图像中检测到相同的关键点。
和 BRIEF 一样 rBRIEF 首先在给定关键点周围的已界定区域中随机选择 256 个像素对,以构建 256 维的位向量。然后根据关键点的方向角度旋转这些随机像素对,使随机点的方向与关键点的一致。最后, rBRIEF 对比随机像素对的亮度并相应地分配 1 和 0 创建对应的特征向量。
二、代码实现
核心函数
cv2.ORB_create(nfeatures = 500, scaleFactor = 1.2, nlevels = 8, edgeThreshold = 31, firstLevel = 0,WTA_K = 2, scoreType = HARRIS_SCORE, patchSize = 31, fastThreshold = 20)
生成关键点的函数
nfeatures
:默认500,确定要查找的最大关键点数scaleFactor
:默认1.2,金字塔抽取率,必须大于1。ORB使用图像金字塔来查找要素,因此必须提供金字塔中每个图层与金字塔所具有的级别数之间的比例因子。scaleFactor = 2表示经典金字塔,其中每个下一级别的像素比前一级低4倍。大比例因子将减少发现的功能数量nlevels
:默认8,金字塔等级的数量edgeThreshold
:默认31,未检测到要素的边框大小。由于关键点具有特定的像素大小,因此必须从搜索中排除图像的边缘。 edgeThreshold的大小应该等于或大于patchSize参数firstLevel
:默认0,此参数确定应将哪个级别视为金字塔中的第一级别。它在当前实现中应为0。通常具有统一标度的金字塔等级被认为是第一级WTA_K
:默认2,用于生成定向的BRIEF描述符的每个元素的随机像素的数量。可能的值为2,3和4,其中2为默认值scoreType
:默认HARRIS_SCORE,此参数可以设置为HARRIS_SCORE或FAST_SCORE。HARRIS_SCORE表示Harris角算法用于对要素进行排名。该分数仅用于保留最佳功能。 FAST_SCORE生成的关键点稍差,但计算起来要快一些patchSize
:生成描述符使用区域的大小。当然,在较小的金字塔层上,由特征覆盖的感知图像区域将更大
使用步骤
(1)创建orb对象;cv2.ORB_create()
(2)关键点检测和特征匹配:kp,des=orb.detectAndCompute(gray,mask);
import os
import cv2
import numpy as np
img=cv2.imread('lena.png')
img=cv2.resize(src=img,dsize=(450,450))
gray=cv2.cvtColor(src=img,code=cv2.COLOR_BGR2GRAY)
#SIFT对象创建
orb=cv2.ORB_create()
#进行检测,其中第二个参数为None,表示对整张图进行检测
kp=orb.detect(gray,None)
#进行特征匹配
# kp,des=surf.compute(gray,kp)
kp,des=orb.detectAndCompute(gray,None)
print(des)
#绘制角点
cv2.drawKeypoints(image=gray,keypoints=kp,outImage=img,color=(0,255,0))
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
print('Pycharm')