def concatenate(box_mask_lists, fields=None): """Concatenate list of box_mask_lists. This op concatenates a list of input box_mask_lists into a larger box_mask_list. It also handles concatenation of box_mask_list fields as long as the field tensor shapes are equal except for the first dimension. Args: box_mask_lists: list of np_box_mask_list.BoxMaskList objects fields: optional list of fields to also concatenate. By default, all fields from the first BoxMaskList in the list are included in the concatenation. Returns: a box_mask_list with number of boxes equal to sum([box_mask_list.num_boxes() for box_mask_list in box_mask_list]) Raises: ValueError: if box_mask_lists is invalid (i.e., is not a list, is empty, or contains non box_mask_list objects), or if requested fields are not contained in all box_mask_lists """ if fields is not None: if 'masks' not in fields: fields.append('masks') return box_list_to_box_mask_list( np_box_list_ops.concatenate(boxlists=box_mask_lists, fields=fields))
def test_concatenate(self): boxlist1 = np_box_list.BoxList( np.array([[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype=np.float32)) boxlist2 = np_box_list.BoxList( np.array([[0.5, 0.25, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]], dtype=np.float32)) boxlists = [boxlist1, boxlist2] boxlist_concatenated = np_box_list_ops.concatenate(boxlists) boxlist_concatenated_expected = np_box_list.BoxList( np.array([[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75], [0.5, 0.25, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]], dtype=np.float32)) self.assertAllClose(boxlist_concatenated_expected.get(), boxlist_concatenated.get())
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)