siibra 1.0.1a0__py3-none-any.whl → 1.0.1a1__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 +4 -4
- siibra/commons.py +8 -7
- siibra/configuration/__init__.py +1 -1
- siibra/configuration/configuration.py +1 -1
- siibra/configuration/factory.py +1 -1
- siibra/core/__init__.py +1 -1
- siibra/core/assignment.py +1 -1
- siibra/core/atlas.py +1 -1
- siibra/core/concept.py +3 -3
- siibra/core/parcellation.py +6 -6
- siibra/core/region.py +59 -49
- siibra/core/space.py +1 -1
- siibra/core/structure.py +2 -2
- siibra/exceptions.py +6 -2
- siibra/experimental/__init__.py +1 -1
- siibra/experimental/contour.py +2 -2
- siibra/experimental/cortical_profile_sampler.py +1 -1
- siibra/experimental/patch.py +1 -1
- siibra/experimental/plane3d.py +4 -4
- siibra/explorer/__init__.py +1 -1
- siibra/explorer/url.py +2 -2
- siibra/explorer/util.py +1 -1
- siibra/features/__init__.py +1 -1
- siibra/features/anchor.py +2 -2
- siibra/features/connectivity/__init__.py +1 -1
- siibra/features/connectivity/functional_connectivity.py +1 -1
- siibra/features/connectivity/regional_connectivity.py +2 -2
- siibra/features/connectivity/streamline_counts.py +1 -1
- siibra/features/connectivity/streamline_lengths.py +1 -1
- siibra/features/connectivity/tracing_connectivity.py +1 -1
- siibra/features/dataset/__init__.py +1 -1
- siibra/features/dataset/ebrains.py +1 -1
- siibra/features/feature.py +10 -10
- siibra/features/image/__init__.py +1 -1
- siibra/features/image/image.py +2 -2
- siibra/features/image/sections.py +1 -1
- siibra/features/image/volume_of_interest.py +1 -1
- siibra/features/tabular/__init__.py +1 -1
- siibra/features/tabular/bigbrain_intensity_profile.py +1 -1
- siibra/features/tabular/cell_density_profile.py +2 -2
- siibra/features/tabular/cortical_profile.py +3 -3
- siibra/features/tabular/gene_expression.py +1 -1
- siibra/features/tabular/layerwise_bigbrain_intensities.py +1 -1
- siibra/features/tabular/layerwise_cell_density.py +1 -1
- siibra/features/tabular/receptor_density_fingerprint.py +13 -10
- siibra/features/tabular/receptor_density_profile.py +1 -1
- siibra/features/tabular/regional_timeseries_activity.py +2 -2
- siibra/features/tabular/tabular.py +7 -5
- siibra/livequeries/__init__.py +1 -1
- siibra/livequeries/allen.py +3 -3
- siibra/livequeries/bigbrain.py +15 -6
- siibra/livequeries/ebrains.py +1 -1
- siibra/livequeries/query.py +2 -2
- siibra/locations/__init__.py +2 -2
- siibra/locations/boundingbox.py +3 -7
- siibra/locations/location.py +1 -1
- siibra/locations/point.py +3 -3
- siibra/locations/pointcloud.py +9 -14
- siibra/retrieval/__init__.py +1 -1
- siibra/retrieval/cache.py +1 -1
- siibra/retrieval/datasets.py +4 -4
- siibra/retrieval/exceptions/__init__.py +1 -1
- siibra/retrieval/repositories.py +4 -4
- siibra/retrieval/requests.py +6 -6
- siibra/vocabularies/__init__.py +1 -1
- siibra/volumes/__init__.py +1 -1
- siibra/volumes/parcellationmap.py +31 -10
- siibra/volumes/providers/__init__.py +1 -1
- siibra/volumes/providers/freesurfer.py +3 -3
- siibra/volumes/providers/gifti.py +1 -1
- siibra/volumes/providers/neuroglancer.py +6 -6
- siibra/volumes/providers/nifti.py +1 -1
- siibra/volumes/providers/provider.py +1 -1
- siibra/volumes/sparsemap.py +1 -1
- siibra/volumes/volume.py +12 -12
- {siibra-1.0.1a0.dist-info → siibra-1.0.1a1.dist-info}/METADATA +12 -3
- siibra-1.0.1a1.dist-info/RECORD +84 -0
- {siibra-1.0.1a0.dist-info → siibra-1.0.1a1.dist-info}/WHEEL +1 -1
- siibra-1.0.1a0.dist-info/RECORD +0 -84
- {siibra-1.0.1a0.dist-info → siibra-1.0.1a1.dist-info}/LICENSE +0 -0
- {siibra-1.0.1a0.dist-info → siibra-1.0.1a1.dist-info}/top_level.txt +0 -0
siibra/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.1-alpha.
|
|
1
|
+
1.0.1-alpha.1
|
siibra/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -108,7 +108,7 @@ def set_feasible_download_size(maxsize_gbyte):
|
|
|
108
108
|
|
|
109
109
|
def set_cache_size(maxsize_gbyte: int):
|
|
110
110
|
"""
|
|
111
|
-
siibra runs
|
|
111
|
+
siibra runs maintenance on its local cache to keep it under a predetermined
|
|
112
112
|
size of 2 gigabytes. This method changes the cache size.
|
|
113
113
|
|
|
114
114
|
Parameters
|
|
@@ -129,9 +129,9 @@ def warm_cache(level=WarmupLevel.INSTANCE):
|
|
|
129
129
|
Preload preconfigured siibra concepts.
|
|
130
130
|
|
|
131
131
|
Siibra relies on preconfigurations that simplify integrating various
|
|
132
|
-
concepts such as parcellations,
|
|
132
|
+
concepts such as parcellations, reference spaces, and multimodal data
|
|
133
133
|
features. By preloading the instances, siibra commits all preconfigurations
|
|
134
|
-
to the memory at once instead of
|
|
134
|
+
to the memory at once instead of committing them when required.
|
|
135
135
|
"""
|
|
136
136
|
Warmup.warmup(level)
|
|
137
137
|
|
siibra/commons.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -139,7 +139,7 @@ class InstanceTable(Generic[T], Iterable):
|
|
|
139
139
|
return (w for w in self._elements.values())
|
|
140
140
|
|
|
141
141
|
def __contains__(self, key: Union[str, T]) -> bool:
|
|
142
|
-
"""Test
|
|
142
|
+
"""Test whether the given key or element is defined by the registry."""
|
|
143
143
|
if isinstance(key, str):
|
|
144
144
|
return key in self._elements
|
|
145
145
|
return key in [item for _, item in self._elements.values()]
|
|
@@ -536,7 +536,8 @@ def resample_img_to_img(
|
|
|
536
536
|
resampled_img = resample_to_img(
|
|
537
537
|
source_img=source_img,
|
|
538
538
|
target_img=target_img,
|
|
539
|
-
interpolation=interpolation
|
|
539
|
+
interpolation=interpolation,
|
|
540
|
+
force_resample=True, # False is intended for testing. see nilearn docs
|
|
540
541
|
)
|
|
541
542
|
return resampled_img
|
|
542
543
|
|
|
@@ -554,7 +555,7 @@ def connected_components(
|
|
|
554
555
|
|
|
555
556
|
Note
|
|
556
557
|
----
|
|
557
|
-
`Uses skimage.measure.label()` to determine foreground
|
|
558
|
+
`Uses skimage.measure.label()` to determine foreground components.
|
|
558
559
|
|
|
559
560
|
Parameters
|
|
560
561
|
----------
|
|
@@ -568,7 +569,7 @@ def connected_components(
|
|
|
568
569
|
Yields
|
|
569
570
|
------
|
|
570
571
|
Generator[Tuple[int, np.ndarray], None, None]
|
|
571
|
-
tuple of integer label of the component and component as an
|
|
572
|
+
tuple of integer label of the component and component as an ndarray in
|
|
572
573
|
the shape of the original image.
|
|
573
574
|
"""
|
|
574
575
|
from skimage import measure
|
|
@@ -644,11 +645,11 @@ def MI(arr1, arr2, nbins=100, normalized=True):
|
|
|
644
645
|
assert (all(arr.size > 0) for arr in [arr1, arr2])
|
|
645
646
|
|
|
646
647
|
# compute the normalized joint 2D histogram as an
|
|
647
|
-
# empirical measure of the joint
|
|
648
|
+
# empirical measure of the joint probability of arr1 and arr2
|
|
648
649
|
pxy, _, _ = np.histogram2d(arr1.ravel(), arr2.ravel(), bins=nbins)
|
|
649
650
|
pxy /= pxy.sum()
|
|
650
651
|
|
|
651
|
-
# extract the empirical
|
|
652
|
+
# extract the empirical probabilities of intensities
|
|
652
653
|
# from the joint histogram
|
|
653
654
|
px = np.sum(pxy, axis=1) # marginal for x over y
|
|
654
655
|
py = np.sum(pxy, axis=0) # marginal for y over x
|
siibra/configuration/__init__.py
CHANGED
siibra/configuration/factory.py
CHANGED
siibra/core/__init__.py
CHANGED
siibra/core/assignment.py
CHANGED
siibra/core/atlas.py
CHANGED
siibra/core/concept.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -145,9 +145,9 @@ class AtlasConcept:
|
|
|
145
145
|
@property
|
|
146
146
|
def authors(self):
|
|
147
147
|
return [
|
|
148
|
-
|
|
148
|
+
contributor['name']
|
|
149
149
|
for ds in self.datasets
|
|
150
|
-
for
|
|
150
|
+
for contributor in ds.contributors
|
|
151
151
|
]
|
|
152
152
|
|
|
153
153
|
@property
|
siibra/core/parcellation.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -17,7 +17,7 @@ from . import region
|
|
|
17
17
|
|
|
18
18
|
from ..commons import logger, MapType, Species
|
|
19
19
|
from ..volumes import parcellationmap
|
|
20
|
-
from ..exceptions import
|
|
20
|
+
from ..exceptions import MapNotFound
|
|
21
21
|
|
|
22
22
|
from functools import lru_cache
|
|
23
23
|
import re
|
|
@@ -193,13 +193,13 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
|
|
|
193
193
|
and m.parcellation.matches(self)
|
|
194
194
|
]
|
|
195
195
|
if len(candidates) == 0:
|
|
196
|
-
raise
|
|
196
|
+
raise MapNotFound(f"No '{maptype}' map in '{space}' available for {str(self)}")
|
|
197
197
|
if len(candidates) > 1:
|
|
198
198
|
spec_candidates = [
|
|
199
199
|
c for c in candidates if all(w.lower() in c.id.lower() for w in spec.split())
|
|
200
200
|
]
|
|
201
201
|
if len(spec_candidates) == 0:
|
|
202
|
-
raise
|
|
202
|
+
raise MapNotFound(f"'{spec}' does not match any options from {[c.name for c in candidates]}.")
|
|
203
203
|
if len(spec_candidates) > 1:
|
|
204
204
|
logger.warning(
|
|
205
205
|
f"Multiple maps are available in this specification of space, parcellation, and map type.\n"
|
|
@@ -221,7 +221,7 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
|
|
|
221
221
|
"""
|
|
222
222
|
spec = re.sub(r'Group: *', '', spec)
|
|
223
223
|
for substr in re.findall(r'\(.*?\)', spec):
|
|
224
|
-
#
|
|
224
|
+
# temporarily replace commas inside brackets with a placeholder
|
|
225
225
|
# because these are not region spec delimiters
|
|
226
226
|
spec = spec.replace(substr, re.sub(r', *', '##', substr))
|
|
227
227
|
# process the comma separated substrings
|
|
@@ -354,7 +354,7 @@ def find_regions(
|
|
|
354
354
|
):
|
|
355
355
|
"""
|
|
356
356
|
Find regions matching the given region specification across all parcellation
|
|
357
|
-
instances in the
|
|
357
|
+
instances in the registry.
|
|
358
358
|
|
|
359
359
|
Parameters
|
|
360
360
|
----------
|
siibra/core/region.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -31,7 +31,7 @@ from ..exceptions import NoMapAvailableError, SpaceWarpingFailedError
|
|
|
31
31
|
|
|
32
32
|
import re
|
|
33
33
|
import anytree
|
|
34
|
-
from typing import List, Union, Iterable, Dict, Callable, Tuple
|
|
34
|
+
from typing import List, Union, Iterable, Dict, Callable, Tuple, Set
|
|
35
35
|
from difflib import SequenceMatcher
|
|
36
36
|
from ebrains_drive import BucketApiClient
|
|
37
37
|
import json
|
|
@@ -53,8 +53,8 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
53
53
|
_regex_re = re.compile(r'^\/(?P<expression>.+)\/(?P<flags>[a-zA-Z]*)$')
|
|
54
54
|
_accepted_flags = "aiLmsux"
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
_GETMASK_CACHE = {}
|
|
57
|
+
_GETMASK_CACHE_MAX_ENTRIES = 1
|
|
58
58
|
|
|
59
59
|
def __init__(
|
|
60
60
|
self,
|
|
@@ -111,20 +111,20 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
111
111
|
)
|
|
112
112
|
|
|
113
113
|
# anytree node will take care to use this appropriately
|
|
114
|
-
self.parent = parent
|
|
115
|
-
self.children = children
|
|
114
|
+
self.parent: "Region" = parent
|
|
115
|
+
self.children: List["Region"] = children
|
|
116
116
|
# convert hex to int tuple if rgb is given
|
|
117
117
|
self.rgb = (
|
|
118
118
|
None if rgb is None
|
|
119
119
|
else tuple(int(rgb[p:p + 2], 16) for p in [1, 3, 5])
|
|
120
120
|
)
|
|
121
|
-
self._supported_spaces = None # computed on 1st call of self.supported_spaces
|
|
121
|
+
self._supported_spaces: Set[_space.Space] = None # computed on 1st call of self.supported_spaces
|
|
122
122
|
self._str_aliases = None
|
|
123
123
|
self.find = lru_cache(maxsize=3)(self.find)
|
|
124
124
|
|
|
125
125
|
def get_related_regions(self) -> Iterable["RegionRelationAssessments"]:
|
|
126
126
|
"""
|
|
127
|
-
Get
|
|
127
|
+
Get assessments on relations of this region to others defined on EBRAINS.
|
|
128
128
|
|
|
129
129
|
Yields
|
|
130
130
|
------
|
|
@@ -133,8 +133,8 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
133
133
|
Example
|
|
134
134
|
-------
|
|
135
135
|
>>> region = siibra.get_region("monkey", "PG")
|
|
136
|
-
>>> for
|
|
137
|
-
>>> print(
|
|
136
|
+
>>> for assessment in region.get_related_regions():
|
|
137
|
+
>>> print(assessment)
|
|
138
138
|
'PG' is homologous to 'Area PGa (IPL)'
|
|
139
139
|
'PG' is homologous to 'Area PGa (IPL) left'
|
|
140
140
|
'PG' is homologous to 'Area PGa (IPL) right'
|
|
@@ -441,12 +441,21 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
441
441
|
maptype = MapType[maptype.upper()]
|
|
442
442
|
|
|
443
443
|
threshold_info = "" if maptype == MapType.LABELLED else f"(threshold: {threshold}) "
|
|
444
|
+
# check cache
|
|
445
|
+
getmask_hash = hash(f"{self.id} - {space} - {maptype}{threshold_info}")
|
|
446
|
+
if getmask_hash in self._GETMASK_CACHE:
|
|
447
|
+
return self._GETMASK_CACHE[getmask_hash]
|
|
448
|
+
|
|
444
449
|
name = f"Mask {threshold_info}of '{self.name} ({self.parcellation})' in "
|
|
445
450
|
try:
|
|
446
451
|
regional_map = self.get_regional_map(space=space, maptype=maptype)
|
|
447
452
|
if maptype == MapType.LABELLED:
|
|
448
453
|
assert threshold == 0.0, f"threshold can only be set for {MapType.STATISTICAL} maps."
|
|
449
|
-
result =
|
|
454
|
+
result = volume.FilteredVolume(
|
|
455
|
+
parent_volume=regional_map,
|
|
456
|
+
label=regional_map.label,
|
|
457
|
+
fragment=regional_map.fragment
|
|
458
|
+
)
|
|
450
459
|
result._boundingbox = None
|
|
451
460
|
if maptype == MapType.STATISTICAL:
|
|
452
461
|
result = volume.FilteredVolume(
|
|
@@ -456,21 +465,30 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
456
465
|
if threshold == 0.0:
|
|
457
466
|
result._boundingbox = regional_map._boundingbox
|
|
458
467
|
name += f"'{result.space}'"
|
|
459
|
-
except NoMapAvailableError:
|
|
468
|
+
except NoMapAvailableError as e:
|
|
460
469
|
# This region is not mapped directly in any map in the registry.
|
|
461
470
|
# Try building a map from the child regions
|
|
462
|
-
if (len(self.children) > 0) and
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
471
|
+
if (len(self.children) > 0) and self.mapped_in_space(space, recurse=True):
|
|
472
|
+
mapped_descendants: List[Region] = [
|
|
473
|
+
d for d in self.descendants if d.mapped_in_space(space, recurse=False)
|
|
474
|
+
]
|
|
475
|
+
logger.info(f"{self.name} is not mapped in {space}. Merging the masks of its {len(mapped_descendants)} map descendants.")
|
|
476
|
+
descendant_volumes = [
|
|
477
|
+
descendant.get_regional_mask(space=space, maptype=maptype, threshold=threshold)
|
|
478
|
+
for descendant in mapped_descendants
|
|
467
479
|
]
|
|
468
480
|
result = volume.FilteredVolume(
|
|
469
|
-
volume.merge(
|
|
481
|
+
volume.merge(descendant_volumes),
|
|
470
482
|
label=1
|
|
471
483
|
)
|
|
472
|
-
name += f"'{result.space}' (built by merging the mask {threshold_info} of its
|
|
484
|
+
name += f"'{result.space}' (built by merging the mask {threshold_info} of its descendants)"
|
|
485
|
+
else:
|
|
486
|
+
raise e
|
|
473
487
|
result._name = name
|
|
488
|
+
|
|
489
|
+
while len(self._GETMASK_CACHE) > self._GETMASK_CACHE_MAX_ENTRIES:
|
|
490
|
+
self._GETMASK_CACHE.pop(next(iter(self._GETMASK_CACHE)))
|
|
491
|
+
self._GETMASK_CACHE[getmask_hash] = result
|
|
474
492
|
return result
|
|
475
493
|
|
|
476
494
|
def get_regional_map(
|
|
@@ -479,7 +497,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
479
497
|
maptype: MapType = MapType.LABELLED,
|
|
480
498
|
) -> Union[volume.FilteredVolume, volume.Volume, volume.Subvolume]:
|
|
481
499
|
"""
|
|
482
|
-
Get a volume
|
|
500
|
+
Get a volume representing this region in the given space and MapType.
|
|
483
501
|
|
|
484
502
|
Note
|
|
485
503
|
----
|
|
@@ -515,34 +533,31 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
515
533
|
and self.name in m.regions
|
|
516
534
|
):
|
|
517
535
|
return m.get_volume(region=self)
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
)
|
|
536
|
+
raise NoMapAvailableError(
|
|
537
|
+
f"{self.name} is not mapped in {space} as a {str(maptype)} map."
|
|
538
|
+
" Please try getting the children or getting the mask."
|
|
539
|
+
)
|
|
523
540
|
|
|
524
|
-
def mapped_in_space(self, space, recurse: bool =
|
|
541
|
+
def mapped_in_space(self, space, recurse: bool = False) -> bool:
|
|
525
542
|
"""
|
|
526
|
-
Verifies
|
|
543
|
+
Verifies whether this region is defined by an explicit map in the given space.
|
|
527
544
|
|
|
528
545
|
Parameters
|
|
529
546
|
----------
|
|
530
547
|
space: Space or str
|
|
531
548
|
reference space
|
|
532
|
-
recurse: bool, default:
|
|
533
|
-
If True, check if all child regions are mapped instead
|
|
549
|
+
recurse: bool, default: False
|
|
550
|
+
If True, check if itself or all child regions are mapped instead recursively.
|
|
534
551
|
Returns
|
|
535
552
|
-------
|
|
536
553
|
bool
|
|
537
554
|
"""
|
|
538
555
|
from ..volumes.parcellationmap import Map
|
|
539
556
|
for m in Map.registry():
|
|
540
|
-
# Use and operant for efficiency (short circuiting logic)
|
|
541
|
-
# Put the most inexpensive logic first
|
|
542
557
|
if (
|
|
543
|
-
|
|
544
|
-
and m.space.matches(space)
|
|
558
|
+
m.space.matches(space)
|
|
545
559
|
and m.parcellation.matches(self.parcellation)
|
|
560
|
+
and self.name in m.regions
|
|
546
561
|
):
|
|
547
562
|
return True
|
|
548
563
|
if recurse and not self.is_leaf:
|
|
@@ -553,21 +568,16 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
553
568
|
@property
|
|
554
569
|
def supported_spaces(self) -> List[_space.Space]:
|
|
555
570
|
"""
|
|
556
|
-
The
|
|
557
|
-
|
|
571
|
+
The list of spaces for which a mask could be extracted from an existing
|
|
572
|
+
map or combination of masks of its children.
|
|
558
573
|
"""
|
|
559
574
|
if self._supported_spaces is None:
|
|
560
|
-
self._supported_spaces = sorted(
|
|
561
|
-
|
|
562
|
-
|
|
575
|
+
self._supported_spaces = sorted({
|
|
576
|
+
s for s in _space.Space.registry()
|
|
577
|
+
if self.mapped_in_space(s, recurse=True)
|
|
578
|
+
})
|
|
563
579
|
return self._supported_spaces
|
|
564
580
|
|
|
565
|
-
def supports_space(self, space: _space.Space):
|
|
566
|
-
"""
|
|
567
|
-
Return true if this region supports the given space, else False.
|
|
568
|
-
"""
|
|
569
|
-
return any(s.matches(space) for s in self.supported_spaces)
|
|
570
|
-
|
|
571
581
|
@property
|
|
572
582
|
def spaces(self):
|
|
573
583
|
return InstanceTable(
|
|
@@ -610,7 +620,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
610
620
|
return self._ASSIGNMENT_CACHE[other, self].invert()
|
|
611
621
|
|
|
612
622
|
if isinstance(other, (location.Location, volume.Volume)):
|
|
613
|
-
if self.mapped_in_space(other.space):
|
|
623
|
+
if self.mapped_in_space(other.space, recurse=True):
|
|
614
624
|
regionmap = self.get_regional_mask(other.space)
|
|
615
625
|
self._ASSIGNMENT_CACHE[self, other] = regionmap.assign(other)
|
|
616
626
|
return self._ASSIGNMENT_CACHE[self, other]
|
|
@@ -842,7 +852,7 @@ class Region(anytree.NodeMixin, concept.AtlasConcept, structure.BrainStructure):
|
|
|
842
852
|
def intersection(self, other: "location.Location") -> "location.Location":
|
|
843
853
|
"""Use this region for filtering a location object."""
|
|
844
854
|
|
|
845
|
-
if self.
|
|
855
|
+
if self.mapped_in_space(other.space, recurse=True):
|
|
846
856
|
try:
|
|
847
857
|
volume = self.get_regional_mask(other.space)
|
|
848
858
|
if volume is not None:
|
|
@@ -908,7 +918,7 @@ def get_peid_from_region(region: Region) -> str:
|
|
|
908
918
|
|
|
909
919
|
def get_related_regions(region: Region) -> Iterable["RegionRelationAssessments"]:
|
|
910
920
|
"""
|
|
911
|
-
Get
|
|
921
|
+
Get assessments on relations of a region to others defined on EBRAINS.
|
|
912
922
|
|
|
913
923
|
Parameters
|
|
914
924
|
----------
|
|
@@ -921,8 +931,8 @@ def get_related_regions(region: Region) -> Iterable["RegionRelationAssessments"]
|
|
|
921
931
|
Example
|
|
922
932
|
-------
|
|
923
933
|
>>> region = siibra.get_region("monkey", "PG")
|
|
924
|
-
>>> for
|
|
925
|
-
>>> print(
|
|
934
|
+
>>> for assessment in siibra.core.region.get_related_regions(region):
|
|
935
|
+
>>> print(assessment)
|
|
926
936
|
'PG' is homologous to 'Area PGa (IPL)'
|
|
927
937
|
'PG' is homologous to 'Area PGa (IPL) left'
|
|
928
938
|
'PG' is homologous to 'Area PGa (IPL) right'
|
siibra/core/space.py
CHANGED
siibra/core/structure.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -76,7 +76,7 @@ class BrainStructure(ABC):
|
|
|
76
76
|
# Two cases:
|
|
77
77
|
# 1) self is location, other is location -> look at spatial intersection/relationship, do it here
|
|
78
78
|
# 2) self is location, other is region -> get region map, then call again. do it here
|
|
79
|
-
# If self is region -> Region overwrite this method,
|
|
79
|
+
# If self is region -> Region overwrite this method, addressed there
|
|
80
80
|
|
|
81
81
|
assert not isinstance(self, _region.Region) # method is overwritten by Region!
|
|
82
82
|
if (self, other) in self._ASSIGNMENT_CACHE:
|
siibra/exceptions.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -55,9 +55,13 @@ class NoneCoordinateSuppliedError(ValueError):
|
|
|
55
55
|
pass
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
class
|
|
58
|
+
class MapNotFound(ValueError):
|
|
59
59
|
pass
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class EmptyPointCloudError(ValueError):
|
|
63
63
|
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class NoPredifinedColormapException(ValueError):
|
|
67
|
+
pass
|
siibra/experimental/__init__.py
CHANGED
siibra/experimental/contour.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -46,7 +46,7 @@ class Contour(pointcloud.PointCloud):
|
|
|
46
46
|
|
|
47
47
|
if cropped is not None and not isinstance(cropped, point.Point):
|
|
48
48
|
assert isinstance(cropped, pointcloud.PointCloud)
|
|
49
|
-
#
|
|
49
|
+
# Identify contour splits are by discontinuouities ("jumps")
|
|
50
50
|
# of their labels, which denote positions in the original contour
|
|
51
51
|
jumps = np.diff([self.labels.index(lb) for lb in cropped.labels])
|
|
52
52
|
splits = [0] + list(np.where(jumps > 1)[0] + 1) + [len(cropped)]
|
siibra/experimental/patch.py
CHANGED
siibra/experimental/plane3d.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -33,7 +33,7 @@ class Plane3D:
|
|
|
33
33
|
The plane's reference space is defined by the first point.
|
|
34
34
|
"""
|
|
35
35
|
self.space = point1.space
|
|
36
|
-
# normal is the cross product of two
|
|
36
|
+
# normal is the cross product of two arbitrary in-plane vectors
|
|
37
37
|
n = np.cross(
|
|
38
38
|
(point2.warp(self.space) - point1).coordinate,
|
|
39
39
|
(point3.warp(self.space) - point1).coordinate,
|
|
@@ -107,7 +107,7 @@ class Plane3D:
|
|
|
107
107
|
)[0]
|
|
108
108
|
faces = mesh["faces"][face_indices]
|
|
109
109
|
|
|
110
|
-
# for each of N selected faces, indicate
|
|
110
|
+
# for each of N selected faces, indicate whether we cross the plane
|
|
111
111
|
# as we go from vertex 2->0, 0->1, 1->2, respectively.
|
|
112
112
|
# This gives us an Nx3 array, where forward crossings are identified by 1,
|
|
113
113
|
# and backward crossings by -1.
|
|
@@ -160,7 +160,7 @@ class Plane3D:
|
|
|
160
160
|
face_id = 0 # index of the mesh face to consider
|
|
161
161
|
while len(face_indices) > 0:
|
|
162
162
|
|
|
163
|
-
# continue the contour with the next
|
|
163
|
+
# continue the contour with the next forward edge intersection
|
|
164
164
|
p = fwd_intersections[face_id]
|
|
165
165
|
points.append(p)
|
|
166
166
|
# Remember the ids of the face and start-/end vertices for the point
|
siibra/explorer/__init__.py
CHANGED
siibra/explorer/url.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -211,7 +211,7 @@ def get_hash(full_string: str):
|
|
|
211
211
|
for char in full_string:
|
|
212
212
|
# overflowing is expected and in fact the whole reason why convert number to int32
|
|
213
213
|
|
|
214
|
-
# in windows, int32((0 - min_int32) << 5), rather than overflow to
|
|
214
|
+
# in windows, int32((0 - min_int32) << 5), rather than overflow to wrapper around, raises OverflowError
|
|
215
215
|
shifted_5 = int32(
|
|
216
216
|
(return_val - min_int32) if return_val > max_int32 else return_val << 5
|
|
217
217
|
)
|
siibra/explorer/util.py
CHANGED
siibra/features/__init__.py
CHANGED
siibra/features/anchor.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -88,7 +88,7 @@ class AnatomicalAnchor:
|
|
|
88
88
|
|
|
89
89
|
@property
|
|
90
90
|
def space(self) -> Space:
|
|
91
|
-
# may be
|
|
91
|
+
# may be overridden by derived classes, e.g. in features.VolumeOfInterest
|
|
92
92
|
return None if self.location is None else self.location.space
|
|
93
93
|
|
|
94
94
|
@property
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2018-
|
|
1
|
+
# Copyright 2018-2025
|
|
2
2
|
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
3
|
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -275,7 +275,7 @@ class RegionalConnectivity(Feature, Compoundable):
|
|
|
275
275
|
|
|
276
276
|
regions = [r for r in matrix.index if matches(r, region)]
|
|
277
277
|
if len(regions) == 0:
|
|
278
|
-
raise ValueError(f"Invalid region
|
|
278
|
+
raise ValueError(f"Invalid region specification: {region}")
|
|
279
279
|
elif len(regions) > 1:
|
|
280
280
|
raise ValueError(f"Region specification {region} matched more than one profile: {regions}")
|
|
281
281
|
else:
|