Learning Open3DTutorial上級編→カラーマップ最適化

Color Map Optimization

カラーマップ最適化

深度カメラから再構築されたジオメトリへのカラーマッピングについて考える。 カラーフレームと深度フレームは完全には位置合わせされないため、カラーイメージを用いたテクスチャ・マッピングは、結果としてカラーマップがぼやけてしまう。 Open3Dでは、Zhou & Koltun (2014)によるカラーマップ最適化方法を提供している。 まず、ここから噴水データセットをダウンロードする。 次のプログラムは、カラーマップの最適化の例を示すものである。

Zhou, Q.-Y. & Koltun, V. (2014) Color Map Optimization for 3D Reconstruction with Consumer Depth Cameras. In SIGGRAPH 2014.

注意: 作業(カレント)ディレクトリがexamples/Python/Advancedになっていることを確認すること

注意1.: fountain.zipから展開されたfountain_smallディレクトリまでのパスを変数pathにセットするよう適切なコードを書くこと

注意2. 関数get_file_listを使っているが、これはexaples/Python/Utilityの下にあるfile.pyで定義されている。そのため下のプログラムではsys.path.append(../Utility) によってimport対象のディレクトリを追加している

注意3. 2つの3D物体の表示が行われるが、2つめの表示には時間がかかる(option.maximum_iteration=300の設定のため)。この値を小さくするか、ターミナルにデバッグ情報が流されるのでそれを見て300回の反復実行終了まで待つこと。

In [16]:
# examples/Python/Advanced/o3d.color_map.color_map_optimization.py

import open3d as o3d
from trajectory_io import *   # 使われていない?
import os, sys
sys.path.append("../Utility")
from file import *

path = "../../fountain_small/"
debug_mode = False

if __name__ == "__main__":
    o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)

    # (1) Read RGBD images
    rgbd_images = []
    depth_image_path = get_file_list(os.path.join(path, "depth/"),
                                     extension=".png")
    color_image_path = get_file_list(os.path.join(path, "image/"),
                                     extension=".jpg")
    assert (len(depth_image_path) == len(color_image_path))
    for i in range(len(depth_image_path)):
        depth = o3d.io.read_image(os.path.join(depth_image_path[i]))
        color = o3d.io.read_image(os.path.join(color_image_path[i]))
        rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth(
            color, depth, convert_rgb_to_intensity=False)
        if debug_mode:
            pcd = o3d.geometry.PointCloud.create_from_rgbd_image(
                rgbd_image,
                o3d.camera.PinholeCameraIntrinsic(
                    o3d.camera.PinholeCameraIntrinsicParameters.
                    PrimeSenseDefault))
            o3d.visualization.draw_geometries([pcd])
        rgbd_images.append(rgbd_image)

    # (1a) Read camera pose and mesh
    camera = o3d.io.read_pinhole_camera_trajectory(
        os.path.join(path, "scene/key.log"))
    mesh = o3d.io.read_triangle_mesh(
        os.path.join(path, "scene", "integrated.ply"))

    # (1b) Before full optimization, let's just visualize texture map
    # with given geometry, RGBD images, and camera poses.
    option = o3d.color_map.ColorMapOptimizationOption()
    option.maximum_iteration = 0
    o3d.color_map.color_map_optimization(mesh, rgbd_images, camera, option)
    o3d.visualization.draw_geometries([mesh])
    o3d.io.write_triangle_mesh(
        os.path.join(path, "scene", "color_map_before_optimization.ply"), mesh)

    # (2) Optimize texture and save the mesh as texture_mapped.ply
    # This is implementation of following paper
    # Q.-Y. Zhou and V. Koltun,
    # Color Map Optimization for 3D Reconstruction with Consumer Depth Cameras,
    # SIGGRAPH 2014
    option.maximum_iteration = 300
    option.non_rigid_camera_coordinate = True
    o3d.color_map.color_map_optimization(mesh, rgbd_images, camera, option)
    o3d.visualization.draw_geometries([mesh])
    o3d.io.write_triangle_mesh(
        os.path.join(path, "scene", "color_map_after_optimization.ply"), mesh)

Input

examples/Python/Advanced/o3d.color_map.color_map_optimization.pyの最初の部分コード:

In [ ]:
    # Read RGBD images
    rgbd_images = []
    depth_image_path = get_file_list(os.path.join(path, "depth/"),
                                     extension=".png")
    color_image_path = get_file_list(os.path.join(path, "image/"),
                                     extension=".jpg")
    assert (len(depth_image_path) == len(color_image_path))
    for i in range(len(depth_image_path)):
        depth = o3d.io.read_image(os.path.join(depth_image_path[i]))
        color = o3d.io.read_image(os.path.join(color_image_path[i]))
        rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth(
            color, depth, convert_rgb_to_intensity=False)
        if debug_mode:
            pcd = o3d.geometry.PointCloud.create_from_rgbd_image(
                rgbd_image,
                o3d.camera.PinholeCameraIntrinsic(
                    o3d.camera.PinholeCameraIntrinsicParameters.
                    PrimeSenseDefault))
            o3d.visualization.draw_geometries([pcd])
        rgbd_images.append(rgbd_image)

このコードでは、色画像と深度画像のペアを読み込、rgbd_imageを作成する。convert_rgb_to_intensityフラグがFalseとなっていることに注意しよう。 これは1チャンネルの浮動小数点数型の画像を使う代わりに、8ビットのカラーチャンネルを使うためである。

RGBD画像をカラーマップ最適化する前に視覚化することは良いことである。debug_modeフラグは、RGBD画像を視覚化するためのものである。

注意: debug_modeフラグはFalseにセットされている。Trueにするとかなりたくさんの3D画像が表示される

In [ ]:
   # (1a) Read camera pose and mesh
    camera = o3d.io.read_pinhole_camera_trajectory(
        os.path.join(path, "scene/key.log"))
    mesh = o3d.io.read_triangle_mesh(
        os.path.join(path, "scene", "integrated.ply"))

上のコードはカメラのトラジェクトリ(軌跡)とメッシュを読み込むものである。

In [ ]:
    # (1b) Before full optimization, let's just visualize texture map
    # with given geometry, RGBD images, and camera poses.
    option = o3d.color_map.ColorMapOptimizationOption()
    option.maximum_iteration = 0
    o3d.color_map.color_map_optimization(mesh, rgbd_images, camera, option)
    o3d.visualization.draw_geometries([mesh])
    o3d.io.write_triangle_mesh(
        os.path.join(path, "scene", "color_map_before_optimization.ply"), mesh)

カメラのポーズがいかにカラーマッピングに適していないかを視覚化するため、このコードは反復回数を意図的に0に設定してある。これは最適化を行わないことを意味する。color_map_optimizationは、対応するRGBD画像とカメラポーズを使用してメッシュを色付けする。最適化を行わないと、テクスチャマップがぼやける(下図は上図を拡大したもの)。

http://www.open3d.org/docs/release/_images/initial2.pnghttp://www.open3d.org/docs/release/_images/initial_zoom.png

Rigid Optimization

剛体最適化

次のステップでは、カメラのポーズを最適化して、鮮明なカラーマップを取得する。

examples/Python/Advanced/o3d.color_map.color_map_optimization.pyの最後の部分コード:

In [17]:
    # Optimize texture and save the mesh as texture_mapped.ply
    # This is implementation of following paper
    # Q.-Y. Zhou and V. Koltun,
    # Color Map Optimization for 3D Reconstruction with Consumer Depth Cameras,
    # SIGGRAPH 2014
    option.maximum_iteration = 300
    option.non_rigid_camera_coordinate = False
    o3d.color_map.color_map_optimization(mesh, rgbd_images, camera, option)
    o3d.visualization.draw_geometries([mesh])
    o3d.io.write_triangle_mesh(
        os.path.join(path, "scene", "color_map_after_optimization.ply"), mesh)
Out[17]:
True

このコードは、反復回数をmaximum_iteration = 300と設定している。 最適化により、以下のようなエネルギー・プロファイルが表示される。

[ColorMapOptimization] :: Rigid Optimization
[Iteration 0001] Residual error : 21639.276499 (avg : 0.004615)
[Iteration 0002] Residual error : 21461.765357 (avg : 0.004577)
[Iteration 0003] Residual error : 21284.579715 (avg : 0.004540)
:
[Iteration 0298] Residual error : 8891.042884 (avg : 0.001903)
[Iteration 0299] Residual error : 8890.037077 (avg : 0.001903)
[Iteration 0300] Residual error : 8888.970765 (avg : 0.001903)

誤差(residual error)とは、画像強度の不整合を意味する。 残渣が少なければ、カラーマップの品質が向上していることを意味する。 デフォルトでは、ColorMapOptimizationOptionはリジッドな最適化を有効にする。 すべてのカメラの6次元のポーズを最適化する(下図は上図を拡大したもの)。

http://www.open3d.org/docs/release/_images/rigid.png http://www.open3d.org/docs/release/_images/rigid_zoom.png

Non-rigid Optimization

非剛体最適化

アライメントの品質を向上させるために、非剛体最適化という選択肢がある。 これを有効にするには、color_map.color_map_optimizationを呼ぶ前に次を追加するだけでよい:

In [ ]:
option.non_rigid_camera_coordinate = True

6次元のカメラポーズに加えて、非剛性最適化では、アンカーポイントで表される局所的な画像のゆがみも考慮する。 これにより、柔軟性がさらに向上し、高品質のカラーマッピングが実現される。 残差は、厳密な最適化の場合よりも小さくなる。

[ColorMapOptimization] :: Non-Rigid Optimization
[Iteration 0001] Residual error : 21639.276499, reg : 0.000000
[Iteration 0002] Residual error : 21187.225206, reg : 13.918495
[Iteration 0003] Residual error : 20745.248996, reg : 42.234724
:
[Iteration 0298] Residual error : 5589.018747, reg : 2745.364742
[Iteration 0299] Residual error : 5587.180145, reg : 2746.619137
[Iteration 0300] Residual error : 5585.066255, reg : 2747.902979

非剛体最適化の結果は次の通り:(下図は上図の拡大)

http://www.open3d.org/docs/release/_images/non_rigid.png http://www.open3d.org/docs/release/_images/non_rigid_zoom.png

注に

残差が安定的に減少しないようならば、それは画像が急激な湾曲があるためであることが多い。そのような場合、option.non_rigid_anchor_point_weightの値を大きくして、反復をより控え目にすることを検討してみよう。

Previous:RGBD統合                  Next:可視化のカスタム化