Skip to content

Commit 3db1bfe

Browse files
committed
provider.boundingbox -> provider.get_boundingbox()
Solves bounding box of region's possibly resulting to be the whole space
1 parent 3a23783 commit 3db1bfe

File tree

7 files changed

+57
-26
lines changed

7 files changed

+57
-26
lines changed

e2e/core/test_region.py

+17
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,20 @@ def test_related_region_hemisphere():
106106
all_related_reg = [reg for reg in reg.get_related_regions()]
107107
assert any("left" in ass.assigned_structure.name for ass in all_related_reg)
108108
assert any("right" in ass.assigned_structure.name for ass in all_related_reg)
109+
110+
111+
spaces = ['mni152', 'colin27']
112+
113+
114+
@pytest.mark.parametrize("space", spaces)
115+
def test_boundingbox(space):
116+
hoc1_l = siibra.get_region('julich', 'hoc1 left')
117+
hoc1_r = siibra.get_region('julich', 'hoc1 right')
118+
bbox_l = hoc1_l.get_bounding_box(space)
119+
bbox_r = hoc1_r.get_bounding_box(space)
120+
assert bbox_l != bbox_r, "Left and right hoc1 should not have the same bounding boxes"
121+
122+
v_l = hoc1_l.get_regional_map(space)
123+
v_r = hoc1_r.get_regional_map(space)
124+
assert bbox_l == v_l.boundingbox, "Boundingbox of regional mask should be the same as bouding mask of the regions"
125+
assert bbox_r == v_r.boundingbox, "Boundingbox of regional mask should be the same as bouding mask of the regions"

siibra/core/region.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from . import concept, structure, space as _space, parcellation as _parcellation
1818
from .assignment import Qualification, AnatomicalAssignment
1919

20-
from ..locations import location, boundingbox, point, pointset
20+
from ..locations import location, point, pointset
2121
from ..volumes import parcellationmap, volume
2222
from ..commons import (
2323
logger,

siibra/volumes/providers/gifti.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ def __init__(self, url: Union[str, Dict[str, str]], volume=None):
4444
def _url(self) -> Union[str, Dict[str, str]]:
4545
return self._init_url
4646

47-
@property
48-
def boundingbox(self) -> _boundingbox.BoundingBox:
47+
def get_boundingbox(self, clip=False, background=0.0) -> '_boundingbox.BoundingBox':
4948
raise NotImplementedError(
5049
f"Bounding box access to {self.__class__.__name__} objects not yet implemented."
5150
)
@@ -143,10 +142,9 @@ def fetch(self, fragment: str = None, **kwargs):
143142

144143
return {"labels": np.hstack(labels)}
145144

146-
@property
147-
def boundingbox(self) -> _boundingbox.BoundingBox:
145+
def get_boundingbox(self, clip=False, background=0.0) -> '_boundingbox.BoundingBox':
148146
raise NotImplementedError(
149-
f"Bounding boxes of {self.__class__.__name__} objects not defined."
147+
f"Bounding box access to {self.__class__.__name__} objects not yet implemented."
150148
)
151149

152150
@property

siibra/volumes/providers/neuroglancer.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,35 @@ def fetch(
118118

119119
return result
120120

121-
@property
122-
def boundingbox(self):
121+
def get_boundingbox(self, clip=False, background=0) -> "_boundingbox.BoundingBox":
123122
"""
124123
Return the bounding box in physical coordinates
125124
of the union of fragments in this neuroglancer volume.
126125
"""
127126
bbox = None
128127
for frag in self._fragments.values():
129-
next_bbox = _boundingbox.BoundingBox((0, 0, 0), frag.shape, space=None) \
130-
.transform(frag.affine)
128+
if len(frag.shape) > 3:
129+
logger.warning(
130+
f"N-D Neuroglancer volume has shape {frag.shape}, but "
131+
f"bounding box considers only {frag.shape[:3]}"
132+
)
133+
if clip:
134+
img = frag.fetch()
135+
bounds = _boundingbox.BoundingBox._determine_bounds(np.asanyarray(img.dataobj), background)
136+
next_bbox = _boundingbox.BoundingBox(
137+
bounds[:3, 0], bounds[:3, 1], space=None
138+
)
139+
else:
140+
shape = frag.shape[:3]
141+
next_bbox = _boundingbox.BoundingBox(
142+
(0, 0, 0), shape, space=None
143+
).transform(frag.affine)
131144
bbox = next_bbox if bbox is None else bbox.union(next_bbox)
132145
return bbox
133146

134147
def _merge_fragments(self) -> nib.Nifti1Image:
135148
# TODO this only performs nearest neighbor interpolation, optimized for float types.
136-
bbox = self.boundingbox
149+
bbox = self.get_boundingbox(clip=False, background=0.0)
137150
num_conflicts = 0
138151
result = None
139152

@@ -486,10 +499,9 @@ def __init__(self, resource: Union[str, dict], volume=None):
486499
def _url(self) -> Union[str, Dict[str, str]]:
487500
return self._init_url
488501

489-
@property
490-
def boundingbox(self) -> _boundingbox.BoundingBox:
502+
def get_boundingbox(self, clip=False, background=0.0) -> '_boundingbox.BoundingBox':
491503
raise NotImplementedError(
492-
f"Fast bounding box access to {self.__class__.__name__} objects not yet implemented."
504+
f"Bounding box access to {self.__class__.__name__} objects not yet implemented."
493505
)
494506

495507
def _get_fragment_info(self, meshindex: int) -> Dict[str, Tuple[str, ]]:

siibra/volumes/providers/nifti.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ def _url(self) -> Union[str, Dict[str, str]]:
6565
def fragments(self):
6666
return [k for k in self._img_loaders if k is not None]
6767

68-
@property
69-
def boundingbox(self):
68+
def get_boundingbox(self, clip=True, background=0) -> "_boundingbox.BoundingBox":
7069
"""
7170
Return the bounding box in physical coordinates
7271
of the union of fragments in this nifti volume.
@@ -79,15 +78,22 @@ def boundingbox(self):
7978
f"N-D NIfTI volume has shape {img.shape}, but "
8079
f"bounding box considers only {img.shape[:3]}"
8180
)
82-
shape = img.shape[:3]
83-
next_bbox = _boundingbox.BoundingBox((0, 0, 0), shape, space=None) \
84-
.transform(img.affine)
81+
if clip:
82+
bounds = _boundingbox.BoundingBox._determine_bounds(np.asanyarray(img.dataobj), background)
83+
next_bbox = _boundingbox.BoundingBox(
84+
bounds[:3, 0], bounds[:3, 1], space=None
85+
)
86+
else:
87+
shape = img.shape[:3]
88+
next_bbox = _boundingbox.BoundingBox(
89+
(0, 0, 0), shape, space=None
90+
).transform(img.affine)
8591
bbox = next_bbox if bbox is None else bbox.union(next_bbox)
8692
return bbox
8793

8894
def _merge_fragments(self) -> nib.Nifti1Image:
8995
# TODO this only performs nearest neighbor interpolation, optimized for float types.
90-
bbox = self.boundingbox
96+
bbox = self.get_boundingbox(clip=True, background=0.0)
9197
num_conflicts = 0
9298
result = None
9399

siibra/volumes/providers/provider.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,8 @@ def __init_subclass__(cls, srctype: str) -> None:
3535
VolumeProvider._SUBCLASSES.append(cls)
3636
return super().__init_subclass__()
3737

38-
@property
3938
@abstractmethod
40-
def boundingbox(self) -> "BoundingBox":
39+
def get_boundingbox(self, clip=True, background=0.0) -> "BoundingBox":
4140
raise NotImplementedError
4241

4342
@property
@@ -84,9 +83,8 @@ def __init__(self, parent_provider: VolumeProvider, z: int):
8483
self.srctype = parent_provider.srctype
8584
self.z = z
8685

87-
@property
88-
def boundingbox(self) -> "BoundingBox":
89-
return self.provider.boundingbox
86+
def get_boundingbox(self, clip=True, background=0.0) -> "BoundingBox":
87+
return self.provider.get_boundingbox(clip=clip, background=background)
9088

9189
def fetch(self, **kwargs):
9290
# activate caching at the caller using "with SubvolumeProvider.UseCaching():""

siibra/volumes/volume.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def concat(url: Union[str, Dict[str, str]], concat: str):
119119
def boundingbox(self):
120120
for provider in self._providers.values():
121121
try:
122-
bbox = provider.boundingbox
122+
bbox = provider.get_boundingbox()
123123
if bbox.space is None: # provider does usually not know the space!
124124
bbox._space_cached = self.space
125125
bbox.minpoint._space_cached = self.space

0 commit comments

Comments
 (0)