图像处理之SURF特征提取

351 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天

1 概述

通过上一节SURF相关内容的讲解,我们已经对SURF算法有了一定的理解。SURF 算法为每个检测到的特征定义了位置和尺度,尺度值可用于定义围绕特征点的窗口大小。不论物体的尺度在窗口是什么样的,都将包含相同的视觉信息,这些信息用于表示特征点以使得它们与众不同。

在特征匹配中,特征描述子通常用于N维向量,在光照不变以及少许透视变形的情况下很理想。另外,优质的描述子可以通过简单的距离测量进行比较,比如欧氏距离。因此,它们在特征匹配算法中,用处是很大的。

在OpenCV 中,使用 SURF 进行特征点描述主要是 drawMatches 方法和 BruteForceMatcher类的运用,下面让我们一起来认识它们。

2 绘制匹配点:drawMatches()函数

drawMatches 用于绘制出相匹配的两个图像的关键点,它有以下两个版本的C++函数原型。

C++:

void drawMatches (

const Mat& img1,

constvector& keypoints1,

const Mat& img2,

constvector& keypoints2,

constvector& matches1to2,

Mat& outImg,

const Scalar&matchColor=Scalar::all(-1),

const Scalar&singlePointColor=Scalar::all(-1),

const vector&matchesMask=vector(),

intflags=DrawMatchesFlags::DEFAULT

)

C++:

void drawMatches (

const Mat& img1,

constvector& keypoints1,

const Mat& img2,

constvector& keypoints2,

const vector<vector>&matches1to2,

Mat& outImg,

const Scalar&matchColor=Scalar::all(-1),

const Scalar&singlePointColor=Scalar::al1(-1),

constvector<vector>&matchesMask=vector<vector>(),

intflags=DrawMatchesFlags::DEFAULT

)

除了第五个参数matcheslto2和第九个参数 matchesMask 有细微的差别以外,两个版本的基本上相同。下面统一讲解。

  • 第一个参数,const Mat&类型的img1,第一幅源图像。
  • 第二个参数,const vector<KeyPoint>&类型的keypoints1,根据第一幅源图 像得到的特征点,它是一个输出参数。
  • 第三个参数,const Mat&类型的img2,第二幅源图像。
  • 第四个参数,constvector<KeyPoint>&类型的keypoints2,根据第二幅源图 像得到的特征点,它是一个输出参数。
  • 第五个参数,matcheslto2,第一幅图像到第二幅图像的匹配点,即表示每一个图1中的特征点都在图2中有一一对应的点、
  • 第六个参数,Mat&类型的outImg,输出图像,其内容取决于第五个参数标识符falgs。
  • 第七个参数,const Scalar&类型的matchColor,匹配的输出颜色,即线和关 键点的颜色。它有默认值Scalar::all(—1),表示颜色是随机生成的。
  • 第八个参数,const Scalar&类型的singlePointColor,单一特征点的颜色,它 也表示随机生成颜色的默认值 Scalar::all(—1)。
  • 第九个参数,matchesMask,确定哪些匹配是会绘制出来的掩膜,如果掩膜为空,表示所有匹配都进行绘制。
  • 第十个参数,int类型的 flags,特征绘制的标识符,有默认值DrawMatchesFlags::DEFAULT。可以在如下这个DrawMatchesFlags 结构体 中选取值:
struct DrawMatchesFlags
{
    enum 
    {
        DEFAULT0,//创建输出图像矩阵(使用Mat::create)。使用现存的输出图像绘制匹配对和特征点。且对每一个关键点,只绘制中间点
        DRAW_OVER_OUTIMG=1,//不创建输出图像矩阵,而是在输出图像上绘制匹配对
        NOT_DRAW_SINGLE_POINTS=2,//单点特征点不被绘制
        DRAW_RICH_KEYPOINTS=4//对每一个关键点,绘制带大小和方向的关键点圆圈
    };
};

3 BruteForceMatcher类源码分析

接下来,我们看看本节中会用到的 BruteForceMatcher 类的源码分析,BruteForceMatcher 类用于进行暴力匹配相关的操作。

在......\opencv\sources modules\legacy\includelopencv2\ legacy\legacy.hpp 路径下,可以找到 BruteForceMatcher类的定义。

template<class Distance>
class CV_EXPORTS BruteForceMatcher: publicBFMatcher
{
    public:
        BruteForceMatcher(Distance d =Distance())
            : BFMatcher (Distance::normType,false)
        {
            (void)d;
        }
        virtual ~BruteForceMatcher(){}
};

其公共继承自BFMatcher类。

而BFMatcher类位于···\opencv\sources\modules\features2d\include\opencv2\features2d\features2d.hpp 路径之下,

我们一起看看其代码。

class CV_EXPORTS_W BFMatcher: publicDescriptorMatcher
{
    //......
};

发现公共其继承自 DescriptorMatcher 类,再次进行溯源,在D:\Program Files(x86)\opencv\sources\modules\features2d\include\opencv2\features2d\features2d. hpp路径下找到DescriptorMatcher类的定义。

class CV_EXPORTS W DescriptorMatcher :public Algorithm 
{
    // 
};

可以发现,DescriptorMatcher 类和之前我们讲到 FeatureDetector类和 DescriptorExtractor类一样,都是继承自它们“德高望重的祖先”Algorithm基类的。

而用BruteForceMatcher类时用到最多的match方法,是它从DescriptorMatcher 类那里继承而来。定义如下。

//为各种描述符找到一个最佳的匹配(若掩膜为空)
CV_WRAP void match( const Mat& queryDescriptors, const Mat&trainDescriptors,
    CV_OUTvector<DMatch>&matches,const Mat& mask=Mat())const; 

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天