本章使用的是opencv-contrib-python的3.4.2.16版本,安装过程如下:
pip uninstall opencv-python
pip install opencv-contrib-python==3.4.2.16
目录
0 原理 1 OpenCV 中的 SURF 2 举例
在上一节中我们学习了使用 SIFT 算法进行关键点检测和描述。但是这种算法的执行速度比较慢,人们需要速度更快的算法。在 2006 年 Bay,H.,Tuytelaars,T. 和 Van Gool,L 共同提出了 SURF(加速稳健特征) 算法。跟它的名字一样,这是个算法是加速版的 SIFT。
基本步骤:
在SIFT中,Lowe在构建尺度空间时使用DoG对LoG进行近似。SURF使用盒子滤波器(box_filter)对 LoG 进行近似。下图显示了这种近似。在进 行卷积计算时可以利用积分图像(积分图像的一大特点是:计算图像中某个窗 口内所有像素和时,计算量的大小与窗口大小无关),是盒子滤波器的一大优点。而且这种计算可以在不同尺度空间同时进行。同样 SURF 算法计算关键点 的尺度和位置是也是依赖与 Hessian 矩阵行列式的。
为了保证特征矢量具有选装不变形,需要对于每一个特征点分配一个主要方向。需要以特征点为中心,以 6s(s 为特征点的尺度)为半径的圆形区域 内,对图像进行 Harr 小波相应运算。这样做实际就是对图像进行梯度运算, 但是利用积分图像,可以提高计算图像梯度的效率,为了求取主方向值,需要设计一个以方向为中心,张角为 60 度的扇形滑动窗口,以步长为 0.2 弧度 左右旋转这个滑动窗口,并对窗口内的图像 Haar 小波的响应值进行累加。主 方向为最大的 Haar 响应累加值对应的方向。在很多应用中根本就不需要旋转 不变性,所以没有必要确定它们的方向,如果不计算方向的话,又可以使算法 提速。SURF 提供了成为 U-SURF 的功能,它具有更快的速度,同时保持了 对 +/-15 度旋转的稳定性。OpenCV 对这两种模式同样支持,只需要对参数upright 进行设置,当 upright 为 0 时计算方向,为 1 时不计算方向,同时速度更快。
生成特征点的特征矢量需要计算图像的 Haar 小波响应。在一个矩形的区域内,以特征点为中心,沿主方向将 20s*20s 的图像划分成 4*4 个子块,每 个子块利用尺寸 2s 的 Haar 小波模版进行响应计算,然后对响应值进行统计, 组成向量 v = (
∑ dx, ∑ dy, ∑ |dx|, ∑ |dy|)。这个描述符的长度为 64。降低的维度可以加速计算和匹配,但又能提供更容易区分的特征。
为了增加特征点的独特性,SURF 还提供了一个加强版 128 维的特征描述符。当 dy 大于 0 和小于 0 时分别对 dx 和 |dx| 的和进行计算,计算 dy 和 |dy| 时也进行区分,这样获得特征就会加倍,但又不会增加计算的复杂度。 OpenCV 同样提供了这种功能,当参数 extended 设置为 1 时为 128 维,当 参数为 0 时为 64 维,默认情况为 128 维。
在检测特征点的过程中计算了 Hessian 矩阵的行列式,与此同时,计算得到了 Hessian 矩阵的迹,矩阵的迹为对角元素之和。
按照亮度的不同,可以将特征点分为两种,第一种为特征点迹其周围小邻域的亮度比背景区域要亮,Hessian 矩阵的迹为正;另外一种为特征点迹其周 围小邻域的亮度比背景区域要暗,Hessian 矩阵为负值。根据这个特性,首先 对两个特征点的 Hessian 的迹进行比较。如果同号,说明两个特征点具有相同 的对比度;如果异号的话,说明两个特征点的对比度不同,放弃特征点之间的 后续的相似性度量。
对于两个特征点描述子的相似性度量,我们采用欧式距离进行计算。
简单来说 SURF 算法采用了很多方法来对每一步进行优化从而提高速度。分析显示在结果效果相当的情况下 SURF 的速度是 SIFT 的 3 倍。SURF 善 于处理具有模糊和旋转的图像,但是不善于处理视角变化和关照变化。
与SIFT比较:
SIFT | SURF | |
尺度空间 | DOG与不同尺度的图片卷积 | 不同尺度的box filters与原图片卷积 |
特征点检测 | 先进行非极大抑制,再去除低对比度 点。再通过Hessian矩阵去除边缘的点 | 先利用Hessian矩阵确定候选点,然 后进行非极大抑制 |
方向 | 在正方形区域内统计梯度的幅值的直方图,找max对应的方向。可以有多个方向。 | 在圆形区域内,计算各个扇形范围内 X、y方向的haar小波响应,找模最大的扇形方向 |
特征描述子 | 16x16的采样点划分为4x4的区域, 计算每个区域的采样点的梯度方向和 幅值,统计成8bin直方图,一共 4x4x8=128维 | 20x20s的区域划分为4x4的子区域, 每个子区域找5x5个采样点,计算采样 点的haar小波响应,记录Σdx, Σdy,Σ|dx|,Σ|dy|, —共 4x4x4=64 |
与 SIFT 相同 OpenCV 也提供了 SURF 的相关函数。首先我们要初始化一个 SURF 对象,同时设置好可选参数:64/128 维描述符,Upright/ Normal 模式等。所有的细节都已经在文档中解释的很明白了。就像我们在 SIFT 中一样,我们可以使用函数 SURF.detect(),SURF.compute() 等 来进行关键点搀着和描述。
创建一个SURF对象:
cv2.xfeatures2d.SURF_create(, hessianThreshold, nOctaves, nOctaveLayers, extended, upright)
之后也可以通过类似getHessianThreshold(),setHessianThreshold()等函数来获取或修改上述参数值,例如
surf.setHessianThreshold(True)表示将HessianThreshold参数修改为True。
绘制特征点:
cv2.drawKeypoint(image, keypoints, outImage, color, flags)
或:outImage = cv2.drawKeypoint(image, keypoints, None, color, flags)
cv2.DRAW_MATCHES_FLAGS_DEFAULT:
默认值,只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:
绘制特征点的时候绘制的是带有方向的圆,这种方法同时显示图像的坐标,size,和方向,是最能显示特征的一种绘制方式。
cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG:
只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。
cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINT:
单点的特征点不被绘制
程序如下:
import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('test32.jpg', 0)
surf = cv2.xfeatures2d.SURF_create(30000)
kp = surf.detect(img, None)
img2 = cv2.drawKeypoints(img, kp, None, (255, 0, 0), 4)
# 不检查关键点的方向
surf.setUpright(True)
# 修改阈值
surf.setHessianThreshold(40000)
kp = surf.detect(img, None)
img3 = cv2.drawKeypoints(img, kp, None, (255, 0, 0), 4)
plt.subplot(121), plt.imshow(img2),
plt.title('Dstination'), plt.axis('off')
plt.subplot(122), plt.imshow(img3),
plt.title('Dstination'), plt.axis('off')
plt.show()
结果如下: