@ 《视觉SLAM十四讲》知识点与习题
《视觉SLAM十四讲》第八讲知识点整理+习题
正在学习SLAM相关知识,将一些关键点及时记录下来。
知识点整理
本讲主要介绍了直接法来实现视觉里程计,包含光流跟踪、直接法等
- 特征点法的缺点:关键点的提取和描述子的计算非常耗时;使用特征点法时,忽略了特征点以外的其他特征;当相机运动到特征缺失的地方,会因为缺少纹理信息而无法找到足够的匹配点来计算相机运动
- 针对1中的缺陷,从关键点和描述子两个方面提出解决方案:
- 保留特征点,但是只计算关键点,不计算描述子,使用光流法来跟踪特征点的运动
- 只计算关键点,不计算描述子。且直接用直接法来计算特征点在下一时刻图像中的位置
- 关键点和描述子都不计算,根据像素灰度的差异,直接计算相机运动
- 在直接法中,不需要知道点与点之间的对应关系,而是通过最小化光度误差来求得
- 光流:描述像素随时间在图像之间运动的方法,跟踪同一个像素的运动过程。光流法基于灰度不变假设。可以加速基于特征点的VO算法,避免计算和匹配描述子的过程,但要求相机运动较慢(或采集频率较高)
- 灰度不变假设:同一个空间点的像素灰度值,在各个图像中是固定不变的。虽然这个假设很强,然而特征点法对光照就具有一定的容忍性。但是基于该假设,可以来看如何计算像素的运动
- Lucas-Kanade光流:稀疏光流(跟踪部分像素运动),可用于跟踪特征点位置。除了灰度不变假设,还具有以下假设:某一个窗口内的像素具有相同的运动
- 通过光流跟踪的特征点,用PnP,ICP或对极几何来估计相机运动
- 直接法:思路是根据当前相机的位姿估计值来寻找第二个图像对应的相机位姿,所以,只需要通过解一个优化问题,即采用光度误差的二范式来进行优化,由像素梯度引导优化的方向。若想要得到正确的优化结果,就必须保证大部分像素梯度能够把优化引导到正确的方向,即必须保证沿着图像梯度走时,灰度误差会不断下降
- 直接法的讨论:
- 稀疏直接法:P来自于稀疏关键点,仅使用数百个至上千个关键点,假设其周围像素是不变的。多用于快速求解相机位姿
- 半稠密直接法:只是用带有梯度的像素点,舍弃像素梯度不明显的地方
- 稠密直接法:计算所有像素,多数不能在现有的CPU上实时计算。像素梯度不明显的点,在运动估计中不会有太大贡献,在重构时也会难以估计位置。多用于构建完整地图
- 在直接法中,会用像素的局部梯度来近似它附近的灰度分布。只有当相机运动很小,图像中的梯度不会有很强的非凸性时,直接法才能成立。且有时会使用小的图像块,使用更复杂的差异度量方式来提高区分性
实践
- LK光流:使用freiburg1_room数据集进行测试,部分测试结果如图
tracked keypoints: 2320
tracked keypoints: 1946
tracked keypoints: 1251
tracked points: 569
tracked keypoints: 59
tracked keyponts: 6
tracked points: 5
由上述结果,很明显的发现,当后来重新回到初始位置时,之前那些跟踪到过的特征点竟然也无法再被跟踪到了,当然有一个很显然的原因是后面的图片明显模糊了,第一直觉是因为相机运动变快了,这使得无法找到清晰的特征,但会不会还有其他的原因呢?仔细阅读源码- 迭代100次,即总共对freiburg1_room文件夹里面的前100组depth和color图片进行光流跟踪。其中,使用associate.txt来将depth和color文件夹中的图片配对起来
- 仅从第一张图片中提取FAST特征点,在后续每一次使用中,会将那些已经无法跟踪到的点删除,并且设置成新的能够哦检测到的关键点。将这些特征点存在keypoints向量中,并且将第一张图片作为last_color
if (index ==0 ) { // 对第一帧提取FAST特征点 vector<cv::KeyPoint> kps; cv::Ptr<cv::FastFeatureDetector> detector = cv::FastFeatureDetector::create(); detector->detect( color, kps ); for ( auto kp:kps ) keypoints.push_back( kp.pt ); last_color = color; continue; } if ( color.data==nullptr || depth.data==nullptr ) continue;
- 调用OpenCV中的LK光流跟踪算法。由于LK算法具有灰度不变假设,所以需要last_color和color两个矩阵来计算图像灰度随着时间的变化量。next_keypoints中存储的是在第二幅图像中,输入特征们新的位置,并设置status(若特征的flow被检测道,就设置为true)及error。具体LK光流跟踪的源码在lkpyramid.cpp文件中实现,先是void cv::calcOpticalFlowPyrLK(…),然后在该函数中调用void SparsePyrLKOpticalFlowImpl::calc(…)函数。(具体实现还没看,之后填坑)
cv::calcOpticalFlowPyrLK( last_color, color, prev_keypoints, next_keypoints, status, error );
- 下述步骤即为步骤1中描述的对keypoints的更新
// 把跟丢的点删掉S int i=0; for ( auto iter=keypoints.begin(); iter!=keypoints.end(); i++) { if ( status[i] == 0 ) { iter = keypoints.erase(iter); continue; } *iter = next_keypoints[i]; iter++; }
- 调用OpenCV中的LK光流跟踪算法。由于LK算法具有灰度不变假设,所以需要last_color和color两个矩阵来计算图像灰度随着时间的变化量。next_keypoints中存储的是在第二幅图像中,输入特征们新的位置,并设置status(若特征的flow被检测道,就设置为true)及error。具体LK光流跟踪的源码在lkpyramid.cpp文件中实现,先是void cv::calcOpticalFlowPyrLK(…),然后在该函数中调用void SparsePyrLKOpticalFlowImpl::calc(…)函数。(具体实现还没看,之后填坑)
- RGB-D的直接法——稀疏直接法
此时图优化问题由一个相机位姿顶点与许多条一元边组成
优化问题对应的线性方程是计算李代数增量,本身规模不大,所以主要的计算时间会花费在每条边的误差与雅可比矩阵的计算上
随着相机的移动,特征点的对应关系也会展示出来
- RGB-D的直接法——半稠密直接法
此时就会感觉相机旋转时,参与估计的像素像是固定在空间中的一样