Windows 中编译 ElasticFusion 的详细步骤

2020/04/05 Library

ElasticFusion 是一个很有名的基于 surfel 的开源实时三维重建算法。它在 Linux 中的安装很方便,作者提供的 build.sh 可以安装全部依赖库和工程本身。但是,ElasticFusion 在 Windows 中的安装则臭名昭度,特别繁琐,因为它的几个依赖库在 Windows 中非常难编译,例如 SuiteSparse 和 Pangolin。

网上已经有一个教程解释的很详细了:https://blog.csdn.net/zhuxiaoyang2000/article/details/70171128,不过笔者还是遇到了不少该教程中没有写到的问题,因此再总结一个更详细的文档。


基础环境

  • Windows 10

  • Visual Studio 2013 Update 5

  • CUDA 8.2

  • CMake 3:任意大于 3 的版本都足够了,推荐安装带 cmake-gui 的版本

笔者未尝试过其他的系统、编译器和 CUDA 版本,不过相信也是可以的,只是某些依赖库的安装需要特别注意罢了。

编译并安装 SuiteSparse

参考 CSDN 教程:https://blog.csdn.net/xiamentingtao/article/details/50100549

SuiteSparse 是一个很基础的数学库。在 Linux 的安装很容易,不过 Windows 安装比较繁琐,因为它依赖于另外几个数学库,例如 blas,lapack,cholmod 等。好在有大牛已经归纳了在 Windows 中使用的所有必要的依赖库到 suitesparse-metis-for-windows 这个 repo 中,使用它可以极大节省在 Windows 中的编译时间。这个 repo 中也给出了安装步骤(下面称之为 repo 教程),笔者严格按照这个步骤就能成功编译,并未遇到什么问题。

注意,suitesparse-metis-for-windows 中的 blas 和 lapack 库文件在 lapack_windows路径中。

安装步骤

安装 SuiteSparse 需要的依赖库都已经在这个 repo 里面了,无需安装其他的了。这里主要参考 repo 教程的步骤。

1) 从 suitesparse-metis-for-windows 中 clone 最新代码。不过,CSDN 教程用的是一个稍微老的 v1.3.0 版本的代码,笔者测试过这两个版本都可行。这里记代码根目录是 SP_ROOT,例如笔者的就是 C:/dev/suitesparse-metis-for-windows

2) 接下来是一个 optional 步骤,包含手动下载 SuiteSparse-X.Y.Z.tar.gz 和 metis 等,笔者建议直接跳过。

3) 简单修改一下 Metis 的 CMakeLists.txt 文件。这一步是新加的,来自 CSDN 教程,不过其实也算是 optional 的,它只会带来一个在最终编译 ElasticFusion 代码时遇到的一个 warning,并不影响编译。打开${SP_ROOT}/SuiteSparse/metis-5.1.0/CMakeLists.txt 文件(如果是 v1.3.0 版本,该路径在${SP_ROOT}/metis/CMakeLists.txt中)。然后,在开始的project(METIS) 这一行后面加上这一行:

cmake_policy(SET CMP0022 NEW)

其余部分不要动。

4) 接下来就是 repo 教程中的标准步骤了。打开 cmake-gui,设置 source directory 为代码根目录${SP_ROOT},建议设置 build directory 为根目录下新建的 build 文件夹,即 ${SP_ROOT}/build。 5) 点击 Configure,然后选择 Visual Studio 12 2013 Win64,一定记得选 64 位的(某些 CMake 版本中,32 还是 64 位的选择都是在同一个下拉列表中的,但某些 CMake 版本则是分两个下拉列表,这里一定要注意)。继续后,可能会出现很多红色项,不过不用管,之后它们会自动清除。这里建议修改 CMAKE_INSTALL_PREFIX 路径为${SP_ROOT}/build/install

如果遇到 “Cannot find source file: GKlib/conf/check_thread_storage.c”,此时可以手动设置 GKLIB_PATH 路径为根目录下的${SP_ROOT}/SuiteSparse/metis-5.1.0/GKlib路径(如果是 v1.3.0 版本则是 ${SP_ROOT}/metis/GKlib)。不过笔者并未遇到该问题。

5) 再次 Configure,发现全部的红色项应该都消失了才对。 6) 最后是 Generate。它会在 build 文件夹中生成对应的 sln 等 Visual Studio 工程文件。 7) 使用 Visual Studio 2013 打开 build 路径中的 sln 工程文件,然后编译名为 INSTALL 的工程即可。建议编译 Debug 和 Release 两个模式,它最终会将所有的库文件都放在你设置的CMAKE_INSTALL_PREFIX路径下。

编译错误和解决方法

https://github.com/jlblancoc/suitesparse-metis-for-windows/issues/30

一个可能遇到的错误是:

// Line 515 in `math.h`
_CRTIMP double __cdecl rint(_In_ double _X);

原因是,VS 2013 Update 5 中的 math.h 已经定义了 rint() 函数(似乎之前版本的 VS 中没有),但是 metis 中又定义了一个相同名称相同参数的函数。因此,解决方法是,找到并打开根目录下的${SP_ROOT}/SuiteSparse/metis-5.1.0/GKlib/gk_arch.h文件(如果是 v1.3.0 版本则是${SP_ROOT}/metis/GKlib/gk_arch.h ),然后找到类似如下的一段代码(在该文件最下面):

#ifdef __MSC__
#if (_MSC_VER < 1800) // 源代码这里是 1900
/* MSC does not have rint() function */
#define rint(x) ((int)((x)+0.5))
#endif
//....

将里面的 1900 改为 1800,这样便可以跳过定义 rint() 这个函数了。

安装后的各种路径汇总

这里汇总了笔者的多个路径,方便后面加入 VS 工程中。这里已经把所有的斜杠写成左斜杠。

CMake 文件路径

suitesparse-config.cmake 文件所在位置:

${SP_ROOT}/build/install/lib/cmake/suitesparse-5.4.0/

通常你要在 CMakeLists.txt 中使用 find_package(SuiteSparse) 时,需要指定这个位置。

头文件位置

${SP_ROOT}/build/install/include/suitesparse

静态库文件路径

${SP_ROOT}/build/install/lib
${SP_ROOT}/lapack_windows/x64 

这里第二个路径是 LAPACK 和 BLAS 库文件的路径,是最开始已经提供了的。当然你可以把该路径下的库文件都拷贝到第一个路径中,更方便。

库文件列表

  • Debug 模式下的静态库
libamdd.lib
libbtfd.lib
libcamdd.lib
libccolamdd.lib
libcholmodd.lib
libcolamdd.lib
libcxsparsed.lib
libklud.lib
libldld.lib
libspqrd.lib
libumfpackd.lib
metisd.lib
suitesparseconfigd.lib
libblas.lib
liblapack.lib
  • Release 模式下的静态库
libamd.lib
libbtf.lib
libcamd.lib
libccolamd.lib
libcholmod.lib
libcolamd.lib
libcxsparse.lib
libklu.lib
libldl.lib
libspqr.lib
metis.lib
suitesparseconfig.lib
libblas.lib
liblapack.lib
  • 动态库
libblas.dll
libgcc_s_dw2-1.dll
libgfortran-3.dll
liblapack.dll
libquadmath-0.dll

注意:

  • 和 Debug 模式下的库文件列表的区别是,Release 模式下的文件名去掉了最后的 ‘d’,不过最后两个文件 libblas 和 liblapack 例外,它们的 debug 和 release 文件名相同,因为是 suitesparse-for-windows 最初已经提供的,并非编译生成的。
  • 动态库文件全都在 ${SP_ROOT}/lapack_windows/x64 路径下,也是 suitesparse-for-windows 最初已经提供的。如果要使用 SuiteSparse,需要将这几个 dll 放到你的可执行文件 exe 的目录中(但是,运行 ElasticFusion 时并不需要使用这几个 dll)。

测试安装成功

repo 教程suitesparse-metis-for-windows 已经给出了两个 cmake 工程例子,方便测试。CSDN 教程中也给出了例子。

编译并安装 Pangolin

参考教程:

  • CSDN 教程:https://blog.csdn.net/zhuxiaoyang2000/article/details/70031571

  • Pangolin 官网:https://github.com/stevenlovegrove/Pangolin

Pangolin 是一个轻量级的集成了 OpenGL 进行图形渲染和交互的工具,它还支持视频 I/O。优点是:

  • 操作简单。它封装了繁琐的 OpenGL、视频操作以及 GUI 设置等复杂的地方,提供了简单的接口以便使用。这点有点类似 ImGUI。
  • 跨平台,并且还支持移动端。目前支持 Windows, Linux, OSX, Android and IOS。

从源码编译安装 Pangolin 其实并不难,其实只要参考官网给出的步骤就行了。但是,我们最终目的是为了编译 ElasticFusion,相应的,编译 Pangolin 时需要做一点相应的修改。

依赖库

  • Eigen 3:header-only 的,从官网下载后解压到本地即可。
  • (Optional) 其余的依赖库诸如 zlib, glew, jpeg 等,在编译 Pangolin 时默认是从网上下载源码并编译安装的。

编译步骤

0) 首先注意 Visual Studio 的版本和 Pangolin 的适配问题。

  • 使用 Visual Studio 2013 x64 编译 Pangolin 最新的 Github 上的代码会出现很多 Error,因为最新版的 Pangolin 代码需要 C++11 支持,但是 VS 2013 Update 5 并不能完全支持。因此,如果必须要用 VS 2013,推荐使用 Pangolin 0.5 版本的代码。经测试编译成功。本文就以 Pangolin 0.5 版本为例。
  • 使用 Visual Studio 2017 或 2019 x64 编译 Pangolin 最新代码并没有问题(可以参考这个教程:https://zhuanlan.zhihu.com/p/74958936)。不过,在 VS 工程中,有可能会遇到找不到 glew 的问题,需要用户自己设置 glew 的各种路径。

记 Pangolin 的根目录为 Pangolin_DIR

1) 打开 cmake-gui,指定 source directory 是 ${Pangolin_DIR},build directory 就是 ${Pangolin_DIR}/build,点击 Configure,然后同样选择 Visual Studio 12 2013 Win64 的编译器,继续。

2) 根据ElasticFusion建议,取消勾选 MSVC_USE_STATIC_CRT 和 BUILD_EXAMPLES。另外,推荐修改CMAKE_INSTALL_PREFIX 路径为${Pangolin_DIR}/build/install

3) 接下来一步非常关键,它直接影响了后面编译 ElasticFusion 中可能出现的一个错误。此时要先勾选 cmake-gui 中的 Advanced 选框,这样就会列出全部的参数。然后,找到 EIGEN3_INCLUDE_DIR(如果没有这个参数,那么点击 Add Entry 添加一个,选择 Type 是 Path),修改它的值为你本地的 Eigen3 的路径。

注意,这里的操作和 CSDN 教程不同。教程写的是直接 Add Entry 添加这个参数,但是笔者经过测试,首先该参数是已经存在的,无需再添加;另外,如果直接选择 Add Entry 添加,反倒是没有添加成功,该参数的值一直是 Not Found。加上 Eigen3 的支持是因为,Pangolin 中定义了两种矩阵形式,一种是 Eigen 的,另一种似乎是自定义的,而默认如果找不到 Eigen 路径时,它就会使用自定义的矩阵形式。而 ElasticFusion 中某些函数使用 Eigen 矩阵作为 Pangolin 函数的传入参数,因此如果不设置 Eigen 就会出错。

4) 再次 Configure,这次应该就没错误了。然后 Generate,生成 VS 的工程文件到${Pangolin_DIR}/build路径下。

5) 使用 VS 2013 打开刚生成的 sln 文件,然后编译 INSTALL 工程即可,推荐 Debug 和 Release 两种模式都编译。

编译成功后,各种小的依赖库诸如 glew, jpeg, zlib 等都会放在 ${Pangolin_DIR}/build/external路径下。记着这个路径,接下来编译 ElasticFusion 还要用到。

编译 ElasticFusion

参考教程:https://blog.csdn.net/zhuxiaoyang2000/article/details/70171128

终于到了最后一步了。首先从 ElasticFusion 官网 clone 最新代码,记根目录为ElasticFusion

依赖库

除了上面编译的库外,这里还需要 OpenNI2,这个就很简单了,直接去官网下载 Windows 的 Binary 安装文件安装即可。

编译 Core

ElasticFusion 分两块,一块是 Core,包含核心代码。另一部分是 GUI,包含了图形界面和使用 Core 生成的库文件。

步骤如下:

1) 在 ElasticFusion/Core/src/CMakeLists.txt文件中增加以下两个路径变量名:

set(SUITESPARSE_INCLUDE_DIR "C:/dev/suitesparse-metis-for-windows/build/install/include/suitesparse")
set(SUITESPARSE_LIBRARY_DIR "C:/dev/suitesparse-metis-for-windows/build/install/lib")

否则 cmake 找不到 SuiteSparse 依赖库的安装位置。注意把这两行放在 find_package(SuiteSparse)这一行之前。这两个路径就是你的 SuiteSparse 安装路径。

一般情况下 ,cmake 都能够找到 Pangolin 的位置。如果找不到,那么可以在 CMakeLists 中的 find_package 语句中加上你安装的 Pangolin 的路径,类似如下:

find_package(Pangolin REQUIRED PATHS C:/dev/Pangolin/build/src)

2) 使用 cmake-gui,指定 source directory 为 ElasticFusion/Core/src,build directory 为 ElasticFusion/Core/build。点击 Configure,然后同样选择 Visual Studio 12 2013 Win64 的编译器,继续。

3) 再次 Configure,红色项应该全都会消失。接着 Generate 。然后使用 VS 2013 中打开 ElasticFusion/Core/build 中生成的 sln 文件。

4) 接下来这一步非常关键,这一步是为了解决 ElasticFusion 代码的一个 Bug,它可以编译成功,但是运行时候直接崩溃。这是网上其他人的解决方法,具体参见上面给出的 CSDN 教程。

首先,将下面的代码保存为 static_glew_init.hpp文件,保存在ElasticFusion/Core/src路径下,然后在你的 VS 中的 efusion 工程的 Header Files 中添加这个文件。

/*
Function to get, find, or initialize Pangolin context and initialize glew
*/
#if !defined(__STATIC_GLEW_INIT_HPP__)
#define __STATIC_GLEW_INIT_HPP__
#define GLEW_STATIC
#include <pangolin/pangolin.h>
#include <pangolin/gl/gl.h>
#include <pangolin/gl/glplatform.h>
#include <pangolin/display/display_internal.h>
#include <string>

static inline void staticGlewInit(std::string name = "Main")
{
    // GetCurrentContext
    pangolin::PangolinGl* context = pangolin::GetCurrentContext();
    if (context == NULL)
    {
        // Find Context
        context = pangolin::FindContext(name);
        if (context == NULL)
        {
            // Create new
            std::shared_ptr<pangolin::PangolinGl> newcontext(new pangolin::PangolinGl());
            AddNewContext(name, newcontext);
            context = newcontext.get();
            std::cout << "Pangolin Context" << name << "created." << std::endl;
        }
        else
        {
            std::cout << "Pangolin Context" << name << "already exists." << std::endl;
        }
        // Make Current Context
        context->MakeCurrent();

        //  Initialize GLEW
        glewExperimental = GL_TRUE; // GL_FALSE; 
        GLenum error = glGetError();
        if (error != GL_NO_ERROR)
        {
            std::cout << "OpenGL Error: " << error << std::endl;
        }
        GLenum glewinit = glewInit();
        if (glewinit != GLEW_OK) {
            std::cout << "Glew Error: " << glewGetErrorString(glewinit) << std::endl;
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        std::cout << "Pangolin Current Context exists." << std::endl;
    }
}
#endif /* !__STATIC_GLEW_INIT_HPP__ */

接着,打开 ElasticFusion/Core/src/Shaders/Shaders.h 文件,在 Shader类的构造函数中加上上面新添加的那个 staticGlewInit() 函数,类似如下:

class Shader : public pangolin::GlSlProgram
{
    public:
        Shader()
        {
            // glewInit
            staticGlewInit(); // 新加这一行,这里的构造函数默认是空的。
        }
    ...
        

这样就可以了。

5) 继续编译,如果还遇到小的编译错误,参考下面的介绍。 编译成功后会生成静态库和动态库。

编译错误

可能遇到的错误是找不到 cholmod.h 文件或者 glew 文件。很简单,找到对应路径并加入 VS 工程即可。cholmod.h 就在 SuiteSparse 安装路径的头文件中,前面已经介绍了,例如 ${SP_ROOT}/build/install/include/suitesparse。glew 文件则可以使用编译 Pangolin 时编译的 glew 文件,通常在${Pangolin_DIR}/build/external/glew路径下,包括头文件和 lib 等。

值得注意的是,前面编译 Pangolin 时的第 3 步加入了 Eigen3 的路径,否则,这里会遇到一个 pangolin 某个函数找不到 overloading functions 的问题。

编译 GUI

到了最后一步了,最后这一步倒是挺简单的了。

步骤:

1) 在 ElasticFusion/GUI/src/CMakeLists.txt文件中增加以下下面所有相关路径:

set(JPEG_LIBRARY "C:/dev/Pangolin/build/external/libjpeg/lib/jpeg.lib")
set(JPEG_INCLUDE_DIR "C:/dev/Pangolin/build/external/libjpeg/include")
set(ZLIB_LIBRARY "C:/dev/Pangolin/build/external/zlib/lib/zlibd.lib")
set(ZLIB_INCLUDE_DIR "C:/dev/Pangolin/build/external/zlib/include")
set(OPENNI2_INCLUDE_DIR "C:/Program Files/OpenNI2/Include")
set(OPENNI2_LIBRARY "C:/Program Files/OpenNI2/Lib/OpenNI2.lib")
set(SUITESPARSE_INCLUDE_DIR "C:/dev/suitesparse-metis-for-windows/build/install/include/suitesparse")
set(SUITESPARSE_LIBRARY_DIR "C:/dev/suitesparse-metis-for-windows/build/install/lib")
set(EFUSION_LIBRARY "C:/dev/ElasticFusion/Core/build/Debug/efusion.lib")
set(BLAS_LIBRARIES_DIR "C:/dev/suitesparse-metis-for-windows/lapack_windows/x64")
set(LAPACK_LIBRARIES_DIR "C:/dev/suitesparse-metis-for-windows/lapack_windows/x64")

这里的 jpeg 和 zlib 库都是编译 Pangolin 的依赖库,它们都放在 Pangolin 的生成目录下。EFUSION_LIBRARY就是上一步生成的 Core 的库文件。

2) 接下来就还是 cmake-gui 打开 –> 指定位置 –> Configure –> Generate –> 使用 VS 打开工程目录并编译的标准流程了。常见的编译错误参见下面一章的介绍。

3) 编译成功后,要使用 exe 的话,还要将相关的 dll 文件拷贝到对应 exe 的目录下,例如 efusion.dll, OpenNI2.dll, zlib.dll, glew.dll 等。

编译错误

1) 找不到 pangolin::glDrawFrustum 函数

这个函数在 GUI\src\Tools\GUI.h中,其实是写错了,应该是 glDrawFrustrum,少了个 ‘r’。:-<

2) glew 的问题

https://github.com/mp3guy/ElasticFusion/issues/65

可能会遇到这个问题:

error LNK2005: glewInit already defined in efusion.lib(efusion.dll)	C:\dev\ElasticFusion\GUI\build\glewd.lib(glew.obj)

解决方法很简单:在 VS 的工程设置中,把 efusion.lib 放到 glew(d).lib 的后面就行了。

编译成功纪念

终于成功了,给出一个小的 demo,使用的是 ./ElasticFusion.exe -l dyson_lab.klg 命令。

Search

    Table of Contents