Shortcuts

Source code for mmrotate.core.bbox.transforms

# Copyright (c) OpenMMLab. All rights reserved.
import math

import cv2
import numpy as np
import torch


def bbox_flip(bboxes, img_shape, direction='horizontal'):
    """Flip bboxes horizontally or vertically.

    Args:
        bboxes (Tensor): Shape (..., 5*k)
        img_shape (tuple): Image shape.
        direction (str): Flip direction, options are "horizontal", "vertical",
            "diagonal". Default: "horizontal"

    Returns:
        Tensor: Flipped bboxes.
    """
    version = 'oc'
    assert bboxes.shape[-1] % 5 == 0
    assert direction in ['horizontal', 'vertical', 'diagonal']
    flipped = bboxes.clone()
    if direction == 'horizontal':
        flipped[:, 0] = img_shape[1] - bboxes[:, 0] - 1
    elif direction == 'vertical':
        flipped[:, 1] = img_shape[0] - bboxes[:, 1] - 1
    else:
        flipped[:, 0] = img_shape[1] - bboxes[:, 0] - 1
        flipped[:, 1] = img_shape[0] - bboxes[:, 1] - 1
    if version == 'oc':
        rotated_flag = (bboxes[:, 4] != np.pi / 2)
        flipped[rotated_flag, 4] = np.pi / 2 - bboxes[rotated_flag, 4]
        flipped[rotated_flag, 2] = bboxes[rotated_flag, 3]
        flipped[rotated_flag, 3] = bboxes[rotated_flag, 2]
    else:
        flipped[:, 4] = norm_angle(np.pi - bboxes[:, 4], version)
    return flipped


[docs]def bbox_mapping_back(bboxes, img_shape, scale_factor, flip, flip_direction='horizontal'): """Map bboxes from testing scale to original image scale.""" new_bboxes = bbox_flip(bboxes, img_shape, flip_direction) if flip else bboxes new_bboxes[:, :4] = new_bboxes[:, :4] / new_bboxes.new_tensor(scale_factor) return new_bboxes.view(bboxes.shape)
[docs]def rbbox2result(bboxes, labels, num_classes): """Convert detection results to a list of numpy arrays. Args: bboxes (torch.Tensor): shape (n, 6) labels (torch.Tensor): shape (n, ) num_classes (int): class number, including background class Returns: list(ndarray): bbox results of each class """ if bboxes.shape[0] == 0: return [np.zeros((0, 6), dtype=np.float32) for _ in range(num_classes)] else: bboxes = bboxes.cpu().numpy() labels = labels.cpu().numpy() return [bboxes[labels == i, :] for i in range(num_classes)]
[docs]def rbbox2roi(bbox_list): """Convert a list of bboxes to roi format. Args: bbox_list (list[Tensor]): a list of bboxes corresponding to a batch of images. Returns: Tensor: shape (n, 6), [batch_ind, cx, cy, w, h, a] """ rois_list = [] for img_id, bboxes in enumerate(bbox_list): if bboxes.size(0) > 0: img_inds = bboxes.new_full((bboxes.size(0), 1), img_id) rois = torch.cat([img_inds, bboxes[:, :5]], dim=-1) else: rois = bboxes.new_zeros((0, 6)) rois_list.append(rois) rois = torch.cat(rois_list, 0) return rois
[docs]def poly2obb(polys, version='oc'): """Convert polygons to oriented bounding boxes. Args: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] version (Str): angle representations. Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ if version == 'oc': results = poly2obb_oc(polys) elif version == 'le135': results = poly2obb_le135(polys) elif version == 'le90': results = poly2obb_le90(polys) else: raise NotImplementedError return results
[docs]def poly2obb_np(polys, version='oc'): """Convert polygons to oriented bounding boxes. Args: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3] version (Str): angle representations. Returns: obbs (ndarray): [x_ctr,y_ctr,w,h,angle] """ if version == 'oc': results = poly2obb_np_oc(polys) elif version == 'le135': results = poly2obb_np_le135(polys) elif version == 'le90': results = poly2obb_np_le90(polys) else: raise NotImplementedError return results
[docs]def obb2hbb(rbboxes, version='oc'): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] version (Str): angle representations. Returns: hbbs (torch.Tensor): [x_ctr,y_ctr,w,h,-pi/2] """ if version == 'oc': results = obb2hbb_oc(rbboxes) elif version == 'le135': results = obb2hbb_le135(rbboxes) elif version == 'le90': results = obb2hbb_le90(rbboxes) else: raise NotImplementedError return results
[docs]def obb2poly(rbboxes, version='oc'): """Convert oriented bounding boxes to polygons. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] version (Str): angle representations. Returns: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] """ if version == 'oc': results = obb2poly_oc(rbboxes) elif version == 'le135': results = obb2poly_le135(rbboxes) elif version == 'le90': results = obb2poly_le90(rbboxes) else: raise NotImplementedError return results
[docs]def obb2poly_np(rbboxes, version='oc'): """Convert oriented bounding boxes to polygons. Args: obbs (ndarray): [x_ctr,y_ctr,w,h,angle] version (Str): angle representations. Returns: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3] """ if version == 'oc': results = obb2poly_np_oc(rbboxes) elif version == 'le135': results = obb2poly_np_le135(rbboxes) elif version == 'le90': results = obb2poly_np_le90(rbboxes) else: raise NotImplementedError return results
[docs]def obb2xyxy(rbboxes, version='oc'): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] version (Str): angle representations. Returns: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] """ if version == 'oc': results = obb2xyxy_oc(rbboxes) elif version == 'le135': results = obb2xyxy_le135(rbboxes) elif version == 'le90': results = obb2xyxy_le90(rbboxes) else: raise NotImplementedError return results
[docs]def hbb2obb(hbboxes, version='oc'): """Convert horizontal bounding boxes to oriented bounding boxes. Args: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] version (Str): angle representations. Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ if version == 'oc': results = hbb2obb_oc(hbboxes) elif version == 'le135': results = hbb2obb_le135(hbboxes) elif version == 'le90': results = hbb2obb_le90(hbboxes) else: raise NotImplementedError return results
def poly2obb_oc(polys): """Convert polygons to oriented bounding boxes. Args: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ points = torch.reshape(polys, [-1, 4, 2]) cxs = torch.unsqueeze(torch.sum(points[:, :, 0], axis=1), axis=1) / 4. cys = torch.unsqueeze(torch.sum(points[:, :, 1], axis=1), axis=1) / 4. _ws = torch.unsqueeze(dist_torch(points[:, 0], points[:, 1]), axis=1) _hs = torch.unsqueeze(dist_torch(points[:, 1], points[:, 2]), axis=1) _thetas = torch.unsqueeze( torch.atan2(-(points[:, 1, 0] - points[:, 0, 0]), points[:, 1, 1] - points[:, 0, 1]), axis=1) odd = torch.eq(torch.remainder((_thetas / (np.pi * 0.5)).floor_(), 2), 0) ws = torch.where(odd, _hs, _ws) hs = torch.where(odd, _ws, _hs) thetas = torch.remainder(_thetas, np.pi * 0.5) rbboxes = torch.cat([cxs, cys, ws, hs, thetas], axis=1) return rbboxes def poly2obb_le135(polys): """Convert polygons to oriented bounding boxes. Args: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ polys = torch.reshape(polys, [-1, 8]) pt1, pt2, pt3, pt4 = polys[..., :8].chunk(4, 1) edge1 = torch.sqrt( torch.pow(pt1[..., 0] - pt2[..., 0], 2) + torch.pow(pt1[..., 1] - pt2[..., 1], 2)) edge2 = torch.sqrt( torch.pow(pt2[..., 0] - pt3[..., 0], 2) + torch.pow(pt2[..., 1] - pt3[..., 1], 2)) angles1 = torch.atan2((pt2[..., 1] - pt1[..., 1]), (pt2[..., 0] - pt1[..., 0])) angles2 = torch.atan2((pt4[..., 1] - pt1[..., 1]), (pt4[..., 0] - pt1[..., 0])) angles = polys.new_zeros(polys.shape[0]) angles[edge1 > edge2] = angles1[edge1 > edge2] angles[edge1 <= edge2] = angles2[edge1 <= edge2] angles = norm_angle(angles, 'le135') x_ctr = (pt1[..., 0] + pt3[..., 0]) / 2.0 y_ctr = (pt1[..., 1] + pt3[..., 1]) / 2.0 edges = torch.stack([edge1, edge2], dim=1) width, _ = torch.max(edges, 1) height, _ = torch.min(edges, 1) return torch.stack([x_ctr, y_ctr, width, height, angles], 1) def poly2obb_le90(polys): """Convert polygons to oriented bounding boxes. Args: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ polys = torch.reshape(polys, [-1, 8]) pt1, pt2, pt3, pt4 = polys[..., :8].chunk(4, 1) edge1 = torch.sqrt( torch.pow(pt1[..., 0] - pt2[..., 0], 2) + torch.pow(pt1[..., 1] - pt2[..., 1], 2)) edge2 = torch.sqrt( torch.pow(pt2[..., 0] - pt3[..., 0], 2) + torch.pow(pt2[..., 1] - pt3[..., 1], 2)) angles1 = torch.atan2((pt2[..., 1] - pt1[..., 1]), (pt2[..., 0] - pt1[..., 0])) angles2 = torch.atan2((pt4[..., 1] - pt1[..., 1]), (pt4[..., 0] - pt1[..., 0])) angles = polys.new_zeros(polys.shape[0]) angles[edge1 > edge2] = angles1[edge1 > edge2] angles[edge1 <= edge2] = angles2[edge1 <= edge2] angles = norm_angle(angles, 'le90') x_ctr = (pt1[..., 0] + pt3[..., 0]) / 2.0 y_ctr = (pt1[..., 1] + pt3[..., 1]) / 2.0 edges = torch.stack([edge1, edge2], dim=1) width, _ = torch.max(edges, 1) height, _ = torch.min(edges, 1) return torch.stack([x_ctr, y_ctr, width, height, angles], 1) def poly2obb_np_oc(poly): """Convert polygons to oriented bounding boxes. Args: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3] Returns: obbs (ndarray): [x_ctr,y_ctr,w,h,angle] """ bboxps = np.array(poly).reshape((4, 2)) rbbox = cv2.minAreaRect(bboxps) x, y, w, h, a = rbbox[0][0], rbbox[0][1], rbbox[1][0], rbbox[1][1], rbbox[ 2] if w < 2 or h < 2: return while not 0 < a <= 90: if a == -90: a += 180 else: a += 90 w, h = h, w a = a / 180 * np.pi assert 0 < a <= np.pi / 2 return x, y, w, h, a def poly2obb_np_le135(poly): """Convert polygons to oriented bounding boxes. Args: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3] Returns: obbs (ndarray): [x_ctr,y_ctr,w,h,angle] """ poly = np.array(poly[:8], dtype=np.float32) pt1 = (poly[0], poly[1]) pt2 = (poly[2], poly[3]) pt3 = (poly[4], poly[5]) pt4 = (poly[6], poly[7]) edge1 = np.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) edge2 = np.sqrt((pt2[0] - pt3[0]) * (pt2[0] - pt3[0]) + (pt2[1] - pt3[1]) * (pt2[1] - pt3[1])) if edge1 < 2 or edge2 < 2: return width = max(edge1, edge2) height = min(edge1, edge2) angle = 0 if edge1 > edge2: angle = np.arctan2(float(pt2[1] - pt1[1]), float(pt2[0] - pt1[0])) elif edge2 >= edge1: angle = np.arctan2(float(pt4[1] - pt1[1]), float(pt4[0] - pt1[0])) angle = norm_angle(angle, 'le135') x_ctr = float(pt1[0] + pt3[0]) / 2 y_ctr = float(pt1[1] + pt3[1]) / 2 return x_ctr, y_ctr, width, height, angle def poly2obb_np_le90(poly): """Convert polygons to oriented bounding boxes. Args: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3] Returns: obbs (ndarray): [x_ctr,y_ctr,w,h,angle] """ bboxps = np.array(poly).reshape((4, 2)) rbbox = cv2.minAreaRect(bboxps) x, y, w, h, a = rbbox[0][0], rbbox[0][1], rbbox[1][0], rbbox[1][1], rbbox[ 2] if w < 2 or h < 2: return a = a / 180 * np.pi if w < h: w, h = h, w a += np.pi / 2 while not np.pi / 2 > a >= -np.pi / 2: if a >= np.pi / 2: a -= np.pi else: a += np.pi assert np.pi / 2 > a >= -np.pi / 2 return x, y, w, h, a def obb2poly_oc(rboxes): """Convert oriented bounding boxes to polygons. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] """ x = rboxes[:, 0] y = rboxes[:, 1] w = rboxes[:, 2] h = rboxes[:, 3] a = rboxes[:, 4] cosa = torch.cos(a) sina = torch.sin(a) wx, wy = w / 2 * cosa, w / 2 * sina hx, hy = -h / 2 * sina, h / 2 * cosa p1x, p1y = x - wx - hx, y - wy - hy p2x, p2y = x + wx - hx, y + wy - hy p3x, p3y = x + wx + hx, y + wy + hy p4x, p4y = x - wx + hx, y - wy + hy return torch.stack([p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y], dim=-1) def obb2poly_le135(rboxes): """Convert oriented bounding boxes to polygons. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] """ N = rboxes.shape[0] if N == 0: return rboxes.new_zeros((rboxes.size(0), 8)) x_ctr, y_ctr, width, height, angle = rboxes.select(1, 0), rboxes.select( 1, 1), rboxes.select(1, 2), rboxes.select(1, 3), rboxes.select(1, 4) tl_x, tl_y, br_x, br_y = \ -width * 0.5, -height * 0.5, \ width * 0.5, height * 0.5 rects = torch.stack([tl_x, br_x, br_x, tl_x, tl_y, tl_y, br_y, br_y], dim=0).reshape(2, 4, N).permute(2, 0, 1) sin, cos = torch.sin(angle), torch.cos(angle) M = torch.stack([cos, -sin, sin, cos], dim=0).reshape(2, 2, N).permute(2, 0, 1) polys = M.matmul(rects).permute(2, 1, 0).reshape(-1, N).transpose(1, 0) polys[:, ::2] += x_ctr.unsqueeze(1) polys[:, 1::2] += y_ctr.unsqueeze(1) return polys.contiguous() def obb2poly_le90(rboxes): """Convert oriented bounding boxes to polygons. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: polys (torch.Tensor): [x0,y0,x1,y1,x2,y2,x3,y3] """ N = rboxes.shape[0] if N == 0: return rboxes.new_zeros((rboxes.size(0), 8)) x_ctr, y_ctr, width, height, angle = rboxes.select(1, 0), rboxes.select( 1, 1), rboxes.select(1, 2), rboxes.select(1, 3), rboxes.select(1, 4) tl_x, tl_y, br_x, br_y = \ -width * 0.5, -height * 0.5, \ width * 0.5, height * 0.5 rects = torch.stack([tl_x, br_x, br_x, tl_x, tl_y, tl_y, br_y, br_y], dim=0).reshape(2, 4, N).permute(2, 0, 1) sin, cos = torch.sin(angle), torch.cos(angle) M = torch.stack([cos, -sin, sin, cos], dim=0).reshape(2, 2, N).permute(2, 0, 1) polys = M.matmul(rects).permute(2, 1, 0).reshape(-1, N).transpose(1, 0) polys[:, ::2] += x_ctr.unsqueeze(1) polys[:, 1::2] += y_ctr.unsqueeze(1) return polys.contiguous() def obb2hbb_oc(rbboxes): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: hbbs (torch.Tensor): [x_ctr,y_ctr,w,h,pi/2] """ w = rbboxes[:, 2::5] h = rbboxes[:, 3::5] a = rbboxes[:, 4::5] cosa = torch.cos(a) sina = torch.sin(a) hbbox_w = cosa * w + sina * h hbbox_h = sina * w + cosa * h hbboxes = rbboxes.clone().detach() hbboxes[:, 2::5] = hbbox_h hbboxes[:, 3::5] = hbbox_w hbboxes[:, 4::5] = np.pi / 2 return hbboxes def obb2hbb_le135(rotatex_boxes): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: hbbs (torch.Tensor): [x_ctr,y_ctr,w,h,-pi/2] """ polys = obb2poly_le135(rotatex_boxes) xmin, _ = polys[:, ::2].min(1) ymin, _ = polys[:, 1::2].min(1) xmax, _ = polys[:, ::2].max(1) ymax, _ = polys[:, 1::2].max(1) bboxes = torch.stack([xmin, ymin, xmax, ymax], dim=1) x_ctr = (bboxes[:, 2] + bboxes[:, 0]) / 2.0 y_ctr = (bboxes[:, 3] + bboxes[:, 1]) / 2.0 edges1 = torch.abs(bboxes[:, 2] - bboxes[:, 0]) edges2 = torch.abs(bboxes[:, 3] - bboxes[:, 1]) angles = bboxes.new_zeros(bboxes.size(0)) inds = edges1 < edges2 rotated_boxes = torch.stack((x_ctr, y_ctr, edges1, edges2, angles), dim=1) rotated_boxes[inds, 2] = edges2[inds] rotated_boxes[inds, 3] = edges1[inds] rotated_boxes[inds, 4] = np.pi / 2.0 return rotated_boxes def obb2hbb_le90(obboxes): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: hbbs (torch.Tensor): [x_ctr,y_ctr,w,h,-pi/2] """ center, w, h, theta = torch.split(obboxes, [2, 1, 1, 1], dim=-1) Cos, Sin = torch.cos(theta), torch.sin(theta) x_bias = torch.abs(w / 2 * Cos) + torch.abs(h / 2 * Sin) y_bias = torch.abs(w / 2 * Sin) + torch.abs(h / 2 * Cos) bias = torch.cat([x_bias, y_bias], dim=-1) hbboxes = torch.cat([center - bias, center + bias], dim=-1) _x = (hbboxes[..., 0] + hbboxes[..., 2]) * 0.5 _y = (hbboxes[..., 1] + hbboxes[..., 3]) * 0.5 _w = hbboxes[..., 2] - hbboxes[..., 0] _h = hbboxes[..., 3] - hbboxes[..., 1] _theta = theta.new_zeros(theta.size(0)) obboxes1 = torch.stack([_x, _y, _w, _h, _theta], dim=-1) obboxes2 = torch.stack([_x, _y, _h, _w, _theta - np.pi / 2], dim=-1) obboxes = torch.where((_w >= _h)[..., None], obboxes1, obboxes2) return obboxes def hbb2obb_oc(hbboxes): """Convert horizontal bounding boxes to oriented bounding boxes. Args: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ x = (hbboxes[..., 0] + hbboxes[..., 2]) * 0.5 y = (hbboxes[..., 1] + hbboxes[..., 3]) * 0.5 w = hbboxes[..., 2] - hbboxes[..., 0] h = hbboxes[..., 3] - hbboxes[..., 1] theta = x.new_zeros(*x.shape) rbboxes = torch.stack([x, y, h, w, theta + np.pi / 2], dim=-1) return rbboxes def hbb2obb_le135(hbboxes): """Convert horizontal bounding boxes to oriented bounding boxes. Args: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ x = (hbboxes[..., 0] + hbboxes[..., 2]) * 0.5 y = (hbboxes[..., 1] + hbboxes[..., 3]) * 0.5 w = hbboxes[..., 2] - hbboxes[..., 0] h = hbboxes[..., 3] - hbboxes[..., 1] theta = x.new_zeros(*x.shape) obboxes1 = torch.stack([x, y, w, h, theta], dim=-1) obboxes2 = torch.stack([x, y, h, w, theta + np.pi / 2], dim=-1) obboxes = torch.where((w >= h)[..., None], obboxes1, obboxes2) return obboxes def hbb2obb_le90(hbboxes): """Convert horizontal bounding boxes to oriented bounding boxes. Args: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] Returns: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] """ x = (hbboxes[..., 0] + hbboxes[..., 2]) * 0.5 y = (hbboxes[..., 1] + hbboxes[..., 3]) * 0.5 w = hbboxes[..., 2] - hbboxes[..., 0] h = hbboxes[..., 3] - hbboxes[..., 1] theta = x.new_zeros(*x.shape) obboxes1 = torch.stack([x, y, w, h, theta], dim=-1) obboxes2 = torch.stack([x, y, h, w, theta - np.pi / 2], dim=-1) obboxes = torch.where((w >= h)[..., None], obboxes1, obboxes2) return obboxes def obb2xyxy_oc(rbboxes): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] """ w = rbboxes[:, 2::5] h = rbboxes[:, 3::5] a = rbboxes[:, 4::5] cosa = torch.cos(a) sina = torch.sin(a) hbbox_w = cosa * w + sina * h hbbox_h = sina * w + cosa * h # pi/2 >= a > 0, so cos(a)>0, sin(a)>0 dx = rbboxes[..., 0] dy = rbboxes[..., 1] dw = hbbox_w.reshape(-1) dh = hbbox_h.reshape(-1) x1 = dx - dw / 2 y1 = dy - dh / 2 x2 = dx + dw / 2 y2 = dy + dh / 2 return torch.stack((x1, y1, x2, y2), -1) def obb2xyxy_le135(rotatex_boxes): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] """ N = rotatex_boxes.shape[0] if N == 0: return rotatex_boxes.new_zeros((rotatex_boxes.size(0), 4)) polys = obb2poly_le135(rotatex_boxes) xmin, _ = polys[:, ::2].min(1) ymin, _ = polys[:, 1::2].min(1) xmax, _ = polys[:, ::2].max(1) ymax, _ = polys[:, 1::2].max(1) return torch.stack([xmin, ymin, xmax, ymax], dim=1) def obb2xyxy_le90(obboxes): """Convert oriented bounding boxes to horizontal bounding boxes. Args: obbs (torch.Tensor): [x_ctr,y_ctr,w,h,angle] Returns: hbbs (torch.Tensor): [x_lt,y_lt,x_rb,y_rb] """ # N = obboxes.shape[0] # if N == 0: # return obboxes.new_zeros((obboxes.size(0), 4)) center, w, h, theta = torch.split(obboxes, [2, 1, 1, 1], dim=-1) Cos, Sin = torch.cos(theta), torch.sin(theta) x_bias = torch.abs(w / 2 * Cos) + torch.abs(h / 2 * Sin) y_bias = torch.abs(w / 2 * Sin) + torch.abs(h / 2 * Cos) bias = torch.cat([x_bias, y_bias], dim=-1) return torch.cat([center - bias, center + bias], dim=-1) def obb2poly_np_oc(rbboxes): """Convert oriented bounding boxes to polygons. Args: obbs (ndarray): [x_ctr,y_ctr,w,h,angle,score] Returns: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3,score] """ x = rbboxes[:, 0] y = rbboxes[:, 1] w = rbboxes[:, 2] h = rbboxes[:, 3] a = rbboxes[:, 4] score = rbboxes[:, 5] cosa = np.cos(a) sina = np.sin(a) wx, wy = w / 2 * cosa, w / 2 * sina hx, hy = -h / 2 * sina, h / 2 * cosa p1x, p1y = x - wx - hx, y - wy - hy p2x, p2y = x + wx - hx, y + wy - hy p3x, p3y = x + wx + hx, y + wy + hy p4x, p4y = x - wx + hx, y - wy + hy polys = np.stack([p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, score], axis=-1) polys = get_best_begin_point(polys) return polys def obb2poly_np_le135(rrects): """Convert oriented bounding boxes to polygons. Args: obbs (ndarray): [x_ctr,y_ctr,w,h,angle,score] Returns: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3,score] """ polys = [] for rrect in rrects: x_ctr, y_ctr, width, height, angle, score = rrect[:6] tl_x, tl_y, br_x, br_y = -width / 2, -height / 2, width / 2, height / 2 rect = np.array([[tl_x, br_x, br_x, tl_x], [tl_y, tl_y, br_y, br_y]]) R = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) poly = R.dot(rect) x0, x1, x2, x3 = poly[0, :4] + x_ctr y0, y1, y2, y3 = poly[1, :4] + y_ctr poly = np.array([x0, y0, x1, y1, x2, y2, x3, y3, score], dtype=np.float32) polys.append(poly) polys = np.array(polys) polys = get_best_begin_point(polys) return polys def obb2poly_np_le90(obboxes): """Convert oriented bounding boxes to polygons. Args: obbs (ndarray): [x_ctr,y_ctr,w,h,angle,score] Returns: polys (ndarray): [x0,y0,x1,y1,x2,y2,x3,y3,score] """ try: center, w, h, theta, score = np.split(obboxes, (2, 3, 4, 5), axis=-1) except: # noqa: E722 results = np.stack([0., 0., 0., 0., 0., 0., 0., 0., 0.], axis=-1) return results.reshape(1, -1) Cos, Sin = np.cos(theta), np.sin(theta) vector1 = np.concatenate([w / 2 * Cos, w / 2 * Sin], axis=-1) vector2 = np.concatenate([-h / 2 * Sin, h / 2 * Cos], axis=-1) point1 = center - vector1 - vector2 point2 = center + vector1 - vector2 point3 = center + vector1 + vector2 point4 = center - vector1 + vector2 polys = np.concatenate([point1, point2, point3, point4, score], axis=-1) polys = get_best_begin_point(polys) return polys def cal_line_length(point1, point2): """Calculate the length of line. Args: point1 (List): [x,y] point2 (List): [x,y] Returns: length (float) """ return math.sqrt( math.pow(point1[0] - point2[0], 2) + math.pow(point1[1] - point2[1], 2)) def get_best_begin_point_single(coordinate): """Get the best begin point of the single polygon. Args: coordinate (List): [x1, y1, x2, y2, x3, y3, x4, y4, score] Returns: reorder coordinate (List): [x1, y1, x2, y2, x3, y3, x4, y4, score] """ x1, y1, x2, y2, x3, y3, x4, y4, score = coordinate xmin = min(x1, x2, x3, x4) ymin = min(y1, y2, y3, y4) xmax = max(x1, x2, x3, x4) ymax = max(y1, y2, y3, y4) combine = [[[x1, y1], [x2, y2], [x3, y3], [x4, y4]], [[x2, y2], [x3, y3], [x4, y4], [x1, y1]], [[x3, y3], [x4, y4], [x1, y1], [x2, y2]], [[x4, y4], [x1, y1], [x2, y2], [x3, y3]]] dst_coordinate = [[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax]] force = 100000000.0 force_flag = 0 for i in range(4): temp_force = cal_line_length(combine[i][0], dst_coordinate[0]) \ + cal_line_length(combine[i][1], dst_coordinate[1]) \ + cal_line_length(combine[i][2], dst_coordinate[2]) \ + cal_line_length(combine[i][3], dst_coordinate[3]) if temp_force < force: force = temp_force force_flag = i if force_flag != 0: pass return np.hstack( (np.array(combine[force_flag]).reshape(8), np.array(score))) def get_best_begin_point(coordinates): """Get the best begin points of polygons. Args: coordinate (ndarray): shape(n, 9). Returns: reorder coordinate (ndarray): shape(n, 9). """ coordinates = list(map(get_best_begin_point_single, coordinates.tolist())) coordinates = np.array(coordinates) return coordinates
[docs]def norm_angle(angle, angle_range): """Limit the range of angles. Args: angle (ndarray): shape(n, ). angle_range (Str): angle representations. Returns: angle (ndarray): shape(n, ). """ if angle_range == 'oc': return angle elif angle_range == 'le135': return (angle + np.pi / 4) % np.pi - np.pi / 4 elif angle_range == 'le90': return (angle + np.pi / 2) % np.pi - np.pi / 2 else: print('Not yet implemented.')
def dist_torch(point1, point2): """Calculate the distance between two points. Args: point1 (torch.Tensor): shape(n, 2). point2 (torch.Tensor): shape(n, 2). Returns: distance (torch.Tensor): shape(n, 1). """ return torch.norm(point1 - point2, dim=-1)
[docs]def gaussian2bbox(gmm): """Convert Gaussian distribution to polygons by SVD. Args: gmm (dict[str, torch.Tensor]): Dict of Gaussian distribution. Returns: torch.Tensor: Polygons. """ try: from torch_batch_svd import svd except ImportError: svd = None L = 3 var = gmm.var mu = gmm.mu assert mu.size()[1:] == (1, 2) assert var.size()[1:] == (1, 2, 2) T = mu.size()[0] var = var.squeeze(1) if svd is None: raise ImportError('Please install torch_batch_svd first.') U, s, Vt = svd(var) size_half = L * s.sqrt().unsqueeze(1).repeat(1, 4, 1) mu = mu.repeat(1, 4, 1) dx_dy = size_half * torch.tensor([[-1, 1], [1, 1], [1, -1], [-1, -1]], dtype=torch.float32, device=size_half.device) bboxes = (mu + dx_dy.matmul(Vt.transpose(1, 2))).reshape(T, 8) return bboxes
[docs]def gt2gaussian(target): """Convert polygons to Gaussian distributions. Args: target (torch.Tensor): Polygons with shape (N, 8). Returns: dict[str, torch.Tensor]: Gaussian distributions. """ L = 3 center = torch.mean(target, dim=1) edge_1 = target[:, 1, :] - target[:, 0, :] edge_2 = target[:, 2, :] - target[:, 1, :] w = (edge_1 * edge_1).sum(dim=-1, keepdim=True) w_ = w.sqrt() h = (edge_2 * edge_2).sum(dim=-1, keepdim=True) diag = torch.cat([w, h], dim=-1).diag_embed() / (4 * L * L) cos_sin = edge_1 / w_ neg = torch.tensor([[1, -1]], dtype=torch.float32).to(cos_sin.device) R = torch.stack([cos_sin * neg, cos_sin[..., [1, 0]]], dim=-2) return (center, R.matmul(diag).matmul(R.transpose(-1, -2)))
Read the Docs v: v0.3.4
Versions
latest
stable
1.x
v1.0.0rc0
v0.3.4
v0.3.3
v0.3.2
v0.3.1
v0.3.0
v0.2.0
v0.1.1
v0.1.0
main
dev
Downloads
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.