【概要】
3次元の点が多数集まったデータです。Python と open3d を使い、色付き点群の表示、間引きと外れ値除去、法線の推定、メッシュ化を行います。
【目次】
【関連する外部ページ】
【サイト内の関連情報】
3次元の点が多数記述されたデータのことです。1つ1つの点が空間中の位置を表し、それらが集まって物体の形や空間の様子を表現します。
それぞれの点は、次のような属性を持ちえます。
要点:1つのオブジェクトの表面を、多数の点の集まり(3次元点群データ)として扱います。
3次元点群データには、用途に応じて複数の標準フォーマットが使われています。代表的なものは次のとおりです。
1行で1つの点を表します。データは半角の空白文字で区切られています。行の中には x, y, z の値が並び、あわせて付加情報が付くことが多いです。前にも使ったファイル cloud_0.asc では、ラベル番号(「2」などの数値)が付いています。
このあとの実習では、次に紹介する Redwood データセットの点群を使います。

https://rgbd-dataset.cs.washington.edu/

http://redwood-data.org/3dscan/dataset.html
ここまでは cloud_0.asc(ラベル番号付きの ASC 形式)を例にファイル形式を見てきました。ここからは、色情報(RGB)を持つ点群を題材に、Python での可視化と基本的な処理を行います。使用するのは、先に紹介した Redwood データセットのリビングルームを撮影した、色付きの PLY 形式の点群データです。
このあとの演習のコードは、いずれも同じ点群データを、プログラム(スクリプト)自身が置かれているディレクトリにダウンロードして利用します。各コードは前のコードに依存せず、それぞれ単体で実行できます。
点群ファイルの読み込みと可視化には open3d を、数値計算には numpy を使います。あらかじめ次のコマンドでインストールしてください(Python 3.12 環境が対象です)。
pip install -U --no-user numpy open3d
テーマ名:色付き点群をダウンロードして、open3d のビューアで表示する。
手順:
ヒント:
print(pcd) の出力に、点の数が表示される。考察ポイント:
import os
import urllib.request
import open3d as o3d
# このスクリプトが存在するディレクトリを取得(カレントディレクトリではない)
script_dir = os.path.dirname(os.path.abspath(__file__))
# 色情報(RGB)付きの PLY 形式の点群データ(Redwood リビングルーム)
url = "https://github.com/isl-org/open3d_downloads/releases/download/20220201-data/fragment.ply"
# ダウンロード先のフルパス(スクリプトと同じディレクトリ)
ply_path = os.path.join(script_dir, "fragment.ply")
# まだダウンロードされていなければ取得する
if not os.path.exists(ply_path):
print("点群データをダウンロードしています ...")
urllib.request.urlretrieve(url, ply_path)
print("ダウンロード完了:", ply_path)
# PLY ファイルを読み込む
pcd = o3d.io.read_point_cloud(ply_path)
print(pcd) # 点の数などの情報を表示
# インタラクティブに表示(マウスで回転・拡大縮小できる)
o3d.visualization.draw_geometries([pcd])

テーマ名:点群を間引き、外れ値(ノイズ)を除去してから表示する。
手順:
ヒント:
考察ポイント:
import os
import urllib.request
import open3d as o3d
# このスクリプトが存在するディレクトリを取得(カレントディレクトリではない)
script_dir = os.path.dirname(os.path.abspath(__file__))
# 色情報(RGB)付きの PLY 形式の点群データ(Redwood リビングルーム)
url = "https://github.com/isl-org/open3d_downloads/releases/download/20220201-data/fragment.ply"
ply_path = os.path.join(script_dir, "fragment.ply")
if not os.path.exists(ply_path):
print("点群データをダウンロードしています ...")
urllib.request.urlretrieve(url, ply_path)
print("ダウンロード完了:", ply_path)
pcd = o3d.io.read_point_cloud(ply_path)
print("元の点の数:", len(pcd.points))
# ボクセルダウンサンプリング(voxel_size を大きくするほど点が減る)
voxel_size = 0.02
down = pcd.voxel_down_sample(voxel_size=voxel_size)
print("間引き後の点の数:", len(down.points))
# 統計的外れ値除去(周囲の点から見て離れた点を取り除く)
clean, _ = down.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
print("外れ値除去後の点の数:", len(clean.points))
# 前処理後の点群を表示
o3d.visualization.draw_geometries([clean])

テーマ名:各点の法線を推定し、法線の向きを色に変換して面の構造を表示する。
法線(ほうせん)とは、各点における「面が向いている向き」を表すベクトルです。法線は、面の向きの計算、陰影付け(ライティング)、メッシュ化などに使う情報です。この演習では法線を推定したうえで、法線ベクトルの向きを色に変換して表示します。こうすると、同じ向きを向いた面(たとえば床・壁・天井)が異なる色で塗り分けられます。面の向きを見やすくするため、点が本来持っている色(RGB)を、法線から作った色で塗り替えます。
手順:
ヒント:
考察ポイント:
import os
import urllib.request
import numpy as np
import open3d as o3d
# このスクリプトが存在するディレクトリを取得(カレントディレクトリではない)
script_dir = os.path.dirname(os.path.abspath(__file__))
# 色情報(RGB)付きの PLY 形式の点群データ(Redwood リビングルーム)
url = "https://github.com/isl-org/open3d_downloads/releases/download/20220201-data/fragment.ply"
ply_path = os.path.join(script_dir, "fragment.ply")
if not os.path.exists(ply_path):
print("点群データをダウンロードしています ...")
urllib.request.urlretrieve(url, ply_path)
print("ダウンロード完了:", ply_path)
pcd = o3d.io.read_point_cloud(ply_path)
# 各点の法線を推定する
# radius: 法線推定に使う近傍の半径, max_nn: 使う近傍点の最大数
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
)
# 法線の向きをそろえる(向きの反転を減らす)
pcd.orient_normals_consistent_tangent_plane(k=30)
# 【活用例】法線ベクトル (x, y, z) を色 (R, G, B) に変換して塗り分ける
# (面の向きを見やすくするため、元の色はここで上書きする)
# 成分は -1〜1 の範囲なので、0〜1 に変換して色として使う
normals = np.asarray(pcd.normals)
colors = (normals + 1.0) / 2.0
pcd.colors = o3d.utility.Vector3dVector(colors)
# 面の向きごとに色分けされた点群を表示する
o3d.visualization.draw_geometries([pcd])

テーマ名:色を保ったまま点群をメッシュ化し、立体モデルとして表示する。
メッシュ化とは、点の集まりである点群から、三角形の面をつないだ立体モデル(メッシュ)を復元することです。点だけでは面や表面が示されませんが、メッシュにすると連続した表面として扱えます。この演習では、点が本来持っている色(RGB)を保ったままメッシュ化するボールピボット法を使います。この方法は入力点をそのままメッシュの頂点として使うため、点群の色がそのまま頂点の色になります。メッシュ化には各点の法線が必要なので、法線を推定してから実行します。
手順:
ヒント:
print(mesh) の出力に、頂点数と三角形数が表示される。考察ポイント:
import os
import urllib.request
import open3d as o3d
# このスクリプトが存在するディレクトリを取得(カレントディレクトリではない)
script_dir = os.path.dirname(os.path.abspath(__file__))
# 色情報(RGB)付きの PLY 形式の点群データ(Redwood リビングルーム)
url = "https://github.com/isl-org/open3d_downloads/releases/download/20220201-data/fragment.ply"
ply_path = os.path.join(script_dir, "fragment.ply")
if not os.path.exists(ply_path):
print("点群データをダウンロードしています ...")
urllib.request.urlretrieve(url, ply_path)
print("ダウンロード完了:", ply_path)
pcd = o3d.io.read_point_cloud(ply_path)
# メッシュ化には法線が必要なので、各点の法線を推定する
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
)
pcd.orient_normals_consistent_tangent_plane(k=30)
# ボールピボット法でメッシュ化する(点の色はそのまま頂点色になる)
# radii: 点群の上を転がすボールの半径のリスト(複数指定できる)
radii = [0.01, 0.02, 0.04]
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
pcd, o3d.utility.DoubleVector(radii)
)
print(mesh) # 頂点数・三角形数などの情報を表示
# 復元した色付きメッシュを表示する
o3d.visualization.draw_geometries([mesh])
