siibra 1.0a14__py3-none-any.whl → 1.0.1a0__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.

Files changed (80) hide show
  1. siibra/VERSION +1 -1
  2. siibra/__init__.py +15 -5
  3. siibra/commons.py +3 -48
  4. siibra/configuration/__init__.py +1 -1
  5. siibra/configuration/configuration.py +1 -1
  6. siibra/configuration/factory.py +164 -127
  7. siibra/core/__init__.py +1 -1
  8. siibra/core/assignment.py +1 -1
  9. siibra/core/atlas.py +24 -17
  10. siibra/core/concept.py +18 -9
  11. siibra/core/parcellation.py +76 -55
  12. siibra/core/region.py +163 -183
  13. siibra/core/space.py +3 -1
  14. siibra/core/structure.py +1 -2
  15. siibra/exceptions.py +17 -1
  16. siibra/experimental/contour.py +6 -6
  17. siibra/experimental/patch.py +2 -2
  18. siibra/experimental/plane3d.py +8 -8
  19. siibra/explorer/__init__.py +1 -1
  20. siibra/explorer/url.py +15 -0
  21. siibra/explorer/util.py +1 -1
  22. siibra/features/__init__.py +1 -1
  23. siibra/features/anchor.py +13 -14
  24. siibra/features/connectivity/__init__.py +1 -1
  25. siibra/features/connectivity/functional_connectivity.py +1 -1
  26. siibra/features/connectivity/regional_connectivity.py +7 -5
  27. siibra/features/connectivity/streamline_counts.py +1 -1
  28. siibra/features/connectivity/streamline_lengths.py +1 -1
  29. siibra/features/connectivity/tracing_connectivity.py +1 -1
  30. siibra/features/dataset/__init__.py +1 -1
  31. siibra/features/dataset/ebrains.py +1 -1
  32. siibra/features/feature.py +50 -28
  33. siibra/features/image/__init__.py +1 -1
  34. siibra/features/image/image.py +18 -13
  35. siibra/features/image/sections.py +1 -1
  36. siibra/features/image/volume_of_interest.py +1 -1
  37. siibra/features/tabular/__init__.py +1 -1
  38. siibra/features/tabular/bigbrain_intensity_profile.py +2 -2
  39. siibra/features/tabular/cell_density_profile.py +102 -66
  40. siibra/features/tabular/cortical_profile.py +5 -3
  41. siibra/features/tabular/gene_expression.py +1 -1
  42. siibra/features/tabular/layerwise_bigbrain_intensities.py +1 -1
  43. siibra/features/tabular/layerwise_cell_density.py +8 -25
  44. siibra/features/tabular/receptor_density_fingerprint.py +5 -3
  45. siibra/features/tabular/receptor_density_profile.py +5 -3
  46. siibra/features/tabular/regional_timeseries_activity.py +7 -5
  47. siibra/features/tabular/tabular.py +5 -3
  48. siibra/livequeries/__init__.py +1 -1
  49. siibra/livequeries/allen.py +46 -20
  50. siibra/livequeries/bigbrain.py +9 -9
  51. siibra/livequeries/ebrains.py +1 -1
  52. siibra/livequeries/query.py +1 -2
  53. siibra/locations/__init__.py +10 -10
  54. siibra/locations/boundingbox.py +77 -38
  55. siibra/locations/location.py +12 -4
  56. siibra/locations/point.py +14 -9
  57. siibra/locations/{pointset.py → pointcloud.py} +69 -27
  58. siibra/retrieval/__init__.py +1 -1
  59. siibra/retrieval/cache.py +1 -1
  60. siibra/retrieval/datasets.py +1 -1
  61. siibra/retrieval/exceptions/__init__.py +1 -1
  62. siibra/retrieval/repositories.py +10 -27
  63. siibra/retrieval/requests.py +20 -3
  64. siibra/vocabularies/__init__.py +1 -1
  65. siibra/volumes/__init__.py +2 -2
  66. siibra/volumes/parcellationmap.py +121 -94
  67. siibra/volumes/providers/__init__.py +1 -1
  68. siibra/volumes/providers/freesurfer.py +1 -1
  69. siibra/volumes/providers/gifti.py +1 -1
  70. siibra/volumes/providers/neuroglancer.py +68 -42
  71. siibra/volumes/providers/nifti.py +18 -28
  72. siibra/volumes/providers/provider.py +2 -2
  73. siibra/volumes/sparsemap.py +128 -247
  74. siibra/volumes/volume.py +252 -65
  75. {siibra-1.0a14.dist-info → siibra-1.0.1a0.dist-info}/METADATA +17 -4
  76. siibra-1.0.1a0.dist-info/RECORD +84 -0
  77. {siibra-1.0a14.dist-info → siibra-1.0.1a0.dist-info}/WHEEL +1 -1
  78. siibra-1.0a14.dist-info/RECORD +0 -84
  79. {siibra-1.0a14.dist-info → siibra-1.0.1a0.dist-info}/LICENSE +0 -0
  80. {siibra-1.0a14.dist-info → siibra-1.0.1a0.dist-info}/top_level.txt +0 -0
siibra/volumes/volume.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2018-2023
1
+ # Copyright 2018-2024
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");
@@ -19,10 +19,11 @@ from .providers import provider as _provider
19
19
  from .. import logger
20
20
  from ..retrieval import requests
21
21
  from ..core import space as _space, structure
22
- from ..locations import location, point, pointset, boundingbox
23
- from ..commons import resample_img_to_img, siibra_tqdm
24
- from ..exceptions import NoMapAvailableError, SpaceWarpingFailedError
22
+ from ..locations import point, pointcloud, boundingbox
23
+ from ..commons import resample_img_to_img, siibra_tqdm, affine_scaling, connected_components
24
+ from ..exceptions import NoMapAvailableError, SpaceWarpingFailedError, EmptyPointCloudError
25
25
 
26
+ from dataclasses import dataclass
26
27
  from nibabel import Nifti1Image
27
28
  import numpy as np
28
29
  from typing import List, Dict, Union, Set, TYPE_CHECKING
@@ -36,7 +37,62 @@ if TYPE_CHECKING:
36
37
  TypeDataset = EbrainsDataset
37
38
 
38
39
 
39
- class Volume(location.Location):
40
+ @dataclass
41
+ class ComponentSpatialProperties:
42
+ """
43
+ Centroid and nonzero volume of an image.
44
+ """
45
+ centroid: point.Point
46
+ volume: int
47
+
48
+ @staticmethod
49
+ def compute_from_image(
50
+ img: Nifti1Image,
51
+ space: Union[str, "_space.Space"],
52
+ split_components: bool = True
53
+
54
+ ) -> List["ComponentSpatialProperties"]:
55
+ """
56
+ Find the center of an image in its (non-zero) voxel space and and its
57
+ volume.
58
+
59
+ Parameters
60
+ ----------
61
+ img: Nifti1Image
62
+ space: str, Space
63
+ split_components: bool, default: True
64
+ If True, finds the spatial properties for each connected component
65
+ found by skimage.measure.label.
66
+ """
67
+ scale = affine_scaling(img.affine)
68
+ if split_components:
69
+ iter_components = lambda img: connected_components(
70
+ np.asanyarray(img.dataobj),
71
+ connectivity=None
72
+ )
73
+ else:
74
+ iter_components = lambda img: [(0, np.asanyarray(img.dataobj))]
75
+
76
+ spatial_props: List[ComponentSpatialProperties] = []
77
+ for _, component in iter_components(img):
78
+ nonzero: np.ndarray = np.c_[np.nonzero(component)]
79
+ spatial_props.append(
80
+ ComponentSpatialProperties(
81
+ centroid=point.Point(
82
+ np.dot(img.affine, np.r_[nonzero.mean(0), 1])[:3],
83
+ space=space
84
+ ),
85
+ volume=nonzero.shape[0] * scale,
86
+ )
87
+ )
88
+
89
+ # sort by volume
90
+ spatial_props.sort(key=lambda cmp: cmp.volume, reverse=True)
91
+
92
+ return spatial_props
93
+
94
+
95
+ class Volume(structure.BrainStructure):
40
96
  """
41
97
  A volume is a specific mesh or 3D array,
42
98
  which can be accessible via multiple providers in different formats.
@@ -77,12 +133,14 @@ class Volume(location.Location):
77
133
  name: str = "",
78
134
  variant: str = None,
79
135
  datasets: List['TypeDataset'] = [],
136
+ bbox: "boundingbox.BoundingBox" = None
80
137
  ):
81
138
  self._name = name
82
139
  self._space_spec = space_spec
83
140
  self.variant = variant
84
141
  self._providers: Dict[str, _provider.VolumeProvider] = {}
85
142
  self.datasets = datasets
143
+ self._boundingbox = bbox
86
144
  for provider in providers:
87
145
  srctype = provider.srctype
88
146
  assert srctype not in self._providers
@@ -142,6 +200,22 @@ class Volume(location.Location):
142
200
  RuntimeError
143
201
  If the volume provider does not have a bounding box calculator.
144
202
  """
203
+ if self._boundingbox is not None and len(fetch_kwargs) == 0:
204
+ return self._boundingbox
205
+
206
+ if not self.provides_image:
207
+ raise NotImplementedError("Bounding box calculation of meshes is not implemented yet.")
208
+
209
+ if clip: # clippin requires fetching the image
210
+ img = self.fetch(**fetch_kwargs)
211
+ assert isinstance(img, Nifti1Image)
212
+ return boundingbox.from_array(
213
+ array=np.asanyarray(img.dataobj),
214
+ background=background,
215
+ ).transform(img.affine, space=self.space)
216
+
217
+ # if clipping is not required, providers migth have methods of creating
218
+ # bounding boxes without fetching the image
145
219
  fmt = fetch_kwargs.get("format")
146
220
  if (fmt is not None) and (fmt not in self.formats):
147
221
  raise ValueError(
@@ -151,15 +225,15 @@ class Volume(location.Location):
151
225
  providers = [self._providers[fmt]] if fmt else self._providers.values()
152
226
  for provider in providers:
153
227
  try:
228
+ assert clip is False
154
229
  bbox = provider.get_boundingbox(
155
- clip=clip, background=background, **fetch_kwargs
230
+ background=background, **fetch_kwargs
156
231
  )
157
- if bbox.space is None: # provider does usually not know the space!
232
+ if bbox.space is None: # provider do not know the space!
158
233
  bbox._space_cached = self.space
159
234
  bbox.minpoint._space_cached = self.space
160
235
  bbox.maxpoint._space_cached = self.space
161
- except NotImplementedError as e:
162
- logger.info(e)
236
+ except NotImplementedError:
163
237
  continue
164
238
  return bbox
165
239
  raise RuntimeError(f"No bounding box specified by any volume provider of {str(self)}")
@@ -214,7 +288,7 @@ class Volume(location.Location):
214
288
 
215
289
  def evaluate_points(
216
290
  self,
217
- points: Union['point.Point', 'pointset.PointSet'],
291
+ points: Union['point.Point', 'pointcloud.PointCloud'],
218
292
  outside_value: Union[int, float] = 0,
219
293
  **fetch_kwargs
220
294
  ) -> np.ndarray:
@@ -233,7 +307,7 @@ class Volume(location.Location):
233
307
 
234
308
  Parameters
235
309
  ----------
236
- points: PointSet
310
+ points: PointCloud
237
311
  outside_value: int, float. Default: 0
238
312
  fetch_kwargs: dict
239
313
  Any additional arguments are passed to the `fetch()` call for
@@ -253,9 +327,8 @@ class Volume(location.Location):
253
327
  raise NotImplementedError("Filtering of points by pure mesh volumes not yet implemented.")
254
328
 
255
329
  # make sure the points are in the same physical space as this volume
256
- warped = (
257
- pointset.from_points([points]) if isinstance(points, point.Point) else points
258
- ).warp(self.space)
330
+ as_pointcloud = pointcloud.from_points([points]) if isinstance(points, point.Point) else points
331
+ warped = as_pointcloud.warp(self.space)
259
332
  assert warped is not None, SpaceWarpingFailedError
260
333
 
261
334
  # get the voxel array of this volume
@@ -282,57 +355,50 @@ class Volume(location.Location):
282
355
 
283
356
  def _points_inside(
284
357
  self,
285
- points: Union['point.Point', 'pointset.PointSet'],
358
+ points: Union['point.Point', 'pointcloud.PointCloud'],
286
359
  keep_labels: bool = True,
287
360
  outside_value: Union[int, float] = 0,
288
361
  **fetch_kwargs
289
- ) -> 'pointset.PointSet':
362
+ ) -> 'pointcloud.PointCloud':
290
363
  """
291
- Reduce a pointset to the points which fall inside nonzero pixels of
364
+ Reduce a pointcloud to the points which fall inside nonzero pixels of
292
365
  this map.
293
366
 
294
367
 
295
368
  Paramaters
296
369
  ----------
297
- points: PointSet
370
+ points: PointCloud
298
371
  keep_labels: bool
299
- If False, the returned PointSet will be labeled with their indices
300
- in the original PointSet.
372
+ If False, the returned PointCloud will be labeled with their indices
373
+ in the original PointCloud.
301
374
  fetch_kwargs: dict
302
375
  Any additional arguments are passed to the `fetch()` call for
303
376
  retrieving the image data.
304
377
 
305
378
  Returns
306
379
  -------
307
- PointSet
308
- A new PointSet containing only the points inside the volume.
380
+ PointCloud
381
+ A new PointCloud containing only the points inside the volume.
309
382
  Labels reflect the indices of the original points if `keep_labels`
310
383
  is False.
311
384
  """
312
- ptset = pointset.from_points([points]) if isinstance(points, point.Point) else points
385
+ ptset = pointcloud.from_points([points]) if isinstance(points, point.Point) else points
313
386
  values = self.evaluate_points(ptset, outside_value=outside_value, **fetch_kwargs)
314
387
  inside = list(np.where(values != outside_value)[0])
315
- return pointset.from_points(
388
+ return pointcloud.from_points(
316
389
  [ptset[i] for i in inside],
317
390
  newlabels=None if keep_labels else inside
318
391
  )
319
392
 
320
- def union(self, other: location.Location):
321
- if isinstance(other, Volume):
322
- return merge([self, other])
323
- else:
324
- raise NotImplementedError(
325
- f"There are no union method for {(self.__class__.__name__, other.__class__.__name__)}"
326
- )
327
-
328
393
  def intersection(self, other: structure.BrainStructure, **fetch_kwargs) -> structure.BrainStructure:
329
394
  """
330
395
  Compute the intersection of a location with this volume. This will
331
396
  fetch actual image data. Any additional arguments are passed to fetch.
332
397
  """
333
- if isinstance(other, (pointset.PointSet, point.Point)):
334
- points_inside = self._points_inside(other, keep_labels=False, **fetch_kwargs)
335
- if len(points_inside) == 0:
398
+ if isinstance(other, (pointcloud.PointCloud, point.Point)):
399
+ try:
400
+ points_inside = self._points_inside(other, keep_labels=False, **fetch_kwargs)
401
+ except EmptyPointCloudError:
336
402
  return None # BrainStructure.intersects checks for not None
337
403
  if isinstance(other, point.Point): # preserve the type
338
404
  return points_inside[0]
@@ -340,6 +406,8 @@ class Volume(location.Location):
340
406
  elif isinstance(other, boundingbox.BoundingBox):
341
407
  return self.get_boundingbox(clip=True, background=0.0, **fetch_kwargs).intersection(other)
342
408
  elif isinstance(other, Volume):
409
+ if self.space != other.space:
410
+ raise NotImplementedError("Cannot intersect volumes from different spaces. Try comparing their boudning boxes.")
343
411
  format = fetch_kwargs.pop('format', 'image')
344
412
  v1 = self.fetch(format=format, **fetch_kwargs)
345
413
  v2 = other.fetch(format=format, **fetch_kwargs)
@@ -361,15 +429,6 @@ class Volume(location.Location):
361
429
  except NoMapAvailableError:
362
430
  return None
363
431
 
364
- def transform(self, affine: np.ndarray, space=None):
365
- raise NotImplementedError("Volume transformation is not yet implemented.")
366
-
367
- def warp(self, space):
368
- if self.space.matches(space):
369
- return self
370
- else:
371
- raise SpaceWarpingFailedError('Warping of full volumes is not yet supported.')
372
-
373
432
  def fetch(
374
433
  self,
375
434
  format: str = None,
@@ -416,6 +475,20 @@ class Volume(location.Location):
416
475
  f"volume are: {self.formats}"
417
476
  )
418
477
 
478
+ # ensure the voi is inside the template
479
+ voi = kwargs.get("voi", None)
480
+ if voi is not None and voi.space is not None:
481
+ assert isinstance(voi, boundingbox.BoundingBox)
482
+ tmplt_bbox = voi.space.get_template().get_boundingbox(clip=False)
483
+ intersection_bbox = voi.intersection(tmplt_bbox)
484
+ if intersection_bbox is None:
485
+ raise RuntimeError(f"voi provided ({voi}) lies out side the voxel space of the {voi.space.name} template.")
486
+ if intersection_bbox != voi:
487
+ logger.info(
488
+ f"Since provided voi lies outside the template ({voi.space}) it is clipped as: {intersection_bbox}"
489
+ )
490
+ kwargs["voi"] = intersection_bbox
491
+
419
492
  result = None
420
493
  # try each possible format
421
494
  for fmt in possible_formats:
@@ -460,19 +533,37 @@ class Volume(location.Location):
460
533
 
461
534
  return self._FETCH_CACHE[fetch_hash]
462
535
 
463
- def fetch_connected_components(self, **kwargs):
536
+ def fetch_connected_components(self, **fetch_kwargs):
464
537
  """
465
- Provide an iterator over masks of connected components in the volume
538
+ Provide an generator over masks of connected components in the volume
466
539
  """
467
- img = self.fetch(**kwargs)
468
- from skimage import measure
469
- imgdata = np.asanyarray(img.dataobj).squeeze()
470
- components = measure.label(imgdata > 0)
471
- component_labels = np.unique(components)
472
- assert component_labels[0] == 0
473
- return (
474
- (label, Nifti1Image((components == label).astype('uint8'), img.affine))
475
- for label in component_labels[1:]
540
+ img = self.fetch(**fetch_kwargs)
541
+ assert isinstance(img, Nifti1Image), NotImplementedError(
542
+ f"Connected components for type {type(img)} is not yet implemeneted."
543
+ )
544
+ for label, component in connected_components(np.asanyarray(img.dataobj)):
545
+ yield (
546
+ label,
547
+ Nifti1Image(component, img.affine)
548
+ )
549
+
550
+ def compute_spatial_props(self, split_components: bool = True, **fetch_kwargs) -> List[ComponentSpatialProperties]:
551
+ """
552
+ Find the center of this volume in its (non-zero) voxel space and and its
553
+ volume.
554
+
555
+ Parameters
556
+ ----------
557
+ split_components: bool, default: True
558
+ If True, finds the spatial properties for each connected component
559
+ found by skimage.measure.label.
560
+ """
561
+ assert self.provides_image, NotImplementedError("Spatial properties can currently on be calculated for images.")
562
+ img = self.fetch(format=fetch_kwargs.pop("format", "image"), **fetch_kwargs)
563
+ return ComponentSpatialProperties.compute_from_image(
564
+ img=img,
565
+ space=self.space,
566
+ split_components=split_components
476
567
  )
477
568
 
478
569
  def draw_samples(self, N: int, sample_size: int = 100, e: float = 1, sigma_mm=None, invert=False, **kwargs):
@@ -502,7 +593,7 @@ class Volume(location.Location):
502
593
  samples.extend(list(pts[inside, :][choice, :]))
503
594
  if len(samples) > N:
504
595
  break
505
- voxels = pointset.PointSet(
596
+ voxels = pointcloud.PointCloud(
506
597
  np.random.permutation(samples)[:N, :],
507
598
  space=None
508
599
  )
@@ -523,11 +614,96 @@ class Volume(location.Location):
523
614
  img = self.fetch(**kwargs)
524
615
  array = np.asanyarray(img.dataobj)
525
616
  voxels = skimage_feature.peak_local_max(array, min_distance=mindist)
526
- points = pointset.PointSet(voxels, space=None, labels=list(range(len(voxels)))).transform(img.affine, space=self.space)
617
+ points = pointcloud.PointCloud(voxels, space=None, labels=list(range(len(voxels)))).transform(img.affine, space=self.space)
527
618
  points.sigma_mm = [sigma_mm for _ in points]
528
619
  return points
529
620
 
530
621
 
622
+ class FilteredVolume(Volume):
623
+
624
+ def __init__(
625
+ self,
626
+ parent_volume: Volume,
627
+ label: int = None,
628
+ fragment: str = None,
629
+ threshold: float = None,
630
+ ):
631
+ """
632
+ A prescribed Volume to fetch specified label and fragment.
633
+ If threshold is defined, a mask of the values above the threshold.
634
+
635
+ Parameters
636
+ ----------
637
+ parent_volume : Volume
638
+ label : int, default: None
639
+ Get the mask of value equal to label.
640
+ fragment : str, default None
641
+ If a volume is fragmented, get a specified one.
642
+ threshold : float, default None
643
+ Provide a float value to threshold the image.
644
+ """
645
+ name = parent_volume.name
646
+ if label:
647
+ name += f" - label: {label}"
648
+ if fragment:
649
+ name += f" - fragment: {fragment}"
650
+ if threshold:
651
+ name += f" - threshold: {threshold}"
652
+ Volume.__init__(
653
+ self,
654
+ space_spec=parent_volume._space_spec,
655
+ providers=list(parent_volume._providers.values()),
656
+ name=name
657
+ )
658
+ self.fragment = fragment
659
+ self.label = label
660
+ self.threshold = threshold
661
+
662
+ def fetch(
663
+ self,
664
+ format: str = None,
665
+ **kwargs
666
+ ):
667
+ if "fragment" in kwargs:
668
+ assert kwargs.get("fragment") == self.fragment, f"This is a filtered volume that can only fetch fragment '{self.fragment}'."
669
+ else:
670
+ kwargs["fragment"] = self.fragment
671
+ if "label" in kwargs:
672
+ assert kwargs.get("label") == self.label, f"This is a filtered volume that can only fetch label '{self.label}' only."
673
+ else:
674
+ kwargs["label"] = self.label
675
+
676
+ result = super().fetch(format=format, **kwargs)
677
+
678
+ if self.threshold is not None:
679
+ assert self.label is None
680
+ if not isinstance(result, Nifti1Image):
681
+ raise NotImplementedError("Cannot threshold meshes.")
682
+ imgdata = np.asanyarray(result.dataobj)
683
+ return Nifti1Image(
684
+ dataobj=(imgdata > self.threshold).astype("uint8"),
685
+ affine=result.affine,
686
+ dtype="uint8"
687
+ )
688
+
689
+ return result
690
+
691
+ def get_boundingbox(
692
+ self,
693
+ clip: bool = True,
694
+ background: float = 0.0,
695
+ **fetch_kwargs
696
+ ) -> "boundingbox.BoundingBox":
697
+ # NOTE: since some providers enable different simpllified ways to create a
698
+ # bounding box without fetching the image, the correct kwargs must be
699
+ # forwarded since FilteredVolumes enforce their specs to be fetched.
700
+ return super().get_boundingbox(
701
+ clip=clip,
702
+ background=background,
703
+ **fetch_kwargs
704
+ )
705
+
706
+
531
707
  class Subvolume(Volume):
532
708
  """
533
709
  Wrapper class for exposing a z level of a 4D volume to be used like a 3D volume.
@@ -540,7 +716,8 @@ class Subvolume(Volume):
540
716
  providers=[
541
717
  _provider.SubvolumeProvider(p, z=z)
542
718
  for p in parent_volume._providers.values()
543
- ]
719
+ ],
720
+ name=parent_volume.name + f" - z: {z}"
544
721
  )
545
722
 
546
723
 
@@ -588,8 +765,8 @@ def from_array(
588
765
  )
589
766
 
590
767
 
591
- def from_pointset(
592
- points: pointset.PointSet,
768
+ def from_pointcloud(
769
+ points: pointcloud.PointCloud,
593
770
  label: int = None,
594
771
  target: Volume = None,
595
772
  normalize=True,
@@ -601,7 +778,7 @@ def from_pointset(
601
778
 
602
779
  Parameters
603
780
  ----------
604
- points: pointset.PointSet
781
+ points: pointcloud.PointCloud
605
782
  label: int, default: None
606
783
  If None, finds the KDE for all points. Otherwise, selects the points
607
784
  labelled with this integer value.
@@ -633,11 +810,11 @@ def from_pointset(
633
810
  )
634
811
  voxelcount_img[tuple(unique_coords.T)] = counts
635
812
 
636
- # TODO: consider how to handle pointsets with varied sigma_mm
813
+ # TODO: consider how to handle pointclouds with varied sigma_mm
637
814
  sigmas = np.array(points.sigma_mm)[selection]
638
815
  bandwidth = np.mean(sigmas)
639
816
  if len(np.unique(sigmas)) > 1:
640
- logger.warning(f"KDE of pointset uses average bandwith {bandwidth} instead of the points' individual sigmas.")
817
+ logger.warning(f"KDE of pointcloud uses average bandwith {bandwidth} instead of the points' individual sigmas.")
641
818
 
642
819
  filtered_arr = filters.gaussian(voxelcount_img, bandwidth)
643
820
  if normalize:
@@ -653,7 +830,7 @@ def from_pointset(
653
830
 
654
831
  def merge(volumes: List[Volume], labels: List[int] = [], **fetch_kwargs) -> Volume:
655
832
  """
656
- Merge a list of volumes in the same space into a single volume.
833
+ Merge a list of nifti volumes in the same space into a single volume.
657
834
 
658
835
  Note
659
836
  ----
@@ -670,6 +847,10 @@ def merge(volumes: List[Volume], labels: List[int] = [], **fetch_kwargs) -> Volu
670
847
  -------
671
848
  Volume
672
849
  """
850
+ if len(volumes) == 1:
851
+ logger.debug("Only one volume supplied returning as is (kwargs are ignored).")
852
+ return volumes[0]
853
+
673
854
  assert len(volumes) > 1, "Need to supply at least two volumes to merge."
674
855
  if labels:
675
856
  assert len(volumes) == len(labels), "Need to supply as many labels as volumes."
@@ -677,8 +858,14 @@ def merge(volumes: List[Volume], labels: List[int] = [], **fetch_kwargs) -> Volu
677
858
  space = volumes[0].space
678
859
  assert all(v.space == space for v in volumes), "Cannot merge volumes from different spaces."
679
860
 
861
+ if len(labels) > 0:
862
+ dtype = 'int32'
863
+ elif FilteredVolume in {type(v) for v in volumes}:
864
+ dtype = 'uint8'
865
+ else:
866
+ dtype = volumes[0].fetch().dataobj.dtype
680
867
  template_img = space.get_template().fetch(**fetch_kwargs)
681
- merged_array = np.zeros(template_img.shape, dtype='uint8')
868
+ merged_array = np.zeros(template_img.shape, dtype=dtype)
682
869
 
683
870
  for i, vol in siibra_tqdm(
684
871
  enumerate(volumes),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siibra
3
- Version: 1.0a14
3
+ Version: 1.0.1a0
4
4
  Summary: siibra - Software interfaces for interacting with brain atlases
5
5
  Home-page: https://github.com/FZJ-INM1-BDA/siibra-python
6
6
  Author: Big Data Analytics Group, Forschungszentrum Juelich, Institute of Neuroscience and Medicine (INM-1)
@@ -24,9 +24,9 @@ Requires-Dist: scikit-image
24
24
  Requires-Dist: requests
25
25
  Requires-Dist: neuroglancer-scripts
26
26
  Requires-Dist: nilearn
27
+ Requires-Dist: typing-extensions; python_version < "3.8"
27
28
  Requires-Dist: filelock
28
- Requires-Dist: ebrains-drive >=0.6.0
29
- Requires-Dist: typing-extensions ; python_version < "3.8"
29
+ Requires-Dist: ebrains-drive>=0.6.0
30
30
 
31
31
  |License| |PyPI version| |doi| |Python versions| |Documentation Status|
32
32
 
@@ -34,7 +34,7 @@ Requires-Dist: typing-extensions ; python_version < "3.8"
34
34
  siibra - Software interface for interacting with brain atlases
35
35
  ==============================================================
36
36
 
37
- Copyright 2020-2023, Forschungszentrum Jülich GmbH
37
+ Copyright 2020-2024, Forschungszentrum Jülich GmbH
38
38
 
39
39
  *Authors: Big Data Analytics Group, Institute of Neuroscience and
40
40
  Medicine (INM-1), Forschungszentrum Jülich GmbH*
@@ -145,3 +145,16 @@ https://doi.org/10.5281/zenodo.7885728`.
145
145
  :target: https://siibra-python.readthedocs.io/en/latest/?badge=latest
146
146
  .. |doi| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.7885728.svg
147
147
  :target: https://doi.org/10.5281/zenodo.7885728
148
+
149
+
150
+ Versioning
151
+ ==========
152
+ Given a version number MAJOR.MINOR.PATCH, increments imply:
153
+ - MAJOR: incompatible API changes
154
+ - MINOR: a functionality in a backward compatible manner is added
155
+ - PATCH: backward compatible bug fixes and new configuration added such as new maps or features
156
+
157
+ Pre-release
158
+ -----------
159
+ For x.y.z, a full release,
160
+ - `x.y.z-alpha.t` is the development prerelease. By changing `t`, different siibra-configurations are targeted.
@@ -0,0 +1,84 @@
1
+ siibra/VERSION,sha256=obkBMWS4uPgy0Q8o6UWN0GqfjWFwM9_DzViNq5cAX98,14
2
+ siibra/__init__.py,sha256=odym0KtWXn55LqBXZDRgXCzSd5daKXrC0D6lSeYi3po,4715
3
+ siibra/commons.py,sha256=L0tEiZycG0GyDo-KxTeEs8abfTZC98VR_xsDRDdWWNQ,27561
4
+ siibra/exceptions.py,sha256=ruU9IGFBqD52mXUryBF41uE6mDV3kabfSAzUunhCWR8,1319
5
+ siibra/configuration/__init__.py,sha256=Kp00hs44rVSU5OPuZ26KFJpd6HXtMocEiLiZihdvav4,752
6
+ siibra/configuration/configuration.py,sha256=um31WGN0Ozl-3v5bUYECkJEJ5GMrEnrJO3_syUXgzFc,7263
7
+ siibra/configuration/factory.py,sha256=8PoPydqc_4csKF8f-E7Un1QnQ0yKDRgYyO1jbVhKeQU,22627
8
+ siibra/core/__init__.py,sha256=907tEZ3HxZSZqqhv0md3Sk8t9iy0-aWtbuo1G1oxN_w,735
9
+ siibra/core/assignment.py,sha256=9g2lLOj_ftuS623ZlVauTTLtJGAvDfE10wknJXJA7m4,3819
10
+ siibra/core/atlas.py,sha256=BNp96YZ3s6f5MrnuEWL8OktFtM528Dj9Zv6e9FbOAyg,8549
11
+ siibra/core/concept.py,sha256=cmWR2PFdWR3pdVa0NSOiPC-EWl0i_WBjyF3JjKt74gc,10889
12
+ siibra/core/parcellation.py,sha256=AF7yODxdhZZymFNzQdaL2qzSDwkgsogPs74e603Zl_Q,14506
13
+ siibra/core/region.py,sha256=6UDx31FmFYtFQJs5Z0fbpQ2yT9MCh4QCCANIa5GxTXQ,43423
14
+ siibra/core/space.py,sha256=0v_7aERZcVNPJmbr_LbeLRkbJj8TGaTrbqnXzr-I5Jg,4586
15
+ siibra/core/structure.py,sha256=eHvCRBZEDjTI8xc2UmSgt26ujgDxHTLai8NoGmeUU64,4497
16
+ siibra/experimental/__init__.py,sha256=s4bEYpMO4Ox-Xlx3FdnRUNKYs0mTHz5Hu4VnfNXpgxU,791
17
+ siibra/experimental/contour.py,sha256=uZOltesITk5t7EbohZtqA8gJRc3UuiqpJdJQhFP1gXo,2454
18
+ siibra/experimental/cortical_profile_sampler.py,sha256=JsX0FCJqvozdhWuayYJ-r3kHNsgSUfA00O9D0X6D7Rg,2086
19
+ siibra/experimental/patch.py,sha256=0VKVU7F7xMBr1hwtL-Z4K-_1XKw88WImPG4fWaiH5bw,3809
20
+ siibra/experimental/plane3d.py,sha256=XgmNemO9peDRDtHE_8joqroGpjCKI5H_vzyIFXDh434,10853
21
+ siibra/explorer/__init__.py,sha256=ZEBJlKs3r5tFWWoKvfrASb1M031FPwSVeysaktUnhbQ,781
22
+ siibra/explorer/url.py,sha256=1UWJyt18gMKTXuixfO8P1Xa5SJDUUagollow67VYT20,7068
23
+ siibra/explorer/util.py,sha256=lAy1pd-ym90H-BZx2X8GMT3qJIsRTgGCz6tW3t1duig,2083
24
+ siibra/features/__init__.py,sha256=NMmDmfrzscV-RHHZMoyjlH_bYfjNhGDX72dQ6-JCYXg,3970
25
+ siibra/features/anchor.py,sha256=FfeMfJxwApFE0iWo2RJj5_XFr0L5gF76V4ZxaPL2VUc,9130
26
+ siibra/features/feature.py,sha256=ls1E7BjRdPOkbtwzU-d01lwTRA_33IlzB44-tL0tg2g,35194
27
+ siibra/features/connectivity/__init__.py,sha256=A7fcXqlAtfFmuymfyOl7A7ecaP8nqIBPFaM76qwh4HA,1161
28
+ siibra/features/connectivity/functional_connectivity.py,sha256=3UiPpHfbYAOBwMn7TI79wy1fR9lWwI3ePMSRVziJONw,2122
29
+ siibra/features/connectivity/regional_connectivity.py,sha256=13uZ-TJBv_J3J-6KuJcKmJMKf7yWy21pzdsyvcWkWXg,18287
30
+ siibra/features/connectivity/streamline_counts.py,sha256=OBOQblhsqnfsvf2kdySRAoWxGsFi4Ulr2PNT9wOu1Ls,1064
31
+ siibra/features/connectivity/streamline_lengths.py,sha256=B9D6Y7szMRCuHRwisAorgTJfkq-1aTNB08H_HVZHpJs,1067
32
+ siibra/features/connectivity/tracing_connectivity.py,sha256=JehDrEEeYl_i-T81AE_AnamlKfzHkZ3nbheRAybFn24,1083
33
+ siibra/features/dataset/__init__.py,sha256=KlHddELER_5IQ-xQ1xx5ExcEtqXWI8gpNvzGlC2on-Y,748
34
+ siibra/features/dataset/ebrains.py,sha256=G3-jWGBMLyJm3X8g8vTVtXN2Kws3JZ7zIEDkO9jwWxc,2544
35
+ siibra/features/image/__init__.py,sha256=HZ5LdR6DzkkkgsftTu314-DAMMLdf6Sfg6ceYVzYjas,1013
36
+ siibra/features/image/image.py,sha256=QtFtXBt4rRkFq6AHeUOaQSCOjgBygIJ7lhz1gjsQ4c4,3585
37
+ siibra/features/image/sections.py,sha256=oQparLp7Nj5dLBvUAs09rgnSgAxH86wN1SMmwkit0S0,961
38
+ siibra/features/image/volume_of_interest.py,sha256=FuYxt-r6YOWN4tLaaZPL3_GAvX_WcZz3jRHxEnWySnQ,2654
39
+ siibra/features/tabular/__init__.py,sha256=sldsgKiwpGOk5PvntIO16lJlWiTy68qVI_pLhepqbdI,1176
40
+ siibra/features/tabular/bigbrain_intensity_profile.py,sha256=noXtsmQjW75epnpfHex0VAbvz7QLwvlkTn_PLSwASIQ,2708
41
+ siibra/features/tabular/cell_density_profile.py,sha256=W1GtrOCacankczxpkz2V3n9zJyf2WCW3W_BpYrEc6yk,10820
42
+ siibra/features/tabular/cortical_profile.py,sha256=I3MFsytAjvi6A2GaMhgXnF9-94LnAScIo_GR-HMEAv0,12540
43
+ siibra/features/tabular/gene_expression.py,sha256=GbXmsdHCz8eFErTLUd6DXhQaeObftrwl9aIEgzlEdXA,9827
44
+ siibra/features/tabular/layerwise_bigbrain_intensities.py,sha256=GIYjX7_Bfs7ywhJBkXd878BZGSiaKUV0BhILr-6D4zc,2117
45
+ siibra/features/tabular/layerwise_cell_density.py,sha256=V8c16KdBACUYFxMYh1T9tdlXs_V67iFAZqlYx7y_4RY,3783
46
+ siibra/features/tabular/receptor_density_fingerprint.py,sha256=GudVpZ2wWF1yGsDFKz4AEU761qA45rItLOwHYpY4Zcs,7299
47
+ siibra/features/tabular/receptor_density_profile.py,sha256=ZYgL-3WxevVFtmEQSbR8Ot24VCMg5xzfXYEPBH1oM6k,3725
48
+ siibra/features/tabular/regional_timeseries_activity.py,sha256=vfoEIu92l3wVdUbdnNqZ5nr9oBuahOrIGS5xhA8cW7s,10026
49
+ siibra/features/tabular/tabular.py,sha256=NoNexdPJ3nhSsztWdWnnIHcX6PakcjEAOV-f9azqa-s,5323
50
+ siibra/livequeries/__init__.py,sha256=yZ2bFYizwF_Z3F9VLNI95IrHcmSO5lqfFML_juifVHs,875
51
+ siibra/livequeries/allen.py,sha256=2jj1gMr9mZFaOfRSf9lPzrcRLAoAMuWjvT_N3_KUkHI,14809
52
+ siibra/livequeries/bigbrain.py,sha256=V7yRQIx6VLIkA9fwW6PfEw7kSkrwp3oE_-WYXe3bjMk,8365
53
+ siibra/livequeries/ebrains.py,sha256=nAIhyPlVeRPNmmMKkGATUSDRoGK7aAg8zOGEKuS-z_w,5820
54
+ siibra/livequeries/query.py,sha256=Yh24vRdcSYBTNx61sMoRCyXyNZ7TX1iqoiJ8fxOJ3V8,1849
55
+ siibra/locations/__init__.py,sha256=q97HYl9mdBWNuQKvgh1Wi0eAz1TlG_dLSIxpyJuHCZo,3342
56
+ siibra/locations/boundingbox.py,sha256=pdLZNyxUD-WvGHNYOvuqZtputCd5tPOxLYTvH6oBBNg,16492
57
+ siibra/locations/location.py,sha256=Taw7oUIFBdXTE8C3b2vbGicPusJlad_KI1wW6iLL324,4361
58
+ siibra/locations/point.py,sha256=wmtH0Isx1-bsk3TfNxXHFcYUmbsQXBt34l8LpFWxhHU,12863
59
+ siibra/locations/pointcloud.py,sha256=61sUvXw5dtKouNKinQNwgGOFd7iwgJLf4D4zYNWo7Js,12474
60
+ siibra/retrieval/__init__.py,sha256=bgoasCN6LFDlKVFCJC0q4kfGMZCsxt_NVCcJ5qy0sIo,1062
61
+ siibra/retrieval/cache.py,sha256=kVB-hYtvQIQmlCozKlYJPaQxgtCKWOS3jB2qqefCVNc,7832
62
+ siibra/retrieval/datasets.py,sha256=Hjai2cSFK1WoMgXp4uMJWBUqlO_APLQ_iDQ__8EpKEI,11031
63
+ siibra/retrieval/repositories.py,sha256=L-JrEuKF6Xq7sEIdJ3iPFQPWaf5CiHMt96X1zi-gOVM,29936
64
+ siibra/retrieval/requests.py,sha256=oup12LGw-MnSc7c3IwlX9Z5TrVXzCCRfGZFLLLR4jAs,23069
65
+ siibra/retrieval/exceptions/__init__.py,sha256=CEY_n-Eh0_EBXYHDPxmIXzO6C_Km0WQFvENAMvRXmYg,875
66
+ siibra/vocabularies/__init__.py,sha256=Qn_xBKrFhla7d_PjDgN6gX4gxroeSRP3ntBZ6gNnvtE,1288
67
+ siibra/vocabularies/gene_names.json,sha256=i-gnh753GyZtQfX_dWibNYr_d5ccDPHooOwsdeKUYqE,1647972
68
+ siibra/vocabularies/receptor_symbols.json,sha256=F6DZIArPCBmJV_lWGV-zDpBBH_GOJOZm67LBE4qzMa4,5722
69
+ siibra/vocabularies/region_aliases.json,sha256=T2w1wRlxPNTsPppXn0bzC70tNsb8mOjLsoHuxDSYm2w,8563
70
+ siibra/volumes/__init__.py,sha256=j7h6WNQk2juIyxs2OlruWj5VcKxsy1g-wD2Q05rM-wM,935
71
+ siibra/volumes/parcellationmap.py,sha256=lgqVjwHdmjmKj_2PguAH4jda3dc-L0p0QzfQ9emPJRg,49774
72
+ siibra/volumes/sparsemap.py,sha256=ZHkHh7NNEPZzRE2-XGWWPRaVVL2V4voYYzYdZ7RCxRg,17437
73
+ siibra/volumes/volume.py,sha256=-ULibtP5MCy0a3tHqbeFweIUP7sLZGuhj6eW558BRlM,32619
74
+ siibra/volumes/providers/__init__.py,sha256=0uDbnsjlAHnxoSdFghzYfhtjVki9721hi7Sp1P6GjNY,934
75
+ siibra/volumes/providers/freesurfer.py,sha256=LxPHbfVJLXkP4WVXCYhsildRUZUvzgv6jJOWeNLavw8,4893
76
+ siibra/volumes/providers/gifti.py,sha256=mPVFkBFnwbCbMks_ysuNbhe0Pj5ndxONPmq-Xr-ir3w,6231
77
+ siibra/volumes/providers/neuroglancer.py,sha256=JH8z6jb7u6nkqtNR3ZFYPrtwexAS6z68pesrLA2lYrk,28712
78
+ siibra/volumes/providers/nifti.py,sha256=AXhWvbt78uEvIT6Z8ABWZ6k0zYUEWZKniSxfLAqDE3k,10135
79
+ siibra/volumes/providers/provider.py,sha256=jxhOGHrSEeBgN0tAi7p8ju3twtUkhkjrk-e-3-chtxI,3695
80
+ siibra-1.0.1a0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
81
+ siibra-1.0.1a0.dist-info/METADATA,sha256=jY1qR1aP2er68Kb1URFJ-bE4i8ZXYj8wlLviXdlkxlI,8884
82
+ siibra-1.0.1a0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
83
+ siibra-1.0.1a0.dist-info/top_level.txt,sha256=NF0OSGLL0li2qyC7MaU0iBB5Y9S09_euPpvisD0-8Hg,7
84
+ siibra-1.0.1a0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.2.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5