def get_boxes_3D(path, class_to_group): # Load labels from txt file df = pd.read_csv(path, names=KITTI_COLUMN_NAMES, header=None, delim_whitespace=True) # Convert label to list of Box3D gt_boxes_3D = [] for _, row in df.iterrows(): if row['Type'] in class_to_group: gt_boxes_3D += [ Box3D(row['3D_H'], row['3D_W'], row['3D_L'], row['3D_X'], row['3D_Y'], row['3D_Z'], row['Rot_Y'], cls=class_to_group[row['Type']]) ] return gt_boxes_3D
box_limits = [] all_limits = [] rand_num = 30 random_cars_dir = 'data_utils/aug_utils/annotations/cars/' random_cars = os.listdir(random_cars_dir) np.random.seed(0) random_cars = np.random.choice(random_cars, size=rand_num) sampled_cars = [] for random_car in random_cars: sampled_car = dd.io.load(os.path.join(random_cars_dir, random_car)) sampled_pts = sampled_car['pts'].T sampled_box = Box3D(h=sampled_car['box_h'], w=sampled_car['box_w'], l=sampled_car['box_l'], x=sampled_car['box_x'], y=sampled_car['box_y'], z=sampled_car['box_z'], yaw=sampled_car['box_yaw'], cls=sampled_car['box_cls']) data = { 'pts': sampled_pts, 'num_pts': sampled_pts.shape[1], 'box': sampled_box, 'bev_corners': sampled_box.get_bev_box().T, 'bev_center': sampled_box.get_bev_center(), } sampled_cars.append(data) sampled_cars = sorted(sampled_cars, key=itemgetter('num_pts'), reverse=False)
def decode(self, target, confidence_thresh): boxes_3D = [] cls_map = np.rot90(target[..., 0], -1) cls_map_flat = cls_map.flatten() geometry_map = np.rot90(target[..., 1:], -1) for positive_pt in self.feature_map_pts[ cls_map_flat > confidence_thresh]: # Denormalize # geometry_map[positive_pt[1], positive_pt[0]] *= self.target_stds # geometry_map[positive_pt[1], positive_pt[0]] += self.target_means # Offset z = positive_pt[0] / self.qf[0] # Convert to physical space x = positive_pt[1] / self.qf[ 1] - self.P_HEIGHT / 2 # Convert to physical space and move reference frame from top to middle z -= self.__denormalize( 'dz', geometry_map[positive_pt[1], positive_pt[0], 2]) x -= self.__denormalize( 'dx', geometry_map[positive_pt[1], positive_pt[0], 3]) y = self.__denormalize( 'alt', geometry_map[positive_pt[1], positive_pt[0], 4]) # # # Size # # scaling ratio experiment # w1 = np.exp(geometry_map[positive_pt[1], positive_pt[0], 8] * self.stats['mean']['log_w']) # l1 = np.exp(geometry_map[positive_pt[1], positive_pt[0], 9] * self.stats['mean']['log_l']) # h1 = np.exp(geometry_map[positive_pt[1], positive_pt[0], 10] * self.stats['mean']['log_h']) # ap = np.log(w1) * np.log(l1) # at = self.stats['mean']['log_w'] * self.stats['mean']['log_l'] # if ap < at / 3: # continue # # # Size # # # w, l = np.exp(geometry_map[positive_pt[1], positive_pt[0], (4, 5)]) w = np.exp( self.__denormalize( 'log_w', geometry_map[positive_pt[1], positive_pt[0], 5])) l = np.exp( self.__denormalize( 'log_l', geometry_map[positive_pt[1], positive_pt[0], 6])) h = np.exp( self.__denormalize( 'log_h', geometry_map[positive_pt[1], positive_pt[0], 7])) # w = np.mean([w1, w])#w1# # l = np.mean([l1, l])#l1# # h = np.mean([h1, h])#h1# yaw_dir = geometry_map[positive_pt[1], positive_pt[0], 8] if yaw_dir > 0: yaw_dir = 1 else: yaw_dir = -1 # Angle yaw = np.arctan2( self.__denormalize( 'sin', geometry_map[positive_pt[1], positive_pt[0], 1]), self.__denormalize( 'cos', geometry_map[positive_pt[1], positive_pt[0], 0])) * yaw_dir decoded_box = Box3D(h=h, w=w, l=l, x=x, y=y, z=z, yaw=yaw, confidence=cls_map[positive_pt[1], positive_pt[0]], cls='Car') boxes_3D.append(decoded_box) return boxes_3D
def __generate_map(self, target_box): inner_box = Box3D(h=target_box.h, w=target_box.w * self.subsampling_factor[0], l=target_box.l * self.subsampling_factor[0], x=target_box.x, y=target_box.y, z=target_box.z, yaw=target_box.yaw, cls=target_box.cls) outer_box = Box3D(h=target_box.h, w=target_box.w * self.subsampling_factor[1], l=target_box.l * self.subsampling_factor[1], x=target_box.x, y=target_box.y, z=target_box.z, yaw=target_box.yaw, cls=target_box.cls) # Obj mask - (0 - ignore, 1 - consider in loss) inner_mask = self.__get_positive_pts_mask(inner_box) outer_mask = self.__get_positive_pts_mask(outer_box) obj_mask = np.logical_not(np.logical_xor(inner_mask, outer_mask)) # Generate corresponding geometry map for pts in rectangle # cos, sin, z, x, width, length, y, h # y, h - are not regressed in original paper # Geo mask (0 - ignore, 1 - positive) geo_mask = self.__get_positive_pts_mask(target_box) geometry_map = np.zeros(shape=(self.target_height, self.target_width, self.num_channels), dtype=np.float32) # Generate corresponding geometry map for pts in rectangle for positive_pt in self.feature_map_pts[geo_mask]: # Angle # replacing (theta) -> (2 * theta) as proposed by BoxNet paper if target_box.yaw > 0: yaw_dir = 1 else: yaw_dir = -1 # should be values between [-1, 1] # geometry_map[positive_pt[1], positive_pt[0], (0, 1)] = (np.cos(box_3D.yaw), np.sin(box_3D.yaw)) geometry_map[positive_pt[1], positive_pt[0], (0, 1)] = (self.__normalize( 'cos', np.cos(target_box.yaw * yaw_dir)), self.__normalize( 'sin', np.sin(target_box.yaw * yaw_dir))) # Offset from center physical_z = positive_pt[0] / self.qf[ 0] # Convert to physical space physical_x = positive_pt[1] / self.qf[ 1] - 40 # Convert to physical space and move RF from top to middle # geometry_map[positive_pt[1], positive_pt[0], (2, 3)] = ((physical_z - box_3D.z), (physical_x - box_3D.x)) geometry_map[positive_pt[1], positive_pt[0], (2, 3, 4)] = (self.__normalize('dz', physical_z - target_box.z), self.__normalize('dx', physical_x - target_box.x), self.__normalize('alt', target_box.y)) # # Width and Length # # geometry_map[positive_pt[1], positive_pt[0], (4, 5)] = (np.log(box_3D.w), np.log(box_3D.l)) geometry_map[positive_pt[1], positive_pt[0], (5, 6, 7)] = (self.__normalize('log_w', np.log(target_box.w)), self.__normalize('log_l', np.log(target_box.l)), self.__normalize('log_h', np.log(target_box.h))) geometry_map[positive_pt[1], positive_pt[0], 8] = yaw_dir # # scaling ratio experiment # geometry_map[positive_pt[1], positive_pt[0], (8, 9, 10)] = (np.log(target_box.w) / self.stats['mean']['log_w'], # np.log(target_box.l) / self.stats['mean']['log_l'], # np.log(target_box.h) / self.stats['mean']['log_h'],) # # Normalize # geometry_map[positive_pt[1], positive_pt[0]] -= self.target_means # geometry_map[positive_pt[1], positive_pt[0]] /= self.target_stds # Reshape flat masks into 2D array obj_map = inner_mask.astype(np.float32).reshape( self.target_height, self.target_width) obj_mask = obj_mask.astype(np.float32).reshape(self.target_height, self.target_width) geo_mask = geo_mask.astype(np.float32).reshape(self.target_height, self.target_width) return obj_map, obj_mask, geometry_map, geo_mask
def add_random_sample( num_samples=30, sort=False, sort_desc=False, filter_wall_thresh=150, random_samples_dir='data_utils/aug_utils/annotations/cars/'): random_samples = os.listdir(random_samples_dir) random_samples = np.random.choice(random_samples, size=num_samples) samples = [] for random_sample in random_samples: sample = dd.io.load(os.path.join(random_samples_dir, random_sample)) sample_pts = sample['pts'].T sample_box = Box3D(h=sample['box_h'], w=sample['box_w'], l=sample['box_l'], x=sample['box_x'], y=sample['box_y'], z=sample['box_z'], yaw=sample['box_yaw'], cls=sample['box_cls']) samples.append({ 'frame_id': sample['frame_id'], 'pts': sample_pts, 'num_pts': sample_pts.shape[1], 'box': sample_box, }) if sort: samples = sorted(samples, key=itemgetter('num_pts'), reverse=sort_desc) lidar_src = (0, 0) lidar_src_3d = (0, 0, 0) border_start = (70, 40) border_end = (70, -40) def _get_box_limits(box, name=None): diag1, diag2 = box.get_bev_diags() diag1_3d, diag2_3d = box.get_3d_diag() angle1 = get_angle_between_vectors(lidar_src, diag1[0], diag1[1]) angle2 = get_angle_between_vectors(lidar_src, diag2[0], diag2[1]) if angle1 > angle2: ref_diag = diag1 diag_3d = diag1_3d else: ref_diag = diag2 diag_3d = diag2_3d inter1_x, inter1_y, _, _, _ = find_intersection_point( border_start, border_end, lidar_src, (ref_diag[0][0], ref_diag[0][1])) inter2_x, inter2_y, _, _, _ = find_intersection_point( border_start, border_end, lidar_src, (ref_diag[1][0], ref_diag[1][1])) min_h = diag_3d[0][0][-1] max_h = diag_3d[1][0][-1] # if name is not None: # all_limits.append(np.array([[inter1_y, 0, inter1_x], # [0,0,0]])) # all_limits.append(np.array([[inter2_y, 0, inter2_x], # [0,0,0]])) # all_limits.append(np.array([[ref_diag[0][1], min_z, ref_diag[0][0]], # [0,0,0]])) # all_limits.append(np.array([[ref_diag[1][1], min_z, ref_diag[1][0]], # [0,0,0]])) return { 'inter1': (inter1_x, inter1_y), 'inter2': (inter2_x, inter2_y), 'ref_diag': ref_diag, 'max_h': max_h, 'min_h': min_h, } def _add_random_sample(gt_boxes, pts, ref=None): if pts.shape[0] != 3: pts = pts.T ys = sorted([box.y for box in gt_boxes], reverse=False) median_y = np.median(ys) box_limits = [] for box in gt_boxes: limit_dict = _get_box_limits(box) box_limits.append(limit_dict) valid_samples = [] valid_samples_limits = {} for sample in samples: valid = True valid_samples_limits[sample['frame_id']] = [] for limit in box_limits: (x1, y1) = limit['inter1'] (x2, y2) = limit['inter2'] box_bev = sample['box'].get_bev_box().T limit_p = Polygon([lidar_src, (x1, y1), (x2, y2)]) box_p = Polygon([(box_bev[0][0], box_bev[0][1]), (box_bev[1][0], box_bev[1][1]), (box_bev[2][0], box_bev[2][1]), (box_bev[3][0], box_bev[3][1])]) if limit_p.intersection(box_p).area > 0: valid = False del limit_p, box_p if valid: y_diff = median_y - sample['box'].y new_pts, new_boxes, _ = PointCloudAugmenter.rotate_translate( rotation=0, translation=[[0, y_diff, 0]])(gt_boxes_3d=[sample['box']], pts=sample['pts'].T) new_box = new_boxes[0] new_box = Box3D(h=new_box.h, w=new_box.w, l=new_box.l, x=new_box.x, y=new_box.y, z=new_box.z, yaw=new_box.yaw, cls=new_box.cls) sample['box'] = new_box sample['pts'] = new_pts.T new_limit = _get_box_limits(sample['box']) box_limits.append(new_limit) valid_samples_limits[sample['frame_id']].append(new_limit) valid_samples.append(sample) new_pts = [] final_samples = [] for sample in valid_samples: for limit in valid_samples_limits[sample['frame_id']]: (y1, x1) = limit['inter1'] (y2, x2) = limit['inter2'] max_h, min_h = limit['max_h'], limit['min_h'] ref_diag = limit['ref_diag'] side1_a, side1_b = lidar_src, (x2, y2 ) # left of frustum (line) side2_a, side2_b = lidar_src, (x1, y1 ) # right of frustum (line) side4_a, side4_b, side4_c = lidar_src_3d, ( ref_diag[0][0], min_h, ref_diag[0][1]), (ref_diag[1][0], min_h, ref_diag[1][1] ) # bottom diag (plane) side5_a, side5_b, side5_c = lidar_src_3d, ( ref_diag[0][0], max_h, ref_diag[0][1]), (ref_diag[1][0], max_h, ref_diag[1][1] ) # top diag (plane) # check the side of all points using the left line of the frustum d_l = check_point_side_2d(side1_a[1], side1_a[0], side1_b[1], side1_b[0], pts[2, :], pts[0, :]) # check the side of all points using the right line of the frustum d_r = check_point_side_2d(side2_a[1], side2_a[0], side2_b[1], side2_b[0], pts[2, :], pts[0, :]) # check the side of all points using the plane from lidar source to the coordinates of the bottom diagonal d_b = check_point_side_3d(side4_a, side4_b, side4_c, pts[(2, 1, 0), :]) # check the side of all points using the plane from lidar source to the coordinates of the top diagonal d_t = check_point_side_3d(side5_a, side5_b, side5_c, pts[(2, 1, 0), :]) # check the side of all points using a line between the diagonal coordinates (find pts in_front/behind the car using its diagonal as reference) d_d = check_point_side_2d(ref_diag[0][0], ref_diag[0][1], ref_diag[1][0], ref_diag[1][1], pts[2, :], pts[0, :]) inds_d1 = np.where((d_l == 1) & (d_r == -1) & (d_b == 1))[0] inds_d2 = np.where((d_l == -1) & (d_r == 1) & (d_b == -1))[0] # cur_del are the points that are going to be deleted from the original PC to add the new sample if len(inds_d1) is not 0: cur_del_ids = np.where((d_l == 1) & (d_r == -1) & (d_b == 1) & (d_d == -1))[0] cur_del = pts[:, cur_del_ids] elif len(inds_d2) is not 0: cur_del_ids = np.where((d_l == -1) & (d_r == 1) & (d_b == -1) & (d_d == 1))[0] cur_del = pts[:, cur_del_ids] # keep the points that are above the top diagonals of the new sampled box (possible buildings behind the box) keep_ids1 = np.where((d_l == 1) & (d_r == -1) & (d_t == 1) & (d_d == 1))[0] keep_ids2 = np.where((d_l == -1) & (d_r == 1) & (d_t == -1) & (d_d == -1))[0] pts_keep = None if len(keep_ids1) is not 0: pts_keep = pts[:, keep_ids1] pts_keep = np.concatenate((pts_keep, pts[:, keep_ids2]), axis=1) elif len(keep_ids2) is not 0: pts_keep = pts[:, keep_ids2] if pts_keep is not None: # get the points that are above the car (on top) to be removed bev_box = sample['box'].get_bev_box().T l1 = check_point_side_2d(bev_box[0][0], bev_box[0][1], bev_box[1][0], bev_box[1][1], pts_keep[2, :], pts_keep[0, :]) l2 = check_point_side_2d(bev_box[1][0], bev_box[1][1], bev_box[2][0], bev_box[2][1], pts_keep[2, :], pts_keep[0, :]) l3 = check_point_side_2d(bev_box[2][0], bev_box[2][1], bev_box[3][0], bev_box[3][1], pts_keep[2, :], pts_keep[0, :]) l4 = check_point_side_2d(bev_box[3][0], bev_box[3][1], bev_box[0][0], bev_box[0][1], pts_keep[2, :], pts_keep[0, :]) d_t2 = check_point_side_3d(side5_a, side5_b, side5_c, pts_keep[(2, 1, 0), :]) rem_top = np.where((l1 == 1) & (l3 == 1) & (l2 == 1) & (l4 == 1) & (d_t2 == 1))[0] if len(rem_top) > 0: mask = np.ones((pts_keep.shape[1]), dtype=bool) mask[rem_top] = False pts_keep = pts_keep[:, mask] if cur_del is not None: if cur_del.shape[1] <= filter_wall_thresh: mask = np.ones((pts.shape[1]), dtype=bool) mask[inds_d1] = False mask[inds_d2] = False pts = pts[:, mask] final_samples.append(sample['box']) pts = np.concatenate((pts, sample['pts']), axis=1) pts = np.concatenate((pts, pts_keep), axis=1) # _get_box_limits(sample['box'], '') # print('added {0} new boxes in the scene'.format(len(final_samples))) gt_boxes.extend(final_samples) return pts, gt_boxes, ref return _add_random_sample
def _add_random_sample(gt_boxes, pts, ref=None): if pts.shape[0] != 3: pts = pts.T ys = sorted([box.y for box in gt_boxes], reverse=False) median_y = np.median(ys) box_limits = [] for box in gt_boxes: limit_dict = _get_box_limits(box) box_limits.append(limit_dict) valid_samples = [] valid_samples_limits = {} for sample in samples: valid = True valid_samples_limits[sample['frame_id']] = [] for limit in box_limits: (x1, y1) = limit['inter1'] (x2, y2) = limit['inter2'] box_bev = sample['box'].get_bev_box().T limit_p = Polygon([lidar_src, (x1, y1), (x2, y2)]) box_p = Polygon([(box_bev[0][0], box_bev[0][1]), (box_bev[1][0], box_bev[1][1]), (box_bev[2][0], box_bev[2][1]), (box_bev[3][0], box_bev[3][1])]) if limit_p.intersection(box_p).area > 0: valid = False del limit_p, box_p if valid: y_diff = median_y - sample['box'].y new_pts, new_boxes, _ = PointCloudAugmenter.rotate_translate( rotation=0, translation=[[0, y_diff, 0]])(gt_boxes_3d=[sample['box']], pts=sample['pts'].T) new_box = new_boxes[0] new_box = Box3D(h=new_box.h, w=new_box.w, l=new_box.l, x=new_box.x, y=new_box.y, z=new_box.z, yaw=new_box.yaw, cls=new_box.cls) sample['box'] = new_box sample['pts'] = new_pts.T new_limit = _get_box_limits(sample['box']) box_limits.append(new_limit) valid_samples_limits[sample['frame_id']].append(new_limit) valid_samples.append(sample) new_pts = [] final_samples = [] for sample in valid_samples: for limit in valid_samples_limits[sample['frame_id']]: (y1, x1) = limit['inter1'] (y2, x2) = limit['inter2'] max_h, min_h = limit['max_h'], limit['min_h'] ref_diag = limit['ref_diag'] side1_a, side1_b = lidar_src, (x2, y2 ) # left of frustum (line) side2_a, side2_b = lidar_src, (x1, y1 ) # right of frustum (line) side4_a, side4_b, side4_c = lidar_src_3d, ( ref_diag[0][0], min_h, ref_diag[0][1]), (ref_diag[1][0], min_h, ref_diag[1][1] ) # bottom diag (plane) side5_a, side5_b, side5_c = lidar_src_3d, ( ref_diag[0][0], max_h, ref_diag[0][1]), (ref_diag[1][0], max_h, ref_diag[1][1] ) # top diag (plane) # check the side of all points using the left line of the frustum d_l = check_point_side_2d(side1_a[1], side1_a[0], side1_b[1], side1_b[0], pts[2, :], pts[0, :]) # check the side of all points using the right line of the frustum d_r = check_point_side_2d(side2_a[1], side2_a[0], side2_b[1], side2_b[0], pts[2, :], pts[0, :]) # check the side of all points using the plane from lidar source to the coordinates of the bottom diagonal d_b = check_point_side_3d(side4_a, side4_b, side4_c, pts[(2, 1, 0), :]) # check the side of all points using the plane from lidar source to the coordinates of the top diagonal d_t = check_point_side_3d(side5_a, side5_b, side5_c, pts[(2, 1, 0), :]) # check the side of all points using a line between the diagonal coordinates (find pts in_front/behind the car using its diagonal as reference) d_d = check_point_side_2d(ref_diag[0][0], ref_diag[0][1], ref_diag[1][0], ref_diag[1][1], pts[2, :], pts[0, :]) inds_d1 = np.where((d_l == 1) & (d_r == -1) & (d_b == 1))[0] inds_d2 = np.where((d_l == -1) & (d_r == 1) & (d_b == -1))[0] # cur_del are the points that are going to be deleted from the original PC to add the new sample if len(inds_d1) is not 0: cur_del_ids = np.where((d_l == 1) & (d_r == -1) & (d_b == 1) & (d_d == -1))[0] cur_del = pts[:, cur_del_ids] elif len(inds_d2) is not 0: cur_del_ids = np.where((d_l == -1) & (d_r == 1) & (d_b == -1) & (d_d == 1))[0] cur_del = pts[:, cur_del_ids] # keep the points that are above the top diagonals of the new sampled box (possible buildings behind the box) keep_ids1 = np.where((d_l == 1) & (d_r == -1) & (d_t == 1) & (d_d == 1))[0] keep_ids2 = np.where((d_l == -1) & (d_r == 1) & (d_t == -1) & (d_d == -1))[0] pts_keep = None if len(keep_ids1) is not 0: pts_keep = pts[:, keep_ids1] pts_keep = np.concatenate((pts_keep, pts[:, keep_ids2]), axis=1) elif len(keep_ids2) is not 0: pts_keep = pts[:, keep_ids2] if pts_keep is not None: # get the points that are above the car (on top) to be removed bev_box = sample['box'].get_bev_box().T l1 = check_point_side_2d(bev_box[0][0], bev_box[0][1], bev_box[1][0], bev_box[1][1], pts_keep[2, :], pts_keep[0, :]) l2 = check_point_side_2d(bev_box[1][0], bev_box[1][1], bev_box[2][0], bev_box[2][1], pts_keep[2, :], pts_keep[0, :]) l3 = check_point_side_2d(bev_box[2][0], bev_box[2][1], bev_box[3][0], bev_box[3][1], pts_keep[2, :], pts_keep[0, :]) l4 = check_point_side_2d(bev_box[3][0], bev_box[3][1], bev_box[0][0], bev_box[0][1], pts_keep[2, :], pts_keep[0, :]) d_t2 = check_point_side_3d(side5_a, side5_b, side5_c, pts_keep[(2, 1, 0), :]) rem_top = np.where((l1 == 1) & (l3 == 1) & (l2 == 1) & (l4 == 1) & (d_t2 == 1))[0] if len(rem_top) > 0: mask = np.ones((pts_keep.shape[1]), dtype=bool) mask[rem_top] = False pts_keep = pts_keep[:, mask] if cur_del is not None: if cur_del.shape[1] <= filter_wall_thresh: mask = np.ones((pts.shape[1]), dtype=bool) mask[inds_d1] = False mask[inds_d2] = False pts = pts[:, mask] final_samples.append(sample['box']) pts = np.concatenate((pts, sample['pts']), axis=1) pts = np.concatenate((pts, pts_keep), axis=1) # _get_box_limits(sample['box'], '') # print('added {0} new boxes in the scene'.format(len(final_samples))) gt_boxes.extend(final_samples) return pts, gt_boxes, ref
box_limits = [] all_limits = [] rand_num = 30 random_cars_dir = 'data_utils/aug_utils/annotations/cars/' random_cars = os.listdir(random_cars_dir) np.random.seed(0) random_cars = np.random.choice(random_cars, size=rand_num) sampled_cars = [] for random_car in random_cars: sampled_car = dd.io.load(os.path.join(random_cars_dir, random_car)) sampled_pts = sampled_car['pts'].T sampled_box = Box3D(h=sampled_car['box_h'], w=sampled_car['box_w'], l=sampled_car['box_l'], x=sampled_car['box_x'], y=sampled_car['box_y'], z=sampled_car['box_z'], yaw=sampled_car['box_yaw'], cls=sampled_car['box_cls']) data = { 'frame_id': sampled_car['frame_id'], 'pts': sampled_pts, 'num_pts': sampled_pts.shape[1], 'box': sampled_box, 'bev_corners': sampled_box.get_bev_box().T, 'bev_center': sampled_box.get_bev_center(), } sampled_cars.append(data) sampled_cars = sorted(sampled_cars, key=itemgetter('num_pts'),