これは、OpenCV Advent Calendar 2016 16日目の記事です。関連記事は目次にまとめられています。
- はじめに
===
皆さんご存知のように,OpenCV-3.1で(contribに)sfmモジュールが入りました.皆さん使っていますか?もちろん僕は毎日バリバリ使って…ません.ビルドしてサンプルを試しただけで,実用として使ったことは一度もありません.しかし,そこにコードがある限り,完全体を目指してビルドするのは人の性というものでしょう.
ということで,sfmモジュール入りのOpenCVビルドとサンプル実行について,実際に試した人しか知らない落とし穴を前編・後編に分けて紹介します.
本日はVisualStudio 2015でsfmモジュールをビルドします.以下では64bitでReleaseビルドをしています.
cygwinでビルドしたい人は,こちらを参照してください.
Linuxでビルドしたい人は,以下で必要な依存ライブラリは全てapt-getできたはずなので,依存ライブラリのインストール後に3章のOpenCVのビルド設定を参考にしてください.
その他の環境(macOSとかAndroidとか)については全く知らないのでごめんなさい.
なお,軽く検索しただけで,藤本さんがlibmv(←opencv_sfmへ移植される前の元コード)をビルドしていたり,それを参考にしてopencv_sfmをlibmvへ上書きしてビルドしている方などが見受けられますが,ここではOpenCVの素材本来の味を生かした本来のビルドシステムの中でビルドします.
また,以下では各種ライブラリについてgithubでのmasterコードは使わず,バージョンナンバーがある(=リリースされた)コードを使っていますが,コードのバージョンを固定して紹介しただけの理由ですので,masterでも基本的に同様の手順でいけるとは思います.
それと,以下の説明ではcmakeでいろいろな変数設定をしますが,Windowsのcmakeでは,何か変数を設定してからConfigureすると新しい変数が出てきたりするので,必要に応じてConfigureを織り交ぜながら試してみてください.
- 前準備
===
まず,opencv_sfmのビルドに必要な具材を順番にビルドしていきます.
2.1 Eigen
eigen の本稿執筆時の最新版は3.3.0です.
最初に3.3.0を使って作業した際には,最後のOpenCVのビルドの際に意味不明なエラーが出て,二晩ハマってから挫折しました.本稿を書き終えた後で念のため確認したら,今度はすんなりビルドが通りました.なので,3.3.0でも良いかもしれませんが,3.2.10を使った方が安全だと思います.
いずれにせよEigenのセットアップは普通にcmake,ビルド,インストールでOKなはずです.
2.2 Glog
glogの本稿執筆時の最新版は0.3.4です.
配布物にgoogle-glog.slnが含まれているので,それを使います.VisualStudioで開けばわかりますが,これは32bitビルド用なので,構成マネージャで64bitビルドのプロジェクトに変換します.
また,配布されているコードをそのままVisualStudio2015でビルドするとエラーが出ます.こちらに書かれているように,port.ccの中のsnprintf()をコメントアウトすればビルドが通ります.
ここではlibglog_staticではなくlibglogをビルドしました.
2.3 Gflags
BUILD_SHARED_LIBS
にチェックを入れてから,普通にcmakeしてビルドしました.
2.4 SuiteSpase,Metis
Windows対応SuiteSpase + Metisの本稿執筆時の最新版は1.3.1です.
デフォルトのままでcmakeは通りますが,ビルドするとエラーが出ます.この原因はmetis/GKlib/gk_arch.hの61行目からの
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"ではないので,削除してしまっても構いませんが,私は
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の本稿執筆時の最新版は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はvizモジュールには必須(というよりは,vizモジュールが実質的にVTKのwrapper機能である,というべきかもしれない)ですが,当然,sfmにVTKが必要ということはありません.しかし,OpenCVに含まれる「sfmのサンプルコード」のビルドにはvizモジュールが必須なので,今回はVTKも必須です.
VTKのweb siteをチェックすると,どうも本稿を執筆する直前の11/25にVTK-7.1.0がリリースされたようです.ここはヒトバシラーとして,さっそくVTK-7.1.0をcmake,ビルド,インストールして使いましょう.
もちろんVTK-7.0.0や,VTK-6.3.0でも構いません.どれもデフォルト設定のままで普通にビルドが通るはずです.
なお,こちらのページでは「現状、7.0.0ではOpenCVのビルドが上手くいかない」と書かれていますが,後述のようにOpenCV側のちょっとした修正で対応できます.
- OpenCVのビルド
===
本丸のOpenCVですが,とりあえずopencv-3.1.0とopencv_contrib-3.1.0をダウンロード,展開してから,以降の作業に進みましょう.
3.1 vizモジュール設定
まずcmakeでopencv-3.1.0を読み込んでからConfigureすると,毎度おなじみの,設定変数がいろいろ表示された状態になります.そこでOPENCV_EXTRA_MODULES_PATHにopencv_contrib-3.1.0/modules/を設定しましょう.
再度Configureすると,contribのモジュールがビルド対象に追加されます.しかし,vizもsfmも”Unavailable”となります.依存ライブラリを設定していないので当然ですね.
では,VTKのパスを設定しましょう.ビルド後にインストールまでやった場合は,以下のように,VTK_DIRに"<インストール先>/lib/cmake/vtk-7.1/"を指定します.あるいはビルドしたフォルダを指定しても良かったような記憶も薄っすらとあります.
さて,この状態でConfigureをすると,エラーになります.
おそらく,「VTK-7だとビルドがうまくいかない」という風説はこれを指しているかと思います.
エラーは「vtkRenderingOpenGLがあらへんで」と言ってますが,たしかに存在しません.VTK-7からvtkRenderingOpenGL2という新モジュールへ移行したのが原因です.VTKのビルド時にOpenGL2ではなくOpenGLを使うように設定することも可能ですが,そういう後ろ向きの解決方法ではなく,ちゃんとOpenGL2を使うようにしましょう.
この問題を解決するためには opencv-3.1.0/cmake/OpenCVDetectVTK.cmake を修正します.冒頭部分で
# VTK 6.x components
find_package(VTK QUIET COMPONENTS vtkRenderingOpenGL vtkInteractionStyle vtkRenderingLOD vtkIOPLY vtkFiltersTexture vtkRenderingFreeType vtkIOExport NO_MODULE)
となっていますが,私は
# 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モジュールがビルド対象に入ります.
3.2 sfmモジュール設定
次に,sfmに必要なライブラリを設定していきます.
EIGEN_INCLUDE_PATH
WITH_EIGEN
GLOG_Libs
Ceres_DIR
の設定が必要です.これらを設定してConfigureすると,
このようにライブラリが検出されます.これでsfmがビルド対象に入るわけです.sfmがenableされているかどうかを見てみましょう.
ん?あ,あれっ?
"Module opencv_sfm disabled" …だと?!(゚д゚lll)ガーン
…この謎に対応しようと右往左往してネタを引っ張ろうかとも思いましたが,結論から言うと,いくらcmake上で設定をいじっても,ここでopencv_sfmをenableすることは絶対にできません.なぜかというと,opencv_contrib-3.1.0/modules/sfm/CMakeLists.txtに原因があるからです.16行目以降を確認すると
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行目を
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
59 if(Ceres_FOUND)
60 add_definitions("-DCERES_FOUND=1")
61 list(APPEND LIBMV_LIGHT_LIBS simple_pipeline)
62 else()
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()
130 if(Ceres_FOUND)
131 ocv_add_samples(opencv_viz)
132 endif ()
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
20 TARGET_LINK_LIBRARIES(multiview glog numeric)
20 TARGET_LINK_LIBRARIES(multiview ${GLOG_LIBRARIES} numeric)
3.3 ビルド
私の今回の環境ではBUILD_opencv_hdfとBUILD_opencv_bioinspiredにも自動でチェックが入っていたのですが,この2つがビルド時にエラーになりました.(以前にビルドした際はbioinspiredはビルドできていたはずなので,何かライブラリのバージョン依存の問題などかもしれません.)どちらも今回の目的とは関係ないので,ここではチェックを外してビルドしました.
- まとめ
===
ということで,今日はopencv_sfmをVisualStudio 2015を使ってビルドしました.明らかにVisualStudioは想定されていない雰囲気ですが,それでも,それなりに手間をかけることでビルドできるということを紹介しました.
明日は,OpenCVに付属のサンプルコードを使ってSfMの動作確認をします.