def render_cv2(self, im: np.ndarray, view: np.ndarray = np.eye(3), normalize: bool = False, colors: Tuple = ((0, 0, 255), (255, 0, 0), (155, 155, 155)), linewidth: int = 2) -> None: """ Renders box using OpenCV2. :param im: <np.array: width, height, 3>. Image array. Channels are in BGR order. :param view: <np.array: 3, 3>. Define a projection if needed (e.g. for drawing projection in an image). :param normalize: Whether to normalize the remaining coordinate. :param colors: ((R, G, B), (R, G, B), (R, G, B)). Colors for front, side & rear. :param linewidth: Linewidth for plot. """ corners = view_points(self.corners(), view, normalize=normalize)[:2, :] def draw_rect(selected_corners, color): prev = selected_corners[-1] for corner in selected_corners: cv2.line(im, (int(prev[0]), int(prev[1])), (int(corner[0]), int(corner[1])), color, linewidth) prev = corner # Draw the sides for i in range(4): cv2.line(im, (int(corners.T[i][0]), int(corners.T[i][1])), (int(corners.T[i + 4][0]), int(corners.T[i + 4][1])), colors[2][::-1], linewidth) # Draw front (first 4 corners) and rear (last 4 corners) rectangles(3d)/lines(2d) draw_rect(corners.T[:4], colors[0][::-1]) draw_rect(corners.T[4:], colors[1][::-1]) # Draw line indicating the front center_bottom_forward = np.mean(corners.T[2:4], axis=0) center_bottom = np.mean(corners.T[[2, 3, 7, 6]], axis=0) cv2.line(im, (int(center_bottom[0]), int(center_bottom[1])), (int(center_bottom_forward[0]), int(center_bottom_forward[1])), colors[0][::-1], linewidth)
def _render_helper(self, color_channel: int, ax: Axes, view: np.ndarray, x_lim: Tuple, y_lim: Tuple, marker_size: float) -> None: """ Helper function for rendering. :param color_channel: Point channel to use as color. :param ax: Axes on which to render the points. :param view: <np.float: n, n>. Defines an arbitrary projection (n <= 4). :param x_lim: (min <float>, max <float>). :param y_lim: (min <float>, max <float>). :param marker_size: Marker size. """ points = view_points(self.points[:3, :], view, normalize=False) ax.scatter(points[0, :], points[1, :], c=self.points[color_channel, :], s=marker_size) ax.set_xlim(x_lim[0], x_lim[1]) ax.set_ylim(y_lim[0], y_lim[1])
def render(self, axis: Axes, view: np.ndarray = np.eye(3), normalize: bool = False, colors: Tuple = ('b', 'r', 'k'), linewidth: float = 2): """ Renders the box in the provided Matplotlib axis. :param axis: Axis onto which the box should be drawn. :param view: <np.array: 3, 3>. Define a projection in needed (e.g. for drawing projection in an image). :param normalize: Whether to normalize the remaining coordinate. :param colors: (<Matplotlib.colors>: 3). Valid Matplotlib colors (<str> or normalized RGB tuple) for front, back and sides. :param linewidth: Width in pixel of the box sides. """ corners = view_points(self.corners(), view, normalize=normalize)[:2, :] def draw_rect(selected_corners, color): prev = selected_corners[-1] for corner in selected_corners: axis.plot([prev[0], corner[0]], [prev[1], corner[1]], color=color, linewidth=linewidth) prev = corner # Draw the sides for i in range(4): axis.plot([corners.T[i][0], corners.T[i + 4][0]], [corners.T[i][1], corners.T[i + 4][1]], color=colors[2], linewidth=linewidth) # Draw front (first 4 corners) and rear (last 4 corners) rectangles(3d)/lines(2d) draw_rect(corners.T[:4], colors[0]) draw_rect(corners.T[4:], colors[1]) # Draw line indicating the front center_bottom_forward = np.mean(corners.T[2:4], axis=0) center_bottom = np.mean(corners.T[[2, 3, 7, 6]], axis=0) axis.plot([center_bottom[0], center_bottom_forward[0]], [center_bottom[1], center_bottom_forward[1]], color=colors[0], linewidth=linewidth)
def visualize_sample(nusc: NuScenes, sample_token: str, gt_boxes: EvalBoxes, pred_boxes: EvalBoxes, nsweeps: int = 1, conf_th: float = 0.15, eval_range: float = 50, verbose: bool = True, savepath: str = None) -> None: """ Visualizes a sample from BEV with annotations and detection results. :param nusc: NuScenes object. :param sample_token: The nuScenes sample token. :param gt_boxes: Ground truth boxes grouped by sample. :param pred_boxes: Prediction grouped by sample. :param nsweeps: Number of sweeps used for lidar visualization. :param conf_th: The confidence threshold used to filter negatives. :param eval_range: Range in meters beyond which boxes are ignored. :param verbose: Whether to print to stdout. :param savepath: If given, saves the the rendering here instead of displaying. """ # Retrieve sensor & pose records. sample_rec = nusc.get('sample', sample_token) sd_record = nusc.get('sample_data', sample_rec['data']['LIDAR_TOP']) cs_record = nusc.get('calibrated_sensor', sd_record['calibrated_sensor_token']) pose_record = nusc.get('ego_pose', sd_record['ego_pose_token']) # Get boxes. boxes_gt_global = gt_boxes[sample_token] boxes_est_global = pred_boxes[sample_token] # Map GT boxes to lidar. boxes_gt = boxes_to_sensor(boxes_gt_global, pose_record, cs_record) # Map EST boxes to lidar. boxes_est = boxes_to_sensor(boxes_est_global, pose_record, cs_record) # Add scores to EST boxes. for box_est, box_est_global in zip(boxes_est, boxes_est_global): box_est.score = box_est_global.detection_score # Get point cloud in lidar frame. pc, _ = LidarPointCloud.from_file_multisweep(nusc, sample_rec, 'LIDAR_TOP', 'LIDAR_TOP', nsweeps=nsweeps) # Init axes. _, ax = plt.subplots(1, 1, figsize=(9, 9)) # Show point cloud. points = view_points(pc.points[:3, :], np.eye(4), normalize=False) dists = np.sqrt(np.sum(pc.points[:2, :]**2, axis=0)) colors = np.minimum(1, dists / eval_range) ax.scatter(points[0, :], points[1, :], c=colors, s=0.2) # Show ego vehicle. ax.plot(0, 0, 'x', color='black') # Show GT boxes. for box in boxes_gt: box.render(ax, view=np.eye(4), colors=('g', 'g', 'g'), linewidth=2) # Show EST boxes. for box in boxes_est: # Show only predictions with a high score. assert not np.isnan(box.score), 'Error: Box score cannot be NaN!' if box.score >= conf_th: box.render(ax, view=np.eye(4), colors=('b', 'b', 'b'), linewidth=1) # Limit visible range. axes_limit = eval_range + 3 # Slightly bigger to include boxes that extend beyond the range. ax.set_xlim(-axes_limit, axes_limit) ax.set_ylim(-axes_limit, axes_limit) # Show / save plot. if verbose: print('Rendering sample token %s' % sample_token) plt.title(sample_token) if savepath is not None: plt.savefig(savepath) plt.close() else: plt.show()