基本概念
SURF(SpeededUp Robust Features)—加速稳健特征算法, 在2006 年由Bay.H和Van Gool.L共同提出, SURF是尺度不变特征变换SIFT的加速版。一般来说, 标准的SURF算子比SIFT算子快好几倍, 并且在多幅图像下具有更好的稳定性。SURF最大的特征在于采用了harr特征以及积分图像的概念, 这大大加快了程序运行时间,可以应用于物体识别以及三维重建中。
原理
SURF算法简介:
在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, 降低的维度可以加速计算和匹配, 但又能提供更容易区分的特征。
在检测特征点的过程中计算了 Hessian 矩阵的行列式, 与此同时, 计算得到了 Hessian 矩阵的迹, 矩阵的迹为对角元素之和。
按照亮度的不同, 可以将特征点分为两种, 第一种为特征点及其周围小邻域的亮度比背景区域要亮, Hessian矩阵的迹为正; 另外一种为特征点及其周围小邻域的亮度比背景区域要暗, Hessian矩阵为负值。根据这个特性, 首先对两个特征点的Hessian 的迹进行比较, 如果同号, 说明两个特征点具有相同的对比度; 如果异号的话, 说明两个特征点的对比度不同, 放弃特征点之间后续的相似性度量。
对于两个特征点描述子的相似性度量, 我们采用欧式距离进行计算。简单来说 SURF 算法采用了很多方法来对每一步进行优化从而提高速度, 分析显示在结果效果相当的情况下 SURF 的速度是SIFT的3倍。SURF善于处理具有模糊和旋转的图像, 但是不善于处理视角变化和光照变化。
函数原型
- hessianThreshold: Hessian矩阵行列式响应值的阈值
- nOctaves: 图像堆的组数
- nOctaveLayers: 图像堆中每组中的中间层数, 该值加2等于每组图像中所包含的层数
- extended: 表示是128维描述符, 还是64维描述符, 为true 时, 表示128维描述符
- upright: 表示是否采用U-SURF算法, 为true时, 采用U-SURF算法
重载
特征点匹配方法
Brute Force匹配和FLANN匹配是OpenCV二维特征点匹配常见的两种办法, 分别对应BFMatcher和FlannBasedMatcher。
二者的区别:
BFMatcher总是尝试所有可能的匹配, 从而使得它总能够找到最佳匹配。
FlannBasedMatcher中FLANN的含义是Fast Library forApproximate Nearest Neighbors, 它是一种近似法, 算法更快但是找到的是最近邻近似匹配, 当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候可以用FlannBasedMatcher。当然也可以通过调整FlannBasedMatcher的参数来提高匹配的精度或者提高算法速度, 但是相应地算法速度或者算法精度会受到影响。
代码1- - -surf
Mat srcImg1 = imread("11.jpg");
Mat srcImg2 = imread("22.jpg");
//定义SURF特征检测类对象
SurfFeatureDetector surfDetector(400);
//定义KeyPoint变量
vector<KeyPoint>keyPoints1;
vector<KeyPoint>keyPoints2;
//特征点检测
surfDetector.detect(srcImg1, keyPoints1);
surfDetector.detect(srcImg2, keyPoints2);
//绘制特征点(关键点)
Mat feature_pic1, feature_pic2;
//drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar(0, 255, 0));
//drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar(0,255, 0));
drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//显示原图
imshow("src1", srcImg1);
imshow("src2", srcImg2);
//显示结果
imshow("feature1", feature_pic1);
imshow("feature2", feature_pic2);
waitKey(0);
代码2–surf_Matcher
#include "opencv2/opencv.hpp"
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/legacy/legacy.hpp>
#include <vector>
#include<iostream>
using namespace std;
using namespace cv;
void main()
{
Mat srcImg1 = imread("00.jpg");
Mat srcImg2 = imread("01.jpg");
//定义SURF特征检测类对象
SurfFeatureDetector surfDetector(400); //HessianThreshold
//定义KeyPoint变量
vector<KeyPoint>keyPoints1;
vector<KeyPoint>keyPoints2;
//特征点检测
surfDetector.detect(srcImg1, keyPoints1);
surfDetector.detect(srcImg2, keyPoints2);
//绘制特征点(关键点)
Mat feature_pic1, feature_pic2;
drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar::all(-1));
drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar::all(-1));
//drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//显示原图
imshow("src1", srcImg1);
imshow("src2", srcImg2);
//显示结果
imshow("feature1", feature_pic1);
imshow("feature2", feature_pic2);
//计算特征点描述符 / 特征向量提取
SurfDescriptorExtractor descriptor;
Mat description1;
descriptor.compute(srcImg1, keyPoints1, description1);
Mat description2;
descriptor.compute(srcImg2, keyPoints2, description2);
cout<<description1.cols<<endl;
cout<<description1.rows<<endl;
//进行BFMatch暴力匹配
BruteForceMatcher<L2<float>>matcher; //实例化暴力匹配器
vector<DMatch>matches; //定义匹配结果变量
matcher.match(description1, description2, matches); //实现描述符之间的匹配
//计算向量距离的最大值与最小值
double max_dist=0, min_dist=100;
for(int i=0; i<description1.rows; i++)
{
if(matches.at(i).distance > max_dist)
max_dist = matches[i].distance;
if(matches.at(i).distance < min_dist)
min_dist = matches[i].distance;
}
cout<<"min_distance="<<min_dist<<endl;
cout<<"max_distance="<<max_dist<<endl;
//匹配结果删选
vector<DMatch>good_matches;
for(int i=0; i<matches.size(); i++)
{
if(matches[i].distance < 2*min_dist)
good_matches.push_back(matches[i]);
}
Mat result;
//drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, matches, result, Scalar::all(-1), Scalar::all(-1));
drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, good_matches, result, Scalar(0, 255, 0), Scalar::all(-1));
imshow("Match_Result", result);
waitKey(0);
}
代码3- - -surf-FlannBasedMatcher
#include "opencv2/opencv.hpp"
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/legacy/legacy.hpp>
#include <vector>
#include<iostream>
using namespace std;
using namespace cv;
void main()
{
Mat srcImg1 = imread("11.jpg");
Mat srcImg2 = imread("22.jpg");
//定义SURF特征检测类对象
SurfFeatureDetector surfDetector(400); //HessianThreshold
//定义KeyPoint变量
vector<KeyPoint>keyPoints1;
vector<KeyPoint>keyPoints2;
//特征点检测
surfDetector.detect(srcImg1, keyPoints1);
surfDetector.detect(srcImg2, keyPoints2);
//绘制特征点(关键点)
Mat feature_pic1, feature_pic2;
drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar::all(-1));
drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar::all(-1));
//drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//显示原图
imshow("src1", srcImg1);
imshow("src2", srcImg2);
//显示结果
imshow("feature1", feature_pic1);
imshow("feature2", feature_pic2);
//计算特征点描述符 / 特征向量提取
SurfDescriptorExtractor descriptor;
Mat description1;
descriptor.compute(srcImg1, keyPoints1, description1);
Mat description2;
descriptor.compute(srcImg2, keyPoints2, description2);
cout<<description1.cols<<endl;
cout<<description1.rows<<endl;
//进行BFMatch暴力匹配
//BruteForceMatcher<L2<float>>matcher; //实例化暴力匹配器
FlannBasedMatcher matcher; //实例化FLANN匹配器
vector<DMatch>matches; //定义匹配结果变量
matcher.match(description1, description2, matches); //实现描述符之间的匹配
//计算向量距离的最大值与最小值
double max_dist=0, min_dist=100;
for(int i=0; i<description1.rows; i++)
{
if(matches.at(i).distance > max_dist)
max_dist = matches[i].distance;
if(matches.at(i).distance < min_dist)
min_dist = matches[i].distance;
}
cout<<"min_distance="<<min_dist<<endl;
cout<<"max_distance="<<max_dist<<endl;
//匹配结果删选
vector<DMatch>good_matches;
for(int i=0; i<matches.size(); i++)
{
if(matches[i].distance < 2*min_dist)
good_matches.push_back(matches[i]);
}
Mat result;
//drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, matches, result, Scalar::all(-1), Scalar::all(-1));
drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, good_matches, result, Scalar(0, 255, 0), Scalar::all(-1));
imshow("Match_Result", result);
waitKey(0);
}