该文件的主要函数为
void FeatureTracker::readImage(const cv::Mat &_img, double _cur_time)
该文件的中文注释代码以放到我的github上,代码结合我下面画的图更容易理解,欢迎大家star!,本人也是vi-slam新手,分析不到位的地方,欢迎大家批评指正!
当第一帧图像进来时,执行函数` cv::goodFeaturesToTrack(forw_img, n_pts, MAX_CNT - forw_pts.size(), 0.01, MIN_DIST, mask)
,其中forw_img表示当前图像(这里表示第一帧图像),n_pts表示存储的角点坐标集合,MAX_CNT - forw_pts.size() 表示要检测的角点个数,在这里为MAX_CNT;下图表示提取的150个角点,存放到n_pts
在步骤1里检测到角点(n_pts)后,通过函数addPoints()
是将n_pts中的角点放到forw_pts中,ids表示每个角点的编号,track_cnt表示每个角点的跟踪次数,addPoints()
是将检测到的新的角点ID初始化和跟踪次数初始化如下图所示:
prev_img = cur_img;//在第一帧处理中还是等于当前帧forw_img
prev_pts = cur_pts;//在第一帧中不做处理
prev_un_pts = cur_un_pts;//在第一帧中不做处理
cur_img = forw_img; //将当前帧赋值给上一帧
cur_pts = forw_pts;//如下图所示
第二帧图像forw_img与上一帧图像cur_img进行光流跟踪cv::calcOpticalFlowPyrLK(cur_img, forw_img, cur_pts, forw_pts, status, err, cv::Size(21, 21), 3);
cur_pts表示步骤3中存储的角点,forw_img表示在当前图像中跟踪成功的点的集合,status表示 cur_pts和forw_pts中对应点对是否跟踪成功。如下图所示:
光流跟踪结束后,还要判断跟踪成功的角点是否都在图像内,源码:
for (int i = 0; i < int(forw_pts.size()); i++)
if (status[i] && !inBorder(forw_pts[i]))
status[i] = 0;
对步骤5中的点根据状态status进行重组,将staus中为1的对应点对在原点对中保存下来,为0的对应点对去除掉,对应代码:
//将光流跟踪后的点的集合,根据跟踪的状态(status)进行重组
reduceVector(prev_pts, status);
reduceVector(cur_pts, status);
reduceVector(forw_pts, status);
//将光流跟踪后的点的id和跟踪次数,根据跟踪的状态(status)进行重组
reduceVector(ids, status);
reduceVector(cur_un_pts, status);
reduceVector(track_cnt, status);
如图所示:
因为在第二张图像中成功跟踪了四个点(p1,p6,p8,p150),所以要对该点对应的跟踪次数(track_cnt)进行加1操作,代码:
//将track_cnt中的每个数进行加一处理,代表又跟踪了一次
for (auto &n : track_cnt)
n++;
该部分用到的函数主要是rejectWithF();
借助基础矩阵计算函数cv::findFundamentalMat(un_cur_pts, un_forw_pts, cv::FM_RANSAC, F_THRESHOLD, 0.99, status);
来去除去匹配点,然后在根据状态量staus对匹配的点对,与步骤5进行相似的操作。
该部分用到的函数主要是setMask();
,该函数主要有两个作用,
1.对跟踪到的特征点对依据跟踪次数进行从多到少的排序,并放到原集合中,对应代码:
// prefer to keep features that are tracked for long time
//保存长时间跟踪到的特征点
//vector>> cnt_pts_id
vector>> cnt_pts_id;
for (unsigned int i = 0; i < forw_pts.size(); i++)
cnt_pts_id.push_back(make_pair(track_cnt[i], make_pair(forw_pts[i], ids[i])));
//对给定区间的所有元素进行排序,按照点的跟踪次数,从多到少进行排序
sort(cnt_pts_id.begin(), cnt_pts_id.end(), [](const pair> &a, const pair> &b)
{
//降序排列
return a.first > b.first;
});
forw_pts.clear();
ids.clear();
track_cnt.clear();
for (auto &it : cnt_pts_id)
{
if (mask.at(it.second.first) == 255) //检测新建的mask在该点是否为255
{
//将跟踪到的点按照跟踪次数重新排列,并返回到forw_pts,ids,track_cnt
forw_pts.push_back(it.second.first);
ids.push_back(it.second.second);
track_cnt.push_back(it.first);
//图片,点,半径,颜色为0表示在角点检测在该点不起作用,粗细(-1)表示填充
cv::circle(mask, it.second.first, MIN_DIST, 0, -1);
}
}
2.在已跟踪到角点的位置上,将mask对应位置上设为0,意为在cv::goodFeaturesToTrack(forw_img, n_pts, MAX_CNT - forw_pts.size(), 0.01, MIN_DIST, mask);
进行操作时在该点不再重复进行角点检测,这样可以使角点分布更加均匀。
再一次进行角点检测,只不过这次角点检测的角点个数会改变,变为MAX_CNT - forw_pts.size()
从第二张图像输入后每进行一次循环,最后还需要对匹配的特征点对进行畸变矫正,主要函数为undistortedPoints();
`