Rignet / RigNet /geometric_proc /common_ops.py
ckc99u's picture
Upload 116 files
3b8bcb1 verified
#-------------------------------------------------------------------------------
# Name: common_ops.py
# Purpose: common functions for geometry processing
# RigNet Copyright 2020 University of Massachusetts
# RigNet is made available under General Public License Version 3 (GPLv3), or under a Commercial License.
# Please see the LICENSE README.txt file in the main directory for more information and instruction on using and licensing RigNet.
#-------------------------------------------------------------------------------
import numpy as np
import time
import open3d as o3d
from scipy.sparse import lil_matrix
from scipy.sparse.csgraph import dijkstra
def get_bones(skel):
"""
extract bones from skeleton struction
:param skel: input skeleton
:return: bones are B*6 array where each row consists starting and ending points of a bone
bone_name are a list of B elements, where each element consists starting and ending joint name
leaf_bones indicate if this bone is a virtual "leaf" bone.
We add virtual "leaf" bones to the leaf joints since they always have skinning weights as well
"""
bones = []
bone_name = []
leaf_bones = []
this_level = [skel.root]
while this_level:
next_level = []
for p_node in this_level:
p_pos = np.array(p_node.pos)
next_level += p_node.children
for c_node in p_node.children:
c_pos = np.array(c_node.pos)
bones.append(np.concatenate((p_pos, c_pos))[np.newaxis, :])
bone_name.append([p_node.name, c_node.name])
leaf_bones.append(False)
if len(c_node.children) == 0:
bones.append(np.concatenate((c_pos, c_pos))[np.newaxis, :])
bone_name.append([c_node.name, c_node.name+'_leaf'])
leaf_bones.append(True)
this_level = next_level
bones = np.concatenate(bones, axis=0)
return bones, bone_name, leaf_bones
def calc_surface_geodesic(mesh):
# We denselu sample 4000 points to be more accuracy.
samples = mesh.sample_points_poisson_disk(number_of_points=4000)
pts = np.asarray(samples.points)
pts_normal = np.asarray(samples.normals)
time1 = time.time()
N = len(pts)
verts_dist = np.sqrt(np.sum((pts[np.newaxis, ...] - pts[:, np.newaxis, :]) ** 2, axis=2))
verts_nn = np.argsort(verts_dist, axis=1)
conn_matrix = lil_matrix((N, N), dtype=np.float32)
for p in range(N):
nn_p = verts_nn[p, 1:6]
norm_nn_p = np.linalg.norm(pts_normal[nn_p], axis=1)
norm_p = np.linalg.norm(pts_normal[p])
cos_similar = np.dot(pts_normal[nn_p], pts_normal[p]) / (norm_nn_p * norm_p + 1e-10)
nn_p = nn_p[cos_similar > -0.5]
conn_matrix[p, nn_p] = verts_dist[p, nn_p]
[dist, predecessors] = dijkstra(conn_matrix, directed=False, indices=range(N),
return_predecessors=True, unweighted=False)
# replace inf distance with euclidean distance + 8
# 6.12 is the maximal geodesic distance without considering inf, I add 8 to be safer.
inf_pos = np.argwhere(np.isinf(dist))
if len(inf_pos) > 0:
euc_distance = np.sqrt(np.sum((pts[np.newaxis, ...] - pts[:, np.newaxis, :]) ** 2, axis=2))
dist[inf_pos[:, 0], inf_pos[:, 1]] = 8.0 + euc_distance[inf_pos[:, 0], inf_pos[:, 1]]
verts = np.array(mesh.vertices)
vert_pts_distance = np.sqrt(np.sum((verts[np.newaxis, ...] - pts[:, np.newaxis, :]) ** 2, axis=2))
vert_pts_nn = np.argmin(vert_pts_distance, axis=0)
surface_geodesic = dist[vert_pts_nn, :][:, vert_pts_nn]
time2 = time.time()
print('surface geodesic calculation: {} seconds'.format((time2 - time1)))
return surface_geodesic