def test_flat_triangular_mesh(self): data = np.loadtxt("data/flat_triangular_mesh.txt", skiprows=1) vertices = data[0:121].astype(np.float64) triangles = data[121:].astype(np.int32) distances = gdist.local_gdist_matrix(vertices, triangles) epsilon = 1e-6 # the default value used in `assert_array_almost_equal` # test if the obtained matrix is symmetric assert (abs(distances - distances.T) > epsilon).nnz == 0 np.testing.assert_array_almost_equal(distances.toarray()[1][0], 0.2) # set max distance as 0.3 distances = gdist.local_gdist_matrix(vertices, triangles, 0.3) # test if the obtained matrix is symmetric assert (abs(distances - distances.T) > epsilon).nnz == 0 assert np.max(distances) <= 0.3
def test_hedgehog_mesh(self): data = np.loadtxt("data/hedgehog_mesh.txt", skiprows=1) vertices = data[0:300].astype(np.float64) triangles = data[300:].astype(np.int32) distances = gdist.local_gdist_matrix(vertices, triangles) epsilon = 1e-6 # the default value used in `assert_array_almost_equal` # test if the obtained matrix is symmetric assert (abs(distances - distances.T) > epsilon).nnz == 0 np.testing.assert_array_almost_equal(distances.toarray()[1][0], 1.40522) # set max distance as 1.45 distances = gdist.local_gdist_matrix(vertices, triangles, 1.45) # test if the obtained matrix is symmetric assert (abs(distances - distances.T) > epsilon).nnz == 0 assert np.max(distances) <= 1.45
def Farthest_Point_Sampling(mesh_name,n): """ add the new farest point to set and print n out of all of mesh :param mesh_name: :param n: :return: """ mesh = read(mesh_name + ".ply") v, f = (np.array(mesh.points), np.array(mesh.cells_dict['triangle'], dtype=np.int32)) geodesics_dist = gdist.local_gdist_matrix(v.astype(np.float64), f.astype(np.int32)) mesh = Mesh(v=v,f=f) mesh.render_pointcloud(scalar_function=mesh.gaussianCurvature()) s=[] s.append(np.random.randint(0, len(v))) while (len(s)!=n): max_dist = 0 selected_v = None for i,v_i in enumerate(v): min_by_s = np.inf for s_i in s: #get minimum ovver all s_i dist = geodesics_dist[s_i][v_i] if dist < min_by_s: min_by_s = dist if min_by_s > max_dist: max_dist = min_by_s selected_v = v_i v = np.delete(v,selected_v) #dont iterate v over this node anymore s.append(selected_v) f_new=gen_f(s) mesh = Mesh(v=v[np.array(s)], f=f_new) mesh.render_pointcloud(scalar_function=mesh.gaussianCurvature())
def compute_errors(mesh_name, mds, mds_str='mds', embedded_dim=2, snap=True): """ solve 1.4 to calc MDS and geodestic and return normed distance :param mesh_name: :param mds: :param mds_str: :param embedded_dim: :param snap: :return: """ # Load mesh from .ply file ply = meshio.read(mesh_name + ".ply") v,f = ply.points,ply.cells_dict['triangle'] if snap: geodesics_dist = scipy.sparse.load_npz(mesh_name + '.npz').todense() else: geodesics_dist = gdist.local_gdist_matrix(v.astype(np.float64), f.astype(np.int32)) scipy.sparse.save_npz(mesh_name + '.npz', csr_matrix(geodesics_dist)) # to present original mesh """mesh_class = Mesh(v=vertices, f=faces) mesh_class.render_pointcloud(scalar_function=geodesics_dist[:],snap_name = path + " Mesh")""" if snap: if mds_str == 'mds': emb_coordinates = scipy.sparse.load_npz(mesh_name + '_MDS_NO_G.npz').todense() emb_coordinates = np.abs(emb_coordinates) # for sphere else: emb_coordinates = scipy.sparse.load_npz(mesh_name + '_SPHER_MDS_NO_G.npz').todense() emb_coordinates = np.abs(emb_coordinates) out = np.zeros((emb_coordinates.shape[0], 3)) out[:, :2] = +emb_coordinates emb_coordinates = out embedded_geodesics_dist = scipy.sparse.load_npz(mesh_name + mds_str + '.npz').todense() else: emb_coordinates = mds(geodesics_dist, embedded_dim) embedded_geodesics_dist = gdist.local_gdist_matrix(np.array(emb_coordinates).astype(np.float64), f.astype(np.int32)) scipy.sparse.save_npz(mesh_name + mds_str + '.npz', csr_matrix(embedded_geodesics_dist)) # present the mesh of embeded: """embedded_mesh = Mesh(v=emb_coordinates, f=ply.cells_dict['triangle']) embedded_mesh.render_pointcloud(scalar_function=np.sum(embedded_geodesics_dist,axis=1)/len(vertices), snap_name = path + " Mesh using " + method_str)""" err = np.linalg.norm(geodesics_dist - embedded_geodesics_dist) return err / len(v)
def compute_gdist_mat(surf_name='pial', max_distance=40.0): max_distance = float(max_distance) # in case passed from sys.argv for h in 'rl': surf_path = '%s/%s/surf/%sh.%s' % (SUBJECTS_DIR, SUBJECT, h, surf_name) v, f = nibabel.freesurfer.read_geometry(surf_path) mat_path = '%s/%s/surf/%sh.%s.gdist.mat' % (SUBJECTS_DIR, SUBJECT, h, surf_name) mat = gdist.local_gdist_matrix(v, f.astype('<i4'), max_distance=40.0) scipy.io.savemat(mat_path, {'gdist': mat})
def compute_gdist_mat(self, surf_name='pial', max_distance=40.0): "Compute sparse geodesic distance matrix and save in MATLAB format." max_distance = float(max_distance) # in case passed from sys.argv for h in 'rl': surf_path = '%s/%s/surf/%sh.%s' % (self.subjects_dir, self.subject, h, surf_name) v, f = nibabel.freesurfer.read_geometry(surf_path) mat_path = '%s/%s/surf/%sh.%s.gdist.mat' % ( self.subjects_dir, self.subject, h, surf_name) mat = gdist.local_gdist_matrix(v, f.astype('<i4'), max_distance=max_distance) scipy.io.savemat(mat_path, {'gdist': mat})
def compute_sparse_matrix(self): """ NOTE: Before calling this method, the surface field should already be set on the local connectivity. Computes the sparse matrix for this local connectivity. """ if self.surface is None: msg = " Before calling 'compute_sparse_matrix' method, the surface field should already be set." LOG.error(msg) raise Exception(msg) self.matrix = gdist.local_gdist_matrix(self.surface.vertices.astype(numpy.float64), self.surface.triangles.astype(numpy.int32), max_distance=self.cutoff)
def compute_gdist_mat(self, surf_name='pial', max_distance=40.0): max_distance = float(max_distance) # in case passed from sys.argv for h in 'rl': subjects_dir = os.environ['SUBJECTS_DIR'] subject = os.environ['SUBJECT'] surf_path = '%s/%s/surf/%sh.%s' % (subjects_dir, subject, h, surf_name) surface = IOUtils.read_surface(surf_path, False) mat_path = '%s/%s/surf/%sh.%s.gdist.mat' % (subjects_dir, subject, h, surf_name) mat = gdist.local_gdist_matrix(surface.vertices, surface.triangles.astype('<i4'), max_distance=max_distance) scipy.io.savemat(mat_path, {'gdist': mat})
def compute_sparse_matrix(self): """ NOTE: Before calling this method, the surface field should already be set on the local connectivity. Computes the sparse matrix for this local connectivity. """ if self.surface is None: msg = " Before calling 'compute_sparse_matrix' method, the surface field should already be set." LOG.error(msg) raise Exception(msg) self.matrix = gdist.local_gdist_matrix(self.surface.vertices.astype(numpy.float64), self.surface.triangles.astype(numpy.int32), max_distance=self.cutoff)
def compute_gdist_mat(self, surf_name: str='pial', max_distance: float=40.0) -> numpy.ndarray: max_distance = float(max_distance) # in case passed from sys.argv for h in 'rl': subjects_dir = os.environ['SUBJECTS_DIR'] subject = os.environ['SUBJECT'] surf_path = '%s/%s/surf/%sh.%s' % (subjects_dir, subject, h, surf_name) surface = IOUtils.read_surface(surf_path, False) mat_path = '%s/%s/surf/%sh.%s.gdist.mat' % ( subjects_dir, subject, h, surf_name) mat = gdist.local_gdist_matrix( surface.vertices, surface.triangles.astype('<i4'), max_distance=max_distance) scipy.io.savemat(mat_path, {'gdist': mat}) return mat
def local_gdist_matrix(mesh, max_geodist): """ For every vertex, get all the vertices within the maximum distance. NOTE: It will take some time to compute all the geo-distance from point to point. Details about the computing time and memory see in method gdist.local_gdist_matrix :param mesh: :param max_geodist: :return: """ vert = mesh.vertices poly = mesh.faces.astype(np.int32) return gdist.local_gdist_matrix(vert, poly, max_geodist)
def compute_geodesic_distance_matrix(self, max_dist): """ Calculate a sparse matrix of the geodesic distance from each vertex to all vertices within max_dist of them on the surface, ``max_dist``: find the distance to vertices out as far as max_dist. NOTE: Compute time increases rapidly with max_dist and the memory efficiency of the sparse matrices decreases, so, don't use too large a value for max_dist... """ dist = gdist.local_gdist_matrix(self.vertices.astype(numpy.float64), self.triangles.astype(numpy.int32), max_distance=max_dist) self.geodesic_distance_matrix = dist
def test_equality_with_stable(): surface_datas = ["inner_skull_642", "outer_skull_642", "scalp_1082"] for surface_data in surface_datas: expected = np.loadtxt( f"data/surface_data/{surface_data}/gdist_matrix.txt") vertices = np.loadtxt( f"data/surface_data/{surface_data}/vertices.txt", dtype=np.float64, ) triangles = np.loadtxt( f"data/surface_data/{surface_data}/triangles.txt", dtype=np.int32, ) actual = gdist.local_gdist_matrix( vertices=vertices, triangles=triangles, ) actual = actual.toarray() np.testing.assert_array_almost_equal(actual, expected)
def getRadialGeodesicPatches(mesh, radius, add_self_loops=True, to_csr=False): try: import gdist except ModuleNotFoundError: raise ModuleNotFoundError( "The dependency 'gdist' is required for this functionality!") V = mesh.vertices.astype(np.float64) F = mesh.faces.astype(np.int32) patches = gdist.local_gdist_matrix(V, F, max_distance=radius) with warnings.catch_warnings(): warnings.simplefilter("ignore") if add_self_loops: patches.setdiag(-1) if to_csr: patches = patches.tocsr() return patches
def compute_geodesic_distance_matrix(self, max_dist): """ Calculate a sparse matrix of the geodesic distance from each vertex to all vertices within max_dist of them on the surface, ``max_dist``: find the distance to vertices out as far as max_dist. NOTE: Compute time increases rapidly with max_dist and the memory efficiency of the sparse matrices decreases, so, don't use too large a value for max_dist... """ #TODO: Probably should check that max_dist isn't "too" large or too # small, min should probably be max edge length... #if NO_GEODESIC_DISTANCE: # LOG.error("%s: The geodesic distance library didn't load" % repr(self)) # return dist = gdist.local_gdist_matrix(self.vertices.astype(numpy.float64), self.triangles.astype(numpy.int32), max_distance=max_dist) self.geodesic_distance_matrix = dist
def compute_geodesic_distance_matrix(self, max_dist): """ Calculate a sparse matrix of the geodesic distance from each vertex to all vertices within max_dist of them on the surface, ``max_dist``: find the distance to vertices out as far as max_dist. NOTE: Compute time increases rapidly with max_dist and the memory efficiency of the sparse matrices decreases, so, don't use too large a value for max_dist... """ #TODO: Probably should check that max_dist isn't "too" large or too # small, min should probably be max edge length... #if NO_GEODESIC_DISTANCE: # LOG.error("%s: The geodesic distance library didn't load" % repr(self)) # return dist = gdist.local_gdist_matrix(self.vertices.astype(numpy.float64), self.triangles.astype(numpy.int32), max_distance=max_dist) self.geodesic_distance_matrix = dist
def local_gdist_matrix_wrapper(verts, triangles, max_distance, queue): mtx = gdist.local_gdist_matrix(verts.astype(np.float64), triangles.astype(np.int32), max_distance) queue.put(mtx)
def geodesic_distance(pos, face, src=None, dest=None, norm=True, max_distance=None): r"""Computes (normalized) geodesic distances of a mesh given by :obj:`pos` and :obj:`face`. If :obj:`src` and :obj:`dest` are given, this method only computes the geodesic distances for the respective source and target node-pairs. .. note:: This function requires the :obj:`gdist` package. To install, run :obj:`pip install cython && pip install gdist`. Args: pos (Tensor): The node positions. face (LongTensor): The face indices. src (LongTensor, optional): If given, only compute geodesic distances for the specified source indices. (default: :obj:`None`) dest (LongTensor, optional): If given, only compute geodesic distances for the specified target indices. (default: :obj:`None`) norm (bool, optional): Normalizes geodesic distances by :math:`\sqrt{\textrm{area}(\mathcal{M})}`. (default: :obj:`True`) max_distance (float, optional): If given, only yields results for geodesic distances less than :obj:`max_distance`. This will speed up runtime dramatically. (default: :obj:`None`) :rtype: Tensor """ if gdist is None: raise ImportError('Package `gdist` could not be found.') max_distance = float('inf') if max_distance is None else max_distance if norm: area = (pos[face[1]] - pos[face[0]]).cross(pos[face[2]] - pos[face[0]]) norm = (area.norm(p=2, dim=1) / 2).sum().sqrt().item() else: # pragma: no cover norm = 1.0 dtype = pos.dtype pos = pos.detach().cpu().to(torch.double).numpy() face = face.detach().t().cpu().to(torch.int).numpy() if src is None and dest is None: out = torch.from_numpy( gdist.local_gdist_matrix(pos, face, max_distance * norm).toarray() / norm).to(dtype) else: if src is None: src = np.arange(pos.size(0), dtype=np.int32) else: src = src.detach().cpu().to(torch.int).numpy() dest = None if dest is None else dest.detach().cpu().to( torch.int).numpy() outs = [] for i in range(len(src)): s = src[i:i + 1] d = None if dest is None else dest[i:i + 1] out = gdist.compute_gdist(pos, face, s, d, max_distance * norm) / norm out = torch.from_numpy(out).to(dtype) outs.append(out) out = torch.cat(outs, dim=0) if dest is None: out = out.view(-1, pos.shape[0]) return out
complex_file = '/scr/ilz3/myelinconnect/struct/surf_%s/orig/mid_surface/%s_%s_mid.vtk'%(hemi, sub, hemi) simple_file = '/scr/ilz3/myelinconnect/groupavg/indv_space/%s/lowres_%s_d_def.vtk'%(sub, hemi)# version d complex_v, complex_f, complex_d = read_vtk(complex_file) complex_vertices = complex_v.astype(np.float64) complex_faces = complex_f.astype(np.int32) simple_v, simple_f, simple_d = read_vtk(simple_file) simple_vertices = simple_v.astype(np.float64) simple_faces = simple_f.astype(np.int32) complex_radius = 2 print time.ctime() complex_matrix=gdist.local_gdist_matrix(complex_vertices, complex_faces, max_distance=complex_radius) for sv in range(len(simple_v.shape[0])): dist = 1000000 for cv in range(len(complex_v.shape[0])): if sv in complex_matrix[:,cv].indices: dist_now = complex_matrix[:,cv][sv].data if dist_now < dist: dist = dist_now quatsch, need to tunr around # inradius=list(complex_matrix[:,v].indices) # find
from vtk_rw import read_vtk import numpy as np import gdist mesh_file="/scr/ilz3/myelinconnect/new_groupavg/surfs/lowres/%s_lowres_new.vtk" hemis=['rh', 'lh'] for hemi in hemis: v, f, _ = read_vtk(mesh_file %hemi) v = v.astype(np.float64) f = f.astype(np.int32) dist_matrix=gdist.local_gdist_matrix(v, f)
def geodesic_distance(pos, face, src=None, dest=None, norm=True, max_distance=None, num_workers=0): r"""Computes (normalized) geodesic distances of a mesh given by :obj:`pos` and :obj:`face`. If :obj:`src` and :obj:`dest` are given, this method only computes the geodesic distances for the respective source and target node-pairs. .. note:: This function requires the :obj:`gdist` package. To install, run :obj:`pip install cython && pip install gdist`. Args: pos (Tensor): The node positions. face (LongTensor): The face indices. src (LongTensor, optional): If given, only compute geodesic distances for the specified source indices. (default: :obj:`None`) dest (LongTensor, optional): If given, only compute geodesic distances for the specified target indices. (default: :obj:`None`) norm (bool, optional): Normalizes geodesic distances by :math:`\sqrt{\textrm{area}(\mathcal{M})}`. (default: :obj:`True`) max_distance (float, optional): If given, only yields results for geodesic distances less than :obj:`max_distance`. This will speed up runtime dramatically. (default: :obj:`None`) num_workers (int, optional): How many subprocesses to use for calculating geodesic distances. :obj:`0` means that computation takes place in the main process. :obj:`-1` means that the available amount of CPU cores is used. (default: :obj:`0`) :rtype: Tensor """ import gdist max_distance = float('inf') if max_distance is None else max_distance if norm: area = (pos[face[1]] - pos[face[0]]).cross(pos[face[2]] - pos[face[0]]) norm = (area.norm(p=2, dim=1) / 2).sum().sqrt().item() else: norm = 1.0 dtype = pos.dtype pos = pos.detach().cpu().to(torch.double).numpy() face = face.detach().t().cpu().to(torch.int).numpy() if src is None and dest is None: out = gdist.local_gdist_matrix(pos, face, max_distance * norm).toarray() / norm return torch.from_numpy(out).to(dtype) if src is None: src = np.arange(pos.shape[0], dtype=np.int32) else: src = src.detach().cpu().to(torch.int).numpy() dest = None if dest is None else dest.detach().cpu().to(torch.int).numpy() def _parallel_loop(pos, face, src, dest, max_distance, norm, i, dtype): s = src[i:i + 1] d = None if dest is None else dest[i:i + 1] out = gdist.compute_gdist(pos, face, s, d, max_distance * norm) / norm return torch.from_numpy(out).to(dtype) num_workers = mp.cpu_count() if num_workers <= -1 else num_workers if num_workers > 0: with mp.Pool(num_workers) as pool: outs = pool.starmap( _parallel_loop, [(pos, face, src, dest, max_distance, norm, i, dtype) for i in range(len(src))]) else: outs = [ _parallel_loop(pos, face, src, dest, max_distance, norm, i, dtype) for i in range(len(src)) ] out = torch.cat(outs, dim=0) if dest is None: out = out.view(-1, pos.shape[0]) return out