夏が終わって秋らしい気候になってきました。秋は色々と創作意欲を刺激されるイベントが多いので、今のうちから準備をやっていきたいと思います。
OpenMVGとは
公式はこれですね。 github.com
MultiViewGeometryの略で、複数視点から三次元ジオメトリを再構成しようってやつです。
技術的なカテゴリとしてはフォトグラメトリとかSLAMに近いもののようです。
これらの細かい区別はよくわかりません。あしからず。
モチベーション
なんでこういうのに興味を持ったかというと、ARアプリと3Dプリンタを組み合わせるときに大事な技術になるのではないかという思いからです。
以前のエントリで、ゲーム中の登場アイテムを3Dプリンタで制作してARエフェクトを出してみるってのをやりました。
これの最大の問題点は工数がかかりすぎという点で、趣味レベルで余暇を使ってやっているとはいえざっとこれくらいかかりました。
- モデリング2か月
- プリント, 仕上げ塗装 それぞれ1か月
- ARアプリ1か月
スケールアップを考えたとき、一番ネックになりそうなのはモデリングかなと思っていて、ここをツール群を使って自動化したいなと考えました。
で、画像からの3Dモデル再構築ってどうなのだろうってことで試してみました。
もちろんゲーム中のSSを解析したりする行為は著作権やソフトウェア利用許諾に記載されているリバースエンジニアリングの禁止条項にかかる懸念はありますので、それはその問題として別途解決しておく必要はあります。
では早速try。
OpenMVG実行環境をdockerで作る
まずは環境作りからです。
ビルド環境
環境はいつものです。 WIndows10-pro上のdocker desktop for winwowsを使用します。
公式リポジトリをcloneしてきて、ビルドパラメータを修正します。 dockerfile末尾でmakeしている箇所の-jオプションを削除します。
# Build RUN mkdir /opt/openMVG_Build; \ cd /opt/openMVG_Build; \ cmake -DCMAKE_BUILD_TYPE=RELEASE \ -DCMAKE_INSTALL_PREFIX="/opt/openMVG_Build/install" \ -DOpenMVG_BUILD_TESTS=ON \ -DOpenMVG_BUILD_EXAMPLES=OFF \ -DFLANN_INCLUDE_DIR_HINTS=/usr/include/flann \ -DLEMON_INCLUDE_DIR_HINTS=/usr/include/lemon \ -DCOINUTILS_INCLUDE_DIR_HINTS=/usr/include \ -DCLP_INCLUDE_DIR_HINTS=/usr/include \ -DOSI_INCLUDE_DIR_HINTS=/usr/include \ ../openMVG/src; \ # make -j 4; make;
jオプションを削除する意味
まずmakeのjオプションはジョブの個数を指定するオプションです。デフォルトの指定だと4プロセス平行に走らせるという意味です。
大雑把にいうと4CPUあればいい感じに走るわけなのですが、Docker for windowsのデフォルト設定ではコンテナに1CPUしか割り当てていません(うちの環境だけかも?)
この1CPU環境でjオプションを指定しているとコンテナごと応答なし状態になってしまったので、1プロセスでやってしまうということにしました。
起動パラメータ
また、このdockerfileのあるディレクトリの一つ上の階層にdocker-compose.ymlファイルを配置します。 openMVGのdockerfileが生成するコンテナの起動パラメータをまとめただけのものです。
version: "3" services: openmvg: build: context: ./openMVG dockerfile: ./Dockerfile image: openmvg container_name: openmvg tty: true volumes: - ./work:/mnt/work/
動作確認
チュートリアルらしいファイルが置かれているので叩いてみます。
対象ファイルは/opt/openMVG/src/software/SfM/tutorial_demo.py.in
ですが、そのままではパス指定がダメなので少し編集します
Linux上で編集したりするの大変なので、/mnt/workにでもコピーしてきて編集するのが楽です。(docker-composeでホスト側にマウントするよう指定した)
冒頭の部分を次のように書き換えます。
# Indicate the openMVG binary directory #OPENMVG_SFM_BIN = "@OPENMVG_SOFTWARE_SFM_BUILD_DIR@" OPENMVG_SFM_BIN = "/opt/openMVG_Build/Linux-x86_64-RELEASE/" # Indicate the openMVG camera sensor width directory #CAMERA_SENSOR_WIDTH_DIRECTORY = "@OPENMVG_SOFTWARE_SFM_SRC_DIR@" + "/../../openMVG/exif/sensor_width_database" CAMERA_SENSOR_WIDTH_DIRECTORY = "/opt/openMVG/src/software/SfM/" + "/../../openMVG/exif/sensor_width_database"
これでOK。
実行は
python tutorial_demo.py.in
です。建物の画像をダウンロードしてきて、解析した結果をtutorial_outディレクトリに出してくれます。
ply形式なので、meshlabとかで見られます。
少しいじってみる(失敗)
チュートリアルのコードを見ると、GitHub上から画像データをダウンロードしてきて、それをもとにSfMをやってるということがわかります。 せっかくなので自前のデータを用意して差し替えてみましょう。 コードを次のようにしてみました。
def get_parent_dir(directory): return os.path.dirname(directory) #os.chdir(os.path.dirname(os.path.abspath(__file__))) #input_eval_dir = os.path.abspath("./ImageDataset_SceauxCastle") ## Checkout an OpenMVG image dataset with Git #if not os.path.exists(input_eval_dir): # pImageDataCheckout = subprocess.Popen([ "git", "clone", "https://github.com/openMVG/ImageDataset_SceauxCastle.git" ]) # pImageDataCheckout.wait() # #output_eval_dir = os.path.join(get_parent_dir(input_eval_dir), "tutorial_out") #input_eval_dir = os.path.join(input_eval_dir, "images") #if not os.path.exists(output_eval_dir): # os.mkdir(output_eval_dir) input_eval_dir = "./images" output_eval_dir = "./tutorial_out" if not os.path.exists(output_eval_dir): os.mkdir(output_eval_dir) input_dir = input_eval_dir output_dir = output_eval_dir print ("Using input dir : ", input_dir) print (" output_dir : ", output_dir)
step5あたりで
The input SfM_Data file "./tutorial_out/reconstruction_global/sfm_data.bin" cannot be read.
と言われてしまいます。
このエラーの原因は、直前のプロセスまでに生成されているべきSFM_data.binが存在しないために発生するエラーです。
なぜsfm_dataが生成されていないかというと、今回はゲーム中の適当な画像を使ったのでexifヘッダが存在しないためでした。
どうもOpenMVGの実装上、jpegフォーマットのexifヘッダからカメラ内部パラメータを推測しているようです。
(カメラパラメータは、例えばテストデータとして使っているリポジトリにはK.txt
などといった形で記載されているものです。)
さらに少しいじってみる(少し進展した)
step1でsfmdata\jsonを生成していますが、ここでカメラ内部パラメータを与えることができます。 適当に引っ張ってきた値なので、使用する画像に合わせたパラメータに置き換える必要があります。
kp = "2905.88;0;1920;0;2905.88;1017;0;0;1" print ("1. Intrinsics analysis") #pIntrisics = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_SfMInit_ImageListing"), "-i", input_dir, "-o", matches_dir, "-d", camera_file_params, "-c", "3"] ) pIntrisics = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_SfMInit_ImageListing"), "-i", input_dir, "-o", matches_dir, "-k", kp, "-d", camera_file_params, "-c", "3"] ) pIntrisics.wait()
これで、plyファイルが出力されるようになりました。meshlabで見てみるとこんな感じになりました。 うーん、よくわからないですね。