SURF的原理我不再叙述,本文主要是应用SURF进行两张图片的关键点匹配。
opencv二维特征点匹配常见的两种办法Brute Force匹配和FLANN匹配,分别对应BruteForceMatcher类和FlannBasedMatcher类。二者的区别在于BFMatcher总是尝试所有可能的匹配,从而使得它总能够找到最佳匹配,这也是Brute Force(暴力法)的原始含义。而FlannBasedMatcher中FLANN的含义是Fast Library forApproximate Nearest Neighbors,从字面意思可知它是一种近似法,算法更快但是找到的是最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。当然也可以通过调整FlannBasedMatcher的参数来提高匹配的精度或者提高算法速度,但是相应地算法速度或者算法精度会受到影响。
程序参考《OpenCV3编程入门》配套示例程序:SURF特征点匹配,对该程序进行了一些修改,主要有以下功能:
1.通过滚动条来实现hessian阈值的动态调节;
2.通过滚动条来实现绘点方式的选择;
3.通过滚动条来实现匹配方式的选择;
4.显示Brute Force和FLANN这两种匹配方法的耗时;
代码如下:
//--------------------------------------【程序说明】-------------------------------------------
// 程序说明:参考《OpenCV3编程入门》配套示例程序90和91
// 程序描述:SURF特征描述及匹配
// 开发测试所用操作系统: Windows 7 32bit
// 开发测试所用IDE版本:Visual Studio 2010
// 开发测试所用OpenCV版本: 2.4.10
//------------------------------------------------------------------------------------------------
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include
#include
#include
#include
using namespace cv;
using namespace std;
//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数的声明
//-----------------------------------------------------------------------------------------------
static void ShowHelpText( );//输出帮助文字
void on_surf(int,void*);//回调函数
//-----------------------------------【全局变量声明部分】--------------------------------------
Mat srcImage1,srcImage2,srcImage3;
int g_minHessian = 70;//定义SURF中的hessian阈值特征点检测算子,阈值为:g_minHessian*10
int g_flags=0; //绘点方式
int g_method=0; //匹配的方法
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main( )
{
//【0】改变console字体颜色
system("color 1F");
//【0】显示欢迎和帮助文字
ShowHelpText( );
//【1】载入素材图
srcImage1 = imread("1.jpg",1);
srcImage2 = imread("2.jpg",1);
if( !srcImage1.data || !srcImage2.data )
{
printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n");
return false;
}
hconcat(srcImage1,srcImage2,srcImage3);//等同于srcImage3=[srcImage1 srcImage2],将两张原图前后拼接到一起
imshow("原始图合并",srcImage3);
createTrackbar("阈值/10","原始图合并",&g_minHessian,150,on_surf);
createTrackbar("绘点方式","原始图合并",&g_flags,2,on_surf);
createTrackbar("匹配方法","原始图合并",&g_method,1,on_surf);
on_surf(0,0);
waitKey(0);
return 0;
}
void on_surf(int,void*)
{
//【2】使用SURF算子检测关键点
SURF detector( g_minHessian*10 );//定义一个SurfFeatureDetector(SURF) 特征检测类对象
vector keyPoint1, keyPoints2;//vector模板类,存放任意类型的动态数组
//【3】调用detect函数检测出SURF特征关键点,保存在vector容器中
detector.detect( srcImage1, keyPoint1 );
detector.detect( srcImage2, keyPoints2 );
//【4】计算描述符(特征向量)
SURF extractor;
Mat descriptors1, descriptors2;
extractor.compute( srcImage1, keyPoint1, descriptors1 );
extractor.compute( srcImage2, keyPoints2, descriptors2 );
//开始计算处理时间
double dTime = (double)getTickCount();
//选择不同方法进行匹配
vector< DMatch > matches;
if(g_method==0)
{
//【5】使用BruteForce进行匹配
// 实例化一个匹配器
BruteForceMatcher< L2 > matcher;
//匹配两幅图中的描述子(descriptors)
matcher.match( descriptors1, descriptors2, matches );
}
else
{
//【5.1】采用FLANN算法匹配描述符向量
FlannBasedMatcher matcher;
vector< DMatch > Original_matches;
matcher.match( descriptors1, descriptors2, Original_matches );
double max_dist = 0; double min_dist = 100;
//【5.2】快速计算关键点之间的最大和最小距离
for( int i = 0; i < descriptors1.rows; i++ )
{
double dist = Original_matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
//输出距离信息
printf("> FLANN算法的最大距离(Max dist) : %f \n", max_dist );
printf("> FLANN算法的最小距离(Min dist) : %f \n", min_dist );
//【5.3】存下符合条件的匹配结果(即其距离小于2* min_dist的),使用radiusMatch同样可行
for( int i = 0; i < descriptors1.rows; i++ )
{
if( Original_matches[i].distance < 2*min_dist )
{ matches.push_back( Original_matches[i]); }
}
}
//计算匹配处理时间
dTime = (double)getTickCount() - dTime;
printf( ">\t方法%d的处理时间 = %gms\n", g_method,dTime*1000./getTickFrequency() );//将匹配用时输出到窗口中
//【6】绘制从两个图像中匹配出的关键点
Mat imgMatches;
drawMatches( srcImage1, keyPoint1, srcImage2, keyPoints2, matches, imgMatches ,Scalar::all(-1),Scalar::all(-1),Mat(), g_flags*2 );//进行绘制
//【7】显示效果图
imshow("匹配图", imgMatches );
}
//-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
//输出OpenCV版本
printf("\t\t\t 当前使用的OpenCV版本为:" CV_VERSION );
//输出一些帮助信息
printf("\n\n\t【SURF特征点描述】程序\n\n");
printf("\n\t该程序带有三个滚动条\n\n");
printf("\n\t滚动条1:SURF的hessian的阈值(实际阈值为该值的10倍)\n\n");
printf("\n\t滚动条2:绘制关键点的不同方式:\n\n"
"\t\t0——对应DEFAULT方式\n\n"
"\t\t1——对应NOT_DRAW_SINGLE_POINTS方式\n\n"
"\t\t2——对应DRAW_RICH_KEYPOINTS方式\n\n");
printf("\n\t滚动条3:匹配关键点的不同方法:\n\n"
"\t\t0——对应Brute Force匹配方式\n\n"
"\t\t1——对应FLANN匹配方式\n\n");
printf( "\n\t按键操作说明: \n\n"
"\t\t键盘按键任意键- 退出程序\n\n" );
}
效果图如下:
1.提示信息及原图
2.Brute Force匹配
3.FLANN匹配
4.采用DRAW_RICH_KEYPOINTS绘点方式的FLANN匹配