【Pythonチュートリアル】GoogleColabでOpen3Dを動かして可視化する
はじめに
Open3D 0.16 以降のバージョンではGoogleColab上で点群を可視化する機能が追加されました。
draw_plotlyメソッドを使うことで、 Open3Dの点群やメッシュデータを可視化することができます。
この記事ではGoogleColabでOpen3Dの簡単な処理をして可視化するまでのチュートリアルを紹介します。
ざっくりOpen3Dとは
⇒3Dデータを扱うオープンソースのライブラリ
Open3dのコア機能:
・3Dデータ構造
・3Dデータ処理アルゴリズム
・シーン再構築
・表面のアライメント
・3Dビジュアライゼーション
・物理ベースレンダリング (PBR)
・PyTorch と TensorFlowによる3次元機械学習対応
・コア3D演算のためのGPUアクセラレーション
・C++とPythonで利用可能
GoogleColabでOpen3Dを動かす
ライブラリのインストール
GoogleColabで新規ファイルを開いて、Open3Dをインストールします。
!pip install open3d
先ほどインストールしたOpen3Dの他に、 numpyとmatplotlibをインポートして準備完了です。
import numpy as np
import matplotlib.pyplot as plt
import opene3d as o3d
PLY, PCDファイルの可視化
実際に可視化してみます。 初めにPLYファイルを使用します。
PLYは Polygon File Format の略で3Dデータをポリゴンの集合体として保存するためのファイル形式です。
ply_point_cloud = o3d.data.PLYPointCloud()
pcd = o3d.io.read_point_cloud(ply_point_cloud.path)
print(pcd)
print(np.asarray(pcd.points))
o3d.visualization.draw_plotly([pcd],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
まず、 Open3DからPLYPointCloud
クラスのインスタンスを作成します。
ply_point_cloud = o3d.data.PLYPointCloud()
そしてOpen3Dが提供するread_point_cloud
関数を使って、作成したインスタンスのパスを読み込んでpcd変数に格納します。
これをprintで表示すると、点の数や座標の範囲など、 点群に関する基本的な情報が出力されます。
open3d形式のデータを np.asarrayを使ってnumpyの配列に変換すると、生成された配列には点群の各点の(X、Y、Z) 座標が含まれます。
可視化するためには、draw_plotly
関数を使用します。
次にPCDファイルを使用します。
PCDはPoint Cloud Data の略で、このファイル形式では通常、(X,Y,Z) 座 標、強度、色の情報を保存します。
dataset = o3d.data.PCDPointCloud()
pcd = o3d.io.read_point_cloud(dataset.path)
print(pcd)
print(np.asarray(pcd.points))
o3d.visualization.draw_plotly([pcd],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
点群の前処理
ボクセルダウンサンプリング
3次元点群をボクセルにダウンサンプリングすることで、点群の全体的な構造を維持したままデータ量を削減することができます。
ボクセルとは、点群内の近くの点をまとめた3次元空間の小さな立方体の容積のことです。
ボクセルダウンサンプリングについては以前記事で紹介しています。
print("Downsample the point cloud with a voxel of 0.02")
downpcd = pcd.voxel_down_sample(voxel_size=0.02)
o3d.visualization.draw_plotly([downpcd],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
ボクセルサイズを0.02にしてダウンサンプリングした結果です。
図からわかるように、ボクセルサイズ0.02にポイントがまとめられているため、表示される点の数はかなり少なくなっています。
ボクセルサイズが大きくなると、表示される点は少なくなります。
点群のトリミング
次に、Open3Dで利用できるデモポリゴンを使って、点群データの中から椅子を抽出してみます。
まずDemoCropPointCloudを使って、デモ点群とトリミングに使用するポリゴンファイルパスをダウンロードします。
print("Load a polygon volume and use it to crop the original point cloud")
demo_crop_data = o3d.data.DemoCropPointCloud()
pcd = o3d.io.read_point_cloud(demo_crop_data.point_cloud_path)
vol = o3d.visualization.read_selection_polygon_volume(demo_crop_data.cropped_json_path)
chair = vol.crop_point_cloud(pcd)
o3d.visualization.draw_plotly([chair],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
read_point_cloud()
関数が、 “demo_crop_data.point_cloud_path
" で提供されるファイルパスから点群データを読み込みます。
read_selection_polygon_volume()
関数を呼び出して、 “demo_crop_data.cropped_json_path
“によって提供されるファイルパスからポリゴンオブジ ェクトを読み込みます。
ポリゴンオブジェクトの crop_point_cloud()
関数を使うことで、ポリゴンで定義されたボリュームに基づいて元の点群を切り出し、 変数 “chair" に割り当てを行います。
最後に、draw_plotly()
“で切り取られた点群を表示します。
次に、椅子を切り取るのではなく、全体の点群から椅子を取り除いてみます。
そのためには、「compute_point_cloud_distance
」メソッドを使用してソース点群からターゲット点群までの距離を計算して処理をします。
簡単にいうと椅子の点群と全ての点群の距離を計算して、距離の遠い点のみを選択することで椅子を除いた点群が得られます。
まず、pcdの各点と椅子の点群に最も近い点との間の距離を計算します。
そして、これをnumpyの配列に変換し、演算や操作を実行します。
次に np.where (dists 0.01) [0] を通して、椅子に0.01以上近いインデックスを削除し、0.01より大きいインデックスのみを保存します。
次に、pcdオブジェクトのselect_by_index()
関数を用いて、pcd内のindexが ind にある点のみを選択し、 椅子に近すぎる点を効率的に除去し、点群オブジェクト pcd_without_chair に保存します。
distance= pcd.compute_point_cloud_distance(chair)
dists = np.asarray(distance)
ind = np.where(dists > 0.01)[0]
pcd_without_chair = pcd.select_by_index(ind)
o3d.visualization.draw_plotly([pcd_without_chair],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
点群の凸包(コンベックスハル)
次に点群の凸包を計算してみます。
凸包とは、点の集合を包み込むことができる最小の凸多角形のことです。
凸包についても過去に記事を書いています。
点群の凸包を計算するために、 Open3Dでは以下のようにcompute_convex_hull
を使用します。
hull, _ = chair.compute_convex_hull()
hull_ls = o3d.geometry.LineSet.create_from_triangle_mesh(hull)
hull_ls.paint_uniform_color((1, 0, 0))
o3d.visualization.draw_plotly([chair, hull_ls])
セグメンテーションとクラスタリング
次はクラスタリングをしてみます。
Open3Dでは、DBSCANというクラスタリング手法を利用しできます。
DBSCANを利用するためには、 2つの変数を指定する必要があります:
・イプシロン(eps): (円の半径) 0.02
・クラスターを形成するための最小ポイント数(min_points): 10ポイント
open3dの関数では、クラスタのラベルを返し、 -1の場合はノイズであることを示します。
クラスタリングについて詳しく知りたい方は下記記事をご参照ください。
with o3d.utility.VerbosityContextManager(
o3d.utility.VerbosityLevel.Debug) as cm:
labels = np.array(
pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))
クラスごとに色をつけて可視化するために、 matplotlibのcolormap 「get_cmap」を使用します。
引数として"tab20″を与えることでプ ロットの色を20色用意します。
負のラベル値を持つ点には、ゼロの色値が割り当てられるようにしています。
import matplotlib.pyplot as plt
max_label = labels.max()
print(f"point cloud has {max_label + 1} clusters")
colors = plt.get_cmap("tab20")(labels / (max_label if max_label > 0 else 1))
colors[labels < 0] = 0
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_plotly([pcd],
zoom=0.455,
front=[-0.4999, -0.1659, -0.8499],
lookat=[2.1813, 2.0619, 2.0999],
up=[0.1204, -0.9852, 0.1215])
この例では、最大ラベルまたはクラスタは10クラスタです。
ディスカッション
コメント一覧
まだ、コメントがありません