open cvSURF特征检测
什么是SURF特征检测(加速鲁棒特征)
1.属于尺度不变特征的检测,即不仅在任何尺度下拍摄的物体都能检测到一致的关键点,而且每个被检测的特征点都对应一个尺度因子
2.SURF 特征,它的全称为加速稳健特征( Speeded Up Robust Feature ),它不仅是尺度不变特征,而且是具有较高计算效率的特征
3.这个算法可被用于对象定位和识别、人脸识别、3D重建、对象跟踪和提取兴趣点 他部分的灵感来自于 SIFT 算法
应用场景
SURF可以用于对象定位和识别、人脸识别、3D重建、对象跟踪和提取兴趣点等。常用来进行物体辨识和图像匹配
SURF 全称 speed up robust feature 是加速版的SIFT
由于SURF特征检测较于Harris等检测算子有良好的尺度不变性,所有在不同景深不同高度不同方向不同视角都可以快速精准的匹配特征信息
如何提取SURF特征?两步:检测和描述
检测特征点
描述特征点
什么样的点是特征点
检测到,只要这个点所对应的那个物体还在摄像头的视野范围内,那么这个点就能被检测出来。比如角点,边界点,亮处的暗点,暗处的亮点等,一言以蔽之,就是与周围有反差的点
不受明暗光线变化的影响
工作原理
1.选择图像中POI(Points of Interest)Hessian Matrix
2.在不同的尺度空间发现关键点,非最大信号抑制
3.发现特征点方向、旋转不变性要求
4.生成特征向量
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2/xfeatures2d.hpp>//opencv第三方扩展库文件 用于特征SURF SIFT检测等 必须添加此库文件
#include <iostream>
#include <math.h>
using namespace cv;
using namespace cv::xfeatures2d;
using namespace std;
Mat src, dst;
int HessianNum = 200;
int HessianMax = 500;
void SurfFeature(int, void*);
int main()
{
src = imread("D:/实验台/机器视觉/测试图片/白人女.jpg");
if (src.empty())//如果src这个数据库属性为空
{
cout << "无法打开" << endl;
return -1;
}
imshow("原图", src);
namedWindow("Surf特征关键点位置图像",CV_WINDOW_AUTOSIZE);
createTrackbar("特征检测器阈值", "Surf特征关键点位置图像" ,&HessianNum, HessianMax,SurfFeature);
SurfFeature(0, 0);
waitKey(0);
return 0;
}
//教程代码
/*void SurfFeatureDemo(int, void*)
{
int HessianMin = HessianNum;
Ptr<SURF>detector = SURF::create(HessianMin, 4, 3, false, false); //todo构建特征检测器
vector<KeyPoint>keypoints;
detector->detect(src, keypoints, Mat());// todo使用Hessian算法检测关键点,并且对每个关键点周围的区域计算特征向量。该函数返回关键点的信息和描述符
Mat keypointimg;
drawKeypoints(src, keypoints, keypointimg, Scalar::all(-1), DrawMatchesFlags::DEFAULT);//todo绘制关键点
imshow("Surf特征关键点位置图像", keypointimg);
return;
}*/
void SurfFeature(int, void*)
{
//特征检测器阈值(是海塞矩阵的阈值 不是限定特征点个数的阈值)
int HessianMin = HessianNum;
//SURF对象创建函数api
Ptr<SURF>detector = SURF::create(HessianMin,4 ,3, false,false); //todo构建特征检测器
//声明类型SURF类的 指针类名对象detector 为 SURF类中的 成员函数create构建的特征检测器
//.xfeatures库中 类SURF 中 成员函数create() 参数说明:
//static Ptr<SURF> create
//1.double hessianThreshold -- hessian关键点检测器的阈值,默认在300-500之间(此值越大 特征点数量越小)
//2.int nOctaves -- 表示在4个尺度空间(用高斯尺度变化来模拟SIFR金字塔缩放,这里表示缩放级数,一般四级足够用了 越小特征点越少)
//3.int nOctaveLayers -- 表示每个尺度空间的层数 每个尺度空间的层数最少三层 (层数越小特征点越少)
//4.bool extended -- 扩展描述符标志(true使用扩展的128个元素的描述符,false使用64个元素的描述符)
//5.bool upright -- 计算旋转不变性的标志(true(1)不计算方向,false(0)计算方向 不计算速度更快)
//标注 Ptr<类型名>-专门用来管理 类型的指针
//detector只是SURF类的名字 其实他就是一个定义好的SURF类 有SURF类的数据成员
//todo设置用于存放特征点信息的 KeyPoint类的集合向量 keypoints
vector<KeyPoint>keypoints;
//?KeyPoint类 --一个为特征点检测而生的数据结构 此类中包含7个不同的元素
/*class KeyPoint 数据结构中包含的元素内容有
{
int size():特征点的总个数(进行提取显示时不需要进行标号处理 为keypoints.size())
以下6个元素在进行提取显示时需要进行标号处理才不会出现缺少指针的报警( 为keypoints[i].xxx )
Point2f i pt:特征点坐标
float i size: 特征点的邻域直径 特征点的大小
float i angle 特征点的方向,值为[0,360度),负值表示不使用
float i response:特征点的响应强度,代表了该点的稳健鲁棒程度 可以在Surf特征探测器的含参构造函数中设置响应强度的最低阈值 (可用于后续处理时的特征点排序工作)
int i octave:特征点所在的图像金字塔的哪一组
int i class_id:当要对图片进行分类时,用class_id对每个关键点进行区分,默认为-1 也就是特征点的分类信息
}*/
//SURF提取关键点函数api
detector->detect(src, keypoints, Mat());// todo使用Hessian算法检测关键点,并且对每个关键点周围的区域计算特征向量。该函数返回关键点的信息和描述符
//名为detector的SURF类指针 调用 SURF类detector中的成员子函数detect (位于其父类Feature2D中)并进行特征点向量计算
//利用类内的detect()函数可以检测出SURF特征的关键点,保存在vector点集合容器keypoints中,参数说明
//tvoid detect
//1.InputArray image -- 需要进行特征检测的输入图像
//2.vector<KeyPoint>& keypoints -- 保存检测出的关键点信息的Keypoint类向量数组
//3.InputArray mask=noArray() -- 设置ROI 指定在哪里寻找关键点的掩码(必须是在感兴趣区域中具有非零值的8位整数矩阵)
//标注 访问指向类的指针的成员,需要使用成员访问运算符 -> (不能使用直接成员访问符 . ),就像访问指向结构的指针一样.与所有的指针一样,您必须在使用指针之前,对指针进行初始化
/*
//显示经过关键点提取后的Keypoint类 向量参数keypoints的各个元素的信息
for (int i = 0; i < keypoints.size(); i++)
{
cout <<"此图像上的关键点总数为"<< keypoints.size() << endl;
cout << "第" << i + 1 << "个关键点的坐标为" << keypoints[i].pt << endl;
cout << "第" << i + 1 << "个关键点的领域直径大小为" << keypoints[i].size << endl;
cout << "第" << i + 1 << "个关键点的角度方向为" << keypoints[i].angle << endl;
cout << "第" << i + 1 << "个关键点鲁棒响应强度为" << keypoints[i].response << endl;
cout << "第" << i + 1 << "个关键点在金字塔的层数为" << keypoints[i].octave<< endl;
cout << "第" << i + 1 << "个关键点的classID分类信息为" << keypoints[i].class_id << endl;
}*/
Mat keypointimg(src.size(), src.type()); //用于绘制DRAW_OVER_OUTIMG类型的特征点
//Mat keypointimg;
//SURF绘制关键点函数api
drawKeypoints(src, keypoints, keypointimg, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);//todo绘制关键点
//void drawKeypoints
//1.InputArray image -- 输入图像,三通道或单通道图像
//2.vector<KeyPoint>& keypoints -- 特征点向量,向量内每一个元素是一个KeyPoint对象,包含了特征点的各种属性信息
//3.InputOutputArray outImage -- 输出图像(特征点绘制的画布图像,可以是原图像 内容决定于第五个参数标识符flags)
//4.const Scalar& color=Scalar::all(-1) -- 关键点的颜色 默认值Scalar::all(-1),表示颜色是随机生成的
//5.int flags = DrawMatchesFlags::DEFAULT -- 特征点的绘制模式,其实就是设置特征点的那些信息需要绘制,那些不需要绘制,有以下几种模式可选:
//DEFAULT:默认的选项 只绘制特征点的坐标点, 显示在图像上就是一个个小圆点, 每个小圆点的圆心坐标都是特征点的坐标(keypoints[i].pt)
//DRAW_OVER_OUTIMG:函数不创建输出的图像, 而是直接在输出图像变量空间绘制, 要求本身输出图像变量就是一个初始化好了的, size与type都是已经初始化好的变量
//NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制
//DRAW_RICH_KEYPOINTS:绘制特征点的时候绘制的是一个个带有方向的圆,这种方法同时显示图像的坐标,size,和方向,是最能显示特征信息的一种绘制方式
//注意
//Scalar::all(-1) 意为随机颜色
//特征点绘制是为了把检测出来的Surf特征点在原图上绘制出来,这一步是为了把特征点直观的显示出来给我们看,跟整个Surf算子的特征提取和匹配流程没关系
imshow("Surf特征关键点位置图像", keypointimg);
return;
}
surf算法原理-简单易懂版本
https://blog.csdn.net/ecnu18918079120/article/details/78195792?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159516249319724846406248%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159516249319724846406248&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-3-78195792.pc_ecpm_v3_pc_rank_v3&utm_term=surf
量子与太极-SURF特征原理检测详解
https://www.cnblogs.com/Jack-Elvis/p/11680776.html
https://blog.csdn.net/YJThuicheng/article/details/83472163
SURF特征API检测详解
https://www.cnblogs.com/long5683/p/9692987.html
https://blog.csdn.net/andylanzhiyong/article/details/84676985
KeyPoint/drawKeypoints/drawMatches等函数内元素的解释
https://www.pianshen.com/article/5607690694/
https://www.cnblogs.com/Jack-Elvis/p/11683893.html