def set_objects(self, object_labels, colour_scheme=None, show_orientations=True): """ Parses a list of ObjectLabel to set boxes and their colours :param object_labels: list of ObjectLabel to visualize :param colour_scheme: colours for each class (e.g. COLOUR_SCHEME_KITTI) :param show_orientations: (optional) if True, show box orientations """ box_corners = [] box_colours = [] pyramid_tips = None if show_orientations: pyramid_tips = [] for obj in object_labels: # Ignore DontCare boxes since they are at (-1000, -1000, -1000) if obj.type == 'DontCare': continue # Get box corners corners = np.array(obj_utils.compute_box_corners_3d(obj)) if corners.size != 0: box_corners.append(corners.transpose()) # Get colours if colour_scheme is not None: if obj.type in colour_scheme: box_colours.append(colour_scheme[obj.type]) else: # Default (White) box_colours.append((255, 255, 255)) if show_orientations: # Overwrite self.vtk_actor to contain both actors vtk_boxes_actor = self.vtk_actor self.vtk_actor = vtk.vtkAssembly() self.vtk_actor.AddPart(vtk_boxes_actor) self.vtk_actor.AddPart(self.vtk_pyramid_actor) # Distance off the ground arrow_height = obj.h / 2.0 pyramid_tip_length = obj.l * 0.2 half_obj_l = obj.l / 2.0 # Distance from centroid pyramid_tip_dist = half_obj_l + pyramid_tip_length # Start and end points # arrow_start = np.add(obj.t, [0, -arrow_height, 0]) pyramid_tip =\ np.add(obj.t, [pyramid_tip_dist * np.cos(obj.ry), -arrow_height, pyramid_tip_dist * -np.sin(obj.ry)]) pyramid_tips.append(pyramid_tip) self._set_boxes(box_corners, box_colours, pyramid_tips)
def set_objects(self, object_labels, colour_scheme=None, show_orientations=False): """ Parses a list of ObjectLabel to set boxes and their colours :param object_labels: list of ObjectLabel to visualize :param colour_scheme: colours for each class (e.g. COLOUR_SCHEME_KITTI) :param show_orientations: (optional) if True, self.vtk_lines_actor can be used to display the orientations of each box """ box_corners = [] box_colours = [] orientation_vectors = None if show_orientations: orientation_vectors = [] for obj in object_labels: # Ignore DontCare boxes since they are at (-1000, -1000, -1000) if obj.type == 'DontCare': continue # Get box corners corners = np.array(obj_utils.compute_box_corners_3d(obj)) if corners.size != 0: box_corners.append(corners.transpose()) # Get colours if colour_scheme is not None: if obj.type in colour_scheme: box_colours.append(colour_scheme[obj.type]) else: # Default (White) box_colours.append((255, 255, 255)) if show_orientations: # Overwrite self.vtk_actor to contain both actors vtk_boxes_actor = self.vtk_actor self.vtk_actor = vtk.vtkAssembly() self.vtk_actor.AddPart(vtk_boxes_actor) self.vtk_actor.AddPart(self.vtk_lines_actor) # Distance off the ground arrow_height = obj.h / 2.0 # Start and end points arrow_start = np.add(obj.t, [0, -arrow_height, 0]) arrow_end = np.add(obj.t, [ obj.l * np.cos(obj.ry), -arrow_height, obj.l * -np.sin(obj.ry) ]) orientation_vectors.append([arrow_start, arrow_end]) self._set_boxes(box_corners, box_colours, orientation_vectors)
def test_compute_box_3d(self): # read in calib file and label file and mat file calib_frame = calib.read_calibration(self.test_data_calib_dir, 724513) objects = obj_utils.read_labels(self.test_data_label_dir, 5258) label_true = scipy.io.loadmat(self.test_data_dir + '/compute3d.mat') # compute corners_3d = obj_utils.compute_box_corners_3d(objects[0]) corners, face_idx = obj_utils.project_box3d_to_image( corners_3d, calib_frame.p2) # compare data np.testing.assert_almost_equal(corners, label_true['corners']) orientation = obj_utils.compute_orientation_3d(objects[0], calib_frame.p2) # -1 for index in python vs matlab self.assertTrue((face_idx == label_true['face_idx']-1).all()) # Test orientation self.assertAlmostEqual(orientation.all(), label_true['orientation'].all()) return
def draw_box_3d(ax, obj, p, show_orientation=True, color_table=None, line_width=3, double_line=True, box_color=None): """Draws the 3D boxes given the subplot, object label, and frame transformation matrix :param ax: subplot handle :param obj: object file to draw bounding box :param p:stereo frame transformation matrix :param show_orientation: optional, draw a line showing orientaion :param color_table: optional, a custom table for coloring the boxes, should have 4 values to match the 4 truncation values. This color scheme is used to display boxes colored based on difficulty. :param line_width: optional, custom line width to draw the box :param double_line: optional, overlays a thinner line inside the box lines :param box_color: optional, use a custom color for box (instead of the default color_table. """ corners3d = obj_utils.compute_box_corners_3d(obj) corners, face_idx = obj_utils.project_box3d_to_image(corners3d, p) # define colors if color_table: if len(color_table) != 4: raise ValueError('Invalid color table length, must be 4') else: color_table = ["#00cc00", 'y', 'r', 'w'] trun_style = ['solid', 'dashed'] trc = int(obj.truncation > 0.1) if len(corners) > 0: for i in range(4): x = np.append(corners[0, face_idx[i, ]], corners[0, face_idx[i, 0]]) y = np.append(corners[1, face_idx[i, ]], corners[1, face_idx[i, 0]]) # Draw the boxes if box_color is None: box_color = color_table[int(obj.occlusion)] ax.plot(x, y, linewidth=line_width, color=box_color, linestyle=trun_style[trc]) # Draw a thinner second line inside if double_line: ax.plot(x, y, linewidth=line_width / 3.0, color='b') if show_orientation: # Compute orientation 3D orientation = obj_utils.compute_orientation_3d(obj, p) if orientation is not None: x = np.append(orientation[0, ], orientation[0, ]) y = np.append(orientation[1, ], orientation[1, ]) # draw the boxes ax.plot(x, y, linewidth=4, color='w') ax.plot(x, y, linewidth=2, color='k')
def generate_negative_3d_bb(obj_label, boxes3d, iou_threshold_min, iou_threshold_max, samples): """Generates negative 3D bounding boxes. This is the 3D version of generate_negative_3d_bb. Keyword arguments: obj_label: single label for a detected 3D object boxes3d: a list of numpy array representing all 3D bounding boxes in the image iou_threshold_min: determines the min variation between the original iou and the negative samples iou_threshold_max: determines the max variation between the original iou and the negative samples samples: number of negative samples per detected object Returns: a list of generated ObjectLabels """ box_corners = od.compute_box_corners_3d(obj_label) # make sure this is not empty assert (len(box_corners) > 0) P1 = box_corners[:, 0] P2 = box_corners[:, 1] P4 = box_corners[:, 3] current_samples = 0 new_objects = [] while current_samples < samples: # Generate random 3D point inside the box # we keep the same y, only generate x and z new_xp = float(np.random.uniform(P1[0], P4[0], 1)) new_zp = float(np.random.uniform(P1[2], P2[2], 1)) # create a new ObjectLabel new_obj = copy.copy(obj_label) # update the new obj.t # everything else is the same, only changing the # centroid point t which remains the same along # the y direction new_obj.t = (new_xp, obj_label.t[1], new_zp) _, box_to_test, _ = od.build_bbs_from_objects([new_obj], 'All') assert (len(box_to_test) == 1) # we are dealing with one box here so its the first element iou_3d = evaluation.three_d_iou(box_to_test[0], boxes3d) # check if iou_3d is a list, take the largest # this compares to all the boxes if isinstance(iou_3d, np.ndarray): iou_max = np.max(iou_3d) else: iou_max = iou_3d if iou_threshold_min < iou_max < iou_threshold_max: # keep the new object label new_objects.append(new_obj) current_samples += 1 return new_objects
def project_to_image_space(box_3d, calib_p2, truncate=False, image_size=None, discard_before_truncation=True): """ Projects a box_3d into image space Args: box_3d: single box_3d to project calib_p2: stereo calibration p2 matrix truncate: if True, 2D projections are truncated to be inside the image image_size: [w, h] must be provided if truncate is True, used for truncation discard_before_truncation: If True, discard boxes that are larger than 80% of the image in width OR height BEFORE truncation. If False, discard boxes that are larger than 80% of the width AND height AFTER truncation. Returns: Projected box in image space [x1, y1, x2, y2] Returns None if box is not inside the image """ format_checker.check_box_3d_format(box_3d) obj_label = box_3d_encoder.box_3d_to_object_label(box_3d) corners_3d = obj_utils.compute_box_corners_3d(obj_label) projected = calib_utils.project_to_image(corners_3d, calib_p2) x1 = np.amin(projected[0]) y1 = np.amin(projected[1]) x2 = np.amax(projected[0]) y2 = np.amax(projected[1]) img_box = np.array([x1, y1, x2, y2]) if truncate: if not image_size: raise ValueError('Image size must be provided') image_w = image_size[0] image_h = image_size[1] # Discard invalid boxes (outside image space) if img_box[0] > image_w or \ img_box[1] > image_h or \ img_box[2] < 0 or \ img_box[3] < 0: return None # Discard boxes that are larger than 80% of the image width OR height if discard_before_truncation: img_box_w = img_box[2] - img_box[0] img_box_h = img_box[3] - img_box[1] if img_box_w > (image_w * 0.8) or img_box_h > (image_h * 0.8): return None # Truncate remaining boxes into image space if img_box[0] < 0: img_box[0] = 0 if img_box[1] < 0: img_box[1] = 0 if img_box[2] > image_w: img_box[2] = image_w if img_box[3] > image_h: img_box[3] = image_h # Discard boxes that are covering the the whole image after truncation if not discard_before_truncation: img_box_w = img_box[2] - img_box[0] img_box_h = img_box[3] - img_box[1] if img_box_w > (image_w * 0.8) and img_box_h > (image_h * 0.8): return None return img_box