Beispiel #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
Beispiel #2
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