Spaces:
Running
Running
import glob | |
import os | |
import numpy as np | |
import torch | |
import torch.nn.functional as F | |
import trimesh | |
import scipy.sparse as sp | |
import collections | |
def rodrigues(theta): | |
"""Convert axis-angle representation to rotation matrix. | |
Args: | |
theta: size = [B, 3] | |
Returns: | |
Rotation matrix corresponding to the quaternion -- size = [B, 3, 3] | |
""" | |
l1norm = torch.norm(theta + 1e-8, p=2, dim=1) | |
angle = torch.unsqueeze(l1norm, -1) | |
normalized = torch.div(theta, angle) | |
angle = angle * 0.5 | |
v_cos = torch.cos(angle) | |
v_sin = torch.sin(angle) | |
quat = torch.cat([v_cos, v_sin * normalized], dim=1) | |
return quat2mat(quat) | |
def quat2mat(quat): | |
"""Convert quaternion coefficients to rotation matrix. | |
Args: | |
quat: size = [B, 4] 4 <===>(w, x, y, z) | |
Returns: | |
Rotation matrix corresponding to the quaternion -- size = [B, 3, 3] | |
""" | |
norm_quat = quat | |
norm_quat = norm_quat / norm_quat.norm(p=2, dim=1, keepdim=True) | |
w, x, y, z = norm_quat[:, 0], norm_quat[:, 1], norm_quat[:, 2], norm_quat[:, 3] | |
B = quat.size(0) | |
w2, x2, y2, z2 = w.pow(2), x.pow(2), y.pow(2), z.pow(2) | |
wx, wy, wz = w * x, w * y, w * z | |
xy, xz, yz = x * y, x * z, y * z | |
rotMat = torch.stack([w2 + x2 - y2 - z2, 2 * xy - 2 * wz, 2 * wy + 2 * xz, | |
2 * wz + 2 * xy, w2 - x2 + y2 - z2, 2 * yz - 2 * wx, | |
2 * xz - 2 * wy, 2 * wx + 2 * yz, w2 - x2 - y2 + z2], dim=1).view(B, 3, 3) | |
return rotMat | |
def inv_4x4(mats): | |
"""Calculate the inverse of homogeneous transformations | |
:param mats: [B, 4, 4] | |
:return: | |
""" | |
Rs = mats[:, :3, :3] | |
ts = mats[:, :3, 3:] | |
# R_invs = torch.transpose(Rs, 1, 2) | |
R_invs = torch.inverse(Rs) | |
t_invs = -torch.matmul(R_invs, ts) | |
Rt_invs = torch.cat([R_invs, t_invs], dim=-1) # [B, 3, 4] | |
device = R_invs.device | |
pad_row = torch.FloatTensor([0, 0, 0, 1]).to(device).view(1, 1, 4).expand(Rs.shape[0], -1, -1) | |
mat_invs = torch.cat([Rt_invs, pad_row], dim=1) | |
return mat_invs | |
def as_mesh(scene_or_mesh): | |
""" | |
Convert a possible scene to a mesh. | |
If conversion occurs, the returned mesh has only vertex and face data. | |
""" | |
if isinstance(scene_or_mesh, trimesh.Scene): | |
if len(scene_or_mesh.geometry) == 0: | |
mesh = None # empty scene | |
else: | |
# we lose texture information here | |
mesh = trimesh.util.concatenate( | |
tuple(trimesh.Trimesh(vertices=g.vertices, faces=g.faces) | |
for g in scene_or_mesh.geometry.values())) | |
else: | |
assert(isinstance(scene_or_mesh, trimesh.Trimesh)) | |
mesh = scene_or_mesh | |
return mesh | |
def get_edge_unique(faces): | |
""" | |
Parameters | |
------------ | |
faces: n x 3 int array | |
Should be from a watertight mesh without degenerated triangles and intersection | |
""" | |
faces = np.asanyarray(faces) | |
# each face has three edges | |
edges = faces[:, [0, 1, 1, 2, 2, 0]].reshape((-1, 2)) | |
flags = edges[:, 0] < edges[:, 1] | |
edges = edges[flags] | |
return edges | |
def get_neighbors(edges): | |
neighbors = collections.defaultdict(set) | |
[(neighbors[edge[0]].add(edge[1]), | |
neighbors[edge[1]].add(edge[0])) | |
for edge in edges] | |
max_index = edges.max() + 1 | |
array = [list(neighbors[i]) for i in range(max_index)] | |
return array | |
def construct_degree_matrix(vnum, faces): | |
row = col = list(range(vnum)) | |
value = [0] * vnum | |
es = get_edge_unique(faces) | |
for e in es: | |
if e[0] < e[1]: | |
value[e[0]] += 1 | |
value[e[0]] += 1 | |
dm = sp.coo_matrix((value, (row, col)), shape=(vnum, vnum), dtype=np.float32) | |
return dm | |
def construct_neighborhood_matrix(vnum, faces): | |
row = list() | |
col = list() | |
value = list() | |
es = get_edge_unique(faces) | |
for e in es: | |
if e[0] < e[1]: | |
row.append(e[0]) | |
col.append(e[1]) | |
value.append(1) | |
row.append(e[1]) | |
col.append(e[0]) | |
value.append(1) | |
nm = sp.coo_matrix((value, (row, col)), shape=(vnum, vnum), dtype=np.float32) | |
return nm | |
def construct_laplacian_matrix(vnum, faces, normalized=False): | |
edges = get_edge_unique(faces) | |
neighbors = get_neighbors(edges) | |
col = np.concatenate(neighbors) | |
row = np.concatenate([[i] * len(n) | |
for i, n in enumerate(neighbors)]) | |
col = np.concatenate([col, np.arange(0, vnum)]) | |
row = np.concatenate([row, np.arange(0, vnum)]) | |
if normalized: | |
data = [[1.0 / len(n)] * len(n) for n in neighbors] | |
data += [[-1.0] * vnum] | |
else: | |
data = [[1.0] * len(n) for n in neighbors] | |
data += [[-len(n) for n in neighbors]] | |
data = np.concatenate(data) | |
# create the sparse matrix | |
matrix = sp.coo_matrix((data, (row, col)), shape=[vnum] * 2) | |
return matrix | |
def rotationx_4x4(theta): | |
return np.array([ | |
[1.0, 0.0, 0.0, 0.0], | |
[0.0, np.cos(theta / 180 * np.pi), np.sin(theta / 180 * np.pi), 0.0], | |
[0.0, -np.sin(theta / 180 * np.pi), np.cos(theta / 180 * np.pi), 0.0], | |
[0.0, 0.0, 0.0, 1.0] | |
]) | |
def rotationy_4x4(theta): | |
return np.array([ | |
[np.cos(theta / 180 * np.pi), 0.0, np.sin(theta / 180 * np.pi), 0.0], | |
[0.0, 1.0, 0.0, 0.0], | |
[-np.sin(theta / 180 * np.pi), 0.0, np.cos(theta / 180 * np.pi), 0.0], | |
[0.0, 0.0, 0.0, 1.0] | |
]) | |
def rotationz_4x4(theta): | |
return np.array([ | |
[np.cos(theta / 180 * np.pi), np.sin(theta / 180 * np.pi), 0.0, 0.0], | |
[-np.sin(theta / 180 * np.pi), np.cos(theta / 180 * np.pi), 0.0, 0.0], | |
[0.0, 0.0, 1.0, 0.0], | |
[0.0, 0.0, 0.0, 1.0] | |
]) | |
def rotationx_3x3(theta): | |
return np.array([ | |
[1.0, 0.0, 0.0], | |
[0.0, np.cos(theta / 180 * np.pi), np.sin(theta / 180 * np.pi)], | |
[0.0, -np.sin(theta / 180 * np.pi), np.cos(theta / 180 * np.pi)], | |
]) | |
def rotationy_3x3(theta): | |
return np.array([ | |
[np.cos(theta / 180 * np.pi), 0.0, np.sin(theta / 180 * np.pi)], | |
[0.0, 1.0, 0.0], | |
[-np.sin(theta / 180 * np.pi), 0.0, np.cos(theta / 180 * np.pi)], | |
]) | |
def rotationz_3x3(theta): | |
return np.array([ | |
[np.cos(theta / 180 * np.pi), np.sin(theta / 180 * np.pi), 0.0], | |
[-np.sin(theta / 180 * np.pi), np.cos(theta / 180 * np.pi), 0.0], | |
[0.0, 0.0, 1.0], | |
]) | |
def generate_point_grids(vol_res): | |
x_coords = np.array(range(0, vol_res), dtype=np.float32) | |
y_coords = np.array(range(0, vol_res), dtype=np.float32) | |
z_coords = np.array(range(0, vol_res), dtype=np.float32) | |
yv, xv, zv = np.meshgrid(x_coords, y_coords, z_coords) | |
xv = np.reshape(xv, (-1, 1)) | |
yv = np.reshape(yv, (-1, 1)) | |
zv = np.reshape(zv, (-1, 1)) | |
pts = np.concatenate([xv, yv, zv], axis=-1) | |
pts = pts.astype(np.float32) | |
return pts | |
def infer_occupancy_value_grid_octree(test_res, pts, query_fn, init_res=64, ignore_thres=0.05): | |
pts = np.reshape(pts, (test_res, test_res, test_res, 3)) | |
pts_ov = np.zeros([test_res, test_res, test_res]) | |
dirty = np.ones_like(pts_ov, dtype=np.bool) | |
grid_mask = np.zeros_like(pts_ov, dtype=np.bool) | |
reso = test_res // init_res | |
while reso > 0: | |
grid_mask[0:test_res:reso, 0:test_res:reso, 0:test_res:reso] = True | |
test_mask = np.logical_and(grid_mask, dirty) | |
pts_ = pts[test_mask] | |
pts_ov[test_mask] = np.reshape(query_fn(pts_), pts_ov[test_mask].shape) | |
if reso <= 1: | |
break | |
for x in range(0, test_res - reso, reso): | |
for y in range(0, test_res - reso, reso): | |
for z in range(0, test_res - reso, reso): | |
# if center marked, return | |
if not dirty[x + reso // 2, y + reso // 2, z + reso // 2]: | |
continue | |
v0 = pts_ov[x, y, z] | |
v1 = pts_ov[x, y, z + reso] | |
v2 = pts_ov[x, y + reso, z] | |
v3 = pts_ov[x, y + reso, z + reso] | |
v4 = pts_ov[x + reso, y, z] | |
v5 = pts_ov[x + reso, y, z + reso] | |
v6 = pts_ov[x + reso, y + reso, z] | |
v7 = pts_ov[x + reso, y + reso, z + reso] | |
v = np.array([v0, v1, v2, v3, v4, v5, v6, v7]) | |
v_min = np.min(v) | |
v_max = np.max(v) | |
# this cell is all the same | |
if (v_max - v_min) < ignore_thres: | |
pts_ov[x:x + reso, y:y + reso, z:z + reso] = (v_max + v_min) / 2 | |
dirty[x:x + reso, y:y + reso, z:z + reso] = False | |
reso //= 2 | |
return pts_ov | |
def batch_rod2quat(rot_vecs): | |
batch_size = rot_vecs.shape[0] | |
angle = torch.norm(rot_vecs + 1e-16, dim=1, keepdim=True) | |
rot_dir = rot_vecs / angle | |
cos = torch.cos(angle / 2) | |
sin = torch.sin(angle / 2) | |
# Bx1 arrays | |
rx, ry, rz = torch.split(rot_dir, 1, dim=1) | |
qx = rx * sin | |
qy = ry * sin | |
qz = rz * sin | |
qw = cos-1.0 | |
return torch.cat([qx,qy,qz,qw], dim=1) | |
def batch_quat2matrix(rvec): | |
''' | |
args: | |
rvec: (B, N, 4) | |
''' | |
B, N, _ = rvec.size() | |
theta = torch.sqrt(1e-5 + torch.sum(rvec ** 2, dim=2)) | |
rvec = rvec / theta[:, :, None] | |
return torch.stack(( | |
1. - 2. * rvec[:, :, 1] ** 2 - 2. * rvec[:, :, 2] ** 2, | |
2. * (rvec[:, :, 0] * rvec[:, :, 1] - rvec[:, :, 2] * rvec[:, :, 3]), | |
2. * (rvec[:, :, 0] * rvec[:, :, 2] + rvec[:, :, 1] * rvec[:, :, 3]), | |
2. * (rvec[:, :, 0] * rvec[:, :, 1] + rvec[:, :, 2] * rvec[:, :, 3]), | |
1. - 2. * rvec[:, :, 0] ** 2 - 2. * rvec[:, :, 2] ** 2, | |
2. * (rvec[:, :, 1] * rvec[:, :, 2] - rvec[:, :, 0] * rvec[:, :, 3]), | |
2. * (rvec[:, :, 0] * rvec[:, :, 2] - rvec[:, :, 1] * rvec[:, :, 3]), | |
2. * (rvec[:, :, 0] * rvec[:, :, 3] + rvec[:, :, 1] * rvec[:, :, 2]), | |
1. - 2. * rvec[:, :, 0] ** 2 - 2. * rvec[:, :, 1] ** 2 | |
), dim=2).view(B, N, 3, 3) | |
def get_posemap(map_type, n_joints, parents, n_traverse=1, normalize=True): | |
pose_map = torch.zeros(n_joints,n_joints-1) | |
if map_type == 'parent': | |
for i in range(n_joints-1): | |
pose_map[i+1,i] = 1.0 | |
elif map_type == 'children': | |
for i in range(n_joints-1): | |
parent = parents[i+1] | |
for j in range(n_traverse): | |
pose_map[parent, i] += 1.0 | |
if parent == 0: | |
break | |
parent = parents[parent] | |
if normalize: | |
pose_map /= pose_map.sum(0,keepdim=True)+1e-16 | |
elif map_type == 'both': | |
for i in range(n_joints-1): | |
pose_map[i+1,i] += 1.0 | |
parent = parents[i+1] | |
for j in range(n_traverse): | |
pose_map[parent, i] += 1.0 | |
if parent == 0: | |
break | |
parent = parents[parent] | |
if normalize: | |
pose_map /= pose_map.sum(0,keepdim=True)+1e-16 | |
else: | |
raise NotImplementedError('unsupported pose map type [%s]' % map_type) | |
pose_map = torch.cat([torch.zeros(n_joints, 1), pose_map], dim=1) | |
return pose_map | |
def vertices_to_triangles(vertices, faces): | |
""" | |
:param vertices: [batch size, number of vertices, 3] | |
:param faces: [batch size, number of faces, 3) | |
:return: [batch size, number of faces, 3, 3] | |
""" | |
assert (vertices.ndimension() == 3) | |
assert (faces.ndimension() == 3) | |
assert (vertices.shape[0] == faces.shape[0]) | |
assert (vertices.shape[2] == 3) | |
assert (faces.shape[2] == 3) | |
bs, nv = vertices.shape[:2] | |
bs, nf = faces.shape[:2] | |
device = vertices.device | |
faces = faces + (torch.arange(bs, dtype=torch.int32).to(device) * nv)[:, None, None] | |
vertices = vertices.reshape((bs * nv, 3)) | |
# pytorch only supports long and byte tensors for indexing | |
return vertices[faces.long()] | |
def calc_face_normals(vertices, faces): | |
assert len(vertices.shape) == 3 | |
assert len(faces.shape) == 2 | |
if isinstance(faces, np.ndarray): | |
faces = torch.from_numpy(faces.astype(np.int64)).to(vertices.device) | |
batch_size, pt_num = vertices.shape[:2] | |
face_num = faces.shape[0] | |
triangles = vertices_to_triangles(vertices, faces.unsqueeze(0).expand(batch_size, -1, -1)) | |
triangles = triangles.reshape((batch_size * face_num, 3, 3)) | |
v10 = triangles[:, 0] - triangles[:, 1] | |
v12 = triangles[:, 2] - triangles[:, 1] | |
# pytorch normalize divides by max(norm, eps) instead of (norm+eps) in chainer | |
normals = F.normalize(torch.cross(v10, v12), eps=1e-5) | |
normals = normals.reshape((batch_size, face_num, 3)) | |
return normals | |
def calc_vert_normals(vertices, faces): | |
""" | |
vertices: [B, N, 3] | |
faces: [F, 3] | |
""" | |
normals = torch.zeros_like(vertices) | |
v0s = torch.index_select(vertices, dim=1, index=faces[:, 0]) # [B, F, 3] | |
v1s = torch.index_select(vertices, dim=1, index=faces[:, 1]) | |
v2s = torch.index_select(vertices, dim=1, index=faces[:, 2]) | |
normals = torch.index_add(normals, dim=1, index=faces[:, 1], source=torch.cross(v2s-v1s, v0s-v1s, dim=-1)) | |
normals = torch.index_add(normals, dim=1, index=faces[:, 2], source=torch.cross(v0s-v2s, v1s-v2s, dim=-1)) | |
normals = torch.index_add(normals, dim=1, index=faces[:, 0], source=torch.cross(v1s-v0s, v2s-v0s, dim=-1)) | |
normals = F.normalize(normals, dim=-1) | |
return normals | |
def calc_vert_normals_numpy(vertices, faces): | |
assert len(vertices.shape) == 2 | |
assert len(faces.shape) == 2 | |
nmls = np.zeros_like(vertices) | |
fv0 = vertices[faces[:, 0]] | |
fv1 = vertices[faces[:, 1]] | |
fv2 = vertices[faces[:, 2]] | |
face_nmls = np.cross(fv1-fv0, fv2-fv0, axis=-1) | |
face_nmls = face_nmls / (np.linalg.norm(face_nmls, axis=-1, keepdims=True) + 1e-20) | |
for f, fn in zip(faces, face_nmls): | |
nmls[f] += fn | |
nmls = nmls / (np.linalg.norm(nmls, axis=-1, keepdims=True) + 1e-20) | |
return nmls | |
def glUV2torchUV(gl_uv): | |
torch_uv = torch.stack([ | |
gl_uv[..., 0]*2.0-1.0, | |
gl_uv[..., 1]*-2.0+1.0 | |
], dim=-1) | |
return torch_uv | |
def normalize_vert_bbox(verts, dim=-1, per_axis=False): | |
bbox_min = torch.min(verts, dim=dim, keepdim=True)[0] | |
bbox_max = torch.max(verts, dim=dim, keepdim=True)[0] | |
verts = verts - 0.5 * (bbox_max + bbox_min) | |
if per_axis: | |
verts = 2 * verts / (bbox_max - bbox_min) | |
else: | |
verts = 2 * verts / torch.max(bbox_max-bbox_min, dim=dim, keepdim=True)[0] | |
return verts | |
def upsample_sdf_volume(sdf, upsample_factor): | |
assert sdf.shape[0] == sdf.shape[1] == sdf.shape[2] | |
coarse_resolution = sdf.shape[0] | |
fine_resolution = coarse_resolution * upsample_factor | |
sdf_interp_buffer = np.zeros([2, 2, 2, coarse_resolution, coarse_resolution, coarse_resolution], | |
dtype=np.float32) | |
dx_list = [0, 1, 0, 1, 0, 1, 0, 1] | |
dy_list = [0, 0, 1, 1, 0, 0, 1, 1] | |
dz_list = [0, 0, 0, 0, 1, 1, 1, 1] | |
for dx, dy, dz in zip(dx_list, dy_list, dz_list): | |
sdf_interp_buffer[dx, dy, dz, :, :, :] = np.roll(sdf, (-dx, -dy, -dz), axis=(0, 1, 2)) | |
sdf_fine = np.zeros([fine_resolution, fine_resolution, fine_resolution], dtype=np.float32) | |
for dx in range(upsample_factor): | |
for dy in range(upsample_factor): | |
for dz in range(upsample_factor): | |
wx = (1.0 - dx / upsample_factor) | |
wy = (1.0 - dy / upsample_factor) | |
wz = (1.0 - dz / upsample_factor) | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
wx * wy * wz * sdf_interp_buffer[0, 0, 0] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
(1.0 - wx) * wy * wz * sdf_interp_buffer[1, 0, 0] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
wx * (1.0 - wy) * wz * sdf_interp_buffer[0, 1, 0] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
(1.0 - wx) * (1.0 - wy) * wz * sdf_interp_buffer[1, 1, 0] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
wx * wy * (1.0 - wz) * sdf_interp_buffer[0, 0, 1] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
(1.0 - wx) * wy * (1.0 - wz) * sdf_interp_buffer[1, 0, 1] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
wx * (1.0 - wy) * (1.0 - wz) * sdf_interp_buffer[0, 1, 1] | |
sdf_fine[dx::upsample_factor, dy::upsample_factor, dz::upsample_factor] += \ | |
(1.0 - wx) * (1.0 - wy) * (1.0 - wz) * sdf_interp_buffer[1, 1, 1] | |
return sdf_fine | |
def search_nearest_correspondence(src, tgt): | |
tgt_idx = np.zeros(len(src), dtype=np.int32) | |
tgt_dist = np.zeros(len(src), dtype=np.float32) | |
for i in range(len(src)): | |
dist = np.linalg.norm(tgt - src[i:(i+1)], axis=1, keepdims=False) | |
tgt_idx[i] = np.argmin(dist) | |
tgt_dist[i] = dist[tgt_idx[i]] | |
return tgt_idx, tgt_dist | |
def estimate_rigid_transformation(src, tgt): | |
src = src.transpose() | |
tgt = tgt.transpose() | |
mu1, mu2 = src.mean(axis=1, keepdims=True), tgt.mean(axis=1, keepdims=True) | |
X1, X2 = src - mu1, tgt - mu2 | |
K = X1.dot(X2.T) | |
U, s, Vh = np.linalg.svd(K) | |
V = Vh.T | |
Z = np.eye(U.shape[0]) | |
Z[-1, -1] *= np.sign(np.linalg.det(U.dot(V.T))) | |
R = V.dot(Z.dot(U.T)) | |
t = mu2 - R.dot(mu1) | |
# orient, _ = cv.Rodrigues(R) | |
# orient = orient.reshape([-1]) | |
# t = t.reshape([-1]) | |
# return orient, t | |
transf = np.eye(4, dtype=np.float32) | |
transf[:3, :3] = R | |
transf[:3, 3] = t.reshape([-1]) | |
return transf | |