def generate_vehicle_bev(dataset_dir="", log_id="", output_dir=""): argoverse_loader = ArgoverseTrackingLoader(dataset_dir) argoverse_data = argoverse_loader.get(log_id) camera = argoverse_loader.CAMERA_LIST[7] calib = argoverse_data.get_calibration(camera) sdb = SynchronizationDB(dataset_dir, collect_single_log_id=log_id) calib_path = f"{dataset_dir}/{log_id}/vehicle_calibration_info.json" calib_data = read_json_file(calib_path) ind = 0 for i in range(9): if calib_data['camera_data_'][i]['key'] == \ 'image_raw_stereo_front_left': ind = i break rotation = np.array(calib_data['camera_data_'][ind]['value'] ['vehicle_SE3_camera_']['rotation']['coefficients']) translation = np.array(calib_data['camera_data_'][ind]['value'] ['vehicle_SE3_camera_']['translation']) egovehicle_SE3_cam = SE3(rotation=quat2rotmat(rotation), translation=translation) if not os.path.exists(os.path.join(output_dir, log_id, "car_bev_gt")): os.makedirs(os.path.join(output_dir, log_id, "car_bev_gt")) lidar_dir = os.path.join(dataset_dir, log_id, "lidar") ply_list = os.listdir(lidar_dir) pfa = PerFrameLabelAccumulator(dataset_dir, dataset_dir, "argoverse_bev_viz", save=False, bboxes_3d=True) pfa.accumulate_per_log_data(log_id) log_timestamp_dict = pfa.log_timestamp_dict for i, ply_name in enumerate(ply_list): lidar_timestamp = ply_name.split('.')[0].split('_')[1] lidar_timestamp = int(lidar_timestamp) cam_timestamp = sdb.get_closest_cam_channel_timestamp( lidar_timestamp, "stereo_front_left", str(log_id)) image_path = os.path.join( output_dir, str(log_id), "car_bev_gt", "stereo_front_left_" + str(cam_timestamp) + ".jpg") objects = log_timestamp_dict[log_id][lidar_timestamp] top_view = np.zeros((256, 256)) all_occluded = True for frame_rec in objects: if frame_rec.occlusion_val != IS_OCCLUDED_FLAG: all_occluded = False if not all_occluded: for i, frame_rec in enumerate(objects): bbox_ego_frame = frame_rec.bbox_ego_frame uv = calib.project_ego_to_image(bbox_ego_frame).T idx_ = np.all( np.logical_and( np.logical_and( np.logical_and(uv[0, :] >= 0.0, uv[0, :] < size[1] - 1.0), np.logical_and(uv[1, :] >= 0.0, uv[1, :] < size[0] - 1.0)), uv[2, :] > 0)) if not idx_: continue bbox_cam_fr = egovehicle_SE3_cam.inverse().\ transform_point_cloud(bbox_ego_frame) X = bbox_cam_fr[:, 0] Z = bbox_cam_fr[:, 2] if (frame_rec.occlusion_val != IS_OCCLUDED_FLAG and frame_rec.obj_class_str == "VEHICLE"): y_img = (-Z / res).astype(np.int32) x_img = (X / res).astype(np.int32) x_img -= int(np.floor(-20 / res)) y_img += int(np.floor(40 / res)) box = np.array([x_img[2], y_img[2]]) box = np.vstack((box, [x_img[6], y_img[6]])) box = np.vstack((box, [x_img[7], y_img[7]])) box = np.vstack((box, [x_img[3], y_img[3]])) cv2.drawContours(top_view, [box], 0, 255, -1) cv2.imwrite(image_path, top_view)
def generate_road_bev(dataset_dir="", log_id="", output_dir=""): argoverse_loader = ArgoverseTrackingLoader(dataset_dir) argoverse_data = argoverse_loader.get(log_id) city_name = argoverse_data.city_name avm = ArgoverseMap() sdb = SynchronizationDB(dataset_dir, collect_single_log_id=log_id) try: path = os.path.join(output_dir, log_id, 'road_gt') command = "rm -r " + path os.system(command) except BaseException: pass try: os.makedirs(os.path.join(output_dir, log_id, 'road_gt')) except BaseException: pass ply_fpath = os.path.join(dataset_dir, log_id, 'lidar') ply_locs = [] for idx, ply_name in enumerate(os.listdir(ply_fpath)): ply_loc = np.array([idx, int(ply_name.split('.')[0].split('_')[-1])]) ply_locs.append(ply_loc) ply_locs = np.array(ply_locs) lidar_timestamps = sorted(ply_locs[:, 1]) calib_path = f"{dataset_dir}/{log_id}/vehicle_calibration_info.json" calib_data = read_json_file(calib_path) ind = 0 for i in range(9): if calib_data['camera_data_'][i]['key'] ==\ 'image_raw_stereo_front_left': ind = i break rotation = np.array(calib_data['camera_data_'][ind]['value'] ['vehicle_SE3_camera_']['rotation']['coefficients']) translation = np.array(calib_data['camera_data_'][ind]['value'] ['vehicle_SE3_camera_']['translation']) egovehicle_SE3_cam = SE3(rotation=quat2rotmat(rotation), translation=translation) for idx in range(len(lidar_timestamps)): lidar_timestamp = lidar_timestamps[idx] cam_timestamp = sdb.get_closest_cam_channel_timestamp( lidar_timestamp, "stereo_front_left", str(log_id)) occupancy_map = np.zeros((256, 256)) pose_fpath = os.path.join( dataset_dir, log_id, "poses", "city_SE3_egovehicle_" + str(lidar_timestamp) + ".json") if not Path(pose_fpath).exists(): continue pose_data = read_json_file(pose_fpath) rotation = np.array(pose_data["rotation"]) translation = np.array(pose_data["translation"]) xcenter = translation[0] ycenter = translation[1] city_to_egovehicle_se3 = SE3(rotation=quat2rotmat(rotation), translation=translation) ego_car_nearby_lane_ids = avm.get_lane_ids_in_xy_bbox( xcenter, ycenter, city_name, 50.0) occupancy_map = get_lane_bev(ego_car_nearby_lane_ids, avm, city_name, city_to_egovehicle_se3, egovehicle_SE3_cam, occupancy_map, res, 255) output_loc = os.path.join( output_dir, log_id, 'road_gt', 'stereo_front_left_' + str(cam_timestamp) + '.png') cv2.imwrite(output_loc, occupancy_map)
class SimpleArgoverseTrackingDataLoader: """ Simple abstraction for retrieving log data, given a path to the dataset. """ def __init__(self, data_dir: str, labels_dir: str) -> None: """ Args: data_dir: str, representing path to raw Argoverse data labels_dir: strrepresenting path to Argoverse data labels """ self.data_dir = data_dir self.labels_dir = labels_dir self.sdb = SynchronizationDB(data_dir) def get_city_name(self, log_id: str) -> str: """ Args: log_id: str Returns: city_name: str """ city_info_fpath = f"{self.data_dir}/{log_id}/city_info.json" city_info = read_json_file(city_info_fpath) city_name = city_info["city_name"] assert isinstance(city_name, str) return city_name def get_log_calibration_data(self, log_id: str) -> Mapping[str, Any]: """ Args: log_id: str Returns: log_calib_data: dictionary """ calib_fpath = f"{self.data_dir}/{log_id}/vehicle_calibration_info.json" log_calib_data = read_json_file(calib_fpath) assert isinstance(log_calib_data, dict) return log_calib_data def get_city_to_egovehicle_se3(self, log_id: str, timestamp: int) -> Optional[SE3]: """ Args: log_id: str, unique ID of vehicle log timestamp: int, timestamp of sensor observation, in nanoseconds Returns: city_to_egovehicle_se3: SE3 transformation to bring egovehicle frame point into city frame. """ pose_fpath = f"{self.data_dir}/{log_id}/poses/city_SE3_egovehicle_{timestamp}.json" if not Path(pose_fpath).exists(): return None pose_data = read_json_file(pose_fpath) rotation = np.array(pose_data["rotation"]) translation = np.array(pose_data["translation"]) city_to_egovehicle_se3 = SE3(rotation=quat2rotmat(rotation), translation=translation) return city_to_egovehicle_se3 def get_closest_im_fpath(self, log_id: str, camera_name: str, lidar_timestamp: int) -> Optional[str]: """ Args: log_id: str, unique ID of vehicle log camera_name: str lidar_timestamp: int, timestamp of LiDAR sweep capture, in nanoseconds Returns: im_fpath, string representing path to image, or else None. """ cam_timestamp = self.sdb.get_closest_cam_channel_timestamp( lidar_timestamp, camera_name, log_id) if cam_timestamp is None: return None im_dir = f"{self.data_dir}/{log_id}/{camera_name}" im_fname = f"{camera_name}_{cam_timestamp}.jpg" im_fpath = f"{im_dir}/{im_fname}" return im_fpath def get_closest_lidar_fpath(self, log_id: str, cam_timestamp: int) -> Optional[str]: """ Args: log_id: str, unique ID of vehicle log cam_timestamp: int, timestamp of image capture, in nanoseconds Returns: ply_fpath: str, string representing path to PLY file, or else None. """ lidar_timestamp = self.sdb.get_closest_lidar_timestamp( cam_timestamp, log_id) if lidar_timestamp is None: return None lidar_dir = f"{self.data_dir}/{log_id}/lidar" ply_fname = f"PC_{lidar_timestamp}.ply" ply_fpath = f"{lidar_dir}/{ply_fname}" return ply_fpath def get_ordered_log_ply_fpaths(self, log_id: str) -> List[str]: """ Args: log_id: str, unique ID of vehicle log Returns: ply_fpaths: List of strings, representing paths to ply files in this log """ ply_fpaths = sorted( glob.glob(f"{self.data_dir}/{log_id}/lidar/PC_*.ply")) return ply_fpaths def get_ordered_log_cam_fpaths(self, log_id: str, camera_name: str) -> List[str]: """ Args log_id: str, unique ID of vehicle log Returns cam_img_fpaths: List of strings, representing paths to JPEG files in this log, for a specific camera """ cam_img_fpaths = sorted( glob.glob( f"{self.data_dir}/{log_id}/{camera_name}/{camera_name}_*.jpg")) return cam_img_fpaths def get_labels_at_lidar_timestamp( self, log_id: str, lidar_timestamp: int) -> Optional[List[Mapping[str, Any]]]: """ Args: log_id: str, unique ID of vehicle log lidar_timestamp: int, timestamp of LiDAR sweep capture, in nanoseconds Returns: labels: dictionary """ timestamp_track_label_fpath = ( f"{self.labels_dir}/{log_id}/per_sweep_annotations_amodal/tracked_object_labels_{lidar_timestamp}.json" ) if not Path(timestamp_track_label_fpath).exists(): return None labels = read_json_file(timestamp_track_label_fpath) assert isinstance(labels, list), labels return labels
def draw_ground_pts_in_image( sdb: SynchronizationDB, lidar_points: np.ndarray, city_to_egovehicle_se3: SE3, dataset_map: ArgoverseMap, log_id: str, lidar_timestamp: int, city_name: str, dataset_dir: str, experiment_prefix: str, plot_ground: bool = True, motion_compensate: bool = False, camera: Optional[str] = None, ) -> Union[None, np.ndarray]: """Write an image to disk with rendered ground points for every camera. Args: sdb: instance of SynchronizationDB lidar_points: Numpy array of shape (N,3) in egovehicle frame city_to_egovehicle_se3: SE3 instance which takes a point in egovehicle frame and brings it into city frame dataset_map: Map dataset instance log_id: ID of the log city_name: A city's name (e.g. 'MIA' or 'PIT') motion_compensate: Whether to bring lidar points from world frame @ lidar timestamp, to world frame @ camera timestamp camera: camera name, if specified will return image of that specific camera, if None, will save all camera to disk and return None """ # put into city coords, then prune away ground and non-RoI points lidar_points = city_to_egovehicle_se3.transform_point_cloud(lidar_points) lidar_points = dataset_map.remove_non_driveable_area_points( lidar_points, city_name) _, not_ground_logicals = dataset_map.remove_ground_surface( copy.deepcopy(lidar_points), city_name, return_logicals=True) lidar_points = lidar_points[np.logical_not(not_ground_logicals) if plot_ground else not_ground_logicals] # put back into ego-vehicle coords lidar_points = city_to_egovehicle_se3.inverse_transform_point_cloud( lidar_points) calib_fpath = f"{dataset_dir}/{log_id}/vehicle_calibration_info.json" calib_data = read_json_file(calib_fpath) # repeat green to red colormap every 50 m. colors_arr = np.array([ [color_obj.rgb] for color_obj in Color("red").range_to(Color("green"), NUM_RANGE_BINS) ]).squeeze() np.fliplr(colors_arr) for cam_idx, camera_name in enumerate(RING_CAMERA_LIST + STEREO_CAMERA_LIST): im_dir = f"{dataset_dir}/{log_id}/{camera_name}" # load images, e.g. 'image_raw_ring_front_center_000000486.jpg' cam_timestamp = sdb.get_closest_cam_channel_timestamp( lidar_timestamp, camera_name, log_id) if cam_timestamp is None: continue im_fname = f"{camera_name}_{cam_timestamp}.jpg" im_fpath = f"{im_dir}/{im_fname}" # Swap channel order as OpenCV expects it -- BGR not RGB # must make a copy to make memory contiguous img = imageio.imread(im_fpath)[:, :, ::-1].copy() points_h = point_cloud_to_homogeneous(copy.deepcopy(lidar_points)).T if motion_compensate: uv, uv_cam, valid_pts_bool = project_lidar_to_img_motion_compensated( points_h, # these are recorded at lidar_time copy.deepcopy(calib_data), camera_name, cam_timestamp, lidar_timestamp, dataset_dir, log_id, False, ) else: uv, uv_cam, valid_pts_bool = project_lidar_to_img( points_h, copy.deepcopy(calib_data), camera_name, False) if valid_pts_bool is None or uv is None or uv_cam is None: continue if valid_pts_bool.sum() == 0: continue uv = np.round(uv[valid_pts_bool]).astype(np.int32) uv_cam = uv_cam.T[valid_pts_bool] pt_ranges = np.linalg.norm(uv_cam[:, :3], axis=1) rgb_bins = np.round(pt_ranges).astype(np.int32) # account for moving past 100 meters, loop around again rgb_bins = rgb_bins % NUM_RANGE_BINS uv_colors = (255 * colors_arr[rgb_bins]).astype(np.int32) img = draw_point_cloud_in_img_cv2(img, uv, np.fliplr(uv_colors)) if not Path(f"{experiment_prefix}_ground_viz/{log_id}/{camera_name}" ).exists(): os.makedirs( f"{experiment_prefix}_ground_viz/{log_id}/{camera_name}") save_dir = f"{experiment_prefix}_ground_viz/{log_id}/{camera_name}" cv2.imwrite(f"{save_dir}/{camera_name}_{lidar_timestamp}.jpg", img) if camera == camera_name: return cv2.cvtColor(img, cv2.COLOR_BGR2RGB) return None
class SimpleArgoverseTrackingDataLoader: """ Simple abstraction for retrieving log data, given a path to the dataset. """ def __init__(self, data_dir: str, labels_dir: str) -> None: """ Args: data_dir: str, representing path to raw Argoverse data labels_dir: str representing path to Argoverse data labels (e.g. labels or estimated detections/tracks) """ self.data_dir = data_dir self.labels_dir = labels_dir self.sdb = SynchronizationDB(data_dir) def get_city_name(self, log_id: str) -> str: """ Args: log_id: str Returns: city_name: str """ city_info_fpath = f"{self.data_dir}/{log_id}/city_info.json" city_name = read_city_name(city_info_fpath) assert isinstance(city_name, str) return city_name def get_log_calibration_data(self, log_id: str) -> Mapping[str, Any]: """ Args: log_id: str Returns: log_calib_data: dictionary """ calib_fpath = f"{self.data_dir}/{log_id}/vehicle_calibration_info.json" log_calib_data = read_json_file(calib_fpath) assert isinstance(log_calib_data, dict) return log_calib_data def get_city_to_egovehicle_se3(self, log_id: str, timestamp: int) -> Optional[SE3]: """Deprecated version of get_city_SE3_egovehicle() below, as does not follow standard naming convention Args: log_id: str, unique ID of vehicle log timestamp: int, timestamp of sensor observation, in nanoseconds Returns: city_SE3_egovehicle: SE3 transformation to bring points in egovehicle frame into city frame. """ return self.get_city_SE3_egovehicle(log_id, timestamp) def get_city_SE3_egovehicle(self, log_id: str, timestamp: int) -> Optional[SE3]: """ Args: log_id: str, unique ID of vehicle log timestamp: int, timestamp of sensor observation, in nanoseconds Returns: city_SE3_egovehicle: SE3 transformation to bring points in egovehicle frame into city frame. """ return get_city_SE3_egovehicle_at_sensor_t(timestamp, self.data_dir, log_id) def get_closest_im_fpath(self, log_id: str, camera_name: str, lidar_timestamp: int) -> Optional[str]: """ Args: log_id: str, unique ID of vehicle log camera_name: str lidar_timestamp: int, timestamp of LiDAR sweep capture, in nanoseconds Returns: im_fpath, string representing path to image, or else None. """ cam_timestamp = self.sdb.get_closest_cam_channel_timestamp( lidar_timestamp, camera_name, log_id) if cam_timestamp is None: return None im_dir = f"{self.data_dir}/{log_id}/{camera_name}" im_fname = f"{camera_name}_{cam_timestamp}.jpg" im_fpath = f"{im_dir}/{im_fname}" return im_fpath def get_closest_lidar_fpath(self, log_id: str, cam_timestamp: int) -> Optional[str]: """ Args: log_id: str, unique ID of vehicle log cam_timestamp: int, timestamp of image capture, in nanoseconds Returns: ply_fpath: str, string representing path to PLY file, or else None. """ lidar_timestamp = self.sdb.get_closest_lidar_timestamp( cam_timestamp, log_id) if lidar_timestamp is None: return None lidar_dir = f"{self.data_dir}/{log_id}/lidar" ply_fname = f"PC_{lidar_timestamp}.ply" ply_fpath = f"{lidar_dir}/{ply_fname}" return ply_fpath def get_ordered_log_ply_fpaths(self, log_id: str) -> List[str]: """ Args: log_id: str, unique ID of vehicle log Returns: ply_fpaths: List of strings, representing paths to chronologically ordered ply files in this log File paths are strings are of the same length ending with a nanosecond timestamp, thus sorted() will place them in numerical order. """ ply_fpaths = sorted( glob.glob(f"{self.data_dir}/{log_id}/lidar/PC_*.ply")) return ply_fpaths def get_ordered_log_cam_fpaths(self, log_id: str, camera_name: str) -> List[str]: """ Args log_id: str, unique ID of vehicle log Returns cam_img_fpaths: List of strings, representing paths to ordered JPEG files in this log, for a specific camera """ cam_img_fpaths = sorted( glob.glob( f"{self.data_dir}/{log_id}/{camera_name}/{camera_name}_*.jpg")) return cam_img_fpaths def get_labels_at_lidar_timestamp( self, log_id: str, lidar_timestamp: int) -> Optional[List[Mapping[str, Any]]]: """ Args: log_id: str, unique ID of vehicle log lidar_timestamp: int, timestamp of LiDAR sweep capture, in nanoseconds Returns: labels: dictionary """ timestamp_track_label_fpath = ( f"{self.labels_dir}/{log_id}/per_sweep_annotations_amodal/tracked_object_labels_{lidar_timestamp}.json" ) if not Path(timestamp_track_label_fpath).exists(): return None labels = read_json_file(timestamp_track_label_fpath) assert isinstance(labels, list), labels return labels