From ef2df64e574b348b3c895dcfb2dfefa9ae04a626 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 15 Jun 2021 11:19:49 +0800 Subject: [PATCH 1/3] [DLMED] enhance GradCam return type Signed-off-by: Nic Ma --- monai/visualize/class_activation_maps.py | 17 +++++++++++------ tests/test_vis_gradcam.py | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/monai/visualize/class_activation_maps.py b/monai/visualize/class_activation_maps.py index c63e8e51d9..eed0468d49 100644 --- a/monai/visualize/class_activation_maps.py +++ b/monai/visualize/class_activation_maps.py @@ -17,6 +17,7 @@ import torch.nn as nn import torch.nn.functional as F +from monai.config import NdarrayTensor from monai.transforms import ScaleIntensity from monai.utils import ensure_tuple, get_torch_version_tuple from monai.visualize.visualizer import default_upsampler @@ -24,17 +25,21 @@ __all__ = ["CAM", "GradCAM", "GradCAMpp", "ModelWithHooks", "default_normalizer"] -def default_normalizer(x) -> np.ndarray: +def default_normalizer(x: NdarrayTensor) -> NdarrayTensor: """ A linear intensity scaling by mapping the (min, max) to (1, 0). + The output data type and device will be consistent with input data. - N.B.: This will flip magnitudes (i.e., smallest will become biggest and vice versa). + Note: This will flip magnitudes (i.e., smallest will become biggest and vice versa). """ + def _compute(data: np.ndarray): + scaler = ScaleIntensity(minv=1.0, maxv=0.0) + return np.stack([scaler(i) for i in data], axis=0) + if isinstance(x, torch.Tensor): - x = x.detach().cpu().numpy() - scaler = ScaleIntensity(minv=1.0, maxv=0.0) - x = [scaler(x) for x in x] - return np.stack(x, axis=0) + return torch.as_tensor(_compute(x.detach().cpu().numpy()), device=x.device) + + return _compute(x) class ModelWithHooks: diff --git a/tests/test_vis_gradcam.py b/tests/test_vis_gradcam.py index f8e49f486f..eebf32d70b 100644 --- a/tests/test_vis_gradcam.py +++ b/tests/test_vis_gradcam.py @@ -86,7 +86,7 @@ def test_shape(self, input_data, expected_shape): self.assertTupleEqual(result.shape, expected_shape) # check result is same whether class_idx=None is used or not result2 = cam(x=image, layer_idx=-1, class_idx=model(image).max(1)[-1].cpu()) - np.testing.assert_array_almost_equal(result, result2) + torch.testing.assert_allclose(result, result2) if __name__ == "__main__": From 6e5088242de23b11c33fa89b6a13dfc59df5e240 Mon Sep 17 00:00:00 2001 From: monai-bot Date: Tue, 15 Jun 2021 03:28:45 +0000 Subject: [PATCH 2/3] [MONAI] python code formatting Signed-off-by: monai-bot --- monai/visualize/class_activation_maps.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monai/visualize/class_activation_maps.py b/monai/visualize/class_activation_maps.py index eed0468d49..6fc0d34f15 100644 --- a/monai/visualize/class_activation_maps.py +++ b/monai/visualize/class_activation_maps.py @@ -32,6 +32,7 @@ def default_normalizer(x: NdarrayTensor) -> NdarrayTensor: Note: This will flip magnitudes (i.e., smallest will become biggest and vice versa). """ + def _compute(data: np.ndarray): scaler = ScaleIntensity(minv=1.0, maxv=0.0) return np.stack([scaler(i) for i in data], axis=0) From 50d18d26472d74844405778d62274069e97eade1 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 15 Jun 2021 17:14:59 +0800 Subject: [PATCH 3/3] [DLMED] update according to comments Signed-off-by: Nic Ma --- monai/visualize/class_activation_maps.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monai/visualize/class_activation_maps.py b/monai/visualize/class_activation_maps.py index 6fc0d34f15..992eaecdac 100644 --- a/monai/visualize/class_activation_maps.py +++ b/monai/visualize/class_activation_maps.py @@ -28,12 +28,13 @@ def default_normalizer(x: NdarrayTensor) -> NdarrayTensor: """ A linear intensity scaling by mapping the (min, max) to (1, 0). - The output data type and device will be consistent with input data. + If the input data is PyTorch Tensor, the output data will be Tensor on the same device, + otherwise, output data will be numpy array. Note: This will flip magnitudes (i.e., smallest will become biggest and vice versa). """ - def _compute(data: np.ndarray): + def _compute(data: np.ndarray) -> np.ndarray: scaler = ScaleIntensity(minv=1.0, maxv=0.0) return np.stack([scaler(i) for i in data], axis=0)