本記事では、Open3Dの平面検出手法の一例として、RANSACを利用したsegment_plane関数について解説する。
また、本記事の作成にあたり、以下の書籍が非常に参考になりましたので良ければご参考に。
リンク
なぜ平面検出を行う?
平面検出を実行するシチュエーションは以下が考えられる
- 「平面」の特徴を持った物体(地面やテーブル)を点群情報から抽出したい
- 平面以外の物体を点群情報から抽出したい
平面でない物体が抽出対象の場合、平面を構成する点群情報は”除去対象”である。
そのため、物体検出のための前処理として平面検出することがある。
また、平面上に位置する物体(地面の車やテーブル上のマグカップなど)の検出するシチュエーションにおいて、平面を除去または「平面上に位置している」ことを活用する手法もある。
実行環境
本記事の実行環境です
- OS:Windows 10
- Python Ver.3.11
- Open3d Ver.0.18.0
インストールはこちらを参考にしました。
処理対象の点群および課題設定
処理対象の点群
RealSenseにより取得した点群およびテキスチャ情報です。
以下のRealSenseの箱とマウスの情報を、Ply形式で保存しています。
課題設定
以下の2つの平面を検出する
- 箱とマウスが置かれている床
- 写真上側にある、床の平面に対して垂直な壁
RANSACを用いた平面検出
簡単なRANSACの理論
- データからランダムに少数のサンプル(例: 3点)を選ぶ。
- サンプルを使ってモデル(平面や直線など)を仮定する。
- 仮定したモデルに近いデータ点(インライア)を数える。
- この手順を繰り返し、最も多くのインライアを含むモデルを選ぶ。
Open3Dの関数の詳細
関数
open3d.geometry.PointCloud.segment_plane
引数
- distance_threshold: 平面上の点とみなすための最大距離(単位:m)
- ransac_n: RANSACアルゴリズムで選択するサンプル数(通常は3)
- num_iterations: アルゴリズムの最大反復回数
戻り値
- plane_model: 平面の方程式([a, b, c, d])。平面は ax+by+cz+d=0 で表される。
- inliers: 平面とみなされた点
サンプルコード
サンプルコードでは以下を実行している
- Plyファイル読み込みおよび点群モデルを取得
- 外れ値除去
- ダウンサンプリング
- RANSACを用いた平面検出
- 平面検出により抽出した点群が1000点以上であった場合、平面とみなす
import open3d as o3d
import random
import numpy as np
# 点群を読み込む
filename = 'realsensebox.ply'
print(o3d.io.read_file_geometry_type(filename))
mesh = o3d.io.read_triangle_mesh(filename)
# メッシュモデルから点群モデルへの変換
vertices = mesh.vertices
pcd = o3d.geometry.PointCloud()
pcd.points = vertices
# 外れ値除去のパラメータ設定
nb_neighbors = 20 # 各点の近傍点数
std_ratio = 2.0 # 標準偏差比(高い値にすると除去が緩やかに)
# 外れ値除去(統計的アウトライヤ除去)
pcd, ind = pcd.remove_statistical_outlier(nb_neighbors=nb_neighbors, std_ratio=std_ratio)
# ダウンサンプリング
voxel_size = 0.01 # ボクセルサイズを設定
pcd = pcd.voxel_down_sample(voxel_size=voxel_size)
o3d.visualization.draw_geometries([pcd])
# パラメータ設定
distance_threshold = 0.01 # 平面との距離の閾値
ransac_n = 3 # 平面モデルの最小点数
num_iterations = 1000 # RANSACの試行回数
# 検出された平面を格納するリスト
planes = []
while True:
# RANSACを使って平面を検出
plane_model, inliers = pcd.segment_plane(
distance_threshold=distance_threshold,
ransac_n=ransac_n,
num_iterations=num_iterations,
)
# 小さい平面の場合、検出を終了
if len(inliers) < 1000:
break
# 平面の点群を抽出
plane_cloud = pcd.select_by_index(inliers)
# ランダムな色を生成して塗り分け
color = [random.uniform(0, 1) for _ in range(3)]
plane_cloud.paint_uniform_color(color)
# 平面情報を格納
planes.append(plane_cloud)
o3d.visualization.draw_geometries(planes)
# 検出された平面を除去
pcd = pcd.select_by_index(inliers, invert=True)
o3d.visualization.draw_geometries([pcd])
# 残りの点群を特定の色で可視化
if len(pcd.points) > 0:
pcd.paint_uniform_color([0.0, 0.0, 0.0]) # 黒色で塗りつぶし
planes.append(pcd)
# 結果を可視化
o3d.visualization.draw_geometries(planes)
まとめ
本記事では、Open3Dの平面検出手法の一例として、RANSACを利用したsegment_plane関数について解説した。
三次元点群処理は二次元の画像処理よりも情報が少なく、手探りで書籍や論文、公式ドキュメントを調査する日々です。
その中で、導入しやすいPythonとOpen3Dで三次元点群処理を体系的にまとめた書籍が以下になります。現在数少ない三次元点群処理の書籍ですので私も重宝しています。是非参考にしてみてください。
リンク