※当サイトはPRを含みます

【Python_点群処理】点群データをメッシュに変換してプロットする方法

2023年7月20日

はじめに

今回は点群からメッシュを作成して可視化してみます。

メッシュの作成方法はいくつかありますが、今回は点群からドロネー三角形を計算してメッシュを生成します。

ドロネー三角形の詳細については改めて記事を書く予定です。

ドロネー三角形の計算はScipyやmatplotlibで用意されています。

どちらも点群のx、y座標をもとにドロネー三角を計算し、面を形成する3点のインデックスが配列として返されます。np.array([面の数,3])

点群データ test_dataのドロネー三角形を計算して、返ってきた配列の一つが [1,28,12]であった場合、test_dataの1番目と28番目と12番目の点を繋ぐことで面を一つ作ることができます。

この記事では

  • matplotlibとscipyを使って点群からメッシュを作成する方法
  • Open3Dでメッシュをプロット
  • k3dでメッシュをプロット
  • plotlyでメッシュをプロット

する方法を紹介します。

今回使用するデータは以下のような2次元サイン波の点群です。

点群データからメッシュを作成する方法

matplotlibを使う場合

from matplotlib.tri import Triangulation
tri_matplot = Triangulation(test_data[:,0], test_data[:,1])

面を生成するインデックスはtri_matplot.trianglesに入っており、以下のような中身になっています。

# tri_matplot.trianglesのOutput
array([[9621, 2021, 4857],
       [4857, 2021, 8940],
       [  90, 7348, 2851],
       ...,
       [9874,  781, 7234],
       [3010, 7612, 7234],
       [7234,  781, 3010]], dtype=int32)

scipyを使う場合

from scipy.spatial import Delaunay
xy_catalog = []
for point in test_data:
    xy_catalog.append([point[0], point[1]])
tri_scipy = Delaunay(np.array(xy_catalog))

同様に面を生成するインデックスはtri_scipy.simplicesに入っており、以下のような中身になっています。

# tri_scipy.simplicesのOutput
array([[8131, 4444, 5209],
       [4444, 8131,  807],
       [8131, 4881,   83],
       ...,
       [3166, 6907,  464],
       [5191, 3166, 9884],
       [6907, 3166, 5191]], dtype=int32)

Open3Dでメッシュをプロットする方法

元の点群データと面の情報をOpen3Dの形に変換してメッシュをプロットします。

下記結果ではワイヤーフレームを表示しています。

import open3d as o3d
surface = o3d.geometry.TriangleMesh()
surface.vertices = o3d.utility.Vector3dVector(test_data)
surface.triangles = o3d.utility.Vector3iVector(tri_matplot.triangles)
# surface.triangles = o3d.utility.Vector3iVector(tri_scipy.simplices)
o3d.visualization.draw_geometries ([surface], mesh_show_wireframe=True)

K3Dでメッシュをプロットする方法

K3Dを使ってメッシュをプロットします。

点群データはk3d.points()を使いましたが、メッシュを表示する際にはk3d.mesh()を使用します。

import k3d
plot = k3d.plot(camera_auto_fit=True) 
point_size=0.1
plot += k3d.points(test_data, point_size=point_size, shader="3d", color = 0xff0000)
plot += k3d.mesh(test_data, tri_matplot.triangles)
# plot += k3d.mesh(test_data, tri_scipy.simplices)
plot.display()

上がワイヤーフレーム非表示、下がワイヤーフレームを表示した時の結果です。

plotlyでメッシュをプロットする方法

plotlyを使ってメッシュを表示します。綺麗に表示するためにz軸や表示サイズなどを設定しています。そのためopen3dやK3Dと比べて使い勝手は良くない印象です。

import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Mesh3d(x=test_data[:,0], y=test_data[:,1], z=test_data[:,2], 
                        i= tri_matplot.triangles[:,0], j= tri_matplot.triangles[:,1], k=tri_matplot.triangles[:,2])
             )
# fig.add_trace(go.Mesh3d(x=test_data[:,0], y=test_data[:,1], z=test_data[:,2],
#                         i= tri_scipy.simplices[:,0], j= tri_scipy.simplices[:,1], k= tri_scipy.simplices[:,2])) 
fig.update_layout(scene = dict(xaxis= dict(range=[test_data[:,0].min(), test_data[:,0].max()],),
                               yaxis= dict(range=[test_data[:,1].min(), test_data[:,1].max()],),
                               zaxis= dict(range= [test_data[:,2].min(), test_data[:,2].max()+20],)),
                  width=700,
                  margin=dict(r=20, l=10, b=10, t=10))
fig.show()