aharley's picture
added basics
6d95ea1
import torch
import utils.basic
# import utils.box
# import utils.vox
import numpy as np
import torchvision.ops as ops
from utils.basic import print_
def split_intrinsics(K):
# K is B x 3 x 3 or B x 4 x 4
fx = K[:,0,0]
fy = K[:,1,1]
x0 = K[:,0,2]
y0 = K[:,1,2]
return fx, fy, x0, y0
# def apply_pix_T_cam_py(pix_T_cam, xyz):
# fx, fy, x0, y0 = split_intrinsics(pix_T_cam)
# # xyz is shaped B x H*W x 3
# # returns xy, shaped B x H*W x 2
# B, N, C = list(xyz.shape)
# assert(C==3)
# x, y, z = xyz[:,:,0], xyz[:,:,1], xyz[:,:,2]
# fx = np.reshape(fx, [B, 1])
# fy = np.reshape(fy, [B, 1])
# x0 = np.reshape(x0, [B, 1])
# y0 = np.reshape(y0, [B, 1])
# EPS = 1e-4
# z = np.clip(z, EPS, None)
# x = (x*fx)/(z)+x0
# y = (y*fy)/(z)+y0
# xy = np.stack([x, y], axis=-1)
# return xy
def apply_pix_T_cam(pix_T_cam, xyz):
fx, fy, x0, y0 = split_intrinsics(pix_T_cam)
# xyz is shaped B x H*W x 3
# returns xy, shaped B x H*W x 2
B, N, C = list(xyz.shape)
assert(C==3)
x, y, z = torch.unbind(xyz, axis=-1)
fx = torch.reshape(fx, [B, 1])
fy = torch.reshape(fy, [B, 1])
x0 = torch.reshape(x0, [B, 1])
y0 = torch.reshape(y0, [B, 1])
EPS = 1e-4
z = torch.clamp(z, min=EPS)
x = (x*fx)/(z)+x0
y = (y*fy)/(z)+y0
xy = torch.stack([x, y], axis=-1)
return xy
# def apply_4x4_py(RT, xyz):
# # print('RT', RT.shape)
# B, N, _ = list(xyz.shape)
# ones = np.ones_like(xyz[:,:,0:1])
# xyz1 = np.concatenate([xyz, ones], 2)
# # print('xyz1', xyz1.shape)
# xyz1_t = xyz1.transpose(0,2,1)
# # print('xyz1_t', xyz1_t.shape)
# # this is B x 4 x N
# xyz2_t = np.matmul(RT, xyz1_t)
# # print('xyz2_t', xyz2_t.shape)
# xyz2 = xyz2_t.transpose(0,2,1)
# # print('xyz2', xyz2.shape)
# xyz2 = xyz2[:,:,:3]
# return xyz2
def apply_4x4(RT, xyz):
B, N, _ = list(xyz.shape)
ones = torch.ones_like(xyz[:,:,0:1])
xyz1 = torch.cat([xyz, ones], 2)
xyz1_t = torch.transpose(xyz1, 1, 2)
# this is B x 4 x N
xyz2_t = torch.matmul(RT, xyz1_t)
xyz2 = torch.transpose(xyz2_t, 1, 2)
xyz2 = xyz2[:,:,:3]
return xyz2
def apply_3x3(RT, xy):
B, N, _ = list(xy.shape)
ones = torch.ones_like(xy[:,:,0:1])
xy1 = torch.cat([xy, ones], 2)
xy1_t = torch.transpose(xy1, 1, 2)
# this is B x 4 x N
xy2_t = torch.matmul(RT, xy1_t)
xy2 = torch.transpose(xy2_t, 1, 2)
xy2 = xy2[:,:,:2]
return xy2
def generate_polygon(ctr_x, ctr_y, avg_r, irregularity, spikiness, num_verts):
'''
Start with the center of the polygon at ctr_x, ctr_y,
Then creates the polygon by sampling points on a circle around the center.
Random noise is added by varying the angular spacing between sequential points,
and by varying the radial distance of each point from the centre.
Params:
ctr_x, ctr_y - coordinates of the "centre" of the polygon
avg_r - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
spikiness - [0,1] indicating how much variance there is in each vertex from the circle of radius avg_r. [0,1] will map to [0, avg_r]
num_verts
Returns:
np.array [num_verts, 2] - CCW order.
'''
# spikiness
spikiness = np.clip(spikiness, 0, 1) * avg_r
# generate n angle steps
irregularity = np.clip(irregularity, 0, 1) * 2 * np.pi / num_verts
lower = (2*np.pi / num_verts) - irregularity
upper = (2*np.pi / num_verts) + irregularity
# angle steps
angle_steps = np.random.uniform(lower, upper, num_verts)
sc = (2 * np.pi) / angle_steps.sum()
angle_steps *= sc
# get all radii
angle = np.random.uniform(0, 2*np.pi)
radii = np.clip(np.random.normal(avg_r, spikiness, num_verts), 0, 2 * avg_r)
# compute all points
points = []
for i in range(num_verts):
x = ctr_x + radii[i] * np.cos(angle)
y = ctr_y + radii[i] * np.sin(angle)
points.append([x, y])
angle += angle_steps[i]
return np.array(points).astype(int)
def get_random_affine_2d(B, rot_min=-5.0, rot_max=5.0, tx_min=-0.1, tx_max=0.1, ty_min=-0.1, ty_max=0.1, sx_min=-0.05, sx_max=0.05, sy_min=-0.05, sy_max=0.05, shx_min=-0.05, shx_max=0.05, shy_min=-0.05, shy_max=0.05):
'''
Params:
rot_min: rotation amount min
rot_max: rotation amount max
tx_min: translation x min
tx_max: translation x max
ty_min: translation y min
ty_max: translation y max
sx_min: scaling x min
sx_max: scaling x max
sy_min: scaling y min
sy_max: scaling y max
shx_min: shear x min
shx_max: shear x max
shy_min: shear y min
shy_max: shear y max
Returns:
transformation matrix: (B, 3, 3)
'''
# rotation
if rot_max - rot_min != 0:
rot_amount = np.random.uniform(low=rot_min, high=rot_max, size=B)
rot_amount = np.pi/180.0*rot_amount
else:
rot_amount = rot_min
rotation = np.zeros((B, 3, 3)) # B, 3, 3
rotation[:, 2, 2] = 1
rotation[:, 0, 0] = np.cos(rot_amount)
rotation[:, 0, 1] = -np.sin(rot_amount)
rotation[:, 1, 0] = np.sin(rot_amount)
rotation[:, 1, 1] = np.cos(rot_amount)
# translation
translation = np.zeros((B, 3, 3)) # B, 3, 3
translation[:, [0,1,2], [0,1,2]] = 1
if (tx_max - tx_min) > 0:
trans_x = np.random.uniform(low=tx_min, high=tx_max, size=B)
translation[:, 0, 2] = trans_x
# else:
# translation[:, 0, 2] = tx_max
if ty_max - ty_min != 0:
trans_y = np.random.uniform(low=ty_min, high=ty_max, size=B)
translation[:, 1, 2] = trans_y
# else:
# translation[:, 1, 2] = ty_max
# scaling
scaling = np.zeros((B, 3, 3)) # B, 3, 3
scaling[:, [0,1,2], [0,1,2]] = 1
if (sx_max - sx_min) > 0:
scale_x = 1 + np.random.uniform(low=sx_min, high=sx_max, size=B)
scaling[:, 0, 0] = scale_x
# else:
# scaling[:, 0, 0] = sx_max
if (sy_max - sy_min) > 0:
scale_y = 1 + np.random.uniform(low=sy_min, high=sy_max, size=B)
scaling[:, 1, 1] = scale_y
# else:
# scaling[:, 1, 1] = sy_max
# shear
shear = np.zeros((B, 3, 3)) # B, 3, 3
shear[:, [0,1,2], [0,1,2]] = 1
if (shx_max - shx_min) > 0:
shear_x = np.random.uniform(low=shx_min, high=shx_max, size=B)
shear[:, 0, 1] = shear_x
# else:
# shear[:, 0, 1] = shx_max
if (shy_max - shy_min) > 0:
shear_y = np.random.uniform(low=shy_min, high=shy_max, size=B)
shear[:, 1, 0] = shear_y
# else:
# shear[:, 1, 0] = shy_max
# compose all those
rt = np.einsum("ijk,ikl->ijl", rotation, translation)
ss = np.einsum("ijk,ikl->ijl", scaling, shear)
trans = np.einsum("ijk,ikl->ijl", rt, ss)
return trans
def get_centroid_from_box2d(box2d):
ymin = box2d[:,0]
xmin = box2d[:,1]
ymax = box2d[:,2]
xmax = box2d[:,3]
x = (xmin+xmax)/2.0
y = (ymin+ymax)/2.0
return y, x
def normalize_boxlist2d(boxlist2d, H, W):
boxlist2d = boxlist2d.clone()
ymin, xmin, ymax, xmax = torch.unbind(boxlist2d, dim=2)
ymin = ymin / float(H)
ymax = ymax / float(H)
xmin = xmin / float(W)
xmax = xmax / float(W)
boxlist2d = torch.stack([ymin, xmin, ymax, xmax], dim=2)
return boxlist2d
def unnormalize_boxlist2d(boxlist2d, H, W):
boxlist2d = boxlist2d.clone()
ymin, xmin, ymax, xmax = torch.unbind(boxlist2d, dim=2)
ymin = ymin * float(H)
ymax = ymax * float(H)
xmin = xmin * float(W)
xmax = xmax * float(W)
boxlist2d = torch.stack([ymin, xmin, ymax, xmax], dim=2)
return boxlist2d
def unnormalize_box2d(box2d, H, W):
return unnormalize_boxlist2d(box2d.unsqueeze(1), H, W).squeeze(1)
def normalize_box2d(box2d, H, W):
return normalize_boxlist2d(box2d.unsqueeze(1), H, W).squeeze(1)
def get_size_from_box2d(box2d):
ymin = box2d[:,0]
xmin = box2d[:,1]
ymax = box2d[:,2]
xmax = box2d[:,3]
height = ymax-ymin
width = xmax-xmin
return height, width
def crop_and_resize(im, boxlist, PH, PW, boxlist_is_normalized=False):
B, C, H, W = im.shape
B2, N, D = boxlist.shape
assert(B==B2)
assert(D==4)
# PH, PW is the size to resize to
# print('im', im.shape)
# output is B,N,C,PH,PW
# pt wants xy xy, unnormalized
if boxlist_is_normalized:
boxlist_unnorm = unnormalize_boxlist2d(boxlist, H, W)
else:
boxlist_unnorm = boxlist
ymin, xmin, ymax, xmax = boxlist_unnorm.unbind(2)
# boxlist_pt = torch.stack([boxlist_unnorm[:,1], boxlist_unnorm[:,0], boxlist_unnorm[:,3], boxlist_unnorm[:,2]], dim=1)
boxlist_pt = torch.stack([xmin, ymin, xmax, ymax], dim=2)
# we want a B-len list of K x 4 arrays
# print('im', im.shape)
# print('boxlist', boxlist.shape)
# print('boxlist_pt', boxlist_pt.shape)
# boxlist_pt = list(boxlist_pt.unbind(0))
# crops = []
# for b in range(B):
# crops_b = ops.roi_align(im[b:b+1], [boxlist_pt[b]], output_size=(PH, PW))
# crops.append(crops_b)
# # # crops = im
# inds = torch.arange(B).reshape(B,1)
# # boxlist_pt = torch.cat([inds, boxlist_
# boxlist_pt = list(boxlist_pt.unbind(0)
# crops = ops.roi_align(im, [boxlist_pt[b]], output_size=(PH, PW))
crops = ops.roi_align(im, list(boxlist_pt.unbind(0)), output_size=(PH, PW))
# print('crops', crops.shape)
# crops = crops.reshape(B,N,C,PH,PW)
# crops = []
# for b in range(B):
# crop_b = ops.roi_align(im[b:b+1], [boxlist_pt[b]], output_size=(PH, PW))
# print('crop_b', crop_b.shape)
# crops.append(crop_b)
# crops = torch.stack(crops, dim=0)
# print('crops', crops.shape)
# boxlist_list = boxlist_pt.unbind(0)
# print('rgb_crop', rgb_crop.shape)
return crops
# def get_boxlist_from_centroid_and_size(cy, cx, h, w, clip=True):
# # cy,cx are both B,N
# ymin = cy - h/2
# ymax = cy + h/2
# xmin = cx - w/2
# xmax = cx + w/2
# box = torch.stack([ymin, xmin, ymax, xmax], dim=-1)
# if clip:
# box = torch.clamp(box, 0, 1)
# return box
def get_boxlist_from_centroid_and_size(cy, cx, h, w):#, clip=False):
# cy,cx are the same shape
ymin = cy - h/2
ymax = cy + h/2
xmin = cx - w/2
xmax = cx + w/2
# if clip:
# ymin = torch.clamp(ymin, 0, H-1)
# ymax = torch.clamp(ymax, 0, H-1)
# xmin = torch.clamp(xmin, 0, W-1)
# xmax = torch.clamp(xmax, 0, W-1)
box = torch.stack([ymin, xmin, ymax, xmax], dim=-1)
return box
def get_box2d_from_mask(mask, normalize=False):
# mask is B, 1, H, W
B, C, H, W = mask.shape
assert(C==1)
xy = utils.basic.gridcloud2d(B, H, W, norm=False, device=mask.device) # B, H*W, 2
box = torch.zeros((B, 4), dtype=torch.float32, device=mask.device)
for b in range(B):
xy_b = xy[b] # H*W, 2
mask_b = mask[b].reshape(H*W)
xy_ = xy_b[mask_b > 0]
x_ = xy_[:,0]
y_ = xy_[:,1]
ymin = torch.min(y_)
ymax = torch.max(y_)
xmin = torch.min(x_)
xmax = torch.max(x_)
box[b] = torch.stack([ymin, xmin, ymax, xmax], dim=0)
if normalize:
box = normalize_boxlist2d(box.unsqueeze(1), H, W).squeeze(1)
return box
def convert_box2d_to_intrinsics(box2d, pix_T_cam, H, W, use_image_aspect_ratio=True, mult_padding=1.0):
# box2d is B x 4, with ymin, xmin, ymax, xmax in normalized coords
# ymin, xmin, ymax, xmax = torch.unbind(box2d, dim=1)
# H, W is the original size of the image
# mult_padding is relative to object size in pixels
# i assume we're rendering an image the same size as the original (H, W)
if not mult_padding==1.0:
y, x = get_centroid_from_box2d(box2d)
h, w = get_size_from_box2d(box2d)
box2d = get_box2d_from_centroid_and_size(
y, x, h*mult_padding, w*mult_padding, clip=False)
if use_image_aspect_ratio:
h, w = get_size_from_box2d(box2d)
y, x = get_centroid_from_box2d(box2d)
# note h,w are relative right now
# we need to undo this, to see the real ratio
h = h*float(H)
w = w*float(W)
box_ratio = h/w
im_ratio = H/float(W)
# print('box_ratio:', box_ratio)
# print('im_ratio:', im_ratio)
if box_ratio >= im_ratio:
w = h/im_ratio
# print('setting w:', h/im_ratio)
else:
h = w*im_ratio
# print('setting h:', w*im_ratio)
box2d = get_box2d_from_centroid_and_size(
y, x, h/float(H), w/float(W), clip=False)
assert(h > 1e-4)
assert(w > 1e-4)
ymin, xmin, ymax, xmax = torch.unbind(box2d, dim=1)
fx, fy, x0, y0 = split_intrinsics(pix_T_cam)
# the topleft of the new image will now have a different offset from the center of projection
new_x0 = x0 - xmin*W
new_y0 = y0 - ymin*H
pix_T_cam = pack_intrinsics(fx, fy, new_x0, new_y0)
# this alone will give me an image in original resolution,
# with its topleft at the box corner
box_h, box_w = get_size_from_box2d(box2d)
# these are normalized, and shaped B. (e.g., [0.4], [0.3])
# we are going to scale the image by the inverse of this,
# since we are zooming into this area
sy = 1./box_h
sx = 1./box_w
pix_T_cam = scale_intrinsics(pix_T_cam, sx, sy)
return pix_T_cam, box2d
def generatePolygon(ctr_x, ctr_y, avg_r, irregularity, spikiness, num_verts):
'''
Start with the center of the polygon at ctr_x, ctr_y,
Then creates the polygon by sampling points on a circle around the center.
Random noise is added by varying the angular spacing between sequential points,
and by varying the radial distance of each point from the centre.
Params:
ctr_x, ctr_y - coordinates of the "centre" of the polygon
avg_r - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
spikiness - [0,1] indicating how much variance there is in each vertex from the circle of radius avg_r. [0,1] will map to [0, avg_r]
num_verts
Returns:
np.array [num_verts, 2] - CCW order.
'''
# spikiness
spikiness = np.clip(spikiness, 0, 1) * avg_r
# generate n angle steps
irregularity = np.clip(irregularity, 0, 1) * 2 * np.pi / num_verts
lower = (2*np.pi / num_verts) - irregularity
upper = (2*np.pi / num_verts) + irregularity
# angle steps
angle_steps = np.random.uniform(lower, upper, num_verts)
sc = (2 * np.pi) / angle_steps.sum()
angle_steps *= sc
# get all radii
angle = np.random.uniform(0, 2*np.pi)
radii = np.clip(np.random.normal(avg_r, spikiness, num_verts), 0, 2 * avg_r)
# compute all points
points = []
for i in range(num_verts):
x = ctr_x + radii[i] * np.cos(angle)
y = ctr_y + radii[i] * np.sin(angle)
points.append([x, y])
angle += angle_steps[i]
return np.array(points).astype(int)
def get_random_affine_2d(B, rot_min=-5.0, rot_max=5.0, tx_min=-0.1, tx_max=0.1, ty_min=-0.1, ty_max=0.1, sx_min=-0.05, sx_max=0.05, sy_min=-0.05, sy_max=0.05, shx_min=-0.05, shx_max=0.05, shy_min=-0.05, shy_max=0.05):
'''
Params:
rot_min: rotation amount min
rot_max: rotation amount max
tx_min: translation x min
tx_max: translation x max
ty_min: translation y min
ty_max: translation y max
sx_min: scaling x min
sx_max: scaling x max
sy_min: scaling y min
sy_max: scaling y max
shx_min: shear x min
shx_max: shear x max
shy_min: shear y min
shy_max: shear y max
Returns:
transformation matrix: (B, 3, 3)
'''
# rotation
if rot_max - rot_min != 0:
rot_amount = np.random.uniform(low=rot_min, high=rot_max, size=B)
rot_amount = np.pi/180.0*rot_amount
else:
rot_amount = rot_min
rotation = np.zeros((B, 3, 3)) # B, 3, 3
rotation[:, 2, 2] = 1
rotation[:, 0, 0] = np.cos(rot_amount)
rotation[:, 0, 1] = -np.sin(rot_amount)
rotation[:, 1, 0] = np.sin(rot_amount)
rotation[:, 1, 1] = np.cos(rot_amount)
# translation
translation = np.zeros((B, 3, 3)) # B, 3, 3
translation[:, [0,1,2], [0,1,2]] = 1
if tx_max - tx_min != 0:
trans_x = np.random.uniform(low=tx_min, high=tx_max, size=B)
translation[:, 0, 2] = trans_x
else:
translation[:, 0, 2] = tx_max
if ty_max - ty_min != 0:
trans_y = np.random.uniform(low=ty_min, high=ty_max, size=B)
translation[:, 1, 2] = trans_y
else:
translation[:, 1, 2] = ty_max
# scaling
scaling = np.zeros((B, 3, 3)) # B, 3, 3
scaling[:, [0,1,2], [0,1,2]] = 1
if sx_max - sx_min != 0:
scale_x = 1 + np.random.uniform(low=sx_min, high=sx_max, size=B)
scaling[:, 0, 0] = scale_x
else:
scaling[:, 0, 0] = sx_max
if sy_max - sy_min != 0:
scale_y = 1 + np.random.uniform(low=sy_min, high=sy_max, size=B)
scaling[:, 1, 1] = scale_y
else:
scaling[:, 1, 1] = sy_max
# shear
shear = np.zeros((B, 3, 3)) # B, 3, 3
shear[:, [0,1,2], [0,1,2]] = 1
if shx_max - shx_min != 0:
shear_x = np.random.uniform(low=shx_min, high=shx_max, size=B)
shear[:, 0, 1] = shear_x
else:
shear[:, 0, 1] = shx_max
if shy_max - shy_min != 0:
shear_y = np.random.uniform(low=shy_min, high=shy_max, size=B)
shear[:, 1, 0] = shear_y
else:
shear[:, 1, 0] = shy_max
# compose all those
rt = np.einsum("ijk,ikl->ijl", rotation, translation)
ss = np.einsum("ijk,ikl->ijl", scaling, shear)
trans = np.einsum("ijk,ikl->ijl", rt, ss)
return trans
def pixels2camera(x,y,z,fx,fy,x0,y0):
# x and y are locations in pixel coordinates, z is a depth in meters
# they can be images or pointclouds
# fx, fy, x0, y0 are camera intrinsics
# returns xyz, sized B x N x 3
B = x.shape[0]
fx = torch.reshape(fx, [B,1])
fy = torch.reshape(fy, [B,1])
x0 = torch.reshape(x0, [B,1])
y0 = torch.reshape(y0, [B,1])
x = torch.reshape(x, [B,-1])
y = torch.reshape(y, [B,-1])
z = torch.reshape(z, [B,-1])
# unproject
x = (z/fx)*(x-x0)
y = (z/fy)*(y-y0)
xyz = torch.stack([x,y,z], dim=2)
# B x N x 3
return xyz
def camera2pixels(xyz, pix_T_cam):
# xyz is shaped B x H*W x 3
# returns xy, shaped B x H*W x 2
fx, fy, x0, y0 = split_intrinsics(pix_T_cam)
x, y, z = torch.unbind(xyz, dim=-1)
B = list(z.shape)[0]
fx = torch.reshape(fx, [B,1])
fy = torch.reshape(fy, [B,1])
x0 = torch.reshape(x0, [B,1])
y0 = torch.reshape(y0, [B,1])
x = torch.reshape(x, [B,-1])
y = torch.reshape(y, [B,-1])
z = torch.reshape(z, [B,-1])
EPS = 1e-4
z = torch.clamp(z, min=EPS)
x = (x*fx)/z + x0
y = (y*fy)/z + y0
xy = torch.stack([x, y], dim=-1)
return xy
def depth2pointcloud(z, pix_T_cam):
B, C, H, W = list(z.shape)
device = z.device
y, x = utils.basic.meshgrid2d(B, H, W, device=device)
z = torch.reshape(z, [B, H, W])
fx, fy, x0, y0 = split_intrinsics(pix_T_cam)
xyz = pixels2camera(x, y, z, fx, fy, x0, y0)
return xyz
def create_depth_image_single(xy, z, H, W, force_positive=True, max_val=100.0, serial=False, slices=20):
# turn the xy coordinates into image inds
xy = torch.round(xy).long()
depth = torch.zeros(H*W, dtype=torch.float32, device=xy.device)
depth[:] = max_val
# lidar reports a sphere of measurements
# only use the inds that are within the image bounds
# also, only use forward-pointing depths (z > 0)
valid_inds = (xy[:,0] <= W-1) & (xy[:,1] <= H-1) & (xy[:,0] >= 0) & (xy[:,1] >= 0) & (z[:] > 0)
# gather these up
xy = xy[valid_inds]
z = z[valid_inds]
inds = utils.basic.sub2ind(H, W, xy[:,1], xy[:,0]).long()
if not serial:
depth[inds] = z
else:
if False:
for (index, replacement) in zip(inds, z):
if depth[index] > replacement:
depth[index] = replacement
# ok my other idea is:
# sort the depths by distance
# create N depth maps
# merge them back-to-front
# actually maybe you don't even need the separate maps
sort_inds = torch.argsort(z, descending=True)
xy = xy[sort_inds]
z = z[sort_inds]
N = len(sort_inds)
def split(a, n):
k, m = divmod(len(a), n)
return (a[i*k+min(i, m):(i+1)*k+min(i+1, m)] for i in range(n))
slice_inds = split(range(N), slices)
for si in slice_inds:
mini_z = z[si]
mini_xy = xy[si]
inds = utils.basic.sub2ind(H, W, mini_xy[:,1], mini_xy[:,0]).long()
depth[inds] = mini_z
# cool; this is rougly as fast as the parallel, and as accurate as the serial
if False:
print('inds', inds.shape)
unique, inverse, counts = torch.unique(inds, return_inverse=True, return_counts=True)
print('unique', unique.shape)
perm = torch.arange(inverse.size(0), dtype=inverse.dtype, device=inverse.device)
inverse, perm = inverse.flip([0]), perm.flip([0])
perm = inverse.new_empty(unique.size(0)).scatter_(0, inverse, perm)
# new_inds = inds[inverse_inds]
# depth[new_inds] = z[unique_inds]
depth[unique] = z[perm]
# now for the duplicates...
dup = counts > 1
dup_unique = unique[dup]
print('dup_unique', dup_unique.shape)
depth[dup_unique] = 0.5
if force_positive:
# valid = (depth > 0.0).float()
depth[torch.where(depth == 0.0)] = max_val
# else:
# valid = torch.ones_like(depth)
valid = (depth > 0.0).float() * (depth < max_val).float()
depth = torch.reshape(depth, [1, H, W])
valid = torch.reshape(valid, [1, H, W])
return depth, valid
def create_depth_image(pix_T_cam, xyz_cam, H, W, offset_amount=0, max_val=100.0, serial=False, slices=20):
B, N, D = list(xyz_cam.shape)
assert(D==3)
B2, E, F = list(pix_T_cam.shape)
assert(B==B2)
assert(E==4)
assert(F==4)
xy = apply_pix_T_cam(pix_T_cam, xyz_cam)
z = xyz_cam[:,:,2]
depth = torch.zeros(B, 1, H, W, dtype=torch.float32, device=xyz_cam.device)
valid = torch.zeros(B, 1, H, W, dtype=torch.float32, device=xyz_cam.device)
for b in list(range(B)):
xy_b, z_b = xy[b], z[b]
ind = z_b > 0
xy_b = xy_b[ind]
z_b = z_b[ind]
depth_b, valid_b = create_depth_image_single(xy_b, z_b, H, W, max_val=max_val, serial=serial, slices=slices)
if offset_amount:
depth_b = depth_b.reshape(-1)
valid_b = valid_b.reshape(-1)
for off_x in range(offset_amount):
for off_y in range(offset_amount):
for sign in [-1,1]:
offset = np.array([sign*off_x,sign*off_y]).astype(np.float32)
offset = torch.from_numpy(offset).reshape(1, 2).to(xyz_cam.device)
# offsets.append(offset)
depth_, valid_ = create_depth_image_single(xy_b + offset, z_b, H, W, max_val=max_val)
depth_ = depth_.reshape(-1)
valid_ = valid_.reshape(-1)
# at invalid locations, use this new value
depth_b[valid_b==0] = depth_[valid_b==0]
valid_b[valid_b==0] = valid_[valid_b==0]
depth_b = depth_b.reshape(1, H, W)
valid_b = valid_b.reshape(1, H, W)
depth[b] = depth_b
valid[b] = valid_b
return depth, valid