一、前面说了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. <Graph> Topological + Undirected Graph 310 Union Find 261 + 323 + (hard)305

    310. Minimum Height Trees queue:  degree为1的顶点 degree[ i ] : 和 i 顶点关联的边数. 先添加整个图,然后BFS删除每一层degree为1的节 ...

  2. DevExpress中TreeList树样式调整

    DevExpress的TreeList默认是没有树状线的,修改TreeLineStyle属性无效,这对于Tree并不好看. 解决方案一 官方解释说对于DevExpress的标准主题是不支持TreeLi ...

  3. IT兄弟连 Java语法教程 三目运算符

    Java提供了一个特殊的三目(三个分支)运算符,它可以替代特定类型的if-then-else语句结构.这个运算符是“?”乍一看可能有一些困惑,但一旦理解“?”运算符,就可以高效地使用它.“?”运算符的 ...

  4. gcc 编译安装

    wget https://kojipkgs.fedoraproject.org//packages/gcc/7.1.1/1.module_1a179a7b/src/gcc-7.1.1-1.module ...

  5. idea创建maven的web项目

    前言 今天搭xfire的时候,我想对xfire进行一下测试,就想弄个web工程试试,发现网上写的很多没有用的,就自己写了一下.十分精简.也介绍了如何解决maven骨架建立项目非常慢的问题. 介绍 1. ...

  6. 前端之javascript2

    js组成和标签获取元素 javascript组成 1.ECMAscript javascript的语法(变量.函数.循环语句等语法)2.DOM 文档对象模型 操作html和css的方法(比如通过id或 ...

  7. .net core项目启动时报_未处理Socket异常(以一种访问权限不允许的方式做了一个访问套接字的尝试。)

    解决方案:一般的原因就是程序的端口被占用了,关掉占用端口的程序即可正常使用.  查看启动地址和配置的webserver服务器的端口号是否被占用,可能占用的有:IIS启用项目,解决方案中其他启动项目配置 ...

  8. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之四 || Swagger的使用 3.2

    本文梯子 本文3.0版本文章 前言 一.swagger的一般用法 0.设置swagger页面为首页——开发环境 1.设置默认直接首页访问 —— 生产环境 2.为接口添加注释 3.对 Model 也添加 ...

  9. 《0day安全软件漏洞分析技术》学习笔记

    最近因为工作需要在看0day的软件漏洞分析,发现这本<0day安全软件漏洞分析技术(第2版)>真是本好书,唯一缺点就是书上的环境是Windows XP 32Bit的,基于现状难以进行实践, ...

  10. 为Dynamics 365 USD设置使用Chrome进程来驻留Web应用程序

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...