From 612841306c7128ef20ce040895c312f5c95a2ec8 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 12 Jan 2021 20:29:38 +0800 Subject: [PATCH 1/7] [DLMED] add TorchVision transform Signed-off-by: Nic Ma --- monai/transforms/utility/array.py | 32 ++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index bbb78fb7bb..eafb811562 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -22,7 +22,7 @@ from monai.transforms.compose import Randomizable, Transform from monai.transforms.utils import extreme_points_to_image, get_extreme_points, map_binary_to_indices -from monai.utils import ensure_tuple +from monai.utils import ensure_tuple, optional_import __all__ = [ "Identity", @@ -42,6 +42,7 @@ "LabelToMask", "FgBgToIndices", "AddExtremePointsChannel", + "TorchVision", ] # Generic type which can represent either a numpy.ndarray or a torch.Tensor @@ -617,3 +618,32 @@ def __call__( ) return np.concatenate([img, points_image], axis=0) + + +class TorchVision: + """ + This is a wrapper transform for PyTorch TorchVision transform based on the specified transform name and args. + As most of the TorchVision transforms only work for PIL image and PyTorch Tensor, this transform expects input + data to be PyTorch Tensor, users can easily call `ToTensor` transform to convert a Numpy array to Tensor. + + """ + + def __init__(self, name: str, *args, **kwargs) -> None: + """ + Args: + name: The transform name in TorchVision package. + args: parameters for the TorchVision transform. + kwargs: parameters for the TorchVision transform. + + """ + super().__init__() + transform = optional_import("torchvision.transforms", name=name) + self.trans = transform(*args, **kwargs) + + def __call__(self, img: torch.Tensor) -> torch.Tensor: + """ + Args: + img: PyTorch Tensor data for the TorchVision transform. + + """ + return self.trans(img) From da15289ac6bf586fc148079541dc8496baf980d2 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 12 Jan 2021 23:09:03 +0800 Subject: [PATCH 2/7] [DLMED] add unit tests Signed-off-by: Nic Ma --- docs/source/transforms.rst | 12 ++++ monai/transforms/__init__.py | 2 + monai/transforms/utility/array.py | 2 +- monai/transforms/utility/dictionary.py | 29 ++++++++++ tests/min_tests.py | 2 + tests/test_torchvision.py | 80 ++++++++++++++++++++++++++ tests/test_torchvisiond.py | 80 ++++++++++++++++++++++++++ 7 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 tests/test_torchvision.py create mode 100644 tests/test_torchvisiond.py diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index f7e075f376..d434b3d218 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -504,6 +504,12 @@ Utility :members: :special-members: __call__ +`TorchVision` +""""""""""""" +.. autoclass:: TorchVision + :members: + :special-members: __call__ + Dictionary Transforms --------------------- @@ -969,6 +975,12 @@ Utility (Dict) :members: :special-members: __call__ +`TorchVisiond` +"""""""""""""" +.. autoclass:: TorchVision + :members: + :special-members: __call__ + Transform Adaptors ------------------ .. automodule:: monai.transforms.adaptors diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index 305c27607e..46522db5b5 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -235,6 +235,7 @@ SplitChannel, SqueezeDim, ToNumpy, + TorchVision, ToTensor, Transpose, ) @@ -259,5 +260,6 @@ SplitChanneld, SqueezeDimd, ToNumpyd, + TorchVisiond, ToTensord, ) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index eafb811562..3e866e75a8 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -637,7 +637,7 @@ def __init__(self, name: str, *args, **kwargs) -> None: """ super().__init__() - transform = optional_import("torchvision.transforms", name=name) + transform, _ = optional_import("torchvision.transforms", name=name) self.trans = transform(*args, **kwargs) def __call__(self, img: torch.Tensor) -> torch.Tensor: diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index 21c30f6010..5727f66697 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -41,6 +41,7 @@ SqueezeDim, ToNumpy, ToTensor, + TorchVision, ) from monai.utils import ensure_tuple, ensure_tuple_rep @@ -66,6 +67,7 @@ "FgBgToIndicesd", "ConvertToMultiChannelBasedOnBratsClassesd", "AddExtremePointsChanneld", + "TorchVisiond", ] @@ -747,6 +749,32 @@ def __call__(self, data): d[key] = np.concatenate([img, points_image], axis=0) return d +class TorchVisiond(MapTransform): + """ + Dictionary-based wrapper of :py:class:`monai.transforms.TorchVision`. + As most of the TorchVision transforms only work for PIL image and PyTorch Tensor, this transform expects input + data to be dict of PyTorch Tensors, users can easily call `ToTensord` transform to convert Numpy to Tensor. + """ + + def __init__(self, keys: KeysCollection, name: str, *args, **kwargs) -> None: + """ + Args: + keys: keys of the corresponding items to be transformed. + See also: :py:class:`monai.transforms.compose.MapTransform` + name: The transform name in TorchVision package. + args: parameters for the TorchVision transform. + kwargs: parameters for the TorchVision transform. + + """ + super().__init__(keys) + self.trans = TorchVision(name=name, *args, **kwargs) + + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + d = dict(data) + for key in self.keys: + d[key] = self.trans(d[key]) + return d + IdentityD = IdentityDict = Identityd AsChannelFirstD = AsChannelFirstDict = AsChannelFirstd @@ -769,3 +797,4 @@ def __call__(self, data): ConvertToMultiChannelBasedOnBratsClassesDict ) = ConvertToMultiChannelBasedOnBratsClassesd AddExtremePointsChannelD = AddExtremePointsChannelDict = AddExtremePointsChanneld +TorchVisionD = TorchVisionDict = TorchVisiond diff --git a/tests/min_tests.py b/tests/min_tests.py index 32e61ed6f7..0e2e8d3917 100644 --- a/tests/min_tests.py +++ b/tests/min_tests.py @@ -102,6 +102,8 @@ def run_testsuit(): "test_zoom_affine", "test_zoomd", "test_occlusion_sensitivity", + "test_torchvision", + "test_torchvisiond", ] assert sorted(exclude_cases) == sorted(set(exclude_cases)), f"Duplicated items in {exclude_cases}" diff --git a/tests/test_torchvision.py b/tests/test_torchvision.py new file mode 100644 index 0000000000..3f7e4eba9d --- /dev/null +++ b/tests/test_torchvision.py @@ -0,0 +1,80 @@ +# Copyright 2020 - 2021 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import torch +from parameterized import parameterized +from monai.utils import set_determinism +from monai.transforms import TorchVision + +TEST_CASE_1 = [ + {"name": "ColorJitter"}, + torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), +] + +TEST_CASE_2 = [ + {"name": "ColorJitter", "brightness": 0.5, "contrast": 0.5, "saturation": [0.1, 0.8], "hue": 0.5}, + torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + torch.tensor( + [ + [ + [0.1090, 0.6193], [0.6193, 0.9164], + ], + [ + [0.1090, 0.6193], [0.6193, 0.9164], + ], + [ + [0.1090, 0.6193], [0.6193, 0.9164], + ], + ], + ), +] + +TEST_CASE_3 = [ + {"name": "Pad", "padding": [1, 1, 1, 1]}, + torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + torch.tensor( + [ + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 1., 2., 0.], + [0., 0., 0., 0.], + ], + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 1., 2., 0.], + [0., 0., 0., 0.], + ], + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 1., 2., 0.], + [0., 0., 0., 0.], + ], + ] + ) +] + + +class TestTorchVision(unittest.TestCase): + @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3]) + def test_value(self, input_param, input_data, expected_value): + set_determinism(seed=0) + result = TorchVision(**input_param)(input_data) + torch.testing.assert_allclose(result, expected_value) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_torchvisiond.py b/tests/test_torchvisiond.py new file mode 100644 index 0000000000..f634163d7d --- /dev/null +++ b/tests/test_torchvisiond.py @@ -0,0 +1,80 @@ +# Copyright 2020 - 2021 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import torch +from parameterized import parameterized +from monai.utils import set_determinism +from monai.transforms import TorchVisiond + +TEST_CASE_1 = [ + {"keys": "img", "name": "ColorJitter"}, + {"img": torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]])}, + torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), +] + +TEST_CASE_2 = [ + {"keys": "img", "name": "ColorJitter", "brightness": 0.5, "contrast": 0.5, "saturation": [0.1, 0.8], "hue": 0.5}, + {"img": torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]])}, + torch.tensor( + [ + [ + [0.1090, 0.6193], [0.6193, 0.9164], + ], + [ + [0.1090, 0.6193], [0.6193, 0.9164], + ], + [ + [0.1090, 0.6193], [0.6193, 0.9164], + ], + ], + ), +] + +TEST_CASE_3 = [ + {"keys": "img", "name": "Pad", "padding": [1, 1, 1, 1]}, + {"img": torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]])}, + torch.tensor( + [ + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 1., 2., 0.], + [0., 0., 0., 0.], + ], + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 1., 2., 0.], + [0., 0., 0., 0.], + ], + [ + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 1., 2., 0.], + [0., 0., 0., 0.], + ], + ] + ) +] + + +class TestTorchVisiond(unittest.TestCase): + @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3]) + def test_value(self, input_param, input_data, expected_value): + set_determinism(seed=0) + result = TorchVisiond(**input_param)(input_data) + torch.testing.assert_allclose(result["img"], expected_value) + + +if __name__ == "__main__": + unittest.main() From 32f414c709c859260ac4228ee1b8173ae4685a55 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 12 Jan 2021 23:11:05 +0800 Subject: [PATCH 3/7] [DLMED] fix typo Signed-off-by: Nic Ma --- docs/source/transforms.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index d434b3d218..4fad271109 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -977,7 +977,7 @@ Utility (Dict) `TorchVisiond` """""""""""""" -.. autoclass:: TorchVision +.. autoclass:: TorchVisiond :members: :special-members: __call__ From 230d72c55c9c7a066754fae4dc5c6a724b4e4a2d Mon Sep 17 00:00:00 2001 From: monai-bot Date: Tue, 12 Jan 2021 15:14:43 +0000 Subject: [PATCH 4/7] [MONAI] python code formatting Signed-off-by: monai-bot --- monai/csrc/resample/pushpull_cpu.cpp | 44 +++++++++++++++--------- monai/csrc/resample/pushpull_cuda.cu | 44 +++++++++++++++--------- monai/transforms/utility/dictionary.py | 3 +- tests/test_torchvision.py | 46 ++++++++++++++------------ tests/test_torchvisiond.py | 46 ++++++++++++++------------ 5 files changed, 108 insertions(+), 75 deletions(-) diff --git a/monai/csrc/resample/pushpull_cpu.cpp b/monai/csrc/resample/pushpull_cpu.cpp index 204fa846d3..40743a6cf1 100644 --- a/monai/csrc/resample/pushpull_cpu.cpp +++ b/monai/csrc/resample/pushpull_cpu.cpp @@ -97,19 +97,25 @@ MONAI_NAMESPACE_DEVICE { // cpu bool do_sgrad) : dim(dim), bound0(bound.size() > 0 ? bound[0] : BoundType::Replicate), - bound1(bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound1( + bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), bound2( - bound.size() > 2 ? bound[2] - : bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound.size() > 2 ? bound[2] + : bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), interpolation0(interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), interpolation1( - interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + interpolation.size() > 1 ? interpolation[1] + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), interpolation2( - interpolation.size() > 2 - ? interpolation[2] + interpolation.size() > 2 ? interpolation[2] : interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), extrapolate(extrapolate), do_pull(do_pull), do_push(do_push), @@ -136,13 +142,14 @@ MONAI_NAMESPACE_DEVICE { // cpu bound2(bound), interpolation0(interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), interpolation1( - interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + interpolation.size() > 1 ? interpolation[1] + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), interpolation2( - interpolation.size() > 2 - ? interpolation[2] + interpolation.size() > 2 ? interpolation[2] : interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), extrapolate(extrapolate), do_pull(do_pull), do_push(do_push), @@ -165,10 +172,15 @@ MONAI_NAMESPACE_DEVICE { // cpu bool do_sgrad) : dim(dim), bound0(bound.size() > 0 ? bound[0] : BoundType::Replicate), - bound1(bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound1( + bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), bound2( - bound.size() > 2 ? bound[2] - : bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound.size() > 2 ? bound[2] + : bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), interpolation0(interpolation), interpolation1(interpolation), interpolation2(interpolation), diff --git a/monai/csrc/resample/pushpull_cuda.cu b/monai/csrc/resample/pushpull_cuda.cu index cbb0a36a80..ecfeb562ab 100644 --- a/monai/csrc/resample/pushpull_cuda.cu +++ b/monai/csrc/resample/pushpull_cuda.cu @@ -94,19 +94,25 @@ MONAI_NAMESPACE_DEVICE { // cuda bool do_sgrad) : dim(dim), bound0(bound.size() > 0 ? bound[0] : BoundType::Replicate), - bound1(bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound1( + bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), bound2( - bound.size() > 2 ? bound[2] - : bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound.size() > 2 ? bound[2] + : bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), interpolation0(interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), interpolation1( - interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + interpolation.size() > 1 ? interpolation[1] + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), interpolation2( - interpolation.size() > 2 - ? interpolation[2] + interpolation.size() > 2 ? interpolation[2] : interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), extrapolate(extrapolate), do_pull(do_pull), do_push(do_push), @@ -133,13 +139,14 @@ MONAI_NAMESPACE_DEVICE { // cuda bound2(bound), interpolation0(interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), interpolation1( - interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + interpolation.size() > 1 ? interpolation[1] + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), interpolation2( - interpolation.size() > 2 - ? interpolation[2] + interpolation.size() > 2 ? interpolation[2] : interpolation.size() > 1 ? interpolation[1] - : interpolation.size() > 0 ? interpolation[0] : InterpolationType::Linear), + : interpolation.size() > 0 ? interpolation[0] + : InterpolationType::Linear), extrapolate(extrapolate), do_pull(do_pull), do_push(do_push), @@ -162,10 +169,15 @@ MONAI_NAMESPACE_DEVICE { // cuda bool do_sgrad) : dim(dim), bound0(bound.size() > 0 ? bound[0] : BoundType::Replicate), - bound1(bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound1( + bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), bound2( - bound.size() > 2 ? bound[2] - : bound.size() > 1 ? bound[1] : bound.size() > 0 ? bound[0] : BoundType::Replicate), + bound.size() > 2 ? bound[2] + : bound.size() > 1 ? bound[1] + : bound.size() > 0 ? bound[0] + : BoundType::Replicate), interpolation0(interpolation), interpolation1(interpolation), interpolation2(interpolation), diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index 5727f66697..42cce368de 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -40,8 +40,8 @@ SplitChannel, SqueezeDim, ToNumpy, - ToTensor, TorchVision, + ToTensor, ) from monai.utils import ensure_tuple, ensure_tuple_rep @@ -749,6 +749,7 @@ def __call__(self, data): d[key] = np.concatenate([img, points_image], axis=0) return d + class TorchVisiond(MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.TorchVision`. diff --git a/tests/test_torchvision.py b/tests/test_torchvision.py index 3f7e4eba9d..65bc1dfd8a 100644 --- a/tests/test_torchvision.py +++ b/tests/test_torchvision.py @@ -13,28 +13,32 @@ import torch from parameterized import parameterized -from monai.utils import set_determinism + from monai.transforms import TorchVision +from monai.utils import set_determinism TEST_CASE_1 = [ {"name": "ColorJitter"}, - torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), - torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]]), + torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]]), ] TEST_CASE_2 = [ {"name": "ColorJitter", "brightness": 0.5, "contrast": 0.5, "saturation": [0.1, 0.8], "hue": 0.5}, - torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]]), torch.tensor( [ [ - [0.1090, 0.6193], [0.6193, 0.9164], + [0.1090, 0.6193], + [0.6193, 0.9164], ], [ - [0.1090, 0.6193], [0.6193, 0.9164], + [0.1090, 0.6193], + [0.6193, 0.9164], ], [ - [0.1090, 0.6193], [0.6193, 0.9164], + [0.1090, 0.6193], + [0.6193, 0.9164], ], ], ), @@ -42,29 +46,29 @@ TEST_CASE_3 = [ {"name": "Pad", "padding": [1, 1, 1, 1]}, - torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]]), torch.tensor( [ [ - [0., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 2., 0.], - [0., 0., 0., 0.], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 0.0], ], [ - [0., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 2., 0.], - [0., 0., 0., 0.], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 0.0], ], [ - [0., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 2., 0.], - [0., 0., 0., 0.], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 0.0], ], ] - ) + ), ] diff --git a/tests/test_torchvisiond.py b/tests/test_torchvisiond.py index f634163d7d..b0ee6ee2ee 100644 --- a/tests/test_torchvisiond.py +++ b/tests/test_torchvisiond.py @@ -13,28 +13,32 @@ import torch from parameterized import parameterized -from monai.utils import set_determinism + from monai.transforms import TorchVisiond +from monai.utils import set_determinism TEST_CASE_1 = [ {"keys": "img", "name": "ColorJitter"}, - {"img": torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]])}, - torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]]), + {"img": torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]])}, + torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]]), ] TEST_CASE_2 = [ {"keys": "img", "name": "ColorJitter", "brightness": 0.5, "contrast": 0.5, "saturation": [0.1, 0.8], "hue": 0.5}, - {"img": torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]])}, + {"img": torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]])}, torch.tensor( [ [ - [0.1090, 0.6193], [0.6193, 0.9164], + [0.1090, 0.6193], + [0.6193, 0.9164], ], [ - [0.1090, 0.6193], [0.6193, 0.9164], + [0.1090, 0.6193], + [0.6193, 0.9164], ], [ - [0.1090, 0.6193], [0.6193, 0.9164], + [0.1090, 0.6193], + [0.6193, 0.9164], ], ], ), @@ -42,29 +46,29 @@ TEST_CASE_3 = [ {"keys": "img", "name": "Pad", "padding": [1, 1, 1, 1]}, - {"img": torch.tensor([[[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]], [[0., 1.], [1., 2.]]])}, + {"img": torch.tensor([[[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]], [[0.0, 1.0], [1.0, 2.0]]])}, torch.tensor( [ [ - [0., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 2., 0.], - [0., 0., 0., 0.], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 0.0], ], [ - [0., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 2., 0.], - [0., 0., 0., 0.], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 0.0], ], [ - [0., 0., 0., 0.], - [0., 0., 1., 0.], - [0., 1., 2., 0.], - [0., 0., 0., 0.], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 1.0, 2.0, 0.0], + [0.0, 0.0, 0.0, 0.0], ], ] - ) + ), ] From 139dd32a072ebcd3f7c144737fd9959173dbb4be Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Wed, 13 Jan 2021 07:35:00 +0800 Subject: [PATCH 5/7] [DLMED] fix flake8 issue Signed-off-by: Nic Ma --- monai/transforms/utility/array.py | 2 +- monai/transforms/utility/dictionary.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 819c865aed..359e925690 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -638,7 +638,7 @@ def __init__(self, name: str, *args, **kwargs) -> None: transform, _ = optional_import("torchvision.transforms", name=name) self.trans = transform(*args, **kwargs) - def __call__(self, img: torch.Tensor) -> torch.Tensor: + def __call__(self, img: torch.Tensor): """ Args: img: PyTorch Tensor data for the TorchVision transform. diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index b952c56f41..5c08f72c92 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -752,7 +752,7 @@ def __init__(self, keys: KeysCollection, name: str, *args, **kwargs) -> None: """ super().__init__(keys) - self.trans = TorchVision(name=name, *args, **kwargs) + self.trans = TorchVision(name, *args, **kwargs) def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: d = dict(data) From 170ffb16b16c333fa23119c4b412a8f353cf2214 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Wed, 13 Jan 2021 12:28:04 +0800 Subject: [PATCH 6/7] [DLMED] fix CI tests Signed-off-by: Nic Ma --- monai/transforms/utility/array.py | 4 ++-- tests/test_torchvision.py | 2 ++ tests/test_torchvisiond.py | 2 ++ tests/utils.py | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 359e925690..a43dcf2e5f 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -22,7 +22,7 @@ from monai.transforms.compose import Randomizable, Transform from monai.transforms.utils import extreme_points_to_image, get_extreme_points, map_binary_to_indices -from monai.utils import ensure_tuple, optional_import +from monai.utils import ensure_tuple, optional_import, min_version __all__ = [ "Identity", @@ -635,7 +635,7 @@ def __init__(self, name: str, *args, **kwargs) -> None: """ super().__init__() - transform, _ = optional_import("torchvision.transforms", name=name) + transform, _ = optional_import("torchvision.transforms", "0.8.0", min_version, name=name) self.trans = transform(*args, **kwargs) def __call__(self, img: torch.Tensor): diff --git a/tests/test_torchvision.py b/tests/test_torchvision.py index 65bc1dfd8a..0846b7f6b6 100644 --- a/tests/test_torchvision.py +++ b/tests/test_torchvision.py @@ -16,6 +16,7 @@ from monai.transforms import TorchVision from monai.utils import set_determinism +from tests.utils import SkipIfBeforePyTorchVersion TEST_CASE_1 = [ {"name": "ColorJitter"}, @@ -72,6 +73,7 @@ ] +@SkipIfBeforePyTorchVersion((1, 7)) class TestTorchVision(unittest.TestCase): @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3]) def test_value(self, input_param, input_data, expected_value): diff --git a/tests/test_torchvisiond.py b/tests/test_torchvisiond.py index b0ee6ee2ee..4f42bc95f7 100644 --- a/tests/test_torchvisiond.py +++ b/tests/test_torchvisiond.py @@ -16,6 +16,7 @@ from monai.transforms import TorchVisiond from monai.utils import set_determinism +from tests.utils import SkipIfBeforePyTorchVersion TEST_CASE_1 = [ {"keys": "img", "name": "ColorJitter"}, @@ -72,6 +73,7 @@ ] +@SkipIfBeforePyTorchVersion((1, 7)) class TestTorchVisiond(unittest.TestCase): @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3]) def test_value(self, input_param, input_data, expected_value): diff --git a/tests/utils.py b/tests/utils.py index efedcdc859..20de17bbff 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -121,7 +121,7 @@ def __call__(self, obj): class SkipIfAtLeastPyTorchVersion(object): """Decorator to be used if test should be skipped - with PyTorch versions older than that given.""" + with PyTorch versions newer than that given.""" def __init__(self, pytorch_version_tuple): self.max_version = pytorch_version_tuple From 2cbf2873f1d95fb8beda0448a602b97493617b19 Mon Sep 17 00:00:00 2001 From: monai-bot Date: Wed, 13 Jan 2021 04:32:02 +0000 Subject: [PATCH 7/7] [MONAI] python code formatting Signed-off-by: monai-bot --- monai/transforms/utility/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index a43dcf2e5f..7e7fe816a9 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -22,7 +22,7 @@ from monai.transforms.compose import Randomizable, Transform from monai.transforms.utils import extreme_points_to_image, get_extreme_points, map_binary_to_indices -from monai.utils import ensure_tuple, optional_import, min_version +from monai.utils import ensure_tuple, min_version, optional_import __all__ = [ "Identity",