Spaces:
Running
Running
| import numpy as np | |
| # import scipy.sparse as sparse | |
| import visualization.Animation as Animation | |
| """ Family Functions """ | |
| def joints(parents): | |
| """ | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| joints : (J) ndarray | |
| Array of joint indices | |
| """ | |
| return np.arange(len(parents), dtype=int) | |
| def joints_list(parents): | |
| """ | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| joints : [ndarray] | |
| List of arrays of joint idices for | |
| each joint | |
| """ | |
| return list(joints(parents)[:, np.newaxis]) | |
| def parents_list(parents): | |
| """ | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| parents : [ndarray] | |
| List of arrays of joint idices for | |
| the parents of each joint | |
| """ | |
| return list(parents[:, np.newaxis]) | |
| def children_list(parents): | |
| """ | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| children : [ndarray] | |
| List of arrays of joint indices for | |
| the children of each joint | |
| """ | |
| def joint_children(i): | |
| return [j for j, p in enumerate(parents) if p == i] | |
| return list(map(lambda j: np.array(joint_children(j)), joints(parents))) | |
| def descendants_list(parents): | |
| """ | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| descendants : [ndarray] | |
| List of arrays of joint idices for | |
| the descendants of each joint | |
| """ | |
| children = children_list(parents) | |
| def joint_descendants(i): | |
| return sum([joint_descendants(j) for j in children[i]], list(children[i])) | |
| return list(map(lambda j: np.array(joint_descendants(j)), joints(parents))) | |
| def ancestors_list(parents): | |
| """ | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| ancestors : [ndarray] | |
| List of arrays of joint idices for | |
| the ancestors of each joint | |
| """ | |
| decendants = descendants_list(parents) | |
| def joint_ancestors(i): | |
| return [j for j in joints(parents) if i in decendants[j]] | |
| return list(map(lambda j: np.array(joint_ancestors(j)), joints(parents))) | |
| """ Mask Functions """ | |
| def mask(parents, filter): | |
| """ | |
| Constructs a Mask for a give filter | |
| A mask is a (J, J) ndarray truth table for a given | |
| condition over J joints. For example there | |
| may be a mask specifying if a joint N is a | |
| child of another joint M. | |
| This could be constructed into a mask using | |
| `m = mask(parents, children_list)` and the condition | |
| of childhood tested using `m[N, M]`. | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| filter : (J) ndarray -> [ndarray] | |
| function that outputs a list of arrays | |
| of joint indices for some condition | |
| Returns | |
| ------- | |
| mask : (N, N) ndarray | |
| boolean truth table of given condition | |
| """ | |
| m = np.zeros((len(parents), len(parents))).astype(bool) | |
| jnts = joints(parents) | |
| fltr = filter(parents) | |
| for i, f in enumerate(fltr): m[i, :] = np.any(jnts[:, np.newaxis] == f[np.newaxis, :], axis=1) | |
| return m | |
| def joints_mask(parents): return np.eye(len(parents)).astype(bool) | |
| def children_mask(parents): return mask(parents, children_list) | |
| def parents_mask(parents): return mask(parents, parents_list) | |
| def descendants_mask(parents): return mask(parents, descendants_list) | |
| def ancestors_mask(parents): return mask(parents, ancestors_list) | |
| """ Search Functions """ | |
| def joint_chain_ascend(parents, start, end): | |
| chain = [] | |
| while start != end: | |
| chain.append(start) | |
| start = parents[start] | |
| chain.append(end) | |
| return np.array(chain, dtype=int) | |
| """ Constraints """ | |
| def constraints(anim, **kwargs): | |
| """ | |
| Constraint list for Animation | |
| This constraint list can be used in the | |
| VerletParticle solver to constrain | |
| a animation global joint positions. | |
| Parameters | |
| ---------- | |
| anim : Animation | |
| Input animation | |
| masses : (F, J) ndarray | |
| Optional list of masses | |
| for joints J across frames F | |
| defaults to weighting by | |
| vertical height | |
| Returns | |
| ------- | |
| constraints : [(int, int, (F, J) ndarray, (F, J) ndarray, (F, J) ndarray)] | |
| A list of constraints in the format: | |
| (Joint1, Joint2, Masses1, Masses2, Lengths) | |
| """ | |
| masses = kwargs.pop('masses', None) | |
| children = children_list(anim.parents) | |
| constraints = [] | |
| points_offsets = Animation.offsets_global(anim) | |
| points = Animation.positions_global(anim) | |
| if masses is None: | |
| masses = 1.0 / (0.1 + np.absolute(points_offsets[:, 1])) | |
| masses = masses[np.newaxis].repeat(len(anim), axis=0) | |
| for j in range(anim.shape[1]): | |
| """ Add constraints between all joints and their children """ | |
| for c0 in children[j]: | |
| dists = np.sum((points[:, c0] - points[:, j]) ** 2.0, axis=1) ** 0.5 | |
| constraints.append((c0, j, masses[:, c0], masses[:, j], dists)) | |
| """ Add constraints between all children of joint """ | |
| for c1 in children[j]: | |
| if c0 == c1: continue | |
| dists = np.sum((points[:, c0] - points[:, c1]) ** 2.0, axis=1) ** 0.5 | |
| constraints.append((c0, c1, masses[:, c0], masses[:, c1], dists)) | |
| return constraints | |
| """ Graph Functions """ | |
| def graph(anim): | |
| """ | |
| Generates a weighted adjacency matrix | |
| using local joint distances along | |
| the skeletal structure. | |
| Joints which are not connected | |
| are assigned the weight `0`. | |
| Joints which actually have zero distance | |
| between them, but are still connected, are | |
| perturbed by some minimal amount. | |
| The output of this routine can be used | |
| with the `scipy.sparse.csgraph` | |
| routines for graph analysis. | |
| Parameters | |
| ---------- | |
| anim : Animation | |
| input animation | |
| Returns | |
| ------- | |
| graph : (N, N) ndarray | |
| weight adjacency matrix using | |
| local distances along the | |
| skeletal structure from joint | |
| N to joint M. If joints are not | |
| directly connected are assigned | |
| the weight `0`. | |
| """ | |
| graph = np.zeros(anim.shape[1], anim.shape[1]) | |
| lengths = np.sum(anim.offsets ** 2.0, axis=1) ** 0.5 + 0.001 | |
| for i, p in enumerate(anim.parents): | |
| if p == -1: continue | |
| graph[i, p] = lengths[p] | |
| graph[p, i] = lengths[p] | |
| return graph | |
| def distances(anim): | |
| """ | |
| Generates a distance matrix for | |
| pairwise joint distances along | |
| the skeletal structure | |
| Parameters | |
| ---------- | |
| anim : Animation | |
| input animation | |
| Returns | |
| ------- | |
| distances : (N, N) ndarray | |
| array of pairwise distances | |
| along skeletal structure | |
| from some joint N to some | |
| joint M | |
| """ | |
| distances = np.zeros((anim.shape[1], anim.shape[1])) | |
| generated = distances.copy().astype(bool) | |
| joint_lengths = np.sum(anim.offsets ** 2.0, axis=1) ** 0.5 | |
| joint_children = children_list(anim) | |
| joint_parents = parents_list(anim) | |
| def find_distance(distances, generated, prev, i, j): | |
| """ If root, identity, or already generated, return """ | |
| if j == -1: return (0.0, True) | |
| if j == i: return (0.0, True) | |
| if generated[i, j]: return (distances[i, j], True) | |
| """ Find best distances along parents and children """ | |
| par_dists = [(joint_lengths[j], find_distance(distances, generated, j, i, p)) for p in joint_parents[j] if | |
| p != prev] | |
| out_dists = [(joint_lengths[c], find_distance(distances, generated, j, i, c)) for c in joint_children[j] if | |
| c != prev] | |
| """ Check valid distance and not dead end """ | |
| par_dists = [a + d for (a, (d, f)) in par_dists if f] | |
| out_dists = [a + d for (a, (d, f)) in out_dists if f] | |
| """ All dead ends """ | |
| if (out_dists + par_dists) == []: return (0.0, False) | |
| """ Get minimum path """ | |
| dist = min(out_dists + par_dists) | |
| distances[i, j] = dist; | |
| distances[j, i] = dist | |
| generated[i, j] = True; | |
| generated[j, i] = True | |
| for i in range(anim.shape[1]): | |
| for j in range(anim.shape[1]): | |
| find_distance(distances, generated, -1, i, j) | |
| return distances | |
| def edges(parents): | |
| """ | |
| Animation structure edges | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| edges : (M, 2) ndarray | |
| array of pairs where each | |
| pair contains two indices of a joints | |
| which corrisponds to an edge in the | |
| joint structure going from parent to child. | |
| """ | |
| return np.array(list(zip(parents, joints(parents)))[1:]) | |
| def incidence(parents): | |
| """ | |
| Incidence Matrix | |
| Parameters | |
| ---------- | |
| parents : (J) ndarray | |
| parents array | |
| Returns | |
| ------- | |
| incidence : (N, M) ndarray | |
| Matrix of N joint positions by | |
| M edges which each entry is either | |
| 1 or -1 and multiplication by the | |
| joint positions returns the an | |
| array of vectors along each edge | |
| of the structure | |
| """ | |
| es = edges(parents) | |
| inc = np.zeros((len(parents) - 1, len(parents))).astype(np.int) | |
| for i, e in enumerate(es): | |
| inc[i, e[0]] = 1 | |
| inc[i, e[1]] = -1 | |
| return inc.T | |