CoolFace
Apppublic

iti/HandMesh

sourceHugging Faceupdated 5y agoView on Hugging Face
0likes
mesh_sampling.py304 linesDownload Raw Back to utils
1import math2import heapq3import numpy as np4import os5import scipy.sparse as sp6from psbody.mesh import Mesh7 8 9def row(A):10    return A.reshape((1, -1))11 12 13def col(A):14    return A.reshape((-1, 1))15 16 17def get_vert_connectivity(mesh_v, mesh_f):18    """Returns a sparse matrix (of size #verts x #verts) where each nonzero19    element indicates a neighborhood relation. For example, if there is a20    nonzero element in position (15,12), that means vertex 15 is connected21    by an edge to vertex 12."""22 23    vpv = sp.csc_matrix((len(mesh_v), len(mesh_v)))24 25    # for each column in the faces...26    for i in range(3):27        IS = mesh_f[:, i]28        JS = mesh_f[:, (i + 1) % 3]29        data = np.ones(len(IS))30        ij = np.vstack((row(IS.ravel()), row(JS.ravel())))31        mtx = sp.csc_matrix((data, ij), shape=vpv.shape)32        vpv = vpv + mtx + mtx.T33 34    return vpv35 36 37def get_vertices_per_edge(mesh_v, mesh_f):38    """Returns an Ex2 array of adjacencies between vertices, where39    each element in the array is a vertex index. Each edge is included40    only once. If output of get_faces_per_edge is provided, this is used to41    avoid call to get_vert_connectivity()"""42 43    vc = sp.coo_matrix(get_vert_connectivity(mesh_v, mesh_f))44    result = np.hstack((col(vc.row), col(vc.col)))45    result = result[result[:, 0] < result[:, 1]]  # for uniqueness46 47    return result48 49 50def vertex_quadrics(mesh):51    """Computes a quadric for each vertex in the Mesh.52 53    Returns:54       v_quadrics: an (N x 4 x 4) array, where N is # vertices.55    """56 57    # Allocate quadrics58    v_quadrics = np.zeros((59        len(mesh.v),60        4,61        4,62    ))63 64    # For each face...65    for f_idx in range(len(mesh.f)):66 67        # Compute normalized plane equation for that face68        vert_idxs = mesh.f[f_idx]69        verts = np.hstack((mesh.v[vert_idxs], np.array([1, 1,70                                                        1]).reshape(-1, 1)))71        u, s, v = np.linalg.svd(verts)72        eq = v[-1, :].reshape(-1, 1)73        eq = eq / (np.linalg.norm(eq[0:3]))74 75        # Add the outer product of the plane equation to the76        # quadrics of the vertices for this face77        for k in range(3):78            v_quadrics[mesh.f[f_idx, k], :, :] += np.outer(eq, eq)79 80    return v_quadrics81 82 83def setup_deformation_transfer(source, target, use_normals=False):84    rows = np.zeros(3 * target.v.shape[0])85    cols = np.zeros(3 * target.v.shape[0])86    coeffs_v = np.zeros(3 * target.v.shape[0])87    coeffs_n = np.zeros(3 * target.v.shape[0])88 89    nearest_faces, nearest_parts, nearest_vertices = source.compute_aabb_tree(90    ).nearest(target.v, True)91    nearest_faces = nearest_faces.ravel().astype(np.int64)92    nearest_parts = nearest_parts.ravel().astype(np.int64)93    nearest_vertices = nearest_vertices.ravel()94 95    for i in range(target.v.shape[0]):96        # Closest triangle index97        f_id = nearest_faces[i]98        # Closest triangle vertex ids99        nearest_f = source.f[f_id]100 101        # Closest surface point102        nearest_v = nearest_vertices[3 * i:3 * i + 3]103        # Distance vector to the closest surface point104        dist_vec = target.v[i] - nearest_v105 106        rows[3 * i:3 * i + 3] = i * np.ones(3)107        cols[3 * i:3 * i + 3] = nearest_f108 109        n_id = nearest_parts[i]110        if n_id == 0:111            # Closest surface point in triangle112            A = np.vstack((source.v[nearest_f])).T113            coeffs_v[3 * i:3 * i + 3] = np.linalg.lstsq(A, nearest_v,114                                                        rcond=-1)[0]115        elif n_id > 0 and n_id <= 3:116            # Closest surface point on edge117            A = np.vstack((source.v[nearest_f[n_id - 1]],118                           source.v[nearest_f[n_id % 3]])).T119            tmp_coeffs = np.linalg.lstsq(A, target.v[i], rcond=-1)[0]120            coeffs_v[3 * i + n_id - 1] = tmp_coeffs[0]121            coeffs_v[3 * i + n_id % 3] = tmp_coeffs[1]122        else:123            # Closest surface point a vertex124            coeffs_v[3 * i + n_id - 4] = 1.0125 126    matrix = sp.csc_matrix((coeffs_v, (rows, cols)),127                           shape=(target.v.shape[0], source.v.shape[0]))128    return matrix129 130 131def qslim_decimator_transformer(mesh, factor=None, n_verts_desired=None):132    """Return a simplified version of this mesh.133 134    A Qslim-style approach is used here.135 136    :param factor: fraction of the original vertices to retain137    :param n_verts_desired: number of the original vertices to retain138    :returns: new_faces: An Fx3 array of faces, mtx: Transformation matrix139    """140 141    if factor is None and n_verts_desired is None:142        raise Exception('Need either factor or n_verts_desired.')143 144    if n_verts_desired is None:145        n_verts_desired = math.ceil(len(mesh.v) * factor)146 147    Qv = vertex_quadrics(mesh)148 149    # fill out a sparse matrix indicating vertex-vertex adjacency150    # from psbody.mesh.topology.connectivity import get_vertices_per_edge151    vert_adj = get_vertices_per_edge(mesh.v, mesh.f)152    # vert_adj = sp.lil_matrix((len(mesh.v), len(mesh.v)))153    # for f_idx in range(len(mesh.f)):154    #     vert_adj[mesh.f[f_idx], mesh.f[f_idx]] = 1155 156    vert_adj = sp.csc_matrix(157        (vert_adj[:, 0] * 0 + 1, (vert_adj[:, 0], vert_adj[:, 1])),158        shape=(len(mesh.v), len(mesh.v)))159    vert_adj = vert_adj + vert_adj.T160    vert_adj = vert_adj.tocoo()161 162    def collapse_cost(Qv, r, c, v):163        Qsum = Qv[r, :, :] + Qv[c, :, :]164        p1 = np.vstack((v[r].reshape(-1, 1), np.array([1]).reshape(-1, 1)))165        p2 = np.vstack((v[c].reshape(-1, 1), np.array([1]).reshape(-1, 1)))166 167        destroy_c_cost = p1.T.dot(Qsum).dot(p1)168        destroy_r_cost = p2.T.dot(Qsum).dot(p2)169        result = {170            'destroy_c_cost': destroy_c_cost,171            'destroy_r_cost': destroy_r_cost,172            'collapse_cost': min([destroy_c_cost, destroy_r_cost]),173            'Qsum': Qsum174        }175        return result176 177    # construct a queue of edges with costs178    queue = []179    for k in range(vert_adj.nnz):180        r = vert_adj.row[k]181        c = vert_adj.col[k]182 183        if r > c:184            continue185 186        cost = collapse_cost(Qv, r, c, mesh.v)['collapse_cost']187        heapq.heappush(queue, (cost, (r, c)))188 189    # decimate190    collapse_list = []191    nverts_total = len(mesh.v)192    faces = mesh.f.copy()193    while nverts_total > n_verts_desired:194        e = heapq.heappop(queue)195        r = e[1][0]196        c = e[1][1]197        if r == c:198            continue199 200        cost = collapse_cost(Qv, r, c, mesh.v)201        if cost['collapse_cost'] > e[0]:202            heapq.heappush(queue, (cost['collapse_cost'], e[1]))203            # print 'found outdated cost, %.2f < %.2f' % (e[0], cost['collapse_cost'])204            continue205        else:206 207            # update old vert idxs to new one,208            # in queue and in face list209            if cost['destroy_c_cost'] < cost['destroy_r_cost']:210                to_destroy = c211                to_keep = r212            else:213                to_destroy = r214                to_keep = c215 216            collapse_list.append([to_keep, to_destroy])217 218            # in our face array, replace "to_destroy" vertidx with "to_keep" vertidx219            np.place(faces, faces == to_destroy, to_keep)220 221            # same for queue222            which1 = [223                idx for idx in range(len(queue))224                if queue[idx][1][0] == to_destroy225            ]226            which2 = [227                idx for idx in range(len(queue))228                if queue[idx][1][1] == to_destroy229            ]230            for k in which1:231                queue[k] = (queue[k][0], (to_keep, queue[k][1][1]))232            for k in which2:233                queue[k] = (queue[k][0], (queue[k][1][0], to_keep))234 235            Qv[r, :, :] = cost['Qsum']236            Qv[c, :, :] = cost['Qsum']237 238            a = faces[:, 0] == faces[:, 1]239            b = faces[:, 1] == faces[:, 2]240            c = faces[:, 2] == faces[:, 0]241 242            # remove degenerate faces243            def logical_or3(x, y, z):244                return np.logical_or(x, np.logical_or(y, z))245 246            faces_to_keep = np.logical_not(logical_or3(a, b, c))247            faces = faces[faces_to_keep, :].copy()248 249        nverts_total = (len(np.unique(faces.flatten())))250 251    new_faces, mtx = _get_sparse_transform(faces, len(mesh.v))252    return new_faces, mtx253 254 255def _get_sparse_transform(faces, num_original_verts):256    verts_left = np.unique(faces.flatten())257    IS = np.arange(len(verts_left))258    JS = verts_left259    data = np.ones(len(JS))260 261    mp = np.arange(0, np.max(faces.flatten()) + 1)262    mp[JS] = IS263    new_faces = mp[faces.copy().flatten()].reshape((-1, 3))264 265    ij = np.vstack((IS.flatten(), JS.flatten()))266    mtx = sp.csc_matrix((data, ij),267                        shape=(len(verts_left), num_original_verts))268 269    return (new_faces, mtx)270 271 272def generate_transform_matrices(mesh, factors):273    """Generates len(factors) meshes, each of them is scaled by factors[i] and274       computes the transformations between them.275 276    Returns:277       M: a set of meshes downsampled from mesh by a factor specified in factors.278       A: Adjacency matrix for each of the meshes279       D: csc_matrix Downsampling transforms between each of the meshes280       U: Upsampling transforms between each of the meshes281       F: a list of faces282    """283 284    factors = map(lambda x: 1.0 / x, factors)285    M, A, D, U, F, V = [], [], [], [], [], []286    F.append(mesh.f)  # F[0]287    V.append(mesh.v)288    A.append(get_vert_connectivity(mesh.v, mesh.f).astype('float32'))  # A[0]289    M.append(mesh)  # M[0]290 291    for factor in factors:292        ds_f, ds_D = qslim_decimator_transformer(M[-1], factor=factor)293        D.append(ds_D.astype('float32'))294        new_mesh_v = ds_D.dot(M[-1].v)295        new_mesh = Mesh(v=new_mesh_v, f=ds_f)296        F.append(new_mesh.f)297        V.append(new_mesh.v)298        M.append(new_mesh)299        A.append(300            get_vert_connectivity(new_mesh.v, new_mesh.f).astype('float32'))301        U.append(setup_deformation_transfer(M[-1], M[-2]).astype('float32'))302 303    return M, A, D, U, F, V304