Spaces:
Runtime error
Runtime error
| 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 | |