opencv中fast特征匹配_SLAM从0到1——5. ORB特征提取及特征匹配

2756445bcff85e656e1078688de0e99f.png

「本文演示了使用OpenCV对两帧图像进行ORB特征提取和特征匹配,对程序中出现的知识点作了整理总结,一如往常在程序中作了非常详尽的注释」

对两幅图像进行特征提取,可选的特征点种类有很多,不同的特征点计算方式和提取方式也各不相同。

本案例中采用的是ORB特征点,其关键点是改进的FAST角点,改进之处在于计算了特征点的主方向进而增加了旋转不变特性;描述子采用的是改进的BRIEF描述子,改进之处在于使用了之前计算的关键点方向信息。

我在公开数据集(here)中选取了两张图像:

e863e0e168994523f4605151e1855bad.png

b4cd9ac4807c495b935c3299453252a9.png

对以上两张图像分步进行角点检测、描述子计算、特征点匹配、匹配点筛选、绘制匹配结果,得到了筛选匹配关键点后的图像:

b740d0c9761dd0e55b0ffbe7c4b6f02c.png

在实现过程中主要学习了以下的知识点(部分列出):

  1. cv::assert()计算括号内的表达式,如果表达式为FALSE (或0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。
  2. opencv中keypoint类的默认构造函数为:CV_WRAP KeyPoint() : pt(0,0), size(0), angle(-1), response(0), octave(0), class_id(-1) {} 其中pt(x,y):关键点的点坐标; size():该关键点邻域直径大小;angle:角度,表示关键点的方向,值为[0,360),负值表示不使用。 response:响应强度,选择响应最强的关键点; octacv:从哪一层金字塔得到的此关键点。 class_id:当要对图片进行分类时,用class_id对每个关键点进行区分,默认为-1。
  3. “Ptr<FeatureDetector> detector = ”等价于 “FeatureDetector * detector =”。Ptr是OpenCV中使用的智能指针模板类,可以轻松管理各种类型的指针。
  4. 特征检测器FeatureDetetor是虚类,通过定义FeatureDetector的对象可以使用多种特征检测及匹配方法,通过create()函数调用。
  5. 描述子提取器DescriptorExtractor是提取关键点的描述向量类抽象基类。描述子匹配器DescriptorMatcher用于特征匹配,"BruteForce-Hamming"表示使用汉明距离进行匹配。
  6. computer()计算关键点的描述子向量(注意思考参数设置的合理性)。
  7. DMatch是匹配关键点描述子 类, matches用于存放匹配项。
  8. auto 可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型
  9. minmax_element()返回指向范围内最小和最大元素的一对迭代器。参数1 2为起止迭代器范围,参数3是二进制函数,该函数接受范围内的两个元素作为参数,并返回可转换为bool的值。返回的值指示作为第一个参数传递的元素是否小于第二个,该函数不得修改其任何参数。
  10. drawMatches用于绘制两幅图像的匹配关键点。参数1是第一个源图像,参数2是其关键点数组;参数3是第二张原图像,参数4是其关键点数组。参数5是两张图像的匹配关键点数组,参数6用于存放函数的绘制结果。
  11. …………

关于此案例的主程序和CMakeLists.txt均在下方。

主程序:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <chrono>

using namespace std;
using namespace cv;

int main(int argc, char **argv){
    if(argc != 3){     //注意这里的参数为3,是因为argv[0]是指向输入的程序路径及名称:./orb_cv
        cout << "usage: feature_extraction img1 img2 "<< endl;
        return 1;
    }
   //读取图像
   Mat img_1 = imread(argv[1], CV_LOAD_IMAGE_COLOR);
   Mat img_2 = imread(argv[2], CV_LOAD_IMAGE_COLOR);
   assert(img_1.data != nullptr && img_2.data != nullptr);
   //在程序运行时cv::assert()计算括号内的表达式,如果表达式为FALSE (或0), 程序将报告错误,并终止执行。
   //如果表达式不为0,则继续执行后面的语句。

   //初始化
   vector<KeyPoint> keypoints_1, keypoints_2;   //关键点/角点
   /**
   opencv中keypoint类的默认构造函数为:
   CV_WRAP KeyPoint() : pt(0,0), size(0), angle(-1), response(0), octave(0), class_id(-1) {}
   pt(x,y):关键点的点坐标; // size():该关键点邻域直径大小; // angle:角度,表示关键点的方向,值为[0,360),负值表示不使用。
   response:响应强度,选择响应最强的关键点;   octacv:从哪一层金字塔得到的此关键点。
   class_id:当要对图片进行分类时,用class_id对每个关键点进行区分,默认为-1。
   **/
   Mat descriptors_1, descriptors_2;      //描述子
   //创建ORB对象,参数为默认值
   Ptr<FeatureDetector> detector = ORB::create();
   Ptr<DescriptorExtractor> descriptor = ORB::create();
   Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
   /**
   “Ptr<FeatureDetector> detector = ”等价于 “FeatureDetector * detector =”
   Ptr是OpenCV中使用的智能指针模板类,可以轻松管理各种类型的指针。
   特征检测器FeatureDetetor是虚类,通过定义FeatureDetector的对象可以使用多种特征检测及匹配方法,通过create()函数调用。
   描述子提取器DescriptorExtractor是提取关键点的描述向量类抽象基类。
   描述子匹配器DescriptorMatcher用于特征匹配,"Brute-force-Hamming"表示使用汉明距离进行匹配。
   **/

   //第一步,检测Oriented Fast角点位置
   chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
   detector->detect(img_1, keypoints_1);     //对参数1图像进行特征的提取,并存放入参数2的数组中
   detector->detect(img_2, keypoints_2);

   //第二步,根据角点计算BREIF描述子
   descriptor->compute(img_1, keypoints_1, descriptors_1);   //computer()计算关键点的描述子向量(注意思考参数设置的合理性)
   descriptor->compute(img_2, keypoints_2, descriptors_2);
   chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
   chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
   cout << "extract ORB cost = " << time_used.count() << " seconds. " << endl;
   Mat outimg1;
   drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
   imshow("ORB features", outimg1);
   imwrite("feaure1.png", outimg1);

   //第三步, 对两幅图像中的描述子进行匹配,使用hamming距离
   vector<DMatch> matches;    //DMatch是匹配关键点描述子 类, matches用于存放匹配项
   t1 = chrono::steady_clock::now();
   matcher->match(descriptors_1, descriptors_2, matches); //对参数1 2的描述子进行匹配,并将匹配项存放于matches中
   t2 = chrono::steady_clock::now();
   time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
   cout << "match the ORB cost: " << time_used.count() << "seconds. " << endl;

   //第四步,匹配点对筛选
   //计算最小距离和最大距离
   auto min_max = minmax_element(matches.begin(), matches.end(),
       [](const DMatch &m1, const DMatch &m2){ return m1.distance < m2.distance; });
   // auto 可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型
   // minmax_element()返回指向范围内最小和最大元素的一对迭代器。参数1 2为起止迭代器范围
   // 参数3是二进制函数,该函数接受范围内的两个元素作为参数,并返回可转换为bool的值。
   // 返回的值指示作为第一个参数传递的元素是否小于第二个。该函数不得修改其任何参数。
   double min_dist = min_max.first->distance;  // min_max存储了一堆迭代器,first指向最小元素
   double max_dist = min_max.second->distance; // second指向最大元素

   printf("-- Max dist : %f n", max_dist);
   printf("-- Min dist : %f n", min_dist);

   //当描述子之间的距离大于两倍最小距离时,就认为匹配有误。但有时最小距离会非常小,所以要设置一个经验值30作为下限。
   vector<DMatch> good_matches;  //存放良好的匹配项
   for(int i = 0; i < descriptors_1.rows; ++i){
       if(matches[i].distance <= max(2 * min_dist, 30.0)){
           good_matches.push_back(matches[i]);
       }
   }

   //第五步,绘制匹配结果
   Mat img_match;         //存放所有匹配点
   Mat img_goodmatch;     //存放好的匹配点
   // drawMatches用于绘制两幅图像的匹配关键点。
   // 参数1是第一个源图像,参数2是其关键点数组;参数3是第二张原图像,参数4是其关键点数组
   // 参数5是两张图像的匹配关键点数组,参数6用于存放函数的绘制结果
   drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_match);
   drawMatches(img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch);
   imshow("all matches", img_match);
   imshow("good matches", img_goodmatch);
   imwrite("match1.png", img_match);
   imwrite("goodmatch1.png", img_goodmatch);

   waitKey(0);

    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(vo1)

set(CMAKE_BUILD_TYPE "Release")
add_definitions("-DENABLE_SSE")

#设置使用C++11编译
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
    message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

find_package(OpenCV 3 REQUIRED)
include_directories( ${OpenCV_INCLUDE_DIRS} )

add_executable(orb_cv orb_cv.cpp)
target_link_libraries(orb_cv ${OpenCV_LIBS})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值