NeuralBody / lib /utils /data_utils.py
pengsida
initial commit
1ba539f
raw
history blame
11.8 kB
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