def test_with_descending_sorting(self): sorted_boxlist = np_box_list_ops.sort_by_field(self.boxlist, 'scores') expected_boxes = np.array([[14.0, 14.0, 15.0, 15.0], [3.0, 4.0, 6.0, 8.0], [0.0, 0.0, 20.0, 20.0]], dtype=float) self.assertAllClose(expected_boxes, sorted_boxlist.get()) expected_scores = np.array([0.9, 0.5, 0.4], dtype=float) self.assertAllClose(expected_scores, sorted_boxlist.get_field('scores'))
def test_with_descending_sorting(self): sorted_boxlist = np_box_list_ops.sort_by_field(self.boxlist, 'scores') expected_boxes = np.array([[14.0, 14.0, 15.0, 15.0], [3.0, 4.0, 6.0, 8.0], [0.0, 0.0, 20.0, 20.0]], dtype=float) self.assertAllClose(expected_boxes, sorted_boxlist.get()) expected_scores = np.array([0.9, 0.5, 0.4], dtype=float) self.assertAllClose(expected_scores, sorted_boxlist.get_field('scores'))
def sort_by_field(box_mask_list, field, order=np_box_list_ops.SortOrder.DESCEND): """Sort boxes and associated fields according to a scalar field. A common use case is reordering the boxes according to descending scores. Args: box_mask_list: BoxMaskList holding N boxes. field: A BoxMaskList field for sorting and reordering the BoxMaskList. order: (Optional) 'descend' or 'ascend'. Default is descend. Returns: sorted_box_mask_list: A sorted BoxMaskList with the field in the specified order. """ return box_list_to_box_mask_list( np_box_list_ops.sort_by_field( boxlist=box_mask_list, field=field, order=order))
def _get_overlaps_and_scores_relation_tuples(self, detected_box_tuples, detected_scores, groundtruth_box_tuples): """Computes overlaps and scores between detected and groundtruth tuples. Both detections and groundtruth boxes have the same class tuples. Args: detected_box_tuples: A numpy array of structures with shape [N,], representing N tuples, each tuple containing the same number of named bounding boxes. Each box is of the format [y_min, x_min, y_max, x_max] detected_scores: A float numpy array of shape [N,], representing the confidence scores of the detected N object instances. groundtruth_box_tuples: A float numpy array of structures with the shape [M,], representing M tuples, each tuple containing the same number of named bounding boxes. Each box is of the format [y_min, x_min, y_max, x_max] Returns: result_iou: A float numpy array of size [num_detected_tuples, num_gt_box_tuples]. scores: The score of the detected boxlist. """ result_iou = np.ones( (detected_box_tuples.shape[0], groundtruth_box_tuples.shape[0]), dtype=float) for field in detected_box_tuples.dtype.fields: detected_boxlist_field = np_box_list.BoxList( detected_box_tuples[field]) detected_boxlist_field.add_field('scores', detected_scores) detected_boxlist_field = np_box_list_ops.sort_by_field( detected_boxlist_field, 'scores') gt_boxlist_field = np_box_list.BoxList( groundtruth_box_tuples[field]) iou_field = np_box_list_ops.iou(detected_boxlist_field, gt_boxlist_field) result_iou = np.minimum(iou_field, result_iou) scores = detected_boxlist_field.get_field('scores') return result_iou, scores
def multi_class_non_max_suppression(boxlist, score_thresh, iou_thresh, max_output_size): if not 0 <= iou_thresh <= 1.0: raise ValueError('thresh must be between 0 and 1') scores = boxlist.get_field('scores') classes = boxlist.get_field('classes') num_boxes = boxlist.num_boxes() selected_boxes_list = [] for cls in np.unique(classes): inds = np.where(cls == classes)[0] subboxlist = gather(boxlist, inds) boxlist_filt = filter_scores_greater_than(subboxlist, score_thresh) nms_result = non_max_suppression(boxlist_filt, max_output_size=max_output_size, iou_threshold=iou_thresh, score_threshold=score_thresh) selected_boxes_list.append(nms_result) selected_boxes = concatenate(selected_boxes_list) sorted_boxes = sort_by_field(selected_boxes, 'scores') return sorted_boxes
def multi_class_non_max_suppression_with_extra_fields( boxlist: BoxList, score_thresh: float, iou_thresh: float, max_output_size: int) -> BoxList: """ Is the same method as `object_detection.utils.np_box_list_ops.multi_class_non_max_suppression` but also preserves all the extra fields of the boxlist after nms """ # Modification: omit all the assertions # End of modification scores = boxlist.get_field('scores') num_scores = scores.shape[0] num_classes = scores.shape[1] selected_boxes_list = [] for class_idx in range(num_classes): boxlist_and_class_scores = np_box_list.BoxList(boxlist.get()) class_scores = np.reshape(scores[0:num_scores, class_idx], [-1]) boxlist_and_class_scores.add_field('scores', class_scores) # Modification: add extra fields to boxlist_and_class_scores for each_extra_field in boxlist.get_extra_fields(): if (not boxlist_and_class_scores.has_field(each_extra_field) and each_extra_field != "classes"): boxlist_and_class_scores.add_field( each_extra_field, boxlist.get_field(each_extra_field)) # End of modification boxlist_filt = np_box_list_ops.filter_scores_greater_than( boxlist_and_class_scores, score_thresh) nms_result = np_box_list_ops.non_max_suppression( boxlist_filt, max_output_size=max_output_size, iou_threshold=iou_thresh, score_threshold=score_thresh) nms_result.add_field( 'classes', np.zeros_like(nms_result.get_field('scores')) + class_idx) selected_boxes_list.append(nms_result) selected_boxes = np_box_list_ops.concatenate(selected_boxes_list) sorted_boxes = np_box_list_ops.sort_by_field(selected_boxes, 'scores') return sorted_boxes
def _get_overlaps_and_scores_relation_tuples( self, detected_box_tuples, detected_scores, groundtruth_box_tuples): """Computes overlaps and scores between detected and groundtruth tuples. Both detections and groundtruth boxes have the same class tuples. Args: detected_box_tuples: A numpy array of structures with shape [N,], representing N tuples, each tuple containing the same number of named bounding boxes. Each box is of the format [y_min, x_min, y_max, x_max] detected_scores: A float numpy array of shape [N,], representing the confidence scores of the detected N object instances. groundtruth_box_tuples: A float numpy array of structures with the shape [M,], representing M tuples, each tuple containing the same number of named bounding boxes. Each box is of the format [y_min, x_min, y_max, x_max] Returns: result_iou: A float numpy array of size [num_detected_tuples, num_gt_box_tuples]. scores: The score of the detected boxlist. """ result_iou = np.ones( (detected_box_tuples.shape[0], groundtruth_box_tuples.shape[0]), dtype=float) for field in detected_box_tuples.dtype.fields: detected_boxlist_field = np_box_list.BoxList(detected_box_tuples[field]) detected_boxlist_field.add_field('scores', detected_scores) detected_boxlist_field = np_box_list_ops.sort_by_field( detected_boxlist_field, 'scores') gt_boxlist_field = np_box_list.BoxList(groundtruth_box_tuples[field]) iou_field = np_box_list_ops.iou(detected_boxlist_field, gt_boxlist_field) result_iou = np.minimum(iou_field, result_iou) scores = detected_boxlist_field.get_field('scores') return result_iou, scores
def test_with_invalid_sorting_order(self): with self.assertRaises(ValueError): np_box_list_ops.sort_by_field(self.boxlist, 'scores', 'Descending')
def test_with_invalid_field(self): with self.assertRaises(ValueError): np_box_list_ops.sort_by_field(self.boxlist, 'objectness') with self.assertRaises(ValueError): np_box_list_ops.sort_by_field(self.boxlist, 'labels')
def multi_class_non_max_suppression(box_mask_list, score_thresh, iou_thresh, max_output_size): """Multi-class version of non maximum suppression. This op greedily selects a subset of detection bounding boxes, pruning away boxes that have high IOU (intersection over union) overlap (> thresh) with already selected boxes. It operates independently for each class for which scores are provided (via the scores field of the input box_list), pruning boxes with score less than a provided threshold prior to applying NMS. Args: box_mask_list: np_box_mask_list.BoxMaskList holding N boxes. Must contain a 'scores' field representing detection scores. This scores field is a tensor that can be 1 dimensional (in the case of a single class) or 2-dimensional, in which case we assume that it takes the shape [num_boxes, num_classes]. We further assume that this rank is known statically and that scores.shape[1] is also known (i.e., the number of classes is fixed and known at graph construction time). score_thresh: scalar threshold for score (low scoring boxes are removed). iou_thresh: scalar threshold for IOU (boxes that that high IOU overlap with previously selected boxes are removed). max_output_size: maximum number of retained boxes per class. Returns: a box_mask_list holding M boxes with a rank-1 scores field representing corresponding scores for each box with scores sorted in decreasing order and a rank-1 classes field representing a class label for each box. Raises: ValueError: if iou_thresh is not in [0, 1] or if input box_mask_list does not have a valid scores field. """ if not 0 <= iou_thresh <= 1.0: raise ValueError('thresh must be between 0 and 1') if not isinstance(box_mask_list, np_box_mask_list.BoxMaskList): raise ValueError('box_mask_list must be a box_mask_list') if not box_mask_list.has_field('scores'): raise ValueError('input box_mask_list must have \'scores\' field') scores = box_mask_list.get_field('scores') if len(scores.shape) == 1: scores = np.reshape(scores, [-1, 1]) elif len(scores.shape) == 2: if scores.shape[1] is None: raise ValueError('scores field must have statically defined second ' 'dimension') else: raise ValueError('scores field must be of rank 1 or 2') num_boxes = box_mask_list.num_boxes() num_scores = scores.shape[0] num_classes = scores.shape[1] if num_boxes != num_scores: raise ValueError('Incorrect scores field length: actual vs expected.') selected_boxes_list = [] for class_idx in range(num_classes): box_mask_list_and_class_scores = np_box_mask_list.BoxMaskList( box_data=box_mask_list.get(), mask_data=box_mask_list.get_masks()) class_scores = np.reshape(scores[0:num_scores, class_idx], [-1]) box_mask_list_and_class_scores.add_field('scores', class_scores) box_mask_list_filt = filter_scores_greater_than( box_mask_list_and_class_scores, score_thresh) nms_result = non_max_suppression( box_mask_list_filt, max_output_size=max_output_size, iou_threshold=iou_thresh, score_threshold=score_thresh) nms_result.add_field( 'classes', np.zeros_like(nms_result.get_field('scores')) + class_idx) selected_boxes_list.append(nms_result) selected_boxes = np_box_list_ops.concatenate(selected_boxes_list) sorted_boxes = np_box_list_ops.sort_by_field(selected_boxes, 'scores') return box_list_to_box_mask_list(boxlist=sorted_boxes)
def test_with_invalid_sorting_order(self): with self.assertRaises(ValueError): np_box_list_ops.sort_by_field(self.boxlist, 'scores', 'Descending')
def test_with_invalid_field(self): with self.assertRaises(ValueError): np_box_list_ops.sort_by_field(self.boxlist, 'objectness') with self.assertRaises(ValueError): np_box_list_ops.sort_by_field(self.boxlist, 'labels')