Skip to content

Commit

Permalink
BoundingBox.decompose fix (plus some other minor changes) (#299)
Browse files Browse the repository at this point in the history
* add .idea to gitignore

* fix BoundingBox.decompose bug

* fix (commented) bug in precomputed volume_path correction

* minor style and typing fixes

* add BoundingBox.decompose unit test, and additional minor improvements to BBox and Cartesian tests

* fix spelling of PhysicalBoudingBox -> PhysicalBoundingBox

---------

Co-authored-by: Erik Schomburg <[email protected]>
  • Loading branch information
eschombu and eschombu authored Feb 24, 2024
1 parent 97ed0b0 commit c183ccb
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ docs/source/_build/
*.DS_Store
*.log
chunkflow/plugins/chunkflow-plugins
.idea/
6 changes: 3 additions & 3 deletions chunkflow/chunk/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from scipy.ndimage import gaussian_filter

from cloudvolume.lib import yellow, Bbox
from chunkflow.lib.cartesian_coordinate import BoundingBox, Cartesian, PhysicalBoudingBox
from chunkflow.lib.cartesian_coordinate import BoundingBox, Cartesian, PhysicalBoundingBox

# from typing import Tuple
# Offset = Tuple[int, int, int]
Expand Down Expand Up @@ -568,8 +568,8 @@ def bounding_box(self) -> BoundingBox:
return self.bbox

@property
def physical_bounding_box(self) -> PhysicalBoudingBox:
return PhysicalBoudingBox(
def physical_bounding_box(self) -> PhysicalBoundingBox:
return PhysicalBoundingBox(
self.start, self.stop, self.voxel_size)

@property
Expand Down
2 changes: 1 addition & 1 deletion chunkflow/flow/save_precomputed.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self,
self.mip = mip

# if not volume_path.startswith('precomputed://'):
# volume_path += 'precomputed://'
# volume_path = 'precomputed://' + volume_path
self.volume_path = volume_path

# gevent.monkey.patch_all(thread=False)
Expand Down
26 changes: 13 additions & 13 deletions chunkflow/lib/cartesian_coordinate.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@

BOUNDING_BOX_RE = re.compile(r'(-?\d+)-(-?\d+)_(-?\d+)-(-?\d+)_(-?\d+)-(-?\d+)(?:\.gz|\.br|\.h5|\.json|\.npy|\.tif|\.csv|\.pkl|\.png|\.jpg)?$')

def to_cartesian(x: Union[tuple, list]):

def to_cartesian(x: Union[tuple, list, None]):
if x is None:
return None
else:
assert len(x) == 3
return Cartesian.from_collection(x)


class Cartesian(namedtuple('Cartesian', ['z', 'y', 'x'])):
"""Cartesian coordinate or offset."""
__slots__ = ()
Expand Down Expand Up @@ -186,7 +188,7 @@ def inverse(self):


@dataclass(frozen=True)
class BoundingBox():
class BoundingBox:
start: Cartesian
stop: Cartesian
# def __post_init__(self, start, stop) -> BoundingBox:
Expand Down Expand Up @@ -361,7 +363,7 @@ def __floordiv__(self, other: Number | Cartesian | BoundingBox) -> BoundingBox:
minpt = self.minpt // other.minpt
maxpt = self.maxpt // other.maxpt
elif isinstance(other, np.ndarray):
other = Cartesian.from_collection(other)
other = Cartesian.from_collection(other)
minpt = self.start // other
maxpt = self.stop // other
else:
Expand All @@ -387,7 +389,6 @@ def __iadd__(self, other: Cartesian | Number):
stop = self.stop + other
return BoundingBox(start, stop)


def clone(self):
return BoundingBox(self.start, self.stop)

Expand Down Expand Up @@ -468,9 +469,9 @@ def decompose(self, block_size: Cartesian,

bboxes = BoundingBoxes()

for z in range(self.start.z, self.stop.z-block_size.z, block_size.z):
for y in range(self.start.y, self.stop.y-block_size.y, block_size.y):
for x in range(self.start.x, self.stop.x-block_size.x, block_size.x):
for z in range(self.start.z, self.stop.z-block_size.z+1, block_size.z):
for y in range(self.start.y, self.stop.y-block_size.y+1, block_size.y):
for x in range(self.start.x, self.stop.x-block_size.x+1, block_size.x):
bbox = BoundingBox.from_delta(Cartesian(z,y,x), block_size)
bboxes.append(bbox)
return bboxes
Expand All @@ -489,7 +490,7 @@ def shape(self):

@cached_property
def left_neighbors(self):
sz = self.size3()
sz = self.shape

minpt = deepcopy(self.minpt)
minpt[0] -= sz[0]
Expand Down Expand Up @@ -695,20 +696,20 @@ def __len__(self):


@dataclass(frozen=True)
class PhysicalBoudingBox(BoundingBox):
class PhysicalBoundingBox(BoundingBox):
voxel_size: Cartesian

@classmethod
def from_bounding_box(cls, bbox: BoundingBox,
voxel_size: Cartesian) -> PhysicalBoudingBox:
voxel_size: Cartesian) -> PhysicalBoundingBox:
return cls(bbox.start, bbox.stop,
voxel_size)

@cached_property
def voxel_bounding_box(self) -> BoundingBox:
return BoundingBox(self.start, self.stop)

def to_other_voxel_size(self, voxel_size2: Cartesian) -> PhysicalBoudingBox:
def to_other_voxel_size(self, voxel_size2: Cartesian) -> PhysicalBoundingBox:
assert voxel_size2 != self.voxel_size

if voxel_size2 >= self.voxel_size:
Expand All @@ -720,5 +721,4 @@ def to_other_voxel_size(self, voxel_size2: Cartesian) -> PhysicalBoudingBox:
factors = self.voxel_size // voxel_size2
start = self.start * factors
stop = self.stop * factors
return PhysicalBoudingBox(start, stop, voxel_size2)

return PhysicalBoundingBox(start, stop, voxel_size2)
8 changes: 4 additions & 4 deletions chunkflow/volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from cloudvolume import CloudVolume
from chunkflow.lib.utils import str_to_dict
from .lib.cartesian_coordinate import \
BoundingBox, Cartesian, BoundingBoxes, PhysicalBoudingBox
BoundingBox, Cartesian, BoundingBoxes, PhysicalBoundingBox
from .chunk import Chunk


Expand Down Expand Up @@ -111,8 +111,8 @@ def block_size(self):
self.vol.chunk_size[::-1])

@cached_property
def physical_bounding_box(self) -> PhysicalBoudingBox:
return PhysicalBoudingBox(
def physical_bounding_box(self) -> PhysicalBoundingBox:
return PhysicalBoundingBox(
self.start, self.stop, self.voxel_size)

@cached_property
Expand Down Expand Up @@ -296,7 +296,7 @@ def get_candidate_block_bounding_boxes_with_different_voxel_size(
voxel_size_low
)
for bbox_low in pbbox_low.decompose(block_size_low):
pbbox_low = PhysicalBoudingBox(
pbbox_low = PhysicalBoundingBox(
bbox_low.start, bbox_low.stop, voxel_size_low)
pbbox_high = pbbox_low.to_other_voxel_size(chunk.voxel_size)
chunk_high = block_high.cutout(pbbox_high)
Expand Down
24 changes: 18 additions & 6 deletions tests/lib/test_cartesian_coordinate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

from cloudvolume.lib import Bbox, Vec

from chunkflow.lib.cartesian_coordinate import BoundingBox, Cartesian, to_cartesian, BoundingBoxes, PhysicalBoudingBox
from chunkflow.lib.cartesian_coordinate import BoundingBox, Cartesian, to_cartesian, BoundingBoxes, PhysicalBoundingBox


def test_cartesian():
assert to_cartesian(None) == None
assert to_cartesian(None) is None
ct = (1,2,3)
assert to_cartesian(ct) == Cartesian(1,2,3)

Expand Down Expand Up @@ -47,12 +47,13 @@ def test_cartesian():
assert Cartesian(1,2,3).tuple == (1,2,3)
assert Cartesian(1,2,3).vec is not None


def test_bounding_box():
bbox = BoundingBox.from_string('3166-3766_7531-8131_2440-3040')
bbox == BoundingBox(Cartesian(3166, 7531, 2440), Cartesian(3766, 8131, 3040))
assert bbox == BoundingBox(Cartesian(3166, 7531, 2440), Cartesian(3766, 8131, 3040))

bbox = BoundingBox.from_string('Sp1,3166-3766_7531-8131_2440-3040.h5')
bbox == BoundingBox(Cartesian(3166, 7531, 2440), Cartesian(3766, 8131, 3040))
assert bbox == BoundingBox(Cartesian(3166, 7531, 2440), Cartesian(3766, 8131, 3040))

bbox = Bbox.from_delta((1,3,2), (64, 32, 8))
bbox = BoundingBox.from_bbox(bbox)
Expand All @@ -65,6 +66,9 @@ def test_bounding_box():
minpt = Cartesian(1,2,3)
maxpt = Cartesian(2,3,4)
bbox = BoundingBox(minpt, maxpt)
assert bbox.start == minpt
assert bbox.stop == maxpt
assert bbox.shape == Cartesian(1,1,1)

bbox = BoundingBox.from_center(Cartesian(1,2,3), 3)
assert bbox == BoundingBox.from_list([-2, -1, 0, 4, 5, 6])
Expand All @@ -77,6 +81,14 @@ def test_bounding_box():
assert bbox1.union(bbox2) == BoundingBox.from_list([0,1,2, 3,4,5])
assert bbox1.intersection(bbox2) == BoundingBox.from_list([1,2,3, 2,3,4])

minpt = Cartesian(1,2,3)
maxpt = Cartesian(3,4,5)
bbox = BoundingBox(minpt, maxpt)
bbox_decomp = bbox.decompose(bbox.shape // 2)
assert len(bbox_decomp) == 8
assert bbox_decomp[0].start == minpt
assert bbox_decomp[-1].stop == maxpt


def test_bounding_boxes():
fname = os.path.join(os.path.dirname(__file__), 'sp3_bboxes.txt')
Expand All @@ -90,7 +102,7 @@ def test_physical_bounding_box():
start = Cartesian(0, 1, 2)
stop = Cartesian(2, 3, 4)
voxel_size = Cartesian(2, 2, 2)
pbbox = PhysicalBoudingBox(start, stop, voxel_size)
pbbox = PhysicalBoundingBox(start, stop, voxel_size)

pbbox2 = pbbox.to_other_voxel_size(Cartesian(1,1,1))
assert pbbox2.start == Cartesian(0,2,4)
assert pbbox2.start == Cartesian(0,2,4)

0 comments on commit c183ccb

Please sign in to comment.