前言
1.在目标检测、识别,匹配的应用中,特征点的提取是不可缺少的部分的步骤,在之前博文我演示了HOG特性提取与使用HOG与SVM训练自己的样本进行行人检测。
2.我的编程环境是Windows 7 64位,IDE是VS2015,配置了OpenCV3.3与OpenCV_Contrib,Boost 1.66,其中Boost是用来操作文件和目录用的,是于如果配置以上的环境,可以看我之前写的博文。
一、SURF概述
1.SIFT、SURF、ORB、HOG、LBP等都是特征点检测及匹配基本算法,所谓特征检测,就是不管所检测的目标如何旋转,图像中所体现出的目标不管远近,特征都是是变,这就是特征检测的两个特性,就是旋转不变性与尺度不变性。
2.SIFT是目前应用最为的广泛的特征提取及匹配的算法,SIFT具有旋转、尺度、平移、视角及光照不变性;SIFT特征对参数调整鲁棒性比较好,在进行特征描述时,可以根据场景需要可调整适宜的特征点数量,以便进行特征分析。但SITF这个算法有个缺点,就是是如果不借助硬件加速或专门的图像处理器很难达到实时的效果。
3.2006年在ECCV大会Herbert Bay等人提出,对SIFT的改进,就是SURF,SURF算法对物体的旋转、光照等情况有较好的鲁棒性,且比SIFT算法而言计算速度更快。SURF算法使用海森矩阵(Hessian)的行列式值作特征点响应侦测并用积分图加速运算;SURF 的描述子基于 2D 离散小波变换响应Harr小波并且有效地利用了积分图,这大大的加速了程序的运行时间。SURF算法不仅保持了SIFT算法的尺度不变和旋转不变的特性,而且对光照变化和放射变化同样具有很强的鲁棒性。
4.通过SURF算法检测到的特征点其描述符包含了这个点的位置和尺度信息,故对两幅图片进行匹配时可以通过两幅图中特征点匹配对进行匹配。即使物体位置和光照的改变也能够有良好的匹配效果。
二、SURF 演示
1.关于SURF构建的流程,可以查看一些相关的论文和介绍博客,我这里只写主要的步骤,因为OpenCV官方已经封装相关的类,我们拿出来应用就可以,是于低层的实现,参考OpenCV官方源码。
(1).构建Hessian(黑塞矩阵),计算特征值α ,生成所有的兴趣点,用于特征的提取。
(2).构建尺度空间.
(3)特征点定位
(4) 特征点主方向分配
(5) 生成特征点描述子
(6). 特征点匹配
2.代码演示
SURF特征点提取与匹配代码
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\xfeatures2d\nonfree.hpp>
#include <opencv2\xfeatures2d.hpp>
#include <opencv2\ml\ml.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\features2d.hpp>
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
using namespace ml;
int main(void)
{
//图像绝对路径
string image1_path = "C:/Users/matt/Desktop/face/4.jpg";
string image2_path = "C:/Users/matt/Desktop/face/5.jpg";
//读取图像
Mat src_image1 = imread(image1_path, 1);
Mat src_image2 = imread(image2_path, 1);
//判断是否为空
if (src_image1.empty() || src_image2.empty())
{
std::cerr << "读取图片出错!" << endl;
return -1;
}
namedWindow("srcImage1", WINDOW_NORMAL);
namedWindow("srcImage2", WINDOW_NORMAL);
imshow("srcImage1", src_image1);
imshow("srcImage2", src_image2);
int minHessian = 100;
Ptr<SURF> detector = SURF::create(minHessian);
vector<KeyPoint> key_points_1, key_points_2;
Mat dst_image1, dst_image2;
//检测特征关键点
detector->detectAndCompute(src_image1, Mat(), key_points_1, dst_image1);
detector->detectAndCompute(src_image2, Mat(), key_points_2, dst_image2);
namedWindow("keyPoint1", WINDOW_NORMAL);
namedWindow("keyPoint2", WINDOW_NORMAL);
//画出特征点
Mat img_keypoints_1, img_keypoints_2;
drawKeypoints(src_image1, key_points_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
drawKeypoints(src_image2, key_points_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
//显示所画的特征点
imshow("keyPoint1", img_keypoints_1);
imshow("keyPoint2", img_keypoints_2);
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("FlannBased");
vector<DMatch>mach;
matcher->match(dst_image1, dst_image2, mach);
double Max_dist = 0;
double Min_dist = 100;
for (size_t i = 0; i < dst_image1.rows; i++)
{
double dist = mach[i].distance;
if (dist < Min_dist)
{
Min_dist = dist;
}
else if (dist > Max_dist)
{
Max_dist = dist;
}
}
std::cout << "最短距离:" << Min_dist << endl;
std::cout << "最长距离:" << Max_dist << endl;
vector<DMatch>goodmaches;
for (size_t i = 0; i < dst_image1.rows; i++)
{
if (mach[i].distance < 2 * Min_dist)
{
goodmaches.push_back(mach[i]);
}
}
Mat img_maches;
drawMatches(src_image1, key_points_1, src_image2, key_points_2, goodmaches, img_maches);
for (int i = 0; i < goodmaches.size(); i++)
{
std::cout << "符合条件的匹配:" << goodmaches[i].queryIdx << "--" << goodmaches[i].trainIdx << endl;
}
namedWindow("matching", WINDOW_NORMAL);
imshow("matching", img_maches);
waitKey(0);
system("pause");
return 0;
}
3.运行结果
(1)原始图与关键点
(2)匹配的效果
结语
1.以上只是SURF特征提取和匹配的演示,如果想了解SURF在项目中的应用,可以看我另一个博文,使用SURF、SVM、BOW来对图像进行分类。
2.关于工程的源码,运行程序时的bug,都可以加这个群(487350510)互相讨论学习。