开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天
1 SURF类相关OpenCV源码剖析
OpenCV中关于SURF算法的部分,常常涉及到的是SURF、SurfFeatureDetector、 SurfDescriptorExtractor 这三个类,这一小节我们就来看看它们究竟是什么来头。
在·······\opencv\sources\modules\nonfree\include\opencv2\nonfree路径下的 features2d.hpp头文件中,可以发现这样两句定义:
typedef SURF SurfFeatureDetector; typedef SURF SurfDescriptorExtractor;
我们都知道,typedef声明是为现有类型创建一个新的名字,类型别名。这就表示,SURF类忽然同时有了两个新名字 SurfFeatureDetector 和 SurfDescriptorExtractor。
也就是说,我们平常使用的 SurfFeatureDetector 类和 SurfDescriptorExtractor 类,其实就是SURF类,它们三者等价。
然后在这两句定义的上方,可以看到SURF类的类声明全貌,在这里由于篇幅原因,只贴出其轮廓。
class CV_EXPORTS_W SURF : public Feature2D
{
// ......
};
可以观察到,SURF类公共继承自Feature2D类,我们再次进行转到定义,可以在路径···\opencv\build\include \opencv2\features2d\features2d.hpp 看到 Feature2D 类的声明。
class CV_EXPORTS_W Feature2D : public FeatureDetector, public DescriptorExtractor
{
//......
};
显然,Feature2D类又是公共继承自FeatureDetector以及 DescriptorExtractor类。 继续刨根问底,看看其父类FeatureDetector 以及 DescriptorExtractor类的定义。
首先是FeatureDetector类。
class CV_EXPORTS_W FeatureDetector:public virtual Algorithm
{
// ......
};
这里,我们看到了以后经常会用到的 detect()方法重载的两个原型,原来是SURF类经过两层的继承,从FeatureDetector类继承而来的。
CV_WRAP void detect(const Mat& image, CV_OUT vector& keypoints,const Mat& mask=Mat())const;
void detect(const vector& images, vector<vector>& keypoints, const vector& masks=vector())const;
同样,看看SURF类的另一个“爷爷”DescriptorExtractor类的声明。
class CV_EXPORTS_W DescriptorExtractor : public virtual Algorithm
{
//......
};
上述代码表明 FeatureDetector 类和 DescriptorExtractor 类都虚继承自 Algorithm基类。
终于,我们找到SURF类“德高望重”的祖先-OpenCV中的Algorithm基类。其原型声明如下。
class CV_EXPORTS_W Algorithm
{
// ......
};
2 绘制关键点:drawKeypoints()函数
因为接下来的示例程序需要用到 drawKeypoints 函数,在这里顺便讲一讲。顾名思义,此函数用于绘制关键点。
C++:
void drawKeypoints(const Mat&image, const vector& keypoints,Mat& outImage,constscalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT)
- 第一个参数,const Mat&类型的src,输入图像。
- 第二个参数,const vector<KeyPoint>&类型的keypoints,根据源图像得到的 特征点。它是一个输出参数。
- 第三个参数,Mat&类型的outImage,输出图像,其内容取决于第五个参数标识符falgs。
- 第四个参数,const Scalar&类型的 color,关键点的颜色,有默认值Scalar::all(-1)。
- 第五个参数,int 类型的 flags,绘制关键点的特征标识符,有默认值DrawMatchesFlags::DEFAULT。可以在下面这个结构体中选取值。
struct DrawMatchesFlags
{
enum
{
DEFAULT=0,//创建输出图像矩阵(使用Mat::create)。使用现存的输出图像绘制匹配对和特征点。且对每一个关键点,只绘制中间点
DRAW_OVER_OUTIMG=1,//不创建输出图像矩阵,而是在输出图像上绘制匹配对
NOT_DRAW_SINGLE_POINTS=2,//单点特征点不被绘制
DRAW_RICH_KEYPOINTS=4//对每一个关键点,绘制带大小和方向的关键点圆圈
};
};
3 KeyPoint类
KeyPoint类是一个为特征点检测而生的数据结构,用于表示特征点。
class KeyPoint
{
Point2f pt;//坐标
float size;//特征点邻域直径 float
angle;//特征点的方向,值为[零,三百六十),负值表示不使用float
response;
int octave;//特征点所在的图像金字塔的组int class_id;//用于聚类的id
}
4 示例程序:SURF特征点检测
这个示例程涉及到如下三个方面:
- 使用FeatureDetector接口来发现感兴趣点。
- 使用 SurfFeatureDetector以及其函数 detect 来实现检测过程
- 使用函数drawKeypoints绘制检测到的关键点。
详细注释的源代码如下。
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//---------------------------------------------------------------------------------------
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include <iostream>
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
//【0】改变console字体颜色
system("color 2F");
//【1】载入源图片并显示
Mat srcImage1 = imread("1.jpg", 1 );
Mat srcImage2 = imread("2.jpg", 1 );
if( !srcImage1.data || !srcImage2.data )//检测是否读取成功
{ printf("读取图片错误,请确定目录下是否有imread函数指定名称的图片存在~! \n"); return false; }
imshow("原始图1",srcImage1);
imshow("原始图2",srcImage2);
//【2】定义需要用到的变量和类
int minHessian = 400;//定义SURF中的hessian阈值特征点检测算子
SurfFeatureDetector detector( 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("特征点检测效果图1", img_keypoints_1 );
imshow("特征点检测效果图2", img_keypoints_2 );
waitKey(0);
return 0;
}
原图
效果图