SURF特征点检测--SurfFeatureDetector、SurfDescriptorExtractor和FlannBasedMatcher
阅读原文时间:2021年04月21日阅读:1

一、关于SURF算法

SURF,我们简单介绍一下,英语全称为SpeededUp Robust Features,直译的话就是“加速版的具有鲁棒性的特征“算法,由Bay在2006年首次提出。SURF是尺度不变特征变换算法(SIFT算法)的加速版。一般来说,标准的SURF算子比SIFT算子快好几倍,并且在多幅图片下具有更好的稳定性。SURF最大的特征在于采用了harr特征以及积分图像的概念,这大大加快了程序的运行时间。SURF可以应用于计算机视觉的物体识别以及3D重构中。

OpenCV中关于SURF算法的部分,常常涉及到的是SURF、SurfFeatureDetector、SurfDescriptorExtractor这三个类;

features2d.hpp头文件中,有:typedef SURF SurfFeatureDetector;和typedef SURF SurfDescriptorExtractor;typedef声明是为现有类型创建一个新的名字,类型别名,即SURF类有了两个新名字SurfFeatureDetector以及SurfDescriptorExtractor。

也就是说,SurfFeatureDetector类和SurfDescriptorExtractor类,其实就是SURF类,他们三者等价。

由类的继承,追根溯源可得:

<span style="font-size:14px;"><span style="font-size:14px;">int minHessian = 400;      设定阈值minHessian,关系到最后提取的特征点,这个是surf算法的一个参数;
SurfFeatureDetector detector( minHessian );   
std::vector< KeyPoint > keypoints_object, keypoints_scene;  定义两个KeyPoint 向量keypoints_object, keypoints_scene来存放检测出来的特征点;
detector.detect( gray_image1, keypoints_object );
detector.detect( gray_image2, keypoints_scene );
</span></span>



<span style="font-size:14px;"><span style="font-size:14px;">SurfDescriptorExtractor extractor;声明的surf类提取器;
Mat descriptors_1, descriptors_2;定义两个Mat类矩阵:descriptors_1, descriptors_2,存放提取出来的关键点;
extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );</span></span>

二、drawKeypoints()

作用:绘制关键点。

形式:void drawKeypoints(const Mat&image, const vector& keypoints, Mat& outImage, constScalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT );

参数:

image:const Mat&类型的src,输入图像;
keypoints:const vector&类型的keypoints,根据源图像得到的特征点,它是一个输出参数;
outImage:Mat&类型的outImage,输出图像,其内容取决于第五个参数标识符falgs;
color:const Scalar&类型的color,关键点的颜色,有默认值Scalar::all(-1);
flags:int类型的flags,绘制关键点是的特征标识符,有默认值DrawMatchesFlags::DEFAULT;

三、用BruteForceMatcher类中的函数match来匹配两幅图像的特征向量

相关代码如下:

<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">//实例化一个匹配器
  BruteForceMatcher<L2<float> > matcher;
  std::vector<DMatch > matches;
  //匹配两幅图中的描述子(descriptors)
  matcher.match(descriptors1, descriptors2, matches );</span></span></span>

BruteForceMatcher是由DescriptorMatcher派生出来的一个类,而DescriptorMatcher定义了不同的匹配策略的共同接口。调用match方法后,在其第三个参数输出一个cv::DMatch向量。于是我们定义一个std::vector类型的matches。

四、drawMatches()

作用:从两幅图片中画出发现的匹配的关键点。

形式:void drawMatches(const Mat& img1, const vector& keypoints1, const Mat& img2, const vector& keypoints2, const vector& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector& matchesMask=vector(), int flags=DrawMatchesFlags::DEFAULT );

参数:

img1、img2:两幅源图像;
keypoints1、keypoints2:两幅源图像中的关键点;

matches1to2:从第一幅图像来匹配第二幅图像,即从keypoints1[i]中寻找与keypoints2[i]的对应点;

outImg:输出图像;它的值取决于flags--在图像中绘制的内容;

matchColor:匹配颜色(线和点的颜色),如果matchColor==Scalar::all(-1),颜色随机生成;

singlePointColor:单个关键点的颜色,即没有关键点与之匹配,如果matchColor==Scalar::all(-1),颜色随机生成;

matchesMask:掩码决定画哪个匹配的关键点,如果掩码为空,画出所有的匹配的关键点;

flags:设置绘图功能,可能标志位的值由“DrawMatchesFlags”确定;

五、FlannBasedMatcher

算法更快但是找到的是最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。当然也可以通过调整FlannBasedMatcher的参数来提高匹配的精度或者提高算法速度,但是相应地算法速度或者算法精度会受到影响。

<span style="font-size:14px;"><span style="font-size:14px;">  FlannBasedMatcher matcher;
  std::vector< DMatch > matches;       定义了一个DMatch类向量matches,用来存放descriptors_1, descriptors_2中匹配的关键点
  matcher.match( descriptors_1, descriptors_2, matches );
</span></span>

六、push_back()

算法语言里面的一个函数名,如c++中的vector头文件里面就有这个push_back函数,在vector类中作用为在vector尾部加入一个数据,如:good_matches.push_back( matches[i]);把matches[i]加到向量good_matches的最后一位。

七、findHomography()

作用:寻找两个平面匹配上的关键点的变换。

形式:Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() );

参数:

srcPoints:原始平面中点的坐标,即一个CV_32FC2 or vector 型矩阵;

dstPoints:目标平面中点的坐标,即一个CV_32FC2 or vector 型矩阵;

method:用于计算单应矩阵的方法--0:使用所有点的常规方法;

CV_RANSAC:基于RANSAC的鲁棒方法;

CV_LMEDS:最不平均方法;

ransacReprojThreshold:仅当使用CV_RANSAC方法时,把一双点作为内窗的最大投影误差;

mask:鲁棒方法的可选输出掩码设置;

八、perspectiveTransform()

作用:向量的投影矩阵转换;

形式:void perspectiveTransform(InputArray src, OutputArray dst, InputArray m);

参数:

src:输入2通道或3通道的浮点阵列,每个元素是一个将被转换的2D/3D向量;

dst:与输入有相同大小和类型的输出矩阵;

m:3x3 或4x4的浮点转换矩阵;

<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
using namespace cv;

void readme();

/** @function main */
int main( int argc, char** argv )
{
 if( argc != 1 )
  { readme(); return -1; }

  Mat img_1 = imread("8.jpg", CV_LOAD_IMAGE_GRAYSCALE );
  Mat img_2 = imread("9.jpg", CV_LOAD_IMAGE_GRAYSCALE );

  if( !img_1.data || !img_2.data )
  { std::cout<< " --(!) Error reading images " << std::endl; return -1; }

  //-- Step 1: Detect the keypoints using SURF Detector
  int minHessian = 400;

  SurfFeatureDetector detector( minHessian );

  std::vector<KeyPoint> keypoints_1, keypoints_2;

  detector.detect( img_1, keypoints_1 );
  detector.detect( img_2, keypoints_2 );

  //-- Draw keypoints
  Mat img_keypoints_1; Mat img_keypoints_2;

  drawKeypoints( img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );
  drawKeypoints( img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT );

  //-- Show detected (drawn) keypoints
  imshow("Keypoints 1", img_keypoints_1 );
  imshow("Keypoints 2", img_keypoints_2 );

  waitKey(0);

  return 0;
  }

  /** @function readme */
  void readme()
  { std::cout << " Usage: ./SURF_detector <img1> <img2>" << std::endl; }</span></span></span></span>







<span style="font-size:14px;"><span style="font-size:14px;">#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

void readme();

/** @function main */
int main( int argc, char** argv )
{
  if( argc != 3 )
   { return -1; }

  Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
  Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );

  if( !img_1.data || !img_2.data )
   { return -1; }

  //-- Step 1: Detect the keypoints using SURF Detector
  int minHessian = 400;

  SurfFeatureDetector detector( minHessian );

  std::vector<KeyPoint> keypoints_1, keypoints_2;

  detector.detect( img_1, keypoints_1 );
  detector.detect( img_2, keypoints_2 );

  //-- Step 2: Calculate descriptors (feature vectors)
  SurfDescriptorExtractor extractor;

  Mat descriptors_1, descriptors_2;

  extractor.compute( img_1, keypoints_1, descriptors_1 );
  extractor.compute( img_2, keypoints_2, descriptors_2 );

  //-- Step 3: Matching descriptor vectors with a brute force matcher
  BruteForceMatcher< L2<float> > matcher;
  std::vector< DMatch > matches;
  matcher.match( descriptors_1, descriptors_2, matches );

  //-- Draw matches
  Mat img_matches;
  drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches );

  //-- Show detected matches
  imshow("Matches", img_matches );

  waitKey(0);

  return 0;
  }

 /** @function readme */
 void readme()
 { std::cout << " Usage: ./SURF_descriptor <img1> <img2>" << std::endl; }</span></span>

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章