opencv--图像特征提取与描述

opencv--图像特征提取与描述

  • 1.图像的特征
  • 2. Harris和Shi-Tomas算法
    • 2.1 Harris角点检测
      • 2.1.1 原理
      • 2.1.2 实现
    • 2.2 Shi-Tomasi角点检测
      • 2.2.1原理
      • 2.2.2 实现
  • 3.SIFT/SURF算算法
    • 3.1SIFT算法
      • 3.1.1 SIFT原理
      • 3.1.2 SIFT算法基本流程
      • 3.1.3 尺度空间极值检测
      • 3.1.4 关键点定位
      • 3.1.5 关键点方向确定
      • 3.1.6 关键点描述
    • 3.2 SURF原理
    • 3.3 实现
  • 4.Fast和ORB算法
    • 4.1 Fast算法
      • 4.1.1 Fast算法原理.
      • 4.1.2 Fast算法实现
    • 4.2 ORB 算法
      • 4.2.1 ORB 算法原理
      • 4.2.2 BRIEF算法
      • 4.2.2 ORB算法实现

1.图像的特征

opencv--图像特征提取与描述_第1张图片
对于一幅图像,隶属于图像内(平面,如蓝框)的特征很难被找到,即在只给出蓝框特征下,很难在原始图像中找出它自身的位置和区域。其次,对于图像边缘(如黑框)特征,可以简单地找出其近似位置,但无法准确定位到其在原始图像中的区域,这是因为与其相似的地方有很多。最后,对于位于图像角点(如红框)的特征,很容易被找到,这是因为在角点的地方,其特点很明显。

因此,图像的角点特征是很重要的特征,对图像和图形的理解和分析有很重要的作用。角点在三维场景重建、运动估计、目标跟踪、目标识别、图形配准与匹配等领域起着非常重要的作用。现实世界中,角点对应于物体的拐角,道路的十字路口、丁字路口等。

注:图像的特征要具有区分性,容易被比较。通常认为角点、斑点是较好的图像特征。

  • 特征检测:找出图像中的特征
  • 特征描述:对特征及其周围的区域进行描述。

2. Harris和Shi-Tomas算法

2.1 Harris角点检测

2.1.1 原理

Harris角点检测的思想是通过局部的小窗口来观察图像,角点特征的特点是窗口沿着任意方向移动都会导致图像灰度的明显变化,如下图所示:
opencv--图像特征提取与描述_第2张图片
将上述思想转换为数学形式,即将局部窗口向各个方向移动 ( u , v ) (u,v) (u,v)并计算出所有灰度差异的总和,表达式如下:
E ( u , v ) = ∑ x , y w ( x , y ) [ I ( x + u , y + v ) − I ( x , y ) ] 2 E(u,v) = \sum_{x, y} w(x,y) [I(x+u,y+v) - I(x,y)]^2 E(u,v)=x,yw(x,y)[I(x+u,y+v)I(x,y)]2
其中 I ( x , y ) I(x,y) I(x,y)是局部窗口的图像灰度, I ( x + u , y + v ) I(x+u,y+v) I(x+u,y+v)是平移后的图像灰度, w ( x , y ) w(x,y) w(x,y)是窗口函数,该窗口可以是矩形窗口,也可以是对每一个像素赋予不同权重的高斯窗口,如下:
opencv--图像特征提取与描述_第3张图片
角点检测中使 E ( u , v ) E(u,v) E(u,v)的值最大。利用一阶泰勒展开有:
I ( x + u , y + v ) = I ( x , y ) + I x u + I y v I(x+u, y+v) = I(x, y) + I_xu+I_yv I(x+u,y+v)=I(x,y)+Ixu+Iyv
其中, I x 和 I y I_x和I_y IxIy使沿 x x x y y y方向的导数,可用sobel算子计算。
推导公式如下:
opencv--图像特征提取与描述_第4张图片
M M M矩阵决定了 E ( u , v ) E(u,v) E(u,v)的取值。下面利用 M M M求角点: M M M I x I_x Ix I y I_y Iy的二次项函数,可以表示成椭圆的形状,椭圆的长短半轴有 M M M的特征值 λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2决定,方向由特征矢量决定,即:
opencv--图像特征提取与描述_第5张图片
椭圆函数特征值与图像中的角点、直线(边缘)和平面之间的关系如下图所示:opencv--图像特征提取与描述_第6张图片
共可分为三种情况:

  • 图像中的直线。一个特征值大,另一个特征值小,λ1>>λ2或 λ2>>λ1。椭圆函数值在某一方向上大,在其他方向上小。
  • 图像中的平面。两个特征值都小,且近似相等;椭圆函数数值在各个方向上都小。
  • 图像中的角点。两个特征值都大,且近似相等,椭圆函数在所有方向都增大
    Harris给出的角点计算方法并不需要计算具体的特征值,而是计算一个角点响应值 R R R来判断角点。 R R R的计算公式为:
    R = d e t M − α ( t r a c e M ) 2 R = detM - \alpha(traceM)^2 R=detMα(traceM)2
    其中, d e t M detM detM为矩阵M的行列式; t r a c e M traceM traceM为矩阵M的迹, α \alpha α为常数,取值范围为0.04–0.06.事实上,特征是隐含在 d e t M detM detM t r a c e M traceM traceM,这是因为:
    opencv--图像特征提取与描述_第7张图片
    那怎么判断角点呢?如下图所示:
    opencv--图像特征提取与描述_第8张图片
  • 当R为大数值的正数时是角点
  • 当R为大数值的负数时是边界
  • 当R为小数是认为是平坦区域

2.1.2 实现

在OpenCV中实现Hariis检测使用的API是:dst=cv2.cornerHarris(src, blockSize, ksize, k)
参数:

  • img:数据类型为 float32 的输入图像。
  • blockSize:角点检测中要考虑的邻域大小。
  • ksize:sobel求导使用的核大小
  • k :角点检测方程中的自由参数,取值参数为 [0.04,0.06].
import cv2 as cv
import numpy as np 
import matplotlib.pyplot as plt
# 1 读取图像,并转换成灰度图像
img = cv.imread('./image/chessboard.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2 角点检测
# 2.1 输入图像必须是 float32
gray = np.float32(gray)

# 2.2 最后一个参数在 0.04 到 0.05 之间
dst = cv.cornerHarris(gray,2,3,0.04)
# 3 设置阈值,将角点绘制出来,阈值根据图像进行选择
img[dst>0.001*dst.max()] = [0,0,255]
# 4 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('Harris角点检测')
plt.xticks([]), plt.yticks([])
plt.show()

opencv--图像特征提取与描述_第9张图片
Harris角点检测的优缺点:
优点:

  • 旋转不变性,椭圆转过一定角度但是其形状保持不变(特征值保持不变)
  • 对于图像灰度的仿射变化具有部分的不变性,由于仅仅使用了图像的一介导数,对于图像灰度平移变化不变;对于图像灰度尺度变化不变
    缺点:
  • 对尺度很敏感,不具备几何尺度不变性。
  • 提取的角点是像素级的

2.2 Shi-Tomasi角点检测

2.2.1原理

Shi-Tomasi算法是对Harris角点检测算法的改进,Harris 算法的角点响应函数是将矩阵 M 的行列式值与 M 的迹相减,利用差值判断是否为角点。Shi-Tomasi算法提出改进的方法是,若矩阵M的两个特征值中较小的一个大于阈值,则认为他是角点,即:
R = m i n ( λ 1 , λ 2 ) R = min(\lambda_1, \lambda_2) R=min(λ1,λ2)
opencv--图像特征提取与描述_第10张图片
从这幅图中,可以看出来只有当 λ1 和 λ 2 都大于最小值时,才被认为是角点。

2.2.2 实现

在OpenCV中实现Shi-Tomasi角点检测使用API:corners = cv2.goodFeaturesToTrack ( image, maxcorners, qualityLevel, minDistance )
参数:

  • Image: 输入灰度图像
  • maxCorners : 获取角点数的数目。
  • qualityLevel:该参数指出最低可接受的角点质量水平,在0-1之间。
  • minDistance:角点之间最小的欧式距离,避免得到相邻特征点。
  • Corners: 搜索到的角点,在这里所有低于质量水平的角点被排除掉,然后把合格的角点按质量排序,然后将质量较好的角点附近(小于最小欧式距离)的角点删掉,最后找到maxCorners个角点返回。
import numpy as np 
import cv2 as cv
import matplotlib.pyplot as plt
# 1 读取图像
img = cv.imread('./image/tv.jpg') 
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2 角点检测
corners = cv.goodFeaturesToTrack(gray,1000,0.01,10)  
# 3 绘制角点
for i in corners:
    x,y = i.ravel()
    cv.circle(img,(x,y),2,(0,0,255),-1)
# 4 图像展示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('shi-tomasi角点检测')
plt.xticks([]), plt.yticks([])
plt.show()

3.SIFT/SURF算算法

3.1SIFT算法

3.1.1 SIFT原理

Harris和Shi-Tomasi角点检测算法具有旋转不变性,但不具有尺度不变性,以下图为例,在左侧小图中可以检测到角点,但是图像被放大后,在使用同样的窗口,就检测不到角点了。
opencv--图像特征提取与描述_第11张图片
所以,下面介绍一种计算机视觉的算法,尺度不变特征转换即SIFT (Scale-invariant feature transform)。它用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量。应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对等领域。

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

3.1.2 SIFT算法基本流程

SIFT算法分解为如下四步:

  1. 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯差分函数来识别潜在的对于尺度和旋转不变的关键点。
  2. 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
  3. 关键点方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而保证了对于这些变换的不变性。
  4. 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度作为关键点的描述符,它允许比较大的局部形状的变形或光照变化。

3.1.3 尺度空间极值检测

在不同的尺度空间是不能使用相同的窗口检测极值点,对小的关键点使用小的窗口,对大的关键点使用大的窗口,为了达到上述目的,我们使用尺度空间滤波器。
一个图像的尺度空间L(x,y,σ),定义为原始图像I(x,y)与一个可变尺度的2维高斯函数G(x,y,σ)卷积运算 ,即:
L ( x , y , σ ) = G ( x , y , σ ) ∗ I ( x , y ) L(x, y, \sigma) = G(x, y, \sigma) * I(x, y) L(x,y,σ)=G(x,y,σ)I(x,y)
其中:
G ( x , y , σ ) = 1 2 π σ 2 e − x 2 + y 2 x σ 2 G(x, y, \sigma) = \frac{1}{2\pi \sigma^2}e^{-\frac{x^2 + y^2}{x\sigma^2}} G(x,y,σ)=2πσ21exσ2x2+y2
σ是尺度空间因子,它决定了图像的模糊的程度。在大尺度下(σ值大)表现的是图像的概貌信息,在小尺度下(σ值小)表现的是图像的细节信息。

在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。所以,在实际应用中,只计算(6σ+1)*(6σ+1)的高斯卷积核就可以保证相关像素影响。

下面我们构建图像的高斯金字塔,它采用高斯函数对图像进行模糊以及降采样处理得到的,高斯金字塔构建过程中,首先将图像扩大一倍,在扩大的图像的基础之上构建高斯金字塔,然后对该尺寸下图像进行高斯模糊,几幅模糊之后的图像集合构成了一个Octave,然后对该Octave下选择一幅图像进行下采样,长和宽分别缩短一倍,图像面积变为原来四分之一。这幅图像就是下一个Octave的初始图像,在初始图像的基础上完成属于这个Octave的高斯模糊处理,以此类推完成整个算法所需要的所有八度构建,这样这个高斯金字塔就构建出来了,整个流程如下图所示:
opencv--图像特征提取与描述_第12张图片
利用LoG(高斯拉普拉斯方法),即图像的二阶导数,可以在不同的尺度下检测图像的关键点信息,从而确定图像的特征点。但LoG的计算量大,效率低。所以我们通过两个相邻高斯尺度空间的图像的相减,得到DoG(高斯差分)来近似LoG。

为了计算DoG我们构建高斯差分金字塔,该金字塔是在上述的高斯金字塔的基础上构建而成的,建立过程是:在高斯金字塔中每个Octave中相邻两层相减就构成了高斯差分金字塔。如下图所示:
opencv--图像特征提取与描述_第13张图片
高斯差分金字塔的第1组第1层是由高斯金字塔的第1组第2层减第1组第1层得到的。以此类推,逐组逐层生成每一个差分图像,所有差分图像构成差分金字塔。概括为DOG金字塔的第o组第l层图像是有高斯金字塔的第o组第l+1层减第o组第l层得到的。后续Sift特征点的提取都是在DOG金字塔上进行的.

在 DoG 搞定之后,就可以在不同的尺度空间中搜索局部最大值了。对于图像中的一个像素点而言,它需要与自己周围的 8 邻域,以及尺度空间中上下两层中的相邻的 18(2x9)个点相比。如果是局部最大值,它就可能是一个关键点。基本上来说关键点是图像在相应尺度空间中的最好代表。如下图所示:

opencv--图像特征提取与描述_第14张图片
搜索过程从每组的第二层开始,以第二层为当前层,对第二层的DoG图像中的每个点取一个3×3的立方体,立方体上下层为第一层与第三层。这样,搜索得到的极值点既有位置坐标(DoG的图像坐标),又有空间尺度坐标(层坐标)。当第二层搜索完成后,再以第三层作为当前层,其过程与第二层的搜索类似。当S=3时,每组里面要搜索3层,所以在DOG中就有S+2层,在初使构建的金字塔中每组有S+3层。

3.1.4 关键点定位

由于DoG对噪声和边缘比较敏感,因此在上面高斯差分金字塔中检测到的局部极值点需经过进一步的检验才能精确定位为特征点。
使用尺度空间的泰勒级数展开来获得极值的准确位置, 如果极值点的 灰度值小于阈值(一般为0.03或0.04)就会被忽略掉。 在 OpenCV 中这种阈值被称为 contrastThreshold。
DoG 算法对边界非常敏感, 所以我们必须要把边界去除。 Harris 算法除了可以用于角点检测之外还可以用于检测边界。从 Harris 角点检测的算法中,当一个特征值远远大于另外一个特征值时检测到的是边界。那在DoG算法中欠佳的关键点在平行边缘的方向有较大的主曲率,而在垂直于边缘的方向有较小的曲率,两者的比值如果高于某个阈值(在OpenCV中叫做边界阈值),就认为该关键点为边界,将被忽略,一般将该阈值设置为10。

将低对比度和边界的关键点去除,得到的就是我们感兴趣的关键点。

3.1.5 关键点方向确定

经过上述两个步骤,图像的关键点就完全找到了,这些关键点具有尺度不变性。为了实现旋转不变性,还需要为每个关键点分配一个方向角度,也就是根据检测到的关键点所在高斯尺度图像的邻域结构中求得一个方向基准。

对于任一关键点,我们采集其所在高斯金字塔图像以r为半径的区域内所有像素的梯度特征(幅值和幅角),半径r为:
r = 3 × 1.5 σ r = 3 \times 1.5 \sigma r=3×1.5σ
其中σ是关键点所在octave的图像的尺度,可以得到对应的尺度图像。
梯度的幅值和方向的计算公式为:
m ( x , y ) = L ( x + 1 , y ) − L ( x − 1 , y ) 2 + ( L ( x , y + 1 ) − L ( x , y − 1 ) ) 2 θ ( x , y ) = a r c t a n ( L ( x , y + 1 ) − L ( x , y − 1 ) L ( x + 1 , y ) − L ( x − 1 , y ) ) m(x, y) = \sqrt{L(x+1, y) - L(x-1, y)^2 +(L(x, y+1) - L(x, y-1))^2 } \\ \theta(x, y) = arctan(\frac{L(x, y+1) - L(x, y-1)}{L(x+1, y) - L(x-1, y)}) m(x,y)=L(x+1,y)L(x1,y)2+(L(x,y+1)L(x,y1))2 θ(x,y)=arctan(L(x+1,y)L(x1,y)L(x,y+1)L(x,y1))
邻域像素梯度的计算结果如下图所示:
opencv--图像特征提取与描述_第15张图片
完成关键点梯度计算后,使用直方图统计关键点邻域内像素的梯度幅值和方向。具体做法是,将360°分为36柱,每10°为一柱,然后在以r为半径的区域内,将梯度方向在某一个柱内的像素找出来,然后将他们的幅值相加在一起作为柱的高度。因为在r为半径的区域内像素的梯度幅值对中心像素的贡献是不同的,因此还需要对幅值进行加权处理,采用高斯加权,方差为1.5σ。如下图所示,为简化图中只画了8个方向的直方图。
opencv--图像特征提取与描述_第16张图片
每个特征点必须分配一个主方向,还需要一个或多个辅方向,增加辅方向的目的是为了增强图像匹配的鲁棒性。辅方向的定义是,当一个柱体的高度大于主方向柱体高度的80%时,则该柱体所代表的的方向就是给特征点的辅方向。
直方图的峰值,即最高的柱代表的方向是特征点邻域范围内图像梯度的主方向,但该柱体代表的角度是一个范围,所以我们还要对离散的直方图进行插值拟合,以得到更精确的方向角度值。利用抛物线对离散的直方图进行拟合,如下图所示:
opencv--图像特征提取与描述_第17张图片
获得图像关键点主方向后,每个关键点有三个信息(x,y,σ,θ):位置、尺度、方向。由此我们可以确定一个SIFT特征区域。通常使用一个带箭头的圆或直接使用箭头表示SIFT区域的三个值:中心表示特征点位置,半径表示关键点尺度,箭头表示方向。如下图所示:
opencv--图像特征提取与描述_第18张图片

3.1.6 关键点描述

通过以上步骤,每个关键点就被分配了位置,尺度和方向信息。接下来我们为每个关键点建立一个描述符,该描述符既具有可区分性,又具有对某些变量的不变性,如光照,视角等。而且描述符不仅仅包含关键点,也包括关键点周围对其有贡献的的像素点。主要思路就是通过将关键点周围图像区域分块,计算块内的梯度直方图,生成具有特征向量,对图像信息进行抽象。

描述符与特征点所在的尺度有关,所以我们在关键点所在的高斯尺度图像上生成对应的描述符。以特征点为中心,将其附近邻域划分为d*dd∗d个子区域(一般取d=4),每个子区域都是一个正方形,边长为3σ,考虑到实际计算时,需进行三次线性插值,所以特征点邻域的为3\sigma(d+1)*3\sigma(d+1)3σ(d+1)∗3σ(d+1)的范围,如下图所示:

opencv--图像特征提取与描述_第19张图片
为了保证特征点的旋转不变性,以特征点为中心,将坐标轴旋转为关键点的主方向,如下图所示:

opencv--图像特征提取与描述_第20张图片
计算子区域内的像素的梯度,并按照σ=0.5d进行高斯加权,然后插值计算得到每个种子点的八个方向的梯度,插值方法如下图所示:
opencv--图像特征提取与描述_第21张图片
每个种子点的梯度都是由覆盖其的4个子区域插值而得的。如图中的红色点,落在第0行和第1行之间,对这两行都有贡献。对第0行第3列种子点的贡献因子为dr,对第1行第3列的贡献因子为1-dr,同理,对邻近两列的贡献因子为dc和1-dc,对邻近两个方向的贡献因子为do和1-do。则最终累加在每个方向上的梯度大小为:
w e i g h t = w ∗ d r k ( 1 − d r ) 1 − k d c m ( 1 − d c ) 1 − m d o n ( 1 − d o ) 1 − n weight = w * dr^k (1-dr)^{1-k} dc^ m (1-dc)^{1-m} do^n (1-do)^{1-n} weight=wdrk(1dr)1kdcm(1dc)1mdon(1do)1n
其中k,m,n为0或为1。 如上统计 4 ∗ 4 ∗ 8 = 128 4*4*8=128 448=128个梯度信息即为该关键点的特征向量,按照特征点的对每个关键点的特征向量进行排序,就得到了SIFT特征描述向量。
SIFT在图像的不变特征提取方面拥有无与伦比的优势,但并不完美,仍然存在实时性不高,有时特征点较少,对边缘光滑的目标无法准确提取特征点等缺陷,自SIFT算法问世以来,人们就一直对其进行优化和改进,其中最著名的就是SURF算法。

3.2 SURF原理

使用 SIFT 算法进行关键点检测和描述的执行速度比较慢, 需要速度更快的算法。SURF 算法是SIFT算法的增强版,它的计算量小,运算速度快,提取的特征与SIFT几乎相同,将其与SIFT算法对比如下:
opencv--图像特征提取与描述_第22张图片

3.3 实现

在OpenCV中利用SIFT检测关键点的流程如下所示:

  1. 实例化sift
import cv2
sift = cv2.xfeatures2d.SIFT_create()
  1. 利用sift.detectAndCompute()检测关键点并计算
kp,des = sift.detectAndCompute(gray,None)

参数:

  • gray: 进行关键点检测的图像,注意是灰度图像
  • kp: 关键点信息,包括位置,尺度,方向信息
  • des: 关键点描述符,每个关键点对应128个梯度信息的特征向量
  1. 将关键点检测结果绘制在图像上
cv.drawKeypoints(image, keypoints, outputimage, color, flags)

参数:

  • image: 原始图像
  • keypoints:关键点信息,将其绘制在图像上
  • outputimage:输出图片,可以是原始图像
  • color:颜色设置,通过修改(b,g,r)的值,更改画笔的颜色,b=蓝色,g=绿色,r=红色。
  • 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:单点的特征点不被绘制
import cv2 as cv 
import numpy as np
import matplotlib.pyplot as plt
# 1 读取图像
img = cv.imread('./image/tv.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2 sift关键点检测
# 2.1 实例化sift对象
sift = cv.xfeatures2d.SIFT_create()

# 2.2 关键点检测:kp关键点信息包括方向,尺度,位置信息,des是关键点的描述符
kp,des=sift.detectAndCompute(gray,None)
# 2.3 在图像上绘制关键点的检测结果
cv.drawKeypoints(img,kp,img,flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# 3 图像显示
plt.figure(figsize=(8,6),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('sift检测')
plt.xticks([]), plt.yticks([])
plt.show()

4.Fast和ORB算法

4.1 Fast算法

4.1.1 Fast算法原理.

FAST (全称Features from accelerated segment test)是一种用于角点检测的算法,该算法的原理是取图像中检测点,以该点为圆心的周围邻域内像素点判断检测点是否为角点,通俗的讲就是若一个像素周围有一定数量的像素与该点像素值不同,则认为其为角点

FAST算法的基本流程

  1. 在图像中选取一个像素点 p,来判断它是不是关键点。 I p I_p Ip等于像素点 p的灰度值。
  2. 以r为半径画圆,覆盖p点周围的M个像素,通常情狂下,设置 r=3,则 M=16,如下图所示:
    opencv--图像特征提取与描述_第23张图片
  3. 设置一个阈值t,如果在这 16 个像素点中存在 n 个连续像素点的灰度值都高于 I p + t I_p + t Ip+t,或者低于 I p − t I_p - t Ipt,那么像素点 p 就被认为是一个角点。如上图中的虚线所示,n 一般取值为 12。
  4. 由于在检测特征点时是需要对图像中所有的像素点进行检测,然而图像中的绝大多数点都不是特征点,如果对每个像素点都进行上述的检测过程,那显然会浪费许多时间,因此采用一种进行非特征点判别的方法:首先对候选点的周围每个 90 度的点:1,9,5,13 进行测试(先测试 1 和 19, 如果它们符合阈值要求再测试 5 和 13)。如果 p 是角点,那么这四个点中至少有 3 个要符合阈值要求,否则直接剔除。对保留下来的点再继续进行测试(是否有 12 的点符合阈值要求)。

虽然这个检测器的效率很高,但它有以下几条缺点:

  • 获得的候选点比较多
  • 特征点的选取不是最优的,因为它的效果取决与要解决的问题和角点的分布情况。
  • 进行非特征点判别时大量的点被丢弃
  • 检测到的很多特征点都是相邻的

前 3 个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑制的方法解决。

机器学习的角点检测器

  1. 选择一组训练图片(最好是跟最后应用相关的图片)
  2. 使用 FAST 算法找出每幅图像的特征点,对图像中的每一个特征点,将其周围的 16 个像素存储构成一个向量P。
    opencv--图像特征提取与描述_第24张图片
  3. 每一个特征点的 16 像素点都属于下列三类中的一种
    S p → x = { d , I p → x ≤ I p − t s , I p − t < I p → x < I p + t b , I p + t ≤ I p → x S_{p \to x} = \begin{cases} d, \quad I_{p \to x} \leq I_p - t \\ s,\quad I_p - t < I_{p \to x} < I_p + t \\ b, \quad I_p + t \leq I_{p \to x} \end{cases} Spx=d,IpxIpts,Ipt<Ipx<Ip+tb,Ip+tIpx
  4. 根据这些像素点的分类,特征向量 P 也被分为 3 个子集:Pd ,Ps ,Pb,
  5. 定义一个新的布尔变量 K p K_p Kp,如果 p 是角点就设置为 Ture,如果不是就设置为 False。
  6. 利用特征值向量p,目标值是 K p K_p Kp,训练ID3 树(决策树分类器)。
  7. 将构建好的决策树运用于其他图像的快速的检测。

非极大值抑制
在筛选出来的候选角点中有很多是紧挨在一起的,需要通过非极大值抑制来消除这种影响。

为所有的候选角点都确定一个打分函数 V V V V V V的值可这样计算:先分别计算 I p I_p Ip与圆上16个点的像素值差值,取绝对值,再将这16个绝对值相加,就得到了 V V V的值.
V = ∑ i 16 ∣ I p − I i ∣ V = \sum_{i}^{16}|I_p-I_i| V=i16IpIi
最后比较毗邻候选角点的 V 值,把V值较小的候选角点pass掉。

FAST算法的思想与我们对角点的直观认识非常接近,化繁为简。FAST算法比其它角点的检测算法快,但是在噪声较高时不够稳定,这需要设置合适的阈值。

4.1.2 Fast算法实现

OpenCV中的FAST检测算法是用传统方法实现的,
首先,实例化fast:

fast = =cv.FastFeatureDetector_create( threshold, nonmaxSuppression)

参数:

  • threshold:阈值t,有默认值10
  • nonmaxSuppression:是否进行非极大值抑制,默认值True
  • Fast:创建的FastFeatureDetector对象

其次,利用fast.detect检测关键点,没有对应的关键点描述

kp = fast.detect(grayImg, None)

参数:

  • gray: 进行关键点检测的图像,注意是灰度图像
  • kp: 关键点信息,包括位置,尺度,方向信息

然后:将关键点检测结果绘制在图像上,与在sift中是一样的cv.drawKeypoints(image, keypoints, outputimage, color, flags)

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 1 读取图像
img = cv.imread('./image/tv.jpg')
# 2 Fast角点检测
# 2.1 创建一个Fast对象,传入阈值,注意:可以处理彩色空间图像
fast = cv.FastFeatureDetector_create(threshold=30)

# 2.2 检测图像上的关键点
kp = fast.detect(img,None)
# 2.3 在图像上绘制关键点
img2 = cv.drawKeypoints(img, kp, None, color=(0,0,255))

# 2.4 输出默认参数
print( "Threshold: {}".format(fast.getThreshold()) )
print( "nonmaxSuppression:{}".format(fast.getNonmaxSuppression()) )
print( "neighborhood: {}".format(fast.getType()) )
print( "Total Keypoints with nonmaxSuppression: {}".format(len(kp)) )


# 2.5 关闭非极大值抑制
fast.setNonmaxSuppression(0)
kp = fast.detect(img,None)

print( "Total Keypoints without nonmaxSuppression: {}".format(len(kp)) )
# 2.6 绘制为进行非极大值抑制的结果
img3 = cv.drawKeypoints(img, kp, None, color=(0,0,255))

# 3 绘制图像
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img2[:,:,::-1])
axes[0].set_title("加入非极大值抑制")
axes[1].imshow(img3[:,:,::-1])
axes[1].set_title("未加入非极大值抑制")
plt.show()

4.2 ORB 算法

4.2.1 ORB 算法原理

ORB(Oriented Fast and Rotated Brief)是开源的,它可以用来对图像中的关键点快速创建特征向量,并用这些特征向量来识别图像中的对象。

ORB算法流程
ORB算法结合了Fast和Brief算法,提出了构造金字塔,为Fast特征点添加了方向,从而使得关键点具有了尺度不变性和旋转不变性。具体流程描述如下:

  1. 构造尺度金字塔,金字塔共有n层,与SIFT不同的是,每一层仅有一幅图像。第s层的尺度为:
    σ s = σ 0 s \sigma_s=\sigma_0^s σs=σ0s
    σ 0 \sigma_0 σ0是初始尺度,默认为1.2,原图在第0层。
    opencv--图像特征提取与描述_第25张图片
    第s层图像的大小:
    S I Z E = ( H ∗ 1 σ s ) × ( W ∗ 1 σ s ) SIZE = (H*\frac{1}{\sigma_s})\times(W*\frac{1}{\sigma_s}) SIZE=(Hσs1)×(Wσs1)

  2. 在不同的尺度上利用Fast算法检测特征点,采用Harris角点响应函数,根据角点的响应值排序,选取前N个特征点,作为本尺度的特征点。

  3. 计算特征点的主方向,计算以特征点为圆心半径为r的圆形邻域内的灰度质心位置,将从特征点位置到质心位置的方向做特征点的主方向。

计算方法如下:
m p q = ∑ x , y x p y q I ( x , y ) m_{pq}=\sum_{x,y}x^py^qI(x,y) mpq=x,yxpyqI(x,y)
质心位置:
C = ( m 10 m 00 , m 01 m 10 ) C=(\frac{m_{10}}{m_{00}},\frac{m_{01}}{m_{10}}) C=(m00m10,m10m01)
主方向:
θ = a r c t a n ( m 01 , m 10 ) \theta = arctan(m_{01},m_{10}) θ=arctan(m01,m10)

  1. 为了解决旋转不变性,将特征点的邻域旋转到主方向上利用Brief算法构建特征描述符,至此就得到了ORB的特征描述向量。

4.2.2 BRIEF算法

BRIEF是一种特征描述子提取算法,并非特征点的提取算法,一种生成二值化描述子的算法,不提取代价低,匹配只需要使用简单的汉明距离(Hamming Distance)利用比特之间的异或操作就可以完成。因此,时间代价低,空间代价低,效果还挺好是最大的优点。

算法的步骤介绍如下

  1. 图像滤波:原始图像中存在噪声时,会对结果产生影响,所以需要对图像进行滤波,去除部分噪声。
  2. 选取点对:以特征点为中心,取S*S的邻域窗口,在窗口内随机选取N组点对,一般N=128,256,512,默认是256,关于如何选取随机点对,提供了五种形式,结果如下图所示:
    opencv--图像特征提取与描述_第26张图片
    • x,y方向平均分布采样
    • x,y均服从Gauss(0,S^2/25)各向同性采样
    • x服从Gauss ( 0 , S 2 / 25 ) , y 服 从 G a u s s ( 0 , S 2 / 100 ) (0,S^2/25),y服从Gauss(0,S^2/100) (0,S2/25)yGauss(0,S2/100)采样
    • x,y从网格中随机获取
    • x一直在(0,0),y从网格中随机选取

图中一条线段的两个端点就是一组点对,其中第二种方法的结果比较好。

  1. 构建描述符:假设x,y是某个点对的两个端点,p(x),p(y)是两点对应的像素值,则有:
    t ( x , y ) = { 1 i f p ( x ) > p ( y ) 0 e l s e t(x,y)=\begin{cases}1 &if p(x)>p(y)\\ 0& else\end{cases} t(x,y)={10ifp(x)>p(y)else
    对每一个点对都进行上述的二进制赋值,形成BRIEF的关键点的描述特征向量,该向量一般为 128-512 位的字符串,其中仅包含 1 和 0,如下图所示:opencv--图像特征提取与描述_第27张图片

4.2.2 ORB算法实现

在OPenCV中实现ORB算法,使用的是:

  1. 实例化ORB.
orb = cv.xfeatures2d.orb_create(nfeatures)

参数:

  • nfeatures: 特征点的最大数量
  1. 利用orb.detectAndCompute()检测关键点并计算
kp,des = orb.detectAndCompute(gray,None)

参数:

  • gray: 进行关键点检测的图像,注意是灰度图像
  • kp: 关键点信息,包括位置,尺度,方向信息
  • des: 关键点描述符,每个关键点BRIEF特征向量,二进制字符串,
  1. 将关键点检测结果绘制在图像上
cv.drawKeypoints(image, keypoints, outputimage, color, flags)
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 1 图像读取
img = cv.imread('./image/tv.jpg')

# 2 ORB角点检测
# 2.1 实例化ORB对象
orb = cv.ORB_create(nfeatures=500)
# 2.2 检测关键点,并计算特征描述符
kp,des = orb.detectAndCompute(img,None)

print(des.shape)

# 3 将关键点绘制在图像上
img2 = cv.drawKeypoints(img, kp, None, color=(0,0,255), flags=0)

# 4. 绘制图像
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img2[:,:,::-1])
plt.xticks([]), plt.yticks([])
plt.show()

你可能感兴趣的:(opencv,计算机视觉)