OpenVSLAMをVisual Studio 2017でビルドする

# OpenVSLAM

https://github.com/xdspacelab/openvslam
ぱっと見はORB-SLAMを綺麗にして各種テストデータやWebカメラ対応されたようなイメージ
Ubuntuでのビルドは可能でWSLでもキッチリコンパイル可能

# この記事のモチベーション

今後機能拡張を検討するに当たってLinuxネイティブな環境を用意するのがしんどいため、慣れているWindows+VS(VC)環境に持っていく
※エディタにVSCode使えばLinuxでも問題ないんですがやっぱりWindowsが良いんですよね

# 前提

依存パッケージがいろいろあって、公式では

- Eigen3
- g2o
- SuiteSparse
- DBoW2(カスタム)
- yaml-cpp
- OpenCV
- gflags
- glog
- Pangolin(ビューワ)

が必要と書いてあるのですが、実際にはaptでインストールされるパッケージがあるため、
Windowsでは

- LAPACK
- BLAS

を追加で何とかする必要があります。
lapack3はSuiteSparseのビルドに必要なためビルド済みバイナリを使用します。
(全部コンパイルしてもいいけどMinGWを使ったコンパイル環境整えるのが超めんどくさい)

**※実はSuiteSparseのCMake時にHunterをEnableにした場合はLAPACK3は自前で用意しなくて良いみたいです**
**Hunterに関してはこちら ⇒ https://docs.hunter.sh/en/latest/packages/all.html**

# 本題
## 手順1: リポジトリ集め


### OpenVSLAM(本体)

openvslamリポジトリには直下に3rdディレクトリがあるのでそこに必要なパッケージを入れます
また、インストールディレクトリを作っておいた方が良いため、ディレクトリを作っておきます

```
git clone https://github.com/xdspacelab/openvslam.git
mkdir -p openvslam/3rd/install/include
mkdir -p openvslam/3rd/install/lib
cd openvslam/3rd
```

### Eigen
```
git clone https://github.com/eigenteam/eigen-git-mirror.git -b 3.3.7
```

### g2o
```
git clone https://github.com/RainerKuemmerle/g2o.git
cd g2o
git checkout 9b41a4ea5ade8e1250b9c1b279f3a9c098811b5a
cd ../
```

### SuiteSparse(Windowsビルド用バージョン)
```
git clone https://github.com/jlblancoc/suitesparse-metis-for-windows.git -b v1.5.0
```

### DBoW2(カスタムバージョン)
```
git clone https://github.com/shinsumicco/DBoW2.git
```

### yaml-cpp
```
git clone https://github.com/jbeder/yaml-cpp.git
```

### OpenCV
```
git clone https://github.com/opencv/opencv.git -b 3.4.0
```

### Pangolin
```
git clone https://github.com/stevenlovegrove/Pangolin.git
cd Pangolin
git checkout ad8b5f83222291c51b4800d5a5873b0e90a0cf81
cd ../
```

### glog
```
git clone https://github.com/google/glog.git -b v0.4.0
```

### gflags
```
git clone https://github.com/gflags/gflags.git -b v2.2.2
```

## 手順2: ビルド済みファイル集め

### lapack3

下記にVSでビルドされたバイナリがあります(RefBLAS, LAPACK, LAPACKE)
https://icl.cs.utk.edu/lapack-for-windows/lapack/#libraries
これらを3rd/install/includeと3rd/install/libに入れておきます

## 手順3: 依存ライブラリをCMakeでビルド

※依存の順番があるためその順に従っていきます
※test/sampleバイナリは不要なので時間短縮するためにビルド設定から省きます

### gflags
※BUILDファイルがあるためbuild_ディレクトリを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/a2ee0f81-f6ef-a8a1-803c-4fb0a6c39ab6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/cfc9cca6-7a20-525d-30be-03e75e054cfd.png)
*Release*でビルドしてINSTALLプロジェクトをビルド

### glog
※下記のようにgflagsのパスを設定します
※BUILDファイルがあるためbuild_ディレクトリを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/5d6c6302-e283-aad8-0cf9-9da066121364.png)
*Release*でビルドしてINSTALLプロジェクトをビルド

### yaml-cpp
※buildディレクトリを作成してCMake
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/69af7fed-ff86-ae4d-f4b3-9300e50315d8.png)
*Release*でビルドしてINSTALLプロジェクトをビルド

### Eigen
※Eigenはヘッダーオンリーライブラリ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/391613e9-5848-589e-4e08-84462ca88427.png)
INSTALLプロジェクトをビルド

### SuiteSparse
※LAPACK3のパスを設定します(**HunterをENABLEにする場合は不要**)
※PCREはDISABLEにしておきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/554be9a8-35e4-6d28-0a30-1ba9119db09b.png)
*Release*でビルドしてINSTALLプロジェクトをビルド

### OpenCV
※公式のページにあるように

```
cmake \
    -DENABLE_CXX11=ON \
    -DBUILD_DOCS=OFF \
    -DBUILD_EXAMPLES=OFF \
    -DBUILD_JASPER=OFF \
    -DBUILD_OPENEXR=OFF \
    -DBUILD_PERF_TESTS=OFF \
    -DBUILD_TESTS=OFF \
    -DWITH_EIGEN=ON \
    -DWITH_FFMPEG=ON \
    -DWITH_OPENMP=ON \
```
となるように設定します
※今回のケースでは不要だとは思いますがEIGEN_INCLUDE_PATHは3rd/install/include/eigen3を設定します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/69a3a4bb-e8ca-c133-3ed3-c1a9dfe330b1.png)
*Debug*と*Release*でビルドしてINSTALLプロジェクトをビルド

### DBoW2
※何故かOpenCV_DIRの解決に失敗するため、DBoW2/CMakeLists.txtを下記のように書き換えます

```
set("OpenCV_DIR" "../install")
find_package(OpenCV QUIET)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/8737d93e-cac2-4bf2-9a90-afcb44e7697b.png)
*Release*でビルドしてINSTALLプロジェクトをビルド
※ちなみにDebugではconvert_orb_vocab.exeのビルドに失敗します

### g2o
※こちらも公式にある通りに

```
cmake \
    -DBUILD_SHARED_LIBS=ON \
    -DBUILD_UNITTESTS=OFF \
    -DG2O_USE_CHOLMOD=ON \
    -DG2O_USE_CSPARSE=ON \
    -DG2O_USE_OPENGL=OFF \
    -DG2O_USE_OPENMP=ON \
```
となるように設定します

※CHOLMOD_LIBRARYが無いよと言われるはずなので3rd/install/lib/libcholmod.libを設定すると他のライブラリも解決してくれるようになります
※CHOLMOD_INCLUDE_DIRは3rd/install/include/suitesparseを指定します
※CHOLMOD_SOLVERプロジェクトを生成するためにBLAS_LIBRARIES_DIRとLAPACK_LIBRARIES_DIRに3rd/install/libをしています(**SuiteSparseでHunterを使用している場合はそのパスを指定します**)
※さらにLAPACK_LIBRARIESには"liblapack.lib", BLAS_LIBRARIESには"libblas.lib"のファイルパス設定します
※"Found CHOLMOD and its dependencies"のログが出ていることを確認します
※**CSPARSEがEXTERNALのディレクトリを参照していると不整合を起こすため、BUILD_CSPARSEをDisableにした上でCSPARSE_INCLUDE_DIRは3rd/install/include/suitesparse, CSPARSE_LIBRARYを3rd/install/lib/libcxsparse.libを参照するようにします**
※ビルド時間が長いためG2O_BUILD_EXAMPLEはDIABLEにします
※QT5のエラーは無視します
※必ずSHARED_LIBSを有効にします(CSPARSEがLGPLなため)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/49153552-03c1-6d09-9351-3673b3a34755.png)

*DEBUG*でビルドしてINSTALLプロジェクトをビルド
*Release*でビルドしてINSTALLプロジェクトをビルド

### Pangolin

※いったんエラーが出ますが再度ConfigureすればGenerate可能になります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/949dcb46-8699-fdad-9231-0b0e7f9bb5aa.png)
*Release*でビルドしてINSTALLプロジェクトをビルド

※私の環境ではランタイムライブラリの設定が**マルチスレッド(/MT)**になっていたので**マルチスレッドDLL(/MD)**にプロジェクト設定で変更してからビルドしました

## 手順4: OpenVSLAMをCMakeでビルド

※そのままではビルドできません

### openvslam/CMakeLists.txtを編集
- 下記のようにしてC++チェックのFATAL_ERRORを回避(本来はCMAKE_CXX_STANDARDを使うとVC対応ができるとのこと)

```
    message(STATUS "C++11 support: OK (-std=c++0x)")
#else()
#    message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
```

- -WextraとOgはコメントアウト
- VCではM_PIは暗黙的に使えないためM_PIを定義に追加

```
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
add_definitions(-DM_PI=3.14159265358979323846)
```

### Add Entryで項目を追加
- SUITESPARSE_CHECK_INCLUDE_DIRS: 3rd/install/include/suitesparse
- SUITESPARSE_CHECK_LIBRARY_DIRS: 3rd/install/lib/Release
- BLAS_LIBRARIES: install/lib/libblas.lib
- LAPACK_LIBRARIES: install/lib/liblapack.lib

### 〇〇_DIRに〇〇Config.cmakeのあるディレクトリを指定
- 下記のように地道にポチポチするとConfigureが通るようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/dd4f862f-8bef-804b-4e52-71693c70d9d6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/af035eba-8cda-c123-f53b-5e52a2d4498b.png)

### ソースコードの文字コードをSJISに変換
- BOM無しUTF-8のコードが含まれているためコンパイルに失敗するため
    - 本来はEditorConfigの拡張を使うと良いみたいです
- openvslam/src以下を全部変換
    - 一括変換ツールを使うと良いです(KanjiTranslatorとかPowerShellで変換コード組むとか)

### orb_extractor_node.hに#include \<array\>を追加
- 何故か#include \<list\>しかないため

```
#include <list>
#include <array>
```

### color_scheme.hに#include <cctype>を追加
- std::tolowerに必要だけどincludeされていないため

```
#include <array>
#include <string>
#include <cctype>
```
### dirent.hの代わりにmsdirent.hを用意
- 下記をダウンロードしてopenvslam/srcに配置する
    - http://svn.apache.org/repos/asf/avro/trunk/lang/c/tests/msdirent.h
- image_util.ccの#include \<dirent.h\>を#include "msdirent.h"へ書き換える

### 定義を.ccから.hへ移動
- いくつかのクラスに関して親クラスの仮想関数の定義が被ってしまう問題があるため(コンパイラ依存？)、定義を移動させます
- 下記のファイルのコンストラクタを.ccから.hへ移動します
    - equirectangular_pose_opt_edge.cc
    - equirectangular_reproj_edge.cc
    - perspective_pose_opt_edge.cc
    - perspective_reproj_edge.cc
    - shot_vertex.cc (SIM3とSE3の2つあります)
    - forward_reproj_edge.cc
    - transform_vertex.cc
    - landmark_vertex.cc
- 下記のファイルはメンバ関数すべてを.ccから.hへ移動します
    - backward_reproj_edge.cc
    - graph_opt_edge.cc
    - landmark_vertex_container.cc


### ビルド
- プロジェクト設定を改変
    - .DLLが生成される設定になっているので.libが生成されるように変更(openvslamプロジェクトとpangolin_viewerプロジェクト)
        - 出力拡張子も.dllから.libへ変更
    - コンパイラの警告が出れば/NODEFAULTLIBを設定
        - 例えば/NODEFAULTLIB:libcmt.lib
    - 必要なライブラリのリンクを追加
        - 下記のような内容を記述した.cppファイルをプロジェクトに追加すると楽です

```
#pragma comment(lib, "..\\..\\3rd\\install\\lib\\yaml-cpp.lib")
#pragma comment(lib, "..\\..\\3rd\\install\\lib\\g2o_types_slam3d.lib")
```
- コンパイル＆リンクがうまくいくことを確認

## 手順5: 動作確認

- 必要なDLLを集める
    - Dependenciesを見ると下記のようになっているのでそのdllを集めます
    - **FFMPEG(opencv_ffmpeg340_64.dll)は無くても起動でき、Pangolin表示で固まってしまうように見えるので必ず配置します**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/f4986c0e-22e0-e626-b44c-e5300e42ffbc.png)

- 公式の通りに実行します
    - https://openvslam.readthedocs.io/en/master/simple_tutorial.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277379/665e9e12-f41f-a509-0a38-200e4ce23af3.png)

## まとめ
- g2oのビルドがめちゃくちゃ大変ですがそこさえキッチリ行えばビルドできます
- 現状Track timeが100ms以降かかってしまっているのでSSEを有効にしたりFloatの精度を落としたりと工夫が必要そうです
- パフォーマンスモニタを見ながら一つ一つ潰していけば良さそうです