chip_box_utils.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import numpy as np
  15. def bbox_area(boxes):
  16. return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])
  17. def intersection_over_box(chips, boxes):
  18. """
  19. intersection area over box area
  20. :param chips: C
  21. :param boxes: B
  22. :return: iob, CxB
  23. """
  24. M = chips.shape[0]
  25. N = boxes.shape[0]
  26. if M * N == 0:
  27. return np.zeros([M, N], dtype='float32')
  28. box_area = bbox_area(boxes) # B
  29. inter_x2y2 = np.minimum(np.expand_dims(chips, 1)[:, :, 2:],
  30. boxes[:, 2:]) # CxBX2
  31. inter_x1y1 = np.maximum(np.expand_dims(chips, 1)[:, :, :2],
  32. boxes[:, :2]) # CxBx2
  33. inter_wh = inter_x2y2 - inter_x1y1
  34. inter_wh = np.clip(inter_wh, a_min=0, a_max=None)
  35. inter_area = inter_wh[:, :, 0] * inter_wh[:, :, 1] # CxB
  36. iob = inter_area / np.expand_dims(box_area, 0)
  37. return iob
  38. def clip_boxes(boxes, im_shape):
  39. """
  40. Clip boxes to image boundaries.
  41. :param boxes: [N, 4]
  42. :param im_shape: tuple of 2, [h, w]
  43. :return: [N, 4]
  44. """
  45. # x1 >= 0
  46. boxes[:, 0] = np.clip(boxes[:, 0], 0, im_shape[1] - 1)
  47. # y1 >= 0
  48. boxes[:, 1] = np.clip(boxes[:, 1], 0, im_shape[0] - 1)
  49. # x2 < im_shape[1]
  50. boxes[:, 2] = np.clip(boxes[:, 2], 1, im_shape[1])
  51. # y2 < im_shape[0]
  52. boxes[:, 3] = np.clip(boxes[:, 3], 1, im_shape[0])
  53. return boxes
  54. def transform_chip_box(gt_bbox: 'Gx4', boxes_idx: 'B', chip: '4'):
  55. boxes_idx = np.array(boxes_idx)
  56. cur_gt_bbox = gt_bbox[boxes_idx].copy() # Bx4
  57. x1, y1, x2, y2 = chip
  58. cur_gt_bbox[:, 0] -= x1
  59. cur_gt_bbox[:, 1] -= y1
  60. cur_gt_bbox[:, 2] -= x1
  61. cur_gt_bbox[:, 3] -= y1
  62. h = y2 - y1
  63. w = x2 - x1
  64. cur_gt_bbox = clip_boxes(cur_gt_bbox, (h, w))
  65. ws = (cur_gt_bbox[:, 2] - cur_gt_bbox[:, 0]).astype(np.int32)
  66. hs = (cur_gt_bbox[:, 3] - cur_gt_bbox[:, 1]).astype(np.int32)
  67. valid_idx = (ws >= 2) & (hs >= 2)
  68. return cur_gt_bbox[valid_idx], boxes_idx[valid_idx]
  69. def find_chips_to_cover_overlaped_boxes(iob, overlap_threshold):
  70. chip_ids, box_ids = np.nonzero(iob >= overlap_threshold)
  71. chip_id2overlap_box_num = np.bincount(chip_ids) # 1d array
  72. chip_id2overlap_box_num = np.pad(
  73. chip_id2overlap_box_num, (0, len(iob) - len(chip_id2overlap_box_num)),
  74. constant_values=0)
  75. chosen_chip_ids = []
  76. while len(box_ids) > 0:
  77. value_counts = np.bincount(chip_ids) # 1d array
  78. max_count_chip_id = np.argmax(value_counts)
  79. assert max_count_chip_id not in chosen_chip_ids
  80. chosen_chip_ids.append(max_count_chip_id)
  81. box_ids_in_cur_chip = box_ids[chip_ids == max_count_chip_id]
  82. ids_not_in_cur_boxes_mask = np.logical_not(
  83. np.isin(box_ids, box_ids_in_cur_chip))
  84. chip_ids = chip_ids[ids_not_in_cur_boxes_mask]
  85. box_ids = box_ids[ids_not_in_cur_boxes_mask]
  86. return chosen_chip_ids, chip_id2overlap_box_num
  87. def transform_chip_boxes2image_boxes(chip_boxes, chip, img_h, img_w):
  88. chip_boxes = np.array(sorted(chip_boxes, key=lambda item: -item[1]))
  89. xmin, ymin, _, _ = chip
  90. # Transform to origin image loc
  91. chip_boxes[:, 2] += xmin
  92. chip_boxes[:, 4] += xmin
  93. chip_boxes[:, 3] += ymin
  94. chip_boxes[:, 5] += ymin
  95. chip_boxes = clip_boxes(chip_boxes, (img_h, img_w))
  96. return chip_boxes
  97. def nms(dets, thresh):
  98. """Apply classic DPM-style greedy NMS."""
  99. if dets.shape[0] == 0:
  100. return dets[[], :]
  101. scores = dets[:, 1]
  102. x1 = dets[:, 2]
  103. y1 = dets[:, 3]
  104. x2 = dets[:, 4]
  105. y2 = dets[:, 5]
  106. areas = (x2 - x1 + 1) * (y2 - y1 + 1)
  107. order = scores.argsort()[::-1]
  108. ndets = dets.shape[0]
  109. suppressed = np.zeros((ndets), dtype=np.int32)
  110. # nominal indices
  111. # _i, _j
  112. # sorted indices
  113. # i, j
  114. # temp variables for box i's (the box currently under consideration)
  115. # ix1, iy1, ix2, iy2, iarea
  116. # variables for computing overlap with box j (lower scoring box)
  117. # xx1, yy1, xx2, yy2
  118. # w, h
  119. # inter, ovr
  120. for _i in range(ndets):
  121. i = order[_i]
  122. if suppressed[i] == 1:
  123. continue
  124. ix1 = x1[i]
  125. iy1 = y1[i]
  126. ix2 = x2[i]
  127. iy2 = y2[i]
  128. iarea = areas[i]
  129. for _j in range(_i + 1, ndets):
  130. j = order[_j]
  131. if suppressed[j] == 1:
  132. continue
  133. xx1 = max(ix1, x1[j])
  134. yy1 = max(iy1, y1[j])
  135. xx2 = min(ix2, x2[j])
  136. yy2 = min(iy2, y2[j])
  137. w = max(0.0, xx2 - xx1 + 1)
  138. h = max(0.0, yy2 - yy1 + 1)
  139. inter = w * h
  140. ovr = inter / (iarea + areas[j] - inter)
  141. if ovr >= thresh:
  142. suppressed[j] = 1
  143. keep = np.where(suppressed == 0)[0]
  144. dets = dets[keep, :]
  145. return dets