見出し画像

Open3Dによる3次元点群処理まとめ

 こんにちは。3Dエンジニアリングソリューション部のRataです。
 本記事では、部内の勉強会で行ったOpen3Dによる3次元点群処理を紹介します。
 近年発展が著しい人工知能が、人のように外界の情報を処理するためには、3次元世界の情報を3次元のデータとして取得する必要があります。3次元点群の処理方法を学ぶことで、人工知能に提供可能な3次元データを作成することが可能になります。
 本記事で紹介した3次元点群処理は書籍で紹介されているものの一部です。
 参考書籍は以下です。
 詳解 3次元点群処理 Pythonによる基礎アルゴリズムの実装 金崎 朝子 (著), 秋月 秀一 (著), 千葉 直也 (著)



概要

 Open3DとはC++およびPythonを用いて書かれた3次元点群データ処理関数を提供するオープンソースライブラリです。
 本記事ではOpen3Dを用いて点群を位置合わせするための大まかな流れと用語、必要な関数を紹介します。使用言語はPythonです。
 点群の位置合わせをするための流れは以下です。
 まず、点群ファイルを読み込みます。
 次に読み込んだ点群に対して前処理を行います。
 前処理では不要な点を除去する外れ値除去と、点群の特徴を維持したまま点の数を減らすサンプリングを行います。
 位置合わせでは前処理を行った点群に対してICPアルゴリズムまたはRANSACアルゴリズムによって位置合わせ元の点群の位置姿勢を変更するための剛体変換パラメータを推定、適用します。

Open3Dで表示したStanfordBunnyの点群

参考:StanfordBunny
https://graphics.stanford.edu/data/3Dscanrep/#bunny


Open3Dで扱える点群ファイル

 点群を表現することができるファイルフォーマットには色々あります。ここでは、Open3Dが扱うことができる3つのファイルフォーマットについて簡単に紹介します。

xyzファイル:点群の座標が格納されている。通常はプレーンテキスト形式です。

pcdファイル:データ部はxyz座標、RGBカラー値、法線ベクトル、曲率がバイナリデータで格納されています。参考にした書籍では主にこの形式でデータを読み込みます。

plyファイル:データ部は頂点、xyz座標、面、リストプロパティの定義がバイナリデータで格納されています。3Dファイル形式で色情報、頂点の法線、アプリケーション固有のプロパティなど、その他のデータを含めることも可能な、点群処理以外にも幅広く使えるフォーマットです。参考にした書籍ではメッシュデータからサンプリングをする際にこのデータを読み込みました。



外れ値除去

 現実世界の物体をセンサでスキャンして3次元点群データとして扱う場合、取得した点群にはノイズが含まれることがあります。外れ値除去後に行う位置合わせにおいて、ノイズとなる点は精度低下の原因になるため、物体表面ではない点や、密集しすぎた点を除去することでより正確なデータを得ることが出来ます。
 ここでは、統計的外れ値除去と半径外れ値除去について使用方法と結果の比較をします。

統計的外れ値除去:各点とその近傍点との距離の平均値に基づいてある閾値を定め、近傍点との距離が閾値以上になる点を外れ値として除去します。

import open3d as o3d
pcd = o3d.read_point_cloud(ファイル名)

# 統計的外れ値除去
# cl       	:外れ値除去後の点群
# ind     	:外れ値のインデックス
# nb_neighbors :考慮する近傍の個数
# std_ratio	:距離の閾値を指定
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
赤色の部分が外れ値

半径外れ値除去:各点を中心とするある半径の球内の点を近傍点としてみなしたとき、近傍点の個数が閾値未満となる点を外れ値として除去します。

import open3d as o3d
pcd = o3d.read_point_cloud(ファイル名)

# 半径外れ値除去
# nb_points :近傍点の個数の閾値を指定
# radius    :距離の閾値を指定
cl, ind = pcd.remove_radius_outlier(nb_points=16, radius=0.02)
赤色の部分が外れ値

 統計的外れ値除去と半径外れ値除去の結果を比較しても、外れ値として検出される場所に大きな差異は見られませんでした。ノイズが位置合わせに大きな影響を与えると考えられる場合は、位置合わせでは外れ値処理は統計的と半径のどちらかを使って外れ値除去を行う必要があります。


サンプリング

 点群データを扱うためには、点数の多さと解像度のバランスが重要です。3次元計測器により取得した点群データは空間的に不均一に存在しているため、サンプリングにより解像度を落とさずに均一性を増すことが出来ます。
 ここでは、等間隔サンプリングとPoisson Disk Sampling(PDS)について使用方法と結果の比較をします。

等間隔サンプリング:点群からサンプリングする関数。xyz軸方向それぞれに点が整列するような処理(ボクセル化)を行います。

import open3d as o3d
pcd = o3d.read_point_cloud(ファイル名)
mesh = o3d.read_triangle_mesh(ファイル名)

# points		:サンプリング後の点群
# voxcel_size       :ボクセルサイズ指定
points = pcd. voxel_down_sampling(voxcel_size=0.03)
サンプリング後

Poisson Disk Sampling(PDS):メッシュからサンプリングする関数。計算コストが低い。ランダムサンプリングに、サンプリングされた点の2点間距離の最小値を指定することで空間均一性が保証される手法。

import open3d as o3d
pcd = o3d.read_point_cloud(ファイル名)
mesh = o3d.read_triangle_mesh(ファイル名)

# number_of_point   :サンプリング後の点の数
PDS = mesh.sample_points_poisson_disk(number_of_point=500)
サンプリング前
サンプリング後



位置合わせ

 1つの物体を複数の3次元計測器を使って複数視点から取得した点群は、そのまま読み込んでも視点ごとの点群の位置が合わないことがあるため、位置合わせをすることで複数視点の点群を統合し、1つの物体を表す点群として扱うことが出来るようになります。
 位置合わせは、ICPアルゴリズムまたはRANSACアルゴリズムによって位置合わせ元の点群の位置姿勢を変更するための剛体変換パラメータを推定し、それを移動元の点群に適用することで位置合わせを行います。
 ここでは、ICPとRANSACについて使用方法と結果の比較をします。点群によってはサンプリングや外れ値除去などの前処理を行ってから位置合わせを行いますが、今回は参考書籍で紹介されている方法に従い、外れ値除去は行わずサンプリングのみ行った点群を使用しています。
 まず、推定した剛体変換の評価に使われる目的関数を紹介します。

PointToPoint:2点間距離を使う目的関数。
PointToPlane:移動元の点と移動先の面の距離を使う目的関数。

PointToPoint(緑)と PointToPlan(橙)の比較

 次にICPについて説明します。

ICP:重なり合った点群同士の位置を合わせるための回転と並進(剛体変換)を推定する手法。

目的関数(PointToPoint) = o3d.pipelines.registration.TransformationEstimationPointToPoint
# または
目的関数(PointToPlane) = o3d.pipelines.registration.TransformationEstimationPointToPlane

# pcd_s          :位置合わせ元点群
# pcd_t          :位置合わせ先点群
# threshold      :対応付け時の最大距離
# trans_init     :初期姿勢を表す配列
# obj_func       :目的関数
ICP = o3d. pipelines.registration.registration_icp(
pcd_s, pcd_t, threshold,
trans_init, obj_func)


RANSAC:点群の中で外れ値を無視して、最も信頼性の高い回転と並進(剛体変換)を推定する手法。

目的関数(PointToPoint) = o3d.pipelines.registration.TransformationEstimationPointToPoint
# または
目的関数(PointToPlane) = o3d.pipelines.registration.TransformationEstimationPointToPlane

# trans_init	    	:初期姿勢を表す配列
# obj_func      	:目的関数
# s_kp         	 	:移動元点群
# t_kp         		:移動先点群
# corrs         	 	:対応点のインデックスリスト
# s_feature    	 	: 移動元点群の特徴点
# t_feature		:移動先点群の特徴点
# distance_threshold	:対応点ペアの最大距離
# ransac_n      	:剛体変換行列の推定のためにサンプリングする対応点の個数
# checker_func       	:枝切り処理に使われる関数
# ite_max            	:最大試行回数
# accuracy       	:RANSACを終了する精度
RANSAC = o3d.pipelines. registration.registration_ransac_based_on_feature_matching(
s_kp, t_kp, corrs, s_feature, t_feature,
True, distance_threshold, obj_func,
ransac_n=3, checkers=[checker_func],
criteria=o3d. pipelines. egistration.registration.RANSACConvergenceCriteria(ite_max, accuracy))

 今回行った位置合わせでは、ウサギの点群はICPとRANSACのどちらも上手く位置合わせが出来ました。室内の点群はRANSACでのみ上手くできており、ICPは設定値がよくなかったのか上手くいきませんでした。



感想

 今回は位置合わせを行いました。
点群処理は初めてなのでよく分かりませんでしたが、難しいコードを書かなくてもライブラリが賢く行ってくれることが多いので取っ付き易いかなと思いました。点群に合わせて設定値を調整するのが難しかったです。
 次回は物体認識に取り組んでみたいと思います。

#株式会社オープンストリームホールディングス
#ニュートラル株式会社
#openlab
#NTechブログ
#テックブログ
#IT
#エンジニア
#3D
#Open3D
#Python

この記事が気に入ったらサポートをしてみませんか?