Repository: Project-MONAI/MONAI

Issue:
rand zoom "keep_size" should default to True?
**Describe the bug**
a few monai examples use `RandZoom` and `Resize`,
but when RandZoom's `keep_size` option is False (the current default)
the preprocessor generates outputs that are almost identical to the inputs.

**To Reproduce**
this script tries to to 2x zoom and resize to `768x1024`:
```python
import matplotlib.pyplot as plt
import scipy.misc

from monai.transforms import RandZoom, Resize

img = scipy.misc.face(gray=True)[None]
zoom = RandZoom(prob=1.0, min_zoom=2, max_zoom=2)
resize = Resize(spatial_size=(768, 1024))
new_img = resize(zoom(img))

plt.subplots(1, 2)
plt.subplot(1, 2, 1)
plt.imshow(img[0], cmap="gray")
plt.subplot(1, 2, 2)
plt.imshow(new_img[0], cmap="gray")
plt.show()
```
the output is:
![Figure_1](https://user-images.githubusercontent.com/831580/83173251-a780b480-a110-11ea-938e-bce626eb28ce.png)


**Expected behavior**
setting `keep_size` to True 
(`zoom = RandZoom(prob=1.0, min_zoom=2, max_zoom=2, keep_size=True)`) 
gives the expected output:
![correct](https://user-images.githubusercontent.com/831580/83173377-d860e980-a110-11ea-8ada-75d9b9ed3e94.png)


**Environment (please complete the following information):**
 - OS macos
 - Python version 3.6
 - MONAI version 3181e3efb4b359acd7006c71e1146a299a51040e


**Additional context**
quit a few places use `RandZoom(..., keep_size=False)` and `Resize` together, they are probably wrong


Discussion:

Resolving patch:
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
@@ -333,7 +333,7 @@ class Zoom(Transform):
         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).
+        keep_size (bool): Should keep original size (pad if needed), default is True.
     """
 
     def __init__(
@@ -344,7 +344,7 @@ def __init__(
         cval=0,
         prefilter=True,
         use_gpu=False,
-        keep_size=False,
+        keep_size=True,
     ):
         self.zoom = zoom
         self.interp_order = interp_order
@@ -585,7 +585,7 @@ class RandZoom(Randomizable, Transform):
         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).
+        keep_size (bool): Should keep original size (pad if needed), default is True.
     """
 
     def __init__(
@@ -598,7 +598,7 @@ def __init__(
         cval=0,
         prefilter=True,
         use_gpu=False,
-        keep_size=False,
+        keep_size=True,
     ):
         if hasattr(min_zoom, "__iter__") and hasattr(max_zoom, "__iter__"):
             assert len(min_zoom) == len(max_zoom), "min_zoom and max_zoom must have same length."
diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py
--- a/monai/transforms/spatial/dictionary.py
+++ b/monai/transforms/spatial/dictionary.py
@@ -714,7 +714,7 @@ class Zoomd(MapTransform):
         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).
+        keep_size (bool): Should keep original size (pad if needed), default is True.
     """
 
     def __init__(
@@ -726,7 +726,7 @@ def __init__(
         cval=0,
         prefilter=True,
         use_gpu=False,
-        keep_size=False,
+        keep_size=True,
     ):
         super().__init__(keys)
         self.zoomer = Zoom(zoom=zoom, use_gpu=use_gpu, keep_size=keep_size)
@@ -768,7 +768,7 @@ class RandZoomd(Randomizable, MapTransform):
         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).
+        keep_size (bool): Should keep original size (pad if needed), default is True.
     """
 
     def __init__(
@@ -782,7 +782,7 @@ def __init__(
         cval=0,
         prefilter=True,
         use_gpu=False,
-        keep_size=False,
+        keep_size=True,
     ):
         super().__init__(keys)
         if hasattr(min_zoom, "__iter__") and hasattr(max_zoom, "__iter__"):


Test patch:
diff --git a/tests/test_integration_classification_2d.py b/tests/test_integration_classification_2d.py
--- a/tests/test_integration_classification_2d.py
+++ b/tests/test_integration_classification_2d.py
@@ -30,7 +30,6 @@
     RandFlip,
     RandRotate,
     RandZoom,
-    Resize,
     ScaleIntensity,
     ToTensor,
 )
@@ -62,10 +61,9 @@ def run_training_test(root_dir, train_x, train_y, val_x, val_y, device=torch.dev
             LoadPNG(image_only=True),
             AddChannel(),
             ScaleIntensity(),
-            RandRotate(degrees=15, prob=0.5),
+            RandRotate(degrees=15, prob=0.5, reshape=False),
             RandFlip(spatial_axis=0, prob=0.5),
             RandZoom(min_zoom=0.9, max_zoom=1.1, prob=0.5),
-            Resize(spatial_size=(64, 64), mode="constant"),
             ToTensor(),
         ]
     )
@@ -223,11 +221,11 @@ def test_training(self):
 
             # check training properties
             np.testing.assert_allclose(
-                losses, [0.8501208358129878, 0.18469145818121113, 0.08108749352158255, 0.04965383692342005], rtol=1e-3
+                losses, [0.8009556981788319, 0.16794362251356149, 0.07708252014912617, 0.04769148252856959], rtol=1e-3
             )
             repeated[i].extend(losses)
             print("best metric", best_metric)
-            np.testing.assert_allclose(best_metric, 0.9999480167572079, rtol=1e-4)
+            np.testing.assert_allclose(best_metric, 0.9999460507942981, rtol=1e-4)
             repeated[i].append(best_metric)
             np.testing.assert_allclose(best_metric_epoch, 4)
             model_file = os.path.join(self.data_dir, "best_metric_model.pth")
@@ -236,7 +234,7 @@ def test_training(self):
             infer_metric = run_inference_test(self.data_dir, self.test_x, self.test_y, device=self.device)
 
             # check inference properties
-            np.testing.assert_allclose(np.asarray(infer_metric), [1036, 895, 982, 1033, 958, 1047], atol=1)
+            np.testing.assert_allclose(np.asarray(infer_metric), [1034, 895, 982, 1033, 961, 1047], atol=1)
             repeated[i].extend(infer_metric)
 
         np.testing.assert_allclose(repeated[0], repeated[1])