Vocal-Eyes-Fast_api / lib /fpn /box_utils.py
ABDRauf's picture
Upload 77 files
f82a827 verified
import torch
import numpy as np
from torch.nn import functional as F
from lib.fpn.box_intersections_cpu.bbox import bbox_overlaps as bbox_overlaps_np
from lib.fpn.box_intersections_cpu.bbox import bbox_intersections as bbox_intersections_np
def bbox_loss(prior_boxes, deltas, gt_boxes, eps=1e-4, scale_before=1):
"""
Computes the loss for predicting the GT boxes from prior boxes
:param prior_boxes: [num_boxes, 4] (x1, y1, x2, y2)
:param deltas: [num_boxes, 4] (tx, ty, th, tw)
:param gt_boxes: [num_boxes, 4] (x1, y1, x2, y2)
:return:
"""
prior_centers = center_size(prior_boxes) #(cx, cy, w, h)
gt_centers = center_size(gt_boxes) #(cx, cy, w, h)
center_targets = (gt_centers[:, :2] - prior_centers[:, :2]) / prior_centers[:, 2:]
size_targets = torch.log(gt_centers[:, 2:]) - torch.log(prior_centers[:, 2:])
all_targets = torch.cat((center_targets, size_targets), 1)
loss = F.smooth_l1_loss(deltas, all_targets, size_average=False)/(eps + prior_centers.size(0))
return loss
def bbox_preds(boxes, deltas):
"""
Converts "deltas" (predicted by the network) along with prior boxes
into (x1, y1, x2, y2) representation.
:param boxes: Prior boxes, represented as (x1, y1, x2, y2)
:param deltas: Offsets (tx, ty, tw, th)
:param box_strides [num_boxes,] distance apart between boxes. anchor box can't go more than
\pm box_strides/2.0 from its current position. If None then we'll use the widths
and heights
:return: Transformed boxes
"""
if boxes.size(0) == 0:
return boxes
prior_centers = center_size(boxes)
xys = prior_centers[:, :2] + prior_centers[:, 2:] * deltas[:, :2]
whs = torch.exp(deltas[:, 2:]) * prior_centers[:, 2:]
return point_form(torch.cat((xys, whs), 1))
def center_size(boxes):
""" Convert prior_boxes to (cx, cy, w, h)
representation for comparison to center-size form ground truth data.
Args:
boxes: (tensor) point_form boxes
Return:
boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes.
"""
wh = boxes[:, 2:] - boxes[:, :2] + 1.0
if isinstance(boxes, np.ndarray):
return np.column_stack((boxes[:, :2] + 0.5 * wh, wh))
return torch.cat((boxes[:, :2] + 0.5 * wh, wh), 1)
def point_form(boxes):
""" Convert prior_boxes to (xmin, ymin, xmax, ymax)
representation for comparison to point form ground truth data.
Args:
boxes: (tensor) center-size default boxes from priorbox layers.
Return:
boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes.
"""
if isinstance(boxes, np.ndarray):
return np.column_stack((boxes[:, :2] - 0.5 * boxes[:, 2:],
boxes[:, :2] + 0.5 * (boxes[:, 2:] - 2.0)))
return torch.cat((boxes[:, :2] - 0.5 * boxes[:, 2:],
boxes[:, :2] + 0.5 * (boxes[:, 2:] - 2.0)), 1) # xmax, ymax
###########################################################################
### Torch Utils, creds to Max de Groot
###########################################################################
def bbox_intersections(box_a, box_b):
""" We resize both tensors to [A,B,2.0] without new malloc:
[A,2.0] -> [A,ĺeftright,2.0] -> [A,B,2.0]
[B,2.0] -> [ĺeftright,B,2.0] -> [A,B,2.0]
Then we compute the area of intersect between box_a and box_b.
Args:
box_a: (tensor) bounding boxes, Shape: [A,4].
box_b: (tensor) bounding boxes, Shape: [B,4].
Return:
(tensor) intersection area, Shape: [A,B].
"""
if isinstance(box_a, np.ndarray):
assert isinstance(box_b, np.ndarray)
return bbox_intersections_np(box_a, box_b)
A = box_a.size(0)
B = box_b.size(0)
max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2),
box_b[:, 2:].unsqueeze(0).expand(A, B, 2))
min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2),
box_b[:, :2].unsqueeze(0).expand(A, B, 2))
inter = torch.clamp((max_xy - min_xy + 1.0), min=0)
return inter[:, :, 0] * inter[:, :, 1]
def bbox_overlaps(box_a, box_b):
"""Compute the jaccard overlap of two sets of boxes. The jaccard overlap
is simply the intersection over union of two boxes. Here we operate on
ground truth boxes and default boxes.
E.g.:
A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B)
Args:
box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4]
box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4]
Return:
jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)]
"""
if isinstance(box_a, np.ndarray):
assert isinstance(box_b, np.ndarray)
return bbox_overlaps_np(box_a, box_b)
inter = bbox_intersections(box_a, box_b)
area_a = ((box_a[:, 2] - box_a[:, 0] + 1.0) *
(box_a[:, 3] - box_a[:, 1] + 1.0)).unsqueeze(1).expand_as(inter) # [A,B]
area_b = ((box_b[:, 2] - box_b[:, 0] + 1.0) *
(box_b[:, 3] - box_b[:, 1] + 1.0)).unsqueeze(0).expand_as(inter) # [A,B]
union = area_a + area_b - inter
return inter / union # [A,B]
def nms_overlaps(boxes):
""" get overlaps for each channel"""
assert boxes.dim() == 3
N = boxes.size(0)
nc = boxes.size(1)
max_xy = torch.min(boxes[:, None, :, 2:].expand(N, N, nc, 2),
boxes[None, :, :, 2:].expand(N, N, nc, 2))
min_xy = torch.max(boxes[:, None, :, :2].expand(N, N, nc, 2),
boxes[None, :, :, :2].expand(N, N, nc, 2))
inter = torch.clamp((max_xy - min_xy + 1.0), min=0)
# n, n, 151
inters = inter[:,:,:,0]*inter[:,:,:,1]
boxes_flat = boxes.view(-1, 4)
areas_flat = (boxes_flat[:,2]- boxes_flat[:,0]+1.0)*(
boxes_flat[:,3]- boxes_flat[:,1]+1.0)
areas = areas_flat.view(boxes.size(0), boxes.size(1))
union = -inters + areas[None] + areas[:, None]
return inters / union