Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ def sample_jp2(remote_sample: Callable) -> Path:
return remote_sample("jp2-omnyx-small")


@pytest.fixture(scope="session")
def sample_dicom(remote_sample: Callable) -> Path:
"""Sample pytest fixture for dicom images.

Download dicom image for pytest.

"""
return remote_sample("dicom-1")


@pytest.fixture(scope="session")
def sample_all_wsis(
sample_ndpi: Path,
Expand Down
19 changes: 19 additions & 0 deletions tests/test_wsireader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2979,3 +2979,22 @@ def test_fsspec_reader_open_pass_empty_json(tmp_path: Path) -> None:
json_path.write_text("{}")

assert not FsspecJsonWSIReader.is_valid_zarr_fsspec(str(json_path))


def test_oob_read_dicom(sample_dicom: Path) -> None:
"""Test that out of bounds returns background value.

For consistency with openslide, our readers should return a
background tile when reading out of bounds.

"""
wsi = DICOMWSIReader(sample_dicom)
# Read a region that is out of bounds
region = wsi.read_rect(
location=(200000, 200),
size=(100, 100),
)
# Check that the region is the same size as the requested size
assert region.shape == (100, 100, 3)
# Check that the region is white (255)
assert np.all(region == 255)
22 changes: 20 additions & 2 deletions tiatoolbox/visualization/bokeh_app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,16 @@ def populate_slide_list(slide_folder: Path, search_txt: str | None = None) -> No
"""Populate the slide list with the available slides."""
file_list = []
len_slidepath = len(slide_folder.parts)
for ext in ["*.svs", "*ndpi", "*.tiff", "*.mrxs", "*.jpg", "*.png", "*.tif"]:
for ext in [
"*.svs",
"*ndpi",
"*.tiff",
"*.mrxs",
"*.jpg",
"*.png",
"*.tif",
"*.dcm",
]:
file_list.extend(list(Path(slide_folder).glob(str(Path("*") / ext))))
file_list.extend(list(Path(slide_folder).glob(ext)))
if search_txt is None:
Expand Down Expand Up @@ -2086,7 +2095,16 @@ def setup_doc(self: DocConfig, base_doc: Document) -> tuple[Row, Tabs]:

# Set initial slide to first one in base folder
slide_list = []
for ext in ["*.svs", "*ndpi", "*.tiff", "*.tif", "*.mrxs", "*.png", "*.jpg"]:
for ext in [
"*.svs",
"*ndpi",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be ".ndpi" - is the dot missing?

"*.tiff",
"*.tif",
"*.mrxs",
"*.png",
"*.jpg",
"*.dcm",
]:
slide_list.extend(list(doc_config["slide_folder"].glob(ext)))
slide_list.extend(
list(doc_config["slide_folder"].glob(str(Path("*") / ext))),
Expand Down
17 changes: 14 additions & 3 deletions tiatoolbox/wsicore/wsireader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4595,6 +4595,7 @@ def _info(self: DICOMWSIReader) -> WSIMeta:
mpp=mpp,
level_count=len(level_dimensions),
vendor=dataset.Manufacturer,
file_path=self.input_path,
)

def read_rect(
Expand Down Expand Up @@ -4826,8 +4827,19 @@ def read_rect(
_, constrained_read_size = utils.transforms.bounds2locsize(
constrained_read_bounds,
)

# if out of bounds, return empty image consistent with openslide
if np.any(np.array(constrained_read_size) <= 0):
Copy link

Copilot AI May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider updating the docstring for read_rect to mention that out-of-bounds reads return a white background tile, ensuring consistency with OpenSlide behavior.

Copilot uses AI. Check for mistakes.
return (
np.ones(
shape=(int(size[1]), int(size[0]), 3),
dtype=np.uint8,
)
* 255
)

dicom_level = wsi.levels[read_level].level
im_region = wsi.read_region(location, dicom_level, constrained_read_size)
im_region = wsi.read_region(level_location, dicom_level, constrained_read_size)
im_region = np.array(im_region)

# Apply padding outside the slide area
Expand Down Expand Up @@ -5003,7 +5015,6 @@ class docstrings for more information.
wsi = self.wsi

# Read at optimal level and corrected read size
location_at_baseline = bounds_at_baseline[:2]
level_location, size_at_read_level = utils.transforms.bounds2locsize(
bounds_at_read_level,
)
Expand All @@ -5016,7 +5027,7 @@ class docstrings for more information.
_, read_size = utils.transforms.bounds2locsize(read_bounds)
dicom_level = wsi.levels[read_level].level
im_region = wsi.read_region(
location=location_at_baseline,
location=level_location,
level=dicom_level,
size=read_size,
)
Expand Down