ORB-SLAM2应用练习:三维重建系统搭建 (1)

概述


本博客记录我使用ORB-SLAM2+立体匹配的算法实现一个简单的三维重构系统的过程。

我的思路是这样:从一组双目序列中,得到一条轨迹,在轨迹上对一帧中的两张图作三维重构,并画在帧所在的坐标上,这样就能得到一组数据三维重构的结果。因此,可以用ORB-SLAM2来得到这条轨迹;三维重构需要用到立体匹配的算法,我打算采用opencv封装好的SGBM,但不失扩展性我会采取一些方法保证算法可以随时变更。由于系统在以后有可能变成实时的,因此图像序列来源可以来自真实相机,因此我打算对相机也做一层抽象。最终呈现的效果将会是一幅点图,我打算采用pangolin进行绘图。

综上,我的配置过程和本文章的组织顺序是:

  • ORB-SLAM2的封装
  • 相机类的抽象
  • 立体匹配算法的封装
  • 三维重构系统的搭建

开始阐述我的过程之前,首先明确我们的平台和软件工具是:

  • window 10
  • 64位程序
  • vs 2017
  • C++

ORB-SLAM2的封装


我们首先来封装ORB-SLAM2。在这里,我得假设诸位已经成功在目标平台上,成功运行了ORB-SLAM2原始版本的程序。事实上,这篇博客是从[我的上一个系列博客]继承来的,因此我现在的状态是,已经为ORB-SLAM2配置好了各个依赖库,以及以ORB-SLAM2为启动项目,在vs下成功运行了。现在我要做的事情是,将ORB-SLAM2导出为dll,供后面三维重构系统使用。

在vs下配置过导出库的同学都知道,我们需要为被调用到的类加上API宣言,但ORB-SLAM2并不是一个库,它有很多烦人的头文件我们也不会用到,因此我更倾向于将它视为一种服务,我不会在ORB-SLAM2的源码上做修改,而是在ORB-SLAM2的vs工程添加以下两个文件装门用于封装导出:

// SLAM.h
#pragma once
#ifdef SLAM_EXPORTS
#define SLAM_API __declspec(dllexport)
#else
#define SLAM_API __declspec(dllimport)
#endif

#include 
#include 

using std::string;
using cv::Mat;

class SLAM_API SLAM
{
public:
    enum eSensor {
        MONOCULAR = 0,
        STEREO = 1,
        RGBD = 2
    };

public:
    void init(const string &strVocFile, const string &strSettingsFile, const eSensor sensor, const bool bUseViewer = true);
    Mat track(const Mat & im1, const Mat & im2 = Mat());

    static SLAM * createSingleObject();

private:
    SLAM();
    SLAM(const SLAM & slam) = delete;

    ~SLAM();
};

extern SLAM_API SLAM & slam;

SLAM类即是对ORB-SLAM2的导出封装,我采用的是单例模式来设计这个类,同时导出该类唯一已被定义的对象的引用。我对ORB-SLAM2中的System类的接口做一下简化:即将单目和双目的接口统一在一个接口track下(RGBD的选项暂时不被我考虑在内)。对ORB-SLAM2系统的初始化,在SLAM的init接口中完成,因此要求用户需要手动调用init函数。

从该头文件中,看不到有关ORB-SLAM2的痕迹,这样就成功封装了ORB-SLAM2。该类的实现如下文件所示:

// SLAM.cpp
#include "SLAM.h"
#include "System.h"
#include 

using namespace ORB_SLAM2;

System * slamobj = nullptr;

SLAM  & slam = *SLAM::createSingleObject();

SLAM::SLAM()
{
}

SLAM::~SLAM()
{
    slamobj->Shutdown();
    delete slamobj;
    slamobj = nullptr;
}

void SLAM::init(const string & strVocFile, const string & strSettingsFile, const eSensor sensor, const bool bUseViewer)
{
    if (slamobj != nullptr)
    {
        slamobj->Shutdown();
        delete slamobj;
    }

    slamobj = new System(strVocFile, strSettingsFile, ORB_SLAM2::System::eSensor(sensor), bUseViewer);
}

Mat SLAM::track(const Mat & im1, const Mat & im2)
{
    static double timestamp = 0;
    Mat r;
    if (im2.data == nullptr)
        r = slamobj->TrackMonocular(im1, timestamp);
    else r = slamobj->TrackStereo(im1, im2, timestamp);
    timestamp += 0.01;

    return r;
}

SLAM * SLAM::createSingleObject()
{
    static bool firstTime = true;
    if (firstTime)
    {
        firstTime = false;
        return new SLAM();
    }
    else return &slam;
}

现在我们需要配置一下vs工程,让其生成dll:右键项目->属性->常规->配置类型,从exe改成动态库/静态库,同时配置编译模式为Release x64,至此,准备工作已经结束,点击编译,不出意外,在项目文件夹中已经成功生成ORB-SLAM2的库文件。

我们需要测试一下是否真的导出成功,新建一个工程,就起名为“3DRestruct”吧,在该工程项目目录下新建一个文件夹“3rd”,用于放ORB-SLAM2(这个不太正式的依赖库),将SLAM.h放在3rd/include下将ORB-SLAM2生成的库文件(.lib, .dll)放在3rd/lib下。为该工程添加ORB-SLAM2依赖的那些库的属性表到Release x64文件夹下,新建一个main.cpp文件,添加以下测试代码:

#include 

int main()
{
    slam.init("../Run/Vocabulary/ORBvoc.txt", "../Run/Setting/KITTI03.yaml", SLAM::eSensor::STEREO);
    system("pause");

    return 0;
}

注意到,为了让其编译成功,还需要配置一下项目的包含目录和库目录以及库文件,使其能够找到我们的ORB-SLAM2:右键项目->属性->VC++目录->包含目录,添加./3rd/include(目录仅供参考),另外,我直接在当前对话窗口->链接器->输入->附加依赖项,添加./3rd/lib/ORB-SLAM2.lib(目录与名称仅供参考),为库阐明位置,也就不用指定库目录了。

将编译模式调为Release x64,并为该工程配置运行环境:右键项目->属性->调试->环境,输入值:

path=D:/Packages/DBoW2/lib/;D:/Packages/g2o_orbslam/lib/;D:/Packages/Pangolin/lib/;D:/Packages/OpenCV3_1/opencv/build/x64/vc14/bin/;$(ProjectDir)/3rd/lib/;

该环境中的路径仅供参考,要强调的一点是,别忘了配置ORB-SLAM2动态库的路径。OK,现在点击编译运行,出现以下画面就没问题了:
ORB-SLAM2应用练习:三维重建系统搭建 (1)_第1张图片

不想一下子写太多,本文就先到此为止了。


发现以上代码中有误:封装SLAM的track函数,返回值没有赋值。已修正。

你可能感兴趣的:(ORB-SLAM2)