def test_check1(): """Test check without arguments""" bounding_boxes, confidences, labels, _ = setup_test_case() bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) bb.check()
def smoke_test_setup(): bounding_boxes, confidences, labels, _ = setup_test_case() bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) bb.check() metric = DefaultIntersectionOverTheUnion(threshold=0.35, direction="lt") selector = RandomSelector() return bb, metric, selector
def test_check2(): """Test check with arguments""" bounding_boxes, confidences, labels, _ = setup_test_case() bounding_box, confidence, label = one_additional() bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) bb.check(bounding_box=bounding_box, confidence=confidence, label=label)
def test_check7(): """Test check without arguments - fail bb range""" bounding_boxes, confidences, labels, _ = setup_test_case() bounding_boxes[0, 0, 0] = 1.01 bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) with pytest.raises(ValueError): bb.check()
def test_check5(): """Test check without arguments - warn""" bounding_boxes, confidences, labels, _ = setup_test_case() labels = labels.astype(np.float64) bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) with pytest.warns(SyntaxWarning): bb.check()
def test_check6(): """Test check with arguments - warn""" bounding_boxes, confidences, labels, _ = setup_test_case() bounding_box, confidence, label = one_additional() bounding_box = bounding_box.astype(np.int64) bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) with pytest.warns(SyntaxWarning): bb.check(bounding_box=bounding_box, confidence=confidence, label=label)
def test_append1(): """Test append without arguments""" bounding_boxes, confidences, labels, _ = setup_test_case() before_length = bounding_boxes.shape[0] bounding_box, confidence, label = one_additional() bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) bb.append(bounding_box=bounding_box, confidence=confidence, label=label) assert bb.bounding_boxes.shape[0] == before_length + 1
def _find_all_pair_overlaps(self, bounding_box_array: BoundingBoxArray): evaluated_pairs = set() overlapping_pairs = set() for i, bid1 in enumerate(bounding_box_array.bounding_box_ids): for j, bid2 in enumerate(bounding_box_array.bounding_box_ids): if i == j: continue sorted_bid_pair = [bid1, bid2] sorted_bid_pair.sort() sorted_bid_pair = tuple(sorted_bid_pair) if sorted_bid_pair in evaluated_pairs: continue # Calculate whether two bounding boxes have an # overlap metric greater than the threshold # (e.g. if box is 0.95 and threshold is 0.90, # then the boxes overlap and store True). overlap = self.metric.overlap( bounding_box_array.lookup_box(int(bid1)), bounding_box_array.lookup_box(int(bid2)), ) if overlap: overlapping_pairs.add(sorted_bid_pair) evaluated_pairs.add(sorted_bid_pair) return overlapping_pairs
def test_check9(): """Test check without arguments - fail bb shape len""" bounding_boxes, confidences, labels, _ = setup_test_case() bounding_boxes = np.array( ( ((0.04, 0.19, 0.12), (0.14, 0.29, 0.81)), ((0.11, 0.15, 0.02), (0.21, 0.25, 0.14)), ), dtype=np.float64, ) bb = BoundingBoxArray(bounding_boxes=bounding_boxes, confidences=confidences, labels=labels) with pytest.raises(ValueError): bb.check()
def _evaluate_overlap( self, bounding_box_array: BoundingBoxArray, bounding_box_ids: Iterator[Tuple[ Any, Any]], # TODO: Replace with nested loop instead of CP symmetric: bool = False, ) -> Tuple[List[int], Set[int]]: """For a given set of bounding boxes, this method applies cartesian product non-maximum suppression to them. Args: bounding_box_array: The payload of all bounding_boxes, confidences, and labels. bounding_box_ids: A list of bounding box pairs to evaluate. symmetric: True if bounding_box_ids is a True cartesian product (i.e. comparing each box against every other box), otherwise False. Returns: selected_bids: A list of bounding_box_ids that were selected by our non-maximum suppression selector and metric. evaluated_bids: A list of bounding_box_ids that were evaluated in selection. """ bounding_box_ids = [x for x in bounding_box_ids ] # TODO: Replace with optimized version. boundary_boudning_box_idsx = set([ b[0] for b in bounding_box_ids ]) # TODO: Replace with optimized version. all_bounding_box_idxs = set([ b[1] for b in bounding_box_ids ]) # TODO: Replace with optimized version. non_boundary_bounding_box_ids = all_bounding_box_idxs.difference( boundary_boudning_box_idsx) selected_bids = [] complementary_bids = [] evaluated_bids = set() no_overlap = np.full(bounding_box_array.bounding_boxes.shape[0], True, dtype=np.bool) last_bid = -1 empty = True for bids in bounding_box_ids: empty = False if bids[0] != last_bid and len(complementary_bids) > 0: selected_bids.append(self.selector.select(complementary_bids)) complementary_bids = [] if ((bids[1] not in evaluated_bids) and (bids[0] != bids[1]) and self.metric.overlap( bounding_box_array.lookup_box(int(bids[0])), bounding_box_array.lookup_box(int(bids[1])), )): complementary_bids.append(bids[1]) evaluated_bids.add(bids[0]) evaluated_bids.add(bids[1]) no_overlap[bounding_box_array.bounding_box_id_to_ix( bids[0])] = False no_overlap[bounding_box_array.bounding_box_id_to_ix( bids[1])] = False last_bid = bids[0] if not symmetric and len(non_boundary_bounding_box_ids) > 0: no_overlap_ixs = [ int(bounding_box_array.bounding_box_id_to_ix(x)) for x in list(non_boundary_bounding_box_ids) ] print("IXS:") print(no_overlap_ixs) no_overlap[no_overlap_ixs] = False all_bounding_box_idxs_list = list(all_bounding_box_idxs) if not empty: no_overlap_boxes = np.add( np.argwhere(no_overlap).ravel(), np.min(all_bounding_box_idxs_list)).tolist() selected_bids.extend(no_overlap_boxes) evaluated_bids.update(selected_bids) return (selected_bids, set(list(evaluated_bids)))
def _handle_boundaries( self, bounding_box_array: BoundingBoxArray, dividing_lines: List[Tuple[int, bool]], ) -> Optional[BoundingBoxArray]: """Finds all boxes overlapping boundaries. Performs selection on those boxes and any overlapping boxes. Args: bounding_box_array: The payload of the boxes, confidences, and labels. dividing_lines: The dividing_lines of each sector as determined by _create_sectors(). Returns: selected_bids: The bids (box ids) selected by the selection algorithm. bounding_boxes: The original bounding boxes less any bounding boxes that were evaluated for selection. confidences: The original confidences less any confidences that were evaluated for selection. """ assert bounding_box_array.bounding_boxes.shape[0] == bounding_box_array.bounding_box_ids.shape[0] on_boundary = np.full( bounding_box_array.bounding_boxes.shape[0], False, np.bool ) all_bids = np.arange(0, bounding_box_array.bounding_boxes.shape[0]) for bid in all_bids: for line, divide_on_height in dividing_lines: if ( divide_on_height and bounding_box_array.bounding_boxes[bid, 0, 0] < line < bounding_box_array.bounding_boxes[bid, 1, 0] ): on_boundary[bid] = True break elif ( not divide_on_height and bounding_box_array.bounding_boxes[bid, 0, 1] < line < bounding_box_array.bounding_boxes[bid, 1, 1] ): on_boundary[bid] = True break boundary_bids = all_bids[on_boundary] if len(boundary_bids) == 0: return None assert len(bounding_box_array) > 0 all_bids += np.min(bounding_box_array.bounding_box_ids) for bid1 in all_bids[on_boundary]: for bid2 in all_bids[on_boundary]: if bid1 == bid2: continue prod = itertools.product(all_bids[on_boundary], all_bids) selected_bids, evaluated_bids = self._evaluate_overlap(bounding_box_array, prod) evaluated_bids = np.asarray(list(evaluated_bids), dtype=np.int64) evaluated_bids_inv = np.ones( bounding_box_array.bounding_boxes.shape[0], np.bool ) evaluated_bids_inv_ixs = [bounding_box_array.bounding_box_id_to_ix(x) for x in evaluated_bids] evaluated_bids_inv[evaluated_bids_inv_ixs] = 0 return bounding_box_array[np.asarray(selected_bids, dtype=np.int64)]