Opencv(C++)学习系列---特征点检测和匹配

简介: Opencv(C++)学习系列---特征点检测和匹配

关于特征检测和匹配的具体原理会在后续的文章中具体讲解,本文主要介绍Opencv实现的简单过程:

第一步:定义特征检测器(SIFT,SURF,ORB等)。

第二步:对图像中特征点进行检测,并将特征点存储在Keypoints中。

第三步:提取特征点的描述信息。

第四步:定义特征匹配器(特征匹配的方法主要有两种分别为暴力匹配BFmatch和FlannBased)。

第五步:过滤掉较差的匹配点位(一般根据临近两点的距离进行过滤)

主要是根据DMatch中的distance进行过滤,对于distance可以抽象理解为匹配的分值,distance越小说明检测点的相似度越高,效果越好。

第六步:对匹配的特征点显示。

代码1(未滤波,只限制筛选点数为20)

#include <iostream>
#include <opencv2/opencv.hpp>  
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;  //包含cv命名空间
using namespace std;
using namespace xfeatures2d;
int main() {
  system("color 2E");
  //载入图片
  Mat src1 = imread("E:\\乔大花进度\\11-18\\sift特征检测和匹配\\3.jpg",1);
  Mat src2 = imread("E:\\乔大花进度\\11-18\\sift特征检测和匹配\\4.jpg", 1);
  //显示原图
  imshow("原图1",src1);
  imshow("原图2", src2);
  //定义变量
  vector<KeyPoint> keypoints1, keypoints2;//定义检测的特征点存储容器
  Mat descriptors1,descriptors2;//定义特征点描述信息为Mat类型
  Mat result_img;//匹配结果图片
  //创建sift特征检测器实例
  //将SIFT可以换位SURF、ORB
  Ptr<SIFT>detector = SIFT::create();
  //提取特征点
  detector->detect(src1,keypoints1,noArray());
  detector->detect(src2, keypoints2, Mat());
  //获取特征点的描述信息=>特征向量
  detector->compute(src1,keypoints1,descriptors1);
  detector->compute(src2, keypoints2, descriptors2);
  //定义匹配器的实例化=>方法为暴力匹配法
  Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);//create中的参数可以填string FlannBased等匹配方法
  //第二种实例化方法
  //BFMatcher matcher;
  //进行暴力匹配
  vector<DMatch> matches;
  //第一个参数为queryDescription为目标,第二个参数为trainDescription模板
  matcher->match(descriptors1,descriptors2,matches);
  //限制特征点匹配数量=》只匹配前20个较好的特征点
  int num = 20;
  nth_element(matches.begin(), matches.begin()+num,matches.end());
  //vector去除20以后的元素
  matches.erase(matches.begin()+num,matches.end());
  
  //输出关键点和匹配结果
  //其中右侧图为trainDescription模板,左侧图为queryDescription目标
  //左图中的点与右图中进行匹配对应
  drawMatches(src1,keypoints1,src2,keypoints2, matches,result_img);
  drawKeypoints(src1,keypoints1,src1);
  drawKeypoints(src2,keypoints2,src2);
  
  imshow("匹配结果",result_img);
  imshow("特征点1",src1);
  imshow("特征点2",src2);
  waitKey(0);
  system("pause");
  return 0;
}

运行结果为:

代码2(通过距离进行滤波)

#include <iostream>
#include <opencv2/opencv.hpp>  
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;  //包含cv命名空间
using namespace std;
using namespace xfeatures2d;
int main() {
  system("color 2E");
  //载入图片
  Mat src1 = imread("E:\\乔大花进度\\11-18\\sift特征检测和匹配\\3.jpg",1);
  Mat src2 = imread("E:\\乔大花进度\\11-18\\sift特征检测和匹配\\4.jpg", 1);
  //显示原图
  imshow("原图1",src1);
  imshow("原图2", src2);
  //定义变量
  vector<KeyPoint> keypoints1, keypoints2;//定义检测的特征点存储容器
  Mat descriptors1,descriptors2;//定义特征点描述信息为Mat类型
  Mat result_img;//匹配结果图片
  //创建sift特征检测器实例
  //将SIFT可以换位SURF、ORB
  Ptr<SIFT>detector = SIFT::create();
  //提取特征点
  detector->detect(src1,keypoints1,noArray());
  detector->detect(src2, keypoints2, Mat());
  //获取特征点的描述信息=>特征向量
  detector->compute(src1,keypoints1,descriptors1);
  detector->compute(src2, keypoints2, descriptors2);
  //定义匹配器的实例化=>方法为暴力匹配法
  Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);//create中的参数可以填string FlannBased等匹配方法
  //第二种实例化方法
  //BFMatcher matcher;
  //进行暴力匹配
  vector<DMatch> matches;
  //第一个参数为queryDescription为目标,第二个参数为trainDescription模板
  matcher->match(descriptors1,descriptors2,matches);
  //限制特征点匹配数量=》只匹配前20个较好的特征点
  int num = 20;
  nth_element(matches.begin(), matches.begin()+num,matches.end());
  //vector去除20以后的元素
  matches.erase(matches.begin()+num,matches.end());
  
  double Max_distance = matches[1].distance;
  double Min_distance = matches[1].distance;
  vector<DMatch> goodfeatrues;
  
  //根据特征点的距离去筛选
  for (int i = 0; i < matches.size(); i++)
  {
    double dist = matches[i].distance;
    if (dist>Max_distance)
    {
      Max_distance = dist;
    }
    if (dist<Min_distance)
    {
      Min_distance = dist;
    }
    
  }
  cout << "匹配点的最大距离:" << Max_distance << endl;
  cout << "匹配点的最小距离:" << Min_distance << endl;
  //M为距离阈值,M越大点数越多
  double M = 1.3;
  for (int  i = 0; i < matches.size(); i++)
  {
    double dist = matches[i].distance;
    if (dist<M*Min_distance)    {
      goodfeatrues.push_back(matches[i]);
    }
  }
  cout << "最终选取特征点的数量为:" << matches.size() << endl;
  //输出关键点和匹配结果
  //其中右侧图为trainDescription模板,左侧图为queryDescription目标
  //左图中的点与右图中进行匹配对应
  drawMatches(src1,keypoints1,src2,keypoints2, goodfeatrues,result_img);
  drawKeypoints(src1,keypoints1,src1);
  drawKeypoints(src2,keypoints2,src2);
  
  imshow("匹配结果",result_img);
  imshow("特征点1",src1);
  imshow("特征点2",src2);
  waitKey(0);
  system("pause");
  return 0;
}

运行结果为:

代码3(通过knnMatch匹配,可以通过对distance设置阈值进行滤波,效果最好)

#include <iostream>
#include <opencv2/opencv.hpp>  
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;  //包含cv命名空间
using namespace std;
using namespace xfeatures2d;
int main() {
  system("color 2E");
  //载入图片
  Mat src1 = imread("E:\\乔大花进度\\11-18\\sift特征检测和匹配\\3.jpg",1);
  Mat src2 = imread("E:\\乔大花进度\\11-18\\sift特征检测和匹配\\4.jpg", 1);
  //显示原图
  imshow("原图1",src1);
  imshow("原图2", src2);
  //定义变量
  vector<KeyPoint> keypoints1, keypoints2;//定义检测的特征点存储容器
  Mat descriptors1,descriptors2;//定义特征点描述信息为Mat类型
  Mat result_img;//匹配结果图片
  //创建sift特征检测器实例
  //将SIFT可以换位SURF、ORB
  Ptr<SIFT>detector = SIFT::create();
  //提取特征点
  detector->detect(src1,keypoints1,noArray());
  detector->detect(src2, keypoints2, Mat());
  //获取特征点的描述信息=>特征向量
  detector->compute(src1,keypoints1,descriptors1);
  detector->compute(src2, keypoints2, descriptors2);
  //定义匹配器的实例化=>方法为暴力匹配法
  Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);//create中的参数可以填string FlannBased等匹配方法
  //第二种实例化方法
  //BFMatcher matcher;
  //进行暴力匹配
  vector<DMatch> matches;
  vector<Mat>train_desc(1, descriptors2);
  matcher->add(train_desc);
  matcher->train();
  vector<vector<DMatch>> matchpoints;
  matcher->knnMatch(descriptors1,matchpoints,2);
  vector<DMatch> goodfeatur;
  for (int i = 0; i < matchpoints.size(); i++)
  {
    if (matchpoints[i][0].distance<0.15*matchpoints[i][1].distance)
    {
      goodfeatur.push_back(matchpoints[i][0]);
    }
  }
  cout << "筛选后的特征点数量为: " << goodfeatur.size() << endl;
  //输出关键点和匹配结果
  //其中右侧图为trainDescription模板,左侧图为queryDescription目标
  //左图中的点与右图中进行匹配对应
  drawMatches(src1,keypoints1,src2,keypoints2, goodfeatur,result_img);
  drawKeypoints(src1,keypoints1,src1);
  drawKeypoints(src2,keypoints2,src2);
  
  namedWindow("匹配结果",WINDOW_NORMAL);
  resizeWindow("匹配结果",500,500);
  imshow("匹配结果",result_img);
  waitKey(0);
  system("pause");
  return 0;
}

运行结果为:

相关文章
|
3天前
|
C++
c++的学习之路:27、红黑树
c++的学习之路:27、红黑树
32 4
|
3天前
|
Java C++
C++的学习之路:21、继承(2)
C++的学习之路:21、继承(2)
18 0
|
3天前
|
编译器 C语言 C++
c++的学习之路:19、模板
c++的学习之路:19、模板
33 0
|
3天前
|
设计模式 存储 Android开发
c++的学习之路:18、容器适配器与反向迭代器
c++的学习之路:18、容器适配器与反向迭代器
21 0
|
3天前
|
存储 算法 C++
c++的学习之路:17、stack、queue与priority_queue
c++的学习之路:17、stack、queue与priority_queue
31 0
|
3天前
|
存储 C++ 容器
c++的学习之路:26、AVL树
c++的学习之路:26、AVL树
29 0
|
3天前
|
编译器 C++
c++的学习之路:22、多态(1)
c++的学习之路:22、多态(1)
21 0
c++的学习之路:22、多态(1)
|
3天前
|
安全 编译器 程序员
c++的学习之路:20、继承(1)
c++的学习之路:20、继承(1)
31 0
|
3天前
|
编译器 C++
【C++】继续学习 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂… 所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
8 1
|
3天前
|
算法 安全 程序员
【C++】STL学习之旅——初识STL,认识string类
现在我正式开始学习STL,这让我期待好久了,一想到不用手撕链表,手搓堆栈,心里非常爽
16 0