| | import cv2 |
| | import numpy as np |
| | from PIL import Image |
| | import matplotlib.pyplot as plt |
| | from nuscenes.utils.data_classes import LidarPointCloud, Box |
| | from nuscenes.utils.geometry_utils import view_points, box_in_image, BoxVisibility, transform_matrix |
| | from scripts.analysis_tools.visualize.utils import color_mapping, AgentPredictionData |
| | from scripts.analysis_tools.visualize.render.base_render import BaseRender |
| | from pyquaternion import Quaternion |
| |
|
| | |
| | CAM_NAMES_nusc = [ |
| | 'CAM_FRONT_LEFT', |
| | 'CAM_FRONT', |
| | 'CAM_FRONT_RIGHT', |
| | 'CAM_BACK_RIGHT', |
| | 'CAM_BACK', |
| | 'CAM_BACK_LEFT', |
| | ] |
| | CAM_NAMES_carla = [ |
| | 'image_front', |
| | 'image_left', |
| | 'image_right', |
| | 'image_tele', |
| | ] |
| |
|
| | class CameraRender(BaseRender): |
| | """ |
| | Render class for Camera View |
| | """ |
| |
|
| | def __init__(self, |
| | figsize=(53.3333, 20), |
| | show_gt_boxes=False, |
| | dataset=None): |
| | super().__init__(figsize) |
| |
|
| | self.dataset = dataset |
| | if dataset == 'nusc': |
| | self.cams = CAM_NAMES_nusc |
| | elif dataset == 'carla': |
| | self.cams = CAM_NAMES_carla |
| |
|
| | self.show_gt_boxes = show_gt_boxes |
| |
|
| | def get_axis(self, index): |
| | """Retrieve the corresponding axis based on the index.""" |
| | return self.axes[index//3, index % 3] |
| |
|
| | def project_to_cam(self, |
| | agent_prediction_list, |
| | sample_data_token, |
| | nusc, |
| | lidar_cs_record, |
| | project_traj=False, |
| | cam=None, |
| | ): |
| | """Project predictions to camera view.""" |
| | _, cs_record, pose_record, cam_intrinsic, imsize = self.get_image_info( |
| | sample_data_token, nusc) |
| | boxes = [] |
| | |
| | for agent in agent_prediction_list: |
| | box = Box(agent.pred_center, agent.pred_dim, Quaternion(axis=(0.0, 0.0, 1.0), radians=agent.pred_yaw), |
| | name=agent.pred_label, token='predicted') |
| | box.is_sdc = agent.is_sdc |
| | if project_traj: |
| | box.pred_traj = np.zeros((agent.pred_traj_max.shape[0]+1, 3)) |
| | box.pred_traj[:, 0] = agent.pred_center[0] |
| | box.pred_traj[:, 1] = agent.pred_center[1] |
| | box.pred_traj[:, 2] = agent.pred_center[2] - \ |
| | agent.pred_dim[2]/2 |
| | box.pred_traj[1:, :2] += agent.pred_traj_max[:, :2] |
| | box.pred_traj = (Quaternion( |
| | lidar_cs_record['rotation']).rotation_matrix @ box.pred_traj.T).T |
| | box.pred_traj += np.array( |
| | lidar_cs_record['translation'])[None, :] |
| | box.rotate(Quaternion(lidar_cs_record['rotation'])) |
| | box.translate(np.array(lidar_cs_record['translation'])) |
| | boxes.append(box) |
| | |
| |
|
| | box_list = [] |
| | tr_id_list = [] |
| | for i, box in enumerate(boxes): |
| | |
| | box.translate(-np.array(cs_record['translation'])) |
| | box.rotate(Quaternion(cs_record['rotation']).inverse) |
| | if project_traj: |
| | box.pred_traj += -np.array(cs_record['translation'])[None, :] |
| | box.pred_traj = (Quaternion( |
| | cs_record['rotation']).inverse.rotation_matrix @ box.pred_traj.T).T |
| |
|
| | tr_id = agent_prediction_list[i].pred_track_id |
| | if box.is_sdc and cam == 'CAM_FRONT': |
| | box_list.append(box) |
| | if not box_in_image(box, cam_intrinsic, imsize): |
| | continue |
| | box_list.append(box) |
| | tr_id_list.append(tr_id) |
| | return box_list, tr_id_list, cam_intrinsic, imsize |
| |
|
| | def render_image_data(self, sample_token, nusc): |
| | """Load and annotate image based on the provided path.""" |
| | sample = nusc.get('sample', sample_token) |
| | for i, cam in enumerate(self.cams): |
| | sample_data_token = sample['data'][cam] |
| | data_path, _, _, _, _ = self.get_image_info( |
| | sample_data_token, nusc) |
| | image = self.load_image(data_path, cam) |
| | self.update_image(image, i, cam) |
| |
|
| | def render_image_data_carla(self, sample_token, version): |
| | """Load and annotate image based on the provided path.""" |
| | for i, cam in enumerate(self.cams): |
| | scene_token, frame_idstr = sample_token.split('_frame_') |
| | data_path = f'data/carla/{version}/val/{scene_token}/{cam}/{frame_idstr}.png' |
| | image = self.load_image(data_path, cam) |
| | self.update_image(image, i, cam) |
| |
|
| | def load_image(self, data_path, cam): |
| | """Update the axis of the plot with the provided image.""" |
| | image = np.array(Image.open(data_path)) |
| | font = cv2.FONT_HERSHEY_SIMPLEX |
| | org = (50, 60) |
| | fontScale = 2 |
| | color = (0, 0, 0) |
| | thickness = 4 |
| | return cv2.putText(image, cam, org, font, fontScale, color, thickness, cv2.LINE_AA) |
| |
|
| | def update_image(self, image, index, cam): |
| | """Render image data for each camera.""" |
| | ax = self.get_axis(index) |
| | ax.imshow(image) |
| | plt.axis('off') |
| | ax.axis('off') |
| | ax.grid(False) |
| |
|
| | def render_pred_track_bbox(self, predicted_agent_list, sample_token, nusc): |
| | """Render bounding box for predicted tracks.""" |
| | sample = nusc.get('sample', sample_token) |
| | lidar_cs_record = nusc.get('calibrated_sensor', nusc.get( |
| | 'sample_data', sample['data']['LIDAR_TOP'])['calibrated_sensor_token']) |
| | for i, cam in enumerate(self.cams): |
| | sample_data_token = sample['data'][cam] |
| | box_list, tr_id_list, camera_intrinsic, imsize = self.project_to_cam( |
| | predicted_agent_list, sample_data_token, nusc, lidar_cs_record) |
| | for j, box in enumerate(box_list): |
| | if box.is_sdc: |
| | continue |
| | tr_id = tr_id_list[j] |
| | if tr_id is None: |
| | tr_id = 0 |
| | c = color_mapping[tr_id % len(color_mapping)] |
| | box.render( |
| | self.axes[i//3, i % 3], view=camera_intrinsic, normalize=True, colors=(c, c, c)) |
| | |
| | if self.show_gt_boxes: |
| | data_path, boxes, camera_intrinsic = nusc.get_sample_data( |
| | sample_data_token, selected_anntokens=sample['anns']) |
| | for j, box in enumerate(boxes): |
| | c = [0, 1, 0] |
| | box.render( |
| | self.axes[i//3, i % 3], view=camera_intrinsic, normalize=True, colors=(c, c, c)) |
| | self.axes[i//3, i % 3].set_xlim(0, imsize[0]) |
| | self.axes[i//3, i % 3].set_ylim(imsize[1], 0) |
| |
|
| | def render_pred_traj(self, predicted_agent_list, sample_token, nusc, render_sdc=False, points_per_step=10): |
| | """Render predicted trajectories.""" |
| | sample = nusc.get('sample', sample_token) |
| | lidar_cs_record = nusc.get('calibrated_sensor', nusc.get( |
| | 'sample_data', sample['data']['LIDAR_TOP'])['calibrated_sensor_token']) |
| | for i, cam in enumerate(self.cams): |
| | sample_data_token = sample['data'][cam] |
| | box_list, tr_id_list, camera_intrinsic, imsize = self.project_to_cam( |
| | predicted_agent_list, sample_data_token, nusc, lidar_cs_record, project_traj=True, cam=cam) |
| | for j, box in enumerate(box_list): |
| | traj_points = box.pred_traj[:, :3] |
| |
|
| | total_steps = (len(traj_points)-1) * points_per_step + 1 |
| | total_xy = np.zeros((total_steps, 3)) |
| | for k in range(total_steps-1): |
| | unit_vec = traj_points[k//points_per_step + |
| | 1] - traj_points[k//points_per_step] |
| | total_xy[k] = (k/points_per_step - k//points_per_step) * \ |
| | unit_vec + traj_points[k//points_per_step] |
| | in_range_mask = total_xy[:, 2] > 0.1 |
| | traj_points = view_points( |
| | total_xy.T, camera_intrinsic, normalize=True)[:2, :] |
| | traj_points = traj_points[:2, in_range_mask] |
| | if box.is_sdc: |
| | if render_sdc: |
| | self.axes[i//3, i % 3].scatter( |
| | traj_points[0], traj_points[1], color=(1, 0.5, 0), s=150) |
| | else: |
| | continue |
| | else: |
| | tr_id = tr_id_list[j] |
| | if tr_id is None: |
| | tr_id = 0 |
| | c = color_mapping[tr_id % len(color_mapping)] |
| | self.axes[i//3, i % |
| | 3].scatter(traj_points[0], traj_points[1], color=c, s=15) |
| | self.axes[i//3, i % 3].set_xlim(0, imsize[0]) |
| | self.axes[i//3, i % 3].set_ylim(imsize[1], 0) |
| |
|
| | def get_image_info(self, sample_data_token, nusc): |
| | """Retrieve image information.""" |
| | sd_record = nusc.get('sample_data', sample_data_token) |
| | cs_record = nusc.get('calibrated_sensor', |
| | sd_record['calibrated_sensor_token']) |
| | sensor_record = nusc.get('sensor', cs_record['sensor_token']) |
| | pose_record = nusc.get('ego_pose', sd_record['ego_pose_token']) |
| |
|
| | data_path = nusc.get_sample_data_path(sample_data_token) |
| |
|
| | if sensor_record['modality'] == 'camera': |
| | cam_intrinsic = np.array(cs_record['camera_intrinsic']) |
| | imsize = (sd_record['width'], sd_record['height']) |
| | else: |
| | cam_intrinsic = None |
| | imsize = None |
| | return data_path, cs_record, pose_record, cam_intrinsic, imsize |
| |
|