def project_pts_to_image(self, pointcloud: LidarPointCloud, token: str) -> np.ndarray: """ Project lidar points into image. :param pointcloud: The LidarPointCloud in nuScenes lidar frame. :param token: Unique KITTI token. :return: <np.float: N, 3.> X, Y are points in image pixel coordinates. Z is depth in image. """ # Copy and convert pointcloud. pc_image = LidarPointCloud(points=pointcloud.points.copy()) pc_image.rotate(self.kitti_to_nu_lidar_inv) # Rotate to KITTI lidar. # Transform pointcloud to camera frame. transforms = self.get_transforms(token, root=self.root) pc_image.rotate(transforms['velo_to_cam']['R']) pc_image.translate(transforms['velo_to_cam']['T']) # Project to image. depth = pc_image.points[2, :] points_fov = view_points(pc_image.points[:3, :], transforms['p_combined'], normalize=True) points_fov[2, :] = depth return points_fov
def get_gt(nusc, corners, camera_token, pointsensor_token): cam = nusc.get('sample_data', camera_token) pointsensor = nusc.get('sample_data', pointsensor_token) pcl_path = os.path.join(nusc.dataroot, pointsensor['filename']) # im = Image.open(os.path.join(nusc.dataroot, cam['filename'])) pc = LidarPointCloud(corners) # Points live in the point sensor frame. So they need to be transformed via global to the image plane. # First step: transform the point-cloud to the ego vehicle frame for the timestamp of the sweep. cs_record = nusc.get('calibrated_sensor', pointsensor['calibrated_sensor_token']) pc.rotate(Quaternion(cs_record['rotation']).rotation_matrix) pc.translate(np.array(cs_record['translation'])) # Second step: transform to the global frame. poserecord = nusc.get('ego_pose', pointsensor['ego_pose_token']) pc.rotate(Quaternion(poserecord['rotation']).rotation_matrix) pc.translate(np.array(poserecord['translation'])) # Third step: transform into the ego vehicle frame for the timestamp of the image. poserecord = nusc.get('ego_pose', cam['ego_pose_token']) pc.translate(-np.array(poserecord['translation'])) pc.rotate(Quaternion(poserecord['rotation']).rotation_matrix.T) # Fourth step: transform into the camera. cs_record = nusc.get('calibrated_sensor', cam['calibrated_sensor_token']) pc.translate(-np.array(cs_record['translation'])) pc.rotate(Quaternion(cs_record['rotation']).rotation_matrix.T) # Fifth step: actually take a "picture" of the point cloud. # Grab the depths (camera frame z axis points away from the camera). depths = pc.points[2, :] # Retrieve the color from the depth. coloring = depths # Take the actual picture (matrix multiplication with camera-matrix + renormalization). points = view_points(pc.points[:3, :], np.array(cs_record['camera_intrinsic']), normalize=True) # Remove points that are either outside or behind the camera. Leave a margin of 1 pixel for aesthetic reasons. # Also make sure points are at least 1m in front of the camera to avoid seeing the lidar points on the camera # casing for non-keyframes which are slightly out of sync. # mask = np.ones(depths.shape[0], dtype=bool) # mask = np.logical_and(mask, depths > min_dist) # mask = np.logical_and(mask, points[0, :] > 1) # mask = np.logical_and(mask, points[0, :] < im.size[0] - 1) # mask = np.logical_and(mask, points[1, :] > 1) # mask = np.logical_and(mask, points[1, :] < im.size[1] - 1) # points = points[:, mask] # coloring = coloring[mask] return points, coloring, im