视觉里程计--视觉slam7.1/相机运动估计视觉算法
视觉里程计
本篇文章记录了少许阅读《视觉slam14讲》的阅读整理,不是特别全面,只是为了本次项目中特定任务搜查资料,时间比较紧,文章并没有全面涵盖所有知识点。日后若时间有空闲,将回来补充整理。
相机位姿估计
特征点法
首先,视觉里程计的核心问题是根据图像估计相机运动。利用特征点能够有效利用图像矩阵为我们提供的关于相机运动的信息。特征点一般具有可重复可区别高效率和本地性的特点。
特征点组成
关键点key-point 和描述子descriptor
关键点是指特征点的位置,描述子是按照相似的关键点一般具有相似的描述子设计的,如果2个特征点的描述子在向量空间上的距离相近,那么我们称他们是同样的特征点。
ORB特征
分为FAST关键点和BRIEF描述子
| 名称 | FAST关键点 | BRIEF描述子 |
|---|---|---|
| 原理 | 比较像素点之间的亮度差异 | 二进制高维度向量 |
| 优缺点 | 速度快、重复性不强、分布不均匀 不具有尺度不变性以及方向性 |
速度快,有利于存储、适用于实时匹配 不具有旋转不变性 |
| 解决办法 | 尺度:在不同层的图像金字塔匹配 方向性:计算图像灰度质心 |
旋转:关键点方向被计算出来的情况下可以计算旋转之后的Steer BRIEF |
特征匹配
暴力匹配;浮点型关键点->匹配欧氏距离;二进制关键点->匹配汉明距离;特征点个数极多时,考虑快速近似最近邻FLANN算法。
特征点匹配核心代码(OpenCV)
//首先初始化部分、关键点、描述子、计算描述子指针、匹配matcher指针
//-- 初始化
std::vector<KeyPoint> keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
Ptr<FeatureDetector> detector = ORB::create();
Ptr<DescriptorExtractor> descriptor = ORB::create();
// Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name);
// Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name);
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" );
//之后
//-- 第一步:检测 Oriented FAST 角点位置
detector->detect ( img_1,keypoints_1 );
detector->detect ( img_2,keypoints_2 );
//在之后,Mat存储描述子
//-- 第二步:根据角点位置计算 BRIEF 描述子
descriptor->compute ( img_1, keypoints_1, descriptors_1 );
descriptor->compute ( img_2, keypoints_2, descriptors_2 );
可视化可以使用函数
drawKeypoints( img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );
//最后是特征匹配
//-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
vector<DMatch> matches;
//BFMatcher matcher ( NORM_HAMMING );
matcher->match ( descriptors_1, descriptors_2, matches );
关于DMatch这个类,可以理解为匹配关键点描述子的类,有以下成员,存着匹配对的各种信息,用于筛选匹配对
DMatch.distance - 描述符之间的距离。越小越好。
• DMatch.trainIdx - 目标图像中描述符的索引。
• DMatch.queryIdx - 查询图像中描述符的索引。
• DMatch.imgIdx - 目标图像的索引
之后对匹配点对进行筛选
//-- 第四步:匹配点对筛选
double min_dist=10000, max_dist=0;
//找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
for ( int i = 0; i < descriptors_1.rows; i++ )
{
double dist = match[i].distance;
if ( dist < min_dist ) min_dist = dist;
if ( dist > max_dist ) max_dist = dist;
}
//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
for ( int i = 0; i < descriptors_1.rows; i++ )
{
if ( match[i].distance <= max ( 2*min_dist, 30.0 ) )
{
matches.push_back ( match[i] );
}
}
}
以上代码中得到的
std::vector <cv::Dmatch> matches
即为最后获得筛选后的匹配对
之后顺便看到一个像素坐标系转相机坐标系的函数,顺便摘抄作为参考
Point2d pixel2cam ( const Point2d& p, const Mat& K )
{
return Point2d
(
( p.x - K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 ),
( p.y - K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 )
);
}
翻译成公式就是
x
c
a
m
=
x
p
x
l
−
c
x
f
x
y
c
a
m
=
y
p
x
l
−
c
y
f
y
x_{cam} = \frac {x_{pxl} - {c_x}} {f_x} \\ y_{cam} = \frac {y_{pxl} - {c_y}}{f_y}
xcam=fxxpxl−cxycam=fyypxl−cy
计算相机运动
| 已知情况 | 采用方法 | 效果 |
|---|---|---|
| 单目相机、两组2D点 | 对极几何 | 估计相机运动 |
| 双目相机、RGBD相机(两组3D点) | ICP方法 | 得到距离信息,估计相机运动 |
| 一组3D一组2D | PnP求解 | 估计相机运动 |
2D-2D
因为不太适用于本次比赛应用场景,先跳过这一步骤
三角测量
又称三角化,目的是求解目标特征点的空间位置,考虑两张不同视角的二维图,两图之间变换矩阵为T ,I1有特征点p1 , I2有特征点p2 , 都对应p点, 现在x1 x2是两个特征点的归一化坐标,已知R T,要求解两个特征点的深度s1 s2.
如果我们考虑计算s~1,首先我们有
s
2
x
2
=
s
1
R
x
1
+
t
s_2x_2 = s_1Rx_1 + t
s2x2=s1Rx1+t
对上式我们左乘
x
2
Λ
x_2^{\Lambda}
x2Λ
s
2
x
2
Λ
x
2
=
0
=
s
1
x
2
Λ
R
x
1
+
x
2
Λ
t
s_2x_2^{\Lambda}x_2 = 0 = s_1x_2^{\Lambda}Rx_1 + x_2^{\Lambda}t
s2x2Λx2=0=s1x2ΛRx1+x2Λt
可以解方程得到s2,有了s2之后s1也很易得
注意,前提是对极几何中我们求解了相机位子,在此基础之上进行三角化求解特征点的空间位置,这是为了解决单目slam中的单幅图无法获取深度信息
//opencv中提供了封装的函数用于三角化
cv::triangulatePoints( T1, T2, pts_1, pts_2, pts_4d );
// 转换成非齐次坐标
for ( int i=0; i<pts_4d.cols; i++ )
{
Mat x = pts_4d.col(i);
x /= x.at<float>(3,0); // 归一化
Point3d p (
x.at<float>(0,0),
x.at<float>(1,0),
x.at<float>(2,0)
);
points.push_back( p );
}//需要一步将其次坐标归一化并且转换为费其次坐标
//这函数的参数必须都是float类型的
Parameters:
projMatr1
3x4 projection matrix of the first camera.//左侧相机的RT矩阵(一般设置成eyes 0)
projMatr2
3x4 projection matrix of the second camera.//右侧相机的RT矩阵
projPoints1
2xN array of feature points in the first image. In case of c++ version it can be also a vector of feature points or two-channel matrix of size 1xN or Nx1.//左侧相机在相机坐标系下特征点坐标的集合
projPoints2
2xN array of corresponding points in the second image. In case of c++ version it can be also a vector of feature points or two-channel matrix of size 1xN or Nx1.//右侧相机在相机坐标系下特征点坐标的集合
points4D
4xN array of reconstructed points in homogeneous coordinates.//齐次坐标中的4xN
三角化测量中具有的深度不确定性可以根据深度滤波器来改进
3D-2D:PnP
终于来到了PnP,此方法描述了当知道n个3D空间点以及其投影位置时,估计相机的位姿。两张图像中的一张特征点的3D位置已知,最少需要3个点对以及至少1个额外点验证结果来估计相机运动,3D位置可以由三角化和RGBD相机的深度图确定,因此在双目或者rgbd相机的视觉里程计中
这里介绍很多PNP问题的求解方法,并且可以用非线性化的方式构造最小二乘问题迭代求解
直接线性变换DLT
已知一组3D点,以及他们在相机中的投影位置
可以求解给定地图和图像时的相机状态问题,如果把3D点看做另一个相机坐标系点的话,也可以求解两个相机的相对运动问题。
–后记–
关于相机运动估计,最后采取的解决办法实际上是跑一个slam的包,效果会比手写pnp来的更准,且操作也很方便。
视觉里程计--视觉slam7.1/相机运动估计视觉算法的更多相关文章
- SLAM入门之视觉里程计(2):相机模型(内参数,外参数)
相机成像的过程实际是将真实的三维空间中的三维点映射到成像平面(二维空间)过程,可以简单的使用小孔成像模型来描述该过程,以了解成像过程中三维空间到二位图像空间的变换过程. 本文包含两部分内容,首先介绍小 ...
- SLAM入门之视觉里程计(5):单应矩阵
在之前的博文OpenCV,计算两幅图像的单应矩阵,介绍调用OpenCV中的函数,通过4对对应的点的坐标计算两个图像之间单应矩阵\(H\),然后调用射影变换函数,将一幅图像变换到另一幅图像的视角中.当时 ...
- SLAM入门之视觉里程计(1):特征点的匹配
SLAM 主要分为两个部分:前端和后端,前端也就是视觉里程计(VO),它根据相邻图像的信息粗略的估计出相机的运动,给后端提供较好的初始值.VO的实现方法可以根据是否需要提取特征分为两类:基于特征点的方 ...
- (3)视觉里程计 Visual Odometry
首先分析include头文件下的slamBase.h文件 # pragma once // 各种头文件 // C++标准库 #include <fstream> #include < ...
- 第三篇 视觉里程计(VO)的初始化过程以及openvslam中的相关实现详解
视觉里程计(Visual Odometry, VO),通过使用相机提供的连续帧图像信息(以及局部地图,先不考虑)来估计相邻帧的相机运动,将这些相对运行转换为以第一帧为参考的位姿信息,就得到了相机载体( ...
- 视觉slam十四讲个人理解(ch7视觉里程计1)
参考博文::https://blog.csdn.net/david_han008/article/details/53560736 https://blog.csdn.net/n66040927/ar ...
- SLAM——视觉里程计(一)feature
从现在开始下面两篇文章来介绍SLAM中的视觉里程计(Visual Odometry).这个是我们正式进入SLAM工程的第一步,而之前介绍的更多的是一些基础理论.视觉里程计完成的事情是视觉里程计VO的目 ...
- 关于视觉里程计以及VI融合的相关研究(长期更新)
1. svo 源码:https://github.com/uzh-rpg/rpg_svo 国内对齐文章源码的研究: (1)冯斌: 对其代码重写 https://github.com/yueying/O ...
- SLAM入门之视觉里程计(6):相机标定 张正友经典标定法详解
想要从二维图像中获取到场景的三维信息,相机的内参数是必须的,在SLAM中,相机通常是提前标定好的.张正友于1998年在论文:"A Flexible New Technique fro Cam ...
- SLAM入门之视觉里程计(2):两视图对极约束 基础矩阵
在上篇相机模型中介绍了图像的成像过程,场景中的三维点通过"小孔"映射到二维的图像平面,可以使用下面公式描述: \[ x = MX \]其中,\(c\)是图像中的像点,\(M\)是一 ...
随机推荐
- 初次使用gitee的笔记
步骤及问题 1.git config --global user.name "username" 2.git config --global user.email "us ...
- windwos 系统打补丁后重启不了处理方案
如果可以进入WinRE这个修复的高级选项,选择安全模式,是否可以进入,卸载最近安装的补丁,再重启看一下. 如果无法进入安全模式的话,那么选择cmd模式,使用下方命令.这通常会回退pending的upd ...
- 晓晓---python文件的读写模式的理解
1. python读取文件模式的自我理解:'r' open for reading (default)----只读模式打开文件,不能写:'w' open for writing, truncating ...
- tcpdump: error while loading shared libraries: libpcap.so.1: cannot open shared object file: No such file or directory
[root@inner ~]# tcpdump -i any -s 0 -w trunkm.pcaptcpdump: error while loading shared libraries: lib ...
- ESXi 基础安全加强(ing...)
# 查看防火墙规则esxcli network firewall ruleset allowedip list # 允许指定ip使用web服务和客户端访问[root@localhost:~] esxc ...
- 第15周作业--JDBC连接数据库
编写一个应用程序,输入用户名和密码,访问test数据库中t_login表(字段包括id.username.password),验证登录是否成功.当登录成功后,将t_user表(id.name.sex. ...
- Sup, inf convolution for convex functions
Let $\Omega$ be a bounded convex domain in $\mathbb{R}^n$. $f:\Omega\rightarrow\mathbb{R}^n$. If $f$ ...
- IDEA通过Spring Initalizr新建SSM (2)
之前的方式是通过官网初始化demo(URL:https://start.spring.io/)现在记录一下通过IDEA自带的初始化器新建SSM框架 1.打开IDEA,点击新建,出现如下图菜单,点击Sp ...
- unity默认管线lightmap
lightmap采样 https://blog.csdn.net/wodownload2/article/details/94431040
- 【转载】ISTQB AL-TA/TTA连载系列06:白盒测试技术的精炼总结
转载自:https://blog.csdn.net/iteye_13411/article/details/82158591 [概述] 白盒测试,有时候又称为基于结构的测试,特别适用于低级别的测试.根 ...