slam十四讲第七讲之特征提取和匹配代码讲解
以下代码均是ubuntu20.04下完成,版本不一致之处也均已修改,在这个版本下的学习笔记比较少,希望我能帮大家填补一些坑。另外,本人学习过程中发现经常会忘记,特此记录学习过程与心得,希望能帮助和我一样刚刚入门的小萌新。若发现文中的错误也希望大家批评指正。
#include <iostream>
#include <opencv2/core/core.hpp>//处理图像的工具
#include <opencv2/features2d/features2d.hpp>//处理特征点信息
#include <opencv2/highgui/highgui.hpp>//作图绘制关键点圆圈和描述子信息
#include <chrono> //用于计时
using namespace std;//命名空间 类似python import ** as **(胡扯哈哈)
using namespace cv;
int main(int argc, char **argv) { //argc 输入参数的个数,argv 输入的参数
if (argc != 3) { // 系统本身加两张图片argc 表示传入数目
cout << "输入图片一和图片二" << endl;
return 1;
}
Mat img_1 = imread(argv[1], 1);//1 = CV_LOAD_IMAGE_COLOR unbuntu20的 所以用1 ; 表示以彩色图像形式读取argv中的参数信息,并且存贮于img_1中
Mat img_2 = imread(argv[2], 1);//Mat 为 opencv 中的类 ,无需定义维度 ,自适应。另外,这里img_1和img_2存储了两张图像的颜色信息,其维度为2维,大小为480*640(这里存储数据的矩阵维度并非480*640,而是480*(640*3),因为要存储每个像素点的BGR信息,因此每个像素的信息对应1行和3列)。
assert(img_1.data != nullptr && img_2.data != nullptr);//判断传入数据是否为图像
std::vector<KeyPoint> keypoints_1, keypoints_2;//容器vector类型<keypoint>;存储角度 距离 分类 关键点坐标 金字塔层数等关键信息。 std:: 表示std的啥啥啥。。。
Mat descriptors_1, descriptors_2;
Ptr<FeatureDetector> detector = ORB::create();//定义FeatureDector ,调用ORB::create()可以处理图像信息
Ptr<DescriptorExtractor> descriptor = ORB::create();
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
chrono::steady_clock::time_point t1 = chrono::steady_clock::now();// 开始计时
detector -> detect(img_1, keypoints_1);
detector -> detect(img_2, keypoints_2);//提取图像中的特征点放入 keypoint 中,dectector上面定义他的功能了
descriptor -> compute(img_1, keypoints_1, descriptors_1);//提取图像特征点的描述子信息,存于descriptors中
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 << "用时:" << time_used.count() << "seconds" << endl;//cout() 数字形式表示出来
Mat outimg1;//定义输出图像
drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);//画图
imshow("ORB 特征", outimg1);//输出图像
//waitKey(0);//没这个图你看不到,作用就是让图一直显示
vector<DMatch> matches;//定义matches
t1 = chrono::steady_clock::now();//开始计时
matcher->match(descriptors_1, descriptors_2, matches);//描述信息放入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;//最大距离取出
printf("-- Max dist : %f \n", max_dist);
printf("-- Min dist : %f \n", min_dist);
//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
std::vector<DMatch> good_matches;
for (int i = 0; i < descriptors_1.rows; i++) { // 遍历所有描述子
if (matches[i].distance <= max(2 * min_dist, 30.0)) {//30为下限,大于最小距离两倍的点被去除
good_matches.push_back(matches[i]);//好点放入容器
}
}
//以下同上
Mat img_match;
Mat img_goodmatch;
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);
waitKey(0);
return 0;
}
以下为输出的结果:在这里插入图片描述