Example #1
0
def from_PointCloud2(ros_pc):
    """From a ROS PointCloud2 to a Klampt PointCloud"""
    from klampt import PointCloud
    from sensor_msgs.msg import PointField
    from sensor_msgs import point_cloud2
    pc = PointCloud()
    if ros_pc.header.seq > 0:
        pc.setSetting("id", str(ros_pc.header.seq))
    if ros_pc.fields[0].name != 'x' or ros_pc.fields[
            1].name != 'y' or ros_pc.fields[2].name != 'z':
        raise ValueError("PointCloud2 message doesn't have x,y,z fields?")
    for i in range(3, len(ros_pc.fields)):
        pc.addProperty(ros_pc.fields[i].name)
    N = ros_pc.height * ros_pc.width
    plist = []
    proplist = []
    if ros_pc.height > 1:
        structured = True
        pc.settings.set("width", str(pc.width))
        pc.settings.set("height", str(pc.height))
    for p in point_cloud2.read_points(ros_pc):
        plist += [p[0], p[1], p[2]]
        proplist += [float(v) for v in p[3:]]
    pc.setPoints(len(plist) // 3, plist)
    pc.setProperties(proplist)
    return pc
Example #2
0
def from_open3d(obj):
    """Converts open3d geometry to a Klamp't geometry.
    """
    if isinstance(obj,open3d.geometry.PointCloud):
        pc = PointCloud()
        for p in obj.points:
            pc.vertices.append(p[0])
            pc.vertices.append(p[1])
            pc.vertices.append(p[2])
        if obj.has_colors():
            geometry.point_cloud_set_colors(pc,obj.colors,('r','g','b'),'rgb')
        #TODO: other properties
        return pc
    elif isinstance(obj,open3d.geometry.TriangleMesh):
        m = TriangleMesh()
        for p in obj.vertices:
            m.vertices.append(p[0])
            m.vertices.append(p[1])
            m.vertices.append(p[2])
        for i in obj.triangles:
            m.indices.append(int(i[0]))
            m.indices.append(int(i[1]))
            m.indices.append(int(i[2]))
        return m
    elif isinstance(obj,open3d.geometry.VoxelGrid):
        grid = VolumeGrid()
        import numpy as np
        occupied = np.array(obj.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.values.resize(grid.dims[0]*grid.dims[1]*grid.dims[2],-1.0)
        for cell in occupied:
            grid.set(int(cell[0]-imin[0]+1),int(cell[1]-imin[1]+1),int(cell[2]-imin[2]+1),1.0)
        return grid
    raise TypeError("Invalid type")
Example #3
0
def from_numpy(obj, type='auto', template=None):
    """Converts a numpy array or multiple numpy arrays to a Klamp't object.

    Supports:

    * lists and tuples
    * RigidTransform: accepts a 4x4 homogeneous coordinate transform
    * Matrix3, Rotation: accepts a 3x3 matrix.
    * Configs
    * Trajectory: accepts a pair (times,milestones)
    * TriangleMesh: accepts a pair (verts,indices)
    * PointCloud: accepts a n x (3+k) array, where k is the # of properties
    * VolumeGrid: accepts a triple (bmin,bmax,array)
    * Geometry3D: accepts a pair (T,geomdata)
    """
    global supportedTypes
    if type == 'auto' and template is not None:
        otype = types.objectToTypes(template)
        if isinstance(otype, (list, tuple)):
            for t in otype:
                if t in supportedTypes:
                    type = t
                    break
            if type == 'auto':
                raise ValueError('obj is not a supported type: ' +
                                 ', '.join(otype))
        else:
            type = otype
    if type == 'auto':
        if isinstance(obj, (tuple, list)):
            if all(isinstance(v, np.ndarray) for v in obj):
                if len(obj) == 2:
                    if len(obj[0].shape) == 1 and len(obj[1].shape) == 2:
                        type = 'Trajectory'
                    elif len(obj[0].shape) == 2 and len(
                            obj[1].shape
                    ) == 2 and obj[0].shape[1] == 3 and obj[1].shape[1] == 3:
                        type = 'TriangleMesh'
                if len(obj) == 3:
                    if obj[0].shape == (3, ) and obj[1].shape == (3, ):
                        type = 'VolumeGrid'
                if type == 'auto':
                    raise ValueError(
                        "Can't auto-detect type of list of shapes" +
                        ', '.join(str(v.shape) for v in obj))
            else:
                if isinstance(obj[0], np.ndarray) and obj[0].shape == (4, 4):
                    type = 'Geometry3D'
                else:
                    raise ValueError(
                        "Can't auto-detect type of irregular list")
        else:
            assert isinstance(
                obj, np.ndarray
            ), "Can only convert lists, tuples, and arrays from numpy"
            if obj.shape == (3, 3):
                type = 'Matrix3'
            elif obj.shape == (4, 4):
                type = 'RigidTransform'
            elif len(obj.shape) == 1:
                type = 'Config'
            else:
                raise ValueError("Can't auto-detect type of matrix of shape " +
                                 str(obj.shape))
    if type not in supportedTypes:
        raise ValueError(type + ' is not a supported type')
    if type == 'RigidTransform':
        return se3.from_homogeneous(obj)
    elif type == 'Rotation' or type == 'Matrix3':
        return so3.from_matrix(obj)
    elif type == 'Trajectory':
        assert len(obj) == 2, "Trajectory format is (times,milestones)"
        times = obj[0].tolist()
        milestones = obj[1].tolist()
        if template is not None:
            return template.constructor()(times, milestones)
        from klampt.model.trajectory import Trajectory
        return Trajectory(times, milestones)
    elif type == 'TriangleMesh':
        from klampt import TriangleMesh
        res = TriangleMesh()
        vflat = obj[0].flatten()
        res.vertices.resize(len(vflat))
        for i, v in enumerate(vflat):
            res.vertices[i] = float(v)
        iflat = obj[1].flatten()
        res.indices.resize(len(iflat))
        for i, v in enumerate(iflat):
            res.indices[i] = int(v)
        return res
    elif type == 'PointCloud':
        from klampt import PointCloud
        assert len(obj.shape) == 2, "PointCloud array must be a 2D array"
        assert obj.shape[1] >= 3, "PointCloud array must have at least 3 values"
        points = obj[:, :3]
        properties = obj[:, 3:]
        res = PointCloud()
        res.setPoints(points.shape[0], points.flatten())
        if template is not None:
            if len(template.propertyNames) != properties.shape[1]:
                raise ValueError(
                    "Template object doesn't have the same properties as the numpy object"
                )
            for i in range(len(template.propertyNames)):
                res.propertyNames[i] = template.propertyNames[i]
        else:
            for i in range(properties.shape[1]):
                res.propertyNames.append('property %d' % (i + 1))
        if len(res.propertyNames) > 0:
            res.properties.resize(len(res.propertyNames) * points.shape[0])
        if obj.shape[1] >= 3:
            res.setProperties(properties.flatten())
        return res
    elif type == 'VolumeGrid':
        from klampt import VolumeGrid
        assert len(obj) == 3, "VolumeGrid format is (bmin,bmax,values)"
        assert len(obj[2].shape) == 3, "VolumeGrid values must be a 3D array"
        bmin = obj[0]
        bmax = obj[1]
        values = obj[2]
        res = VolumeGrid()
        res.bbox.append(bmin[0])
        res.bbox.append(bmin[1])
        res.bbox.append(bmin[2])
        res.bbox.append(bmax[0])
        res.bbox.append(bmax[1])
        res.bbox.append(bmax[2])
        res.dims.append(values.shape[0])
        res.dims.append(values.shape[1])
        res.dims.append(values.shape[2])
        vflat = values.flatten()
        res.values.resize(len(vflat))
        for i, v in enumerate(vflat):
            res.values[i] = v
        return res
    elif type == 'Group':
        from klampt import Geometry3D
        res = Geometry3D()
        assert isinstance(
            obj,
            (list, tuple)), "Group format is a list or tuple of Geometry3D's"
        for i in range(len(obj)):
            res.setElement(i, from_numpy(obj[i], 'Geometry3D'))
        return res
    elif type == 'Geometry3D':
        from klampt import Geometry3D
        if not isinstance(obj, (list, tuple)) or len(obj) != 2:
            raise ValueError("Geometry3D must be a (transform,geometry) tuple")
        T = from_numpy(obj[0], 'RigidTransform')
        geomdata = obj[1]
        subtype = None
        if template is not None:
            subtype = template.type()
            if subtype == 'PointCloud':
                g = Geometry3D(
                    from_numpy(geomdata, subtype, template.getPointCloud()))
            else:
                g = Geometry3D(from_numpy(geomdata, subtype))
            g.setCurrentTransform(*T)
            return g
        subtype = 'Group'
        if all(isinstance(v, np.ndarray) for v in geomdata):
            if len(geomdata) == 2:
                if len(geomdata[0].shape) == 1 and len(geomdata[1].shape) == 2:
                    subtype = 'Trajectory'
                elif len(geomdata[0].shape) == 2 and len(
                        geomdata[1].shape) == 2 and geomdata[0].shape[
                            1] == 3 and geomdata[1].shape[1] == 3:
                    subtype = 'TriangleMesh'
            if len(geomdata) == 3:
                if geomdata[0].shape == (3, ) and geomdata[1].shape == (3, ):
                    subtype = 'VolumeGrid'
        g = Geometry3D(from_numpy(obj, subtype))
        g.setCurrentTransform(*T)
        return g
    else:
        return obj.flatten()
Example #4
0
        open3d.io.write_point_cloud("testPcd.pcd", open3dPcd)
    else:
        for ele in pcd_cut:
            xyz.append(ele[0:3])
            rgb.append(ele[3:6])
        open3dPcd.points = open3d.utility.Vector3dVector(
            np.asarray(xyz, dtype=np.float32))
        open3dPcd.colors = open3d.utility.Vector3dVector(
            np.asarray(rgb, dtype=np.float32))
        open3d.io.write_point_cloud("testPcd.pcd", open3dPcd)

    pcdK = loader.loadGeometry3D("testPcd.pcd")
    klampt_pcd = pcdK.getPointCloud()
    properties = np.array(klampt_pcd.properties).reshape(
        (klampt_pcd.numPoints(), klampt_pcd.numProperties()))
    pcd_new = PointCloud()
    rs = []
    gs = []
    bs = []
    for a in properties:
        position = a[0:3]
        pcd_new.addPoint(position)
        rgb = a[3]
        (r, g, b) = rgb_int2tuple(rgb)
        rs.append(r)
        gs.append(g)
        bs.append(b)

    pcd_new.addProperty('r', rs)
    pcd_new.addProperty('g', gs)
    pcd_new.addProperty('b', bs)
        for idz in range(grid.dims[2]):
            d=[(idx-grid.dims[0]/2)/float(grid.dims[0]/2),  \
               (idy-grid.dims[1]/2)/float(grid.dims[1]/2),  \
               (idz-grid.dims[2]/2)/float(grid.dims[2]/2)]
            freq = 2.
            amp = .1
            thres = .5 * (1. + math.sin(d[0] * math.pi * freq) * math.sin(
                d[1] * math.pi * freq) * math.sin(d[2] * math.pi * freq) * amp)
            grid.set(idx, idy, idz, op.norm(d) / op.norm([1., 1., 1.]) - thres)
sdf.geometry().setVolumeGrid(grid)
sdf.appearance().setColor(random.uniform(0., 1.), random.uniform(0., 1.),
                          random.uniform(0., 1.))

#example PointCloud
points = world.makeTerrain("PointCloud")
cloud = PointCloud()
for i in range(1000):
    cloud.addPoint([
        random.uniform(0., 0.1),
        random.uniform(0., 0.1),
        random.uniform(0., 0.1)
    ])
points.geometry().setPointCloud(cloud)
points.geometry().transform([1, 0, 0, 0, 1, 0, 0, 0, 1], [0., 1.2, 0.])
points.appearance().setColor(random.uniform(0., 1.), random.uniform(0., 1.),
                             random.uniform(0., 1.))

#robot
robot = world.loadRobot("../../data/robots/tx90robotiq.rob")
for l in range(robot.numLinks()):
    lk = robot.link(l)
Example #6
0
    def get_point_cloud(self,
                        camera=None,
                        colors=True,
                        normals=True,
                        structured=False,
                        near_filter=None,
                        far_filter=None,
                        format='numpy'):
        """Converts an RGBD scan to a point cloud in camera coordinates.
        
        Points whose Z value is less than near_filter or greater than far_filter
        will be excluded.
        
        if format=='numpy':
            If colors=True and normals=True, the result is an N x 9 Numpy array, giving
            colors and normals.  I.e, the matrix has columns X Y Z R G B Nx Ny Nz. 
            R, G, B are in the range [0,1]
            
            If colors=True and normals=False, only the first 6 columns are returned.
            If colors=False and normals=True, only the first 3 and last 3 columns are returned.
            If colors=False and normals=False, only the X,Y,Z columns are returned.
        if format == 'PointCloud':
            A Klampt PointCloud is returned.

        If structured=True, all points are returned, even ones with missing depth
        and those outside of the near/far filter.  Their rows will be zero 
        """
        if camera is None:
            camera = self.camera
        if camera is None or camera.depth_intrinsics is None:
            raise ValueError("Need a camera's intrinsics to be defined")
        if self.depth is None:
            raise ValueError("Depth is not defined for this scan")
        if colors and self.rgb is None:
            print(
                "Warning, requested colors but rgb information is not defined for this scan"
            )
            colors = False
        if colors and normals:
            channels = range(9)
        elif colors:
            channels = range(6)
        elif normals:
            channels = list(range(3)) + list(range(6, 9))
        else:
            channels = range(3)
        if near_filter is None:
            near_filter = camera.depth_minimum
        else:
            near_filter = max(near_filter, camera.depth_minimum)
        if far_filter is None:
            far_filter = camera.depth_maximum
        else:
            far_filter = min(far_filter, camera.depth_maximum)
        fx, fy = camera.depth_intrinsics['fx'], camera.depth_intrinsics['fy']
        cx, cy = camera.depth_intrinsics['cx'], camera.depth_intrinsics['cy']
        depth_scale = camera.depth_image_scale
        h, w = self.depth.shape[0], self.depth.shape[1]
        Z = self.depth * (1.0 / depth_scale)
        X, Y = np.meshgrid(np.array(range(w)) - cx, np.array(range(h)) - cy)
        X *= Z * (1.0 / fx)
        Y *= Z * (1.0 / fy)
        points = np.stack((X, Y, Z), -1).reshape(w * h, 3)
        #compute colors
        if colors:
            if camera.T_color_to_depth is not None:
                raise NotImplementedError(
                    "TODO: map from color to depth frames")
            colors = (self.rgb / 255.0).reshape(w * h, 3)
            have_colors = True
        else:
            colors = np.empty((w * h, 3))
            have_colors = False
        if normals:
            #compute normals from image
            #What's all this fuss? getting good normals in the presence of missing depth readings
            gxc = (Z[:, 2:] - Z[:, :-2]) / (X[:, 2:] - X[:, :-2])
            gxn = (Z[:, 1:] - Z[:, :-1]) / (X[:, 1:] - X[:, :-1])
            gyc = (Z[2:, :] - Z[:-2, :]) / (Y[2:, :] - Y[:-2, :])
            gyn = (Z[1:, :] - Z[:-1, :]) / (Y[1:, :] - Y[:-1, :])
            nancol = np.full((h, 1), np.nan)
            gxc = np.hstack((nancol, gxc, nancol))
            gxp = np.hstack((nancol, gxn))
            gxn = np.hstack((gxn, nancol))
            gx = np.where(
                np.isnan(gxc),
                np.where(np.isnan(gxn), np.where(np.isnan(gxp), 0, gxp), gxn),
                gxc)
            nanrow = np.full((1, w), np.nan)
            gyc = np.vstack((nanrow, gyc, nanrow))
            gyp = np.vstack((nanrow, gyn))
            gyn = np.vstack((gyn, nanrow))
            gy = np.where(
                np.isnan(gyc),
                np.where(np.isnan(gyn), np.where(np.isnan(gyp), 0, gyp), gyn),
                gyc)
            normals = np.stack((gx, gy, -np.ones((h, w))),
                               -1).reshape(w * h, 3)
            np.nan_to_num(normals, copy=False)
            normals = normals / np.linalg.norm(normals, axis=1)[:, np.newaxis]
            np.nan_to_num(normals, copy=False)
            have_normals = True
        else:
            normals = np.empty((w * h, 3))
            have_normals = False
        #join them all up
        points = np.hstack((points, colors, normals))
        #depth filtering
        indices = np.logical_and(points[:, 2] >= near_filter,
                                 points[:, 2] <= far_filter)
        #print(np.sum(indices),"Points pass the depth test")
        #print("Depth range",points[:,2].min(),points[:,2].max())
        if structured:
            res = points[:, channels]
            res[~indices, :] = 0
        else:
            res = points[indices.nonzero()[0]][:, channels]
        if format == 'PointCloud':
            from klampt import PointCloud
            pc = PointCloud()
            pc.setPoints(res.shape[0], res[:, :3].flatten())
            pstart = 0
            if have_colors:
                pc.addProperty('r')
                pc.addProperty('g')
                pc.addProperty('b')
                pc.setProperties(0, res[:, 3])
                pc.setProperties(1, res[:, 4])
                pc.setProperties(2, res[:, 5])
                pstart = 3
            if have_normals:
                pc.addProperty('normal_x')
                pc.addProperty('normal_y')
                pc.addProperty('normal_z')
                pc.setProperties(0 + pstart, res[:, 6])
                pc.setProperties(1 + pstart, res[:, 7])
                pc.setProperties(2 + pstart, res[:, 8])
            return pc
        return res