Skip to content

Commit 2cab27e

Browse files
committed
Correct handling of slices that extend beyond cloudvolume boundaries
- Only grabs areas that exist - Regions beyond the bounds of the volume are set to 0.
1 parent b89eef0 commit 2cab27e

File tree

2 files changed

+25
-8
lines changed

2 files changed

+25
-8
lines changed

python/ouroboros/helpers/volume_cache.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
from dataclasses import astuple
12
import os
23
import sys
34
import traceback
45

5-
from cloudvolume import CloudVolume, VolumeCutout
6+
from cloudvolume import CloudVolume, VolumeCutout, Bbox
67
import numpy as np
78

89
from .bounding_boxes import BoundingBox, boxes_dim_range
@@ -158,16 +159,26 @@ def remove_volume(self, volume_index: int, destroy_shared: bool = False):
158159
def download_volume(
159160
self, volume_index: int, bounding_box: BoundingBox, parallel=False
160161
) -> VolumeCutout:
161-
bbox = bounding_box.to_cloudvolume_bbox()
162+
bbox = bounding_box.to_cloudvolume_bbox().astype(int)
163+
vol_shape = NGOrder(*bbox.size3(), self.cv.cv.num_channels)
164+
165+
# Limit size of area we are grabbing, in case we go out of bounds.
166+
dl_box = Bbox.intersection(self.cv.cv.bounds, bbox)
167+
local_min = [int(start) for start in np.subtract(dl_box.minpt, bbox.minpt)]
168+
169+
local_bounds = np.s_[*[slice(start, stop) for start, stop in
170+
zip(local_min, np.sum([local_min, dl_box.size3()], axis=0))],
171+
:]
162172

163173
# Download the bounding box volume
164174
if self.use_shared:
165-
vol_shape = NGOrder(*bbox.astype(int).size3(), self.cv.cv.num_channels)
166175
volume = self.shm_host.SharedNPArray(vol_shape, np.float32)
167176
with volume as volume_data:
168-
volume_data[:] = self.cv.cv.download(bbox, mip=self.mip, parallel=parallel)
177+
volume_data[:] = 0 # Prob not most efficient but makes math much easier
178+
volume_data[local_bounds] = self.cv.cv.download(dl_box, mip=self.mip, parallel=parallel)
169179
else:
170-
volume = self.cv.cv.download(bbox, mip=self.mip, parallel=parallel)
180+
volume = np.zeros(astuple(vol_shape))
181+
volume[local_bounds] = self.cv.cv.download(dl_box, mip=self.mip, parallel=parallel)
171182

172183
# Store the volume in the cache
173184
self.volumes[volume_index] = volume

python/test/helpers/test_volume_cache.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import pytest
22
from unittest.mock import MagicMock, patch
3+
4+
from cloudvolume import Bbox
5+
import numpy as np
6+
37
from ouroboros.helpers.volume_cache import (
48
VolumeCache,
59
CloudVolumeInterface,
@@ -19,6 +23,8 @@ def mock_cloud_volume():
1923
mock_cv.shape = (100, 100, 100, 3)
2024
mock_cv.cache.flush = MagicMock()
2125
mock_cv.mip_volume_size = lambda mip: (100, 100, 100)
26+
mock_cv.bounds = Bbox((0, 0, 0), (100, 100, 100))
27+
mock_cv.download.return_value = np.zeros((0, 10, 0, 1))
2228
yield mock_cv
2329

2430

@@ -173,8 +179,8 @@ def test_request_volume_for_slice(volume_cache):
173179
volume_data, bounding_box = volume_cache.request_volume_for_slice(slice_index)
174180

175181
mock_volume_index.assert_called_once_with(slice_index)
176-
assert bounding_box == volume_cache.bounding_boxes[1]
177-
assert volume_data == volume_cache.volumes[1]
182+
assert np.all(bounding_box == volume_cache.bounding_boxes[1])
183+
assert np.all(volume_data == volume_cache.volumes[1])
178184

179185

180186
def test_create_processing_data(volume_cache):
@@ -234,7 +240,7 @@ def test_volume_cache_remove_volume(volume_cache):
234240
volume_data, bounding_box = volume_cache.request_volume_for_slice(slice_index)
235241

236242
mock_volume_index.assert_called_once_with(slice_index)
237-
assert volume_data == volume_cache.volumes[1]
243+
assert np.all(volume_data == volume_cache.volumes[1])
238244
volume_cache.remove_volume(1)
239245
assert volume_cache.volumes[1] is None
240246

0 commit comments

Comments
 (0)