Learning Open3D→Tutorial→上級編→カラーマップ最適化
深度カメラから再構築されたジオメトリへのカラーマッピングについて考える。 カラーフレームと深度フレームは完全には位置合わせされないため、カラーイメージを用いたテクスチャ・マッピングは、結果としてカラーマップがぼやけてしまう。 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回の反復実行終了まで待つこと。
# 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)
examples/Python/Advanced/o3d.color_map.color_map_optimization.py
の最初の部分コード:
# 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画像が表示される
# (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)
カメラのポーズがいかにカラーマッピングに適していないかを視覚化するため、このコードは反復回数を意図的に0に設定してある。これは最適化を行わないことを意味する。color_map_optimization
は、対応するRGBD画像とカメラポーズを使用してメッシュを色付けする。最適化を行わないと、テクスチャマップがぼやける(下図は上図を拡大したもの)。
剛体最適化
次のステップでは、カメラのポーズを最適化して、鮮明なカラーマップを取得する。
examples/Python/Advanced/o3d.color_map.color_map_optimization.pyの最後の部分コード:
# 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)
このコードは、反復回数を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次元のポーズを最適化する(下図は上図を拡大したもの)。
非剛体最適化
アライメントの品質を向上させるために、非剛体最適化という選択肢がある。 これを有効にするには、color_map.color_map_optimization
を呼ぶ前に次を追加するだけでよい:
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
非剛体最適化の結果は次の通り:(下図は上図の拡大)
残差が安定的に減少しないようならば、それは画像が急激な湾曲があるためであることが多い。そのような場合、option.non_rigid_anchor_point_weight
の値を大きくして、反復をより控え目にすることを検討してみよう。