Learning Open3DTutorial基礎編→Numpyを用いた処理

Working with Numpy

NumPyを用いた処理

Open3Dのデータ構造は、NumPyと互換性がある。 このチュートリアルでは、NumPyを使用してsinc関数を作り、Open3Dを使用してその関数を可視化する。

注意: コードを走らせる環境に注意すること。TestDataディレクトリがカレント(作業)ディレクトリからみて、祖先ディレクトリの下にあることを確認しよう

In [27]:
# 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])
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]]

上記のコードの最初の部分では $n \times 3$の行列xyzを生成している。その各列は$\displaystyle z = \frac{\sin(x^2+y^2)}{x^2+y^2}$という関係の$x,y,z$の値をもつ。$z_{norm}$は[0,1]の範囲に$z$を正規化している。


From NumPy to open3D PointCloud

NumPyからopen3d.PointCloudへ

examples/Python/Basic/working_with_numpy.pyの2番めの部分コード:

In [ ]:
    # (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.colorspy3d.PointCloud.normalsなど、類似したデータ構造は、NumPyを使用して割り当てたり変更したりできる。 上記のスクリプトは、ポイントクラウドpcdを次のステップのためのPLYファイルとして保存している(拡張子が ply になっていることに注意)。

注: PLYについてはWikipedia記事参照

From open3D PointCloud to NumPy

open3d.PointCloudからNumPyへ

examples/Python/Basic/working_with_numpy.pyの3番目と4番目の部分コード:

In [ ]:
    # (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)

この例が示すように、Vector3dVectornp.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]]

そして、関数の可視化を行う: http://www.open3d.org/docs/release/_images/sync_3d.png

From Numpy to open3D Image

Numpyからopen3d.Imageへ

2D Numpy行列は画像に変換できる 次の例では、z_normopen3d.Imageに変換し、draw_geometriesを使用して画像を視覚化し、画像をpng形式のファイルとして保存する。

examples/Python/Basic/working_with_numpy.pyの最後の部分コード:

In [ ]:
    # (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])

得られる画像:

http://www.open3d.org/docs/release/_images/sync_image.png

注意

この変換は、c_typeストレージ(デフォルトのNumPy動作)、dim = 2 (幅×高さ)、またはdim = 3 (幅 ×高さ×チャンネル)でuint8, uint16, float32をサポートしている

2020年1月の時点でo3d.visualization.draw_geometries([img])はエラーを起こす。 それについて以下でチェックする:

In [2]:
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())
In [3]:
z_norm
Out[3]:
array([[0.17846472, 0.17440115, 0.17063709, ..., 0.17063709, 0.17440115,
        0.17846472],
       [0.17440115, 0.17061936, 0.16743645, ..., 0.16743645, 0.17061936,
        0.17440115],
       [0.17063709, 0.16743645, 0.16508846, ..., 0.16508846, 0.16743645,
        0.17063709],
       ...,
       [0.17063709, 0.16743645, 0.16508846, ..., 0.16508846, 0.16743645,
        0.17063709],
       [0.17440115, 0.17061936, 0.16743645, ..., 0.16743645, 0.17061936,
        0.17440115],
       [0.17846472, 0.17440115, 0.17063709, ..., 0.17063709, 0.17440115,
        0.17846472]])
In [4]:
img = o3d.geometry.Image((z_norm * 255).astype(np.uint8))
In [5]:
img
Out[5]:
Image of size 401x401, with 1 channels.
Use numpy.asarray to access buffer data.

imgはuint8の2次元データになっている。

In [7]:
np.asarray(img)
Out[7]:
array([[45, 44, 43, ..., 43, 44, 45],
       [44, 43, 42, ..., 42, 43, 44],
       [43, 42, 42, ..., 42, 42, 43],
       ...,
       [43, 42, 42, ..., 42, 42, 43],
       [44, 43, 42, ..., 42, 43, 44],
       [45, 44, 43, ..., 43, 44, 45]], dtype=uint8)
In [16]:
o3d.visualization.draw_geometries([img])
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-16-34d045085208> in <module>
----> 1 o3d.visualization.draw_geometries([img])

RuntimeError: [Open3D ERROR] [CreateCoordinateFrame] size <= 0

いまのところ画像に対しo3d.visualization.draw_geometries関数を適用した例が見つからないので、解決策は不明である。