Source code for gluoncv.utils.bbox

"""Calculate Intersection-Over-Union(IOU) of two bounding boxes."""
from __future__ import division

import numpy as np


[docs]def bbox_iou(bbox_a, bbox_b, offset=0): """Calculate Intersection-Over-Union(IOU) of two bounding boxes. Parameters ---------- bbox_a : numpy.ndarray An ndarray with shape :math:`(N, 4)`. bbox_b : numpy.ndarray An ndarray with shape :math:`(M, 4)`. offset : float or int, default is 0 The ``offset`` is used to control the whether the width(or height) is computed as (right - left + ``offset``). Note that the offset must be 0 for normalized bboxes, whose ranges are in ``[0, 1]``. Returns ------- numpy.ndarray An ndarray with shape :math:`(N, M)` indicates IOU between each pairs of bounding boxes in `bbox_a` and `bbox_b`. """ if bbox_a.shape[1] < 4 or bbox_b.shape[1] < 4: raise IndexError("Bounding boxes axis 1 must have at least length 4") tl = np.maximum(bbox_a[:, None, :2], bbox_b[:, :2]) br = np.minimum(bbox_a[:, None, 2:4], bbox_b[:, 2:4]) area_i = np.prod(br - tl + offset, axis=2) * (tl < br).all(axis=2) area_a = np.prod(bbox_a[:, 2:4] - bbox_a[:, :2] + offset, axis=1) area_b = np.prod(bbox_b[:, 2:4] - bbox_b[:, :2] + offset, axis=1) return area_i / (area_a[:, None] + area_b - area_i)
def bbox_xywh_to_xyxy(xywh): """Convert bounding boxes from format (xmin, ymin, w, h) to (xmin, ymin, xmax, ymax) Parameters ---------- xywh : list, tuple or numpy.ndarray The bbox in format (x, y, w, h). If numpy.ndarray is provided, we expect multiple bounding boxes with shape `(N, 4)`. Returns ------- tuple or numpy.ndarray The converted bboxes in format (xmin, ymin, xmax, ymax). If input is numpy.ndarray, return is numpy.ndarray correspondingly. """ if isinstance(xywh, (tuple, list)): if not len(xywh) == 4: raise IndexError( "Bounding boxes must have 4 elements, given {}".format(len(xywh))) w, h = np.maximum(xywh[2] - 1, 0), np.maximum(xywh[3] - 1, 0) return xywh[0], xywh[1], xywh[0] + w, xywh[1] + h elif isinstance(xywh, np.ndarray): if not xywh.size % 4 == 0: raise IndexError( "Bounding boxes must have n * 4 elements, given {}".format(xywh.shape)) xyxy = np.hstack((xywh[:, :2], xywh[:, :2] + np.maximum(0, xywh[:, 2:4] - 1))) return xyxy else: raise TypeError( 'Expect input xywh a list, tuple or numpy.ndarray, given {}'.format(type(xywh))) def bbox_xyxy_to_xywh(xyxy): """Convert bounding boxes from format (xmin, ymin, xmax, ymax) to (x, y, w, h). Parameters ---------- xyxy : list, tuple or numpy.ndarray The bbox in format (xmin, ymin, xmax, ymax). If numpy.ndarray is provided, we expect multiple bounding boxes with shape `(N, 4)`. Returns ------- tuple or numpy.ndarray The converted bboxes in format (x, y, w, h). If input is numpy.ndarray, return is numpy.ndarray correspondingly. """ if isinstance(xyxy, (tuple, list)): if not len(xyxy) == 4: raise IndexError( "Bounding boxes must have 4 elements, given {}".format(len(xyxy))) x1, y1 = xyxy[0], xyxy[1] w, h = xyxy[2] - x1 + 1, xyxy[3] - y1 + 1 return x1, y1, w, h elif isinstance(xyxy, np.ndarray): if not xyxy.size % 4 == 0: raise IndexError( "Bounding boxes must have n * 4 elements, given {}".format(xyxy.shape)) return np.hstack((xyxy[:, :2], xyxy[:, 2:4] - xyxy[:, :2] + 1)) else: raise TypeError( 'Expect input xywh a list, tuple or numpy.ndarray, given {}'.format(type(xyxy))) def bbox_clip_xyxy(xyxy, width, height): """Clip bounding box with format (xmin, ymin, xmax, ymax) to specified boundary. All bounding boxes will be clipped to the new region `(0, 0, width, height)`. Parameters ---------- xyxy : list, tuple or numpy.ndarray The bbox in format (xmin, ymin, xmax, ymax). If numpy.ndarray is provided, we expect multiple bounding boxes with shape `(N, 4)`. width : int or float Boundary width. height : int or float Boundary height. Returns ------- type Description of returned object. """ if isinstance(xyxy, (tuple, list)): if not len(xyxy) == 4: raise IndexError( "Bounding boxes must have 4 elements, given {}".format(len(xyxy))) x1 = np.minimum(width - 1, np.maximum(0, xyxy[0])) y1 = np.minimum(height - 1, np.maximum(0, xyxy[1])) x2 = np.minimum(width - 1, np.maximum(0, xyxy[2])) y2 = np.minimum(height - 1, np.maximum(0, xyxy[3])) return x1, y1, x2, y2 elif isinstance(xyxy, np.ndarray): if not xyxy.size % 4 == 0: raise IndexError( "Bounding boxes must have n * 4 elements, given {}".format(xyxy.shape)) x1 = np.minimum(width - 1, np.maximum(0, xyxy[:, 0])) y1 = np.minimum(height - 1, np.maximum(0, xyxy[:, 1])) x2 = np.minimum(width - 1, np.maximum(0, xyxy[:, 2])) y2 = np.minimum(height - 1, np.maximum(0, xyxy[:, 3])) return np.hstack((x1, y1, x2, y2)) else: raise TypeError( 'Expect input xywh a list, tuple or numpy.ndarray, given {}'.format(type(xyxy)))