一、前面说了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. <Math> 29 365

    29. Divide Two Integers class Solution { public int divide(int dividend, int divisor) { if(dividend ...

  2. 在Windows系统中安装matplotlib,需要注意的问题

    matplotlib的下载地址:https://pypi.org/project/matplotlib/#files 以python3.6为例,适合的版本matplotlib-3.1.1-cp36-c ...

  3. JUnit & JMockit单元测试

    JUnit&JMockit单元测试总结 1.JUnit简介 Java单元测试框架业内应用较多的是JUnit,它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的 ...

  4. ASP.NET Core 模型验证的一个小小坑

    今天在我们的一个项目中遇到一个 asp.net core 模型验证(model validation)的小问题.当模型属性的类型是 bool ,而提交上来的该属性值是 null ,asp.net co ...

  5. UmiJS 目录及约定

    在文件和目录的组织上,umi 更倾向于选择约定的方式. 一个复杂应用的目录结构如下: . ├── dist/ // 默认的 build 输出目录 ├── mock/ // mock 文件所在目录,基于 ...

  6. php逻辑运算符 异或

  7. IT兄弟连 HTML5教程 HTML5和CSS3的关系

    HTML5是第五版HTML的标准,CSS3则是第三版CSS,新增一些非常实用的选择器和样式属性,并且CSS3语言开发是朝着模块化发展的.以前的规范作为一个模块实在是太庞大而且比较复杂,所以,把它分解为 ...

  8. 【zabbix告警配置】zabbix服务配置邮件告警

    一.安装邮件服务 在zabbix_server服务端安装邮件和邮件发送服务,这里可以参考我另一篇邮件服务部署文章:https://www.cnblogs.com/HeiDi-BoKe/p/118833 ...

  9. LeetCode | 2 的幂

    LeetCode 题库的第 231 题 —— 2 的幂 这题也是比较容易的一题,前提是找到规律即可.如果从 10 进制的角度观察 2 的幂次方,可能并不容易发现规律,那么可以从 2 进制的角度进行观察 ...

  10. Exploratory Testing 3.0 - 探索式测试

    最近看了James Bach新发的一篇文章,名为Exploratory Testing 3.0,文章链接:http://www.satisfice.com/blog/archives/1509 这篇文 ...