boxes_overlap.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import numpy as np
  2. def paired_intersection(boxes1, boxes2):
  3. """Compute paired intersection areas between boxes.
  4. Args:
  5. boxes1: a numpy array with shape [N, 4] holding N boxes
  6. boxes2: a numpy array with shape [N, 4] holding N boxes
  7. Returns:
  8. a numpy array with shape [N,] representing itemwise intersection area
  9. References:
  10. `core.box_list_ops.matched_intersection` in Tensorflow object detection API
  11. Notes:
  12. can called as itemwise_intersection, matched_intersection, aligned_intersection
  13. """
  14. max_x_mins = np.maximum(boxes1[:, 0], boxes2[:, 0])
  15. max_y_mins = np.maximum(boxes1[:, 1], boxes2[:, 1])
  16. min_x_maxs = np.minimum(boxes1[:, 2], boxes2[:, 2])
  17. min_y_maxs = np.minimum(boxes1[:, 3], boxes2[:, 3])
  18. intersect_widths = np.maximum(0., min_x_maxs - max_x_mins)
  19. intersect_heights = np.maximum(0., min_y_maxs - max_y_mins)
  20. return intersect_widths * intersect_heights
  21. def pairwise_intersection(boxes1, boxes2):
  22. """Compute pairwise intersection areas between boxes.
  23. Args:
  24. boxes1: a numpy array with shape [N, 4] holding N boxes.
  25. boxes2: a numpy array with shape [M, 4] holding M boxes.
  26. Returns:
  27. a numpy array with shape [N, M] representing pairwise intersection area.
  28. References:
  29. `core.box_list_ops.intersection` in Tensorflow object detection API
  30. `utils.box_list_ops.intersection` in Tensorflow object detection API
  31. """
  32. rows = boxes1.shape[0]
  33. cols = boxes2.shape[0]
  34. intersect_areas = np.zeros((rows, cols), dtype=boxes1.dtype)
  35. if rows * cols == 0:
  36. return intersect_areas
  37. swap = False
  38. if boxes1.shape[0] > boxes2.shape[0]:
  39. boxes1, boxes2 = boxes2, boxes1
  40. intersect_areas = np.zeros((cols, rows), dtype=boxes1.dtype)
  41. swap = True
  42. for i in range(boxes1.shape[0]):
  43. max_x_mins = np.maximum(boxes1[i, 0], boxes2[:, 0])
  44. max_y_mins = np.maximum(boxes1[i, 1], boxes2[:, 1])
  45. min_x_maxs = np.minimum(boxes1[i, 2], boxes2[:, 2])
  46. min_y_maxs = np.minimum(boxes1[i, 3], boxes2[:, 3])
  47. min_x_maxs -= max_x_mins
  48. min_y_maxs -= max_y_mins
  49. np.maximum(min_x_maxs, 0, min_x_maxs)
  50. np.maximum(min_y_maxs, 0, min_y_maxs)
  51. min_x_maxs *= min_y_maxs
  52. intersect_areas[i, :] = min_x_maxs
  53. if swap:
  54. intersect_areas = intersect_areas.T
  55. return intersect_areas
  56. def paired_overlap_ratio(boxes1, boxes2, ratio_type='iou'):
  57. """Compute paired overlap ratio between boxes.
  58. Args:
  59. boxes1: a numpy array with shape [N, 4] holding N boxes
  60. boxes2: a numpy array with shape [N, 4] holding N boxes
  61. ratio_type:
  62. iou: Intersection-over-union (iou).
  63. ioa: Intersection-over-area (ioa) between two boxes box1 and box2 is defined as
  64. their intersection area over box2's area. Note that ioa is not symmetric,
  65. that is, IOA(box1, box2) != IOA(box2, box1).
  66. min: Compute the ratio as the area of intersection between box1 and box2,
  67. divided by the minimum area of the two bounding boxes.
  68. Returns:
  69. a numpy array with shape [N,] representing itemwise overlap ratio.
  70. References:
  71. `core.box_list_ops.matched_iou` in Tensorflow object detection API
  72. `structures.boxes.matched_boxlist_iou` in detectron2
  73. `mmdet.core.bbox.bbox_overlaps`, see https://mmdetection.readthedocs.io/en/v2.17.0/api.html#mmdet.core.bbox.bbox_overlaps
  74. """
  75. intersect_areas = paired_intersection(boxes1, boxes2)
  76. areas1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1])
  77. areas2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1])
  78. if ratio_type in ['union', 'iou', 'giou']:
  79. union_areas = areas1 - intersect_areas
  80. union_areas += areas2
  81. intersect_areas /= union_areas
  82. elif ratio_type == 'min':
  83. min_areas = np.minimum(areas1, areas2)
  84. intersect_areas /= min_areas
  85. elif ratio_type == 'ioa':
  86. intersect_areas /= areas2
  87. else:
  88. raise ValueError('Unsupported ratio_type. Got {}'.format(ratio_type))
  89. if ratio_type == 'giou':
  90. # mebb = minimum enclosing bounding boxes
  91. mebb_xy_mins = np.minimum(boxes1[:, :2], boxes2[:, :2])
  92. mebb_xy_maxs = np.maximum(boxes1[:, 2:], boxes2[:, 2:])
  93. mebb_whs = np.maximum(0.0, mebb_xy_maxs - mebb_xy_mins)
  94. mebb_areas = mebb_whs[:, 0] * mebb_whs[:, 1]
  95. union_areas -= mebb_areas
  96. union_areas /= mebb_areas
  97. intersect_areas += union_areas
  98. return intersect_areas
  99. def pairwise_overlap_ratio(boxes1, boxes2, ratio_type='iou'):
  100. """Compute pairwise overlap ratio between boxes.
  101. Args:
  102. boxes1: a numpy array with shape [N, 4] holding N boxes
  103. boxes2: a numpy array with shape [M, 4] holding M boxes
  104. ratio_type:
  105. iou: Intersection-over-union (iou).
  106. ioa: Intersection-over-area (ioa) between two boxes box1 and box2 is defined as
  107. their intersection area over box2's area. Note that ioa is not symmetric,
  108. that is, IOA(box1, box2) != IOA(box2, box1).
  109. min: Compute the ratio as the area of intersection between box1 and box2,
  110. divided by the minimum area of the two bounding boxes.
  111. Returns:
  112. a numpy array with shape [N, M] representing pairwise overlap ratio.
  113. References:
  114. `utils.np_box_ops.iou` in Tensorflow object detection API
  115. `utils.np_box_ops.ioa` in Tensorflow object detection API
  116. `utils.np_box_ops.giou` in Tensorflow object detection API
  117. `mmdet.core.bbox.bbox_overlaps`, see https://mmdetection.readthedocs.io/en/v2.17.0/api.html#mmdet.core.bbox.bbox_overlaps
  118. `torchvision.ops.box_iou`, see https://pytorch.org/vision/stable/ops.html#torchvision.ops.box_iou
  119. `torchvision.ops.generalized_box_iou`, see https://pytorch.org/vision/stable/ops.html#torchvision.ops.generalized_box_iou
  120. http://ww2.mathworks.cn/help/vision/ref/bboxoverlapratio.html
  121. """
  122. intersect_areas = pairwise_intersection(boxes1, boxes2)
  123. areas1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1])
  124. areas2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1])
  125. if ratio_type in ['union', 'iou', 'giou']:
  126. union_areas = np.expand_dims(areas1, axis=1) - intersect_areas
  127. union_areas += np.expand_dims(areas2, axis=0)
  128. intersect_areas /= union_areas
  129. elif ratio_type == 'min':
  130. min_areas = np.minimum(np.expand_dims(areas1, axis=1), np.expand_dims(areas2, axis=0))
  131. intersect_areas /= min_areas
  132. elif ratio_type == 'ioa':
  133. intersect_areas /= np.expand_dims(areas2, axis=0)
  134. else:
  135. raise ValueError('Unsupported ratio_type. Got {}'.format(ratio_type))
  136. if ratio_type == 'giou':
  137. # mebb = minimum enclosing bounding boxes
  138. mebb_xy_mins = np.minimum(boxes1[:, None, :2], boxes2[:, :2])
  139. mebb_xy_maxs = np.maximum(boxes1[:, None, 2:], boxes2[:, 2:])
  140. mebb_whs = np.maximum(0.0, mebb_xy_maxs - mebb_xy_mins)
  141. mebb_areas = mebb_whs[:, :, 0] * mebb_whs[:, :, 1]
  142. union_areas -= mebb_areas
  143. union_areas /= mebb_areas
  144. intersect_areas += union_areas
  145. return intersect_areas