File size: 8,309 Bytes
320c5b2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | # -*- coding: utf-8 -*-
import torch
from itertools import product
from math import sqrt
import numpy as np
def box_iou(box_a, box_b):
"""
Compute the IoU of two sets of boxes.
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)]
"""
use_batch = True
if box_a.dim() == 2:
use_batch = False
box_a = box_a[None, ...]
box_b = box_b[None, ...]
(n, A), B = box_a.shape[:2], box_b.shape[1]
# add a dimension
box_a = box_a[:, :, None, :].expand(n, A, B, 4)
box_b = box_b[:, None, :, :].expand(n, A, B, 4)
max_xy = torch.min(box_a[..., 2:], box_b[..., 2:])
min_xy = torch.max(box_a[..., :2], box_b[..., :2])
inter = torch.clamp((max_xy - min_xy), min=0)
inter_area = inter[..., 0] * inter[..., 1]
area_a = (box_a[..., 2] - box_a[..., 0]) * (box_a[..., 3] - box_a[..., 1])
area_b = (box_b[..., 2] - box_b[..., 0]) * (box_b[..., 3] - box_b[..., 1])
out = inter_area / (area_a + area_b - inter_area)
return out if use_batch else out.squeeze(0)
def box_iou_numpy(box_a, box_b):
(n, A), B = box_a.shape[:2], box_b.shape[1]
# add a dimension
box_a = np.tile(box_a[:, :, None, :], (1, 1, B, 1))
box_b = np.tile(box_b[:, None, :, :], (1, A, 1, 1))
max_xy = np.minimum(box_a[..., 2:], box_b[..., 2:])
min_xy = np.maximum(box_a[..., :2], box_b[..., :2])
inter = np.clip((max_xy - min_xy), a_min=0, a_max=100000)
inter_area = inter[..., 0] * inter[..., 1]
area_a = (box_a[..., 2] - box_a[..., 0]) * (box_a[..., 3] - box_a[..., 1])
area_b = (box_b[..., 2] - box_b[..., 0]) * (box_b[..., 3] - box_b[..., 1])
return inter_area / (area_a + area_b - inter_area)
def match(cfg, box_gt, anchors, class_gt):
# Convert prior boxes to the form of [xmin, ymin, xmax, ymax].
decoded_priors = torch.cat((anchors[:, :2] - anchors[:, 2:] / 2, anchors[:, :2] + anchors[:, 2:] / 2), 1)
overlaps = box_iou(box_gt, decoded_priors) # (num_gts, num_achors)
_, gt_max_i = overlaps.max(1) # (num_gts, ) the max IoU for each gt box
each_anchor_max, anchor_max_i = overlaps.max(0) # (num_achors, ) the max IoU for each anchor
# For the max IoU anchor for each gt box, set its IoU to 2. This ensures that it won't be filtered
# in the threshold step even if the IoU is under the negative threshold. This is because that we want
# at least one anchor to match with each gt box or else we'd be wasting training data.
each_anchor_max.index_fill_(0, gt_max_i, 2)
# Set the index of the pair (anchor, gt) we set the overlap for above.
for j in range(gt_max_i.size(0)):
anchor_max_i[gt_max_i[j]] = j
anchor_max_gt = box_gt[anchor_max_i] # (num_achors, 4)
# For OCDI dataset
conf = class_gt[anchor_max_i] # the class of the max IoU gt box for each anchor
# Others
# conf = class_gt[anchor_max_i] + 1 # the class of the max IoU gt box for each anchor
conf[each_anchor_max < cfg.pos_iou_thre] = -1 # label as neutral
conf[each_anchor_max < cfg.neg_iou_thre] = 0 # label as background
offsets = encode(anchor_max_gt, anchors)
return offsets, conf, anchor_max_gt, anchor_max_i
def make_anchors(cfg, conv_h, conv_w, scale):
prior_data = []
# Iteration order is important (it has to sync up with the convout)
for j, i in product(range(conv_h), range(conv_w)):
# + 0.5 because priors are in center
x = (i + 0.5) / conv_w
y = (j + 0.5) / conv_h
for ar in cfg.aspect_ratios:
ar = sqrt(ar)
w = scale * ar / cfg.img_size
h = scale / ar / cfg.img_size
prior_data += [x, y, w, h]
return prior_data
def encode(matched, priors):
variances = [0.1, 0.2]
g_cxcy = (matched[:, :2] + matched[:, 2:]) / 2 - priors[:, :2] # 10 * (Xg - Xa) / Wa
g_cxcy /= (variances[0] * priors[:, 2:]) # 10 * (Yg - Ya) / Ha
g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:] # 5 * log(Wg / Wa)
g_wh = torch.log(g_wh) / variances[1] # 5 * log(Hg / Ha)
# return target for smooth_l1_loss
offsets = torch.cat([g_cxcy, g_wh], 1) # [num_priors, 4]
return offsets
def sanitize_coordinates(_x1, _x2, img_size, padding=0):
"""
Sanitizes the input coordinates so that x1 < x2, x1 != x2, x1 >= 0, and x2 <= image_size.
Also converts from relative to absolute coordinates and casts the results to long tensors.
Warning: this does things in-place behind the scenes so copy if necessary.
"""
_x1 = _x1 * img_size
_x2 = _x2 * img_size
x1 = torch.min(_x1, _x2)
x2 = torch.max(_x1, _x2)
x1 = torch.clamp(x1 - padding, min=0)
x2 = torch.clamp(x2 + padding, max=img_size)
return x1, x2
def sanitize_coordinates_numpy(_x1, _x2, img_size, padding=0):
_x1 = _x1 * img_size
_x2 = _x2 * img_size
x1 = np.minimum(_x1, _x2)
x2 = np.maximum(_x1, _x2)
x1 = np.clip(x1 - padding, a_min=0, a_max=1000000)
x2 = np.clip(x2 + padding, a_min=0, a_max=img_size)
return x1, x2
def crop(masks, boxes, padding=1):
"""
"Crop" predicted masks by zeroing out everything not in the predicted bbox.
Args:
- masks should be a size [h, w, n] tensor of masks
- boxes should be a size [n, 4] tensor of bbox coords in relative point form
"""
h, w, n = masks.size()
x1, x2 = sanitize_coordinates(boxes[:, 0], boxes[:, 2], w, padding)
y1, y2 = sanitize_coordinates(boxes[:, 1], boxes[:, 3], h, padding)
rows = torch.arange(w, device=masks.device, dtype=x1.dtype).view(1, -1, 1).expand(h, w, n)
cols = torch.arange(h, device=masks.device, dtype=x1.dtype).view(-1, 1, 1).expand(h, w, n)
masks_left = rows >= x1.view(1, 1, -1)
masks_right = rows < x2.view(1, 1, -1)
masks_up = cols >= y1.view(1, 1, -1)
masks_down = cols < y2.view(1, 1, -1)
crop_mask = masks_left * masks_right * masks_up * masks_down
return masks * crop_mask.float()
def ones_crop(masks, boxes, padding=1):
"""
"Crop" predicted masks by zeroing out everything not in the predicted bbox.
Args:
- masks should be a size [h, w, n] tensor of masks
- boxes should be a size [n, 4] tensor of bbox coords in relative point form
"""
h, w, n = masks.size()
x1, x2 = sanitize_coordinates(boxes[:, 0], boxes[:, 2], w, padding)
y1, y2 = sanitize_coordinates(boxes[:, 1], boxes[:, 3], h, padding)
# ones = torch.ones_like(masks).float().cuda()
ones = torch.ones_like(masks).float().to(masks.device)
rows = torch.arange(w, device=masks.device, dtype=x1.dtype).view(1, -1, 1).expand(h, w, n)
cols = torch.arange(h, device=masks.device, dtype=x1.dtype).view(-1, 1, 1).expand(h, w, n)
masks_left = rows >= x1.view(1, 1, -1)
masks_right = rows < x2.view(1, 1, -1)
masks_up = cols >= y1.view(1, 1, -1)
masks_down = cols < y2.view(1, 1, -1)
crop_mask = masks_left * masks_right * masks_up * masks_down
out_mask = ~crop_mask
return masks * crop_mask.float() + ones * out_mask.float()
def crop_numpy(masks, boxes, padding=1):
h, w, n = masks.shape
x1, x2 = sanitize_coordinates_numpy(boxes[:, 0], boxes[:, 2], w, padding)
y1, y2 = sanitize_coordinates_numpy(boxes[:, 1], boxes[:, 3], h, padding)
rows = np.tile(np.arange(w)[None, :, None], (h, 1, n))
cols = np.tile(np.arange(h)[:, None, None], (1, w, n))
masks_left = rows >= (x1.reshape(1, 1, -1))
masks_right = rows < (x2.reshape(1, 1, -1))
masks_up = cols >= (y1.reshape(1, 1, -1))
masks_down = cols < (y2.reshape(1, 1, -1))
crop_mask = masks_left * masks_right * masks_up * masks_down
return masks * crop_mask
def mask_iou(mask1, mask2):
"""
Inputs inputs are matricies of size _ x N. Output is size _1 x _2.
Note: if iscrowd is True, then mask2 should be the crowd.
"""
intersection = torch.matmul(mask1, mask2.t())
area1 = torch.sum(mask1, dim=1).reshape(1, -1)
area2 = torch.sum(mask2, dim=1).reshape(1, -1)
union = (area1.t() + area2) - intersection
ret = intersection / union
return ret.cpu() |