Learning Open3D→Tutorial→基礎編→Numpyを用いた処理
NumPyを用いた処理
Open3Dのデータ構造は、NumPyと互換性がある。 このチュートリアルでは、NumPyを使用してsinc関数を作り、Open3Dを使用してその関数を可視化する。
注意: コードを走らせる環境に注意すること。TestDataディレクトリがカレント(作業)ディレクトリからみて、祖先ディレクトリの下にあることを確認しよう
# examples/Python/Basic/working_with_numpy.py
import copy
import numpy as np
import open3d as o3d
if __name__ == "__main__":
# (1)generate some neat n times 3 matrix using a variant of sync function
x = np.linspace(-3, 3, 401)
mesh_x, mesh_y = np.meshgrid(x, x)
z = np.sinc((np.power(mesh_x, 2) + np.power(mesh_y, 2)))
z_norm = (z - z.min()) / (z.max() - z.min())
xyz = np.zeros((np.size(mesh_x), 3))
xyz[:, 0] = np.reshape(mesh_x, -1)
xyz[:, 1] = np.reshape(mesh_y, -1)
xyz[:, 2] = np.reshape(z_norm, -1)
print('xyz')
print(xyz)
# (2)Pass xyz to Open3D.o3d.geometry.PointCloud and visualize
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
o3d.io.write_point_cloud("../../TestData/sync.ply", pcd)
# (3)Load saved point cloud and visualize it
pcd_load = o3d.io.read_point_cloud("../../TestData/sync.ply")
o3d.visualization.draw_geometries([pcd_load])
# (4)convert Open3D.o3d.geometry.PointCloud to numpy array
xyz_load = np.asarray(pcd_load.points)
print('xyz_load')
print(xyz_load)
# (5)save z_norm as an image (change [0,1] range to [0,255] range with uint8 type)
img = o3d.geometry.Image((z_norm * 255).astype(np.uint8))
o3d.io.write_image("../../TestData/sync.png", img)
# Caution: the following code raises an error
# o3d.visualization.draw_geometries([img])
上記のコードの最初の部分では $n \times 3$の行列xyz
を生成している。その各列は$\displaystyle z = \frac{\sin(x^2+y^2)}{x^2+y^2}$という関係の$x,y,z$の値をもつ。$z_{norm}$は[0,1]
の範囲に$z$を正規化している。
NumPyからopen3d.PointCloudへ
examples/Python/Basic/working_with_numpy.py
の2番めの部分コード:
# (2)Pass xyz to Open3D.o3d.geometry.PointCloud and visualize
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
o3d.io.write_point_cloud("../../TestData/sync.ply", pcd)
Open3Dは、NumPy行列から3Dベクトルへの変換を提供している。 Vector3dVector
を使用すると、NumPy行列をpy3d.PointCloud.points
に直接割り当てることができる。
このようにして、py3d.PointCloud.colors
やpy3d.PointCloud.normals
など、類似したデータ構造は、NumPyを使用して割り当てたり変更したりできる。 上記のスクリプトは、ポイントクラウドpcdを次のステップのためのPLYファイルとして保存している(拡張子が ply になっていることに注意)。
注: PLYについてはWikipedia記事参照
open3d.PointCloudからNumPyへ
examples/Python/Basic/working_with_numpy.pyの3番目と4番目の部分コード:
# (3)Load saved point cloud and visualize it
pcd_load = o3d.io.read_point_cloud("../../TestData/sync.ply")
o3d.visualization.draw_geometries([pcd_load])
# (4)convert Open3D.o3d.geometry.PointCloud to numpy array
xyz_load = np.asarray(pcd_load.points)
print('xyz_load')
print(xyz_load)
この例が示すように、Vector3dVector
はnp.asarray
を使用してNumPy配列に変換される。
上記のスクリプトは2つの同一の行列を出力する(比較せよ)。
xyz
[[-3. -3. 0.17846472]
[-2.985 -3. 0.17440115]
[-2.97 -3. 0.17063709]
...
[ 2.97 3. 0.17063709]
[ 2.985 3. 0.17440115]
[ 3. 3. 0.17846472]]
xyz_load
[[-3. -3. 0.17846472]
[-2.985 -3. 0.17440115]
[-2.97 -3. 0.17063709]
...
[ 2.97 3. 0.17063709]
[ 2.985 3. 0.17440115]
[ 3. 3. 0.17846472]]
そして、関数の可視化を行う:
Numpyからopen3d.Imageへ
2D Numpy行列は画像に変換できる 次の例では、z_norm
をopen3d.Image
に変換し、draw_geometries
を使用して画像を視覚化し、画像をpng
形式のファイルとして保存する。
examples/Python/Basic/working_with_numpy.pyの最後の部分コード:
# (5)save z_norm as an image (change [0,1] range to [0,255] range with uint8 type)
img = o3d.geometry.Image((z_norm * 255).astype(np.uint8))
o3d.io.write_image("../../TestData/sync.png", img)
# Caution: the following code raises an error
# o3d.visualization.draw_geometries([img])
得られる画像:
この変換は、c_type
ストレージ(デフォルトのNumPy動作)、dim = 2
(幅×高さ)、またはdim = 3
(幅 ×高さ×チャンネル
)でuint8
, uint16
, float32
をサポートしている
2020年1月の時点でo3d.visualization.draw_geometries([img])
はエラーを起こす。
それについて以下でチェックする:
x = np.linspace(-3, 3, 401)
mesh_x, mesh_y = np.meshgrid(x, x)
z = np.sinc((np.power(mesh_x, 2) + np.power(mesh_y, 2)))
z_norm = (z - z.min()) / (z.max() - z.min())
z_norm
img = o3d.geometry.Image((z_norm * 255).astype(np.uint8))
img
img
はuint8の2次元データになっている。
np.asarray(img)
o3d.visualization.draw_geometries([img])
いまのところ画像に対しo3d.visualization.draw_geometries
関数を適用した例が見つからないので、解決策は不明である。