Repository: Project-MONAI/MONAI

Issue:
Different interpolation orders in Transforms for different input dict keys
**Is your feature request related to a problem? Please describe.**
The composable transforms Resized, Rotated, RandRotated, Zoomd and RandZoomd do not allow to define different interpolation orders for different keys in the input dictionary, as the transform input `order` expects only a single integer and cannot accept a list. The possibility to define different interpolation orders would be useful for e.g. image-segmentation pairs, where the former might need interpolation order >= 1, while the segmentation requires interpolation order 0.

**Describe the solution you'd like**
A modification of the transforms to accept a list of orders, similar to what is already implemented for Spacingd (```interp_order``` input) or for the Rand2DElastic (```mode``` input). This would allow to define, e.g.:
```python
Resized(keys=['img', 'seg'], spatial_size=[96, 96], order=(1, 0))
```

**Describe alternatives you've considered**
For the Resized I could have the following workaround:
```python
transf = Compose([
    Resized(keys=['img'], spatial_size=[96, 96], order=1),
    Resized(keys=['seg'], spatial_size=[96, 96], order=0, anti_aliasing=False)])
```
However, this cannot work for data augmentation layers RandRotated and RandZoomd as it would apply different randomisation to the image and its respective segmentation.

**Additional context**
In the case of the Resized, the same feature would be useful for other inputs such as the ```anti_aliasing```.


Discussion:
Nic-Ma: /black

Patch:
diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py
index 527d120d..5c1610cf 100644
--- a/monai/transforms/croppad/array.py
+++ b/monai/transforms/croppad/array.py
@@ -35,8 +35,7 @@ class SpatialPad(Transform):
     """
 
     def __init__(self, spatial_size, method="symmetric", mode="constant"):
-        assert isinstance(spatial_size, (list, tuple)), "spatial_out_size must be list or tuple."
-        self.spatial_size = spatial_size
+        self.spatial_size = ensure_tuple(spatial_size)
         assert method in ("symmetric", "end"), "unsupported padding type."
         self.method = method
         assert isinstance(mode, str), "mode must be str."
@@ -52,10 +51,10 @@ class SpatialPad(Transform):
         else:
             return [(0, max(self.spatial_size[i] - data_shape[i], 0)) for i in range(len(self.spatial_size))]
 
-    def __call__(self, img):
+    def __call__(self, img, mode=None):
         data_pad_width = self._determine_data_pad_width(img.shape[1:])
         all_pad_width = [(0, 0)] + data_pad_width
-        img = np.pad(img, all_pad_width, self.mode)
+        img = np.pad(img, all_pad_width, mode=mode or self.mode)
         return img
 
 
diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py
index 2b8306e0..8ddf8873 100644
--- a/monai/transforms/croppad/dictionary.py
+++ b/monai/transforms/croppad/dictionary.py
@@ -19,7 +19,7 @@ from monai.data.utils import get_random_patch, get_valid_patch_size
 from monai.transforms.compose import MapTransform, Randomizable
 from monai.transforms.croppad.array import SpatialCrop, CenterSpatialCrop, SpatialPad
 from monai.transforms.utils import generate_pos_neg_label_crop_centers, generate_spatial_bounding_box
-from monai.utils.misc import ensure_tuple
+from monai.utils.misc import ensure_tuple, ensure_tuple_rep
 
 
 class SpatialPadd(MapTransform):
@@ -35,18 +35,19 @@ class SpatialPadd(MapTransform):
                 See also: :py:class:`monai.transforms.compose.MapTransform`
             spatial_size (list): the spatial size of output data after padding.
             method (str): pad image symmetric on every side or only pad at the end sides. default is 'symmetric'.
-            mode (str): one of the following string values or a user supplied function: {'constant', 'edge',
-                'linear_ramp', 'maximum', 'mean', 'median', 'minimum', 'reflect', 'symmetric',
+            mode (str or sequence of str): one of the following string values or a user supplied function:
+                {'constant', 'edge', 'linear_ramp', 'maximum', 'mean', 'median', 'minimum', 'reflect', 'symmetric',
                 'wrap', 'empty', <function>}
                 for more details, please check: https://docs.scipy.org/doc/numpy/reference/generated/numpy.pad.html
         """
         super().__init__(keys)
-        self.padder = SpatialPad(spatial_size, method, mode)
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.padder = SpatialPad(spatial_size, method)
 
     def __call__(self, data):
         d = dict(data)
-        for key in self.keys:
-            d[key] = self.padder(d[key])
+        for key, m in zip(self.keys, self.mode):
+            d[key] = self.padder(d[key], mode=m)
         return d
 
 
diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py
index 5e5aa650..4c5426af 100644
--- a/monai/transforms/spatial/array.py
+++ b/monai/transforms/spatial/array.py
@@ -39,7 +39,7 @@ class Spacing(Transform):
     Resample input image into the specified `pixdim`.
     """
 
-    def __init__(self, pixdim, diagonal=False, mode="nearest", cval=0, dtype=None):
+    def __init__(self, pixdim, diagonal=False, interp_order=3, mode="nearest", cval=0, dtype=None):
         """
         Args:
             pixdim (sequence of floats): output voxel spacing.
@@ -54,6 +54,9 @@ class Spacing(Transform):
                 If False, this transform preserves the axes orientation, orthogonal rotation and
                 translation components from the original affine. This option will not flip/swap axes
                 of the original data.
+            interp_order (int): The order of the spline interpolation, default is 3.
+                The order has to be in the range 0-5.
+                https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html
             mode (`reflect|constant|nearest|mirror|wrap`):
                 The mode parameter determines how the input array is extended beyond its boundaries.
             cval (scalar): Value to fill past edges of input if mode is "constant". Default is 0.0.
@@ -61,18 +64,16 @@ class Spacing(Transform):
         """
         self.pixdim = np.array(ensure_tuple(pixdim), dtype=np.float64)
         self.diagonal = diagonal
+        self.interp_order = interp_order
         self.mode = mode
         self.cval = cval
         self.dtype = dtype
 
-    def __call__(self, data_array, affine=None, interp_order=3):
+    def __call__(self, data_array, affine=None, interp_order=None, mode=None, cval=None, dtype=None):
         """
         Args:
             data_array (ndarray): in shape (num_channels, H[, W, ...]).
             affine (matrix): (N+1)x(N+1) original affine matrix for spatially ND `data_array`. Defaults to identity.
-            interp_order (int): The order of the spline interpolation, default is 3.
-                The order has to be in the range 0-5.
-                https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html
         Returns:
             data_array (resampled into `self.pixdim`), original pixdim, current pixdim.
         """
@@ -98,16 +99,16 @@ class Spacing(Transform):
         # adapt to the actual rank
         transform_ = to_affine_nd(sr, transform)
         # resample
-        dtype = data_array.dtype if self.dtype is None else self.dtype
+        _dtype = dtype or self.dtype or data_array.dtype
         output_data = []
         for data in data_array:
             data_ = scipy.ndimage.affine_transform(
-                data.astype(dtype),
+                data.astype(_dtype),
                 matrix=transform_,
                 output_shape=output_shape,
-                order=interp_order,
-                mode=self.mode,
-                cval=self.cval,
+                order=self.interp_order if interp_order is None else interp_order,
+                mode=mode or self.mode,
+                cval=self.cval if cval is None else cval,
             )
             output_data.append(data_)
         output_data = np.stack(output_data)
@@ -222,21 +223,11 @@ class Resize(Transform):
             https://scikit-image.org/docs/dev/user_guide/data_types.html.
         anti_aliasing (bool): Whether to apply a gaussian filter to image before down-scaling.
             Default is True.
-        anti_aliasing_sigma (float, tuple of floats): Standard deviation for gaussian filtering.
     """
 
     def __init__(
-        self,
-        spatial_size,
-        order=1,
-        mode="reflect",
-        cval=0,
-        clip=True,
-        preserve_range=True,
-        anti_aliasing=True,
-        anti_aliasing_sigma=None,
+        self, spatial_size, order=1, mode="reflect", cval=0, clip=True, preserve_range=True, anti_aliasing=True,
     ):
-        assert isinstance(order, int), "order must be integer."
         self.spatial_size = spatial_size
         self.order = order
         self.mode = mode
@@ -244,9 +235,10 @@ class Resize(Transform):
         self.clip = clip
         self.preserve_range = preserve_range
         self.anti_aliasing = anti_aliasing
-        self.anti_aliasing_sigma = anti_aliasing_sigma
 
-    def __call__(self, img):
+    def __call__(
+        self, img, order=None, mode=None, cval=None, clip=None, preserve_range=None, anti_aliasing=None,
+    ):
         """
         Args:
             img (ndarray): channel first array, must have shape: (num_channels, H[, W, ..., ]),
@@ -255,15 +247,14 @@ class Resize(Transform):
         for channel in img:
             resized.append(
                 resize(
-                    channel,
-                    self.spatial_size,
-                    order=self.order,
-                    mode=self.mode,
-                    cval=self.cval,
-                    clip=self.clip,
-                    preserve_range=self.preserve_range,
-                    anti_aliasing=self.anti_aliasing,
-                    anti_aliasing_sigma=self.anti_aliasing_sigma,
+                    image=channel,
+                    output_shape=self.spatial_size,
+                    order=self.order if order is None else order,
+                    mode=mode or self.mode,
+                    cval=self.cval if cval is None else cval,
+                    clip=self.clip if clip is None else clip,
+                    preserve_range=self.preserve_range if preserve_range is None else preserve_range,
+                    anti_aliasing=self.anti_aliasing if anti_aliasing is None else anti_aliasing,
                 )
             )
         return np.stack(resized).astype(img.dtype)
@@ -290,14 +281,14 @@ class Rotate(Transform):
 
     def __init__(self, angle, spatial_axes=(0, 1), reshape=True, order=1, mode="constant", cval=0, prefilter=True):
         self.angle = angle
+        self.spatial_axes = spatial_axes
         self.reshape = reshape
         self.order = order
         self.mode = mode
         self.cval = cval
         self.prefilter = prefilter
-        self.spatial_axes = spatial_axes
 
-    def __call__(self, img):
+    def __call__(self, img, order=None, mode=None, cval=None, prefilter=None):
         """
         Args:
             img (ndarray): channel first array, must have shape: (num_channels, H[, W, ..., ]),
@@ -306,14 +297,14 @@ class Rotate(Transform):
         for channel in img:
             rotated.append(
                 scipy.ndimage.rotate(
-                    channel,
-                    self.angle,
-                    self.spatial_axes,
+                    input=channel,
+                    angle=self.angle,
+                    axes=self.spatial_axes,
                     reshape=self.reshape,
-                    order=self.order,
-                    mode=self.mode,
-                    cval=self.cval,
-                    prefilter=self.prefilter,
+                    order=self.order if order is None else order,
+                    mode=mode or self.mode,
+                    cval=self.cval if cval is None else cval,
+                    prefilter=self.prefilter if prefilter is None else prefilter,
                 )
             )
         return np.stack(rotated).astype(img.dtype)
@@ -330,13 +321,13 @@ class Zoom(Transform):
         order (int): order of interpolation. Default=3.
         mode (str): Determines how input is extended beyond boundaries. Default is 'constant'.
         cval (scalar, optional): Value to fill past edges. Default is 0.
+        prefilter (bool): Apply spline_filter before interpolation. Default: True.
         use_gpu (bool): Should use cpu or gpu. Uses cupyx which doesn't support order > 1 and modes
             'wrap' and 'reflect'. Defaults to cpu for these cases or if cupyx not found.
         keep_size (bool): Should keep original size (pad if needed).
     """
 
     def __init__(self, zoom, order=3, mode="constant", cval=0, prefilter=True, use_gpu=False, keep_size=False):
-        assert isinstance(order, int), "Order must be integer."
         self.zoom = zoom
         self.order = order
         self.mode = mode
@@ -357,7 +348,7 @@ class Zoom(Transform):
         else:
             self._zoom = scipy.ndimage.zoom
 
-    def __call__(self, img):
+    def __call__(self, img, order=None, mode=None, cval=None, prefilter=None):
         """
         Args:
             img (ndarray): channel first array, must have shape: (num_channels, H[, W, ..., ]),
@@ -368,7 +359,12 @@ class Zoom(Transform):
 
             for channel in cupy.array(img):
                 zoom_channel = self._zoom(
-                    channel, zoom=self.zoom, order=self.order, mode=self.mode, cval=self.cval, prefilter=self.prefilter
+                    channel,
+                    zoom=self.zoom,
+                    order=self.order if order is None else order,
+                    mode=self.mode if mode is None else mode,
+                    cval=self.cval if cval is None else cval,
+                    prefilter=self.prefilter if prefilter is None else prefilter,
                 )
                 zoomed.append(cupy.asnumpy(zoom_channel))
         else:
@@ -377,10 +373,10 @@ class Zoom(Transform):
                     self._zoom(
                         channel,
                         zoom=self.zoom,
-                        order=self.order,
-                        mode=self.mode,
-                        cval=self.cval,
-                        prefilter=self.prefilter,
+                        order=self.order if order is None else order,
+                        mode=mode or self.mode,
+                        cval=self.cval if cval is None else cval,
+                        prefilter=self.prefilter if prefilter is None else prefilter,
                     )
                 )
         zoomed = np.stack(zoomed).astype(img.dtype)
@@ -397,7 +393,7 @@ class Zoom(Transform):
                 pad_vec[idx] = [half, diff - half]
             elif diff < 0:  # need slicing
                 slice_vec[idx] = slice(half, half + od)
-        zoomed = np.pad(zoomed, pad_vec, mode="constant")
+        zoomed = np.pad(zoomed, pad_vec, mode="edge")
         return zoomed[tuple(slice_vec)]
 
 
@@ -466,9 +462,9 @@ class RandRotate(Randomizable, Transform):
     """Randomly rotates the input arrays.
 
     Args:
-        prob (float): Probability of rotation.
         degrees (tuple of float or float): Range of rotation in degrees. If single number,
             angle is picked from (-degrees, degrees).
+        prob (float): Probability of rotation.
         spatial_axes (tuple of 2 ints): Spatial axes of rotation. Default: (0, 1).
             This is the first two axis in spatial dimensions.
         reshape (bool): If reshape is true, the output shape is adapted so that the
@@ -484,14 +480,14 @@ class RandRotate(Randomizable, Transform):
     def __init__(
         self, degrees, prob=0.1, spatial_axes=(0, 1), reshape=True, order=1, mode="constant", cval=0, prefilter=True
     ):
-        self.prob = prob
         self.degrees = degrees
+        self.prob = prob
+        self.spatial_axes = spatial_axes
         self.reshape = reshape
         self.order = order
         self.mode = mode
         self.cval = cval
         self.prefilter = prefilter
-        self.spatial_axes = spatial_axes
 
         if not hasattr(self.degrees, "__iter__"):
             self.degrees = (-self.degrees, self.degrees)
@@ -504,11 +500,19 @@ class RandRotate(Randomizable, Transform):
         self._do_transform = self.R.random_sample() < self.prob
         self.angle = self.R.uniform(low=self.degrees[0], high=self.degrees[1])
 
-    def __call__(self, img):
+    def __call__(self, img, order=None, mode=None, cval=None, prefilter=None):
         self.randomize()
         if not self._do_transform:
             return img
-        rotator = Rotate(self.angle, self.spatial_axes, self.reshape, self.order, self.mode, self.cval, self.prefilter)
+        rotator = Rotate(
+            angle=self.angle,
+            spatial_axes=self.spatial_axes,
+            reshape=self.reshape,
+            order=self.order if order is None else order,
+            mode=mode or self.mode,
+            cval=self.cval if cval is None else cval,
+            prefilter=self.prefilter if prefilter is None else prefilter,
+        )
         return rotator(img)
 
 
@@ -552,6 +556,7 @@ class RandZoom(Randomizable, Transform):
         mode ('reflect', 'constant', 'nearest', 'mirror', 'wrap'): Determines how input is
             extended beyond boundaries. Default: 'constant'.
         cval (scalar, optional): Value to fill past edges. Default is 0.
+        prefilter (bool): Apply spline_filter before interpolation. Default: True.
         use_gpu (bool): Should use cpu or gpu. Uses cupyx which doesn't support order > 1 and modes
             'wrap' and 'reflect'. Defaults to cpu for these cases or if cupyx not found.
         keep_size (bool): Should keep original size (pad if needed).
@@ -574,12 +579,13 @@ class RandZoom(Randomizable, Transform):
         self.min_zoom = min_zoom
         self.max_zoom = max_zoom
         self.prob = prob
+        self.use_gpu = use_gpu
+        self.keep_size = keep_size
+
         self.order = order
         self.mode = mode
         self.cval = cval
         self.prefilter = prefilter
-        self.use_gpu = use_gpu
-        self.keep_size = keep_size
 
         self._do_transform = False
         self._zoom = None
@@ -591,12 +597,18 @@ class RandZoom(Randomizable, Transform):
         else:
             self._zoom = self.R.uniform(self.min_zoom, self.max_zoom)
 
-    def __call__(self, img):
+    def __call__(self, img, order=None, mode=None, cval=None, prefilter=None):
         self.randomize()
         if not self._do_transform:
             return img
-        zoomer = Zoom(self._zoom, self.order, self.mode, self.cval, self.prefilter, self.use_gpu, self.keep_size)
-        return zoomer(img)
+        zoomer = Zoom(self._zoom, use_gpu=self.use_gpu, keep_size=self.keep_size)
+        return zoomer(
+            img,
+            order=self.order if order is None else order,
+            mode=mode or self.mode,
+            cval=self.cval if cval is None else cval,
+            prefilter=self.prefilter if prefilter is None else prefilter,
+        )
 
 
 class AffineGrid(Transform):
@@ -772,7 +784,7 @@ class RandDeformGrid(Randomizable, Transform):
 
 
 class Resample(Transform):
-    def __init__(self, padding_mode="zeros", as_tensor_output=False, device=None):
+    def __init__(self, padding_mode="zeros", mode="bilinear", as_tensor_output=False, device=None):
         """
         computes output image using values from `img`, locations from `grid` using pytorch.
         supports spatially 2D or 3D (num_channels, H, W[, D]).
@@ -780,13 +792,15 @@ class Resample(Transform):
         Args:
             padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices. Defaults to 'zeros'.
             as_tensor_output(bool): whether to return a torch tensor. Defaults to False.
+            mode ('nearest'|'bilinear'): interpolation order. Defaults to 'bilinear'.
             device (torch.device): device on which the tensor will be allocated.
         """
         self.padding_mode = padding_mode
+        self.mode = mode
         self.as_tensor_output = as_tensor_output
         self.device = device
 
-    def __call__(self, img, grid, mode="bilinear"):
+    def __call__(self, img, grid, padding_mode=None, mode=None):
         """
         Args:
             img (ndarray or tensor): shape must be (num_channels, H, W[, D]).
@@ -806,7 +820,11 @@ class Resample(Transform):
         grid = grid[range(img.ndim - 2, -1, -1)]
         grid = grid.permute(list(range(grid.ndim))[1:] + [0])
         out = torch.nn.functional.grid_sample(
-            img[None].float(), grid[None].float(), mode=mode, padding_mode=self.padding_mode, align_corners=False
+            img[None].float(),
+            grid[None].float(),
+            mode=mode or self.mode,
+            padding_mode=padding_mode or self.padding_mode,
+            align_corners=False,
         )[0]
         if self.as_tensor_output:
             return out
@@ -860,23 +878,25 @@ class Affine(Transform):
             as_tensor_output=True,
             device=device,
         )
-        self.resampler = Resample(padding_mode=padding_mode, as_tensor_output=as_tensor_output, device=device)
+        self.resampler = Resample(as_tensor_output=as_tensor_output, device=device)
         self.spatial_size = spatial_size
+        self.padding_mode = padding_mode
         self.mode = mode
 
-    def __call__(self, img, spatial_size=None, mode=None):
+    def __call__(self, img, spatial_size=None, padding_mode=None, mode=None):
         """
         Args:
             img (ndarray or tensor): shape must be (num_channels, H, W[, D]),
             spatial_size (list or tuple of int): output image spatial size.
                 if `img` has two spatial dimensions, `spatial_size` should have 2 elements [h, w].
                 if `img` has three spatial dimensions, `spatial_size` should have 3 elements [h, w, d].
+            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices. Defaults to 'zeros'.
             mode ('nearest'|'bilinear'): interpolation order. Defaults to 'bilinear'.
         """
-        spatial_size = spatial_size or self.spatial_size
-        mode = mode or self.mode
-        grid = self.affine_grid(spatial_size=spatial_size)
-        return self.resampler(img=img, grid=grid, mode=mode)
+        grid = self.affine_grid(spatial_size=spatial_size or self.spatial_size)
+        return self.resampler(
+            img=img, grid=grid, padding_mode=padding_mode or self.padding_mode, mode=mode or self.mode
+        )
 
 
 class RandAffine(Randomizable, Transform):
@@ -923,9 +943,10 @@ class RandAffine(Randomizable, Transform):
             as_tensor_output=True,
             device=device,
         )
-        self.resampler = Resample(padding_mode=padding_mode, as_tensor_output=as_tensor_output, device=device)
+        self.resampler = Resample(as_tensor_output=as_tensor_output, device=device)
 
         self.spatial_size = spatial_size
+        self.padding_mode = padding_mode
         self.mode = mode
 
         self.do_transform = False
@@ -940,23 +961,25 @@ class RandAffine(Randomizable, Transform):
         self.do_transform = self.R.rand() < self.prob
         self.rand_affine_grid.randomize()
 
-    def __call__(self, img, spatial_size=None, mode=None):
+    def __call__(self, img, spatial_size=None, padding_mode=None, mode=None):
         """
         Args:
             img (ndarray or tensor): shape must be (num_channels, H, W[, D]),
             spatial_size (list or tuple of int): output image spatial size.
                 if `img` has two spatial dimensions, `spatial_size` should have 2 elements [h, w].
                 if `img` has three spatial dimensions, `spatial_size` should have 3 elements [h, w, d].
+            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices. Defaults to 'zeros'.
             mode ('nearest'|'bilinear'): interpolation order. Defaults to 'bilinear'.
         """
         self.randomize()
-        spatial_size = spatial_size or self.spatial_size
-        mode = mode or self.mode
+        _spatial_size = spatial_size or self.spatial_size
         if self.do_transform:
-            grid = self.rand_affine_grid(spatial_size=spatial_size)
+            grid = self.rand_affine_grid(spatial_size=_spatial_size)
         else:
-            grid = create_grid(spatial_size)
-        return self.resampler(img=img, grid=grid, mode=mode)
+            grid = create_grid(_spatial_size)
+        return self.resampler(
+            img=img, grid=grid, padding_mode=padding_mode or self.padding_mode, mode=mode or self.mode
+        )
 
 
 class Rand2DElastic(Randomizable, Transform):
@@ -1010,9 +1033,10 @@ class Rand2DElastic(Randomizable, Transform):
             as_tensor_output=True,
             device=device,
         )
-        self.resampler = Resample(padding_mode=padding_mode, as_tensor_output=as_tensor_output, device=device)
+        self.resampler = Resample(as_tensor_output=as_tensor_output, device=device)
 
         self.spatial_size = spatial_size
+        self.padding_mode = padding_mode
         self.mode = mode
         self.prob = prob
         self.do_transform = False
@@ -1028,23 +1052,24 @@ class Rand2DElastic(Randomizable, Transform):
         self.deform_grid.randomize(spatial_size)
         self.rand_affine_grid.randomize()
 
-    def __call__(self, img, spatial_size=None, mode=None):
+    def __call__(self, img, spatial_size=None, padding_mode=None, mode=None):
         """
         Args:
             img (ndarray or tensor): shape must be (num_channels, H, W),
             spatial_size (2 ints): specifying output image spatial size [h, w].
+            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices.
+                Defaults to ``'zeros'``.
             mode ('nearest'|'bilinear'): interpolation order. Defaults to ``self.mode``.
         """
         spatial_size = spatial_size or self.spatial_size
         self.randomize(spatial_size)
-        mode = mode or self.mode
         if self.do_transform:
             grid = self.deform_grid(spatial_size=spatial_size)
             grid = self.rand_affine_grid(grid=grid)
             grid = torch.nn.functional.interpolate(grid[None], spatial_size, mode="bicubic", align_corners=False)[0]
         else:
             grid = create_grid(spatial_size)
-        return self.resampler(img, grid, mode)
+        return self.resampler(img, grid, padding_mode=padding_mode or self.padding_mode, mode=mode or self.mode)
 
 
 class Rand3DElastic(Randomizable, Transform):
@@ -1089,11 +1114,12 @@ class Rand3DElastic(Randomizable, Transform):
             - :py:class:`Affine` for the affine transformation parameters configurations.
         """
         self.rand_affine_grid = RandAffineGrid(rotate_range, shear_range, translate_range, scale_range, True, device)
-        self.resampler = Resample(padding_mode=padding_mode, as_tensor_output=as_tensor_output, device=device)
+        self.resampler = Resample(as_tensor_output=as_tensor_output, device=device)
 
         self.sigma_range = sigma_range
         self.magnitude_range = magnitude_range
         self.spatial_size = spatial_size
+        self.padding_mode = padding_mode
         self.mode = mode
         self.device = device
 
@@ -1116,15 +1142,16 @@ class Rand3DElastic(Randomizable, Transform):
         self.sigma = self.R.uniform(self.sigma_range[0], self.sigma_range[1])
         self.rand_affine_grid.randomize()
 
-    def __call__(self, img, spatial_size=None, mode=None):
+    def __call__(self, img, spatial_size=None, padding_mode=None, mode=None):
         """
         Args:
             img (ndarray or tensor): shape must be (num_channels, H, W, D),
             spatial_size (3 ints): specifying spatial 3D output image spatial size [h, w, d].
+            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices.
+                Defaults to ``'zeros'``.
             mode ('nearest'|'bilinear'): interpolation order. Defaults to 'self.mode'.
         """
         spatial_size = spatial_size or self.spatial_size
-        mode = mode or self.mode
         self.randomize(spatial_size)
         grid = create_grid(spatial_size)
         if self.do_transform:
@@ -1132,4 +1159,4 @@ class Rand3DElastic(Randomizable, Transform):
             gaussian = GaussianFilter(3, self.sigma, 3.0).to(device=self.device)
             grid[:3] += gaussian(self.rand_offset[None])[0] * self.magnitude
             grid = self.rand_affine_grid(grid=grid)
-        return self.resampler(img, grid, mode)
+        return self.resampler(img, grid, padding_mode=self.padding_mode, mode=mode or self.mode)
diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py
index 0cab421a..21e6dffc 100644
--- a/monai/transforms/spatial/dictionary.py
+++ b/monai/transforms/spatial/dictionary.py
@@ -32,7 +32,7 @@ from monai.transforms.spatial.array import (
     Zoom,
 )
 from monai.transforms.utils import create_grid
-from monai.utils.misc import ensure_tuple
+from monai.utils.misc import ensure_tuple_rep
 
 
 class Spacingd(MapTransform):
@@ -50,7 +50,7 @@ class Spacingd(MapTransform):
     """
 
     def __init__(
-        self, keys, pixdim, diagonal=False, mode="nearest", cval=0, interp_order=3, dtype=None, meta_key_format="{}.{}"
+        self, keys, pixdim, diagonal=False, interp_order=3, mode="nearest", cval=0, dtype=None, meta_key_format="{}.{}"
     ):
         """
         Args:
@@ -67,30 +67,40 @@ class Spacingd(MapTransform):
                 translations components from the original affine will be
                 preserved in the target affine. This option will not flip/swap
                 axes against the original ones.
-            mode (`reflect|constant|nearest|mirror|wrap`):
-                The mode parameter determines how the input array is extended beyond its boundaries.
-                Default is 'nearest'.
-            cval (scalar): Value to fill past edges of input if mode is "constant". Default is 0.0.
             interp_order (int or sequence of ints): int: the same interpolation order
                 for all data indexed by `self.keys`; sequence of ints, should
                 correspond to an interpolation order for each data item indexed
                 by `self.keys` respectively.
-            dtype (None or np.dtype): output array data type, defaults to None to use input data's dtype.
+            mode (str or sequence of str):
+                Available options are `reflect|constant|nearest|mirror|wrap`.
+                The mode parameter determines how the input array is extended beyond its boundaries.
+                Default is 'nearest'.
+            cval (scalar or sequence of scalars): Value to fill past edges of input if mode is "constant". Default is 0.0.
+            dtype (None or np.dtype or sequence of np.dtype): output array data type, defaults to None to use input data's dtype.
             meta_key_format (str): key format to read/write affine matrices to the data dictionary.
         """
         super().__init__(keys)
-        self.spacing_transform = Spacing(pixdim, diagonal=diagonal, mode=mode, cval=cval, dtype=dtype)
-        interp_order = ensure_tuple(interp_order)
-        self.interp_order = interp_order if len(interp_order) == len(self.keys) else interp_order * len(self.keys)
+        self.spacing_transform = Spacing(pixdim, diagonal=diagonal)
+        self.interp_order = ensure_tuple_rep(interp_order, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.cval = ensure_tuple_rep(cval, len(self.keys))
+        self.dtype = ensure_tuple_rep(dtype, len(self.keys))
         self.meta_key_format = meta_key_format
 
     def __call__(self, data):
         d = dict(data)
-        for key, interp in zip(self.keys, self.interp_order):
+        for idx, key in enumerate(self.keys):
             affine_key = self.meta_key_format.format(key, "affine")
             # resample array of each corresponding key
             # using affine fetched from d[affine_key]
-            d[key], _, new_affine = self.spacing_transform(data_array=d[key], affine=d[affine_key], interp_order=interp)
+            d[key], _, new_affine = self.spacing_transform(
+                data_array=d[key],
+                affine=d[affine_key],
+                interp_order=self.interp_order[idx],
+                mode=self.mode[idx],
+                cval=self.cval[idx],
+                dtype=self.dtype[idx],
+            )
             # set the 'affine' key
             d[affine_key] = new_affine
         return d
@@ -212,37 +222,43 @@ class Resized(MapTransform):
         keys (hashable items): keys of the corresponding items to be transformed.
             See also: :py:class:`monai.transforms.compose.MapTransform`
         spatial_size (tuple or list): expected shape of spatial dimensions after resize operation.
-        order (int): Order of spline interpolation. Default=1.
-        mode (str): Points outside boundaries are filled according to given mode.
+        order (int or sequence of int): Order of spline interpolation. Default=1.
+        mode (str or sequence of str): Points outside boundaries are filled according to given mode.
             Options are 'constant', 'edge', 'symmetric', 'reflect', 'wrap'.
-        cval (float): Used with mode 'constant', the value outside image boundaries.
-        clip (bool): Whether to clip range of output values after interpolation. Default: True.
-        preserve_range (bool): Whether to keep original range of values. Default is True.
+        cval (float or sequence of float): Used with mode 'constant', the value outside image boundaries.
+        clip (bool or sequence of bool): Whether to clip range of output values after interpolation. Default: True.
+        preserve_range (bool or sequence of bool): Whether to keep original range of values. Default is True.
             If False, input is converted according to conventions of img_as_float. See
             https://scikit-image.org/docs/dev/user_guide/data_types.html.
-        anti_aliasing (bool): Whether to apply a gaussian filter to image before down-scaling. Default is True.
-        anti_aliasing_sigma (float, tuple of floats): Standard deviation for gaussian filtering.
+        anti_aliasing (bool or sequence of bool): Whether to apply a gaussian filter to image before down-scaling.
+            Default is True.
     """
 
     def __init__(
-        self,
-        keys,
-        spatial_size,
-        order=1,
-        mode="reflect",
-        cval=0,
-        clip=True,
-        preserve_range=True,
-        anti_aliasing=True,
-        anti_aliasing_sigma=None,
+        self, keys, spatial_size, order=1, mode="reflect", cval=0, clip=True, preserve_range=True, anti_aliasing=True,
     ):
         super().__init__(keys)
-        self.resizer = Resize(spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing, anti_aliasing_sigma)
+        self.order = ensure_tuple_rep(order, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.cval = ensure_tuple_rep(cval, len(self.keys))
+        self.clip = ensure_tuple_rep(clip, len(self.keys))
+        self.preserve_range = ensure_tuple_rep(preserve_range, len(self.keys))
+        self.anti_aliasing = ensure_tuple_rep(anti_aliasing, len(self.keys))
+
+        self.resizer = Resize(spatial_size=spatial_size)
 
     def __call__(self, data):
         d = dict(data)
-        for key in self.keys:
-            d[key] = self.resizer(d[key])
+        for idx, key in enumerate(self.keys):
+            d[key] = self.resizer(
+                d[key],
+                order=self.order[idx],
+                mode=self.mode[idx],
+                cval=self.cval[idx],
+                clip=self.clip[idx],
+                preserve_range=self.preserve_range[idx],
+                anti_aliasing=self.anti_aliasing[idx],
+            )
         return d
 
 
@@ -273,11 +289,12 @@ class RandAffined(Randomizable, MapTransform):
                 if ``data`` component has three spatial dimensions, ``spatial_size`` should have 3 elements [h, w, d].
             prob (float): probability of returning a randomized affine grid.
                 defaults to 0.1, with 10% chance returns a randomized grid.
-            mode ('nearest'|'bilinear'): interpolation order. Defaults to ``'bilinear'``.
+            mode (str or sequence of str): interpolation order.
+                Available options are 'nearest', 'bilinear'. Defaults to ``'bilinear'``.
                 if mode is a tuple of interpolation mode strings, each string corresponds to a key in ``keys``.
                 this is useful to set different modes for different data items.
-            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices.
-                Defaults to ``'zeros'``.
+            padding_mode (str or sequence of str): mode of handling out of range indices.
+                Available options are 'zeros', 'border', 'reflection'.  Defaults to ``'zeros'``.
             as_tensor_output (bool): the computation is implemented using pytorch tensors, this option specifies
                 whether to convert it back to numpy arrays.
             device (torch.device): device on which the tensor will be allocated.
@@ -287,7 +304,6 @@ class RandAffined(Randomizable, MapTransform):
             - :py:class:`RandAffineGrid` for the random affine parameters configurations.
         """
         super().__init__(keys)
-        default_mode = "bilinear" if isinstance(mode, (tuple, list)) else mode
         self.rand_affine = RandAffine(
             prob=prob,
             rotate_range=rotate_range,
@@ -295,12 +311,11 @@ class RandAffined(Randomizable, MapTransform):
             translate_range=translate_range,
             scale_range=scale_range,
             spatial_size=spatial_size,
-            mode=default_mode,
-            padding_mode=padding_mode,
             as_tensor_output=as_tensor_output,
             device=device,
         )
-        self.mode = mode
+        self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
 
     def set_random_state(self, seed=None, state=None):
         self.rand_affine.set_random_state(seed, state)
@@ -318,15 +333,10 @@ class RandAffined(Randomizable, MapTransform):
         if self.rand_affine.do_transform:
             grid = self.rand_affine.rand_affine_grid(spatial_size=spatial_size)
         else:
-            grid = create_grid(spatial_size)
+            grid = create_grid(spatial_size=spatial_size)
 
-        if isinstance(self.mode, (tuple, list)):
-            for key, m in zip(self.keys, self.mode):
-                d[key] = self.rand_affine.resampler(d[key], grid, mode=m)
-            return d
-
-        for key in self.keys:  # same interpolation mode
-            d[key] = self.rand_affine.resampler(d[key], grid, self.rand_affine.mode)
+        for idx, key in enumerate(self.keys):
+            d[key] = self.rand_affine.resampler(d[key], grid, padding_mode=self.padding_mode[idx], mode=self.mode[idx])
         return d
 
 
@@ -361,11 +371,12 @@ class Rand2DElasticd(Randomizable, MapTransform):
             prob (float): probability of returning a randomized affine grid.
                 defaults to 0.1, with 10% chance returns a randomized grid,
                 otherwise returns a ``spatial_size`` centered area extracted from the input image.
-            mode ('nearest'|'bilinear'): interpolation order. Defaults to ``'bilinear'``.
+            mode (str or sequence of str): interpolation order.
+                Available options are 'nearest', 'bilinear'. Defaults to ``'bilinear'``.
                 if mode is a tuple of interpolation mode strings, each string corresponds to a key in ``keys``.
                 this is useful to set different modes for different data items.
-            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices.
-                Defaults to ``'zeros'``.
+            padding_mode (str or sequence of str): mode of handling out of range indices.
+                Available options are 'zeros', 'border', 'reflection'.  Defaults to ``'zeros'``.
             as_tensor_output (bool): the computation is implemented using pytorch tensors, this option specifies
                 whether to convert it back to numpy arrays.
             device (torch.device): device on which the tensor will be allocated.
@@ -374,7 +385,6 @@ class Rand2DElasticd(Randomizable, MapTransform):
             - :py:class:`Affine` for the affine transformation parameters configurations.
         """
         super().__init__(keys)
-        default_mode = "bilinear" if isinstance(mode, (tuple, list)) else mode
         self.rand_2d_elastic = Rand2DElastic(
             spacing=spacing,
             magnitude_range=magnitude_range,
@@ -384,12 +394,11 @@ class Rand2DElasticd(Randomizable, MapTransform):
             translate_range=translate_range,
             scale_range=scale_range,
             spatial_size=spatial_size,
-            mode=default_mode,
-            padding_mode=padding_mode,
             as_tensor_output=as_tensor_output,
             device=device,
         )
-        self.mode = mode
+        self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
 
     def set_random_state(self, seed=None, state=None):
         self.rand_2d_elastic.set_random_state(seed, state)
@@ -411,13 +420,10 @@ class Rand2DElasticd(Randomizable, MapTransform):
         else:
             grid = create_grid(spatial_size)
 
-        if isinstance(self.mode, (tuple, list)):
-            for key, m in zip(self.keys, self.mode):
-                d[key] = self.rand_2d_elastic.resampler(d[key], grid, mode=m)
-            return d
-
-        for key in self.keys:  # same interpolation mode
-            d[key] = self.rand_2d_elastic.resampler(d[key], grid, mode=self.rand_2d_elastic.mode)
+        for idx, key in enumerate(self.keys):
+            d[key] = self.rand_2d_elastic.resampler(
+                d[key], grid, padding_mode=self.padding_mode[idx], mode=self.mode[idx]
+            )
         return d
 
 
@@ -453,11 +459,12 @@ class Rand3DElasticd(Randomizable, MapTransform):
             prob (float): probability of returning a randomized affine grid.
                 defaults to 0.1, with 10% chance returns a randomized grid,
                 otherwise returns a ``spatial_size`` centered area extracted from the input image.
-            mode ('nearest'|'bilinear'): interpolation order. Defaults to ``'bilinear'``.
+            mode (str or sequence of str): interpolation order.
+                Available options are 'nearest', 'bilinear'. Defaults to ``'bilinear'``.
                 if mode is a tuple of interpolation mode strings, each string corresponds to a key in ``keys``.
                 this is useful to set different modes for different data items.
-            padding_mode ('zeros'|'border'|'reflection'): mode of handling out of range indices.
-                Defaults to ``'zeros'``.
+            padding_mode (str or sequence of str): mode of handling out of range indices.
+                Available options are 'zeros', 'border', 'reflection'.  Defaults to ``'zeros'``.
             as_tensor_output (bool): the computation is implemented using pytorch tensors, this option specifies
                 whether to convert it back to numpy arrays.
             device (torch.device): device on which the tensor will be allocated.
@@ -466,7 +473,6 @@ class Rand3DElasticd(Randomizable, MapTransform):
             - :py:class:`Affine` for the affine transformation parameters configurations.
         """
         super().__init__(keys)
-        default_mode = "bilinear" if isinstance(mode, (tuple, list)) else mode
         self.rand_3d_elastic = Rand3DElastic(
             sigma_range=sigma_range,
             magnitude_range=magnitude_range,
@@ -476,12 +482,11 @@ class Rand3DElasticd(Randomizable, MapTransform):
             translate_range=translate_range,
             scale_range=scale_range,
             spatial_size=spatial_size,
-            mode=default_mode,
-            padding_mode=padding_mode,
             as_tensor_output=as_tensor_output,
             device=device,
         )
-        self.mode = mode
+        self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
 
     def set_random_state(self, seed=None, state=None):
         self.rand_3d_elastic.set_random_state(seed, state)
@@ -503,13 +508,10 @@ class Rand3DElasticd(Randomizable, MapTransform):
             grid[:3] += gaussian(self.rand_3d_elastic.rand_offset[None])[0] * self.rand_3d_elastic.magnitude
             grid = self.rand_3d_elastic.rand_affine_grid(grid=grid)
 
-        if isinstance(self.mode, (tuple, list)):
-            for key, m in zip(self.keys, self.mode):
-                d[key] = self.rand_3d_elastic.resampler(d[key], grid, mode=m)
-            return d
-
-        for key in self.keys:  # same interpolation mode
-            d[key] = self.rand_3d_elastic.resampler(d[key], grid, mode=self.rand_3d_elastic.mode)
+        for idx, key in enumerate(self.keys):
+            d[key] = self.rand_3d_elastic.resampler(
+                d[key], grid, padding_mode=self.padding_mode[idx], mode=self.mode[idx]
+            )
         return d
 
 
@@ -577,32 +579,31 @@ class Rotated(MapTransform):
             This is the first two axis in spatial dimensions.
         reshape (bool): If reshape is true, the output shape is adapted so that the
             input array is contained completely in the output. Default is True.
-        order (int): Order of spline interpolation. Range 0-5. Default: 1. This is
+        order (int or sequence of int): Order of spline interpolation. Range 0-5. Default: 1. This is
             different from scipy where default interpolation is 3.
-        mode (str): Points outside boundary filled according to this mode. Options are
+        mode (str or sequence of str): Points outside boundary filled according to this mode. Options are
             'constant', 'nearest', 'reflect', 'wrap'. Default: 'constant'.
-        cval (scalar): Values to fill outside boundary. Default: 0.
-        prefilter (bool): Apply spline_filter before interpolation. Default: True.
+        cval (scalar or sequence of scalar): Values to fill outside boundary. Default: 0.
+        prefilter (bool or sequence of bool): Apply spline_filter before interpolation. Default: True.
     """
 
     def __init__(
         self, keys, angle, spatial_axes=(0, 1), reshape=True, order=1, mode="constant", cval=0, prefilter=True
     ):
         super().__init__(keys)
-        self.rotator = Rotate(
-            angle=angle,
-            spatial_axes=spatial_axes,
-            reshape=reshape,
-            order=order,
-            mode=mode,
-            cval=cval,
-            prefilter=prefilter,
-        )
+        self.rotator = Rotate(angle=angle, spatial_axes=spatial_axes, reshape=reshape)
+
+        self.order = ensure_tuple_rep(order, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.cval = ensure_tuple_rep(cval, len(self.keys))
+        self.prefilter = ensure_tuple_rep(prefilter, len(self.keys))
 
     def __call__(self, data):
         d = dict(data)
-        for key in self.keys:
-            d[key] = self.rotator(d[key])
+        for idx, key in enumerate(self.keys):
+            d[key] = self.rotator(
+                d[key], order=self.order[idx], mode=self.mode[idx], cval=self.cval[idx], prefilter=self.prefilter[idx]
+            )
         return d
 
 
@@ -618,12 +619,12 @@ class RandRotated(Randomizable, MapTransform):
             This is the first two axis in spatial dimensions.
         reshape (bool): If reshape is true, the output shape is adapted so that the
             input array is contained completely in the output. Default is True.
-        order (int): Order of spline interpolation. Range 0-5. Default: 1. This is
+        order (int or sequence of int): Order of spline interpolation. Range 0-5. Default: 1. This is
             different from scipy where default interpolation is 3.
-        mode (str): Points outside boundary filled according to this mode. Options are
+        mode (str or sequence of str): Points outside boundary filled according to this mode. Options are
             'constant', 'nearest', 'reflect', 'wrap'. Default: 'constant'.
-        cval (scalar): Value to fill outside boundary. Default: 0.
-        prefilter (bool): Apply spline_filter before interpolation. Default: True.
+        cval (scalar or sequence of scalar): Value to fill outside boundary. Default: 0.
+        prefilter (bool or sequence of bool): Apply spline_filter before interpolation. Default: True.
     """
 
     def __init__(
@@ -642,12 +643,13 @@ class RandRotated(Randomizable, MapTransform):
         self.prob = prob
         self.degrees = degrees
         self.reshape = reshape
-        self.order = order
-        self.mode = mode
-        self.cval = cval
-        self.prefilter = prefilter
         self.spatial_axes = spatial_axes
 
+        self.order = ensure_tuple_rep(order, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.cval = ensure_tuple_rep(cval, len(self.keys))
+        self.prefilter = ensure_tuple_rep(prefilter, len(self.keys))
+
         if not hasattr(self.degrees, "__iter__"):
             self.degrees = (-self.degrees, self.degrees)
         assert len(self.degrees) == 2, "degrees should be a number or pair of numbers."
@@ -664,9 +666,11 @@ class RandRotated(Randomizable, MapTransform):
         d = dict(data)
         if not self._do_transform:
             return d
-        rotator = Rotate(self.angle, self.spatial_axes, self.reshape, self.order, self.mode, self.cval, self.prefilter)
-        for key in self.keys:
-            d[key] = rotator(d[key])
+        rotator = Rotate(angle=self.angle, spatial_axes=self.spatial_axes, reshape=self.reshape)
+        for idx, key in enumerate(self.keys):
+            d[key] = rotator(
+                d[key], order=self.order[idx], mode=self.mode[idx], cval=self.cval[idx], prefilter=self.prefilter[idx]
+            )
         return d
 
 
@@ -677,9 +681,10 @@ class Zoomd(MapTransform):
         zoom (float or sequence): The zoom factor along the spatial axes.
             If a float, zoom is the same for each spatial axis.
             If a sequence, zoom should contain one value for each spatial axis.
-        order (int): order of interpolation. Default=3.
-        mode (str): Determines how input is extended beyond boundaries. Default is 'constant'.
-        cval (scalar, optional): Value to fill past edges. Default is 0.
+        order (int or sequence of int): order of interpolation. Default=3.
+        mode (str or sequence of str): Determines how input is extended beyond boundaries. Default is 'constant'.
+        cval (scalar or sequence of scalar): Value to fill past edges. Default is 0.
+        prefilter (bool or sequence of bool): Apply spline_filter before interpolation. Default: True.
         use_gpu (bool): Should use cpu or gpu. Uses cupyx which doesn't support order > 1 and modes
             'wrap' and 'reflect'. Defaults to cpu for these cases or if cupyx not found.
         keep_size (bool): Should keep original size (pad if needed).
@@ -687,14 +692,19 @@ class Zoomd(MapTransform):
 
     def __init__(self, keys, zoom, order=3, mode="constant", cval=0, prefilter=True, use_gpu=False, keep_size=False):
         super().__init__(keys)
-        self.zoomer = Zoom(
-            zoom=zoom, order=order, mode=mode, cval=cval, prefilter=prefilter, use_gpu=use_gpu, keep_size=keep_size
-        )
+        self.zoomer = Zoom(zoom=zoom, use_gpu=use_gpu, keep_size=keep_size)
+
+        self.order = ensure_tuple_rep(order, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.cval = ensure_tuple_rep(cval, len(self.keys))
+        self.prefilter = ensure_tuple_rep(prefilter, len(self.keys))
 
     def __call__(self, data):
         d = dict(data)
-        for key in self.keys:
-            d[key] = self.zoomer(d[key])
+        for idx, key in enumerate(self.keys):
+            d[key] = self.zoomer(
+                d[key], order=self.order[idx], mode=self.mode[idx], cval=self.cval[idx], prefilter=self.prefilter[idx]
+            )
         return d
 
 
@@ -710,10 +720,11 @@ class RandZoomd(Randomizable, MapTransform):
         max_zoom (float or sequence): Max zoom factor. Can be float or sequence same size as image.
             If a float, max_zoom is the same for each spatial axis.
             If a sequence, max_zoom should contain one value for each spatial axis.
-        order (int): order of interpolation. Default=3.
-        mode ('reflect', 'constant', 'nearest', 'mirror', 'wrap'): Determines how input is
-            extended beyond boundaries. Default: 'constant'.
-        cval (scalar, optional): Value to fill past edges. Default is 0.
+        order (int or sequence of int): order of interpolation. Default=3.
+        mode (str or sequence of str): Available options are 'reflect', 'constant', 'nearest', 'mirror', 'wrap'.
+            Determines how input is extended beyond boundaries. Default: 'constant'.
+        cval (scalar or sequence of scalar): Value to fill past edges. Default is 0.
+        prefilter (bool or sequence of bool): Apply spline_filter before interpolation. Default: True.
         use_gpu (bool): Should use cpu or gpu. Uses cupyx which doesn't support order > 1 and modes
             'wrap' and 'reflect'. Defaults to cpu for these cases or if cupyx not found.
         keep_size (bool): Should keep original size (pad if needed).
@@ -738,13 +749,14 @@ class RandZoomd(Randomizable, MapTransform):
         self.min_zoom = min_zoom
         self.max_zoom = max_zoom
         self.prob = prob
-        self.order = order
-        self.mode = mode
-        self.cval = cval
-        self.prefilter = prefilter
         self.use_gpu = use_gpu
         self.keep_size = keep_size
 
+        self.order = ensure_tuple_rep(order, len(self.keys))
+        self.mode = ensure_tuple_rep(mode, len(self.keys))
+        self.cval = ensure_tuple_rep(cval, len(self.keys))
+        self.prefilter = ensure_tuple_rep(prefilter, len(self.keys))
+
         self._do_transform = False
         self._zoom = None
 
@@ -760,9 +772,11 @@ class RandZoomd(Randomizable, MapTransform):
         d = dict(data)
         if not self._do_transform:
             return d
-        zoomer = Zoom(self._zoom, self.order, self.mode, self.cval, self.prefilter, self.use_gpu, self.keep_size)
-        for key in self.keys:
-            d[key] = zoomer(d[key])
+        zoomer = Zoom(self._zoom, use_gpu=self.use_gpu, keep_size=self.keep_size)
+        for idx, key in enumerate(self.keys):
+            d[key] = zoomer(
+                d[key], order=self.order[idx], mode=self.mode[idx], cval=self.cval[idx], prefilter=self.prefilter[idx],
+            )
         return d
 
 


Test patch:
diff --git a/tests/test_rand_zoom.py b/tests/test_rand_zoom.py
index 0111c2c1..ea6f4833 100644
--- a/tests/test_rand_zoom.py
+++ b/tests/test_rand_zoom.py
@@ -78,7 +78,7 @@ class TestRandZoom(NumpyImageTestCase2D):
         zoomed = random_zoom(self.imt[0])
         self.assertTrue(np.array_equal(zoomed.shape, self.imt.shape[1:]))
 
-    @parameterized.expand([("no_min_zoom", None, 1.1, 1, TypeError), ("invalid_order", 0.9, 1.1, "s", AssertionError)])
+    @parameterized.expand([("no_min_zoom", None, 1.1, 1, TypeError), ("invalid_order", 0.9, 1.1, "s", TypeError)])
     def test_invalid_inputs(self, _, min_zoom, max_zoom, order, raises):
         with self.assertRaises(raises):
             random_zoom = RandZoom(prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, order=order)
diff --git a/tests/test_rand_zoomd.py b/tests/test_rand_zoomd.py
index acdb6c80..dacd0c75 100644
--- a/tests/test_rand_zoomd.py
+++ b/tests/test_rand_zoomd.py
@@ -83,7 +83,7 @@ class TestRandZoomd(NumpyImageTestCase2D):
         zoomed = random_zoom({key: self.imt[0]})
         self.assertTrue(np.array_equal(zoomed[key].shape, self.imt.shape[1:]))
 
-    @parameterized.expand([("no_min_zoom", None, 1.1, 1, TypeError), ("invalid_order", 0.9, 1.1, "s", AssertionError)])
+    @parameterized.expand([("no_min_zoom", None, 1.1, 1, TypeError), ("invalid_order", 0.9, 1.1, "s", TypeError)])
     def test_invalid_inputs(self, _, min_zoom, max_zoom, order, raises):
         key = "img"
         with self.assertRaises(raises):
diff --git a/tests/test_resize.py b/tests/test_resize.py
index bb5e6145..133a9194 100644
--- a/tests/test_resize.py
+++ b/tests/test_resize.py
@@ -20,23 +20,28 @@ from tests.utils import NumpyImageTestCase2D
 
 
 class TestResize(NumpyImageTestCase2D):
-    @parameterized.expand([("invalid_order", "order", AssertionError)])
-    def test_invalid_inputs(self, _, order, raises):
-        with self.assertRaises(raises):
-            resize = Resize(spatial_size=(128, 128, 3), order=order)
+    def test_invalid_inputs(self):
+        with self.assertRaises(TypeError):
+            resize = Resize(spatial_size=(128, 128, 3), order="order")
             resize(self.imt[0])
 
     @parameterized.expand(
         [
-            ((64, 64), 1, "reflect", 0, True, True, True, None),
-            ((32, 32), 2, "constant", 3, False, False, False, None),
-            ((256, 256), 3, "constant", 3, False, False, False, None),
+            ((64, 64), 1, "reflect", 0, True, True, True),
+            ((32, 32), 2, "constant", 3, False, False, False),
+            ((256, 256), 3, "constant", 3, False, False, False),
         ]
     )
-    def test_correct_results(
-        self, spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing, anti_aliasing_sigma
-    ):
-        resize = Resize(spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing, anti_aliasing_sigma)
+    def test_correct_results(self, spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing):
+        resize = Resize(
+            spatial_size,
+            order=order,
+            mode=mode,
+            cval=cval,
+            clip=clip,
+            preserve_range=preserve_range,
+            anti_aliasing=anti_aliasing,
+        )
         expected = list()
         for channel in self.imt[0]:
             expected.append(
@@ -49,7 +54,6 @@ class TestResize(NumpyImageTestCase2D):
                     clip=clip,
                     preserve_range=preserve_range,
                     anti_aliasing=anti_aliasing,
-                    anti_aliasing_sigma=anti_aliasing_sigma,
                 )
             )
         expected = np.stack(expected).astype(np.float32)
diff --git a/tests/test_resized.py b/tests/test_resized.py
index 90ce2581..cefbe9bc 100644
--- a/tests/test_resized.py
+++ b/tests/test_resized.py
@@ -20,25 +20,20 @@ from tests.utils import NumpyImageTestCase2D
 
 
 class TestResized(NumpyImageTestCase2D):
-    @parameterized.expand([("invalid_order", "order", AssertionError)])
-    def test_invalid_inputs(self, _, order, raises):
-        with self.assertRaises(raises):
-            resize = Resized(keys="img", spatial_size=(128, 128, 3), order=order)
+    def test_invalid_inputs(self):
+        with self.assertRaises(TypeError):
+            resize = Resized(keys="img", spatial_size=(128, 128, 3), order="order")
             resize({"img": self.imt[0]})
 
     @parameterized.expand(
         [
-            ((64, 64), 1, "reflect", 0, True, True, True, None),
-            ((32, 32), 2, "constant", 3, False, False, False, None),
-            ((256, 256), 3, "constant", 3, False, False, False, None),
+            ((64, 64), 1, "reflect", 0, True, True, True),
+            ((32, 32), 2, "constant", 3, False, False, False),
+            ((256, 256), 3, "constant", 3, False, False, False),
         ]
     )
-    def test_correct_results(
-        self, spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing, anti_aliasing_sigma
-    ):
-        resize = Resized(
-            "img", spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing, anti_aliasing_sigma
-        )
+    def test_correct_results(self, spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing):
+        resize = Resized("img", spatial_size, order, mode, cval, clip, preserve_range, anti_aliasing)
         expected = list()
         for channel in self.imt[0]:
             expected.append(
@@ -51,7 +46,6 @@ class TestResized(NumpyImageTestCase2D):
                     clip=clip,
                     preserve_range=preserve_range,
                     anti_aliasing=anti_aliasing,
-                    anti_aliasing_sigma=anti_aliasing_sigma,
                 )
             )
         expected = np.stack(expected).astype(np.float32)
diff --git a/tests/test_spatial_pad.py b/tests/test_spatial_pad.py
index 4ccc309b..b6b4369e 100644
--- a/tests/test_spatial_pad.py
+++ b/tests/test_spatial_pad.py
@@ -33,6 +33,8 @@ class TestSpatialPad(unittest.TestCase):
         padder = SpatialPad(**input_param)
         result = padder(input_data)
         self.assertAlmostEqual(result.shape, expected_val.shape)
+        result = padder(input_data, mode=input_param["mode"])
+        self.assertAlmostEqual(result.shape, expected_val.shape)
 
 
 if __name__ == "__main__":
diff --git a/tests/test_spatial_padd.py b/tests/test_spatial_padd.py
index a885e982..bf947c23 100644
--- a/tests/test_spatial_padd.py
+++ b/tests/test_spatial_padd.py
@@ -26,9 +26,15 @@ TEST_CASE_2 = [
     np.zeros((3, 15, 8, 8)),
 ]
 
+TEST_CASE_3 = [
+    {"keys": ["img"], "spatial_size": [15, 8, 8], "method": "end", "mode": {"constant", "symmetric"}},
+    {"img": np.zeros((3, 8, 8, 4))},
+    np.zeros((3, 15, 8, 8)),
+]
+
 
 class TestSpatialPadd(unittest.TestCase):
-    @parameterized.expand([TEST_CASE_1, TEST_CASE_2])
+    @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
     def test_pad_shape(self, input_param, input_data, expected_val):
         padder = SpatialPadd(**input_param)
         result = padder(input_data)
diff --git a/tests/test_zoom.py b/tests/test_zoom.py
index bbec43af..a02e8f9f 100644
--- a/tests/test_zoom.py
+++ b/tests/test_zoom.py
@@ -28,7 +28,7 @@ VALID_CASES = [
 
 GPU_CASES = [("gpu_zoom", 0.6, 1, "constant", 0, True)]
 
-INVALID_CASES = [("no_zoom", None, 1, TypeError), ("invalid_order", 0.9, "s", AssertionError)]
+INVALID_CASES = [("no_zoom", None, 1, TypeError), ("invalid_order", 0.9, "s", TypeError)]
 
 
 class TestZoom(NumpyImageTestCase2D):
diff --git a/tests/test_zoomd.py b/tests/test_zoomd.py
index 1008c2a3..2ce3c87e 100644
--- a/tests/test_zoomd.py
+++ b/tests/test_zoomd.py
@@ -28,7 +28,7 @@ VALID_CASES = [
 
 GPU_CASES = [("gpu_zoom", 0.6, 1, "constant", 0, True)]
 
-INVALID_CASES = [("no_zoom", None, 1, TypeError), ("invalid_order", 0.9, "s", AssertionError)]
+INVALID_CASES = [("no_zoom", None, 1, TypeError), ("invalid_order", 0.9, "s", TypeError)]
 
 
 class TestZoomd(NumpyImageTestCase2D):