Repository: Project-MONAI/MONAI

Issue:
`iter_patch_slices` Patch Order
Similar to convolutions, the way usually sliding window is implemented is going over the slow dimension first and then go down the other indices (sweeping row by row), like this:
```python
A = [[ a, a, b, b],
     [ a, a, b, b],
     [ c, c, d, d],
     [ c, c, d, d]] 

L = [[[a,a],[a,a]],[[b,b],[b,b]], [[c,c],[c,c]], [[d,d],[d,d]]
```

However, in `iter_patch_slices`, this behavior is intentionally reversed:
https://github.com/Project-MONAI/MONAI/blob/cd77e910400eed5b596e739fb588e118677c813f/monai/data/utils.py#L148-L149
Is there any reason for that? Can we stick to the more common convention?


Discussion:
@wyli @Nic-Ma @ericspod, do you have any input here?
good question, looks like there is an inconsistency:
```py
import numpy as np
from monai.data import dense_patch_slices, iter_patch_slices

A = [['a', 'a', 'b', 'b'], ['a', 'a', 'b', 'b'], ['c', 'c', 'd', 'd'], ['c', 'c', 'd', 'd']]

print('dense_patch_slices')
xx = dense_patch_slices([4,4],[2,2], [2,2])
for i in xx: print(np.asarray(A)[i])

print('iter_patch_slices')
for i in iter_patch_slices([4,4], [2,2]):
    print(np.asarray(A)[i])

```

results:
```
dense_patch_slices
[['a' 'a']
 ['a' 'a']]
[['b' 'b']
 ['b' 'b']]
[['c' 'c']
 ['c' 'c']]
[['d' 'd']
 ['d' 'd']]
iter_patch_slices
[['a' 'a']
 ['a' 'a']]
[['c' 'c']
 ['c' 'c']]
[['b' 'b']
 ['b' 'b']]
[['d' 'd']
 ['d' 'd']]
```
I think we should by default consistently use the row-major order, could you help update `iter_patch_slices`?
Sure, will submit a PR shortly.

Patch:
diff --git a/monai/data/utils.py b/monai/data/utils.py
--- a/monai/data/utils.py
+++ b/monai/data/utils.py
@@ -145,8 +145,8 @@ def iter_patch_slices(
     ranges = tuple(starmap(range, zip(start_pos, dims, patch_size_)))
 
     # choose patches by applying product to the ranges
-    for position in product(*ranges[::-1]):  # reverse ranges order to iterate in index order
-        yield tuple(slice(s, s + p) for s, p in zip(position[::-1], patch_size_))
+    for position in product(*ranges):
+        yield tuple(slice(s, s + p) for s, p in zip(position, patch_size_))
 
 
 def dense_patch_slices(


Test patch:
diff --git a/tests/test_grid_dataset.py b/tests/test_grid_dataset.py
--- a/tests/test_grid_dataset.py
+++ b/tests/test_grid_dataset.py
@@ -60,20 +60,20 @@ def test_loading_array(self):
             np.testing.assert_equal(tuple(item[0].shape), (2, 1, 2, 2))
         np.testing.assert_allclose(
             item[0],
-            np.array([[[[1.4965, 2.4965], [5.4965, 6.4965]]], [[[11.3584, 12.3584], [15.3584, 16.3584]]]]),
+            np.array([[[[7.4965, 8.4965], [11.4965, 12.4965]]], [[[11.3584, 12.3584], [15.3584, 16.3584]]]]),
             rtol=1e-4,
         )
-        np.testing.assert_allclose(item[1], np.array([[[0, 1], [0, 2], [2, 4]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5)
+        np.testing.assert_allclose(item[1], np.array([[[0, 1], [2, 4], [0, 2]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5)
         if sys.platform != "win32":
             for item in DataLoader(ds, batch_size=2, shuffle=False, num_workers=2):
                 np.testing.assert_equal(tuple(item[0].shape), (2, 1, 2, 2))
             np.testing.assert_allclose(
                 item[0],
-                np.array([[[[1.2548, 2.2548], [5.2548, 6.2548]]], [[[9.1106, 10.1106], [13.1106, 14.1106]]]]),
+                np.array([[[[7.2548, 8.2548], [11.2548, 12.2548]]], [[[9.1106, 10.1106], [13.1106, 14.1106]]]]),
                 rtol=1e-3,
             )
             np.testing.assert_allclose(
-                item[1], np.array([[[0, 1], [0, 2], [2, 4]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5
+                item[1], np.array([[[0, 1], [2, 4], [0, 2]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5
             )
 
     def test_loading_dict(self):
@@ -102,20 +102,20 @@ def test_loading_dict(self):
             self.assertListEqual(item[0]["metadata"], ["test string", "test string"])
         np.testing.assert_allclose(
             item[0]["image"],
-            np.array([[[[1.4965, 2.4965], [5.4965, 6.4965]]], [[[11.3584, 12.3584], [15.3584, 16.3584]]]]),
+            np.array([[[[7.4965, 8.4965], [11.4965, 12.4965]]], [[[11.3584, 12.3584], [15.3584, 16.3584]]]]),
             rtol=1e-4,
         )
-        np.testing.assert_allclose(item[1], np.array([[[0, 1], [0, 2], [2, 4]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5)
+        np.testing.assert_allclose(item[1], np.array([[[0, 1], [2, 4], [0, 2]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5)
         if sys.platform != "win32":
             for item in DataLoader(ds, batch_size=2, shuffle=False, num_workers=2):
                 np.testing.assert_equal(item[0]["image"].shape, (2, 1, 2, 2))
             np.testing.assert_allclose(
                 item[0]["image"],
-                np.array([[[[1.2548, 2.2548], [5.2548, 6.2548]]], [[[9.1106, 10.1106], [13.1106, 14.1106]]]]),
+                np.array([[[[7.2548, 8.2548], [11.2548, 12.2548]]], [[[9.1106, 10.1106], [13.1106, 14.1106]]]]),
                 rtol=1e-3,
             )
             np.testing.assert_allclose(
-                item[1], np.array([[[0, 1], [0, 2], [2, 4]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5
+                item[1], np.array([[[0, 1], [2, 4], [0, 2]], [[0, 1], [2, 4], [2, 4]]]), rtol=1e-5
             )