Working with NumPy

Data structure of Open3D is natively compatible with NumPy buffer. The following tutorial generates a variant of sync function using NumPy and visualizes the function using Open3D.

 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# examples/Python/Basic/working_with_numpy.py

import copy
import numpy as np
import open3d as o3d

if __name__ == "__main__":

    # 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)

    # 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)

    # Load saved point cloud and visualize it
    pcd_load = o3d.io.read_point_cloud("../../TestData/sync.ply")
    o3d.visualization.draw_geometries([pcd_load])

    # convert Open3D.o3d.geometry.PointCloud to numpy array
    xyz_load = np.asarray(pcd_load.points)
    print('xyz_load')
    print(xyz_load)

    # 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)
    o3d.visualization.draw_geometries([img])

The first part of the script generates a \(n \times 3\) matrix xyz. Each column has \(x, y, z\) value of a function \(z = \frac{sin (x^2+y^2)}{(x^2+y^2)}\). \(z_{norm}\) is normalized map of \(z\) for [0,1] range.

From NumPy to open3d.PointCloud

25
26
27
28
    # 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 provides conversion from NumPy matrix to a vector of 3D vectors. By using Vector3dVector, NumPy matrix can be directly assigned for open3d.PointCloud.points.

In this manner, any similar data structure such as open3d.PointCloud.colors or open3d.PointCloud.normals can be assigned or modified using NumPy. The script saves the point cloud as a ply file for the next step.

From open3d.PointCloud to NumPy

30
31
32
33
34
35
36
37
    # Load saved point cloud and visualize it
    pcd_load = o3d.io.read_point_cloud("../../TestData/sync.ply")
    o3d.visualization.draw_geometries([pcd_load])

    # convert Open3D.o3d.geometry.PointCloud to numpy array
    xyz_load = np.asarray(pcd_load.points)
    print('xyz_load')
    print(xyz_load)

As shown in this example, Vector3dVector is converted into a NumPy array using np.asarray.

The tutorial script prints two identical matrices

xyz
[[-3.00000000e+00 -3.00000000e+00 -3.89817183e-17]
 [-2.98500000e+00 -3.00000000e+00 -4.94631078e-03]
 [-2.97000000e+00 -3.00000000e+00 -9.52804798e-03]
 ...
 [ 2.97000000e+00  3.00000000e+00 -9.52804798e-03]
 [ 2.98500000e+00  3.00000000e+00 -4.94631078e-03]
 [ 3.00000000e+00  3.00000000e+00 -3.89817183e-17]]
Writing PLY: [========================================] 100%
Reading PLY: [========================================] 100%
xyz_load
[[-3.00000000e+00 -3.00000000e+00 -3.89817183e-17]
 [-2.98500000e+00 -3.00000000e+00 -4.94631078e-03]
 [-2.97000000e+00 -3.00000000e+00 -9.52804798e-03]
 ...
 [ 2.97000000e+00  3.00000000e+00 -9.52804798e-03]
 [ 2.98500000e+00  3.00000000e+00 -4.94631078e-03]
 [ 3.00000000e+00  3.00000000e+00 -3.89817183e-17]]

and visualizes the function:

../../_images/sync_3d.png

From NumPy to open3d.Image

2D Numpy matrix can be converted image. The following example converts z_norm into open3d.Image, visualize the image using draw_geometries, and save the image as a png format file.

39
40
41
42
    # 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)
    o3d.visualization.draw_geometries([img])
../../_images/sync_image.png

Note

The conversion supports uint8, uint16, or float32 with c_type storage (default NumPy behavior), dim=2`` (width * height) or dim=3 (width * height * channel).