Learning Open3D→Tutorial→上級編→ノンブロッキング可視化
draw_geometries()
は、静的ジオメトリの概要をすばやく確認するのに便利な関数である。 ただしこの関数は、視覚化ウィンドウが閉じるまでプロセスを保持する。 これはジオメトリが更新されたときに最適ではないので、ウィンドウを閉じずに可視化する必要がある。 このチュートリアルでは、レンダリングループをカスタマイズする例を紹介する。
draw_geometries
関数の復習
draw_geometries()
関数には次のレンダリング・ループがある(C++実装についてはVisualizer::Run
を参照)。
while(true):
if (geometry has changed): # ジオメトリが変化したなら
re-bind geometry to shaders (ジオメトリをシェーダーに再バインド)
if (view parameters have changed): # 視野パラメタが変わったなら
re-render the scene (シーンを再レンダリング)
if (any user mouse/keyboard input): # マウスやキー入力があれば
respond to it and set flags for re-rendering (それに反応して、再レンダリングのためにフラグをセットする)
バインディング・ジオメトリとレンダリングはどちらもコストのかかる操作であるため、「怠惰」手法により実行されることに注意しよう。それらを個別に制御する2つのフラグがある。関数update_geometry()
およびupdate_renderer()
はこれらのフラグをオンに設定する。バインド/レンダリングの後、これらのフラグはクリアされる。
このレンダリングループは簡単にカスタマイズできる。 たとえば、ICP位置合わせを視覚化するループは次のようにできる。
vis = Visualizer()
vis.create_window()
for i in range(icp_iteration):
# do ICP single iteration
# transform geometry using ICP
vis.update_geometry() # 注: このメソッドは引数を一つ取るはず...
vis.poll_events()
vis.update_renderer()
このアイデアを実装したプログラムは以下:
注意 作業(カレント)ディレクトリがexamples/Python/Advanced
になっていることを確認すること
# examples/Python/Advanced/non_blocking_visualization.py
import open3d as o3d
import numpy as np
import copy
if __name__ == "__main__":
o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)
# (1) Prepare example data
source_raw = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_0.pcd")
target_raw = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_1.pcd")
source = source_raw.voxel_down_sample(voxel_size=0.02)
target = target_raw.voxel_down_sample(voxel_size=0.02)
trans = [[0.862, 0.011, -0.507, 0.0], [-0.139, 0.967, -0.215, 0.7],
[0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]]
source.transform(trans)
flip_transform = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]
source.transform(flip_transform)
target.transform(flip_transform)
# (2) Initialize Visualizer class
vis = o3d.visualization.Visualizer()
vis.create_window()
vis.add_geometry(source)
vis.add_geometry(target)
threshold = 0.05
icp_iteration = 100
save_image = False
# (3) Transform geometry and visualize it
for i in range(icp_iteration):
reg_p2l = o3d.registration.registration_icp(
source, target, threshold, np.identity(4),
o3d.registration.TransformationEstimationPointToPlane(),
o3d.registration.ICPConvergenceCriteria(max_iteration=1))
source.transform(reg_p2l.transformation)
vis.update_geometry(source) # 元ファイルに誤り?
vis.poll_events()
vis.update_renderer()
if save_image:
vis.capture_screen_image("temp_%04d.jpg" % i)
vis.destroy_window()
注意
最後から6行目はオリジナルではvis.update_geometry()
となっておりエラーが起こる。その原因を調べるためのもの:
help(vis.update_geometry)
以降の節でこのプログラムについて説明する。
# (1) Prepare example data
source_raw = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_0.pcd")
target_raw = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_1.pcd")
source = source_raw.voxel_down_sample(voxel_size=0.02)
target = target_raw.voxel_down_sample(voxel_size=0.02)
trans = [[0.862, 0.011, -0.507, 0.0], [-0.139, 0.967, -0.215, 0.7],
[0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]]
source.transform(trans)
flip_transform = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]
source.transform(flip_transform)
target.transform(flip_transform)
この部分は、2つの点群(ポイントクラウド)を読み取り、それらをダウンサンプリングしている。 ソースの点群は、不整合になるよう意図的に変換を施している。可視化を良くするために、点群を2つとも反転している。
Visualizerクラスの初期化
examples/Python/Advanced/non_blocking_visualization.py
の2番目の部分コード:
# (2) Initialize Visualizer class
vis = o3d.visualization.Visualizer()
vis.create_window()
vis.add_geometry(source)
vis.add_geometry(target)
これらは、ビジュアライザー(Visualizer)クラスのインスタンスを作り、ビジュアライザー・ウィンドウを開き、ビジュアライザーに2つのジオメトリを追加している。
ジオメトリを変換し可視化
examples/Python/Advanced/non_blocking_visualization.py
の3番目の部分コード:
for i in range(icp_iteration):
reg_p2l = o3d.registration.registration_icp(
source, target, threshold, np.identity(4),
o3d.registration.TransformationEstimationPointToPlane(),
o3d.registration.ICPConvergenceCriteria(max_iteration=1))
source.transform(reg_p2l.transformation)
vis.update_geometry(source) # 元ファイルに誤り?
vis.poll_events()
vis.update_renderer()
if save_image:
vis.capture_screen_image("temp_%04d.jpg" % i)
vis.destroy_window()
このコードは、繰り返しごとにregistration_icp
を呼び出す。 ICPConvergenceCriteria(max_iteration = 1)
によって明示的に1回だけのICP反復を行わせていることに注意しよう。これは、1回のICP反復から、わずかなポーズの更新を取得するためのトリックである。ICPの後、ソースのジオメトリはそれに応じて変換される。
コードの次の部分がこのチュートリアルの中心部分である。update_geometry
は、ジオメトリが更新されたことをvis
に通知する。最後に、ビジュアライザーはpoll_events
とupdate_renderer
を呼び出して新しいフレームをレンダリングする。for
ループの後、destroy_window
はウィンドウを閉じる。
結果は次のようになる。