前回のエントリでOenMVGを使って三次元再構成ができることが確認できました。 今回はさらに進んで、ジオメトリを再構成してみます。
カメラ内部パラメータの設定
前回のエントリで生成したカメラ内部パラメータは間違えていました。正しくはこうなります。
間違えていた原因としては、OpenCVのキャリブレーション関数でのRow/COlumnsの扱いが逆になっていたためでした。
手作業で格子状の画像からこんな感じの交点を抽出しましたが、これは横7, 縦5個の交点です。
corners = np.array( [ [1013, 321], [1052, 319], [1090, 318], [1129, 316], [1166, 315], [1204, 314], [1240, 313], [1012, 360], [1050, 358], [1088, 357], [1126, 353], [1164, 353], [1201, 352], [1237, 350], [1012, 395], [1050, 394], [1087, 394], [1124, 391], [1160, 389], [1197, 389], [1234, 386], [1011, 432], [1048, 431], [1085, 430], [1122, 427], [1158, 425], [1195, 424], [1230, 422], [1011, 469], [1047, 466], [1083, 465], [1120, 463], [1156, 460], [1192, 458], [1227, 456], ], np.float32 )
どういうことかというと、
rows = 7 cols = 5
として対応する画像上の点を生成するべきなのですが、rowとcolを逆に設定していたためにおかしなことになってました。 opencvあるあるですね。
というわけで、キャリブレーションやりなおすとこうなりました。
kp='2105.02823002;0;795.46581198;0;1386.68788826;422.25675948;0;0;1' 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"] )
キャリブレーションのコードはこちら
cols = 5 rows = 7 # コーナー検出をせずに、自前で入力する corners = np.array( [ [1013, 321], [1052, 319], [1090, 318], [1129, 316], [1166, 315], [1204, 314], [1240, 313], [1012, 360], [1050, 358], [1088, 357], [1126, 353], [1164, 353], [1201, 352], [1237, 350], [1012, 395], [1050, 394], [1087, 394], [1124, 391], [1160, 389], [1197, 389], [1234, 386], [1011, 432], [1048, 431], [1085, 430], [1122, 427], [1158, 425], [1195, 424], [1230, 422], [1011, 469], [1047, 466], [1083, 465], [1120, 463], [1156, 460], [1192, 458], [1227, 456], ], np.float32 ) image_points = [] image_points.append(corners) # 検出した画像座標上の点に対応する3次元上の点を作成する。 world_points = np.zeros((rows * cols, 3), np.float32) world_points[:, :2] = np.mgrid[:cols, :rows].T.reshape(-1, 2) print('world_points shape:', world_points.shape) # world_points shape: (54, 3) for img_pt, world_pt in zip(image_points[0], world_points): print('image coordinate: {} <-> world coordinate: {}'.format(img_pt, world_pt)) # 画像の枚数個複製する。 imagenum = 1 object_points = [world_points] * imagenum np.set_printoptions(suppress=True) ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, (1920, 1017),None,None) print("camera instristics mtx = {}".format(mtx) )
PythonからOpenCVを使ってのキャリブレーションは、こちらのサイトを参考にさせていただきました。 http://pynote.hatenablog.com/entry/opencv-camera-calibration
キャリブレーションで得たパラメータを使ってOpenMVGを叩くコードはこんな感じです。 ほぼチュートリアルのままですが。 https://gist.github.com/javoren/dd7075211776ee51ac45c06b3827d905
これを実行すると
という感じに動きます。
できあがったデータはこんな感じです。
前回のエントリではキャリブレーションパラメータが間違えていて、何が何やら状態でしたが、これなら何か見えてきそうな気がしますね。
openMVS形式にファイルを変換する
できあがったデータはopenMVSにもっていきたいので、プロジェクトを変換します。
openmvgのコンテナ内で次のコマンドを実行します。
cd /opt/openMVG_Build/Linux-x86_64-RELEASE openMVG_main_openMVG2openMVS -i /mnt/work/tutorial_out/reconstruction_global/sfm_data.bin /mnt/work/openMVS/scene.mvs -o /mnt/work/scene.mvs
これで/mnt/work/scene.mvsという、OpenMVSで利用可能なファイルが完成しました。
OpenMVSの用意
openmvsのリポジトリ公式の手順をもとにdockerコンテナを作成します。 まとめるのが下手なので、一旦個別にRUNしていくだけの簡単なものです。 rootディレクトリにすべて展開しちゃってるのはご愛敬ということで。。。
# Use Ubuntu 18.04 (will be supported until April 2023) FROM ubuntu:16.04 # Add openMVG binaries to path ENV PATH $PATH:/opt/openMVG_Build/install/bin # Get dependencies RUN apt-get update && apt-get install -y \ cmake \ build-essential \ graphviz \ git \ coinor-libclp-dev \ libceres-dev \ libflann-dev \ liblemon-dev \ libjpeg-dev \ libpng-dev \ libtiff-dev \ python-minimal; \ apt-get autoclean && apt-get clean ##Prepare and empty machine for building: RUN apt-get -y install build-essential git mercurial cmake libpng-dev libjpeg-dev libtiff-dev libglu1-mesa-dev libxmu-dev libxi-dev RUN hg clone https://bitbucket.org/eigen/eigen#3.2 RUN mkdir eigen_build RUN cd eigen_build && cmake . ../eigen RUN cd eigen_build && make && make install # boost, opencv RUN apt-get -y install libboost-iostreams-dev libboost-program-options-dev libboost-system-dev libboost-serialization-dev RUN apt-get -y install libopencv-dev RUN apt-get -y install libcgal-dev libcgal-qt5-dev ##VCGLib (Required) RUN git clone https://github.com/cdcseacave/VCG.git vcglib RUN apt-get -y install libatlas-base-dev libsuitesparse-dev RUN git clone https://ceres-solver.googlesource.com/ceres-solver ceres-solver RUN mkdir ceres_build RUN cd ceres_build && cmake . ../ceres-solver/ -DMINIGLOG=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF RUN cd ceres_build && make && make install RUN apt-get -y install freeglut3-dev libglew-dev libglfw3-dev RUN git clone https://github.com/cdcseacave/openMVS.git openMVS RUN mkdir openMVS_build RUN cd openMVS_build && cmake . ../openMVS -DCMAKE_BUILD_TYPE=Release -DVCG_ROOT="$main_path/vcglib" RUN cd openMVS_build && make && make install
このコンテナを起動するdocker-compose.ymlは次のようになりました。
version: "3" services: openmvg: build: context: ./openMVG dockerfile: ./Dockerfile image: openmvg container_name: "openmvg" tty: true volumes: - "./work/:/mnt/work" - "./work/images/:/opt/openMVG_Build/Linux-x86_64-RELEASE/images" openmvs: build: context: ./ dockerfile: ./dockerfile image: openmvs container_name: "openmvs" tty: true volumes: - "./work/:/mnt/work" - "./work/images/:/openMVS_build/bin/undistorted_images"
このコンテナに入って、次のコマンドを実行していけば三次元再構成ができます。
cd /openMVS_build/bin # 密な点群にする ./DensifyPointCloud /mnt/work/openMVS/scene.mvs
密な点群にしてみるとこうなります。
点の数がずいぶんと増えたので、キャラクタも武器も目視確認できますね。
cd /openMVS_build/bin # メッシュにする ./ReconstructMesh /mnt/work/openMVS/scene_dense.mvs
点のままでは3DCGソフト上で扱いにくいのでメッシュにします。
Blenderへのインポート時点でテクスチャがはがれてしまいましたが、ひとまず読み込みできました。
試しにキャラクタ部分のみ残してみるようにしたところ、およそ20万ポリゴンでした。
感想と今後の展望
ゲームのスクリーンショットをもとに三次元再構成ができることの確認ができました。 少し手直ししてあげれば3Dプリンタで出力することもできそうです。
ただし、今回の手法は弱点も存在していて、「光沢部分が凸なポリゴンとして認識されてしまう」という課題があります。 これはゲーム画面のレンダリング時は光沢として白色になるわけですが、分析時はその白色は凸のためにできたのか、もともと白かったからなのかという判断がうまくつかないからのようです。
もう少し入力データを工夫して色々と試してみたいと思います。
また、今回利用した手法(SfM)以外にも、DeepLearningを用いた手法もあるようなので、そちらも試していきたいと思います。
それでは今回はこれくらいで。