-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: add function to handle out of bounds coordinates for a RegionBox…
… in an image (#221) This works the same way as `_adjust_negative_coordinates`, but requires the image as an addition input and modifies the RegionBox object directly. A message is logged in the debugger if a dimension is adjusted. I also updated the `crop_image` function to call this before applying the crop.
- Loading branch information
Showing
1 changed file
with
47 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,200 +1,139 @@ | ||
import pytest | ||
import SimpleITK as sitk | ||
from rich import print as rprint | ||
|
||
from imgtools.coretypes import BoxPadMethod, Coordinate3D, RegionBox, Size3D | ||
import SimpleITK as sitk | ||
from imgtools.coretypes import RegionBox, Coordinate3D, Size3D, BoxPadMethod | ||
from imgtools.coretypes.box import BoundingBoxOutsideImageError | ||
from imgtools.datasets import example_data | ||
|
||
|
||
# Simple tests | ||
def test_regionbox_initialization() -> None: | ||
def test_regionbox_initialization(): | ||
min_coord = Coordinate3D(0, 0, 0) | ||
max_coord = Coordinate3D(10, 10, 10) | ||
box = RegionBox(min_coord, max_coord) | ||
assert box.min == min_coord | ||
assert box.max == max_coord | ||
assert box.size == Size3D(10, 10, 10) | ||
|
||
|
||
def test_regionbox_invalid_initialization() -> None: | ||
def test_regionbox_invalid_initialization(): | ||
min_coord = Coordinate3D(10, 10, 10) | ||
max_coord = Coordinate3D(0, 0, 0) | ||
with pytest.raises(ValueError): | ||
RegionBox(min_coord, max_coord) | ||
|
||
|
||
def test_regionbox_from_tuple() -> None: | ||
def test_regionbox_from_tuple(): | ||
box = RegionBox.from_tuple((0, 0, 0), (10, 10, 10)) | ||
assert box.min == Coordinate3D(0, 0, 0) | ||
assert box.max == Coordinate3D(10, 10, 10) | ||
assert box.size == Size3D(10, 10, 10) | ||
|
||
|
||
def test_regionbox_pad_symmetric() -> None: | ||
def test_regionbox_pad_symmetric(): | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(10, 10, 10)) | ||
padded_box = box.pad(5) | ||
assert padded_box.min == Coordinate3D(0, 0, 0) | ||
assert padded_box.max == Coordinate3D(15, 15, 15) | ||
assert padded_box.size == Size3D(15, 15, 15) | ||
|
||
|
||
def test_regionbox_pad_end() -> None: | ||
def test_regionbox_pad_end(): | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(10, 10, 10)) | ||
padded_box = box.pad(5, method=BoxPadMethod.END) | ||
assert padded_box.min == Coordinate3D(5, 5, 5) | ||
assert padded_box.max == Coordinate3D(15, 15, 15) | ||
assert padded_box.size == Size3D(10, 10, 10) | ||
|
||
|
||
def test_regionbox_expand_to_cube() -> None: | ||
def test_regionbox_expand_to_cube(): | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(10, 10, 10)) | ||
cube_box = box.expand_to_cube(15) | ||
assert cube_box.size == Size3D(15, 15, 15) | ||
|
||
|
||
def test_regionbox_expand_to_min_size() -> None: | ||
def test_regionbox_expand_to_min_size(): | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(15, 15, 15)) | ||
min_size_box = box.expand_to_min_size(20) | ||
assert min_size_box.size == Size3D(20, 20, 20) | ||
|
||
|
||
def test_regionbox_crop_image() -> None: | ||
def test_regionbox_crop_image(): | ||
image = sitk.Image(100, 100, 100, sitk.sitkInt16) | ||
box = RegionBox(Coordinate3D(10, 10, 10), Coordinate3D(20, 20, 20)) | ||
cropped_image = box.crop_image(image) | ||
assert cropped_image.GetSize() == (10, 10, 10) | ||
|
||
|
||
def test_regionbox_crop_image_outside() -> None: | ||
def test_regionbox_crop_image_outside(): | ||
image = sitk.Image(100, 100, 100, sitk.sitkInt16) | ||
box = RegionBox(Coordinate3D(90, 90, 90), Coordinate3D(110, 110, 110)) | ||
cropped = box.crop_image(image) | ||
|
||
assert cropped.GetSize() == (20, 20, 20) | ||
|
||
|
||
def test_regionbox_crop_image_and_mask() -> None: | ||
def test_regionbox_crop_image_and_mask(): | ||
image = sitk.Image(100, 100, 100, sitk.sitkInt16) | ||
mask = sitk.Image(100, 100, 100, sitk.sitkUInt8) | ||
box = RegionBox(Coordinate3D(10, 10, 10), Coordinate3D(20, 20, 20)) | ||
cropped_image, cropped_mask = box.crop_image_and_mask(image, mask) | ||
assert cropped_image.GetSize() == (10, 10, 10) | ||
assert cropped_mask.GetSize() == (10, 10, 10) | ||
|
||
|
||
# Edge cases | ||
|
||
def test_regionbox_from_centroid_odd_cube(): | ||
|
||
def test_regionbox_from_centroid_odd_cube() -> None: | ||
mask = example_data()["mask"] | ||
mask = example_data()['mask'] | ||
bbox = RegionBox.from_mask_centroid(mask, 1, 5) | ||
expected = RegionBox(Coordinate3D(47, 26, 73), Coordinate3D(52, 31, 78)) | ||
expected = RegionBox( | ||
Coordinate3D(47, 26, 73), | ||
Coordinate3D(52, 31, 78) | ||
) | ||
|
||
assert bbox.min == expected.min | ||
assert bbox.max == expected.max | ||
assert bbox.size == expected.size | ||
|
||
|
||
def test_bbox_cubed() -> None: | ||
region = RegionBox(Coordinate3D(12, 13, 14), Coordinate3D(21, 23, 25)) | ||
def test_bbox_cubed()->None: | ||
mask = example_data()['mask'] | ||
region = RegionBox( | ||
Coordinate3D(12, 13, 14), | ||
Coordinate3D(21, 23, 25) | ||
) | ||
|
||
expanded_region = region.expand_to_cube(13) | ||
|
||
expected = RegionBox(Coordinate3D(10, 11, 13), Coordinate3D(23, 24, 26)) | ||
expected = RegionBox( | ||
Coordinate3D(10, 11, 13), | ||
Coordinate3D(23, 24, 26) | ||
) | ||
|
||
assert expanded_region.min == expected.min | ||
assert expanded_region.max == expected.max | ||
assert expanded_region.size == expected.size | ||
|
||
|
||
def test_adjust_max() -> None: | ||
region = RegionBox(Coordinate3D(12, 13, 14), Coordinate3D(21, 23, 25)) | ||
def test_adjust_max()->None: | ||
|
||
# save a copy to compare size after | ||
region = RegionBox( | ||
Coordinate3D(12, 13, 14), | ||
Coordinate3D(21, 23, 25) | ||
) | ||
|
||
#save a copy to compare size after | ||
region_copy = region.copy() | ||
|
||
image = sitk.Image(20, 20, 20, sitk.sitkInt16) | ||
|
||
region.check_out_of_bounds_coordinates(image=image) | ||
region.check_out_of_bounds_coordinates( | ||
image=image | ||
) | ||
|
||
assert region.size == region_copy.size | ||
|
||
|
||
expected_min = Coordinate3D(11, 10, 9) | ||
expected_max = Coordinate3D(20, 20, 20) | ||
|
||
assert region.min == expected_min | ||
assert region.max == expected_max | ||
|
||
|
||
def test_expand_cube_smaller_size() -> None: | ||
region = RegionBox(Coordinate3D(12, 13, 14), Coordinate3D(200, 23, 25)) | ||
with pytest.raises(ValueError): | ||
_ = region.expand_to_cube(10) | ||
|
||
|
||
def test_regionbox_from_mask_bbox() -> None: | ||
mask = example_data()["mask"] | ||
bbox = RegionBox.from_mask_bbox(mask) | ||
expected = RegionBox(Coordinate3D(45, 21, 67), Coordinate3D(55, 38, 84)) | ||
|
||
assert bbox.min == expected.min | ||
assert bbox.max == expected.max | ||
assert bbox.size == expected.size | ||
|
||
|
||
def test_regionbox_expand_to_min_size_no_change() -> None: | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(15, 15, 15)) | ||
min_size_box = box.expand_to_min_size(5) | ||
assert min_size_box.size == Size3D(10, 10, 10) | ||
|
||
|
||
def test_regionbox_expand_to_cube_no_change() -> None: | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(15, 15, 15)) | ||
cube_box = box.expand_to_cube(10) | ||
assert cube_box.size == Size3D(10, 10, 10) | ||
|
||
|
||
def test_regionbox_pad_no_change() -> None: | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(10, 10, 10)) | ||
padded_box = box.pad(0) | ||
assert padded_box.min == Coordinate3D(5, 5, 5) | ||
assert padded_box.max == Coordinate3D(10, 10, 10) | ||
assert padded_box.size == Size3D(5, 5, 5) | ||
|
||
|
||
def test_regionbox_repr() -> None: | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(10, 10, 10)) | ||
repr_str = repr(box) | ||
expected_str = ( | ||
"RegionBox(\n" | ||
"\tmin=Coordinate3D(x=5, y=5, z=5),\n" | ||
"\tmax=Coordinate3D(x=10, y=10, z=10)\n" | ||
"\tsize=Size3D(w=5, h=5, d=5)\n" | ||
")" | ||
def test_expand_cube_smaller_size()->None: | ||
|
||
region = RegionBox( | ||
Coordinate3D(12, 13, 14), | ||
Coordinate3D(200, 23, 25) | ||
) | ||
assert repr_str == expected_str | ||
|
||
|
||
def test_regionbox_copy() -> None: | ||
box = RegionBox(Coordinate3D(5, 5, 5), Coordinate3D(10, 10, 10)) | ||
box_copy = box.copy() | ||
assert box_copy.min == box.min | ||
assert box_copy.max == box.max | ||
assert box_copy.size == box.size | ||
|
||
|
||
def test_regionbox_adjust_negative_coordinates() -> None: | ||
min_coord = Coordinate3D(-5, -5, -5) | ||
max_coord = Coordinate3D(10, 10, 10) | ||
RegionBox._adjust_negative_coordinates(min_coord, max_coord) | ||
assert min_coord == Coordinate3D(0, 0, 0) | ||
assert max_coord == Coordinate3D(15, 15, 15) | ||
|
||
|
||
def test_regionbox_check_out_of_bounds_coordinates() -> None: | ||
image = sitk.Image(20, 20, 20, sitk.sitkInt16) | ||
region = RegionBox(Coordinate3D(12, 13, 14), Coordinate3D(21, 23, 25)) | ||
region.check_out_of_bounds_coordinates(image) | ||
assert region.min == Coordinate3D(11, 10, 9) | ||
assert region.max == Coordinate3D(20, 20, 20) | ||
with pytest.raises(ValueError): | ||
expanded_region = region.expand_to_cube(10) |