在【SLAM-Vins_Fusion】总览中讲出
main函数中
d.开启了一个新线程sync_process。
线程的作用:如果图像buffer里面有数据的话,读入数据并且添加到estimator中。
过程为:
void sync_process()
image0 = getImageFromMsg(img0_buf.front());
estimator.inputImage(time, image0, image1);
中有-->featureFrame = featureTracker.trackImage(t, _img);
featureBuf.push(make_pair(t, featureFrame));
featureBuf 为estimator的一个public数据成员 ,
queue<pair<double, map<int, vector<pair<int, Eigen::Matrix<double, 7, 1> > > > > > featureBuf;
(需要注意,VINS-Mono里面feature_tracker和estimator是完全分开的,而这里feature_tracker包含在estimator里,唯一的差别在于减少了前端跟踪和后端之间的rostopic的发布和订阅)
而在
main---> setParameter()--->processMeasurement() 中 读取featureBuf;
feature = featureBuf.front();
//然后有
processIMU(accVector[i].first, dt, accVector[i].second, gyrVector[i].second);
processImage(feature.second, feature.first);
featureFrame = featureTracker.trackImage(t, _img);
前面讲了featureTracker的调用步骤,接下来开始进入featureTracker函数内部。
featureTracker文件夹下有个feature_tracker.h文件,其中定义了一个FeatureTracker类。
发现通篇仅有一个成员函数FeatureTracker::trackImage()中调用了cv::goodFeaturesToTracke()函数来检测特征点,因此,从该函数开始分析:
输入参数:_cur_time,_img,_img1
返回参数:map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> (乍看起来可复杂,细细分析之)首先是一个map,键值对中的值是一个vector,其中每个元素是一个pair。
原文链接:https://blog.csdn.net/CSDN_XCS/article/details/90340274
类中主要函数为trackImage()
TicToc是一个类,感觉和计时有关,使用std::chrono实现(The elements in this header deal with time.)暂时不展开。
predict_pts,cur_pts:类数据成员,vector<cv::Point2f> predict_pts,cur_pts;
hadPrediction:数据成员,bool hasPrediction;,初始值为什么?(254行赋值为hasPrediction = false;)
如果值为true,则调用openCV的函数进行光流跟踪:
调用了cv::goodFeaturesToTracke()函数来检测特征点
函数详细内容可以查看:calcOpticalFlowPyrLK官方介绍
或查看博客:https://blog.csdn.net/CSDN_XCS/article/details/90340274
可知
代码中调用的时候,传入参数表示的含义:
cv::calcOpticalFlowPyrLK(prev_img, cur_img, prev_pts, cur_pts, status, err, cv::Size(21, 21), 1,
cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 30, 0.01), cv::OPTFLOW_USE_INITIAL_FLOW);
/*
prev_img, cur_img:上一帧图片,当前帧图片;
prev_pts, cur_pts:特征点在上一帧中的位置(等待找到对应的流),特整点在当前帧中的 位置(用以保存该算法计算结果);
status:vector<uchar> status;
err:vector<float> err;
cv::Size(21, 21):在每层金字塔中搜索窗的大小(21×21;
1: 对应两层;
cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 30, 0.01):终止条件——迭代次数 和 精度;
cv::OPTFLOW_USE_INITIAL_FLOW:保持和原来一样多特征点数。
*/
然后统计跟踪成功的点数,(即status为1的个数)
如果跟踪成功的点数少于10,那么再执行一次,这次修改传入参数中金字塔的层数为4层
if (succ_num < 10)
cv::calcOpticalFlowPyrLK(prev_img, cur_img, prev_pts, cur_pts, status, err, cv::Size(21, 21), 3);
假设跟踪成功的点的数目足够多了,进行下一步处理操作:
当FLOW_BACK为true时,再调用cv::calcOpticalFlowPyrLK()进行一次反向的光流跟踪(传入参数时将两幅图像、特征点做对应调换)
cv::calcOpticalFlowPyrLK(prev_img, cur_img, prev_pts, cur_pts, status, err, cv::Size(21, 21), 3);
FLOW_BACK 默认为1
定义在catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_imu_config.yaml
符合以下要求的才认为是跟踪成功的点:
status[i] && reverse_status[i] && distance(prev_pts[i], reverse_pts[i]) <= 0.5
将跟踪失败的点删除(即当status[i]值为0,删除各vector中下标i对应的元素):
reduceVector(prev_pts, status);
reduceVector(cur_pts, status);
reduceVector(ids, status);
reduceVector(track_cnt, status);
ids, track_cnt:FeatureTracker类的数据成员vector<int> ids;和vector<int> track_cnt;
这里 track_cnt 代表了什么意义?表示了这个点跟踪的次数。
调用undistortedPts成员函数,该函数比较简单,主要想实现的功能是
将像素坐标系下的坐标,转换为归一化相机坐标系下的坐标?即cur_un_pts为归一化相机坐标系下的坐标
cur_un_pts = undistortedPts(cur_pts, m_camera[0]);
接下来,计算pts_velocity,其为当前帧相对于前一帧 特征点沿x,y方向的像素移动速度。
cur_un_pts_map中存放ids[i]和cur_un_pts[i]构成的键值对。
在prev_un_pts_map非空的情况下,对每一个cur_un_pts_map中的键值对都去寻找是否在prev_un_pts_map中存在,若存在像素移动速度不难计算;若不存在则为0;
如果prev_un_pts_map是空的情况下,置零。
pts_velocity = ptsVelocity(ids, cur_un_pts, cur_un_pts_map, prev_un_pts_map);
左右图像的匹配上,方法也是光流跟踪,由左图跟踪到右图,得到右图的匹配点,在反过来光流,剔除掉偏差大的点,但是这里并没有像orb-slam那样直接就三角化出深度,而是把点保存下来,放到后面计算。
cv::calcOpticalFlowPyrLK(cur_img, rightImg, cur_pts, cur_right_pts, status, err, cv::Size(21, 21), 3);
// reverse check cur right ---- cur left
if(FLOW_BACK)
{
cv::calcOpticalFlowPyrLK(rightImg, cur_img, cur_right_pts, reverseLeftPts, statusRightLeft, err, cv::Size(21, 21), 3);
for(size_t i = 0; i < status.size(); i++)
{
if(status[i] && statusRightLeft[i] && inBorder(cur_right_pts[i]) && distance(cur_pts[i], reverseLeftPts[i]) <= 0.5)
status[i] = 1;
else
status[i] = 0;
}
}
总结
最终返回的 featureFrame
返回值map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> featureFrame
其中Eigen::Matrix<double, 7, 1> xyz_uv_velocity;
包含跟踪点归一化相机坐标系下的坐标3,像素平面上的坐标2,像素移动速度2。
看一下流程过程