# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import numpy as np def bbox_area(boxes): return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]) def intersection_over_box(chips, boxes): """ intersection area over box area :param chips: C :param boxes: B :return: iob, CxB """ M = chips.shape[0] N = boxes.shape[0] if M * N == 0: return np.zeros([M, N], dtype='float32') box_area = bbox_area(boxes) # B inter_x2y2 = np.minimum(np.expand_dims(chips, 1)[:, :, 2:], boxes[:, 2:]) # CxBX2 inter_x1y1 = np.maximum(np.expand_dims(chips, 1)[:, :, :2], boxes[:, :2]) # CxBx2 inter_wh = inter_x2y2 - inter_x1y1 inter_wh = np.clip(inter_wh, a_min=0, a_max=None) inter_area = inter_wh[:, :, 0] * inter_wh[:, :, 1] # CxB iob = inter_area / np.expand_dims(box_area, 0) return iob def clip_boxes(boxes, im_shape): """ Clip boxes to image boundaries. :param boxes: [N, 4] :param im_shape: tuple of 2, [h, w] :return: [N, 4] """ # x1 >= 0 boxes[:, 0] = np.clip(boxes[:, 0], 0, im_shape[1] - 1) # y1 >= 0 boxes[:, 1] = np.clip(boxes[:, 1], 0, im_shape[0] - 1) # x2 < im_shape[1] boxes[:, 2] = np.clip(boxes[:, 2], 1, im_shape[1]) # y2 < im_shape[0] boxes[:, 3] = np.clip(boxes[:, 3], 1, im_shape[0]) return boxes def transform_chip_box(gt_bbox: 'Gx4', boxes_idx: 'B', chip: '4'): boxes_idx = np.array(boxes_idx) cur_gt_bbox = gt_bbox[boxes_idx].copy() # Bx4 x1, y1, x2, y2 = chip cur_gt_bbox[:, 0] -= x1 cur_gt_bbox[:, 1] -= y1 cur_gt_bbox[:, 2] -= x1 cur_gt_bbox[:, 3] -= y1 h = y2 - y1 w = x2 - x1 cur_gt_bbox = clip_boxes(cur_gt_bbox, (h, w)) ws = (cur_gt_bbox[:, 2] - cur_gt_bbox[:, 0]).astype(np.int32) hs = (cur_gt_bbox[:, 3] - cur_gt_bbox[:, 1]).astype(np.int32) valid_idx = (ws >= 2) & (hs >= 2) return cur_gt_bbox[valid_idx], boxes_idx[valid_idx] def find_chips_to_cover_overlaped_boxes(iob, overlap_threshold): chip_ids, box_ids = np.nonzero(iob >= overlap_threshold) chip_id2overlap_box_num = np.bincount(chip_ids) # 1d array chip_id2overlap_box_num = np.pad( chip_id2overlap_box_num, (0, len(iob) - len(chip_id2overlap_box_num)), constant_values=0) chosen_chip_ids = [] while len(box_ids) > 0: value_counts = np.bincount(chip_ids) # 1d array max_count_chip_id = np.argmax(value_counts) assert max_count_chip_id not in chosen_chip_ids chosen_chip_ids.append(max_count_chip_id) box_ids_in_cur_chip = box_ids[chip_ids == max_count_chip_id] ids_not_in_cur_boxes_mask = np.logical_not( np.isin(box_ids, box_ids_in_cur_chip)) chip_ids = chip_ids[ids_not_in_cur_boxes_mask] box_ids = box_ids[ids_not_in_cur_boxes_mask] return chosen_chip_ids, chip_id2overlap_box_num def transform_chip_boxes2image_boxes(chip_boxes, chip, img_h, img_w): chip_boxes = np.array(sorted(chip_boxes, key=lambda item: -item[1])) xmin, ymin, _, _ = chip # Transform to origin image loc chip_boxes[:, 2] += xmin chip_boxes[:, 4] += xmin chip_boxes[:, 3] += ymin chip_boxes[:, 5] += ymin chip_boxes = clip_boxes(chip_boxes, (img_h, img_w)) return chip_boxes def nms(dets, thresh): """Apply classic DPM-style greedy NMS.""" if dets.shape[0] == 0: return dets[[], :] scores = dets[:, 1] x1 = dets[:, 2] y1 = dets[:, 3] x2 = dets[:, 4] y2 = dets[:, 5] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.argsort()[::-1] ndets = dets.shape[0] suppressed = np.zeros((ndets), dtype=np.int32) # nominal indices # _i, _j # sorted indices # i, j # temp variables for box i's (the box currently under consideration) # ix1, iy1, ix2, iy2, iarea # variables for computing overlap with box j (lower scoring box) # xx1, yy1, xx2, yy2 # w, h # inter, ovr for _i in range(ndets): i = order[_i] if suppressed[i] == 1: continue ix1 = x1[i] iy1 = y1[i] ix2 = x2[i] iy2 = y2[i] iarea = areas[i] for _j in range(_i + 1, ndets): j = order[_j] if suppressed[j] == 1: continue xx1 = max(ix1, x1[j]) yy1 = max(iy1, y1[j]) xx2 = min(ix2, x2[j]) yy2 = min(iy2, y2[j]) w = max(0.0, xx2 - xx1 + 1) h = max(0.0, yy2 - yy1 + 1) inter = w * h ovr = inter / (iarea + areas[j] - inter) if ovr >= thresh: suppressed[j] = 1 keep = np.where(suppressed == 0)[0] dets = dets[keep, :] return dets