diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index 13fa47b0af..a41615c908 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -924,6 +924,20 @@ def convert_to_rgb_array(self, raw_region, dtype: DtypeLike = np.uint8): # convert to numpy (if not already in numpy) raw_region = np.asarray(raw_region, dtype=dtype) + # check if the image has three dimensions (2D + color) + if raw_region.ndim != 3: + raise ValueError( + f"The input image dimension should be 3 but {raw_region.ndim} is given. " + "`WSIReader` is designed to work only with 2D colored images." + ) + + # check if the color channel is 3 (RGB) or 4 (RGBA) + if raw_region.shape[-1] not in [3, 4]: + raise ValueError( + f"There should be three or four color channels but {raw_region.shape[-1]} is given. " + "`WSIReader` is designed to work only with 2D colored images." + ) + # remove alpha channel if exist (RGBA) if raw_region.shape[-1] > 3: raw_region = raw_region[..., :3] diff --git a/tests/test_wsireader.py b/tests/test_wsireader.py index 589bd32369..6ee02143b8 100644 --- a/tests/test_wsireader.py +++ b/tests/test_wsireader.py @@ -27,8 +27,8 @@ cucim, has_cucim = optional_import("cucim") has_cucim = has_cucim and hasattr(cucim, "CuImage") -_, has_osl = optional_import("openslide") -imsave, has_tiff = optional_import("tifffile", name="imsave") +openslide, has_osl = optional_import("openslide") +imwrite, has_tiff = optional_import("tifffile", name="imwrite") _, has_codec = optional_import("imagecodecs") has_tiff = has_tiff and has_codec @@ -84,6 +84,9 @@ TEST_CASE_RGB_1 = [np.ones((3, 100, 100), dtype=np.uint8)] # CHW +TEST_CASE_ERROR_GRAY = [np.ones((16, 16), dtype=np.uint8)] # no color channel +TEST_CASE_ERROR_3D = [np.ones((16, 16, 16, 3), dtype=np.uint8)] # 3D + color + def save_rgba_tiff(array: np.ndarray, filename: str, mode: str): """ @@ -91,14 +94,28 @@ def save_rgba_tiff(array: np.ndarray, filename: str, mode: str): Args: array: numpy ndarray with the shape of CxHxW and C==3 representing a RGB image - file_prefix: the filename to be used for the tiff file. '_RGB.tiff' or '_RGBA.tiff' will be appended to this filename. + filename: the filename to be used for the tiff file. '_RGB.tiff' or '_RGBA.tiff' will be appended to this filename. mode: RGB or RGBA """ if mode == "RGBA": array = np.concatenate([array, 255 * np.ones_like(array[0])[np.newaxis]]).astype(np.uint8) img_rgb = array.transpose(1, 2, 0) - imsave(filename, img_rgb, shape=img_rgb.shape, tile=(16, 16)) + imwrite(filename, img_rgb, shape=img_rgb.shape, tile=(16, 16)) + + return filename + + +def save_gray_tiff(array: np.ndarray, filename: str): + """ + Save numpy array into a TIFF file + + Args: + array: numpy ndarray with any shape + filename: the filename to be used for the tiff file. + """ + img_gray = array + imwrite(filename, img_gray, shape=img_gray.shape, photometric="rgb") return filename @@ -170,6 +187,17 @@ def test_read_rgba(self, img_expected): self.assertIsNone(assert_array_equal(image["RGB"], img_expected)) self.assertIsNone(assert_array_equal(image["RGBA"], img_expected)) + @parameterized.expand([TEST_CASE_ERROR_GRAY, TEST_CASE_ERROR_3D]) + @skipUnless(has_tiff, "Requires tifffile.") + def test_read_malformats(self, img_expected): + reader = WSIReader(self.backend) + file_path = save_gray_tiff( + img_expected, os.path.join(os.path.dirname(__file__), "testing_data", "temp_tiff_image_gray.tiff") + ) + with self.assertRaises((RuntimeError, ValueError, openslide.OpenSlideError if has_osl else ValueError)): + with reader.read(file_path) as img_obj: + reader.get_data(img_obj) + @parameterized.expand([TEST_CASE_TRANSFORM_0]) def test_with_dataloader(self, file_path, level, expected_spatial_shape, expected_shape): train_transform = Compose(