OpenCV计算机视觉学习(13)——图像特征点检测(Harris角点检测,sift算法)

如果需要处理的原图及代码,请移步小编的GitHub地址

  传送门:请点击我

  如果点击有误://github.com/LeBron-Jian/ComputerVisionPractice

前言

  特征点检测广泛应用到目标匹配,目标跟踪,三维重建等应用中,在进行目标建模时会对图像进行目标特征的提取,常用的有颜色,角点,特征点,轮廓,纹理等特征。而下面学习常用的特征点检测。

  总结一下提取特征点的作用:

  • 1,运动目标跟踪
  • 2,物体识别
  • 3,图像配准
  • 4,全景图像拼接
  • 5,三维重建

  而一种重要的点特征就是角点,本文就学习基于角点检测的算法:Harris角点检测SIFT特征

1,Sift之前的江湖

  (这段的地址://blog.csdn.net/dcrmg/article/details/52577555)

  在Sift横空出世之前,特征点检测与匹配江湖上占据霸主地位的是角点检测家族。先探究一下角点家族不为人知的恩怨情仇。

  角点家族的族长是Moravec在1977年提出的 Moravec角点检测算子,它是一种基于灰度方差的角点检测方法,该算子计算图像中某个像素点沿水平,垂直方向上的灰度差异,以确定角点位置,Moravec是第一个角点检测算法,也是角点家族的开山鼻祖。

  角点的九袋长老是Chris Harris & Mike Stephens在 1988年提出的 harris角点检测算子。Harris不止是考察水平,垂直4个方向上的灰度差异,而是考察了所有方向上的灰度差异,并且具有旋转不变性和对部分仿射变换的稳定性。Harris是整个角点检测家族的颜值担当。

  角点家族的大护法是 J.shi 和 C.Tomasi在1994年提出的 Shi-Tomasi 角点检测算子,它是对 Harris 角点检测算子的改进,并且有一个直接“叫嚣” Harris算子的名字——“Good Feaures to Track”,在opencv中实现函数是 goodfeaturesToTrack。它通过考察自相关矩阵M的两个特征值中的较小者来确定角点,大部分情况下,有比Harris更好的检测效果。

  角点家族的大堂主是E.Rosten 和 T.Drummond 在 2006年提出的 FAST(Feature from Accelerated Segment Test)算子。它通过考察像素点与周围领域内 16个像素点的差异来确定特征点(角点),并且通过分割测试算法对检测效率做了极大的提升。它信奉“天下武功,唯快不破”的真理,在快的道路上锐意进取,基本可以满足实时检测系统的要求,在现今计算机视觉领域赚足了眼球。

  角点家族这种群雄逐鹿的局面一直持续了很多年,大家你追我赶,在群主 Moravec 的带领下家族基业日渐殷实。直到20年后 1999年的某天,有一个叫Sift的后生出现了。

  1999年,大不列颠哥伦比亚大学的David G.Lowe 教授在现有基于不变量技术的特征检测方法基础上,提出了一种基于尺度空间的,对图形缩放,旋转,甚至仿射变换保持不变性的图像局部特征描述算子——sift(尺度不变特征变换),全称是Scale invariant Feature Transform,并且在2004年,又对Sift算法做了更为系统的完善。

2,角点的定义

  角点可以认为是图像亮度变化剧烈的点或图像边缘曲线上曲率极大值的点。

  角点就是极值点,在某个方面属性特别突出的点。

  角点可以是两条线的交叉处,也可以是位于相邻的两个主要方向不同的事物上的点。

   从图中可以看出:

  角点:最直观的印象就是在水平,垂直两个方向上变换均较大的点,例如上面E和F是角点,可以迅速定位到。

  边缘:仅在水平或者垂直方向有较大的变化,例如上面C和D是边界,可以大致找到位置。

  平坦地区:在水平,垂直方向的变换量均较小,例如上面A和B是平坦地区,在图像中有很多存在

  下面看一下角点的类型:

3,角点检测

  角点检测是获取图像特征的一种方法,广泛用于运行检测,图像匹配,视觉跟踪,三维建模和目标识别等领域。也称为特征点检测。

  角点检测算法可以归为三类:

  • 基于灰度图像的角点检测
  • 基于二值图像的角点检测
  • 基于轮廓曲线的角点检测

  其中,基于灰度图像的角点检测又分为基于梯度,基于模板和基于模板梯度组合三类方法。其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。

  基于图像灰度的方法通过计算点的曲率及梯度来检测角点,常用的基于模板的角点检测算法有:

  • Kitchen-Rosenfeld 角点检测
  • Moravec算子
  • Forstner算子
  • Harris角点检测
  • KLT角点检测
  • SUSAN角点检测

4,特征不变性

  在特征点检测中经常提出尺度不变,旋转不变,抗噪声影响等,这些是判断特征点是否稳定的指标。那么什么是特征呢?什么又是局部不变特征呢?下面一一学习。

什么是特征?

  每个物体,我们总可以用一些词语或部件来描述它,比如人脸的特征:两个眼睛,一个鼻子和一个嘴巴。对于图像而言,我们需要计算机去理解图像,描述图像就需要计算机去取得图像的特征,对图像比较全面的描述即是一个二维矩阵,矩阵内的每个值代表图像的亮度。有时候我们需要让计算机更简化的描述一个图像,抓住一些显著特征,这些特征要具有一些良好的性质,比如局部不变性。局部不变性一般包括两个方面:尺度不变性与旋转不变性。

尺度不变性:人类在识别一个物体时,不管这个物体或远或近,都能对它进行正确的辨识,这就是所谓的尺度不变性。尺度空间理论经常与生物视觉关联,有人也称图像局部不变性特征为基于生物视觉的不变性方法。

旋转不变性:当这个物体发生旋转时,我们照样可以正确地辨认它,这就算所谓的旋转不变性。

局部不变特征

  全局特征:从整个图像中抽取的特征。较多的运用在图像检索领域,如图像颜色直方图。

  局部特征:从图像的局部区域中抽取的特征(这个局部区域往往是图像中的一个像素及它周围的邻域)

  一个好的局部特征应该具有以下的特征:

  1. 可重复性:同一个物体在不同时间,不同角度拍到图像中,检测到的特征对应的越多越好。
  2. 独特性:特征在该物体上表现为独特性,能与场景下其他物体区分。
  3. 局部性:特征往往是物体某个局部的特点,这样才可以避免遮挡时不能匹配的问题。
  4. 数量性:检测到的特征数目一定要多,密集度最好能在一定程度上反映图像的内容。
  5. 准确性:得到的特征应该能被精确定位,能够精确到像素。
  6. 高效性:特征检测算法运算要快。

那么什么是好的角点检测算法

  • 1,检测出图像中“真实”的角点
  • 2,准确的定位性能
  • 3,很高的重复检测率
  • 4,噪声的鲁棒性
  • 5,较高的计算效率

  下面分别学习 Harris角点检测和 SIFT算法。

1,Harris 角点检测

1.1,Harris角点算法概述

  Harris角点是特征点检测的基础,提出了应用邻域像素点灰度差值概念,从而进行判断是否为角点,边缘,平滑区域。Harris角点检测原理是利用移动的窗口在图像中计算灰度变化值,其中关键流程包括转化为灰度图像,计算差分图像,高斯平滑,计算局部极值,确认角点。

1.1.1  Harris角点算法思想

  角点原理来源于人对角点的感性判断,即图像在各个方向灰度有明显变化。算法的核心是利用局部窗口在图像上进行移动判断灰度发生较大的变化,所以此窗口用于计算图像的灰度变化为:[-1, 0, 1;-1, 0, 1;-1, 0, 1]  [-1, -1, -1; 0, 0, 0;1, 1, 1]。人各个方向上移动这个特征的小窗口,如下图3中窗口内区域的灰度发生了较大的变化,那么就认为在窗口内遇到了角点。如下图1中,窗口内图像的灰度没有发生变化,那么窗口内就不存在角点;如果窗口在某一个方向移动时,窗口内图像的灰度发生了较大的变化,而在另一些方向上没有发生变化,那么,窗口内的图像可能就是一条直线的线段。

1.2,Harris角点检测数学推导

  根据算法思想,Harris算法是利用窗口内图像灰度的自相关性进行的,设计一个窗口在图像中移动,构建数学模型,计算移动窗口的灰度差值(即移动前和移动后窗口所在区域图像的自相关系数)。

  Harris算法是利用的窗口内图像灰度的自相关性进行的,设定一个窗口,并在图像中移动,计算移动前与移动后窗口所在区域图像的自相关系数。

  自相关函数计算如下,(x, y)为窗口中心位置,w(u, v)为权重(一般取高斯函数),L表示窗口,(u, v)表示窗口中图像位置。

  对于图像I(x,  y) ,当在点(x, y)处平移(Δx, Δy)后的自相似性:

  W(x,  y) 是以点(x, y)为中心的窗口函数,即可以是常数,也可以是高斯加权函数(但是通常都为高斯加权函数):

  为了减小计算量,基于泰勒展开进行简化公式,对图像l(x, y)在平移(Δx, Δy)后进行一阶近似:

   其中 Ix, Iy 是 I(x, y) 的偏导数。

  将近似值代入自相关函数,有:

  将平方项展示并写成矩阵形式,有:

  回到自相关表达式:

  近似可得下面表达:

   其中M是2*2的矩阵(M矩阵为偏导数矩阵),可由图像的导数求得:

   化简可得:

  经过上面的数学形式推导,已经得到了自相关函数的表达式。可以看得这也是一个椭圆的矩阵表达式形式(非标准椭圆),因此其系数矩阵M的特征值与椭圆的半轴长短有关。

1.2.1  椭圆的矩阵方差表示

  二次项函数本质上就是一个椭圆函数,椭圆如下:

  其椭圆方程式如下:

   如果将上述方差写成矩阵形式如下:

1.2.2 椭圆半轴与系数矩阵的关系

  一个n*n的矩阵,可以求解其特征值,我们对上述系数矩阵(含a、b)进行求解,则可得到特征值与椭圆半轴(a、b)的关系,下面我们看一下椭圆半轴与特征值的关系,如果求上述方程式的特征值,则可以得到特征值与椭圆半轴的关系,推导如下:

  用M表示参数:

  所以椭圆方程为:

  其特征值为 λ1  和 λ2,则特征值与半轴的关系为:

  我们在下面椭圆图中可以看到:

   结论就是:特征值大,半轴短

   边界:一个特征值大,另一个特征值小,λ1 >> λ2  或者 λ2 >> λ1。自相关函数值在某一个方向上大,在其他方向上小。

  平面:两个特征值都小,且近似相等;自相关函数数值在各个方向上都小

  角点:两个特征值都很大且近似相等,自相关函数在所有方向都增大

  经过上面的数学形式推导,已经得到了自相关函数的表达式。可以看得这也是一个椭圆的矩阵表达形式(非标准椭圆),因此其系数矩阵M的特征值与椭圆的半轴长短有关,这与上面预备知识中的结论一样。

  假设M的特征值为 λ1 λ2,则分为以下三种情况:

   通过上面的情况,计算出特征值后就可以判别是否是角点了。

1.2.3  角点响应R值

  M为梯度的协方差矩阵,在实际应用中为了能够应用更好的编程,定义了角点响应函数R,通过判断R大小来判断像素是否为角点。

  R取决于M的特征值,对于角点 |R| 很大,平坦的区域 |R| 很小,边缘的 R 为负值。

  当然,这样计算量非常大,因为图像中的几乎每个点都需要进行一次特征值的计算,下面给出一个角点响应函数R的经验公式:

   detM 表示M的行列式,traceM表示M的迹,R表示角点响应值,alpha表示经验常数,一般在0.04~0.06之间取值。

   判断准则:当R超过某个设定的阈值时,可认为是角点,反之,则不是。

1.3,Harris 角点检测算法流程

  步骤1:利用水平,竖直差分算子对图像 I(x, y)的每个像素进行滤波以求得 X 和 Y方向的梯度  Ix和Iy,进而求得M中的四个元素的值。

  代码中如果array为{ -1, 0, 1, -1, 0, 1, -1, 0, 1 }则是求解X方向的,如果为{-1, -1, -1, 0, 0, 0, 1, 1, 1}为Y方向的,则Ix和Iy求解结束。

  求解IX2 IY2  IXY相对比较简单,像素相乘即可。

  对M的四个元素进行高斯平滑滤波,为的是消除一些不必要的孤立点和凸起,得到新的矩阵M。求M的话,我们使用高斯核对IX2 IY2  IXY进行加权,计算矩阵 M 的元素,A,B,C,然后得到M矩阵。

  步骤2:接下来使用M计算对应每个像素的角点响应函数R

  其中,角点响应函数R 为:

  改进的角点响应函数R为:

   里面没有随意给定的参数k,取值应当比第一个令人满意。

  步骤3:局部最大值抑制,同时选取其最大值

  步骤4:在矩阵R中,同时满足 R(i, j)大于一定阈值 threshold 和 R(i, j) 是某邻域内的局部极大值,则被认为是角点。当角点量小于阈值值,则不是候选角点。

1.4,Harris角点性质

1.4.1  阈值决定检测点数量

  增大 alpha 的值,将减小角点响应值 R,降低角点检测的灵性,减少被检测角点的数量;减少 alpha 值,将增大角点响应值 R,增加角点检测的灵敏性,增加被检测角点的数量。

1.4.2  Harris角点检测算子对亮度和对比度的变化不敏感

  这是因为在进行 Harris角点检测时,使用了微分算子对图像进行微分运算,而微分运算对图像密度的拉升或收缩和对亮度的抬高或下降不敏感。换言之,对亮度和对比度的仿射变换并不改变 Harris响应的极值点出现的位置,但是,由于阈值的选择,可能会影响角点检测的数量。

1.4.3  Harris角点检测算子具有旋转不变性

  Harris角点检测算子使用的是角点附加的区域灰度二阶矩矩阵。而二阶矩矩阵可以表示成一个椭圆,椭圆的长短轴正是二阶矩矩阵特征值平方根的倒数。当特征椭圆转动时,特征值并不发生变换,所以判断角点响应值也不发生变换,由此说明 Harris角点检测算子具有旋转不变性。

1.4.4  Harris角点检测算子不具有尺度不变性

  如下图所示,当右图被缩小时,在检测窗口尺寸不变的前提下,在窗口内所包含图像的内容是完全不同的。左侧的图像可能被检测为边缘或曲线,而右边的图像则可能被检测为一个角点。

1.5,Harris角点检测实战

1.5.1  Harris的opencv源码

  首先我们看一下Harris的opencv源码:

def cornerHarris(src, blockSize, ksize, k, dst=None, borderType=None): # real signature unknown; restored from __doc__
    """
    cornerHarris(src, blockSize, ksize, k[, dst[, borderType]]) -> dst
    .   @brief Harris corner detector.
    .   
    .   The function runs the Harris corner detector on the image. Similarly to cornerMinEigenVal and
    .   cornerEigenValsAndVecs , for each pixel \f$(x, y)\f$ it calculates a \f$2\times2\f$ gradient covariance
    .   matrix \f$M^{(x,y)}\f$ over a \f$\texttt{blockSize} \times \texttt{blockSize}\f$ neighborhood. Then, it
    .   computes the following characteristic:
    .   
    .   \f[\texttt{dst} (x,y) =  \mathrm{det} M^{(x,y)} - k  \cdot \left ( \mathrm{tr} M^{(x,y)} \right )^2\f]
    .   
    .   Corners in the image can be found as the local maxima of this response map.
    .   
    .   @param src Input single-channel 8-bit or floating-point image.
    .   @param dst Image to store the Harris detector responses. It has the type CV_32FC1 and the same
    .   size as src .
    .   @param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ).
    .   @param ksize Aperture parameter for the Sobel operator.
    .   @param k Harris detector free parameter. See the formula above.
    .   @param borderType Pixel extrapolation method. See #BorderTypes.
    """
    pass

   参数含义:

  • src:数据类型为 float32 的输入图像
  • blockSize:角点检测中指定区域的大小
  • ksize:Sobel求导中使用的窗口大小
  • k:取值参数为 [0.04, 0.06]

1.5.2  Harris角点检测opencv实战

  下面以一张图片为例:

import cv2
import numpy as np

img = cv2.imread('test_1.jpg')
print('imgshape', img.shape)
# imgshape (800, 1200, 3)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
print('dst.shape', dst.shape)
# dst.shape (800, 1200)

img[dst>0.01*dst.max()] = [0, 0, 255]
cv2.imshow('dst', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

  比 max 的 0.01 大,则表示为角点,我们拿到其角点。

   棋盘,角点比较多,但是是彩色的,如果想看真的关于角点的效果,可以用黑白棋盘。

   再看一张图,效果如下:

 

2,SIFT特征

2.1  SIFT特征概述

  SIFT 的全称是 Scale Invariant Feature Transform,尺度不变特征变换,由加拿大教授David G.Lowe提出的。SIFT特征对旋转,尺度缩放,亮度变化等保持不变性,是一种非常稳定的局部特征。

  SIFT提取图像的局部特征,在尺度空间寻找极值点,并提取出其位置,尺度,方向信息。SIFT的应用范围包括物体辨识,机器人地图感知与导航,影像拼接,3D模型建立,手势识别,影像追踪等。

2.1.1  SIFT算法具有的特征

  图像的局部特征,对旋转,尺度缩放,亮度变化保持不变,对视角变化,仿射变换,噪声也保持一定程度的稳定性。

  • 1,对旋转,尺度缩放,亮度变化保持不变性,对视角变化,噪声等也存在一定程度的稳定性
  • 2,独特性,信息量丰富,适用于在海量特征数据中进行快速,准确的匹配
  • 3,多量性,即使少数几个物体也可以产生大量的SIFT特征向量
  • 4,可扩展性,可以很方便的与其他形式的特征向量进行联合

  SIFT算法的实质是在不同的尺度空间上寻找关键点(特征点),计算关键点的大小,方向,尺度信息,利用这些信息组成关键点对特征点进行描述的问题。SIFT所查找的关键点都是一些十分突出,不会因光照,仿射变换和噪声等因素而变换的“稳定”特征点,如角点,边缘点,暗区的亮点以及亮区的暗点等。匹配的过程就是对比这些特征点的过程,这个流程可以用下图表述:

2.2  SIFT特征提取和匹配的具体步骤

  首先,我们写出SIFT特征提取和匹配的步骤,如下:

  • 1,生成高斯差分金字塔(DOG金字塔),尺度空间构建
  • 2,尺度空间的极值检测(关键点的初步查探)
  • 3,稳定关键点的精确定位(特征点定位)
  • 4,稳定关键点方向信息分配(特征方向赋值)
  • 5,关键点描述(特征点描述)
  • 6,特征点匹配

  关于高斯金字塔的具体原理,可以参考如下博客:

OpenCV计算机视觉学习(7)——图像金字塔(高斯金字塔,拉普拉斯金字塔,图像缩放resize()函数)

2.2.1  生成高斯差分金字塔(DOG金字塔),尺度空间创建

  (尺度空间构建的基础是 DOG金字塔,而DOG金字塔是由高斯金字塔变换得到,关于高斯金字塔的博客请参考:

OpenCV计算机视觉学习(7)——图像金字塔(高斯金字塔,拉普拉斯金字塔,图像缩放resize()函数)

  在一定的范围内,无论物体是大还是小,人眼都可以分辨出来,然而计算机要有相同的能力却很难,所以要让机器能够对物体在不同尺度下有一个统一的认知,就需要考虑图像在不同的尺度下都存在的特点。

  尺度空间的获取通常使用高斯滤波来实现:

  其中G是高斯函数,高斯函数公式如下:

   尺度空间的获取即给一个图像的不同点分配不同的权重,下面以5*5的高斯核为例,我们从下图可以看到不同点分配的权重是不同的。

  当不同Sigma的高斯函数决定了对图像的平滑程度,越大的Sigma值对应的图像越模糊:

   高斯差分金字塔(DOG)的创建如下:

  每一组在层数上,DOG金字塔比高斯金字塔少一层。后续Sift特征点的提取都是在DOG金字塔上进行的。

  DOG定义公式:

  主要实现是通过对原始图像进行尺度变换,获得图像多尺度下的尺度空间表示序列,对这些序列进行空间主轮廓的图区,并以该主轮廓作为一种特征向量,实现边缘,角点检测不同分辨率上的关键点提取等。

2.2.2  DOG 空间极值点检测(关键点的初步查探)

  搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和选择不变的兴趣点。

  为了寻找尺度空间(DOG函数)的极值点,每个像素点要和其图像域(同一尺度空间)和尺度域(相邻的尺度空间)的所有相邻点进行比较,当其大于(或小于)所有相邻点时,该点就是极值点。如下图所示:

   在二维的图像中,中间的检测点要和其所在图像的3*3邻域8个像素点,以及其相邻的上下两层图像(在同一组内的尺度空间上)的3*3领域的18 个像素点,共 26个像素点进行比较。如此可以保证检测到的关键点在尺度空间和二维图像空间上都是局部极值点。

2.2.3  稳定关键点的精确定位

  DOG 值对噪声和边缘比较敏感,所以在第2步的尺度空间中检测到的局部极值点还要经过进一步的筛选,去除不稳定和错误检测出的极值点,另一点就是在构建高斯金字塔过程中采用了下采样的图像,在下采样图像中提取的极值点对应在原始图像中的确切位置,也是要在本步骤中解决的问题。

  在每个候选的位置上,通过拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。这些候选关键点是DOG空间的局部极值点,而且这些极值点均为离散的点,精确定位极值点的一种方法是,对尺度空间的 DOG 函数进行曲线拟合,计算其极值点,从而实现关键点的精确定位。

  可以利用泰勒级数进行展开:

消除边界响应

  Hessian矩阵:

   令 α = λmax  为最大的特征值,β = λmin最小的特征值

  Lower 在论文中给出 γ = 10,也就是说对于主曲率比值大于10的特征点将被删除。

2.2.4  稳定关键点方向信息分配(特征点的主方向)

  稳定的极值点是在不同尺度空间下提取的,这保证了关键点的尺度不变性。为关键点分配方向信息所要解决的问题是使得关键点对图像角度和旋转具有不变形。方向的分配是通过求每个极值点的梯度来实现的。

  对于任一关键点L(x, y)的梯度幅值(梯度的模) m(x, y) 以及梯度方向 Θ(x, y):

  每个特征点可以得到三个信息(x, y, σ, Θ),即位置,尺度和方向。具有多个方向的关键点可以被复制成多份,然后将方向值分别赋予复制后的特征点,一个特征点就产生了多个坐标,尺度相等,但是方向不同的特征点。

  分类给关键点的方向并不直接是关键点的梯度方向,而是按照一种梯度方向直方图的方式给出的。具体的方法是:计算以关键点为中心的领域内所有点的梯度方向,当然梯度方向一定是在 0~360度范围内,对这些梯度方向归一化到 36 个方向内,每个方向代表了 10度的范围。然后累积落到每个方向内的关键点个数,以此生成梯度方向直方图。

  在完成关键点的梯度计算后,使用直方图统计邻域内像素的梯度和方向

  将梯度方向直方图中纵坐标最大的项代表的方向分配给当前关键点作为主方向,若在梯度直方图中存在一个相当于主峰值 80% 能量的峰值,则将这个方向认为是关键点的辅助方向。辅助方向的设计可以增强匹配的鲁棒性,Lowe指出,大概有 15% 的关键点 具有辅方向,而恰恰是这 15%的关键点对稳定匹配起到关键作用。

2.2.5  生成关键点特征描述

  对关键点的描述是后续实现匹配的关键步骤,描述其实就是一种以数学方式定义关键的过程。描述子不但包含关键点,也包含关键点周围对其有贡献的邻域点。

   为了保证特征矢量的旋转不变性,要以特征点为中心,在附近领域内将坐标轴旋转 Θ 角度,即将坐标轴旋转为特征点的主方向。

  旋转之后的主方向为中心取8*8的窗口,求每个像素的梯度幅值和方向,箭头方向代表梯度方向,长度代表梯度幅值,然后利用高斯窗口对其进行加权运算,最后在每个4*4的小块上绘制8个方向的梯度直方图,计算每个梯度方向的累加值,即可形成一个种子点,即每个特征由4个种子点组成,每个种子点有8个方向的向量信息。

  如下图,对于2*2块,每块的所有像素点的梯度做高斯加权,每块最终取8个方向,即可以生成 2*2*8 维度的向量,以这 2*2*8 维向量作为中心关键点的数学描述。

  论文中(David G.Lowed的实验结果)建议对每个关键点使用 4*4 共 16个种子点来描述,这样一个关键点就产生 128 维的SIFT特征向量。即采用128维向量的描述子进行关键点表征,综合效果最佳:

2.2.6  特征点匹配

  特征点的匹配通过计算两组特征点的 128 维的关键点的欧式距离实现的。欧式距离越小,则相似度越高,当欧式距离小于设定的阈值时,可以判定为匹配成功。

 

2.3,Python实现SIFT特征检测

2.3.1  更新opencv的安装包

  3.4.3 以上 就进入了专利保护的阶段,一些函数已经用不了,他们申请专利了,我们需要降版本。

  所以如果我们的版本大于3.4.3, 则需要降版本,目前最稳定的可以使用SIFT的opencv版本为3.4.1.15。

pip install opencv-python==3.4.1.15

pip install opencv-contrib-python==3.4.1.15

   3.4.1.15 使用SIFT函数是没有任何问题的。

  安装好之后,我们下面实现SIFT特征检测。

2.3.2  SIFT代码实现

  得到关键点之后,我们需要计算出特征,kp不能直接打印,因为是list格式,我们需要转换为np.array()格式。

  拿到所有的向量点为 128维度 6827个特征点。

  代码如下

import cv2
import numpy as np

img = cv2.imread('test_1.jpg')
print('imgshape', img.shape)
# imgshape (800, 1200, 3)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# gray = np.float32(gray)

# 得到特征点
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)
# kp 是封装好的东西,我们看不见,只能展示

img = cv2.drawKeypoints(gray, kp, img)

img = cv2.resize(img, None, fx=0.5, fy=0.5)
cv2.imshow('dst', img)
cv2.waitKey(0)
cv2.destroyAllWindows()


# 计算特征
kp, des = sift.compute(gray, kp)  
print(np.array(kp).shape)  # (6827,)
print(des.shape)  # (6827, 128)
print(des[0])

'''
[  0.   0.   0.   0.   0.   0.   0.   0.  21.   8.   0.   0.   0.   0.
   0.   0. 157.  31.   3.   1.   0.   0.   2.  63.  75.   7.  20.  35.
  31.  74.  23.  66.   0.   0.   1.   3.   4.   1.   0.   0.  76.  15.
  13.  27.   8.   1.   0.   2. 157. 112.  50.  31.   2.   0.   0.   9.
  49.  42. 157. 157.  12.   4.   1.   5.   1.  13.   7.  12.  41.   5.
   0.   0. 104.   8.   5.  19.  53.   5.   1.  21. 157.  55.  35.  90.
  22.   0.   0.  18.   3.   6.  68. 157.  52.   0.   0.   0.   7.  34.
  10.  10.  11.   0.   2.   6.  44.   9.   4.   7.  19.   5.  14.  26.
  37.  28.  32.  92.  16.   2.   3.   4.   0.   0.   6.  92.  23.   0.
   0.   0.]
'''

   图如下:

 

2.3.3  opencv中SIFT 函数中keypoint 数据结构分析

  分析opencv中keypoint数据结构的相关信息,我们首先找到opencv的官方文档:

//docs.opencv.org/java/org/opencv/features2d/KeyPoint.html

  可以看到KP的数据结果中有如下关键点:

  • angle:角度,表示关键点的方向,通过 Lowe 大神的论文可以知道,为了保证方向不变形,SIFT算法通过对关键点周围领域进行梯度运算,求得该点方向,-1为初始值。
  • class_id:当要对图像进行分类时,我们可以用 class_id 对每个特征点进行区分,未设定时为 -1,需要靠自己设定。
  • octave:代表是从金字塔哪一层提取得到的数据
  • pt:关键点点的坐标
  • response:响应程度,代表该点强壮大小
  • size:该点直径大小

  注意:Keypoint只是保存了opencv的sift库检测到的特征点的一些基本信息,也就是上面所说的这些,但sift所提取出来的特征向量其实不在这里面。特征向量是通过SiftDescriptorExtractor 提取,结果放在一个 Mat 的数据结构中。这个数据结构才真正保存了该特征点所对应的特征向量。

 

参考地址://blog.csdn.net/yangleo1987/article/details/53261637

 //blog.csdn.net/Sakura55/article/details/81506151

 //blog.csdn.net/dcrmg/article/details/52577555

//www.cnblogs.com/cj695/p/4041399.html

//www.cnblogs.com/cj695/p/4045443.html

//wenku.baidu.com/view/b0a2788acc22bcd126ff0c48.html

//www.cnblogs.com/ronny/p/3886013.html