pengc02's picture
upload_part1
7648567
raw
history blame
18.4 kB
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