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;
}

运行结果为:

相关文章
|
10月前
|
并行计算 算法 数据可视化
基于OpenCV C++实现的光流法目标检测
基于OpenCV C++实现的光流法目标检测
|
计算机视觉
Opencv学习笔记(八):如何通过cv2读取视频和摄像头来进行人脸检测(jetson nano)
如何使用OpenCV库通过cv2模块读取视频和摄像头进行人脸检测,并提供了相应的代码示例。
569 1
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
470 4
2023/11/10学习记录-C/C++对称分组加密DES
|
XML 机器学习/深度学习 人工智能
使用 OpenCV 和 Python 轻松实现人脸检测
本文介绍如何使用OpenCV和Python实现人脸检测。首先,确保安装了OpenCV库并加载预训练的Haar特征模型。接着,通过读取图像或视频帧,将其转换为灰度图并使用`detectMultiScale`方法进行人脸检测。检测到的人脸用矩形框标出并显示。优化方法包括调整参数、多尺度检测及使用更先进模型。人脸检测是计算机视觉的基础技术,具有广泛应用前景。
842 10
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
273 16
|
机器学习/深度学习 计算机视觉
目标检测笔记(六):如何结合特定区域进行目标检测(基于OpenCV的人脸检测实例)
本文介绍了如何使用OpenCV进行特定区域的目标检测,包括人脸检测实例,展示了两种实现方法和相应的代码。
639 1
目标检测笔记(六):如何结合特定区域进行目标检测(基于OpenCV的人脸检测实例)
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
683 6
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
3324 3
|
存储 计算机视觉 C++
在C++中实现Armadillo库与OpenCV库之间的数据格式转换
在C++中实现Armadillo库与OpenCV库之间的数据格式转换是一项常见且实用的技能。上述步骤提供了一种标准的方法来进行这种转换,可以帮助开发者在两个库之间高效地转移和处理数据。虽然转换过程相对直接,但开发者应留意数据类型匹配和性能优化等关键细节。
306 12
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
265 1