Search code examples
pythonnumpyopen3d

Is it possible to create o3d.geometry.VoxelGrid directly from numpy binary array?


In open3D, we can create point cloud by something like this

import open3d as o3d
import numpy as np
pcl = o3d.geometry.PointCloud()
pcl.points = o3d.utility.Vector3dVector(np.random.randn(500,3))
o3d.visualization.draw_geometries([pcl])

But for binary numpy array, e.g. voxel, if I want to visualize the voxel, I need to convert it to point cloud and then use o3d.geometry.VoxelGrid.create_from_point_cloud to convert the point cloud back to the open3D VoxelGrid

voxel = np.zeros((32, 32, 32), np.uint8)
voxel[16, 16, 16] = 1

# normalize
pcl = o3d.geometry.PointCloud()
pc = np.stack(np.where(voxel == 1)).transpose((1, 0)) / (voxel.shape[0] - 1)
pcl.points = o3d.utility.Vector3dVector(pc)
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcl, voxel_size=0.04)
o3d.visualization.draw_geometries([voxel_grid])

Is there any way to create o3d.geometry.VoxelGrid directly from voxel like

### this doesn't work
voxel = np.zeros((32, 32, 32), np.uint8)
voxel[16, 16, 16] = 1

voxel_grid = o3d.geometry.VoxelGrid()
voxel_grid.voxels = o3d.utility.Vector3dVector(voxel)
o3d.visualization.draw_geometries([voxel_grid])

Solution

  • As of right now, the answer looks to be a firm no, you can't.

    VoxelGrid isn't actually storing the data the way you have it. The underlying datastructure is actually an unordered_map of 3d indices and Voxel objects. I would describe it as a sparse representation, and you have a dense representation of the data (which would cover the entire domain with white and black pixels). You introduce the sparseness when you do: np.where(voxel == 1). It's also missing voxel_size, origin and of course the voxel colors.

    It's not impossible to add some method like create_from_dense_array, but there is nothing of the sort today from what I can see browsing the code.

    They could also add a pybind wrapper to the AddVoxel/add_voxel method easily, but that is also currently overlooked. Even then, it would still require you to compute the sparse voxel yourself and construct the o3d.geometry.Voxel object(s), probably requiring some additional utility helper method like Vector3dVector but for voxels.

    Currently it seems to me that the best route is to go via the PointCloud like you already have done.