siibra 1.0a19__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 (38) hide show
  1. siibra/VERSION +1 -1
  2. siibra/__init__.py +3 -3
  3. siibra/commons.py +0 -46
  4. siibra/configuration/factory.py +10 -20
  5. siibra/core/atlas.py +20 -14
  6. siibra/core/parcellation.py +67 -52
  7. siibra/core/region.py +133 -123
  8. siibra/exceptions.py +8 -0
  9. siibra/experimental/contour.py +6 -6
  10. siibra/experimental/patch.py +2 -2
  11. siibra/experimental/plane3d.py +8 -8
  12. siibra/features/anchor.py +12 -13
  13. siibra/features/connectivity/regional_connectivity.py +2 -2
  14. siibra/features/feature.py +14 -16
  15. siibra/features/tabular/bigbrain_intensity_profile.py +1 -1
  16. siibra/features/tabular/cell_density_profile.py +97 -63
  17. siibra/features/tabular/layerwise_cell_density.py +3 -22
  18. siibra/features/tabular/regional_timeseries_activity.py +2 -2
  19. siibra/livequeries/allen.py +39 -16
  20. siibra/livequeries/bigbrain.py +8 -8
  21. siibra/livequeries/query.py +0 -1
  22. siibra/locations/__init__.py +9 -9
  23. siibra/locations/boundingbox.py +29 -24
  24. siibra/locations/point.py +4 -4
  25. siibra/locations/{pointset.py → pointcloud.py} +30 -22
  26. siibra/retrieval/repositories.py +9 -26
  27. siibra/retrieval/requests.py +19 -2
  28. siibra/volumes/__init__.py +1 -1
  29. siibra/volumes/parcellationmap.py +88 -81
  30. siibra/volumes/providers/neuroglancer.py +62 -36
  31. siibra/volumes/providers/nifti.py +11 -25
  32. siibra/volumes/sparsemap.py +124 -245
  33. siibra/volumes/volume.py +141 -52
  34. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/METADATA +16 -3
  35. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/RECORD +38 -38
  36. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/WHEEL +1 -1
  37. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/LICENSE +0 -0
  38. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/top_level.txt +0 -0
@@ -32,8 +32,7 @@ from ..commons import (
32
32
  generate_uuid
33
33
  )
34
34
  from ..core import concept, space, parcellation, region as _region
35
- from ..locations import location, point, pointset
36
- from ..retrieval import requests
35
+ from ..locations import location, point, pointcloud
37
36
 
38
37
  import numpy as np
39
38
  from typing import Union, Dict, List, TYPE_CHECKING, Iterable, Tuple
@@ -160,6 +159,11 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
160
159
  logger.warning(f"Non unique indices encountered in {self}: {duplicates}")
161
160
  self._affine_cached = None
162
161
 
162
+ @property
163
+ def key(self):
164
+ _id = self.id
165
+ return create_key(_id[len("siibra-map-v0.0.1"):])
166
+
163
167
  @property
164
168
  def species(self) -> Species:
165
169
  # lazy implementation
@@ -319,69 +323,27 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
319
323
  def regions(self):
320
324
  return list(self._indices)
321
325
 
322
- def fetch(
326
+ def get_volume(
323
327
  self,
324
- region_or_index: Union[str, "Region", MapIndex] = None,
328
+ region: Union[str, "Region"] = None,
325
329
  *,
326
330
  index: MapIndex = None,
327
- region: Union[str, "Region"] = None,
328
- **kwargs
329
- ):
330
- """
331
- Fetches one particular volume of this parcellation map.
332
-
333
- If there's only one volume, this is the default, otherwise further
334
- specification is requested:
335
- - the volume index,
336
- - the MapIndex (which results in a regional map being returned)
337
-
338
- You might also consider fetch_iter() to iterate the volumes, or
339
- compress() to produce a single-volume parcellation map.
340
-
341
- Parameters
342
- ----------
343
- region_or_index: str, Region, MapIndex
344
- Lazy match the specification.
345
- index: MapIndex
346
- Explicit specification of the map index, typically resulting
347
- in a regional map (mask or statistical map) to be returned.
348
- Note that supplying 'region' will result in retrieving the map index of that region
349
- automatically.
350
- region: str, Region
351
- Specification of a region name, resulting in a regional map
352
- (mask or statistical map) to be returned.
353
- **kwargs
354
- - resolution_mm: resolution in millimeters
355
- - format: the format of the volume, like "mesh" or "nii"
356
- - voi: a BoundingBox of interest
357
-
358
-
359
- Note
360
- ----
361
- Not all keyword arguments are supported for volume formats. Format
362
- is restricted by available formats (check formats property).
363
-
364
- Returns
365
- -------
366
- An image or mesh
367
- """
331
+ **kwargs,
332
+ ) -> Union[_volume.Volume, _volume.FilteredVolume, _volume.Subvolume]:
368
333
  try:
369
- length = len([arg for arg in [region_or_index, region, index] if arg is not None])
334
+ length = len([arg for arg in [region, index] if arg is not None])
370
335
  assert length == 1
371
336
  except AssertionError:
372
337
  if length > 1:
373
- raise exceptions.ExcessiveArgumentException("One and only one of region_or_index, region, index can be defined for fetch")
374
- # user can provide no arguments, which assumes one and only one volume present
375
-
376
- if isinstance(region_or_index, MapIndex):
377
- index = region_or_index
378
-
379
- if isinstance(region_or_index, (str, _region.Region)):
380
- region = region_or_index
381
-
338
+ raise exceptions.ExcessiveArgumentException(
339
+ "One and only one of region or index can be defined for `get_volume`."
340
+ )
382
341
  mapindex = None
383
342
  if region is not None:
384
- assert isinstance(region, (str, _region.Region))
343
+ try:
344
+ assert isinstance(region, (str, _region.Region))
345
+ except AssertionError:
346
+ raise TypeError(f"Please provide a region name or region instance, not a {type(region)}")
385
347
  mapindex = self.get_index(region)
386
348
  if index is not None:
387
349
  assert isinstance(index, MapIndex)
@@ -390,19 +352,17 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
390
352
  if len(self) == 1:
391
353
  mapindex = MapIndex(volume=0, label=None)
392
354
  elif len(self) > 1:
355
+ assert self.maptype == MapType.LABELLED, f"Cannot merge multiple volumes of map type {self.maptype}. Please specify a region or index."
393
356
  logger.info(
394
357
  "Map provides multiple volumes and no specification is"
395
358
  " provided. Resampling all volumes to the space."
396
359
  )
397
360
  labels = list(range(len(self.volumes)))
398
361
  merged_volume = _volume.merge(self.volumes, labels, **kwargs)
399
- return merged_volume.fetch()
362
+ return merged_volume
400
363
  else:
401
364
  raise exceptions.NoVolumeFound("Map provides no volumes.")
402
365
 
403
- if "resolution_mm" in kwargs and kwargs.get("format") is None:
404
- kwargs["format"] = 'neuroglancer/precomputed'
405
-
406
366
  kwargs_fragment = kwargs.pop("fragment", None)
407
367
  if kwargs_fragment is not None:
408
368
  if (mapindex.fragment is not None) and (kwargs_fragment != mapindex.fragment):
@@ -418,17 +378,60 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
418
378
  raise IndexError(
419
379
  f"{self} provides {len(self)} mapped volumes, but #{mapindex.volume} was requested."
420
380
  )
421
- try:
422
- result = self.volumes[mapindex.volume or 0].fetch(
423
- fragment=mapindex.fragment, label=mapindex.label, **kwargs
424
- )
425
- except requests.SiibraHttpRequestError as e:
426
- print(str(e))
381
+ if mapindex.label is None and mapindex.fragment is None:
382
+ return self.volumes[mapindex.volume]
383
+
384
+ return _volume.FilteredVolume(
385
+ parent_volume=self.volumes[mapindex.volume],
386
+ label=mapindex.label,
387
+ fragment=mapindex.fragment,
388
+ )
389
+
390
+ def fetch(
391
+ self,
392
+ region: Union[str, "Region"] = None,
393
+ *,
394
+ index: MapIndex = None,
395
+ **fetch_kwargs
396
+ ):
397
+ """
398
+ Fetches one particular volume of this parcellation map.
399
+
400
+ If there's only one volume, this is the default, otherwise further
401
+ specification is requested:
402
+ - the volume index,
403
+ - the MapIndex (which results in a regional map being returned)
404
+
405
+ You might also consider fetch_iter() to iterate the volumes, or
406
+ compress() to produce a single-volume parcellation map.
407
+
408
+ Parameters
409
+ ----------
410
+ region: str, Region
411
+ Specification of a region name, resulting in a regional map
412
+ (mask or statistical map) to be returned.
413
+ index: MapIndex
414
+ Explicit specification of the map index, typically resulting
415
+ in a regional map (mask or statistical map) to be returned.
416
+ Note that supplying 'region' will result in retrieving the map index of that region
417
+ automatically.
418
+ **fetch_kwargs
419
+ - resolution_mm: resolution in millimeters
420
+ - format: the format of the volume, like "mesh" or "nii"
421
+ - voi: a BoundingBox of interest
427
422
 
428
- if result is None:
429
- raise RuntimeError(f"Error fetching {mapindex} from {self} as {kwargs.get('format', f'{self.formats}')}.")
430
423
 
431
- return result
424
+ Note
425
+ ----
426
+ Not all keyword arguments are supported for volume formats. Format
427
+ is restricted by available formats (check formats property).
428
+
429
+ Returns
430
+ -------
431
+ An image or mesh
432
+ """
433
+ vol = self.get_volume(region=region, index=index, **fetch_kwargs)
434
+ return vol.fetch(**fetch_kwargs)
432
435
 
433
436
  def fetch_iter(self, **kwargs):
434
437
  """
@@ -531,7 +534,7 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
531
534
  disable=(len(self.fragments) == 1 or self.fragments is None)
532
535
  ):
533
536
  mapindex = MapIndex(volume=volidx, fragment=frag)
534
- img = self.fetch(mapindex)
537
+ img = self.fetch(index=mapindex)
535
538
  if np.allclose(img.affine, result_affine):
536
539
  img_data = np.asanyarray(img.dataobj)
537
540
  else:
@@ -572,11 +575,11 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
572
575
  )]
573
576
  )
574
577
 
575
- def compute_centroids(self, split_components: bool = True) -> Dict[str, pointset.PointSet]:
578
+ def compute_centroids(self, split_components: bool = True, **fetch_kwargs) -> Dict[str, pointcloud.PointCloud]:
576
579
  """
577
580
  Compute a dictionary of all regions in this map to their centroids.
578
581
  By default, the regional masks will be split to connected components
579
- and each point in the PointSet corresponds to a region component.
582
+ and each point in the PointCloud corresponds to a region component.
580
583
 
581
584
  Parameters
582
585
  ----------
@@ -589,6 +592,7 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
589
592
  Dict[str, point.Point]
590
593
  Region names as keys and computed centroids as items.
591
594
  """
595
+ assert self.provides_image, "Centroid computation for meshes is not supported yet."
592
596
  centroids = dict()
593
597
  for regionname, indexlist in siibra_tqdm(
594
598
  self._indices.items(), unit="regions", desc="Computing centroids"
@@ -600,7 +604,7 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
600
604
  merged_volume = _volume.merge(
601
605
  [
602
606
  _volume.from_nifti(
603
- self.fetch(index=index),
607
+ self.fetch(index=index, **fetch_kwargs),
604
608
  self.space,
605
609
  f"{self.name} - {index}"
606
610
  )
@@ -611,13 +615,16 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
611
615
  mapimg = merged_volume.fetch()
612
616
  elif len(indexlist) == 1:
613
617
  index = indexlist[0]
614
- mapimg = self.fetch(index=index) # returns a mask of the region
618
+ mapimg = self.fetch(index=index, **fetch_kwargs) # returns a mask of the region
615
619
  props = _volume.ComponentSpatialProperties.compute_from_image(
616
620
  img=mapimg,
617
621
  space=self.space,
618
622
  split_components=split_components,
619
623
  )
620
- centroids[regionname] = pointset.from_points([c.centroid for c in props])
624
+ try:
625
+ centroids[regionname] = pointcloud.from_points([c.centroid for c in props])
626
+ except exceptions.EmptyPointCloudError:
627
+ centroids[regionname] = None
621
628
  return centroids
622
629
 
623
630
  def get_resampled_template(self, **fetch_kwargs) -> _volume.Volume:
@@ -742,7 +749,7 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
742
749
 
743
750
  Returns
744
751
  -------
745
- PointSet
752
+ PointCloud
746
753
  Sample points in physcial coordinates corresponding to this
747
754
  parcellationmap
748
755
  """
@@ -760,7 +767,7 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
760
767
  np.unravel_index(np.random.choice(len(p), numpoints, p=p), W.shape)
761
768
  ).T
762
769
  XYZ = np.dot(mask.affine, np.c_[XYZ_, np.ones(numpoints)].T)[:3, :].T
763
- return pointset.PointSet(XYZ, space=self.space)
770
+ return pointcloud.PointCloud(XYZ, space=self.space)
764
771
 
765
772
  def to_sparse(self):
766
773
  """
@@ -831,10 +838,10 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
831
838
 
832
839
  if isinstance(item, point.Point):
833
840
  return self._assign_points(
834
- pointset.PointSet([item], item.space, sigma_mm=item.sigma),
841
+ pointcloud.PointCloud([item], item.space, sigma_mm=item.sigma),
835
842
  lower_threshold
836
843
  )
837
- if isinstance(item, pointset.PointSet):
844
+ if isinstance(item, pointcloud.PointCloud):
838
845
  return self._assign_points(item, lower_threshold)
839
846
  if isinstance(item, _volume.Volume):
840
847
  return self._assign_volume(
@@ -989,9 +996,9 @@ class Map(concept.AtlasConcept, configuration_folder="maps"):
989
996
  .dropna(axis='columns', how='all')
990
997
  )
991
998
 
992
- def _assign_points(self, points: pointset.PointSet, lower_threshold: float) -> List[MapAssignment]:
999
+ def _assign_points(self, points: pointcloud.PointCloud, lower_threshold: float) -> List[MapAssignment]:
993
1000
  """
994
- assign a PointSet to this parcellation map.
1001
+ assign a PointCloud to this parcellation map.
995
1002
 
996
1003
  Parameters
997
1004
  -----------
@@ -37,6 +37,37 @@ from typing import Union, Dict, Tuple
37
37
  import json
38
38
 
39
39
 
40
+ def shift_ng_transfrom(
41
+ transform_nm: np.ndarray, scale_resolution_nm: np.ndarray, max_resolution_nm: np.ndarray
42
+ ) -> np.ndarray:
43
+ """
44
+ Helper method to get nifti standard affine.
45
+
46
+ transfrorm.json stored with neuroglancer precomputed images and meshes
47
+ are meant to be used for neuroglancer viewers and hence they are not
48
+ representative of the affine in other tools. This method shifts back
49
+ half a voxel in each axis.
50
+ (see https://neuroglancer-scripts.readthedocs.io/en/latest/neuroglancer-info.html#different-conventions-for-coordinate-transformations)
51
+
52
+ Parameters
53
+ ----------
54
+ transform_nm: np.ndarray
55
+ Transform array created for dispalying an image correctly from
56
+ neuroglancer precomputed format in neuroglancer viewer.
57
+ max_resolution_nm: np.ndarray
58
+ The voxel resolution of the highest level of resolution.
59
+
60
+ Returns
61
+ -------
62
+ np.ndarray
63
+ Standard affine in nm
64
+ """
65
+ scaling = np.diag(np.r_[scale_resolution_nm, 1.0])
66
+ affine = np.dot(transform_nm, scaling)
67
+ affine[:3, 3] += (max_resolution_nm * 0.5)
68
+ return affine
69
+
70
+
40
71
  class NeuroglancerProvider(_provider.VolumeProvider, srctype="neuroglancer/precomputed"):
41
72
 
42
73
  def __init__(self, url: Union[str, Dict[str, str]]):
@@ -128,26 +159,20 @@ class NeuroglancerProvider(_provider.VolumeProvider, srctype="neuroglancer/preco
128
159
  label = None
129
160
  if label is not None:
130
161
  result = nib.Nifti1Image(
131
- (result.get_fdata() == label).astype('uint8'),
132
- result.affine
162
+ (np.asanyarray(result.dataobj) == label).astype('uint8'),
163
+ result.affine,
164
+ dtype='uint8',
133
165
  )
134
166
 
135
167
  return result
136
168
 
137
- def get_boundingbox(self, clip=False, background=0, **fetch_kwargs) -> "_boundingbox.BoundingBox":
169
+ def get_boundingbox(self, **fetch_kwargs) -> "_boundingbox.BoundingBox":
138
170
  """
139
171
  Return the bounding box in physical coordinates of the union of
140
172
  fragments in this neuroglancer volume.
141
173
 
142
174
  Parameters
143
175
  ----------
144
- clip: bool, default: True
145
- Whether to clip the background of the volume.
146
- background: float, default: 0.0
147
- The background value to clip.
148
- Note
149
- ----
150
- To use it, clip must be True.
151
176
  fetch_kwargs:
152
177
  key word arguments that are used for fetchin volumes,
153
178
  such as voi or resolution_mm.
@@ -159,23 +184,17 @@ class NeuroglancerProvider(_provider.VolumeProvider, srctype="neuroglancer/preco
159
184
  f"N-D Neuroglancer volume has shape {frag.shape}, but "
160
185
  f"bounding box considers only {frag.shape[:3]}"
161
186
  )
162
- if clip:
163
- img = frag.fetch(**fetch_kwargs)
164
- next_bbox = _boundingbox.from_array(
165
- np.asanyarray(img.dataobj), threshold=background, space=None
166
- ).transform(img.affine) # use the affine of the image matching fetch_kwargs
187
+ resolution_mm = fetch_kwargs.get("resolution_mm")
188
+ if resolution_mm is None:
189
+ affine = frag.affine
190
+ shape = frag.shape[:3]
167
191
  else:
168
- resolution_mm = fetch_kwargs.get("resolution_mm")
169
- if resolution_mm is None:
170
- affine = frag.affine
171
- shape = frag.shape[:3]
172
- else:
173
- scale = frag._select_scale(resolution_mm=resolution_mm)
174
- affine = scale.affine
175
- shape = scale.size[:3]
176
- next_bbox = _boundingbox.BoundingBox(
177
- (0, 0, 0), shape, space=None
178
- ).transform(affine)
192
+ scale = frag._select_scale(resolution_mm=resolution_mm)
193
+ affine = scale.affine
194
+ shape = scale.size[:3]
195
+ next_bbox = _boundingbox.BoundingBox(
196
+ (0, 0, 0), shape, space=None
197
+ ).transform(affine)
179
198
  bbox = next_bbox if bbox is None else bbox.union(next_bbox)
180
199
  return bbox
181
200
 
@@ -246,7 +265,11 @@ class NeuroglancerVolume:
246
265
  self._io: PrecomputedIO = None
247
266
 
248
267
  @property
249
- def transform_nm(self):
268
+ def transform_nm(self) -> np.ndarray:
269
+ """
270
+ This is the transformation matrix created to cater neuroglancer viewer
271
+ for a neuroglancer precomputed images.
272
+ """
250
273
  if self._transform_nm is not None:
251
274
  return self._transform_nm
252
275
  try:
@@ -326,7 +349,7 @@ class NeuroglancerVolume:
326
349
  ):
327
350
  # the caller has to make sure voi is defined in the correct reference space
328
351
  scale = self._select_scale(resolution_mm=resolution_mm, bbox=voi, max_bytes=max_bytes)
329
- return scale.fetch(voi=voi)
352
+ return scale.fetch(voi=voi, **kwargs)
330
353
 
331
354
  def get_shape(self, resolution_mm=None, max_bytes: float = MAX_BYTES):
332
355
  scale = self._select_scale(resolution_mm=resolution_mm, max_bytes=max_bytes)
@@ -445,10 +468,13 @@ class NeuroglancerScale:
445
468
 
446
469
  @property
447
470
  def affine(self):
448
- scaling = np.diag(np.r_[self.res_nm, 1.0])
449
- affine = np.dot(self.volume.transform_nm, scaling)
450
- affine[:3, :] /= 1e6
451
- return affine
471
+ affine_ = shift_ng_transfrom(
472
+ transform_nm=self.volume.transform_nm,
473
+ scale_resolution_nm=self.res_nm,
474
+ max_resolution_nm=self.volume.scales[0].res_nm[0],
475
+ )
476
+ affine_[:3, :] /= 1e6
477
+ return affine_
452
478
 
453
479
  def _point_to_lower_chunk_idx(self, xyz):
454
480
  return (
@@ -508,9 +534,9 @@ class NeuroglancerScale:
508
534
  for dim in range(3):
509
535
  if bbox_.shape[dim] < 1:
510
536
  logger.warning(
511
- f"Bounding box in voxel space will be enlarged to voxel size 1 along axis {dim}."
537
+ f"Bounding box in voxel space will be enlarged to by {self.res_mm[dim]} along axis {dim}."
512
538
  )
513
- bbox_.maxpoint[dim] = bbox_.maxpoint[dim] + 1
539
+ bbox_.maxpoint[dim] = bbox_.maxpoint[dim] + self.res_mm[dim]
514
540
 
515
541
  # extract minimum and maximum the chunk indices to be loaded
516
542
  gx0, gy0, gz0 = self._point_to_lower_chunk_idx(tuple(bbox_.minpoint))
@@ -533,7 +559,7 @@ class NeuroglancerScale:
533
559
  # exact bounding box requested, to cut off undesired borders
534
560
  data_min = np.array([gx0, gy0, gz0]) * self.chunk_sizes
535
561
  x0, y0, z0 = (np.array(bbox_.minpoint) - data_min).astype("int")
536
- xd, yd, zd = np.ceil((np.array(bbox_.maxpoint))).astype(int) - np.floor((np.array(bbox_.minpoint))).astype(int) # TODO: consider 0.5 voxel shift
562
+ xd, yd, zd = np.ceil((np.array(bbox_.maxpoint))).astype(int) - np.floor((np.array(bbox_.minpoint))).astype(int)
537
563
  offset = tuple(bbox_.minpoint)
538
564
 
539
565
  # build the nifti image
@@ -552,7 +578,7 @@ class NeuroglancerMesh(_provider.VolumeProvider, srctype="neuroglancer/precompme
552
578
 
553
579
  @staticmethod
554
580
  def _fragmentinfo(url: str) -> Dict[str, Union[str, np.ndarray, Dict]]:
555
- """ Prepare basic mesh fragment information from url. """
581
+ """Prepare basic mesh fragment information from url."""
556
582
  return {
557
583
  "url": url,
558
584
  "transform_nm": np.array(requests.HttpRequest(f"{url}/transform.json").data),
@@ -17,7 +17,7 @@ from . import provider as _provider
17
17
 
18
18
  from ...commons import logger, resample_img_to_img
19
19
  from ...retrieval import requests
20
- from ...locations import pointset, boundingbox as _boundingbox
20
+ from ...locations import pointcloud, boundingbox as _boundingbox
21
21
 
22
22
  from typing import Union, Dict, Tuple
23
23
  import nibabel as nib
@@ -65,28 +65,16 @@ class NiftiProvider(_provider.VolumeProvider, srctype="nii"):
65
65
  def fragments(self):
66
66
  return [k for k in self._img_loaders if k is not None]
67
67
 
68
- def get_boundingbox(self, clip=True, background=0., **fetch_kwargs) -> "_boundingbox.BoundingBox":
68
+ def get_boundingbox(self, **fetch_kwargs) -> "_boundingbox.BoundingBox":
69
69
  """
70
70
  Return the bounding box in physical coordinates of the union of
71
71
  fragments in this nifti volume.
72
72
 
73
73
  Parameters
74
74
  ----------
75
- clip : bool, default: True
76
- Whether to clip the background of the volume.
77
- background : float, default: 0.0
78
- The background value to clip.
79
- Note
80
- ----
81
- To use it, clip must be True.
82
75
  fetch_kwargs:
83
76
  Not used
84
77
  """
85
- if fetch_kwargs:
86
- logger.warning(
87
- "`volume.fetch()` keyword arguments supplied. Nifti volumes"
88
- " cannot pass them for bounding box calculation."
89
- )
90
78
  bbox = None
91
79
  for loader in self._img_loaders.values():
92
80
  img = loader()
@@ -95,19 +83,17 @@ class NiftiProvider(_provider.VolumeProvider, srctype="nii"):
95
83
  f"N-D NIfTI volume has shape {img.shape}, but "
96
84
  f"bounding box considers only {img.shape[:3]}"
97
85
  )
98
- if clip:
99
- next_bbox = _boundingbox.from_array(
100
- np.asanyarray(img.dataobj), threshold=background, space=None
101
- ).transform(img.affine)
102
- else:
103
- shape = img.shape[:3]
104
- next_bbox = _boundingbox.BoundingBox(
105
- (0, 0, 0), shape, space=None
106
- ).transform(img.affine)
86
+ shape = img.shape[:3]
87
+ next_bbox = _boundingbox.BoundingBox(
88
+ (0, 0, 0), shape, space=None
89
+ ).transform(img.affine)
107
90
  bbox = next_bbox if bbox is None else bbox.union(next_bbox)
108
91
  return bbox
109
92
 
110
93
  def _merge_fragments(self) -> nib.Nifti1Image:
94
+ """
95
+ Merge all fragments this volume contains into one Nifti1Image.
96
+ """
111
97
  bbox = self.get_boundingbox(clip=False, background=0.0)
112
98
  num_conflicts = 0
113
99
  result = None
@@ -243,7 +229,7 @@ class NiftiProvider(_provider.VolumeProvider, srctype="nii"):
243
229
 
244
230
  Returns:
245
231
  --------
246
- PointSet
232
+ PointCloud
247
233
  """
248
234
 
249
235
  from skimage.feature.peak import peak_local_max
@@ -257,7 +243,7 @@ class NiftiProvider(_provider.VolumeProvider, srctype="nii"):
257
243
  min_distance=dist,
258
244
  )
259
245
  return (
260
- pointset.PointSet(
246
+ pointcloud.PointCloud(
261
247
  [np.dot(img.affine, [x, y, z, 1])[:3] for x, y, z in voxels],
262
248
  space=self.space,
263
249
  ),