Repository: Project-MONAI/MONAI

Issue:
Missed to apply `kwargs` in WSIReader
**Is your feature request related to a problem? Please describe.**
In the `read` function of `WSIReader`, it has `kwargs` arg as other readers, but missed to apply it in the calling of 3rd APIs, so we can't set additional args of these APIs:
https://github.com/Project-MONAI/MONAI/blob/dev/monai/data/image_reader.py#L764

Would be nice to apply the `kwargs` for 3rd APIs.



Discussion:

Resolving patch:
diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py
--- a/monai/data/image_reader.py
+++ b/monai/data/image_reader.py
@@ -711,20 +711,26 @@ class WSIReader(ImageReader):
         backend: backend library to load the images, available options: "cuCIM", "OpenSlide" and "TiffFile".
         level: the whole slide image level at which the image is extracted. (default=0)
             This is overridden if the level argument is provided in `get_data`.
+        kwargs: additional args for backend reading API in `read()`, more details in `cuCIM`, `TiffFile`, `OpenSlide`:
+            https://github.com/rapidsai/cucim/blob/v21.12.00/cpp/include/cucim/cuimage.h#L100.
+            https://github.com/cgohlke/tifffile.
+            https://openslide.org/api/python/#openslide.OpenSlide.
 
     Note:
         While "cuCIM" and "OpenSlide" backends both can load patches from large whole slide images
         without loading the entire image into memory, "TiffFile" backend needs to load the entire image into memory
         before extracting any patch; thus, memory consideration is needed when using "TiffFile" backend for
         patch extraction.
+
     """
 
-    def __init__(self, backend: str = "OpenSlide", level: int = 0):
+    def __init__(self, backend: str = "OpenSlide", level: int = 0, **kwargs):
         super().__init__()
         self.backend = backend.lower()
         func = require_pkg(self.backend)(self._set_reader)
         self.wsi_reader = func(self.backend)
         self.level = level
+        self.kwargs = kwargs
 
     @staticmethod
     def _set_reader(backend: str):
@@ -752,6 +758,11 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs):
 
         Args:
             data: file name or a list of file names to read.
+            kwargs: additional args for backend reading API in `read()`, will override `self.kwargs` for existing keys.
+                more details in `cuCIM`, `TiffFile`, `OpenSlide`:
+                https://github.com/rapidsai/cucim/blob/v21.12.00/cpp/include/cucim/cuimage.h#L100.
+                https://github.com/cgohlke/tifffile.
+                https://openslide.org/api/python/#openslide.OpenSlide.
 
         Returns:
             image object or list of image objects
@@ -760,8 +771,10 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs):
         img_: List = []
 
         filenames: Sequence[PathLike] = ensure_tuple(data)
+        kwargs_ = self.kwargs.copy()
+        kwargs_.update(kwargs)
         for name in filenames:
-            img = self.wsi_reader(name)
+            img = self.wsi_reader(name, **kwargs_)
             if self.backend == "openslide":
                 img.shape = (img.dimensions[1], img.dimensions[0], 3)
             img_.append(img)


Test patch:
diff --git a/tests/test_wsireader.py b/tests/test_wsireader.py
--- a/tests/test_wsireader.py
+++ b/tests/test_wsireader.py
@@ -119,8 +119,9 @@ def test_read_whole_image(self, file_path, level, expected_shape):
 
         @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_5])
         def test_read_region(self, file_path, patch_info, expected_img):
-            reader = WSIReader(self.backend)
-            with reader.read(file_path) as img_obj:
+            kwargs = {"name": None, "offset": None} if self.backend == "tifffile" else {}
+            reader = WSIReader(self.backend, **kwargs)
+            with reader.read(file_path, **kwargs) as img_obj:
                 if self.backend == "tifffile":
                     with self.assertRaises(ValueError):
                         reader.get_data(img_obj, **patch_info)[0]