def disk_conformal_mapping(mesh, lap_type='conformal', boundary=None, boundary_coords=None): """ Computes comformal mapping of a mesh to a disk, see the following references: Ulrich Pinkall and Konrad Polthier, “Computing Discrete Minimal Surfaces and Their Conjugates,” Experimental Mathematics, 1993, 1–33. and Mathieu Desbrun, Mark Meyer, and Pierre Alliez, “Intrinsic Parameterizations of Surface Meshes,” Computer Graphics Forum 21, no. 3 (2002): 209–18, https://doi.org/10.1111/1467-8659.00580. :param mesh: a trimesh object :param lap_type: type of mesh Laplacian to be used, see the function differential_geometry/compute_mesh_weights for more informations :param boundary: boundary of the mesh, resulting from the function topology/mesh_boundary :param boundary_coords: coordindates of the boundary vertices on the output disk, if None then uniform sampling :return: a trimesh object, planar disk representation of the input mesh """ if boundary is None: boundary_t = stop.mesh_boundary(mesh) boundary = boundary_t[0] boundary = np.array(boundary) if boundary_coords is None: p = boundary.size t = np.arange(0, 2 * np.math.pi, (2 * np.math.pi / p)) boundary_coords = np.array([np.cos(t), np.sin(t)]) L, LB = sdg.compute_mesh_laplacian(mesh, lap_type=lap_type) Nv = len(mesh.vertices) # np.array(mesh.vertex()).shape[0] print('Boundary Size:', boundary.shape) print('Laplacian Size:', L.shape) for i in boundary: L[i, :] = 0 L[i, i] = 1 L = L.tocsr() Rx = np.zeros(Nv) Ry = np.zeros(Nv) Rx[boundary] = boundary_coords[0, :] Ry[boundary] = boundary_coords[1, :] x, info = lgmres(L, Rx, tol=solver_tolerance) y, info = lgmres(L, Ry, tol=solver_tolerance) z = np.zeros(Nv) return trimesh.Trimesh(faces=mesh.faces, vertices=np.array([x, y, z]).T, metadata=mesh.metadata, process=False)
def disk_conformal_mapping(mesh, lap_type='conformal', boundary=None, boundary_coords=None): """ compute comformal mapping of a mesh to a disk ADD ref :param mesh: a trimesh object :param lap_type: type of mesh Laplacian to be used, see the function differential_geometry/compute_mesh_weights for more informations :param boundary: boundary of the mesh, resulting from the function topology/mesh_boundary :param boundary_coords: coordindates of the boundary vertices on the output disk, if None then uniform sampling :return: a trimesh object, planar disk representation of the input mesh """ if boundary is None: boundary_t = stop.mesh_boundary(mesh) boundary = boundary_t[0] boundary = np.array(boundary) if boundary_coords is None: p = boundary.size t = np.arange(0, 2 * np.math.pi, (2 * np.math.pi / p)) boundary_coords = np.array([np.cos(t), np.sin(t)]) L, LB = sdg.compute_mesh_laplacian(mesh, lap_type=lap_type) Nv = len(mesh.vertices) # np.array(mesh.vertex()).shape[0] print('Boundary Size:', boundary.shape) print('Laplacian Size:', L.shape) for i in boundary: L[i, :] = 0 L[i, i] = 1 L = L.tocsr() Rx = np.zeros(Nv) Ry = np.zeros(Nv) Rx[boundary] = boundary_coords[0, :] Ry[boundary] = boundary_coords[1, :] x, info = lgmres(L, Rx, tol=solver_tolerance) y, info = lgmres(L, Ry, tol=solver_tolerance) z = np.zeros(Nv) return trimesh.Trimesh(faces=mesh.faces, vertices=np.array([x, y, z]).T, metadata=mesh.metadata, process=False)
import slam.io as sio import slam.differential_geometry as sdg import slam.plot as splt if __name__ == '__main__': # load example data mesh = sio.load_mesh('data/example_mesh.gii') tex = sio.load_texture('data/example_texture.gii') # compute various types of Laplacian of the mesh lap, lap_b = sdg.compute_mesh_laplacian(mesh, lap_type='fem') print(mesh.vertices.shape) print(lap.shape) lap, lap_b = sdg.compute_mesh_laplacian(mesh, lap_type='conformal') lap, lap_b = sdg.compute_mesh_laplacian(mesh, lap_type='meanvalue') lap, lap_b = sdg.compute_mesh_laplacian(mesh, lap_type='authalic') # smooth the mesh using Laplacian s_mesh = sdg.laplacian_mesh_smoothing(mesh, nb_iter=100, dt=0.1) # compute the gradient of texture tex triangle_grad = sdg.triangle_gradient(mesh, tex.darray[0]) print(triangle_grad) grad = sdg.gradient(mesh, tex.darray[0]) print(grad.values) norm_grad = sdg.norm_gradient(mesh, tex.darray[0]) print(norm_grad) # compute the depth potential function dpf = sdg.depth_potential_function(mesh, tex.darray[0], [0.3])
def spherical_mapping(mesh, mapping_type='laplacian_eigenvectors', conformal_w=1, authalic_w=1, dt=0.01, nb_it=10): """ ADD REF :param mesh: :param mapping_type: :param conformal_w: :param authalic_w: :param dt: :param nb_it: :return: """ # computing spherical mapping based on laplacian eigenvectors sph_vert = sdg.mesh_laplacian_eigenvectors(mesh, nb_vectors=3) norm_sph_vert = np.sqrt(np.sum(sph_vert * sph_vert, 1)) sphere_vertices = sph_vert / np.tile(norm_sph_vert, (3, 1)).T if mapping_type == 'laplacian_eigenvectors': return trimesh.Trimesh(faces=mesh.faces, vertices=sphere_vertices, metadata=mesh.metadata, process=False) if mapping_type == 'conformal': """ Desbrun, M., Meyer, M., & Alliez, P. (2002). Intrinsic parameterizations of surface meshes. Computer Graphics Forum, 21(3), 209–218. https://doi.org/10.1111/1467-8659.00580 """ # options.symmetrize = 0; # options.normalize = 1; L, B = sdg.compute_mesh_laplacian(mesh, lap_type='conformal') if mapping_type == 'authalic': # options.symmetrize = 0; # options.normalize = 1; L, B = sdg.compute_mesh_laplacian(mesh, lap_type='authalic') if mapping_type == 'combined': # options.symmetrize = 0; # options.normalize = 1; Lconf, Bconf = sdg.compute_mesh_laplacian(mesh, lap_type='conformal') Laut, Baut = sdg.compute_mesh_laplacian(mesh, lap_type='authalic') L = conformal_w * Lconf + authalic_w * Laut # continue the spherical mappig by minimizig the energy evol = list() for it in range(nb_it): sphere_vertices = sphere_vertices - dt * L.dot(sphere_vertices) # sphere_vertices * L norm_sph_vert = np.sqrt(np.sum(sphere_vertices * sphere_vertices, 1)) sphere_vertices = sphere_vertices / np.tile(norm_sph_vert, (3, 1)).T if it % 10 == 0: sph = trimesh.Trimesh(faces=mesh.faces, vertices=sphere_vertices, process=False) angle_diff = sdst.angle_difference(sph, mesh) area_diff = sdst.area_difference(sph, mesh) edge_diff = sdst.edge_length_difference(sph, mesh) evol.append([ np.sum(np.abs(angle_diff.flatten())), np.sum(np.abs(area_diff.flatten())), np.sum(np.abs(edge_diff.flatten())) ]) # ind = 0; # for it=1:nb_it # % it = 1; # % while sum(I(: ) < 0) # % it = it + 1; # % vertex1 = vertex1 * L; # vertex1 = vertex1 - eta * vertex1 * L; # vertex1 = vertex1. / repmat(sqrt(sum(vertex1. ^ 2, 1)), [3 1]); # if mod(it, 100) == 0 # ind = ind + 1; # NFV.vertices = vertex1 # '; # [nb_inward, inward] = reversed_faces(NFV, 'sphere'); # bil_inward(ind) = nb_inward; # w = zeros(1, m); # E = zeros(1, m); # for i=1:3 # i1 = mod(i, 3) + 1; # % directed # edge # u = vertex1(:, faces(i,:)) - vertex1(:, faces(i1,:)); # % norm # squared # u = sum(u. ^ 2); # % weights # between # the # vertices # for j=1:m # w(j) = L(faces(i, j), faces(i1, j)); # end # % w = W(faces(i,:) + (faces(i1,:) - 1)*n); # E = E + w. * u; # # # end # # bil_E(ind) = sum(E); # if ind > 2 # disp(['Ratio of inverted triangles:' num2str(100 * nb_inward / m, 3) # '% energy decrease:' num2str(bil_E(end - 1) - bil_E(end), 3)]); # end # end # end return trimesh.Trimesh(faces=mesh.faces, vertices=sphere_vertices, metadata=mesh.metadata, process=False), evol
def spherical_mapping(mesh, mapping_type='laplacian_eigenvectors', conformal_w=1, authalic_w=1, dt=0.01, nb_it=10): """ Computes the mapping between the input mesh and a sphere centered on 0 and of radius=1 Several methods are implemented: -for laplacian eigenvectors, see: J. Lefèvre and G. Auzias, "Spherical Parameterization for Genus Zero Surfaces Using Laplace-Beltrami Eigenfunctions," in 2nd Conference on Geometric Science of Information, GSI, 2015, 121–29, https://doi.org/10.1007/978-3-319-25040-3_14. -for conformal mapping, see: Desbrun, M., Meyer, M., & Alliez, P., "Intrinsic parameterizations of surface meshes", Computer Graphics Forum, 21(3), 2002, 209–218. https://doi.org/10.1111/1467-8659.00580 -for authalic and combined methods, see: Rachel a Yotter, Paul M. Thompson, and Christian Gaser, “Algorithms to Improve the Reparameterization of Spherical Mappings of Brain Surface Meshes.,” Journal of Neuroimaging 21, no. 2 (April 2011): e134-47, https://doi.org/10.1111/j.1552-6569.2010.00484.x. or Ilja Friedel, Peter Schröder, and Mathieu Desbrun, “Unconstrained Spherical Parameterization”, Journal of Graphics, GPU, and Game Tools 12, no. 1 (2007): 17–26. :param mesh: Trimesh object, input mesh to be mapped onto a sphere :param mapping_type: string, type of mapping method, possible options are: 'laplacian_eigenvectors', 'conformal', 'authalic' or 'combined' :param conformal_w: Float, weight of the conformal constraint for the 'combined' method :param authalic_w: Float, weight of the authalic constraint for the 'combined' method :param dt: Float, discrtization step :param nb_it: Int, number of iterations :return: Trimesh object: the spherical representation of the input mesh, having the same adjacency (faces, edges, vertex indexing) as the input mesh. """ # computing spherical mapping based on laplacian eigenvectors sph_vert = sdg.mesh_laplacian_eigenvectors(mesh, nb_vectors=3) norm_sph_vert = np.sqrt(np.sum(sph_vert * sph_vert, 1)) sphere_vertices = sph_vert / np.tile(norm_sph_vert, (3, 1)).T if mapping_type == 'laplacian_eigenvectors': return trimesh.Trimesh(faces=mesh.faces, vertices=sphere_vertices, metadata=mesh.metadata, process=False) if mapping_type == 'conformal': L, B = sdg.compute_mesh_laplacian(mesh, lap_type='conformal') if mapping_type == 'authalic': L, B = sdg.compute_mesh_laplacian(mesh, lap_type='authalic') if mapping_type == 'combined': Lconf, Bconf = sdg.compute_mesh_laplacian(mesh, lap_type='conformal') Laut, Baut = sdg.compute_mesh_laplacian(mesh, lap_type='authalic') L = conformal_w * Lconf + authalic_w * Laut # continue the spherical mappig by minimizig the energy evol = list() for it in range(nb_it): sphere_vertices = sphere_vertices - dt * L.dot(sphere_vertices) # sphere_vertices * L norm_sph_vert = np.sqrt(np.sum(sphere_vertices * sphere_vertices, 1)) sphere_vertices = sphere_vertices / np.tile(norm_sph_vert, (3, 1)).T if it % 10 == 0: sph = trimesh.Trimesh(faces=mesh.faces, vertices=sphere_vertices, process=False) angle_diff = sdst.angle_difference(sph, mesh) area_diff = sdst.area_difference(sph, mesh) edge_diff = sdst.edge_length_difference(sph, mesh) evol.append([ np.sum(np.abs(angle_diff.flatten())), np.sum(np.abs(area_diff.flatten())), np.sum(np.abs(edge_diff.flatten())) ]) return trimesh.Trimesh(faces=mesh.faces, vertices=sphere_vertices, metadata=mesh.metadata, process=False), evol