catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

OpenMVG, OpenMVSで3D再構築する

前回のエントリで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

これを実行すると

  • imagesディレクトリの画像をもとに三次元点群を生成する
  • tutorial_out/reconstruction_globalディレクトリに結果を出力する

という感じに動きます。

できあがったデータはこんな感じです。

f:id:Catalina1344:20191007140654p:plain

前回のエントリではキャリブレーションパラメータが間違えていて、何が何やら状態でしたが、これなら何か見えてきそうな気がしますね。

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

密な点群にしてみるとこうなります。 f:id:Catalina1344:20191007140818p:plain

点の数がずいぶんと増えたので、キャラクタも武器も目視確認できますね。

cd /openMVS_build/bin
# メッシュにする
./ReconstructMesh /mnt/work/openMVS/scene_dense.mvs

点のままでは3DCGソフト上で扱いにくいのでメッシュにします。

f:id:Catalina1344:20191007141559p:plain

Blenderへのインポート時点でテクスチャがはがれてしまいましたが、ひとまず読み込みできました。

試しにキャラクタ部分のみ残してみるようにしたところ、およそ20万ポリゴンでした。

感想と今後の展望

ゲームのスクリーンショットをもとに三次元再構成ができることの確認ができました。 少し手直ししてあげれば3Dプリンタで出力することもできそうです。

ただし、今回の手法は弱点も存在していて、「光沢部分が凸なポリゴンとして認識されてしまう」という課題があります。 これはゲーム画面のレンダリング時は光沢として白色になるわけですが、分析時はその白色は凸のためにできたのか、もともと白かったからなのかという判断がうまくつかないからのようです。

もう少し入力データを工夫して色々と試してみたいと思います。

また、今回利用した手法(SfM)以外にも、DeepLearningを用いた手法もあるようなので、そちらも試していきたいと思います。

それでは今回はこれくらいで。