【Python】Open3Dで3D点群処理!RANSACによる平面・直線推定の方法
この記事では、RANSACアルゴリズムを用いて、3次元点群データから「平面」や「直線(エッジ)」を抽出する方法について解説します。
はじめに
点群データから平面や直線を抽出する技術は、自動運転やロボットの環境認識(床面や壁の検出)、AR/VR、そしてリバースエンジニアリングなど、さまざまな3Dビジョンのタスクで基礎となる非常に重要な処理です。
RANSACを使うことで、以下のようにノイズを含むデータの中から綺麗に平面を検出することが可能になります。(参照元)
RANSACとは
RANSAC(Random Sample Consensus)は、外れ値(ノイズ)を多く含む観測データの中から、正しいモデルを推定するためのロバスト推定アルゴリズムです。
通常の「最小二乗法」などでは、少数の極端な外れ値に全体が引っ張られてしまい、正しい推定ができないことがあります。一方RANSACは、最初から「データにはノイズが含まれていること」を前提としているため、LiDARや深度カメラで取得したような現実のノイズが多い3Dセンサーデータに対して非常に有効です。
具体的な処理の手順は以下の通りです。
1. サンプリング: データの中からランダムに数点を抽出する。
2. モデル候補の作成: 抽出した点からモデル(直線や平面など)を計算し、誤差がしきい値以下であれば正しいモデルの候補に追加する。

3. 仮モデルの評価: 抽出したデータを元に最小二乗法などで仮のモデルを推定し、そのモデルと全体のデータとの誤差を求める。

4. インライアの判定: 仮のモデルからの距離がしきい値以下のデータ点を「インライア(外れ値ではない有効なデータ)」とする。

5. 反復処理: インライアの数がしきい値以上になるか、あらかじめ決めた反復回数に達するまで上記のプロセスを繰り返す。
今回は3次元の点群データ(x, y, z)に対して実装を行います。
直線(ベクトルA、Bを用いたモデル)を推定する場合は以下の式を利用し、
y = Ax + B
平面を推定したい場合は以下の一般式を用いて計算を行います。
ax + by + cz + d = 0
Open3Dでの実装
まずは、3Dデータ処理の定番ライブラリである「Open3D」を使って平面推定を実装してみます。
Open3Dのチュートリアルはこちら
平面抽出に使う pcd.segment_plane メソッドの主要な引数は以下の3つです。扱うデータのスケールに合わせて distance_threshold を調整するのが精度の鍵になります。
- distance_threshold: インライアと判定するための距離のしきい値(推定された平面からこの距離以内にある点を平面の一部とみなします)
- ransac_n: 平面を推定するために最低限必要な点の数(平面を決定するには最低3点が必要なため「3」とします)
- num_iterations: RANSACの試行回数(多いほど精度が上がる可能性がありますが、計算時間がかかります)
実際のコードは以下の通りです。
import open3d as o3d
# 点群の読み込み
pcd = o3d.io.read_point_cloud("../../test_data/fragment.pcd")
# 引数を設定して、平面を推定
plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
ransac_n=3,
num_iterations=1000)
# 平面モデルの係数を出力
[a, b, c, d] = plane_model
print(f"Plane equation: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0")
# インライアの点を抽出して色を赤にする
inlier_cloud = pcd.select_by_index(inliers)
inlier_cloud.paint_uniform_color([1.0, 0, 0])
# 平面以外の点を抽出
outlier_cloud = pcd.select_by_index(inliers, invert=True)
# 可視化
o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud],
zoom=0.8,
front=[-0.4999, -0.1659, -0.8499],
lookat=[2.1813, 2.0619, 2.0999],
up=[0.1204, -0.9852, 0.1215])
実行すると、推定された平面の式が以下のように出力されます。
Plane equation: -0.06x + -0.10y + 0.99z + -1.06 = 0
可視化画面では、赤くプロットされている箇所が平面として推定された点群になります。

pyRANSAC-3Dでの実装
続いて「pyRANSAC-3D」という特化型ライブラリを使ってみます。
このライブラリは直線や平面だけでなく、球や円柱などの推定も簡単に行えるのが特徴です。
詳細なドキュメントはこちら(pyRANSAC-3D)をご覧ください。
まずはインストールを行います。
pip3 install pyransac3d
公式のサンプルコードを使用するため、リポジトリをクローンします。
git clone https://github.com/leomariga/pyRANSAC-3D.git
直線推定
直線推定の例を試してみましょう。コマンドラインで以下を実行します。
python3 pyRANSAC-3D/tests/test_line.py
左が元の点群、右がRANSACで直線を推定した結果です。
赤色の点がインライア、赤色の線が近似された直線を表しています。
ソースコードの抜粋は以下の通りです。
import open3d as o3d
import pyransac3d as pyrsc
import numpy as np
# データをnumpy配列に変換
points = np.asarray(pcd_load.points)
# 直線モデルを定義
line = pyrsc.Line()
# RANSACによる直線推定(しきい値は15に設定)
A, B, inliers = line.fit(points, thresh=15)
# 元のデータにおけるインライアの点の色を赤に変更
plane = pcd_load.select_by_index(inliers).paint_uniform_color([1, 0, 0])
# Open3Dで可視化
o3d.visualization.draw_geometries([pcd_load, plane])
平面推定
次に平面推定です。以下のコマンドを実行します。
python3 pyRANSAC-3D/tests/test_plane.py
赤色の点が推定した平面のインライア、青色の線が平面のバウンディングボックス(境界箱)です。

実装コードは以下のようになります。
# 点群データ読み込みとnumpy配列への変換
pcd_load = o3d.io.read_point_cloud('/tests/dataset/caixa.ply')
points = np.asarray(pcd_load.points)
# 平面モデルを定義
plano1 = pyrsc.Plane()
# RANSACによる平面推定(しきい値は0.01に設定)
best_eq, best_inliers = plano1.fit(points, 0.01)
# インライアの点の色を赤に変更
plane = pcd_load.select_by_index(best_inliers).paint_uniform_color([1, 0, 0])
# 平面のバウンディングボックスを取得して青色に設定
obb = plane.get_oriented_bounding_box()
obb.color = [0, 0, 1]
# 平面以外の点を抽出
not_plane = pcd_load.select_by_index(best_inliers, invert=True)
# Open3Dで可視化
o3d.visualization.draw_geometries([not_plane, plane, obb])
おまけ:Jupyter Lab (k3d) での可視化
Open3Dが手元の環境にインストールできない場合や、Jupyter上で直接インタラクティブに可視化したい場合は、k3dというライブラリが便利です。

import k3d
import numpy as np
# 点群データをnumpy配列に変換
not_plane1 = np.asarray(not_plane.points)
plane1 = np.asarray(plane.points)
# プロットの初期化と描画
plot = k3d.plot(name='ransac_plane')
plot += k3d.points(not_plane1, point_size=0.01)
plot += k3d.points(plane1, point_size=0.01, color=0xff0000) # 赤色で描画
plot.display()
※k3dのより詳しい使い方はこちらの記事でも紹介しています。
【Python_K3D】K3Dでクラスごとに色を付けて点群データを可視化
まとめ
本記事では、RANSACアルゴリズムの基本概念と、Open3DおよびpyRANSAC-3Dを利用した3次元点群からの平面・直線の抽出方法について紹介しました。パラメータ(しきい値や反復回数など)の調整によって抽出の精度が大きく変わるため、お手元のデータに合わせて色々と試してみてください。













ディスカッション
コメント一覧
まだ、コメントがありません