"""Conversions to and from the Open3D library.
Open3D is useful for doing various point cloud processing routines.
It can be installed using pip as follows::
pip install open3d-python
"""
import open3d
from klampt import PointCloud,TriangleMesh,VolumeGrid,Geometry3D
from ..model import geometry
[docs]def to_open3d(obj):
"""Converts Klamp't geometry to an open3d geometry.
Geometry3D objects are converted applying the current transform.
If the VolumeGrid is considered to be an occupancy grid (all values
between 0 and 1), then it is converted to a VoxelGrid containing a
voxel for any positive items. If it is considered to be a SDF,
then it contains a voxel of any non-positive items. There may be
issues converting from VolumeGrids whose cells are non-cubic.
"""
if isinstance(obj,PointCloud):
pc = open3d.geometry.PointCloud()
pc.points = open3d.utility.Vector3dVector(obj.getPoints())
#TODO: other properties
colors = geometry.point_cloud_colors(obj,('r','g','b'))
if colors is not None:
for i in range(obj.numPoints()):
pc.colors.append(colors[i])
return pc
elif isinstance(obj,TriangleMesh):
m = open3d.geometry.TriangleMesh()
m.vertices = open3d.utility.Vector3dVector(obj.getVertices())
m.triangles = open3d.utility.Vector3iVector(obj.getIndices())
return m
elif isinstance(obj,VolumeGrid):
import numpy as np
origin = np.array([obj.bbox[0],obj.bbox[1],obj.bbox[2]])
cx = (obj.bbox[3]-obj.bbox[0])/obj.dims[0]
cy = (obj.bbox[4]-obj.bbox[1])/obj.dims[1]
cz = (obj.bbox[5]-obj.bbox[2])/obj.dims[2]
voxel_size = pow(cx*cy*cz,1.0/3.0)
values = obj.getValues()
vrange = np.min(values),np.min(values)
if vrange[0] < 0 or vrange[1] > 1:
#treat as SDF
indices = np.nonzero(values <= 0)
else:
#treat as occupancy grid
indices = np.nonzero(values > 0.5)
pc = open3d.geometry.PointCloud()
for i in indices[0]:
cell = np.array([i//(obj.dims[2]*obj.dims[1]), (i//obj.dims[2]) % obj.dims[1], i%obj.dims[2]])
pt = (cell + 0.5)*voxel_size + origin
pc.points.append(pt)
return open3d.geometry.create_surface_voxel_grid_from_point_cloud(pc,voxel_size)
elif isinstance(obj,Geometry3D):
if obj.type() == 'PointCloud':
pc = obj.getPointCloud()
pc.transform(*obj.getCurrentTransform())
return to_open3d(pc)
elif obj.type() == 'TriangleMesh':
m = obj.getTriangleMesh()
m.transform(*obj.getCurrentTransform())
return to_open3d(m)
raise TypeError("Invalid type")
[docs]def from_open3d(obj):
"""Converts open3d geometry to a Klamp't geometry.
"""
if isinstance(obj,open3d.geometry.PointCloud):
import numpy as np
pc = PointCloud()
pc.setPoints(np.asarray(obj.points))
if obj.has_colors():
geometry.point_cloud_set_colors(pc,np.asarray(obj.colors).T,('r','g','b'),'rgb')
#TODO: other properties
return pc
elif isinstance(obj,open3d.geometry.TriangleMesh):
import numpy as np
m = TriangleMesh()
m.setVertices(np.asarray(obj.vertices))
m.setIndices(np.asarray(obj.triangles))
return m
elif isinstance(obj,open3d.geometry.VoxelGrid):
grid = VolumeGrid()
import numpy as np
if hasattr(obj, 'voxels'):
# open3d <= 0.9.0
occupied = np.array(obj.voxels,dtype=np.int32)
else:
occupied = np.array([v.grid_index for v in obj.get_voxels()],dtype=np.int32)
imin = np.min(occupied,axis=0)
imax = np.max(occupied,axis=0)
assert imin.shape == (3,)
assert imax.shape == (3,)
bmin = obj.origin + (imin-1)*obj.voxel_size
bmax = obj.origin + (imax+2)*obj.voxel_size
grid.bbox.append(bmin[0])
grid.bbox.append(bmin[1])
grid.bbox.append(bmin[2])
grid.bbox.append(bmax[0])
grid.bbox.append(bmax[1])
grid.bbox.append(bmax[2])
grid.dims.append(imax[0]-imin[0]+3)
grid.dims.append(imax[1]-imin[1]+3)
grid.dims.append(imax[2]-imin[2]+3)
grid.setValues(occupied.astype(float))
return grid
raise TypeError("Invalid type")