视觉里程计
本篇文章记录了少许阅读《视觉slam14讲》的阅读整理,不是特别全面,只是为了本次项目中特定任务搜查资料,时间比较紧,文章并没有全面涵盖所有知识点。日后若时间有空闲,将回来补充整理。
相机位姿估计
特征点法
首先,视觉里程计的核心问题是根据图像估计相机运动。利用特征点能够有效利用图像矩阵为我们提供的关于相机运动的信息。特征点一般具有可重复可区别高效率和本地性的特点。
特征点组成
关键点key-point 和描述子descriptor
关键点是指特征点的位置,描述子是按照相似的关键点一般具有相似的描述子设计的,如果2个特征点的描述子在向量空间上的距离相近,那么我们称他们是同样的特征点。
ORB特征
分为FAST关键点和BRIEF描述子
名称 | FAST关键点 | BRIEF描述子 |
---|
原理 | 比较像素点之间的亮度差异 | 二进制高维度向量 |
优缺点 | 速度快、重复性不强、分布不均匀 不具有尺度不变性以及方向性 | 速度快,有利于存储、适用于实时匹配 不具有旋转不变性 |
解决办法 | 尺度:在不同层的图像金字塔匹配 方向性:计算图像灰度质心 | 旋转:关键点方向被计算出来的情况下可以计算旋转之后的Steer BRIEF |
特征匹配
暴力匹配;浮点型关键点->匹配欧氏距离;二进制关键点->匹配汉明距离;特征点个数极多时,考虑快速近似最近邻FLANN算法。
特征点匹配核心代码(OpenCV)
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" );
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 );
可视化可以使用函数
drawKeypoints( img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );
vector<DMatch> matches;
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;
}
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中的单幅图无法获取深度信息
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 );
}
Parameters:
projMatr1
3x4 projection matrix of the first camera.
projMatr2
3x4 projection matrix of the second camera.
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.
三角化测量中具有的深度不确定性可以根据深度滤波器来改进
3D-2D:PnP
终于来到了PnP,此方法描述了当知道n个3D空间点以及其投影位置时,估计相机的位姿。两张图像中的一张特征点的3D位置已知,最少需要3个点对以及至少1个额外点验证结果来估计相机运动,3D位置可以由三角化和RGBD相机的深度图确定,因此在双目或者rgbd相机的视觉里程计中
这里介绍很多PNP问题的求解方法,并且可以用非线性化的方式构造最小二乘问题迭代求解
直接线性变换DLT
已知一组3D点,以及他们在相机中的投影位置
可以求解给定地图和图像时的相机状态问题,如果把3D点看做另一个相机坐标系点的话,也可以求解两个相机的相对运动问题。
–后记–
关于相机运动估计,最后采取的解决办法实际上是跑一个slam的包,效果会比手写pnp来的更准,且操作也很方便。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)