一、前面说了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. Java连载45-继承举例、方法覆盖

    一.Java语言中假设一个类没有显式的继承任何类,那么该类默认继承Java SE库中提供的java.lang.Object类 1.快捷键:Ctrl + shift + T:可以在Myeclipse中查 ...

  2. 1+x证书web前端开发jquery专项练习测试题

    javascript程序设计-题库 1.下面哪一种不属于Jquery的选择器? A. 基本选择器 B. 层次选择器 C. 表单选择器 D. 节点选择器 答案: D 2.如果需要匹配包含文本的元素,用下 ...

  3. Luogu P2570 [ZJOI2010]贪吃的老鼠

    Luogu P2570 [ZJOI2010]贪吃的老鼠 题目描述 奶酪店里最近出现了\(m\)只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产\(n\)块奶酪,其中第\(i\)块的 ...

  4. python 学习(day1)

    初识python python的创始人为吉多*范罗苏姆(Guido van Rossum).1989年圣诞节期间,开发出来的脚本解释程序. python是⼀⻔什么样的语言 python 是一门解释型语 ...

  5. Linux下科学计数法(e)转化为数字的方法 [shell中几种数字计算说明]

    科学计数法使用e标识数值,将科学计算学转化为数字的思路:按e右边的数字移动小数点位数.e右边的数字如果是负数,则向左移动小数点.示例如下: 1.2345678e2 = 123.45678 1.2345 ...

  6. onunload事件不触发的探索

    如果现有一需求:浏览器页面关闭时弹出一个对话框,询问是否要退出,应该怎么做呢?    可用onunload事件来实现,该事件会在刷新和关闭页面时执行 我用如下3种方法绑定该事件,但所有主流浏览器都无法 ...

  7. Java设计模式:Flyweight(享元)模式

    概念定义 享元(Flyweight)模式运用共享技术高效地支持大量细粒度对象的复用. 当系统中存在大量相似或相同的对象时,有可能会造成内存溢出等问题.享元模式尝试重用现有的同类对象,如果未找到匹配的对 ...

  8. idea 方法注释live template

    groovyScript("def result=''; def params="${_1}".replaceAll('[\\[|\\]|\\s]', '').split ...

  9. dictionary vs Hashtables

    DictionaryDictionary is generic type Dictionary<TKey,TValue>Dictionary class is a strong type ...

  10. webpack+vue路由

    只写路由部分的相关内容 需引入路由包 import Vue from 'vue' // 1. 导入 vue-router 包 import VueRouter from 'vue-router' // ...