OpenCV-3.1のsfmモジュール[前編] Visual Studioでビルドする

　
これは、OpenCV Advent Calendar 2016 16日目の記事です。関連記事は[目次](http://qiita.com/advent-calendar/2016/opencv)にまとめられています。

---

1. はじめに
===

皆さんご存知のように，OpenCV-3.1で(contribに)sfmモジュールが入りました．皆さん使っていますか？もちろん僕は毎日バリバリ使って…ません．ビルドしてサンプルを試しただけで，実用として使ったことは一度もありません．しかし，[そこにコードがある限り，完全体を目指してビルドする](http://qiita.com/fukushima1981/items/fae7d40123bfdf426624)のは人の性というものでしょう．

ということで，sfmモジュール入りのOpenCVビルドとサンプル実行について，実際に試した人しか知らない落とし穴を前編・後編に分けて紹介します．

本日はVisualStudio 2015でsfmモジュールをビルドします．以下では64bitでReleaseビルドをしています．
cygwinでビルドしたい人は，[こちら](http://hvrl.ics.keio.ac.jp/kimura/opencv/opencv-3.1.0.html.ja)を参照してください．
Linuxでビルドしたい人は，以下で必要な依存ライブラリは全てapt-getできたはずなので，依存ライブラリのインストール後に3章のOpenCVのビルド設定を参考にしてください．
その他の環境(macOSとかAndroidとか)については全く知らないのでごめんなさい．

なお，軽く検索しただけで，[藤本さんがlibmv（←opencv_sfmへ移植される前の元コード）をビルド](http://qiita.com/SatoshiRobatoFujimoto/items/c87b14cd20cda3c97306)していたり，それを参考にして[opencv_sfmをlibmvへ上書きしてビルドしている方](https://blog.negativemind.com/2016/05/27/build-opencv-3-1-with-contrib-module-on-win-10-pro-visual-studio-2015/)などが見受けられますが，ここではOpenCVの~~素材本来の味を生かした~~本来のビルドシステムの中でビルドします．

また，以下では各種ライブラリについてgithubでのmasterコードは使わず，バージョンナンバーがある（＝リリースされた）コードを使っていますが，コードのバージョンを固定して紹介しただけの理由ですので，masterでも基本的に同様の手順でいけるとは思います．

それと，以下の説明ではcmakeでいろいろな変数設定をしますが，Windowsのcmakeでは，何か変数を設定してからConfigureすると新しい変数が出てきたりするので，必要に応じてConfigureを織り交ぜながら試してみてください．


2. 前準備
===

まず，opencv_sfmのビルドに必要な具材を順番にビルドしていきます．


2.1 Eigen
---
[eigen](http://eigen.tuxfamily.org/) の本稿執筆時の最新版は[3.3.0](http://bitbucket.org/eigen/eigen/get/3.3.0.zip)です．
最初に3.3.0を使って作業した際には，最後のOpenCVのビルドの際に意味不明なエラーが出て，二晩ハマってから挫折しました．本稿を書き終えた後で念のため確認したら，今度はすんなりビルドが通りました．なので，3.3.0でも良いかもしれませんが，[3.2.10](http://bitbucket.org/eigen/eigen/get/3.2.10.zip)を使った方が安全だと思います．
いずれにせよEigenのセットアップは普通にcmake，ビルド，インストールでOKなはずです．


2.2 Glog
---
[glog](https://github.com/google/glog/)の本稿執筆時の最新版は[0.3.4](tree/https://github.com/google/glog/v0.3.4)です．
配布物にgoogle-glog.slnが含まれているので，それを使います．VisualStudioで開けばわかりますが，これは32bitビルド用なので，構成マネージャで64bitビルドのプロジェクトに変換します．

また，配布されているコードをそのままVisualStudio2015でビルドするとエラーが出ます．[こちら](https://github.com/google/glog/issues/41)に書かれているように，port.ccの中のsnprintf()をコメントアウトすればビルドが通ります．

ここではlibglog_staticではなくlibglogをビルドしました．


2.3 Gflags
---
[gflags](https://github.com/gflags/gflags/)の本稿執筆時の最新版は[2.2.0](https://github.com/gflags/gflags/tree/v2.2.0)です．

　BUILD_SHARED_LIBS

にチェックを入れてから，普通にcmakeしてビルドしました．


2.4 SuiteSpase，Metis
---
[Windows対応SuiteSpase + Metis](https://github.com/jlblancoc/suitesparse-metis-for-windows)の本稿執筆時の最新版は[1.3.1](https://github.com/jlblancoc/suitesparse-metis-for-windows/tree/v1.3.1)です．

デフォルトのままでcmakeは通りますが，ビルドするとエラーが出ます．この原因はmetis/GKlib/gk_arch.hの61行目からの

```cpp
 61 #ifdef __MSC__
 62 /* MSC does not have rint() function */
 63 #define rint(x) ((int)((x)+0.5))
 64
 65 /* MSC does not have INFINITY defined */
 66 #ifndef INFINITY
 67 #define INFINITY FLT_MAX
 68 #endif
 69 #endif
```

の部分です．VisualStudio 2015では"does not have"ではないので，削除してしまっても構いませんが，私は

```cpp
 61 #if defined __MSC__ && _MSC_VER < 1900
 62 /* MSC does not have rint() function */
 63 #define rint(x) ((int)((x)+0.5))
 64
 65 /* MSC does not have INFINITY defined */
 66 #ifndef INFINITY
 67 #define INFINITY FLT_MAX
 68 #endif
 69 #endif
```

のように修正しました．


2.5 Ceres-Solver
---
[Ceres-Solver](https://github.com/ceres-solver/ceres-solver)の本稿執筆時の最新版は[1.11.0](https://github.com/ceres-solver/ceres-solver/tree/1.11.0)です．1.12.0のrc版もあるようですが，とりあえず1.11.0を使うことにします．

cmakeするとエラーが出ますが，ここから設定を始めていきます．

　EIGEN_INCLUDE_DIR

を設定してConfigureすると，いくつか新たな変数が出てきます．

　gflags_DIR
　GLOG_INCLUDE_DIR
　GLOG_LIBRARY

を設定します．なお，GLOG_INCLUDE_DIRは"glog-0.3.4/src/"ではなく"glog-0.3.4/src/windows/"なので注意しましょう．続いて，

　CXSPARSE
　LAPACK

にチェックを入れて，

　CXSPARSE_INCLUDE_DIR
　CXSPARSE_LIBRARY
　BLAS_blas_LIBRARY
　LAPACK_lapack_LIBRARY

にsuitesparse-metis-for-windowsに含まれているライブラリを指定します．ライブラリの設定にミスると，Configureした後でチェックが外されてしまうので要注意です．ここまでできたら，

　SUITESPARSE

にチェックを入れてConfigureします．また大量に変数が増えますが，

　METIS_LIBRARY
　UFCONFIG_INCLUDE_DIR
　AMD_INCLUDE_DIR
　AMD_LIBRARY
　CAMD_INCLUDE_DIR
　CAMD_LIBRARY
　CCOLAMD_INCLUDE_DIR
　CCOLAMD_LIBRARY
　CHOLMOD_INCLUDE_DIR
　CHOLMOD_LIBRARY
　COLAMD_INCLUDE_DIR
　COLAMD_LIBRARY
　SUITESPARSE_CONFIG_INCLUDE_DIR
　SUITESPARSE_CONFIG_LIBRARY
　SUITESPARSEQR_INCLUDE_DIR
　SUITESPARSEQR_LIBRARY

を全てsuitesparse-metis-for-windowsに含まれているライブラリで指定します．
ここまでできれば，あとはGenerateしてビルドできるはずです．


2.6 VTK
---
[VTK](http://www.vtk.org/download/)はvizモジュールには必須（というよりは，vizモジュールが実質的にVTKのwrapper機能である，というべきかもしれない）ですが，当然，sfmにVTKが必要ということはありません．しかし，OpenCVに含まれる「sfmのサンプルコード」のビルドにはvizモジュールが必須なので，今回はVTKも必須です．

VTKのweb siteをチェックすると，どうも本稿を執筆する直前の[11/25にVTK-7.1.0がリリースされた](https://blog.kitware.com/vtk-7-1-0/)ようです．ここはヒトバシラーとして，さっそく[VTK-7.1.0](http://www.vtk.org/files/release/7.1/VTK-7.1.0.zip)をcmake，ビルド，インストールして使いましょう．
もちろん[VTK-7.0.0](http://www.vtk.org/files/release/7.0/VTK-7.0.0.zip)や，[VTK-6.3.0](http://www.vtk.org/files/release/6.3/VTK-6.3.0.zip)でも構いません．どれもデフォルト設定のままで普通にビルドが通るはずです．

なお，[こちらのページ](https://blog.negativemind.com/2016/05/27/build-opencv-3-1-with-contrib-module-on-win-10-pro-visual-studio-2015/)では「現状、7.0.0ではOpenCVのビルドが上手くいかない」と書かれていますが，後述のようにOpenCV側のちょっとした修正で対応できます．


3. OpenCVのビルド
===

本丸のOpenCVですが，とりあえず[opencv-3.1.0](https://codeload.github.com/opencv/opencv/zip/3.1.0)と[opencv_contrib-3.1.0](https://codeload.github.com/opencv/opencv_contrib/zip/3.1.0)をダウンロード，展開してから，以降の作業に進みましょう．


3.1 vizモジュール設定
---

まずcmakeでopencv-3.1.0を読み込んでからConfigureすると，毎度おなじみの，設定変数がいろいろ表示された状態になります．そこでOPENCV_EXTRA_MODULES_PATHにopencv_contrib-3.1.0/modules/を設定しましょう．

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/ca6c4bc7-51e9-f94a-684a-d745144cc5ec.png)

再度Configureすると，contribのモジュールがビルド対象に追加されます．しかし，vizもsfmも”Unavailable”となります．依存ライブラリを設定していないので当然ですね．

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/9b7dcfbb-7df3-4af4-1987-ddd80c56e1aa.png)

では，VTKのパスを設定しましょう．ビルド後にインストールまでやった場合は，以下のように，VTK_DIRに"<インストール先>/lib/cmake/vtk-7.1/"を指定します．あるいはビルドしたフォルダを指定しても良かったような記憶も薄っすらとあります．

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/f55ae52a-67a3-8c52-6a3d-00322b8511e8.png)

さて，この状態でConfigureをすると，エラーになります．

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/1102cd46-e870-3139-c9d5-c2eff20639e8.png)

おそらく，「VTK-7だとビルドがうまくいかない」という風説はこれを指しているかと思います．
エラーは「vtkRenderingOpenGLがあらへんで」と言ってますが，たしかに存在しません．VTK-7からvtkRenderingOpenGL2という新モジュールへ移行したのが原因です．VTKのビルド時にOpenGL2ではなくOpenGLを使うように設定することも可能ですが，そういう後ろ向きの解決方法ではなく，ちゃんとOpenGL2を使うようにしましょう．

この問題を解決するためには opencv-3.1.0/cmake/OpenCVDetectVTK.cmake を修正します．冒頭部分で

```cmake:修正前
# VTK 6.x components
find_package(VTK QUIET COMPONENTS vtkRenderingOpenGL vtkInteractionStyle vtkRenderingLOD vtkIOPLY vtkFiltersTexture vtkRenderingFreeType vtkIOExport NO_MODULE)
```

となっていますが，私は

```cmake:修正後
# VTK 6.x/7.x components
find_package(VTK QUIET COMPONENTS vtkInteractionStyle NO_MODULE)
if("${VTK_RENDERING_BACKEND}" STREQUAL "OpenGL2")
  find_package(VTK QUIET COMPONENTS vtkRenderingOpenGL2 vtkInteractionStyle vtkRenderingLOD vtkIOGeometry vtkIOPLY vtkFiltersTexture vtkRenderingFreeType vtkIOExport NO_MODULE)
else()
  find_package(VTK QUIET COMPONENTS vtkRenderingOpenGL vtkInteractionStyle vtkRenderingLOD vtkIOGeometry vtkIOPLY vtkFiltersTexture vtkRenderingFreeType vtkIOExport NO_MODULE)
endif()
```

のように修正しました．
元はvtkRenderingOpenGLなどの有無でVTKの有無を判定していましたが，修正後は「まずはvtk-6,7に共通なモジュールでVTK自体の存在を確認」→「存在した場合には，OpenGLかOpenGL2かを確認」→「場合分けをして，必要な全モジュールの有無をチェック」という処理になっています．
どうもVTK-7.0と7.1で微妙にライブラリの仕様が変わったようで，7.0で動いていた設定では7.1はダメだったのですが，とにかく上記の修正をすれば6.3も7.0も7.1もいけるはずです．

さて，この修正をしてからVTK_DIRを設定してConfigureすれば，VTKも認識され，めでたくvizモジュールがビルド対象に入ります．

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/30958520-9e51-c52c-31ce-dd5e2bba88cb.png)


3.2 sfmモジュール設定
---
次に，sfmに必要なライブラリを設定していきます．

　EIGEN_INCLUDE_PATH
　WITH_EIGEN
　GLOG_Libs
　Ceres_DIR

の設定が必要です．これらを設定してConfigureすると，

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/8f78745c-a65c-56e1-9076-d19e4fac433d.png)

このようにライブラリが検出されます．これでsfmがビルド対象に入るわけです．sfmがenableされているかどうかを見てみましょう．

![image](https://qiita-image-store.s3.amazonaws.com/0/105294/e3d0cd14-2c12-0339-b767-d25264a99e3b.png)

ん？あ，あれっ？

**"Module opencv_sfm disabled" …だと？！(ﾟдﾟlll)ｶﾞｰﾝ**

　
　
…この謎に対応しようと右往左往してネタを引っ張ろうかとも思いましたが，結論から言うと，いくらcmake上で設定をいじっても，ここでopencv_sfmをenableすることは絶対にできません．なぜかというと，opencv_contrib-3.1.0/modules/sfm/CMakeLists.txtに原因があるからです．16行目以降を確認すると

```cmake
 16 if(NOT DEFINED SFM_DEPS_OK)
 17
 18   set(_fname "${CMAKE_CURRENT_BINARY_DIR}/test_sfm_deps.cpp")
 19   file(WRITE "${_fname}" "#include <glog/logging.h>\n#include <gflags/gflags.h>\nint main() { (void)(0); return 0; }\n")
 20   try_compile(SFM_DEPS_OK "${CMAKE_CURRENT_BINARY_DIR}" "${_fname}"
 21       CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${GLOG_INCLUDE_DIRS};${GFLAGS_INCLUDE_DIRS}"
 22       LINK_LIBRARIES ${GLOG_LIBRARIES} ${GFLAGS_LIBRARIES}
 23       OUTPUT_VARIABLE OUTPUT
 24   )
 25   file(REMOVE "${_fname}")
 26   message(STATUS "Checking SFM deps... ${SFM_DEPS_OK}")
 27 endif()
 28
```

というコードになっています．この16行目の"NOT DEFINED"という条件分岐が曲者で，要は，一回でもConfigureしてしまうとSFM_DEPS_OKにはTRUEかFALSEが設定されてしまっているため，その後は何度cmakeを走らせても二度と変更されません．
そして，**Windows環境では最初に一度ConfigureしないとGUIに変数が表示されないので，結果的に必ずdisableされる**わけです．最初これを見たときには殺意を（ry
 

気を取り直して，16行目を

```cmake
 16 if(NOT DEFINED SFM_DEPS_OK OR NOT SFM_DEPS_OK)
```

のように修正します．


ということで，この落とし穴が本日のネタでした．この修正後もうまくいかない場合は，さらに新たな変数として

GFLAGS_INCLUDE_DIRS
GFLAGS_LIBRARIES

を"Add Entry"ボタンから追加して，それぞれ，"\<gflagsインストール先\>/include/"と，"\<gflagsインストール先\>/lib/gflags.lib"を設定してみてください．

その他の修正点を以下に列挙します．

opencv_contrib-3.1.0/modules/sfm/CMakeLists.txt

```cmake:修正前
 59 if(Ceres_FOUND)
 60   add_definitions("-DCERES_FOUND=1")
 61   list(APPEND LIBMV_LIGHT_LIBS simple_pipeline)
 62 else()
```

```cmake:修正後
 59 if(Ceres_FOUND)
 60   add_definitions("-DCERES_FOUND=1")
 61   list(APPEND LIBMV_LIGHT_LIBS simple_pipeline)
 62   list(APPEND LIBMV_LIGHT_INCLUDES ${CERES_INCLUDE_DIRS})
 63 else()
```

```cmake:修正前
130 if(Ceres_FOUND)
131   ocv_add_samples(opencv_viz)
132 endif ()
```

```cmake:修正後
131 if(Ceres_FOUND AND BUILD_opencv_viz)
132   ocv_add_module(viz)
133   ocv_add_samples(opencv_sfm)
134 endif ()
```

opencv_contrib-3.1.0/modules/sfm/src/libmv_light/libmv/multiview/CMakeLists.txt

```cmake:修正前
 20 TARGET_LINK_LIBRARIES(multiview glog numeric)
```

```cmake:修正後
 20 TARGET_LINK_LIBRARIES(multiview ${GLOG_LIBRARIES} numeric)
```

3.3 ビルド
--
私の今回の環境ではBUILD_opencv_hdfとBUILD_opencv_bioinspiredにも自動でチェックが入っていたのですが，この２つがビルド時にエラーになりました．（以前にビルドした際はbioinspiredはビルドできていたはずなので，何かライブラリのバージョン依存の問題などかもしれません．）どちらも今回の目的とは関係ないので，ここではチェックを外してビルドしました．




4. まとめ
===

ということで，今日はopencv_sfmをVisualStudio 2015を使ってビルドしました．明らかにVisualStudioは想定されていない雰囲気ですが，それでも，それなりに手間をかけることでビルドできるということを紹介しました．

---

明日は，OpenCVに付属のサンプルコードを使ってSfMの動作確認をします．