Spaces:
Configuration error
Configuration error
import numpy as np | |
import cv2 | |
import random | |
from torch import nn | |
import torch | |
from imgaug import augmenters as iaa | |
from lib.config import cfg | |
from plyfile import PlyData | |
def gaussian_radius(det_size, min_overlap=0.7): | |
height, width = det_size | |
a1 = 1 | |
b1 = (height + width) | |
c1 = width * height * (1 - min_overlap) / (1 + min_overlap) | |
sq1 = np.sqrt(b1 ** 2 - 4 * a1 * c1) | |
r1 = (b1 + sq1) / 2 | |
a2 = 4 | |
b2 = 2 * (height + width) | |
c2 = (1 - min_overlap) * width * height | |
sq2 = np.sqrt(b2 ** 2 - 4 * a2 * c2) | |
r2 = (b2 + sq2) / 2 | |
a3 = 4 * min_overlap | |
b3 = -2 * min_overlap * (height + width) | |
c3 = (min_overlap - 1) * width * height | |
if b3 ** 2 - 4 * a3 * c3 < 0: | |
r3 = min(r1, r2) | |
else: | |
sq3 = np.sqrt(b3 ** 2 - 4 * a3 * c3) | |
r3 = (b3 + sq3) / 2 | |
return min(r1, r2, r3) | |
def gaussian2D(shape, sigma=(1, 1), rho=0): | |
if not isinstance(sigma, tuple): | |
sigma = (sigma, sigma) | |
sigma_x, sigma_y = sigma | |
m, n = [(ss - 1.) / 2. for ss in shape] | |
y, x = np.ogrid[-m:m+1, -n:n+1] | |
energy = (x * x) / (sigma_x * sigma_x) - 2 * rho * x * y / (sigma_x * sigma_y) + (y * y) / (sigma_y * sigma_y) | |
h = np.exp(-energy / (2 * (1 - rho * rho))) | |
h[h < np.finfo(h.dtype).eps * h.max()] = 0 | |
return h | |
def draw_umich_gaussian(heatmap, center, radius, k=1): | |
diameter = 2 * radius + 1 | |
gaussian = gaussian2D((diameter, diameter), sigma=diameter / 6) | |
x, y = int(center[0]), int(center[1]) | |
height, width = heatmap.shape[0:2] | |
left, right = min(x, radius), min(width - x, radius + 1) | |
top, bottom = min(y, radius), min(height - y, radius + 1) | |
masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right] | |
masked_gaussian = gaussian[radius - top:radius + bottom, radius - left:radius + right] | |
if min(masked_gaussian.shape) > 0 and min(masked_heatmap.shape) > 0: # TODO debug | |
np.maximum(masked_heatmap, masked_gaussian * k, out=masked_heatmap) | |
return heatmap | |
def draw_distribution(heatmap, center, sigma_x, sigma_y, rho, radius, k=1): | |
diameter = 2 * radius + 1 | |
gaussian = gaussian2D((diameter, diameter), (sigma_x/3, sigma_y/3), rho) | |
x, y = int(center[0]), int(center[1]) | |
height, width = heatmap.shape[0:2] | |
left, right = min(x, radius), min(width - x, radius + 1) | |
top, bottom = min(y, radius), min(height - y, radius + 1) | |
masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right] | |
masked_gaussian = gaussian[radius - top:radius + bottom, radius - left:radius + right] | |
if min(masked_gaussian.shape) > 0 and min(masked_heatmap.shape) > 0: # TODO debug | |
np.maximum(masked_heatmap, masked_gaussian * k, out=masked_heatmap) | |
return heatmap | |
def draw_heatmap_np(hm, point, box_size): | |
"""point: [x, y]""" | |
# radius = gaussian_radius(box_size) | |
radius = box_size[0] | |
radius = max(0, int(radius)) | |
ct_int = np.array(point, dtype=np.int32) | |
draw_umich_gaussian(hm, ct_int, radius) | |
return hm | |
def get_edge(mask): | |
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) | |
return mask - cv2.erode(mask, kernel) | |
def compute_gaussian_1d(dmap, sigma=1): | |
"""dmap: each entry means a distance""" | |
prob = np.exp(-dmap / (2 * sigma * sigma)) | |
prob[prob < np.finfo(prob.dtype).eps * prob.max()] = 0 | |
return prob | |
def get_3rd_point(a, b): | |
direct = a - b | |
return b + np.array([-direct[1], direct[0]], dtype=np.float32) | |
def get_dir(src_point, rot_rad): | |
sn, cs = np.sin(rot_rad), np.cos(rot_rad) | |
src_result = [0, 0] | |
src_result[0] = src_point[0] * cs - src_point[1] * sn | |
src_result[1] = src_point[0] * sn + src_point[1] * cs | |
return src_result | |
def get_affine_transform(center, | |
scale, | |
rot, | |
output_size, | |
shift=np.array([0, 0], dtype=np.float32), | |
inv=0): | |
if not isinstance(scale, np.ndarray) and not isinstance(scale, list): | |
scale = np.array([scale, scale], dtype=np.float32) | |
scale_tmp = scale | |
src_w = scale_tmp[0] | |
dst_w = output_size[0] | |
dst_h = output_size[1] | |
rot_rad = np.pi * rot / 180 | |
src_dir = get_dir([0, src_w * -0.5], rot_rad) | |
dst_dir = np.array([0, dst_w * -0.5], np.float32) | |
src = np.zeros((3, 2), dtype=np.float32) | |
dst = np.zeros((3, 2), dtype=np.float32) | |
src[0, :] = center + scale_tmp * shift | |
src[1, :] = center + src_dir + scale_tmp * shift | |
dst[0, :] = [dst_w * 0.5, dst_h * 0.5] | |
dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5], np.float32) + dst_dir | |
src[2:, :] = get_3rd_point(src[0, :], src[1, :]) | |
dst[2:, :] = get_3rd_point(dst[0, :], dst[1, :]) | |
if inv: | |
trans = cv2.getAffineTransform(np.float32(dst), np.float32(src)) | |
else: | |
trans = cv2.getAffineTransform(np.float32(src), np.float32(dst)) | |
return trans | |
def affine_transform(pt, t): | |
"""pt: [n, 2]""" | |
new_pt = np.dot(np.array(pt), t[:, :2].T) + t[:, 2] | |
return new_pt | |
def homography_transform(pt, H): | |
"""pt: [n, 2]""" | |
pt = np.concatenate([pt, np.ones([len(pt), 1])], axis=1) | |
pt = np.dot(pt, H.T) | |
pt = pt[..., :2] / pt[..., 2:] | |
return pt | |
def get_border(border, size): | |
i = 1 | |
while np.any(size - border // i <= border // i): | |
i *= 2 | |
return border // i | |
def grayscale(image): | |
return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
def lighting_(data_rng, image, alphastd, eigval, eigvec): | |
alpha = data_rng.normal(scale=alphastd, size=(3, )) | |
image += np.dot(eigvec, eigval * alpha) | |
def blend_(alpha, image1, image2): | |
image1 *= alpha | |
image2 *= (1 - alpha) | |
image1 += image2 | |
def saturation_(data_rng, image, gs, gs_mean, var): | |
alpha = 1. + data_rng.uniform(low=-var, high=var) | |
blend_(alpha, image, gs[:, :, None]) | |
def brightness_(data_rng, image, gs, gs_mean, var): | |
alpha = 1. + data_rng.uniform(low=-var, high=var) | |
image *= alpha | |
def contrast_(data_rng, image, gs, gs_mean, var): | |
alpha = 1. + data_rng.uniform(low=-var, high=var) | |
blend_(alpha, image, gs_mean) | |
def color_aug(data_rng, image, eig_val, eig_vec): | |
functions = [brightness_, contrast_, saturation_] | |
random.shuffle(functions) | |
gs = grayscale(image) | |
gs_mean = gs.mean() | |
for f in functions: | |
f(data_rng, image, gs, gs_mean, 0.4) | |
lighting_(data_rng, image, 0.1, eig_val, eig_vec) | |
def blur_aug(inp): | |
if np.random.random() < 0.1: | |
if np.random.random() < 0.8: | |
inp = iaa.blur_gaussian_(inp, abs(np.clip(np.random.normal(0, 1.5), -3, 3))) | |
else: | |
inp = iaa.MotionBlur((3, 15), (-45, 45))(images=[inp])[0] | |
def gaussian_blur(image, sigma): | |
from scipy import ndimage | |
if image.ndim == 2: | |
image[:, :] = ndimage.gaussian_filter(image[:, :], sigma, mode="mirror") | |
else: | |
nb_channels = image.shape[2] | |
for channel in range(nb_channels): | |
image[:, :, channel] = ndimage.gaussian_filter(image[:, :, channel], sigma, mode="mirror") | |
def inter_from_mask(pred, gt): | |
pred = pred.astype(np.bool) | |
gt = gt.astype(np.bool) | |
intersection = np.logical_and(gt, pred).sum() | |
return intersection | |
def draw_poly(mask, poly): | |
cv2.fillPoly(mask, [poly], 255) | |
return mask | |
def inter_from_poly(poly, gt, width, height): | |
mask_small = np.zeros((1, height, width), dtype=np.uint8) | |
mask_small = draw_poly(mask_small, poly) | |
mask_gt = gt[..., 0] | |
return inter_from_mask(mask_small, mask_gt) | |
def inter_from_polys(poly, w, h, gt_mask): | |
inter = inter_from_poly(poly, gt_mask, w, h) | |
if inter > 0: | |
return False | |
return True | |
def select_point(shape, poly, gt_mask): | |
for i in range(cfg.max_iter): | |
y = np.random.randint(shape[0] - poly['bbox'][3]) | |
x = np.random.randint(shape[1] - poly['bbox'][2]) | |
delta = np.array([poly['bbox'][0] - x, poly['bbox'][1] - y]) | |
poly_move = np.array(poly['poly']) - delta | |
inter = inter_from_polys(poly_move, shape[1], shape[0], gt_mask) | |
if inter: | |
return x, y | |
x, y = -1, -1 | |
return x, y | |
def transform_small_gt(poly, box, x, y): | |
delta = np.array([poly['bbox'][0] - x, poly['bbox'][1] - y]) | |
poly['poly'] -= delta | |
box[:2] -= delta | |
box[2:] -= delta | |
return poly, box | |
def get_mask_img(img, poly): | |
mask = np.zeros(img.shape[:2])[..., np.newaxis] | |
cv2.fillPoly(mask, [np.round(poly['poly']).astype(int)], 1) | |
poly_img = img * mask | |
mask = mask[..., 0] | |
return poly_img, mask | |
def add_small_obj(img, gt_mask, poly, box, polys_gt): | |
poly_img, mask = get_mask_img(img, poly) | |
x, y = select_point(img.shape, poly.copy(), gt_mask) | |
if x == -1: | |
box = [] | |
return img, poly, box | |
poly, box = transform_small_gt(poly, box, x, y) | |
_, mask_ori = get_mask_img(img, poly) | |
gt_mask += mask_ori[..., np.newaxis] | |
img[mask_ori == 1] = poly_img[mask == 1] | |
return img, poly, box[np.newaxis, :], gt_mask | |
def get_gt_mask(img, poly): | |
mask = np.zeros(img.shape[:2])[..., np.newaxis] | |
for i in range(len(poly)): | |
for j in range(len(poly[i])): | |
cv2.fillPoly(mask, [np.round(poly[i][j]['poly']).astype(int)], 1) | |
return mask | |
def small_aug(img, poly, box, label, num): | |
N = len(poly) | |
gt_mask = get_gt_mask(img, poly) | |
for i in range(N): | |
if len(poly[i]) > 1: | |
continue | |
if poly[i][0]['area'] < 32*32: | |
for k in range(num): | |
img, poly_s, box_s, gt_mask = add_small_obj(img, gt_mask, poly[i][0].copy(), box[i].copy(), poly) | |
if len(box_s) == 0: | |
continue | |
poly.append([poly_s]) | |
box = np.concatenate((box, box_s)) | |
label.append(label[i]) | |
return img, poly, box, label | |
def truncated_normal(mean, sigma, low, high, data_rng=None): | |
if data_rng is None: | |
data_rng = np.random.RandomState() | |
value = data_rng.normal(mean, sigma) | |
return np.clip(value, low, high) | |
def _nms(heat, kernel=3): | |
"""heat: [b, c, h, w]""" | |
pad = (kernel - 1) // 2 | |
# find the local minimum of heat within the neighborhood kernel x kernel | |
hmax = nn.functional.max_pool2d( | |
heat, (kernel, kernel), stride=1, padding=pad) | |
keep = (hmax == heat).float() | |
return heat * keep | |
def _gather_feat(feat, ind, mask=None): | |
dim = feat.size(2) | |
ind = ind.unsqueeze(2).expand(ind.size(0), ind.size(1), dim) | |
feat = feat.gather(1, ind) | |
if mask is not None: | |
mask = mask.unsqueeze(2).expand_as(feat) | |
feat = feat[mask] | |
feat = feat.view(-1, dim) | |
return feat | |
def _topk(scores, K=40): | |
batch, cat, height, width = scores.size() | |
topk_scores, topk_inds = torch.topk(scores.view(batch, cat, -1), K) | |
topk_inds = topk_inds % (height * width) | |
topk_ys = (topk_inds / width).int().float() | |
topk_xs = (topk_inds % width).int().float() | |
topk_score, topk_ind = torch.topk(topk_scores.view(batch, -1), K) | |
topk_clses = (topk_ind / K).int() | |
topk_inds = _gather_feat( | |
topk_inds.view(batch, -1, 1), topk_ind).view(batch, K) | |
topk_ys = _gather_feat(topk_ys.view(batch, -1, 1), topk_ind).view(batch, K) | |
topk_xs = _gather_feat(topk_xs.view(batch, -1, 1), topk_ind).view(batch, K) | |
return topk_score, topk_inds, topk_clses, topk_ys, topk_xs | |
def clip_to_image(bbox, h, w): | |
bbox[..., :2] = torch.clamp(bbox[..., :2], min=0) | |
bbox[..., 2] = torch.clamp(bbox[..., 2], max=w-1) | |
bbox[..., 3] = torch.clamp(bbox[..., 3], max=h-1) | |
return bbox | |
def load_ply(path): | |
ply = PlyData.read(path) | |
data = ply.elements[0].data | |
x, y, z = data['x'], data['y'], data['z'] | |
model = np.stack([x, y, z], axis=-1) | |
return model | |