Repository: Project-MONAI/MONAI

Issue:
Bug for changing `channel_dim` in Whole slide image reader
I tried to change the default value for `channel_dim` in `CuCIMWSIReader` and found that the outputs were not correct. Therefore, I checked the source code and identified the buggy part:
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L821-L827
Suppose I set `channel_dim=-1`, and then `patch = patch[:3] ` is not modifying the channel part at all, instead, it changes the image dimension!

Same problem for `TiffFileWSIReader`:
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L1179-L1186
Bug for changing `channel_dim` in Whole slide image reader
I tried to change the default value for `channel_dim` in `CuCIMWSIReader` and found that the outputs were not correct. Therefore, I checked the source code and identified the buggy part:
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L821-L827
Suppose I set `channel_dim=-1`, and then `patch = patch[:3] ` is not modifying the channel part at all, instead, it changes the image dimension!

Same problem for `TiffFileWSIReader`:
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L1179-L1186


Discussion:
Hi @Dylan-H-Wang, it seems the channel dim changes in L818. Could you please give me a small piece of code that can reproduce the bug, so then I could take a look at it?
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L817-L818
Thanks!
> Hi @Dylan-H-Wang, it seems the channel dim changes in L818. Could you please give me a small piece of code that can reproduce the bug, so then I could take a look at it?
> 
> https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L817-L818
> 
> Thanks!

Yes, that is what causes this bug. Since the `channel_dim` is moved to desired position `self.channel_dim`, this line
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L1186
should also based on the `self.channel_dim` instead of assuming its default value at `self.channel_dim=0`

Here is the code to reproduce the bug
```python
from monai.data import CuCIMWSIReader, TiffFileWSIReader

img_path = "./data/CAMELYON16/images/normal_150.tif"

reader = CuCIMWSIReader(channel_dim=0)
slide = reader.read(img_path)
slide_data= reader.get_data(slide, level=0)[0]
print(slide_data.shape) # (3, 60928, 65536)
      
reader = CuCIMWSIReader(channel_dim=-1)
slide = reader.read(img_path)
slide_data= reader.get_data(slide, level=0)[0]
print(slide_data.shape) # 3, 65536, 3)

reader = TiffFileWSIReader(channel_dim=-1)
slide = reader.read(img_path)
slide_data= reader.get_data(slide, level=0)[0]
print(slide_data.shape) # (3, 65426, 3)
```

 
Thanks, now I can reproduce it, I will create a PR to fix it.
Hi @Dylan-H-Wang, it seems the channel dim changes in L818. Could you please give me a small piece of code that can reproduce the bug, so then I could take a look at it?
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L817-L818
Thanks!
> Hi @Dylan-H-Wang, it seems the channel dim changes in L818. Could you please give me a small piece of code that can reproduce the bug, so then I could take a look at it?
> 
> https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L817-L818
> 
> Thanks!

Yes, that is what causes this bug. Since the `channel_dim` is moved to desired position `self.channel_dim`, this line
https://github.com/Project-MONAI/MONAI/blob/e5cf74aac60cb3512d92f9abb3dd48b5ed36e8d0/monai/data/wsi_reader.py#L1186
should also based on the `self.channel_dim` instead of assuming its default value at `self.channel_dim=0`

Here is the code to reproduce the bug
```python
from monai.data import CuCIMWSIReader, TiffFileWSIReader

img_path = "./data/CAMELYON16/images/normal_150.tif"

reader = CuCIMWSIReader(channel_dim=0)
slide = reader.read(img_path)
slide_data= reader.get_data(slide, level=0)[0]
print(slide_data.shape) # (3, 60928, 65536)
      
reader = CuCIMWSIReader(channel_dim=-1)
slide = reader.read(img_path)
slide_data= reader.get_data(slide, level=0)[0]
print(slide_data.shape) # 3, 65536, 3)

reader = TiffFileWSIReader(channel_dim=-1)
slide = reader.read(img_path)
slide_data= reader.get_data(slide, level=0)[0]
print(slide_data.shape) # (3, 65426, 3)
```

 
Thanks, now I can reproduce it, I will create a PR to fix it.

Resolving patch:
diff --git a/monai/data/wsi_reader.py b/monai/data/wsi_reader.py
--- a/monai/data/wsi_reader.py
+++ b/monai/data/wsi_reader.py
@@ -824,7 +824,7 @@ def _get_patch(
                     f"The image is expected to have three or four color channels in '{mode}' mode but has "
                     f"{patch.shape[self.channel_dim]}. "
                 )
-            patch = patch[:3]
+            patch = np.take(patch, [0, 1, 2], self.channel_dim)
 
         return patch
 
@@ -1183,6 +1183,6 @@ def _get_patch(
                     f"The image is expected to have three or four color channels in '{mode}' mode but has "
                     f"{patch.shape[self.channel_dim]}. "
                 )
-            patch = patch[:3]
+            patch = np.take(patch, [0, 1, 2], self.channel_dim)
 
         return patch


Test patch:
diff --git a/tests/test_wsireader.py b/tests/test_wsireader.py
--- a/tests/test_wsireader.py
+++ b/tests/test_wsireader.py
@@ -81,15 +81,27 @@
 TEST_CASE_3 = [
     WSI_GENERIC_TIFF_PATH,
     {"channel_dim": -1},
-    {"location": (WSI_GENERIC_TIFF_HEIGHT // 2, WSI_GENERIC_TIFF_WIDTH // 2), "size": (2, 1), "level": 0},
-    np.moveaxis(np.array([[[246], [246]], [[246], [246]], [[246], [246]]], dtype=np.uint8), 0, -1),
+    {"location": (WSI_GENERIC_TIFF_HEIGHT // 2, WSI_GENERIC_TIFF_WIDTH // 2), "size": (4, 1), "level": 0},
+    np.moveaxis(
+        np.array(
+            [[[246], [246], [246], [246]], [[246], [246], [246], [246]], [[246], [246], [246], [246]]], dtype=np.uint8
+        ),
+        0,
+        -1,
+    ),
 ]
 
 TEST_CASE_4 = [
     WSI_GENERIC_TIFF_PATH,
     {"channel_dim": 2},
-    {"location": (0, 0), "size": (2, 1), "level": 8},
-    np.moveaxis(np.array([[[242], [242]], [[242], [242]], [[242], [242]]], dtype=np.uint8), 0, -1),
+    {"location": (0, 0), "size": (4, 1), "level": 8},
+    np.moveaxis(
+        np.array(
+            [[[242], [242], [242], [242]], [[242], [242], [242], [242]], [[242], [242], [242], [242]]], dtype=np.uint8
+        ),
+        0,
+        -1,
+    ),
 ]
 
 TEST_CASE_5 = [