工程配置
采用CLion+Ubuntu20.04进行开发,新建工程Study
。
工程结构
结构如下:
.
├── CMakeLists.txt
├── include
│ └── main.h
└── src
├── CMakeLists.txt
└── main.cpp
子目录include
用于存放头文件;子目录src
用于存放源码
CMake配置
根目录下CMakeLists.txt
文件内容如下:
# cmake version
cmake_minimum_required(VERSION 3.21)
# project name
project(Study)
# cpp version
set(CMAKE_CXX_STANDARD 14)
# opencv
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# incldue
include_directories(include)
# src
add_subdirectory(src)
src
目录下CMakeLists.txt
文件内容如下:
# exec
add_executable(Study main.cpp)
# link opencv
target_link_libraries(Study ${OpenCV_LIBS})
头文件配置
include
目录中头文件main.h
内容如下:
#ifndef STUDY_MAIN_H
#define STUDY_MAIN_H
#include <iostream>
#include <cmath>
#include <chrono>
// OpenCV
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h>
// namespace
using namespace std;
using namespace cv;
#endif //STUDY_MAIN_H
源文件
src
下源文件main.cpp
初始内容如下:
#include "main.h"
int main()
{
return 0;
}
工程主代码
#include "main.h"
int main(){
/* 读取图片 */
Mat img_1 = imread("/home/jasonli/workspace/slambook2/ch7/1.png");
Mat img_2 = imread("/home/jasonli/workspace/slambook2/ch7/2.png");
assert(img_1.data != nullptr && img_2.data != nullptr);
/* 初始化 */
// oriented fast 关键点数据
std::vector<KeyPoint> keyPoints_1, keyPoints_2;
// brief 描述子数据
Mat descriptors_1, descriptors_2;
// 用于指向寻找关键点的类
Ptr<FeatureDetector> detector = ORB::create();
// 用于指向计算描述子的类
Ptr<DescriptorExtractor> descriptor = ORB::create();
// 用于指向特征匹配的类
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
/* ORB特征获取 */
chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
// 寻找关键点
detector->detect(img_1, keyPoints_1);
detector->detect(img_2, keyPoints_2);
// 计算描述子
descriptor->compute(img_1, keyPoints_1, descriptors_1);
descriptor->compute(img_2, keyPoints_2, descriptors_2);
// 获取ORB特征消耗时间
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);
imshow("ORB Features", outImg1);
imwrite("/home/jasonli/pic/ORB_Features.png", outImg1);
/* ORB特征匹配 */
// 存储数据
vector<DMatch> matches;
t1 = chrono::steady_clock::now();
// 对BRIEF描述子进行匹配
matcher->match(descriptors_1, descriptors_2, matches);
// 获取特征匹配的消耗时间
t2 = chrono::steady_clock::now();
time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
cout << "match 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; });
double min_dist = min_max.first->distance;
double max_dist = min_max.second->distance;
cout << "Max dist:\t" << max_dist << "\nMin dist:\t" << min_dist << endl;
// 认定描述子间距大于最小距离两倍时,匹配错误
vector<DMatch> good_matches;
for(int i=0; i < descriptors_1.rows; i++){
// 为防止最小间距过小,设置经验数据30
if(matches[i].distance <= max(2 * min_dist , 30.0)){
good_matches.push_back(matches[i]);
}
}
/* 绘制匹配结果 */
// 未筛选匹配结果
Mat img_match;
drawMatches(img_1, keyPoints_1, img_2, keyPoints_2, matches, img_match);
imshow("all matches", img_match);
imwrite("/home/jasonli/pic/all_matches.png", img_match);
// 筛选后匹配结果
Mat img_goodMatch;
drawMatches(img_1, keyPoints_1, img_2, keyPoints_2, good_matches, img_goodMatch);
imshow("good matches", img_goodMatch);
imwrite("/home/jasonli/pic/good_matches.png", img_goodMatch);
waitKey(0);
return 0;
}
输出
内容如下:
extract ORB cost = 0.053812 seconds.
match ORB cost = 0.000748446 seconds.
Max dist: 94
Min dist: 4
特征点图像如下:
未筛选前的匹配如下:
筛选后的匹配如下: