post_process.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # Copyright (c) 2022 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. import cv2
  16. def hard_nms(box_scores, iou_threshold, top_k=-1, candidate_size=200):
  17. """
  18. Args:
  19. box_scores (N, 5): boxes in corner-form and probabilities.
  20. iou_threshold: intersection over union threshold.
  21. top_k: keep top_k results. If k <= 0, keep all the results.
  22. candidate_size: only consider the candidates with the highest scores.
  23. Returns:
  24. picked: a list of indexes of the kept boxes
  25. """
  26. scores = box_scores[:, -1]
  27. boxes = box_scores[:, :-1]
  28. picked = []
  29. indexes = np.argsort(scores)
  30. indexes = indexes[-candidate_size:]
  31. while len(indexes) > 0:
  32. current = indexes[-1]
  33. picked.append(current)
  34. if 0 < top_k == len(picked) or len(indexes) == 1:
  35. break
  36. current_box = boxes[current, :]
  37. indexes = indexes[:-1]
  38. rest_boxes = boxes[indexes, :]
  39. iou = iou_of(
  40. rest_boxes,
  41. np.expand_dims(
  42. current_box, axis=0), )
  43. indexes = indexes[iou <= iou_threshold]
  44. return box_scores[picked, :]
  45. def iou_of(boxes0, boxes1, eps=1e-5):
  46. """Return intersection-over-union (Jaccard index) of boxes.
  47. Args:
  48. boxes0 (N, 4): ground truth boxes.
  49. boxes1 (N or 1, 4): predicted boxes.
  50. eps: a small number to avoid 0 as denominator.
  51. Returns:
  52. iou (N): IoU values.
  53. """
  54. overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2])
  55. overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:])
  56. overlap_area = area_of(overlap_left_top, overlap_right_bottom)
  57. area0 = area_of(boxes0[..., :2], boxes0[..., 2:])
  58. area1 = area_of(boxes1[..., :2], boxes1[..., 2:])
  59. return overlap_area / (area0 + area1 - overlap_area + eps)
  60. def area_of(left_top, right_bottom):
  61. """Compute the areas of rectangles given two corners.
  62. Args:
  63. left_top (N, 2): left top corner.
  64. right_bottom (N, 2): right bottom corner.
  65. Returns:
  66. area (N): return the area.
  67. """
  68. hw = np.clip(right_bottom - left_top, 0.0, None)
  69. return hw[..., 0] * hw[..., 1]
  70. class PPYOLOEPostProcess(object):
  71. """
  72. Args:
  73. input_shape (int): network input image size
  74. scale_factor (float): scale factor of ori image
  75. """
  76. def __init__(self,
  77. score_threshold=0.4,
  78. nms_threshold=0.5,
  79. nms_top_k=10000,
  80. keep_top_k=300):
  81. self.score_threshold = score_threshold
  82. self.nms_threshold = nms_threshold
  83. self.nms_top_k = nms_top_k
  84. self.keep_top_k = keep_top_k
  85. def _non_max_suppression(self, prediction, scale_factor):
  86. batch_size = prediction.shape[0]
  87. out_boxes_list = []
  88. box_num_list = []
  89. for batch_id in range(batch_size):
  90. bboxes, confidences = prediction[batch_id][..., :4], prediction[
  91. batch_id][..., 4:]
  92. # nms
  93. picked_box_probs = []
  94. picked_labels = []
  95. for class_index in range(0, confidences.shape[1]):
  96. probs = confidences[:, class_index]
  97. mask = probs > self.score_threshold
  98. probs = probs[mask]
  99. if probs.shape[0] == 0:
  100. continue
  101. subset_boxes = bboxes[mask, :]
  102. box_probs = np.concatenate(
  103. [subset_boxes, probs.reshape(-1, 1)], axis=1)
  104. box_probs = hard_nms(
  105. box_probs,
  106. iou_threshold=self.nms_threshold,
  107. top_k=self.nms_top_k)
  108. picked_box_probs.append(box_probs)
  109. picked_labels.extend([class_index] * box_probs.shape[0])
  110. if len(picked_box_probs) == 0:
  111. out_boxes_list.append(np.empty((0, 4)))
  112. else:
  113. picked_box_probs = np.concatenate(picked_box_probs)
  114. # resize output boxes
  115. picked_box_probs[:, 0] /= scale_factor[batch_id][1]
  116. picked_box_probs[:, 2] /= scale_factor[batch_id][1]
  117. picked_box_probs[:, 1] /= scale_factor[batch_id][0]
  118. picked_box_probs[:, 3] /= scale_factor[batch_id][0]
  119. # clas score box
  120. out_box = np.concatenate(
  121. [
  122. np.expand_dims(
  123. np.array(picked_labels), axis=-1), np.expand_dims(
  124. picked_box_probs[:, 4], axis=-1),
  125. picked_box_probs[:, :4]
  126. ],
  127. axis=1)
  128. if out_box.shape[0] > self.keep_top_k:
  129. out_box = out_box[out_box[:, 1].argsort()[::-1]
  130. [:self.keep_top_k]]
  131. out_boxes_list.append(out_box)
  132. box_num_list.append(out_box.shape[0])
  133. out_boxes_list = np.concatenate(out_boxes_list, axis=0)
  134. box_num_list = np.array(box_num_list)
  135. return out_boxes_list, box_num_list
  136. def __call__(self, outs, scale_factor):
  137. out_boxes_list, box_num_list = self._non_max_suppression(outs,
  138. scale_factor)
  139. return {'bbox': out_boxes_list, 'bbox_num': box_num_list}