Repository: Project-MONAI/MONAI

Issue:
`SpacingD` transform bug for certain affine input
I use the `SpacingD` transforms after  `LoadImageD`  and `AddChannelD`. I read in a nifti file of shape [368, 336, 368] , the input to the `spacingD` [1, 368, 336, 368]. The outputs of spacingD is a matrix with zeros.

I followed the trace and the last time I see non-zeros matrix is at 

https://github.com/Project-MONAI/MONAI/blob/dev/monai/networks/layers/spatial_transforms.py#L575

The generated grid has a range [1,3] but the `torch.grid_sample` expects range [-1, 1].
More details here- 
https://pytorch.org/docs/stable/generated/torch.nn.functional.affine_grid.html
https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

I tried it various nifti files and gives me the same trouble. When I change the range to the expected range by substracting 2. i get a output, which looks good. 

Can anyone verify the issue. I am using MonAi 1.0.0

_Originally posted by @a-parida12 in https://github.com/Project-MONAI/MONAI/discussions/5241_
    


Discussion:

Patch:
diff --git a/monai/data/utils.py b/monai/data/utils.py
--- a/monai/data/utils.py
+++ b/monai/data/utils.py
@@ -875,17 +875,19 @@ def compute_shape_offset(
     corners_out = inv_mat @ corners
     corners_out = corners_out[:-1] / corners_out[-1]
     out_shape = np.round(corners_out.ptp(axis=1)) if scale_extent else np.round(corners_out.ptp(axis=1) + 1.0)
-    mat = inv_mat[:-1, :-1]
-    i = 0
+    all_dist = inv_mat[:-1, :-1] @ corners[:-1, :]
+    offset = None
     for i in range(corners.shape[1]):
-        min_corner = np.min(mat @ corners[:-1, :] - mat @ corners[:-1, i : i + 1], 1)
+        min_corner = np.min(all_dist - all_dist[:, i : i + 1], 1)
         if np.allclose(min_corner, 0.0, rtol=AFFINE_TOL):
+            offset = corners[:-1, i]  # corner is the smallest, shift the corner to origin
             break
-    offset = corners[:-1, i]
+    if offset is None:  # otherwise make output image center aligned with the input image center
+        offset = in_affine_[:-1, :-1] @ (shape / 2.0) + in_affine_[:-1, -1] - out_affine_[:-1, :-1] @ (out_shape / 2.0)
     if scale_extent:
         in_offset = np.append(0.5 * (shape / out_shape - 1.0), 1.0)
         offset = np.abs((in_affine_ @ in_offset / in_offset[-1])[:-1]) * np.sign(offset)
-    return out_shape.astype(int, copy=False), offset
+    return out_shape.astype(int, copy=False), offset  # type: ignore
 
 
 def to_affine_nd(r: Union[np.ndarray, int], affine: NdarrayTensor, dtype=np.float64) -> NdarrayTensor:


Test patch:
diff --git a/tests/test_spacing.py b/tests/test_spacing.py
--- a/tests/test_spacing.py
+++ b/tests/test_spacing.py
@@ -230,6 +230,24 @@
             *device,
         ]
     )
+    TESTS.append(  # 5D input
+        [
+            {"pixdim": 0.5, "padding_mode": "zeros", "mode": "nearest", "scale_extent": True},
+            torch.ones((1, 368, 336, 368)),  # data
+            torch.tensor(
+                [
+                    [0.41, 0.005, 0.008, -79.7],
+                    [-0.0049, 0.592, 0.0664, -57.4],
+                    [-0.0073, -0.0972, 0.404, -32.1],
+                    [0.0, 0.0, 0.0, 1.0],
+                ]
+            ),
+            {},
+            torch.ones((1, 302, 403, 301)),
+            *device,
+        ]
+    )
+
 
 TESTS_TORCH = []
 for track_meta in (False, True):