Repository: Project-MONAI/MONAI

Issue:
RandZoomd align_corners incorrect
align_corners is handled incorrectly in RandZoomd (and possibly bugs in other transforms)

Example:
`RandZoomd(keys=['image'], prob=0.5, min_zoom=0.9, max_zoom=1.1, mode=['trilinear'], align_corners=[False])`

Will call Zoom transforms with mode = trilinear, but align_corners=None.  Which will also show a lot of pytorch Warnings indicating that align_corners is not set. 

Root cause:
https://github.com/Project-MONAI/MONAI/blob/42159ffd3a3003efdee807683922ea1cec4cb27a/monai/transforms/spatial/array.py#L1279

when calling Zoom transform, for some reason we use parameters as 

```
mode=look_up_option(mode or self.mode, InterpolateMode),
align_corners=align_corners or self.align_corners,
```

The use of 'or' is very ambiguous and leads to a bug in this case. 

A or B ->  returns A (if both A and B are strings or enums)
A or B ->  returns B (if either A==None or B==None)

In our case 
mode or self.mode -> returns mode 
align_corners or self.align_corners -> returns self.align_corners (because align_corners==None)

as a result we get mode='trilinear', align_corners=None  (the original option is lost)

---
We use this 'or' throughout the code for align_corners, and other parameters, which potentially created bugs if one of the options is None...




Discussion:
@yiheng-wang-nv could you please help look into this? I think normally we use the `None` argument input in `__call__` to denote 'no option provided', but here `None` is a valid option of it. Perhaps we can have another `NONE = "none"` for align_corners https://github.com/Project-MONAI/MONAI/blob/42159ffd3a3003efdee807683922ea1cec4cb27a/monai/utils/enums.py#L165
Thanks @myron .
Hi @wyli , the bug of `sth or None` also happened in https://github.com/Project-MONAI/MONAI/issues/3387
I suggest to thoroughly remove the `or` based way in related transforms. Let me have a try and submit a draft PR, what do you think?
Sounds good, thanks @yiheng-wang-nv.

Resolving patch:
diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py
--- a/monai/transforms/croppad/array.py
+++ b/monai/transforms/croppad/array.py
@@ -205,7 +205,7 @@ def __call__(
             # all zeros, skip padding
             return img
 
-        padder = Pad(all_pad_width, mode or self.mode, **self.kwargs)
+        padder = Pad(to_pad=all_pad_width, mode=mode or self.mode, **self.kwargs)
         return padder(img)
 
 
diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py
--- a/monai/transforms/spatial/array.py
+++ b/monai/transforms/spatial/array.py
@@ -1281,7 +1281,7 @@ def __call__(
             keep_size=self.keep_size,
             mode=look_up_option(mode or self.mode, InterpolateMode),
             padding_mode=padding_mode or self.padding_mode,
-            align_corners=align_corners or self.align_corners,
+            align_corners=self.align_corners if align_corners is None else align_corners,
             **self.kwargs,
         )(img)
 
@@ -1796,7 +1796,7 @@ def __call__(
                 Padding mode for outside grid values. Defaults to ``self.padding_mode``.
                 See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html
         """
-        sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:])
+        sp_size = fall_back_tuple(self.spatial_size if spatial_size is None else spatial_size, img.shape[1:])
         grid, affine = self.affine_grid(spatial_size=sp_size)
         ret = self.resampler(img, grid=grid, mode=mode or self.mode, padding_mode=padding_mode or self.padding_mode)
 
@@ -1978,7 +1978,7 @@ def __call__(
 
         # if not doing transform and spatial size doesn't change, nothing to do
         # except convert to float and device
-        sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:])
+        sp_size = fall_back_tuple(self.spatial_size if spatial_size is None else spatial_size, img.shape[1:])
         do_resampling = self._do_transform or (sp_size != ensure_tuple(img.shape[1:]))
         if not do_resampling:
             img, *_ = convert_data_type(img, dtype=torch.float32, device=self.resampler.device)
@@ -2120,7 +2120,7 @@ def __call__(
                 See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html
             randomize: whether to execute `randomize()` function first, default to True.
         """
-        sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:])
+        sp_size = fall_back_tuple(self.spatial_size if spatial_size is None else spatial_size, img.shape[1:])
         if randomize:
             self.randomize(spatial_size=sp_size)
 
@@ -2280,7 +2280,7 @@ def __call__(
                 See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html
             randomize: whether to execute `randomize()` function first, default to True.
         """
-        sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:])
+        sp_size = fall_back_tuple(self.spatial_size if spatial_size is None else spatial_size, img.shape[1:])
         if randomize:
             self.randomize(grid_size=sp_size)
 


Test patch: