siibra 0.4a33__py3-none-any.whl → 0.4a46__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of siibra might be problematic. Click here for more details.
- siibra/VERSION +1 -1
- siibra/__init__.py +2 -0
- siibra/commons.py +53 -8
- siibra/configuration/configuration.py +21 -17
- siibra/configuration/factory.py +95 -19
- siibra/core/atlas.py +11 -8
- siibra/core/concept.py +41 -8
- siibra/core/parcellation.py +94 -43
- siibra/core/region.py +160 -187
- siibra/core/space.py +44 -39
- siibra/features/__init__.py +19 -19
- siibra/features/anchor.py +9 -6
- siibra/features/connectivity/__init__.py +0 -8
- siibra/features/connectivity/functional_connectivity.py +11 -3
- siibra/features/{basetypes → connectivity}/regional_connectivity.py +46 -33
- siibra/features/connectivity/streamline_counts.py +3 -2
- siibra/features/connectivity/streamline_lengths.py +3 -2
- siibra/features/{basetypes → dataset}/__init__.py +2 -0
- siibra/features/{external → dataset}/ebrains.py +3 -3
- siibra/features/feature.py +420 -0
- siibra/{samplers → features/image}/__init__.py +7 -1
- siibra/features/{basetypes/volume_of_interest.py → image/image.py} +12 -7
- siibra/features/{external/__init__.py → image/sections.py} +8 -5
- siibra/features/image/volume_of_interest.py +70 -0
- siibra/features/{cellular → tabular}/__init__.py +7 -11
- siibra/features/{cellular → tabular}/bigbrain_intensity_profile.py +5 -2
- siibra/features/{cellular → tabular}/cell_density_profile.py +6 -2
- siibra/features/{basetypes → tabular}/cortical_profile.py +48 -41
- siibra/features/{molecular → tabular}/gene_expression.py +5 -2
- siibra/features/{cellular → tabular}/layerwise_bigbrain_intensities.py +6 -2
- siibra/features/{cellular → tabular}/layerwise_cell_density.py +9 -3
- siibra/features/{molecular → tabular}/receptor_density_fingerprint.py +3 -2
- siibra/features/{molecular → tabular}/receptor_density_profile.py +6 -2
- siibra/features/tabular/regional_timeseries_activity.py +213 -0
- siibra/features/{basetypes → tabular}/tabular.py +14 -9
- siibra/livequeries/allen.py +1 -1
- siibra/livequeries/bigbrain.py +2 -3
- siibra/livequeries/ebrains.py +3 -9
- siibra/livequeries/query.py +1 -1
- siibra/locations/location.py +4 -3
- siibra/locations/point.py +21 -17
- siibra/locations/pointset.py +2 -2
- siibra/retrieval/__init__.py +1 -1
- siibra/retrieval/cache.py +8 -2
- siibra/retrieval/datasets.py +149 -29
- siibra/retrieval/repositories.py +19 -8
- siibra/retrieval/requests.py +98 -116
- siibra/volumes/gifti.py +26 -11
- siibra/volumes/neuroglancer.py +35 -19
- siibra/volumes/nifti.py +8 -9
- siibra/volumes/parcellationmap.py +341 -184
- siibra/volumes/sparsemap.py +67 -53
- siibra/volumes/volume.py +25 -13
- {siibra-0.4a33.dist-info → siibra-0.4a46.dist-info}/METADATA +4 -3
- siibra-0.4a46.dist-info/RECORD +69 -0
- {siibra-0.4a33.dist-info → siibra-0.4a46.dist-info}/WHEEL +1 -1
- siibra/features/basetypes/feature.py +0 -248
- siibra/features/fibres/__init__.py +0 -14
- siibra/features/functional/__init__.py +0 -14
- siibra/features/molecular/__init__.py +0 -26
- siibra/samplers/bigbrain.py +0 -181
- siibra-0.4a33.dist-info/RECORD +0 -71
- {siibra-0.4a33.dist-info → siibra-0.4a46.dist-info}/LICENSE +0 -0
- {siibra-0.4a33.dist-info → siibra-0.4a46.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# Copyright 2018-2021
|
|
2
|
+
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
|
+
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
from . import anchor as _anchor
|
|
17
|
+
|
|
18
|
+
from ..commons import logger, InstanceTable, siibra_tqdm
|
|
19
|
+
from ..core import concept
|
|
20
|
+
from ..core import space, region, parcellation
|
|
21
|
+
|
|
22
|
+
from typing import Union, TYPE_CHECKING, List, Dict, Type, Tuple
|
|
23
|
+
from hashlib import md5
|
|
24
|
+
from collections import defaultdict
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from ..retrieval.datasets import EbrainsDataset
|
|
28
|
+
TypeDataset = EbrainsDataset
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ParseLiveQueryIdException(Exception):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EncodeLiveQueryIdException(Exception):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class NotFoundException(Exception):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Feature:
|
|
44
|
+
"""
|
|
45
|
+
Base class for anatomically anchored data features.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
SUBCLASSES: Dict[Type['Feature'], List[Type['Feature']]] = defaultdict(list)
|
|
49
|
+
|
|
50
|
+
CATEGORIZED: Dict[str, Type['InstanceTable']] = defaultdict(InstanceTable)
|
|
51
|
+
|
|
52
|
+
category: str = None
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
modality: str,
|
|
57
|
+
description: str,
|
|
58
|
+
anchor: _anchor.AnatomicalAnchor,
|
|
59
|
+
datasets: List['TypeDataset'] = []
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
modality: str
|
|
65
|
+
A textual description of the type of measured information
|
|
66
|
+
description: str
|
|
67
|
+
A textual description of the feature.
|
|
68
|
+
anchor: AnatomicalAnchor
|
|
69
|
+
datasets : list
|
|
70
|
+
list of datasets corresponding to this feature
|
|
71
|
+
"""
|
|
72
|
+
self._modality_cached = modality
|
|
73
|
+
self._description = description
|
|
74
|
+
self._anchor_cached = anchor
|
|
75
|
+
self.datasets = datasets
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def modality(self):
|
|
79
|
+
# allows subclasses to implement lazy loading of the modality
|
|
80
|
+
return self._modality_cached
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def anchor(self):
|
|
84
|
+
# allows subclasses to implement lazy loading of an anchor
|
|
85
|
+
return self._anchor_cached
|
|
86
|
+
|
|
87
|
+
def __init_subclass__(cls, configuration_folder=None, category=None):
|
|
88
|
+
# extend the subclass lists
|
|
89
|
+
|
|
90
|
+
# Iterate over all mro, not just immediate base classes
|
|
91
|
+
for BaseCls in cls.__mro__:
|
|
92
|
+
# some base classes may not be sub class of feature, ignore these
|
|
93
|
+
if not issubclass(BaseCls, Feature):
|
|
94
|
+
continue
|
|
95
|
+
cls.SUBCLASSES[BaseCls].append(cls)
|
|
96
|
+
|
|
97
|
+
cls._live_queries = []
|
|
98
|
+
cls._preconfigured_instances = None
|
|
99
|
+
cls._configuration_folder = configuration_folder
|
|
100
|
+
cls.category = category
|
|
101
|
+
if category is not None:
|
|
102
|
+
cls.CATEGORIZED[category].add(cls.__name__, cls)
|
|
103
|
+
return super().__init_subclass__()
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def _get_subclasses(cls):
|
|
107
|
+
return {Cls.__name__: Cls for Cls in cls.SUBCLASSES}
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def description(self):
|
|
111
|
+
""" Allowssubclasses to overwrite the description with a function call. """
|
|
112
|
+
return self._description
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def name(self):
|
|
116
|
+
"""Returns a short human-readable name of this feature."""
|
|
117
|
+
return f"{self.__class__.__name__} ({self.modality}) anchored at {self.anchor}"
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def get_instances(cls, **kwargs) -> List['Feature']:
|
|
121
|
+
"""
|
|
122
|
+
Retrieve objects of a particular feature subclass.
|
|
123
|
+
Objects can be preconfigured in the configuration,
|
|
124
|
+
or delivered by Live queries.
|
|
125
|
+
"""
|
|
126
|
+
if not hasattr(cls, "_preconfigured_instances"):
|
|
127
|
+
return []
|
|
128
|
+
|
|
129
|
+
if cls._preconfigured_instances is not None:
|
|
130
|
+
return cls._preconfigured_instances
|
|
131
|
+
|
|
132
|
+
if cls._configuration_folder is None:
|
|
133
|
+
cls._preconfigured_instances = []
|
|
134
|
+
return cls._preconfigured_instances
|
|
135
|
+
|
|
136
|
+
from ..configuration.configuration import Configuration
|
|
137
|
+
conf = Configuration()
|
|
138
|
+
Configuration.register_cleanup(cls.clean_instances)
|
|
139
|
+
assert cls._configuration_folder in conf.folders
|
|
140
|
+
cls._preconfigured_instances = [
|
|
141
|
+
o for o in conf.build_objects(cls._configuration_folder)
|
|
142
|
+
if isinstance(o, cls)
|
|
143
|
+
]
|
|
144
|
+
logger.debug(
|
|
145
|
+
f"Built {len(cls._preconfigured_instances)} preconfigured {cls.__name__} "
|
|
146
|
+
f"objects from {cls._configuration_folder}."
|
|
147
|
+
)
|
|
148
|
+
return cls._preconfigured_instances
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def clean_instances(cls):
|
|
152
|
+
""" Removes all instantiated object instances"""
|
|
153
|
+
cls._preconfigured_instances = None
|
|
154
|
+
|
|
155
|
+
def matches(self, concept: concept.AtlasConcept) -> bool:
|
|
156
|
+
if self.anchor and self.anchor.matches(concept):
|
|
157
|
+
self.anchor._last_matched_concept = concept
|
|
158
|
+
return True
|
|
159
|
+
self.anchor._last_matched_concept = None
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def last_match_result(self):
|
|
164
|
+
return None if self.anchor is None \
|
|
165
|
+
else self.anchor.last_match_result
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def last_match_description(self):
|
|
169
|
+
return "" if self.anchor is None \
|
|
170
|
+
else self.anchor.last_match_description
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def id(self):
|
|
174
|
+
prefix = ''
|
|
175
|
+
id_set = {ds.id for ds in self.datasets if hasattr(ds, 'id')}
|
|
176
|
+
if len(id_set) == 1:
|
|
177
|
+
prefix = list(id_set)[0] + '--'
|
|
178
|
+
return prefix + md5(self.name.encode("utf-8")).hexdigest()
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def serialize_query_context(feat: 'Feature', concept: concept.AtlasConcept) -> str:
|
|
182
|
+
"""
|
|
183
|
+
Serialize feature from livequery and query context.
|
|
184
|
+
|
|
185
|
+
It is currently impossible to retrieve a livequery with a generic UUID.
|
|
186
|
+
As such, the query context (e.g. region, space or parcellation) needs to
|
|
187
|
+
be encoded in the id.
|
|
188
|
+
|
|
189
|
+
Whilst it is possible to (de)serialize *any* queries, the method is setup to only serialize
|
|
190
|
+
livequery features.
|
|
191
|
+
|
|
192
|
+
The serialized livequery id follows the following pattern:
|
|
193
|
+
|
|
194
|
+
<livequeryid_version>::<feature_cls_name>::<query_context>::<unserialized_id>
|
|
195
|
+
|
|
196
|
+
Where:
|
|
197
|
+
|
|
198
|
+
- livequeryid_version: version of the serialization. (e.g. lq0)
|
|
199
|
+
- feature_cls_name: class name to query. (e.g. BigBrainIntensityProfile)
|
|
200
|
+
- query_context: string to retrieve atlas concept in the query context. Can be one of the following:
|
|
201
|
+
- s:<space_id>
|
|
202
|
+
- p:<parcellation_id>
|
|
203
|
+
- p:<parcellation_id>::r:<region_id>
|
|
204
|
+
- unserialized_id: id prior to serialization
|
|
205
|
+
|
|
206
|
+
See test/features/test_feature.py for tests and usages.
|
|
207
|
+
"""
|
|
208
|
+
if not hasattr(feat.__class__, '_live_queries'):
|
|
209
|
+
raise EncodeLiveQueryIdException(f"generate_livequery_featureid can only be used on live queries, but {feat.__class__.__name__} is not.")
|
|
210
|
+
|
|
211
|
+
encoded_c = []
|
|
212
|
+
if isinstance(concept, space.Space):
|
|
213
|
+
encoded_c.append(f"s:{concept.id}")
|
|
214
|
+
elif isinstance(concept, parcellation.Parcellation):
|
|
215
|
+
encoded_c.append(f"p:{concept.id}")
|
|
216
|
+
elif isinstance(concept, region.Region):
|
|
217
|
+
encoded_c.append(f"p:{concept.parcellation.id}")
|
|
218
|
+
encoded_c.append(f"r:{concept.name}")
|
|
219
|
+
|
|
220
|
+
if len(encoded_c) == 0:
|
|
221
|
+
raise EncodeLiveQueryIdException("no concept is encoded")
|
|
222
|
+
|
|
223
|
+
return f"lq0::{feat.__class__.__name__}::{'::'.join(encoded_c)}::{feat.id}"
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def deserialize_query_context(Cls, feature_id: str) -> Tuple[Type['Feature'], concept.AtlasConcept, str]:
|
|
227
|
+
"""
|
|
228
|
+
Deserialize id into query context.
|
|
229
|
+
|
|
230
|
+
See docstring of serialize_query_context for context.
|
|
231
|
+
"""
|
|
232
|
+
lq_version, *rest = feature_id.split("::")
|
|
233
|
+
if lq_version != "lq0":
|
|
234
|
+
raise ParseLiveQueryIdException("livequery id must start with lq0::")
|
|
235
|
+
|
|
236
|
+
clsname, *concepts, fid = rest
|
|
237
|
+
|
|
238
|
+
Features = Cls.parse_featuretype(clsname)
|
|
239
|
+
|
|
240
|
+
if len(Features) == 0:
|
|
241
|
+
raise ParseLiveQueryIdException(f"classname {clsname!r} could not be parsed correctly. {feature_id!r}")
|
|
242
|
+
F = Features[0]
|
|
243
|
+
|
|
244
|
+
concept = None
|
|
245
|
+
for c in concepts:
|
|
246
|
+
if c.startswith("s:"):
|
|
247
|
+
if concept is not None:
|
|
248
|
+
raise ParseLiveQueryIdException("Conflicting spec.")
|
|
249
|
+
concept = space.Space.registry()[c.replace("s:", "")]
|
|
250
|
+
if c.startswith("p:"):
|
|
251
|
+
if concept is not None:
|
|
252
|
+
raise ParseLiveQueryIdException("Conflicting spec.")
|
|
253
|
+
concept = parcellation.Parcellation.registry()[c.replace("p:", "")]
|
|
254
|
+
if c.startswith("r:"):
|
|
255
|
+
if concept is None:
|
|
256
|
+
raise ParseLiveQueryIdException("region has been encoded, but parcellation has not been populated in the encoding, {feature_id!r}")
|
|
257
|
+
if not isinstance(concept, parcellation.Parcellation):
|
|
258
|
+
raise ParseLiveQueryIdException("region has been encoded, but previous encoded concept is not parcellation")
|
|
259
|
+
concept = concept.get_region(c.replace("r:", ""))
|
|
260
|
+
if concept is None:
|
|
261
|
+
raise ParseLiveQueryIdException(f"concept was not populated: {feature_id!r}")
|
|
262
|
+
|
|
263
|
+
return (F, concept, fid)
|
|
264
|
+
|
|
265
|
+
@classmethod
|
|
266
|
+
def parse_featuretype(cls, feature_type: str) -> List[Type['Feature']]:
|
|
267
|
+
return [
|
|
268
|
+
feattype
|
|
269
|
+
for FeatCls, feattypes in cls.SUBCLASSES.items()
|
|
270
|
+
if all(w.lower() in FeatCls.__name__.lower() for w in feature_type.split())
|
|
271
|
+
for feattype in feattypes
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
@classmethod
|
|
275
|
+
def livequery(cls, concept: Union[region.Region, parcellation.Parcellation, space.Space], **kwargs) -> List['Feature']:
|
|
276
|
+
if not hasattr(cls, "_live_queries"):
|
|
277
|
+
return []
|
|
278
|
+
|
|
279
|
+
live_instances = []
|
|
280
|
+
for QueryType in cls._live_queries:
|
|
281
|
+
argstr = f" ({', '.join('='.join(map(str,_)) for _ in kwargs.items())})" \
|
|
282
|
+
if len(kwargs) > 0 else ""
|
|
283
|
+
logger.info(
|
|
284
|
+
f"Running live query for {QueryType.feature_type.__name__} "
|
|
285
|
+
f"objects linked to {str(concept)}{argstr}"
|
|
286
|
+
)
|
|
287
|
+
q = QueryType(**kwargs)
|
|
288
|
+
features = [
|
|
289
|
+
Feature.wrap_livequery_feature(feat, Feature.serialize_query_context(feat, concept))
|
|
290
|
+
for feat in q.query(concept)
|
|
291
|
+
]
|
|
292
|
+
live_instances.extend(features)
|
|
293
|
+
return live_instances
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def match(cls, concept: Union[region.Region, parcellation.Parcellation, space.Space], feature_type: Union[str, Type['Feature'], list], **kwargs) -> List['Feature']:
|
|
297
|
+
"""
|
|
298
|
+
Retrieve data features of the desired modality.
|
|
299
|
+
|
|
300
|
+
Parameters
|
|
301
|
+
----------
|
|
302
|
+
concept: AtlasConcept
|
|
303
|
+
An anatomical concept, typically a brain region or parcellation.
|
|
304
|
+
modality: subclass of Feature
|
|
305
|
+
specififies the type of features ("modality")
|
|
306
|
+
"""
|
|
307
|
+
if isinstance(feature_type, list):
|
|
308
|
+
# a list of feature types is given, collect match results on those
|
|
309
|
+
assert all((isinstance(t, str) or issubclass(t, cls)) for t in feature_type)
|
|
310
|
+
return list(set(sum((cls.match(concept, t, **kwargs) for t in feature_type), [])))
|
|
311
|
+
|
|
312
|
+
if isinstance(feature_type, str):
|
|
313
|
+
# feature type given as a string. Decode the corresponding class.
|
|
314
|
+
# Some string inputs, such as connectivity, may hit multiple matches
|
|
315
|
+
# In this case
|
|
316
|
+
candidates = cls.parse_featuretype(feature_type)
|
|
317
|
+
if len(candidates) == 0:
|
|
318
|
+
raise ValueError(f"feature_type {str(feature_type)} did not match with any features. Available features are: {', '.join(cls.SUBCLASSES.keys())}")
|
|
319
|
+
|
|
320
|
+
return list({feat for c in candidates for feat in cls.match(concept, c, **kwargs)})
|
|
321
|
+
|
|
322
|
+
assert issubclass(feature_type, Feature)
|
|
323
|
+
|
|
324
|
+
if not isinstance(concept, (region.Region, parcellation.Parcellation, space.Space)):
|
|
325
|
+
raise ValueError(
|
|
326
|
+
"Feature.match / siibra.features.get only accepts Region, "
|
|
327
|
+
"Space and Parcellation objects as concept."
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
msg = f"Matching {feature_type.__name__} to {concept}"
|
|
331
|
+
instances = [
|
|
332
|
+
instance
|
|
333
|
+
for f_type in cls.SUBCLASSES[feature_type]
|
|
334
|
+
for instance in f_type.get_instances()
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
preconfigured_instances = [f for f in siibra_tqdm(instances, desc=msg, total=len(instances)) if f.matches(concept)]
|
|
338
|
+
|
|
339
|
+
live_instances = feature_type.livequery(concept, **kwargs)
|
|
340
|
+
|
|
341
|
+
return list(set((preconfigured_instances + live_instances)))
|
|
342
|
+
|
|
343
|
+
@classmethod
|
|
344
|
+
def get_instance_by_id(cls, feature_id: str, **kwargs):
|
|
345
|
+
try:
|
|
346
|
+
F, concept, fid = cls.deserialize_query_context(feature_id)
|
|
347
|
+
return [
|
|
348
|
+
f
|
|
349
|
+
for f in F.livequery(concept, **kwargs)
|
|
350
|
+
if f.id == fid or f.id == feature_id
|
|
351
|
+
][0]
|
|
352
|
+
except ParseLiveQueryIdException:
|
|
353
|
+
return [
|
|
354
|
+
inst
|
|
355
|
+
for Cls in Feature.SUBCLASSES[Feature]
|
|
356
|
+
for inst in Cls.get_instances()
|
|
357
|
+
if inst.id == feature_id
|
|
358
|
+
][0]
|
|
359
|
+
except IndexError:
|
|
360
|
+
raise NotFoundException
|
|
361
|
+
|
|
362
|
+
@classmethod
|
|
363
|
+
def get_ascii_tree(cls):
|
|
364
|
+
# build an Ascii representation of class hierarchy
|
|
365
|
+
# under this feature class
|
|
366
|
+
from anytree.importer import DictImporter
|
|
367
|
+
from anytree import RenderTree
|
|
368
|
+
|
|
369
|
+
def create_treenode(feature_type):
|
|
370
|
+
return {
|
|
371
|
+
'name': feature_type.__name__,
|
|
372
|
+
'children': [
|
|
373
|
+
create_treenode(c)
|
|
374
|
+
for c in feature_type.__subclasses__()
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
D = create_treenode(cls)
|
|
378
|
+
importer = DictImporter()
|
|
379
|
+
tree = importer.import_(D)
|
|
380
|
+
return "\n".join(
|
|
381
|
+
"%s%s" % (pre, node.name)
|
|
382
|
+
for pre, _, node in RenderTree(tree)
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
@staticmethod
|
|
386
|
+
def wrap_livequery_feature(feature: 'Feature', fid: str):
|
|
387
|
+
"""
|
|
388
|
+
Wrap live query features, override only the id attribute.
|
|
389
|
+
|
|
390
|
+
Some features do not have setters for the id property. The ProxyFeature class
|
|
391
|
+
allow the id property to be overridden without touching the underlying class.
|
|
392
|
+
|
|
393
|
+
See docstring of serialize_query_context for further context.
|
|
394
|
+
"""
|
|
395
|
+
class ProxyFeature(feature.__class__):
|
|
396
|
+
|
|
397
|
+
# override __class__ property
|
|
398
|
+
# some instances of features accesses inst.__class__
|
|
399
|
+
@property
|
|
400
|
+
def __class__(self):
|
|
401
|
+
return self.inst.__class__
|
|
402
|
+
|
|
403
|
+
def __init__(self, inst: Feature, fid: str):
|
|
404
|
+
self.inst = inst
|
|
405
|
+
self.fid = fid
|
|
406
|
+
|
|
407
|
+
def __str__(self) -> str:
|
|
408
|
+
return self.inst.__str__()
|
|
409
|
+
|
|
410
|
+
def __repr__(self) -> str:
|
|
411
|
+
return self.inst.__repr__()
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def id(self):
|
|
415
|
+
return self.fid
|
|
416
|
+
|
|
417
|
+
def __getattr__(self, __name: str):
|
|
418
|
+
return getattr(self.inst, __name)
|
|
419
|
+
|
|
420
|
+
return ProxyFeature(feature, fid)
|
|
@@ -13,4 +13,10 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
from .
|
|
16
|
+
from .volume_of_interest import (
|
|
17
|
+
CellBodyStainedVolumeOfInterest,
|
|
18
|
+
BlockfaceVolumeOfInterest,
|
|
19
|
+
PLIVolumeOfInterest,
|
|
20
|
+
MRIVolumeOfInterest,
|
|
21
|
+
# SegmentedVolumeOfInterest
|
|
22
|
+
)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
from
|
|
16
|
+
from .. import feature
|
|
17
17
|
|
|
18
18
|
from .. import anchor as _anchor
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ from ...volumes import volume as _volume
|
|
|
22
22
|
from typing import List
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
25
|
+
class ImageAnchor(_anchor.AnatomicalAnchor):
|
|
26
26
|
|
|
27
27
|
def __init__(self, volume: _volume.Volume, region: str = None):
|
|
28
28
|
_anchor.AnatomicalAnchor.__init__(
|
|
@@ -35,7 +35,9 @@ class VolumeOfInterestAnchor(_anchor.AnatomicalAnchor):
|
|
|
35
35
|
|
|
36
36
|
@property
|
|
37
37
|
def location(self):
|
|
38
|
-
"""
|
|
38
|
+
"""
|
|
39
|
+
Loads the bounding box only if required, since it demands image data access.
|
|
40
|
+
"""
|
|
39
41
|
if self._location_cached is None:
|
|
40
42
|
self._location_cached = self.volume.boundingbox
|
|
41
43
|
return self._location_cached
|
|
@@ -45,10 +47,10 @@ class VolumeOfInterestAnchor(_anchor.AnatomicalAnchor):
|
|
|
45
47
|
return self.volume.space
|
|
46
48
|
|
|
47
49
|
def __str__(self):
|
|
48
|
-
return "
|
|
50
|
+
return f"Bounding box of image in {self.space.name}"
|
|
49
51
|
|
|
50
52
|
|
|
51
|
-
class
|
|
53
|
+
class Image(feature.Feature, _volume.Volume):
|
|
52
54
|
|
|
53
55
|
def __init__(
|
|
54
56
|
self,
|
|
@@ -66,13 +68,16 @@ class VolumeOfInterest(feature.Feature, _volume.Volume, configuration_folder="fe
|
|
|
66
68
|
anchor=None, # lazy implementation below!
|
|
67
69
|
datasets=datasets
|
|
68
70
|
)
|
|
71
|
+
|
|
69
72
|
_volume.Volume.__init__(
|
|
70
73
|
self,
|
|
71
74
|
space_spec=space_spec,
|
|
72
75
|
providers=providers,
|
|
73
76
|
name=name,
|
|
77
|
+
datasets=datasets,
|
|
74
78
|
)
|
|
75
|
-
|
|
79
|
+
|
|
80
|
+
self._anchor_cached = ImageAnchor(self, region=region)
|
|
76
81
|
self._description_cached = None
|
|
77
82
|
self._name_cached = name
|
|
78
83
|
|
|
@@ -87,7 +92,7 @@ class VolumeOfInterest(feature.Feature, _volume.Volume, configuration_folder="fe
|
|
|
87
92
|
def description(self):
|
|
88
93
|
if self._description_cached is None:
|
|
89
94
|
self._description_cached = (
|
|
90
|
-
f"
|
|
95
|
+
f"Image feature with modality {self.modality} "
|
|
91
96
|
f"at {self.anchor}"
|
|
92
97
|
)
|
|
93
98
|
return self._description_cached
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
from .
|
|
16
|
+
from . import image
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
class CellbodyStainedSection(
|
|
20
|
+
image.Image,
|
|
21
|
+
configuration_folder='features/images/sections/cellbody',
|
|
22
|
+
category="cellular"
|
|
23
|
+
):
|
|
24
|
+
def __init__(self, **kwargs):
|
|
25
|
+
image.Image.__init__(self, **kwargs, modality="cell body staining")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright 2018-2021
|
|
2
|
+
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
|
+
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
from . import image
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CellBodyStainedVolumeOfInterest(
|
|
20
|
+
image.Image,
|
|
21
|
+
configuration_folder="features/images/vois/cellbody",
|
|
22
|
+
category="cellular"
|
|
23
|
+
):
|
|
24
|
+
def __init__(self, **kwargs):
|
|
25
|
+
image.Image.__init__(self, **kwargs, modality="cell body staining")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BlockfaceVolumeOfInterest(
|
|
29
|
+
image.Image,
|
|
30
|
+
configuration_folder="features/images/vois/blockface",
|
|
31
|
+
category="macrostructural"
|
|
32
|
+
):
|
|
33
|
+
def __init__(self, **kwargs):
|
|
34
|
+
image.Image.__init__(self, **kwargs, modality="blockface")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PLIVolumeOfInterest(
|
|
38
|
+
image.Image,
|
|
39
|
+
configuration_folder="features/images/vois/pli",
|
|
40
|
+
category="fibres"
|
|
41
|
+
):
|
|
42
|
+
def __init__(self, modality, **kwargs):
|
|
43
|
+
image.Image.__init__(self, **kwargs, modality=modality)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class MRIVolumeOfInterest(
|
|
47
|
+
image.Image,
|
|
48
|
+
configuration_folder="features/images/vois/mri",
|
|
49
|
+
category="macrostructural"
|
|
50
|
+
):
|
|
51
|
+
def __init__(self, modality, **kwargs):
|
|
52
|
+
image.Image.__init__(self, **kwargs, modality=modality)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class XPCTVolumeOfInterest(
|
|
56
|
+
image.Image,
|
|
57
|
+
configuration_folder="features/images/vois/xpct",
|
|
58
|
+
category="cellular"
|
|
59
|
+
):
|
|
60
|
+
def __init__(self, modality, **kwargs):
|
|
61
|
+
image.Image.__init__(self, **kwargs, modality=modality)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# class SegmentedVolumeOfInterest(
|
|
65
|
+
# image.Image,
|
|
66
|
+
# configuration_folder="features/images/vois/segmentation",
|
|
67
|
+
# category="segmentation"
|
|
68
|
+
# ):
|
|
69
|
+
# def __init__(self, **kwargs):
|
|
70
|
+
# image.Image.__init__(self, **kwargs, modality="segmentation")
|
|
@@ -13,16 +13,12 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
from .cell_density_profile import CellDensityProfile
|
|
17
|
-
from .layerwise_cell_density import LayerwiseCellDensity
|
|
18
16
|
from .bigbrain_intensity_profile import BigBrainIntensityProfile
|
|
17
|
+
from .cell_density_profile import CellDensityProfile
|
|
18
|
+
from .gene_expression import GeneExpressions
|
|
19
19
|
from .layerwise_bigbrain_intensities import LayerwiseBigBrainIntensities
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"LayerwiseCellDensity",
|
|
26
|
-
"BigBrainIntensityProfile",
|
|
27
|
-
"LayerwiseBigBrainIntensities",
|
|
28
|
-
]
|
|
20
|
+
from .layerwise_cell_density import LayerwiseCellDensity
|
|
21
|
+
from .receptor_density_fingerprint import ReceptorDensityFingerprint
|
|
22
|
+
from .receptor_density_profile import ReceptorDensityProfile
|
|
23
|
+
from .regional_timeseries_activity import RegionalTimeseriesActivity
|
|
24
|
+
from .regional_timeseries_activity import RegionalBOLD
|
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
from
|
|
16
|
+
from . import cortical_profile
|
|
17
17
|
|
|
18
18
|
from ...locations import point
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class BigBrainIntensityProfile(
|
|
21
|
+
class BigBrainIntensityProfile(
|
|
22
|
+
cortical_profile.CorticalProfile,
|
|
23
|
+
category='cellular'
|
|
24
|
+
):
|
|
22
25
|
|
|
23
26
|
DESCRIPTION = (
|
|
24
27
|
"Cortical profiles of BigBrain staining intensities computed by Konrad Wagstyl, "
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
16
|
from .. import anchor as _anchor
|
|
17
|
-
from
|
|
17
|
+
from . import cortical_profile
|
|
18
18
|
|
|
19
19
|
from ...commons import PolyLine, logger, create_key
|
|
20
20
|
from ...retrieval import requests
|
|
@@ -26,7 +26,11 @@ import numpy as np
|
|
|
26
26
|
import pandas as pd
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
class CellDensityProfile(
|
|
29
|
+
class CellDensityProfile(
|
|
30
|
+
cortical_profile.CorticalProfile,
|
|
31
|
+
configuration_folder="features/tabular/corticalprofiles/celldensity",
|
|
32
|
+
category='cellular'
|
|
33
|
+
):
|
|
30
34
|
|
|
31
35
|
DESCRIPTION = (
|
|
32
36
|
"Cortical profile of estimated densities of detected cell bodies (in detected cells per 0.1 cube millimeter) "
|