OpenCV4入门教程112:SURF特征检测及匹配

索引地址:系列索引

SURF(加速版的具有鲁棒性的特征,SpeededUp Robust Features),SURF是尺度不变特征变换算法(SIFT算法)的加速版。SURF最大的特征在于采用了harr特征以及积分图像的概念。

SURF原理

(1)构建Hessian矩阵构造高斯金字塔尺度空间

SIFT采用的是DoG图像,而SURF采用的是Hessian矩阵(SURF算法核心)行列式近似值图像。在数学中,Hessian矩阵是一个自变量为向量的实值函数的二阶偏导数组成的方块矩阵,即每一个像素点都可以求出一个2x2的Hessian矩阵,可计算出其行列式detH,可以利用行列式取值正负来判别该点是或不是极值点来将所有点分类。在SURF算法中,选用二阶标准高斯函数作为滤波器,通过特定核间的卷积计算二阶偏导数,从而计算出Hessian矩阵,但是由于特征点需要具备尺度无关性,所以在进行Hessian矩阵构造前,需要对其进行高斯滤波,即与以方差为自变量的高斯函数的二阶导数进行卷积。通过这种方法可以为图像中每个像素计算出其H的行列式的决定值,并用这个值来判别特征点。
上面说这么多,只是得到了一张近似hessian的行列式图,类似SIFT中的DoG图。但是在金字塔图像中分为很多层, 每一层叫做一个octave,每一个octave中又有几张尺度不同的图片。在SIFT算法中,同一个octave层中的图片尺寸(大小)相同,但是尺度不同(模糊程度)不同,而不同的octave层中的图片尺寸也不相同,因为它是由上一层图片将采样得到的。在进行高斯模糊时,SIFT的高斯模板大小是始终不变的,只是在不同的octave之间改变图片的大小。而在SURF中,图片的大小是一直不变的,不同octave层的待检测图片是改变高斯模糊尺寸大小得到的,当然,同一个octave中不同图片用到的高斯模板尺寸也不同。算法允许尺度空间多层图像同时被处理,不需要对图像进行二次抽样,从而提高算法性能。

(2)利用非极大值抑制初步确定特征点

此步骤和SIFT类似,将经过hessian矩阵处理过的每个像素点与其三维邻域的26个点进行大小比较,如果它是这26个点中的最大值或者最小值,则保留下来,当作初步的特征点。检测过程中使用与该尺度层图像解析度相对应大小的滤波器进行检测。

(3)精确定位极值点

这里也和SIFT算法中类似,采用三维线性插值法得到亚像素级的特征点,同时也去掉那些值小于一定阈值的点,增加极值使检测到的特征点数量减少,最终只有几个特征最强点会被检测出来。

(4)选取特征点的主方向

这一步与SIFT也大有不同,SIFT选取特征点主方向是采用在特征点邻域统计其梯度直方图,取直方图bin值最大的以及超过bin值80%的那些方向作为特征点的主方向。
而在SURF中,不统计其梯度直方图,而是统计特征点邻域内的harr小波特征。即在特征点的邻域(比如说,半径为6s的圆内,s为该点所在的尺度)内,统计60度扇形内所有点的水平haar小波特征和垂直haar小波特征总和,haar小波的尺寸变长为4s,这样一个扇形得到了一个值,然后60度扇形以一定间隔进行旋转,最后将最大值那个扇形的方向作为该特征点的主方向。

(5)构造surf特征点描述算子

在SIFT中,是在特征点周围取16x16的邻域,并把该邻域化为4x4个的小区域,每个小区统计8个方向的梯度,最后得到4x4x8=128维的向量,该向量作为该点SIFT描述子。
在SURF中,也是在特征点周围取一个正方形框,框的边长为20s(s是所检测到该特征点所在的尺度)。该框带方向,方向当然就是第(4)步检测出来的主方向了。然后把该框分为16个子区域,每个子区域统计25个像素的水平方向和垂直方向的haar小博特征,这里的水平和垂直方向都是相对主方向而言的。该haar小波特征为水平方向值之和,水平方向绝对值之和,垂直方向之和,垂直方向绝对值之和。这样每个区域就有4个值,所以每个特征点就是16x4=64维向量,相比于SIFT而言,少了一半,这在特征匹配过程中会大大加快匹配速度。
SURF采用Hessian矩阵获取图像局部最值十分稳定,但是在求主方向阶段太过于依赖局部区域像素的梯度方向,有可能使找到的主方向不准确。后面的特征向量提取以及匹配都严重依赖于主方向,即使不大偏差角度也可以造成后面特征匹配的放大误差,从而使匹配不成功。另外图像金字塔的层取得不够紧密也会使得尺度有误差,后面的特征向量提取同样依赖响应的尺度,发明者在这个问题上的折中解决办法是取适量的层然后进行插值。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/xfeatures2d/nonfree.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;

int main(int argc, char **argv) {
//【1】载入源图片并显示
Mat srcImage1 = imread("box.png", 1);
Mat srcImage2 = imread("box_in_scene.png", 1);
if (!srcImage1.data || !srcImage2.data) //检测是否读取成功
{
printf("读取图片错误,请确定目录下是否有imread函数指定名称的图片存在~! \n");
return false;
}
imshow("src1", srcImage1);
imshow("src2", srcImage2);

//【2】定义需要用到的变量和类
int minHessian = 400; //定义SURF中的hessian阈值特征点检测算子
Ptr<xfeatures2d::SURF> detector = xfeatures2d::SURF::create(minHessian); //定义一个SurfFeatureDetector(SURF) 特征检测类对象
std::vector<KeyPoint> keypoints_1,
keypoints_2; // vector模板类是能够存放任意类型的动态数组,能够增加和压缩数据

//【3】调用detect函数检测出SURF特征关键点,保存在vector容器中
detector->detect(srcImage1, keypoints_1);
detector->detect(srcImage2, keypoints_2);

//【4】绘制特征关键点.
Mat img_keypoints_1;
Mat img_keypoints_2;
drawKeypoints(srcImage1, keypoints_1, img_keypoints_1, Scalar::all(-1),
DrawMatchesFlags::DEFAULT);
drawKeypoints(srcImage2, keypoints_2, img_keypoints_2, Scalar::all(-1),
DrawMatchesFlags::DEFAULT);

//【5】显示效果图
imshow("keypoints1", img_keypoints_1);
imshow("keypoints2", img_keypoints_2);

waitKey(0);
return 0;
}

效果

surf


OpenCV4入门教程112:SURF特征检测及匹配
https://feater.top/opencv/opencv-surf-feature-detect-and-math/
作者
JackeyLea
发布于
2020年11月10日
许可协议