class StimuliSurface(SpatioTemporalPattern): """ A spatio-temporal pattern defined in a Surface DataType. It includes the list of focal points. """ surface = surfaces.CorticalSurface(label="Surface", order=1) focal_points_surface = basic.List(label="Focal points", locked=True, order=4) focal_points_triangles = basic.List(label="Focal points triangles", locked=True, order=4) def configure_space(self, region_mapping=None): """ Do necessary preparations in order to use this stimulus. NOTE: this was previously done in simulator configure_stimuli() method. It no needs to be used in stimulus viewer also. """ dis_shp = (self.surface.number_of_vertices, numpy.size(self.focal_points_surface)) # TODO: When this was in Simulator it was number of nodes, using surface vertices # breaks surface simulations which include non-cortical regions. distance = numpy.zeros(dis_shp) k = -1 for focal_point in self.focal_points_surface: k += 1 foci = numpy.array([focal_point], dtype=numpy.int32) distance[:, k] = self.surface.geodesic_distance(foci) super(StimuliSurface, self).configure_space(distance)
def test_cortical_surface(self): dt = surfaces.CorticalSurface().from_file() dt.__setattr__('valid_for_simulations', True) assert isinstance(dt, surfaces.CorticalSurface) dt.configure() summary_info = dt.summary_info() assert summary_info['Number of edges'] == 49140 assert summary_info['Number of triangles'] == 32760 assert summary_info['Number of vertices'] == 16384 assert dt.surface_type == surfaces.CORTICAL assert len(dt.vertex_neighbours) == 16384 assert isinstance(dt.vertex_neighbours[0], frozenset) assert len(dt.vertex_triangles) == 16384 assert isinstance(dt.vertex_triangles[0], frozenset) assert len(dt.nth_ring(0)) == 17 assert dt.triangle_areas.shape == (32760, 1) assert dt.triangle_angles.shape == (32760, 3) assert len(dt.edges) == 49140 assert abs(dt.edge_mean_length - 3.97605292887) < 0.00000001 assert abs(dt.edge_min_length - 0.6638) < 0.0001 assert abs(dt.edge_max_length - 7.7567) < 0.0001 assert len(dt.edge_triangles) == 49140 assert [] == dt.validate_topology_for_simulations().warnings assert dt.vertices.shape == (16384, 3) assert dt.vertex_normals.shape == (16384, 3) assert dt.triangles.shape == (32760, 3) topologicals = dt.compute_topological_constants() assert 4 == topologicals[0] assert all([a.size == 0 for a in topologicals[1:]])
def test_cortical_surface(self): dt = surfaces.CorticalSurface(load_default=True) self.assertTrue(isinstance(dt, surfaces.CorticalSurface)) dt.configure() summary_info = dt.summary_info self.assertEqual(summary_info['Number of edges'], 49140) self.assertEqual(summary_info['Number of triangles'], 32760) self.assertEqual(summary_info['Number of vertices'], 16384) self.assertEqual(dt.surface_type, surfaces.CORTICAL) self.assertEqual(len(dt.vertex_neighbours), 16384) self.assertTrue(isinstance(dt.vertex_neighbours[0], frozenset)) self.assertEqual(len(dt.vertex_triangles), 16384) self.assertTrue(isinstance(dt.vertex_triangles[0], frozenset)) self.assertEqual(len(dt.nth_ring(0)), 17) self.assertEqual(dt.triangle_areas.shape, (32760, 1)) self.assertEqual(dt.triangle_angles.shape, (32760, 3)) self.assertEqual(len(dt.edges), 49140) self.assertTrue(abs(dt.edge_length_mean - 3.97605292887) < 0.00000001) self.assertTrue(abs(dt.edge_length_min - 0.6638) < 0.0001) self.assertTrue(abs(dt.edge_length_max - 7.7567) < 0.0001) self.assertEqual(len(dt.edge_triangles), 49140) self.assertEqual([], dt.validate_topology_for_simulations().warnings) self.assertEqual(dt.get_data_shape('vertices'), (16384, 3)) self.assertEqual(dt.get_data_shape('vertex_normals'), (16384, 3)) self.assertEqual(dt.get_data_shape('triangles'), (32760, 3)) topologicals = dt.compute_topological_constants() self.assertEqual(4, topologicals[0]) self.assertTrue(all([a.size == 0 for a in topologicals[1:]]))
def test_cortical_surface(self): dt = surfaces.CorticalSurface(load_default=True) self.assertTrue(isinstance(dt, surfaces.CorticalSurface)) dt.configure() summary_info = dt.summary_info self.assertEqual(summary_info['Number of edges'], 49140) self.assertEqual(summary_info['Number of triangles'], 32760) self.assertEqual(summary_info['Number of vertices'], 16384) self.assertEqual(dt.surface_type, surfaces_data.CORTICAL) self.assertEqual(len(dt.vertex_neighbours), 16384) self.assertTrue(isinstance(dt.vertex_neighbours[0], frozenset)) self.assertEqual(len(dt.vertex_triangles), 16384) self.assertTrue(isinstance(dt.vertex_triangles[0], frozenset)) self.assertEqual(len(dt.nth_ring(0)), 17) self.assertEqual(dt.triangle_areas.shape, (32760, 1)) self.assertEqual(dt.triangle_angles.shape, (32760, 3)) self.assertEqual(len(dt.edges), 49140) self.assertTrue(abs(dt.edge_length_mean - 3.97605292887) < 0.00000001) self.assertTrue(abs(dt.edge_length_min - 0.663807567201) < 0.00000001) self.assertTrue(abs(dt.edge_length_max - 7.75671853782) < 0.00000001) self.assertEqual(len(dt.edge_triangles), 49140) self.assertEqual(dt.check(), (True, 4, [], [], [], "")) self.assertEqual(dt.get_data_shape('vertices'), (16384, 3)) self.assertEqual(dt.get_data_shape('vertex_normals'), (16384, 3)) self.assertEqual(dt.get_data_shape('triangles'), (32760, 3))
class ProjectionData(MappedType): """ Base DataType for representing a ProjectionMatrix. The projection is between a source of type CorticalSurface and a set of Sensors. """ projection_type = basic.String __mapper_args__ = {'polymorphic_on': 'projection_type'} brain_skull = surfaces.BrainSkull( label="Brain Skull", default=None, required=False, doc="""Boundary between skull and cortex domains.""") skull_skin = surfaces.SkullSkin( label="Skull Skin", default=None, required=False, doc="""Boundary between skull and skin domains.""") skin_air = surfaces.SkinAir( label="Skin Air", default=None, required=False, doc="""Boundary between skin and air domains.""") conductances = basic.Dict( label="Domain conductances", required=False, default={ 'air': 0.0, 'skin': 1.0, 'skull': 0.01, 'brain': 1.0 }, doc=""" A dictionary representing the conductances of ... """) sources = surfaces.CorticalSurface(label="surface or region", default=None, required=True) sensors = sensors.Sensors( label="Sensors", default=None, required=False, doc=""" A set of sensors to compute projection matrix for them. """) projection_data = arrays.FloatArray(label="Projection Matrix Data", default=None, required=True)
class StimuliSurfaceData(SpatioTemporalPatternData): """ A spatio-temporal pattern defined in a Surface DataType. It includes the list of focal points. """ surface = surfaces.CorticalSurface(label="Surface", order=1) focal_points_surface = basic.List(label="Focal points", locked=True, order=4) focal_points_triangles = basic.List(label="Focal points triangles", locked=True, order=4)
def __init__(self, surface=None, focal_points_surface=None, focal_points_triangles=None, *args, **kwargs): if surface is None: surface = surfaces.CorticalSurface() self.surface = surface if focal_points_surface is None: focal_points_surface = [] self.focal_points_surface = focal_points_surface if focal_points_triangles is None: focal_points_triangles = [] self.focal_points_triangles = focal_points_triangles super(StimuliSurface, self).__init__(*args, **kwargs)
def test_stimulisurface(self): srf = surfaces.CorticalSurface(load_default=True) srf.configure() dt = patterns.StimuliSurface() dt.surface = srf dt.spatial = equations.DiscreteEquation() dt.temporal = equations.Gaussian() dt.focal_points_surface = [0, 1, 2] dt.focal_points_triangles = [0, 1, 2] dt.configure() dt.configure_space() summary = dt.summary_info assert summary['Type'] == "StimuliSurface" assert dt.space.shape == (16384, 3) assert isinstance(dt.spatial, equations.DiscreteEquation) assert dt.spatial_pattern.shape == (16384, 1) assert dt.surface is not None assert isinstance(dt.temporal, equations.Gaussian) assert dt.temporal_pattern is None assert dt.time is None
def test_stimulisurface(self): srf = surfaces.CorticalSurface() srf.configure() dt = patterns.StimuliSurface() dt.surface = srf dt.spatial = equations.DiscreteEquation() dt.temporal = equations.Gaussian() dt.focal_points_surface = [0, 1, 2] dt.focal_points_triangles = [0, 1, 2] dt.configure() dt.configure_space() summary = dt.summary_info self.assertEqual(summary['Type'], "StimuliSurface") self.assertEqual(dt.space.shape, (81924, 3)) self.assertTrue(isinstance(dt.spatial, equations.DiscreteEquation)) self.assertEqual(dt.spatial_pattern.shape, (81924, 1)) self.assertTrue(dt.surface is not None) self.assertTrue(isinstance(dt.temporal, equations.Gaussian)) self.assertTrue(dt.temporal_pattern is None) self.assertTrue(dt.time is None)
def test_default_attributes(self): """ Test that default_console attributes are populated. """ cortex = surfaces.CorticalSurface() cortex.configure() self.assertTrue(cortex.vertices is not None) self.assertEqual(81924, cortex.number_of_vertices) self.assertEqual((81924, 3), cortex.vertices.shape) self.assertEqual((81924, 3), cortex.vertex_normals.shape) self.assertEqual(163840, cortex.number_of_triangles) self.assertEqual((163840, 3), cortex.triangles.shape) conn = connectivity.Connectivity() conn.configure() self.assertTrue(conn.centres is not None) self.assertEqual((74,), conn.region_labels.shape) self.assertEqual('lA1', conn.region_labels[0]) self.assertEquals((74, 3), conn.centres.shape) self.assertEquals((74, 74), conn.weights.shape) self.assertEquals((74, 74), conn.tract_lengths.shape) self.assertEquals(conn.delays.shape, conn.tract_lengths.shape) self.assertEqual(74, conn.number_of_regions)
class LocalConnectivity(types_mapped.MappedType): """ A sparse matrix for representing the local connectivity within the Cortex. """ _ui_name = "Local connectivity" surface = surfaces.CorticalSurface(label="Surface", order=1) matrix = types_mapped.SparseMatrix(order=-1) equation = equations.FiniteSupportEquation(label="Spatial", required=False, default=equations.Gaussian, order=2) cutoff = basic.Float( label="Cutoff distance (mm)", default=40.0, doc="Distance at which to truncate the evaluation in mm.", order=3) def compute(self): """ Compute current Matrix. """ LOG.info("Mapping geodesic distance through the LocalConnectivity.") #Start with data being geodesic_distance_matrix, then map it through equation self.equation.pattern = self.matrix_gdist.data #Then replace original data with result... self.matrix_gdist.data = self.equation.pattern #Homogenise spatial discretisation effects across the surface nv = self.matrix_gdist.shape[0] ind = numpy.arange(nv, dtype=int) pos_mask = self.matrix_gdist.data > 0.0 neg_mask = self.matrix_gdist.data < 0.0 pos_con = self.matrix_gdist.copy() neg_con = self.matrix_gdist.copy() pos_con.data[neg_mask] = 0.0 neg_con.data[pos_mask] = 0.0 pos_contrib = pos_con.sum(axis=1) pos_contrib = numpy.array(pos_contrib).squeeze() neg_contrib = neg_con.sum(axis=1) neg_contrib = numpy.array(neg_contrib).squeeze() pos_mean = pos_contrib.mean() neg_mean = neg_contrib.mean() if ((pos_mean != 0.0 and any(pos_contrib == 0.0)) or (neg_mean != 0.0 and any(neg_contrib == 0.0))): msg = "Cortical mesh is too coarse for requested LocalConnectivity." LOG.warning(msg) bad_verts = () if pos_mean != 0.0: bad_verts = bad_verts + numpy.nonzero(pos_contrib == 0.0) if neg_mean != 0.0: bad_verts = bad_verts + numpy.nonzero(neg_contrib == 0.0) LOG.debug("Problem vertices are: %s" % str(bad_verts)) pos_hf = numpy.zeros(shape=pos_contrib.shape) pos_hf[pos_contrib != 0] = pos_mean / pos_contrib[pos_contrib != 0] neg_hf = numpy.zeros(shape=neg_contrib.shape) neg_hf[neg_contrib != 0] = neg_mean / neg_contrib[neg_contrib != 0] pos_hf_diag = scipy.sparse.csc_matrix((pos_hf, (ind, ind)), shape=(nv, nv)) neg_hf_diag = scipy.sparse.csc_matrix((neg_hf, (ind, ind)), shape=(nv, nv)) homogenious_conn = (pos_hf_diag * pos_con) + (neg_hf_diag * neg_con) #Then replace unhomogenised result with the spatially homogeneous one... if not homogenious_conn.has_sorted_indices: homogenious_conn.sort_indices() self.matrix = homogenious_conn def _validate_before_store(self): """ Overrides MappedType._validate_before_store to use a custom error for missing matrix. """ # Sparse Matrix is required so we should check if there is any data stored for it if self.matrix is None: msg = ("LocalConnectivity can not be stored because it " "has no SparseMatrix attached.") raise exceptions.ValidationException(msg) super(LocalConnectivity, self)._validate_before_store() @staticmethod def from_file(source_file="local_connectivity_16384.mat", instance=None): if instance is None: result = LocalConnectivity() else: result = instance source_full_path = try_get_absolute_path("tvb_data.local_connectivity", source_file) reader = FileReader(source_full_path) result.matrix = reader.read_array(matlab_data_name="LocalCoupling") return result def get_min_max_values(self): """ Retrieve the minimum and maximum values from the metadata. :returns: (minimum_value, maximum_value) """ metadata = self.get_metadata('matrix') return metadata[self.METADATA_ARRAY_MIN], metadata[ self.METADATA_ARRAY_MAX] def _find_summary_info(self): """ Gather scientifically interesting summary information from an instance of this datatype. """ return self.get_info_about_array('matrix', [ self.METADATA_ARRAY_MAX, self.METADATA_ARRAY_MIN, self.METADATA_ARRAY_MEAN, self.METADATA_ARRAY_SHAPE ]) 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: raise AttributeError( 'Require surface to compute local connectivity.') self.matrix_gdist = surfaces.gdist.local_gdist_matrix( self.surface.vertices.astype(numpy.float64), self.surface.triangles.astype(numpy.int32), max_distance=self.cutoff) self.compute() # Avoid having a large data-set in memory. self.matrix_gdist = None
def test_corticalsurface(self): dt = surfaces.CorticalSurface() self.assertEqual(dt.get_data_shape('vertices'), (81924, 3)) self.assertEqual(dt.get_data_shape('vertex_normals'), (81924, 3)) self.assertEqual(dt.get_data_shape('triangles'), (163840, 3))
class ProjectionMatrix(MappedType): """ Base DataType for representing a ProjectionMatrix. The projection is between a source of type CorticalSurface and a set of Sensors. """ projection_type = basic.String __mapper_args__ = {'polymorphic_on': 'projection_type'} brain_skull = surfaces.BrainSkull( label="Brain Skull", default=None, required=False, doc="""Boundary between skull and cortex domains.""") skull_skin = surfaces.SkullSkin( label="Skull Skin", default=None, required=False, doc="""Boundary between skull and skin domains.""") skin_air = surfaces.SkinAir( label="Skin Air", default=None, required=False, doc="""Boundary between skin and air domains.""") conductances = basic.Dict( label="Domain conductances", required=False, default={ 'air': 0.0, 'skin': 1.0, 'skull': 0.01, 'brain': 1.0 }, doc=""" A dictionary representing the conductances of ... """) sources = surfaces.CorticalSurface(label="surface or region", default=None, required=True) sensors = sensors.Sensors( label="Sensors", default=None, required=False, doc=""" A set of sensors to compute projection matrix for them. """) projection_data = arrays.FloatArray(label="Projection Matrix Data", default=None, required=True) @property def shape(self): return self.projection_data.shape @classmethod def from_file(cls, source_file, matlab_data_name=None, is_brainstorm=False, instance=None): if instance is None: proj = cls() else: proj = instance source_full_path = try_get_absolute_path("tvb_data.projectionMatrix", source_file) reader = FileReader(source_full_path) if is_brainstorm: proj.projection_data = reader.read_gain_from_brainstorm() else: proj.projection_data = reader.read_array( matlab_data_name=matlab_data_name) return proj
class ProjectionMatrix(MappedType): """ Base DataType for representing a ProjectionMatrix. The projection is between a source of type CorticalSurface and a set of Sensors. """ projection_type = basic.String __mapper_args__ = {'polymorphic_on': 'projection_type'} brain_skull = surfaces.BrainSkull( label="Brain Skull", default=None, required=False, doc="""Boundary between skull and cortex domains.""") skull_skin = surfaces.SkullSkin( label="Skull Skin", default=None, required=False, doc="""Boundary between skull and skin domains.""") skin_air = surfaces.SkinAir( label="Skin Air", default=None, required=False, doc="""Boundary between skin and air domains.""") conductances = basic.Dict( label="Domain conductances", required=False, default={ 'air': 0.0, 'skin': 1.0, 'skull': 0.01, 'brain': 1.0 }, doc=""" A dictionary representing the conductances of ... """) sources = surfaces.CorticalSurface(label="surface or region", default=None, required=True) sensors = sensors.Sensors( label="Sensors", default=None, required=False, doc=""" A set of sensors to compute projection matrix for them. """) projection_data = arrays.FloatArray(label="Projection Matrix Data", default=None, required=True) @property def shape(self): return self.projection_data.shape @staticmethod def load_surface_projection_matrix(result, source_file): source_full_path = try_get_absolute_path("tvb_data.projectionMatrix", source_file) if source_file.endswith(".mat"): # consider we have a brainstorm format mat = scipy.io.loadmat(source_full_path) gain, loc, ori = (mat[field] for field in 'Gain GridLoc GridOrient'.split()) result.projection_data = (gain.reshape( (gain.shape[0], -1, 3)) * ori).sum(axis=-1) elif source_file.endswith(".npy"): # numpy array with the projection matrix already computed result.projection_data = numpy.load(source_full_path) else: raise Exception( "The projection matrix must be either a numpy array or a brainstorm mat file" ) return result @classmethod def from_file(cls, source_file): proj = cls() ProjectionMatrix.load_surface_projection_matrix(proj, source_file) return proj
def gifti2surface(gifti_image, vertices_array=None, normals_array=None, triangles_array=None, storage_path=''): """ Convert GiftiImage object into our internal Surface DataType """ meta = gifti_image.meta.get_data_as_dict() if vertices_array: tmpdir = os.path.join( gettempdir(), vertices_array.parent_cfile.get_unique_cff_name()) LOG.debug("Using temporary folder for surface import: " + tmpdir) #### Extract SRC from zipFile to TEMP _zipfile = ZipFile(vertices_array.parent_cfile.src, 'r', ZIP_DEFLATED) try: if vertices_array is not None: vertices_array = _zipfile.extract(vertices_array.src, tmpdir) vertices_array = numpy.load(vertices_array) if normals_array is not None: normals_array = _zipfile.extract(normals_array.src, tmpdir) normals_array = numpy.load(normals_array) if triangles_array is not None: triangles_array = _zipfile.extract(triangles_array.src, tmpdir) triangles_array = numpy.load(triangles_array) except: raise RuntimeError('Can not extract %s from Connectome file.' % vertices_array) if vertices_array is None: for i in xrange(int(gifti_image.numDA)): # Intent code is stored in the DataArray structure darray = gifti_image.darrays[i] if darray.intent == GiftiIntentCode.NIFTI_INTENT_POINTSET: vertices_array = darray.data continue if darray.intent == GiftiIntentCode.NIFTI_INTENT_TRIANGLE: triangles_array = darray.data continue if darray.intent == GiftiIntentCode.NIFTI_INTENT_NORMAL: normals_array = darray.data continue zero_based_val = eval(meta[constants.PARAM_ZERO_BASED]) if meta[constants.KEY_UID] in [surfaces.CORTICAL, 'srf_reg13', 'srf_80k']: surface = surfaces.CorticalSurface() elif meta[constants.KEY_UID] in [surfaces.OUTER_SKIN, 'outer_skin']: surface = surfaces.SkinAir() else: raise RuntimeError( 'Can not determine surface type. Abandon import operation.') surface.storage_path = storage_path # Remove mappings from MetaData (or too large exception will be thrown) del meta[constants.MAPPINGS_DICT] surface.set_metadata(meta) if os.path.isdir(tmpdir): shutil.rmtree(tmpdir) surface.zero_based_triangles = zero_based_val surface.vertices = vertices_array surface.vertex_normals = normals_array if zero_based_val: surface.triangles = numpy.array(triangles_array, dtype=numpy.int32) else: surface.triangles = numpy.array(triangles_array, dtype=numpy.int32) - 1 surface.triangle_normals = None # Now check if the triangles of the surface are valid triangles_min_vertex = numpy.amin(surface.triangles) if triangles_min_vertex < 0: if triangles_min_vertex == -1 and not zero_based_val: raise RuntimeError( "Your triangles contains a negative vertex index. May be you have a ZERO based surface." ) else: raise RuntimeError( "Your triangles contains a negative vertex index: %d" % triangles_min_vertex) no_of_vertices = len(surface.vertices) triangles_max_vertex = numpy.amax(surface.triangles) if triangles_max_vertex >= no_of_vertices: if triangles_max_vertex == no_of_vertices and zero_based_val: raise RuntimeError( "Your triangles contains an invalid vertex index: %d. \ May be your surface is NOT ZERO based." % triangles_max_vertex) else: raise RuntimeError( "Your triangles contains an invalid vertex index: %d." % triangles_max_vertex) return surface, meta[constants.KEY_UID]