当前位置: 首页 > news >正文

视觉Slam14讲笔记第7讲视觉里程计-特征提取与匹配

1.特征点法

特征检测指的是在图像中寻找具有显著性的、可重复检测的关键点(KeyPoints)。这些关键点通常是图像中具有独特性质的局部区域。

关键点类型:
角点:两条边缘的交点,如建筑物的拐角
斑点:与周围区域有明显差异的区域,如纹理丰富的区域
边缘:图像中亮度变化剧烈的区域

关键点属性:
位置 (x, y坐标)
尺度 (在不同尺度下都能检测到)
方向 (具有旋转不变性)
响应强度 (特征的显著性程度)

描述子 (Descriptor)
描述子是对关键点周围区域的数学描述,它是一个向量,用于表示关键点的特征。描述子的目的是让计算机能够比较不同图像中的关键点是否匹配。

描述子的作用
将关键点周围的视觉信息编码成数值向量
便于计算机进行相似性比较
实现不同图像间关键点的匹配

描述子特性
独特性:不同关键点应该有不同描述子
不变性:对光照、旋转、尺度变化不敏感
紧凑性:向量长度适中,便于存储和计算

描述子的矩阵存储结构
矩阵的维度含义:
行数 (rows):等于关键点的数量
列数 (cols):等于描述子的维度(长度)
具体示例:
假设在第一张图像中检测到100个关键点,使用ORB特征(256位描述子):

descriptors_1.rows = 100(100个关键点)
descriptors_1.cols = 32(256位 = 32字节)

匹配
遍历所有描述子,求得描述子之间的最小汉明距离

opencv实现版本如下:

#include <chrono>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>using namespace std;
using namespace cv;int main(int argc, char **argv) {if (argc != 3) {cout << "usage: feature_extraction img1 img2" << endl;return 1;}//--读取图像Mat img_1 = imread(argv[1], IMREAD_COLOR);Mat img_2 = imread(argv[2], IMREAD_COLOR);assert((img_1.data != nullptr && img_2.data != nullptr));//--初始化std::vector<KeyPoint> keypoints_1, keypoints_2;Mat descriptors_1, descriptors_2;Ptr<FeatureDetector> detector = ORB::create();Ptr<DescriptorExtractor> descriptor = ORB::create();Ptr<DescriptorMatcher> matcher =DescriptorMatcher::create("BruteForce-Hamming");//--步骤1:检测 Oriented FAST 角点位置chrono::steady_clock::time_point t1 = chrono::steady_clock::now();detector->detect(img_1, keypoints_1);detector->detect(img_2, keypoints_2);//--步骤2:根据角点位置计算BRIEF描述子descriptor->compute(img_1, keypoints_1, descriptors_1);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);//--步骤3:对两幅图像中的BRIEF描述子进行匹配, 使用 Hamming 距离vector<DMatch> matches;t1 = chrono::steady_clock::now();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;//--步骤4:匹配点对筛选// 计算最小距离和最大距离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)) {good_matches.push_back(matches[i]);}}//--步骤5:绘制匹配结果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);
}

实现结果

extract ORB cost = 0.104123 seconds.
match ORB cost = 0.000353772 seconds.
-- Max dist : 94.000000 
-- Min dist : 4.000000 

关键点
在这里插入图片描述

匹配结果
在这里插入图片描述
在这里插入图片描述

二、手写ORB匹配算法

说明:
ORB_pattern的设计:
1.mean(均值)的讲究:
接近0的均值:表示这对像素点的亮度差异在统计上接近平衡
避免极端偏向:确保比较结果不是总是偏向某一方
提高区分度:平衡的对比能更好地区分不同特征

2.correlation(相关性)的讲究:
低相关性:点对之间应该尽可能独立
避免冗余:高相关性的点对提供相似信息,浪费描述子位数
最大化信息量:每个位应该提供独特的信息

3.覆盖不同尺度:
点对分布在关键点周围的不同距离上
有些点对距离较近(捕捉局部细节)
有些点对距离较远(捕捉更大范围的上下文)

4.方向多样性:
点对分布在各个方向上
确保对旋转变化具有鲁棒性
避免所有点对都集中在某个方向

5.距离分布的讲究:
观察一些点对的距离:
(8,-3)和(9,5):距离较近,捕捉细微纹理
(4,2)和(7,-12):距离较远,捕捉宏观特征
这种多尺度设计提高了特征的稳定性

如何做到旋转不变性:
通过灰度质心法计算关键点方向
将比较点对根据关键点方向进行旋转
确保描述子对图像旋转具有鲁棒性

代码:

void ComputeORB(const cv::Mat &img, std::vector<cv::KeyPoint> &keypoints,vector<DescType> &descriptors) {const int half_patch_size = 8;const int half_boundary = 16;int bad_points = 0;// 检查关键点是否离图像边界是否足够远(16像素)// 如果太靠近边界,无法提取完整的16*16像素块for (auto &kp : keypoints) {if (kp.pt.x < half_boundary || kp.pt.y < half_boundary ||kp.pt.x >= img.cols - half_boundary ||kp.pt.y >= img.rows - half_boundary) {bad_points++;descriptors.push_back({});continue;}// 通过灰度质心计算关键点的方向角度float m01 = 0, m10 = 0;for (int dx = -half_patch_size; dx < half_patch_size; ++dx) {for (int dy = -half_patch_size; dy < half_patch_size; ++dy) {uchar pixel = img.at<uchar>(kp.pt.y + dy, kp.pt.x + dx);m01 += dx * pixel;m10 += dy * pixel;}}// angle should be arc tan(m01/m10)float m_sqrt = sqrt(m01 * m01 + m10 * m10);float sin_theta = m01 / m_sqrt;float cos_theta = m10 / m_sqrt;// compute the angle of this point// 8个uint32_t共256位DescType desc(8, 0);for (int i = 0; i < 8; i++) {uint32_t d = 0;// 按照预定义模式遍历32个像素点对for (int k = 0; k < 32; k++) {int idx_pq = i * 32 + k;cv::Point2f p(ORB_pattern[idx_pq * 4], ORB_pattern[idx_pq * 4 + 1]);cv::Point2f q(ORB_pattern[idx_pq * 4 + 2], ORB_pattern[idx_pq * 4 + 3]);// rotate with thetacv::Point2f pp = cv::Point2f(cos_theta * p.x - sin_theta * p.y,sin_theta * p.x + cos_theta * p.y) +kp.pt;cv::Point2f qq = cv::Point2f(cos_theta * q.x - sin_theta * q.y,sin_theta * q.x + cos_theta * q.y) +kp.pt;// 比较两个像素点的亮度// 设置第k位为1if (img.at<uchar>(pp.y, pp.x) < img.at<uchar>(qq.y, qq.x)) {d |= 1 << k;}}desc[i] = d;}descriptors.push_back(desc);}cout << "bad/total: " << bad_points << "/" << keypoints.size() << endl;
}// brute-force matcher
void BfMatch(const vector<DescType> &desc1, const vector<DescType> &desc2,vector<DMatch> &matches) {const int d_max = 40;for (size_t i1 = 0; i1 < desc1.size(); ++i1) {if (desc1[i1].empty()) continue;cv::DMatch m{i1, 0, 256};for (size_t i2 = 0; i2 < desc2.size(); ++i2) {if (desc2[i2].empty()) continue;int distance = 0;for (int k = 0; k < 8; k++) {distance += _mm_popcnt_u32(desc1[i1][k] ^ desc2[i2][k]);}if (distance < d_max && distance < m.distance) {m.distance = distance;m.trainIdx = i2;}}if (m.distance < d_max) {matches.push_back(m);}}
}
http://www.dtcms.com/a/557704.html

相关文章:

  • 站内推广的方法和工具wordpress多站点不同主题
  • LeetCode 面试经典 150_链表_LRU 缓存(66_146_C++_中等)(哈希表 + 双向链表)
  • 海南省建设厅网站深圳办公室装修公司哪家好
  • Android Cursor AI实践技巧
  • 具身导航视角适应性增强!VIL:连续环境视觉语言导航的视角不变学习
  • 如何建立网站网页设计作业下载
  • 珠海科技网站建设广州网站建设规划
  • 酒店和网站对接如何做中园建设银行官方网站
  • 图片画质增强工具:dim2clear - 图画质增强器 v1.2.rar 操作指南
  • Photoshop - Photoshop 工具栏(20)混合器画笔工具
  • 生产管理系统详解:生产管理系统设计与实施文档
  • 实现定时器组件
  • 东莞网站建制作wordpress 跳转首页
  • .net做网站的方式wordpress有时打不开
  • MySQL索引:SQL性能分析工具详解(进阶篇)
  • 旅游网站的建设内容drupal wordpress网站
  • 别人的网站是怎么做的青岛中企动力做网站怎么样
  • python异步编程 -协程的实际意义
  • 如何制作网站主页做ps彩图什么网站好
  • 山西网站建设企业福建定制网站开发
  • 实体店入库出库软件有哪些
  • MySQL的MOD函数介绍
  • python代码之彩虹便利贴
  • QWidget::paintEngine: Should no longer be called错误情况总结
  • 成都的网站建设为什么建设银行网站打不开
  • 山东济南网站建设公司排名建站技术
  • PHP-5.2.1.tar.gz 离线安装教程:从源码编译到配置的详细步骤(附安装包)
  • Postman持久化保存/设置断言详解
  • 有害内容检测系统复现指南
  • dw软件怎么制作网页百度seo网站优化怎么做