一、前面说了ORB-SLAM地图的保存部分,继续说地图如何加载,因为加载部分相比保存要稍微复杂一些,所以要多说一点。

二、ORB-SLAM2地图加载构成

  首先同样是在头文件中声明加载函数,包含地图点和关键帧类的加载。  

void Load( const string &filename, SystemSetting* mySystemSetting );
MapPoint* LoadMapPoint( ifstream &f );
KeyFrame* LoadKeyFrame( ifstream &f, SystemSetting* mySystemSetting );

  下面先是加载主函数Load的构成,关于SystemSetting类后面再说:

void Map::Load ( const string &filename, SystemSetting* mySystemSetting )
{
cerr << "Map reading from:"<<filename<<endl;
ifstream f;
f.open( filename.c_str() ); //按照保存的顺序,先读取MapPoints的数目;
unsigned long int nMapPoints;
f.read((char*)&nMapPoints, sizeof(nMapPoints)); //依次读取每一个MapPoints,并将其加入到地图中
cerr<<"The number of MapPoints:"<<nMapPoints<<endl;
for ( unsigned int i = ; i < nMapPoints; i ++ )
{
MapPoint* mp = LoadMapPoint(f);
AddMapPoint(mp);
} //获取所有的MapPoints;
std::vector<MapPoint*> vmp = GetAllMapPoints(); //读取关键帧的数目;
unsigned long int nKeyFrames;
f.read((char*)&nKeyFrames, sizeof(nKeyFrames));
cerr<<"The number of KeyFrames:"<<nKeyFrames<<endl; //依次读取每一关键帧,并加入到地图;
vector<KeyFrame*>kf_by_order;
for( unsigned int i = ; i < nKeyFrames; i ++ )
{
KeyFrame* kf = LoadKeyFrame(f, mySystemSetting);
AddKeyFrame(kf);
kf_by_order.push_back(kf);
} cerr<<"KeyFrame Load OVER!"<<endl;
//读取生长树;
map<unsigned long int, KeyFrame*> kf_by_id;
for ( auto kf: mspKeyFrames )
kf_by_id[kf->mnId] = kf;
cerr<<"Start Load The Parent!"<<endl;
for( auto kf: kf_by_order )
{
//读取当前关键帧的父节点ID;
unsigned long int parent_id;
f.read((char*)&parent_id, sizeof(parent_id)); //给当前关键帧添加父节点关键帧;
if ( parent_id != ULONG_MAX )
kf->ChangeParent(kf_by_id[parent_id]); //读取当前关键帧的关联关系;
//先读取当前关键帧的关联关键帧的数目;
unsigned long int nb_con;
f.read((char*)&nb_con, sizeof(nb_con));
//然后读取每一个关联关键帧的ID和weight,并把该关联关键帧加入关系图中;
for ( unsigned long int i = ; i < nb_con; i ++ )
{
unsigned long int id;
int weight;
f.read((char*)&id, sizeof(id));
f.read((char*)&weight, sizeof(weight));
kf->AddConnection(kf_by_id[id],weight);
}
}
cerr<<"Parent Load OVER!"<<endl;
for ( auto mp: vmp )
{
if(mp)
{
mp->ComputeDistinctiveDescriptors();
mp->UpdateNormalAndDepth();
}
}
f.close();
cerr<<"Load IS OVER!"<<endl;
return;
}

  其过程就是根据保存的顺序依次加载地图点的数目、地图点、关键帧的数目、关键帧、生长树和关联关系。

  下面是LoadMapPoints函数的构成:

 MapPoint* Map::LoadMapPoint( ifstream &f )
{
//主要包括MapPoints的位姿和ID;
cv::Mat Position(,,CV_32F);
long unsigned int id;
f.read((char*)&id, sizeof(id)); f.read((char*)&Position.at<float>(), sizeof(float));
f.read((char*)&Position.at<float>(), sizeof(float));
f.read((char*)&Position.at<float>(), sizeof(float)); //初始化一个MapPoint,并设置其ID和Position;
MapPoint* mp = new MapPoint(Position, this );
mp->mnId = id;
mp->SetWorldPos( Position ); return mp;
}

  从这里开始涉及到了MapPoint类的初始化问题,由于这里只有Position以及当前的Map,所以需要从新定义MapPoint的构造函数,分别加入到MapPoint的头文件和源文件中:

  MapPoint( const cv::Mat& Pos, Map* pMap );

  MapPoint::MapPoint(const cv::Mat &Pos, Map* pMap):
mnFirstKFid(), mnFirstFrame(), nObs(), mnTrackReferenceForFrame(), mnLastFrameSeen(), mnBALocalForKF(), mnFuseCandidateForKF(), mnLoopPointForKF(), mnCorrectedByKF(),
mnCorrectedReference(), mnBAGlobalForKF(), mpRefKF(static_cast<KeyFrame*>(NULL)), mnVisible(), mnFound(), mbBad(false),
mpReplaced(static_cast<MapPoint*>(NULL)), mfMinDistance(), mfMaxDistance(), mpMap(pMap)
{
Pos.copyTo(mWorldPos);
mNormalVector = cv::Mat::zeros(,,CV_32F); // MapPoints can be created from Tracking and Local Mapping. This mutex avoid conflicts with id.
unique_lock<mutex> lock(mpMap->mMutexPointCreation);
mnId=nNextId++;
}

  紧接着是LoadKeyFrame函数的构成,这里由于KeyFrame类需要的初始化信息比较多,因此定义了一个InitKeyFrame类,它通过SystemSetting进行初始化,二SystemSetting的主要作用就是读取设置文件(相机内参,ORB特征参数等),后面将给出SystemSetting和InitKeyFrame类的代码:

 KeyFrame* Map::LoadKeyFrame( ifstream &f, SystemSetting* mySystemSetting )
{
//声明一个初始化关键帧的类initkf;
InitKeyFrame initkf(*mySystemSetting); //按照保存次序,依次读取关键帧的ID和时间戳;
f.read((char*)&initkf.nId, sizeof(initkf.nId));
f.read((char*)&initkf.TimeStamp, sizeof(double)); //读取关键帧位姿矩阵;
cv::Mat T = cv::Mat::zeros(,,CV_32F);
std::vector<float> Quat();
//Quat.reserve(4);
for ( int i = ; i < ; i ++ )
f.read((char*)&Quat[i],sizeof(float));
cv::Mat R = Converter::toCvMat( Quat );
for ( int i = ; i < ; i ++ )
f.read((char*)&T.at<float>(i,),sizeof(float));
for ( int i = ; i < ; i ++ )
for ( int j = ; j < ; j ++ )
T.at<float>(i,j) = R.at<float>(i,j);
T.at<float>(,) = ; // for ( int i = 0; i < 4; i ++ )
// {
// for ( int j = 0; j < 4; j ++ )
// {
// f.read((char*)&T.at<float>(i,j), sizeof(float));
// cerr<<"T.at<float>("<<i<<","<<j<<"):"<<T.at<float>(i,j)<<endl;
// }
// } //读取当前关键帧特征点的数目;
f.read((char*)&initkf.N, sizeof(initkf.N));
initkf.vKps.reserve(initkf.N);
initkf.Descriptors.create(initkf.N, , CV_8UC1);
vector<float>KeypointDepth; std::vector<MapPoint*> vpMapPoints;
vpMapPoints = vector<MapPoint*>(initkf.N,static_cast<MapPoint*>(NULL));
//依次读取当前关键帧的特征点和描述符;
std::vector<MapPoint*> vmp = GetAllMapPoints();
for(int i = ; i < initkf.N; i ++ )
{
cv::KeyPoint kp;
f.read((char*)&kp.pt.x, sizeof(kp.pt.x));
f.read((char*)&kp.pt.y, sizeof(kp.pt.y));
f.read((char*)&kp.size, sizeof(kp.size));
f.read((char*)&kp.angle,sizeof(kp.angle));
f.read((char*)&kp.response, sizeof(kp.response));
f.read((char*)&kp.octave, sizeof(kp.octave)); initkf.vKps.push_back(kp); //根据需要读取特征点的深度值;
//float fDepthValue = 0.0;
//f.read((char*)&fDepthValue, sizeof(float));
//KeypointDepth.push_back(fDepthValue); //读取当前特征点的描述符;
for ( int j = ; j < ; j ++ )
f.read((char*)&initkf.Descriptors.at<unsigned char>(i,j),sizeof(char)); //读取当前特征点和MapPoints的对应关系;
unsigned long int mpidx;
f.read((char*)&mpidx, sizeof(mpidx)); //从vmp这个所有的MapPoints中查找当前关键帧的MapPoint,并插入
if( mpidx == ULONG_MAX )
vpMapPoints[i] = NULL;
else
vpMapPoints[i] = vmp[mpidx];
} initkf.vRight = vector<float>(initkf.N,-);
initkf.vDepth = vector<float>(initkf.N,-);
//initkf.vDepth = KeypointDepth;
initkf.UndistortKeyPoints();
initkf.AssignFeaturesToGrid(); //使用initkf初始化一个关键帧,并设置相关参数
KeyFrame* kf = new KeyFrame( initkf, this, NULL, vpMapPoints );
kf->mnId = initkf.nId;
kf->SetPose(T);
kf->ComputeBoW(); for ( int i = ; i < initkf.N; i ++ )
{
if ( vpMapPoints[i] )
{
vpMapPoints[i]->AddObservation(kf,i);
if( !vpMapPoints[i]->GetReferenceKeyFrame())
vpMapPoints[i]->SetReferenceKeyFrame(kf);
}
}
return kf;
}

  从文件中读取的内容同保存的一致,同时由于是通过InitKeyFrame初始化的关键帧类KeyFrame,因此这里同样需要添加构造函数以及初始化方式:

KeyFrame(InitKeyFrame &initkf, Map* pMap, KeyFrameDatabase* pKFDB,vector< MapPoint*>& vpMapPoints);

KeyFrame::KeyFrame(InitKeyFrame &initkf, Map *pMap, KeyFrameDatabase *pKFDB, vector<MapPoint*> &vpMapPoints):
mnFrameId(), mTimeStamp(initkf.TimeStamp), mnGridCols(FRAME_GRID_COLS), mnGridRows(FRAME_GRID_ROWS),
mfGridElementWidthInv(initkf.fGridElementWidthInv), mfGridElementHeightInv(initkf.fGridElementHeightInv),
mnTrackReferenceForFrame(), mnFuseTargetForKF(), mnBALocalForKF(), mnBAFixedForKF(),
mnLoopQuery(), mnLoopWords(), mnRelocQuery(), mnRelocWords(), mnBAGlobalForKF(),
fx(initkf.fx), fy(initkf.fy), cx(initkf.cx), cy(initkf.cy), invfx(initkf.invfx),
invfy(initkf.invfy), mbf(initkf.bf), mb(initkf.b), mThDepth(initkf.ThDepth), N(initkf.N),
mvKeys(initkf.vKps), mvKeysUn(initkf.vKpsUn), mvuRight(initkf.vRight), mvDepth(initkf.vDepth),
mDescriptors(initkf.Descriptors.clone()), mBowVec(initkf.BowVec), mFeatVec(initkf.FeatVec),
mnScaleLevels(initkf.nScaleLevels), mfScaleFactor(initkf.fScaleFactor), mfLogScaleFactor(initkf.fLogScaleFactor),
mvScaleFactors(initkf.vScaleFactors), mvLevelSigma2(initkf.vLevelSigma2),mvInvLevelSigma2(initkf.vInvLevelSigma2),
mnMinX(initkf.nMinX), mnMinY(initkf.nMinY), mnMaxX(initkf.nMaxX), mnMaxY(initkf.nMaxY), mK(initkf.K),
mvpMapPoints(vpMapPoints), mpKeyFrameDB(pKFDB), mpORBvocabulary(initkf.pVocabulary),
mbFirstConnection(true), mpParent(NULL), mbNotErase(false), mbToBeErased(false), mbBad(false),
mHalfBaseline(initkf.b/), mpMap(pMap)
{
mnId = nNextId ++;
}

  加载了一个关键帧之后还需要计算其BoW向量等操作,同时指定关键帧对地图点的观测。

三、总结

  加载的过程大概就是这样,时间太晚了,下次在给出InitKeyFrame和SystemSetting两个类的代码,以及其它修改的地方。总结来看,加载时关键帧类和地图点类的初始化是比较烦人的,各种繁琐的参数需要设置,所以很可能存在什么bug也说不定。另外,博客里面的代码部分是参考的网上的代码,部分是自己写的,希望有人看到了有什么错误及时指正。在数据库rgbd_dataset_freiburg1_xyz上的视频测试时是没问题的。

ORB-SLAM2 地图加载的更多相关文章

  1. AMap地图加载完成事件

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta http ...

  2. Openlayers+Geoserver(一):项目介绍以及地图加载

           项目验收完,趁着事情不是很多,对这个项目进行梳理.我主要负责地图模块,网站其他模块主要有两个,一个是报表,主要是100多张报表,技术没有难度,主要是工作量的问题.另一个是数据的校验,就是 ...

  3. ArcGIS API for Silverlight地图加载众多点时,使用Clusterer解决重叠问题

    原文:ArcGIS API for Silverlight地图加载众多点时,使用Clusterer解决重叠问题 问题:如果在地图上加载成百上千工程点时,会密密麻麻,外观不是很好看,怎么破? 解决方法: ...

  4. Android高清巨图加载方案

    1.今天看了鸿洋的<Android高清巨图加载方案>一文,对加载高清巨图时的解决方案有了一定的认识. 思路为: 提供一个设置图片的入口. 重写onTouchEvent,在里面根据用户移动的 ...

  5. arcgis 瓦片图加载规则(转载)

    arcgis 瓦片图加载规则 最近需要做地图离线的功能,要能下载指定区域的瓦片图,我们都知道如何加载谷歌和天地图的加载规则,但是网上貌似没有找到如何加载arcgis自己发布的瓦片图规则,好不容易找到一 ...

  6. Leaflet+heatmap实现离线地图加载和热力图应用

    本人博客主页:http://www.cnblogs.com/webbest/ 2017年春节已经过完,新一年的奋斗也刚刚开始.今年要经历的挑战也是大大的...不扯了. 年底前软件项目相对较多,恰巧在年 ...

  7. arcgis api 3.x for js 地图加载多个 SHP 图层压缩以及 json 文件展示(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  8. 微软必应地图加载错误:Uncaught TypeError: Microsoft.Maps.Location is not a constructor

    微软必应地图在chrome浏览器加载错误:Uncaught TypeError: Microsoft.Maps.Location is not a constructor, 原因是没有等待地图API加 ...

  9. orb slam2 双目摄像头

    主要参考了http://blog.csdn.net/awww797877/article/details/51171099这篇文章,其中需要添加的是:export ROS_PACKAGE_PATH=$ ...

随机推荐

  1. UOJ #450. 【集训队作业2018】复读机

    前置知识单位根反演自己去浅谈单位根反演看(此外可能需要一定的生成函数的姿势) 首先一看\(d\)这么小,那我们来分类讨论一下吧 当\(d=1\)时,显然答案就是\(k^n\) 当\(d=2\)时,如果 ...

  2. C# params 可变参数使用注意

    今天在一个 .NET Core 项目中调用一个自己实现的使用 params 可变参数的方法时触发了 null 引用异常,原以为是方法中没有对参数进行 null 值检查引起的,于是加上 check nu ...

  3. yii2关联表

    asArray()这个方法很好用,返回数组是1版本想要的形式,这种方式有种tp框架的感觉

  4. 创意编程,Python开发多功能壁纸自动切换工具!

    import ctypes import time import requests import os from threading import Thread from tkinter import ...

  5. Google工作法

    本文转自:https://www.yuque.com/heqingbao/msfy2c/zg56gm 这几天去上海参加Google开发者大会,利用空闲时间读了一本快餐书,书名叫<Google工作 ...

  6. IT兄弟连 HTML5教程 HTML5的曲折发展过程 浏览器之间的大战

    播放电影和音乐要使用播放器,浏览网页就需要使用浏览器.浏览器虽然只是一个设备,并不是开发语言,但在Web开发中必不可少,因为浏览器要去解析HTML5.CSS3和JavaScript等语言用于显示网页, ...

  7. 安装v2sora@y

    v2r@y安装 1. 安装nginx 这儿使用tengine进行安装, 可以看以前的博客 1.1) 注意带 http_v2 编译 ./configure --with-http_v2_module 不 ...

  8. Docker容器 MySQL中文乱码解决方案

    docker exec进入容器 sudo docker exec -it 588340b778f6 bash 执行以下命令,将 character-set-server=utf8 写入mysql配置文 ...

  9. 黄聪:PHP转换网址相对路径到绝对路径的一种方法

    相信很多程序(尤其是采集类的程序)都会有需要把网址的相对路径转换成绝对路径的需要,例如采集到某页面的HTML代码中包含资源文件经常会看到这样的文件名: <link rel="style ...

  10. 马蜂窝 iOS App 启动治理:回归用户体验

    增长.活跃.留存是移动 App 的常见核心指标,直接反映一款 App 甚至一个互联网公司运行的健康程度和发展动能.启动流程的体验决定了用户的第一印象,在一定程度上影响了用户活跃度和留存率.因此,确保启 ...