insecta / khandy /boxes /boxes_overlap.py
admin
sync
67a9b5d
import numpy as np
def paired_intersection(boxes1, boxes2):
"""Compute paired intersection areas between boxes.
Args:
boxes1: a numpy array with shape [N, 4] holding N boxes
boxes2: a numpy array with shape [N, 4] holding N boxes
Returns:
a numpy array with shape [N,] representing itemwise intersection area
References:
`core.box_list_ops.matched_intersection` in Tensorflow object detection API
Notes:
can called as itemwise_intersection, matched_intersection, aligned_intersection
"""
max_x_mins = np.maximum(boxes1[:, 0], boxes2[:, 0])
max_y_mins = np.maximum(boxes1[:, 1], boxes2[:, 1])
min_x_maxs = np.minimum(boxes1[:, 2], boxes2[:, 2])
min_y_maxs = np.minimum(boxes1[:, 3], boxes2[:, 3])
intersect_widths = np.maximum(0, min_x_maxs - max_x_mins)
intersect_heights = np.maximum(0, min_y_maxs - max_y_mins)
return intersect_widths * intersect_heights
def pairwise_intersection(boxes1, boxes2):
"""Compute pairwise intersection areas between boxes.
Args:
boxes1: a numpy array with shape [N, 4] holding N boxes.
boxes2: a numpy array with shape [M, 4] holding M boxes.
Returns:
a numpy array with shape [N, M] representing pairwise intersection area.
References:
`core.box_list_ops.intersection` in Tensorflow object detection API
`utils.box_list_ops.intersection` in Tensorflow object detection API
"""
if boxes1.shape[0] * boxes2.shape[0] == 0:
return np.zeros((boxes1.shape[0], boxes2.shape[0]), dtype=boxes1.dtype)
swap = False
if boxes1.shape[0] > boxes2.shape[0]:
boxes1, boxes2 = boxes2, boxes1
swap = True
intersect_areas = np.empty((boxes1.shape[0], boxes2.shape[0]), dtype=boxes1.dtype)
for i in range(boxes1.shape[0]):
max_x_mins = np.maximum(boxes1[i, 0], boxes2[:, 0])
max_y_mins = np.maximum(boxes1[i, 1], boxes2[:, 1])
min_x_maxs = np.minimum(boxes1[i, 2], boxes2[:, 2])
min_y_maxs = np.minimum(boxes1[i, 3], boxes2[:, 3])
intersect_widths = np.maximum(0, min_x_maxs - max_x_mins)
intersect_heights = np.maximum(0, min_y_maxs - max_y_mins)
intersect_areas[i, :] = intersect_widths * intersect_heights
if swap:
intersect_areas = intersect_areas.T
return intersect_areas
def paired_overlap_ratio(boxes1, boxes2, ratio_type='iou'):
"""Compute paired overlap ratio between boxes.
Args:
boxes1: a numpy array with shape [N, 4] holding N boxes
boxes2: a numpy array with shape [N, 4] holding N boxes
ratio_type:
iou: Intersection-over-union (iou).
ioa: Intersection-over-area (ioa) between two boxes box1 and box2 is defined as
their intersection area over box2's area. Note that ioa is not symmetric,
that is, IOA(box1, box2) != IOA(box2, box1).
min: Compute the ratio as the area of intersection between box1 and box2,
divided by the minimum area of the two bounding boxes.
Returns:
a numpy array with shape [N,] representing itemwise overlap ratio.
References:
`core.box_list_ops.matched_iou` in Tensorflow object detection API
`structures.boxes.matched_boxlist_iou` in detectron2
`mmdet.core.bbox.bbox_overlaps`, see https://mmdetection.readthedocs.io/en/v2.17.0/api.html#mmdet.core.bbox.bbox_overlaps
"""
intersect_areas = paired_intersection(boxes1, boxes2)
areas1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1])
areas2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1])
if ratio_type in ['union', 'iou', 'giou']:
union_areas = areas1 - intersect_areas
union_areas += areas2
intersect_areas /= union_areas
elif ratio_type == 'min':
min_areas = np.minimum(areas1, areas2)
intersect_areas /= min_areas
elif ratio_type == 'ioa':
intersect_areas /= areas2
else:
raise ValueError('Unsupported ratio_type. Got {}'.format(ratio_type))
if ratio_type == 'giou':
min_xy_mins = np.minimum(boxes1[:, 0:2], boxes2[:, 0:2])
max_xy_mins = np.maximum(boxes1[:, 2:4], boxes2[:, 2:4])
# mebb = minimum enclosing bounding boxes
mebb_whs = np.maximum(0, max_xy_mins - min_xy_mins)
mebb_areas = mebb_whs[:, 0] * mebb_whs[:, 1]
union_areas -= mebb_areas
union_areas /= mebb_areas
intersect_areas += union_areas
return intersect_areas
def pairwise_overlap_ratio(boxes1, boxes2, ratio_type='iou'):
"""Compute pairwise overlap ratio between boxes.
Args:
boxes1: a numpy array with shape [N, 4] holding N boxes
boxes2: a numpy array with shape [M, 4] holding M boxes
ratio_type:
iou: Intersection-over-union (iou).
ioa: Intersection-over-area (ioa) between two boxes box1 and box2 is defined as
their intersection area over box2's area. Note that ioa is not symmetric,
that is, IOA(box1, box2) != IOA(box2, box1).
min: Compute the ratio as the area of intersection between box1 and box2,
divided by the minimum area of the two bounding boxes.
Returns:
a numpy array with shape [N, M] representing pairwise overlap ratio.
References:
`utils.np_box_ops.iou` in Tensorflow object detection API
`utils.np_box_ops.ioa` in Tensorflow object detection API
`utils.np_box_ops.giou` in Tensorflow object detection API
`mmdet.core.bbox.bbox_overlaps`, see https://mmdetection.readthedocs.io/en/v2.17.0/api.html#mmdet.core.bbox.bbox_overlaps
`torchvision.ops.box_iou`, see https://pytorch.org/vision/stable/ops.html#torchvision.ops.box_iou
`torchvision.ops.generalized_box_iou`, see https://pytorch.org/vision/stable/ops.html#torchvision.ops.generalized_box_iou
http://ww2.mathworks.cn/help/vision/ref/bboxoverlapratio.html
"""
intersect_areas = pairwise_intersection(boxes1, boxes2)
areas1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1])
areas2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1])
if ratio_type in ['union', 'iou', 'giou']:
union_areas = np.expand_dims(areas1, axis=1) - intersect_areas
union_areas += np.expand_dims(areas2, axis=0)
intersect_areas /= union_areas
elif ratio_type == 'min':
min_areas = np.minimum(np.expand_dims(areas1, axis=1), np.expand_dims(areas2, axis=0))
intersect_areas /= min_areas
elif ratio_type == 'ioa':
intersect_areas /= np.expand_dims(areas2, axis=0)
else:
raise ValueError('Unsupported ratio_type. Got {}'.format(ratio_type))
if ratio_type == 'giou':
min_xy_mins = np.minimum(boxes1[:, None, 0:2], boxes2[:, 0:2])
max_xy_mins = np.maximum(boxes1[:, None, 2:4], boxes2[:, 2:4])
# mebb = minimum enclosing bounding boxes
mebb_whs = np.maximum(0, max_xy_mins - min_xy_mins)
mebb_areas = mebb_whs[:, :, 0] * mebb_whs[:, :, 1]
union_areas -= mebb_areas
union_areas /= mebb_areas
intersect_areas += union_areas
return intersect_areas