Skip to content

Commit ecece0d

Browse files
fix: standardize TiffFile output to uint8 and correct CuCIM typing
Signed-off-by: Nikolas Schmitz <nikolas.schmitz@rwth-aachen.de>
1 parent 8988563 commit ecece0d

2 files changed

Lines changed: 27 additions & 23 deletions

File tree

monai/data/wsi_reader.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def set_dtype(self, dtype):
127127
self.dtype = np.dtype(dtype)
128128

129129
def set_device(self, device):
130-
if device is None or isinstance(device, (torch.device, str)):
130+
if device is None or isinstance(device, torch.device | str):
131131
self.device = device
132132
else:
133133
raise ValueError(f"`device` must be `torch.device`, `str` or `None` but {type(device)} is given.")
@@ -358,7 +358,7 @@ def get_data(
358358
metadata_list: list = []
359359

360360
# CuImage object is iterable, so ensure_tuple won't work on single object
361-
if not isinstance(wsi, (list, tuple)):
361+
if not isinstance(wsi, list | tuple):
362362
wsi = (wsi,)
363363
for each_wsi in ensure_tuple(wsi):
364364
# get the valid level based on resolution info
@@ -835,7 +835,9 @@ def get_mpp(self, wsi, level: int) -> tuple[float, float]:
835835

836836
raise ValueError("`mpp` cannot be obtained for this file. Please use `level` instead.")
837837

838-
def get_wsi_at_mpp(self, wsi, mpp: float | tuple[float, float], atol: float = 0.00, rtol: float = 0.05) -> Any:
838+
def get_wsi_at_mpp(
839+
self, wsi, mpp: float | tuple[float, float], atol: float = 0.00, rtol: float = 0.05
840+
) -> np.ndarray:
839841
"""
840842
Returns the representation of the whole slide image at a given micro-per-pixel (mpp) resolution.
841843
The optional tolerance parameters are considered at the level whose mpp value is closest to the one provided by the user.
@@ -852,7 +854,7 @@ def get_wsi_at_mpp(self, wsi, mpp: float | tuple[float, float], atol: float = 0.
852854
rtol: the acceptable relative tolerance for resolution in micro per pixel.
853855
854856
Returns:
855-
Cupy array containing the whole slide image at the requested MPP resolution.
857+
Numpy array containing the whole slide image at the requested MPP resolution.
856858
857859
"""
858860
cp, _ = optional_import("cupy")
@@ -1423,7 +1425,8 @@ def get_wsi_at_mpp(
14231425
if within_tolerance:
14241426
# If the image at the desired mpp resolution is within tolerances, return the image at closest_level.
14251427
# TiffFile does not expose `read_region`; read the whole page instead (consistent with `_get_patch`).
1426-
closest_lvl_wsi = wsi.pages[closest_lvl].asarray()
1428+
pil_image, _ = optional_import("PIL", name="Image")
1429+
closest_lvl_wsi = pil_image.fromarray(wsi.pages[closest_lvl].asarray())
14271430

14281431
elif closest_level_is_bigger:
14291432
# Otherwise, select the level closest to the desired mpp with a higher resolution and downsample it.
@@ -1437,6 +1440,9 @@ def get_wsi_at_mpp(
14371440
closest_lvl = closest_lvl - 1
14381441
closest_lvl_wsi = self._resize_to_mpp_res(wsi, closest_lvl, mpp_list, mpp)
14391442

1443+
# Convert to specified mode to normalize dtype
1444+
closest_lvl_wsi = closest_lvl_wsi.convert(self.mode)
1445+
14401446
wsi_arr = np.array(closest_lvl_wsi)
14411447

14421448
# Ensure channel dimension exists for grayscale
@@ -1561,4 +1567,4 @@ def _resize_to_mpp_res(self, wsi, closest_lvl, mpp_list, user_mpp: tuple):
15611567
# PIL `resize` takes (W, H); use the same row/col order as OpenSlide.
15621568
closest_lvl_wsi = closest_lvl_wsi.resize((target_res_y, target_res_x), pil_image.BILINEAR)
15631569

1564-
return closest_lvl_wsi
1570+
return closest_lvl_wsi

tests/utils/enums/test_wsireader.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
WSI_GENERIC_TIFF_PATH = os.path.join(TESTS_PATH, "testing_data", f"temp_{WSI_GENERIC_TIFF_KEY}.tiff")
4242

4343
WSI_GENERIC_TIFF_CORRECT_MPP_KEY = "wsi_generic_tiff_correct_mpp"
44-
WSI_GENERIC_TIFF_CORRECT_MPP_PATH = os.path.join(TESTS_PATH, "testing_data", f"temp_{WSI_GENERIC_TIFF_CORRECT_MPP_KEY}.tiff")
44+
WSI_GENERIC_TIFF_CORRECT_MPP_PATH = os.path.join(
45+
TESTS_PATH, "testing_data", f"temp_{WSI_GENERIC_TIFF_CORRECT_MPP_KEY}.tiff"
46+
)
4547

4648
WSI_APERIO_SVS_KEY = "wsi_aperio_svs"
4749
WSI_APERIO_SVS_PATH = os.path.join(TESTS_PATH, "testing_data", f"temp_{WSI_APERIO_SVS_KEY}.svs")
@@ -307,11 +309,7 @@
307309
{"openslide": (3, 10949, 15303), "cucim": (3, 10949, 15303), "tifffile": (3, 10949, 15303)},
308310
]
309311

310-
TEST_CASE_SVS_MPP_SCALAR = [
311-
WSI_APERIO_SVS_PATH,
312-
{"mpp": 8.0},
313-
{"openslide": (3, 2057, 2875), "cucim": (3, 2057, 2875)},
314-
]
312+
TEST_CASE_SVS_MPP_SCALAR = [WSI_APERIO_SVS_PATH, {"mpp": 8.0}, {"openslide": (3, 2057, 2875), "cucim": (3, 2057, 2875)}]
315313

316314
TEST_CASE_DEVICE_2 = [
317315
WSI_GENERIC_TIFF_PATH,
@@ -470,17 +468,17 @@ class WSIReaderTests:
470468
class Tests(unittest.TestCase):
471469
backend = None
472470

473-
# @parameterized.expand([TEST_CASE_WHOLE_0])
474-
# def test_read_whole_image(self, file_path, level, expected_shape):
475-
# reader = WSIReader(self.backend, level=level)
476-
# with reader.read(file_path) as img_obj:
477-
# img, meta = reader.get_data(img_obj)
478-
# self.assertTupleEqual(img.shape, expected_shape)
479-
# self.assertEqual(meta["backend"], self.backend)
480-
# self.assertEqual(meta[WSIPatchKeys.PATH].lower(), str(os.path.abspath(file_path)).lower())
481-
# self.assertEqual(meta[WSIPatchKeys.LEVEL], level)
482-
# assert_allclose(meta[WSIPatchKeys.SIZE], expected_shape[1:], type_test=False)
483-
# assert_allclose(meta[WSIPatchKeys.LOCATION], (0, 0), type_test=False)
471+
@parameterized.expand([TEST_CASE_WHOLE_0])
472+
def test_read_whole_image(self, file_path, level, expected_shape):
473+
reader = WSIReader(self.backend, level=level)
474+
with reader.read(file_path) as img_obj:
475+
img, meta = reader.get_data(img_obj)
476+
self.assertTupleEqual(img.shape, expected_shape)
477+
self.assertEqual(meta["backend"], self.backend)
478+
self.assertEqual(meta[WSIPatchKeys.PATH].lower(), str(os.path.abspath(file_path)).lower())
479+
self.assertEqual(meta[WSIPatchKeys.LEVEL], level)
480+
assert_allclose(meta[WSIPatchKeys.SIZE], expected_shape[1:], type_test=False)
481+
assert_allclose(meta[WSIPatchKeys.LOCATION], (0, 0), type_test=False)
484482

485483
@parameterized.expand(
486484
[

0 commit comments

Comments
 (0)