def sens_xyz_ori(v: numpy.ndarray, f: numpy.ndarray, l: numpy.ndarray) -> numpy.ndarray: surface = Surface(v, f, [], None) vn = surface.vertex_normals() pos = numpy.zeros((n_sens, 6)) numpy.add.at(pos, l, numpy.c_[v, vn]) return pos / numpy.bincount(l)[:, numpy.newaxis]
def merge_surfaces(self, surfaces): """ Merge several surfaces, and their region mappings. :return: the merge result surface and region mapping. """ n_surfaces = len(surfaces) out_surface = Surface([], []) # TODO: how to deal with the metadata of merged surfaces, so that freesurfer.io can handle them, e.g., write them # i.e., we need to have a final unique version of the metadata, not a list of them, as I am doing here in the # commented code # out_surface_attributes=dict() # for attribute in ["vertices_coord_system", "generic_metadata", "vertices_metadata", "triangles_metadata"]: # out_surface_attributes[attribute]=[] for i_srf in range(n_surfaces): out_surface.add_vertices_and_triangles(surfaces[i_srf].vertices, surfaces[i_srf].triangles, surfaces[i_srf].area_mask) if len(surfaces[i_srf].center_ras) == 0: pass elif len(out_surface.center_ras) == 0: out_surface.center_ras = surfaces[i_srf].center_ras elif numpy.any( out_surface.center_ras != surfaces[i_srf].center_ras): raise ValueError( "At least two surfaces have different -non empty- centers in RAS coordinates!" ) # #TODO: think about how to better merge these fields # for attribute in ["vertices_coord_system", "generic_metadata", "vertices_metadata", "triangles_metadata"]: # out_surface_attributes[attribute].append(getattr(surfaces[i_srf],attribute)) # setattr(out_surface,attribute,out_surface_attributes[attribute]) return out_surface
def gen_dipoles(self, pos, ori_or_face=None, out_fname=None): "Generate dipoles (or equiv. file) for OpenMEEG." if ori_or_face is None: pos, ori = self.gen_dipole_triplets(pos) else: if ori_or_face.dtype in numpy.floattypes: ori = ori_or_face else: surface = Surface(pos, ori_or_face, [], None) ori = surface.vertex_normals() numpy.savetxt(out_fname, numpy.c_[pos, ori], fmt='%f')
def gen_dipoles(self, pos: Union[numpy.ndarray, list], ori_or_face: Optional[numpy.float]=None, out_fname: Optional[os.PathLike]=None) -> numpy.ndarray: "Generate dipoles (or equiv. file) for OpenMEEG." if ori_or_face is None: pos, ori = self.gen_dipole_triplets(pos) else: if ori_or_face.dtype is not None: ori = ori_or_face else: surface = Surface(pos, ori_or_face, [], None) ori = surface.vertex_normals() dipoles = numpy.c_[pos, ori] numpy.savetxt(out_fname, dipoles, fmt='%f') return dipoles
def read(self, surface_path, use_center_surface): vertices, triangles, metadata = read_geometry(surface_path, read_metadata=True) self.logger.info("From the file %s the extracted metadata is %s", surface_path, metadata) if use_center_surface: cras = [0, 0, 0] self.logger.info( "The --center_ras flag was specified, so the ras centering point is %s", cras) else: if CENTER_RAS_FS_SURF in metadata: cras = metadata[CENTER_RAS_FS_SURF] self.logger.info( "The ras centering point for surface %s is %s", surface_path, cras) else: cras = [0, 0, 0] self.logger.warning( "Could not read the ras centering point from surface %s header. " "The cras will be %s", surface_path, cras) return Surface(vertices, triangles, area_mask=None, center_ras=cras, generic_metadata=metadata)
def extract_subsurf(self, surface, verts_mask, output='surface'): """ Extracts a sub-surface that contains only the masked vertices and the corresponding faces. An important step is to replace old vertices indexes of faces to the new ones. :param: surface: input surface object :param: verts_mask: mask of the sub-surface to be extracted :return: output surface object """ verts_out = surface.vertices[verts_mask] triangles_mask = numpy.c_[verts_mask[surface.triangles[:, 0]], verts_mask[surface.triangles[:, 1]], verts_mask[surface.triangles[:, 2]]].all(axis=1) triangles_out = numpy.array(surface.triangles[triangles_mask, :]) verts_out_inds, = numpy.where(verts_mask) for triang_idx in range(triangles_out.shape[0]): for vertex_idx in range(3): triangles_out[triang_idx, vertex_idx], = \ numpy.where( triangles_out[triang_idx, vertex_idx] == verts_out_inds) if output == 'surface': out_surface = Surface( verts_out, triangles_out, area_mask=surface.area_mask[verts_out_inds], center_ras=surface.center_ras, vertices_coord_system=surface.vertices_coord_system, generic_metadata=surface.generic_metadata, vertices_metadata=surface.vertices_metadata, triangles_metadata=surface.triangles_metadata) return out_surface else: return (verts_out, triangles_out, surface.area_mask[verts_out_inds])
def compute_areas_for_regions(self, regions: list, surface: Surface, region_mapping: list) -> numpy.array: """Compute the areas of given regions""" region_surface_area = numpy.zeros(len(regions)) avt = numpy.array(surface.get_vertex_triangles()) # NOTE: Slightly overestimates as it counts overlapping border triangles, # but, not really a problem provided triangle-size << region-size. for i, k in enumerate(regions): regs = list(map(set, avt[numpy.array(region_mapping) == k])) if len(regs) == 0: continue region_triangles = set.union(*regs) if region_triangles: region_surface_area[i] = surface.get_triangle_areas()[list(region_triangles)].sum() return region_surface_area
def read(self, data_file, use_center_surface): gifti_image = giftiio.read(data_file) image_metadata = gifti_image.meta.metadata self.logger.info( "From the file %s the extracted metadata is %s", data_file, image_metadata) data_arrays = gifti_image.darrays vertices = data_arrays[0].data triangles = data_arrays[1].data vol_geom_center_ras = [0, 0, 0] vertices_metadata = data_arrays[0].metadata self.logger.info( "The metadata from vertices data array is %s", vertices_metadata) vertices_coord_system = data_arrays[0].coordsys self.logger.info( "The coordinate system transform matrix from vertices data array is %s", vertices_coord_system) triangles_metadata = data_arrays[1].metadata self.logger.info( "The metadata from triangles data array is %s", triangles_metadata) if use_center_surface: vol_geom_center_ras = [0, 0, 0] else: vol_geom_center_ras[0] = float( vertices_metadata[CENTER_RAS_GIFTI_SURF[0]]) vol_geom_center_ras[1] = float( vertices_metadata[CENTER_RAS_GIFTI_SURF[1]]) vol_geom_center_ras[2] = float( vertices_metadata[CENTER_RAS_GIFTI_SURF[2]]) return Surface(vertices, triangles, area_mask=None, center_ras=vol_geom_center_ras, vertices_coord_system=vertices_coord_system, generic_metadata=image_metadata, vertices_metadata=vertices_metadata, triangles_metadata=triangles_metadata)
def merge_surfaces(self, surfaces: Surface) -> Surface: """ Merge several surfaces, and their region mappings. :return: the merge result surface and region mapping. """ n_surfaces = len(surfaces) out_surface = Surface([], []) # TODO: how to deal with the metadata of merged surfaces, so that freesurfer.io can handle them, e.g., write them # i.e., we need to have a final unique version of the metadata, not a list of them, as I am doing here in the # commented code # out_surface_attributes=dict() # for attribute in ["vertices_coord_system", "generic_metadata", "vertices_metadata", "triangles_metadata"]: # out_surface_attributes[attribute]=[] for i_srf in range(n_surfaces): out_surface.add_vertices_and_triangles(surfaces[i_srf].vertices, surfaces[i_srf].triangles, surfaces[i_srf].area_mask) if out_surface.get_main_metadata() is None: out_surface.set_main_metadata(surfaces[i_srf].get_main_metadata()) if len(surfaces[i_srf].center_ras) == 0: pass elif len(out_surface.center_ras) == 0: out_surface.center_ras = surfaces[i_srf].center_ras elif numpy.any(out_surface.center_ras != surfaces[i_srf].center_ras): self.logger.warn("At least two surfaces have different -non empty- centers in RAS coordinates!") # #TODO: think about how to better merge these fields # for attribute in ["vertices_coord_system", "generic_metadata", "vertices_metadata", "triangles_metadata"]: # out_surface_attributes[attribute].append(getattr(surfaces[i_srf],attribute)) # setattr(out_surface,attribute,out_surface_attributes[attribute]) return out_surface
def write_surface_with_annotation(self, surface: Surface, annot: Annotation, result_name: str, positions: list = [(0, 0), (0, 90), (0, 180), (0, 270), (90, 0), (270, 0)]): x = surface.vertices[:, 0] y = surface.vertices[:, 1] z = surface.vertices[:, 2] fig = pyplot.figure() ax = Axes3D(fig) min = numpy.min([numpy.min(x), numpy.min(y), numpy.min(z)]) max = numpy.max([numpy.max(x), numpy.max(y), numpy.max(z)]) ax.set_xlim3d(min, max) ax.set_ylim3d(min, max) ax.set_zlim3d(min, max) if annot is not None: face_colors = annot.compute_face_colors(surface.triangles) normals = surface.compute_normals() face_colors = ax._shade_colors(face_colors, normals) poly_line = ax.plot_trisurf(x, y, z, triangles=surface.triangles) if annot is not None: poly_line.set_edgecolor(face_colors) poly_line.set_facecolor(face_colors) pyplot.axis('off') snapshot_index = 0 for e, a in positions: ax.view_init(elev=e, azim=a) ax.dist = 6 pyplot.savefig(self.get_path(result_name + str(snapshot_index)), dpi=fig.dpi) snapshot_index += 1 self.logger.info("The 6 snapshots were generated")
def write_surface_with_annotation(self, surface: Surface, annot: Annotation, result_name:str, positions: list=[(0, 0), (0, 90), (0, 180), (0, 270), (90, 0), (270, 0)]): x = surface.vertices[:, 0] y = surface.vertices[:, 1] z = surface.vertices[:, 2] fig = pyplot.figure() ax = Axes3D(fig, aspect='equal') min = numpy.min([numpy.min(x), numpy.min(y), numpy.min(z)]) max = numpy.max([numpy.max(x), numpy.max(y), numpy.max(z)]) ax.set_xlim3d(min, max) ax.set_ylim3d(min, max) ax.set_zlim3d(min, max) if annot is not None: face_colors = annot.compute_face_colors(surface.triangles) normals = surface.compute_normals() face_colors = ax._shade_colors(face_colors, normals) poly_line = ax.plot_trisurf(x, y, z, triangles=surface.triangles) if annot is not None: poly_line.set_edgecolor(face_colors) poly_line.set_facecolor(face_colors) pyplot.axis('off') snapshot_index = 0 for e, a in positions: ax.view_init(elev=e, azim=a) ax.dist = 6 pyplot.savefig(self.get_path( result_name + str(snapshot_index)), dpi=fig.dpi) snapshot_index += 1 self.logger.info("The 6 snapshots were generated")
def compute_seeg_gain_matrix(self, seeg_xyz: os.PathLike, cort_file: os.PathLike, subcort_file: os.PathLike, cort_rm: os.PathLike, subcort_rm: os.PathLike, out_gain_mat: os.PathLike) -> numpy.ndarray: genericIO = GenericIO() sensors = numpy.genfromtxt(seeg_xyz, usecols=[1, 2, 3]) cort_vertices = genericIO.read_field_from_zip("vertices.txt", cort_file) cort_triangles = genericIO.read_field_from_zip("triangles.txt", cort_file, dtype="i") cort_surf = Surface(cort_vertices, cort_triangles) cort_normals = cort_surf.vertex_normals() cort_areas = cort_surf.get_vertex_areas() subcort_vertices = genericIO.read_field_from_zip("vertices.txt", subcort_file) subcort_triangles = genericIO.read_field_from_zip("triangles.txt", subcort_file, dtype="i") subcort_surf = Surface(subcort_vertices, subcort_triangles) subcort_areas = subcort_surf.get_vertex_areas() cort_rm = list(numpy.genfromtxt(cort_rm, usecols=[0], dtype='i')) subcort_rm = list(numpy.genfromtxt(subcort_rm, usecols=[0], dtype='i')) region_list = numpy.unique(cort_rm + subcort_rm) nr_regions = len(region_list) nr_vertices = cort_surf.vertices.shape[0] + subcort_surf.vertices.shape[0] verts_regions_mat = self._get_verts_regions_matrix(nr_vertices, nr_regions, cort_rm + subcort_rm) gain_matrix = self._gain_matrix_dipole(cort_surf.vertices, cort_normals, cort_areas, sensors) gain_matrix_subcort = self._gain_matrix_inv_square(subcort_surf.vertices, subcort_areas, sensors) gain_total = numpy.concatenate((gain_matrix, gain_matrix_subcort), axis=1) gain_out = gain_total @ verts_regions_mat numpy.savetxt(out_gain_mat, gain_out) return gain_out
def read(self, h5_path, use_center_surface=False): h5_file = h5py.File(h5_path, 'r', libver='latest') vertices = h5_file['/vertices'][()] triangles = h5_file['/triangles'][()] h5_file.close() return Surface(vertices, triangles)