From 46c34cdbd9c05a7597862efebb5db7773de1d78d Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Sun, 12 Sep 2021 20:29:19 +0000 Subject: [PATCH 01/21] Implement CuCIM wrapper transfrom Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 38 +++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index add47e27ca..59f809d982 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -17,7 +17,7 @@ import sys import time import warnings -from typing import Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union import numpy as np import torch @@ -40,7 +40,12 @@ PILImageImage, has_pil = optional_import("PIL.Image", name="Image") pil_image_fromarray, _ = optional_import("PIL.Image", name="fromarray") cp, has_cp = optional_import("cupy") -cp_ndarray, _ = optional_import("cupy", name="ndarray") + +if TYPE_CHECKING: + from cupy import ndarray as cp_ndarray +else: + cp_ndarray, _ = optional_import("cupy", name="ndarray") + __all__ = [ "Identity", @@ -1101,3 +1106,32 @@ def __call__(self, img: torch.Tensor): raise ValueError("img must be PyTorch Tensor, consider converting img by `EnsureType` transform first.") return img.to(self.device, **self.kwargs) + + +class CuCIM: + """ + Wrap cuCIM transform, defined based on the transform name and args. + CuCIM transforms only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + + """ + + def __init__(self, name: str, *args, **kwargs) -> None: + """ + Args: + name: The transform name in CuCIM package. + args: parameters for the CuCIM transform. + kwargs: parameters for the CuCIM transform. + + """ + super().__init__() + transform, _ = optional_import("cucim.transforms", name=name) + self.trans = transform(*args, **kwargs) + + def __call__(self, data: cp_ndarray): + """ + Args: + img: CuPy array for the cuCIM transform. + + """ + return self.trans(data) From fe09a190ddd166c86431bcd3dac388edfb298984 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Sun, 12 Sep 2021 20:29:42 +0000 Subject: [PATCH 02/21] Implement CuCIMd and RandCuCIMd Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/dictionary.py | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index e9bcce93b0..2699ca0a9d 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -35,6 +35,7 @@ CastToType, ClassesToIndices, ConvertToMultiChannelBasedOnBratsClasses, + CuCIM, DataStats, EnsureChannelFirst, EnsureType, @@ -1427,6 +1428,60 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc return d +class CuCIMd(MapTransform): + """ + Dictionary-based wrapper of :py:class:`monai.transforms.CuCIM` for non-randomized transforms. + For randomized transforms of CuCIM use :py:class:`monai.transforms.RandCuCIMd`. + + Note: + CuCIM transforms only work with CuPy arrays, this transform expects input data to be `cupy.ndarray`. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + """ + + def __init__( + self, + keys: KeysCollection, + name: str, + allow_missing_keys: bool = False, + *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 CuCIM package. + allow_missing_keys: don't raise exception if key is missing. + args: parameters for the CuCIM transform. + kwargs: parameters for the CuCIM transform. + + """ + super().__init__(keys, allow_missing_keys) + self.trans = CuCIM(name, *args, **kwargs) + + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + d = dict(data) + for key in self.key_iterator(d): + d[key] = self.trans(d[key]) + return d + + +class RandCuCIMd(Randomizable, CuCIMd): + """ + Dictionary-based wrapper of :py:class:`monai.transforms.CuCIM` for randomized transforms. + For deterministic non-randomized transforms of CuCIM use :py:class:`monai.transforms.CuCIMd`. + + Note: + - CuCIM transforms only work with CuPy arrays, this transform expects input data to be `cupy.ndarray`. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + - This class inherits the ``Randomizable`` purely to prevent any dataset caching to skip the transform + computation. If the random factor of the underlying cuCIM transform is not derived from `self.R`, + the results may not be deterministic. + See Also: :py:class:`monai.transforms.Randomizable`. + + """ + + IdentityD = IdentityDict = Identityd AsChannelFirstD = AsChannelFirstDict = AsChannelFirstd AsChannelLastD = AsChannelLastDict = AsChannelLastd @@ -1463,3 +1518,5 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc MapLabelValueD = MapLabelValueDict = MapLabelValued IntensityStatsD = IntensityStatsDict = IntensityStatsd ToDeviceD = ToDeviceDict = ToDeviced +CuCIMD = CuCIMDict = CuCIMd +RandCuCIMD = RandCuCIMDict = RandCuCIMd From 920ea4e9e91f2f0e5092116ce43a9dc73f66f137 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Sun, 12 Sep 2021 20:38:18 +0000 Subject: [PATCH 03/21] Update init Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index a07dee867b..f5838bc795 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -359,6 +359,7 @@ CastToType, ClassesToIndices, ConvertToMultiChannelBasedOnBratsClasses, + CuCIM, DataStats, EnsureChannelFirst, EnsureType, @@ -407,6 +408,9 @@ ConvertToMultiChannelBasedOnBratsClassesd, ConvertToMultiChannelBasedOnBratsClassesD, ConvertToMultiChannelBasedOnBratsClassesDict, + CuCIMd, + CuCIMD, + CuCIMDict, CopyItemsd, CopyItemsD, CopyItemsDict, @@ -440,6 +444,9 @@ MapLabelValued, MapLabelValueD, MapLabelValueDict, + RandCuCIMd, + RandCuCIMD, + RandCuCIMDict, RandLambdad, RandLambdaD, RandLambdaDict, From 751f1545165a7335a5bb60ac9c1eab87bf4469ec Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Sun, 12 Sep 2021 20:38:29 +0000 Subject: [PATCH 04/21] Update docs Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- docs/source/transforms.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index add6b9b40c..ec2e589f1b 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -733,6 +733,12 @@ Utility :members: :special-members: __call__ +`CuCIM` +""""""" +.. autoclass:: CuCIM + :members: + :special-members: __call__ + Dictionary Transforms --------------------- @@ -1374,6 +1380,17 @@ Utility (Dict) :members: :special-members: __call__ +`CuCIMd` +"""""""" +.. autoclass:: CuCIMd + :members: + :special-members: __call__ + +`RandCuCIMd` +"""""""""""" +.. autoclass:: RandCuCIMd + :members: + :special-members: __call__ Transform Adaptors ------------------ From 4a942e297f2a25276a795ef426fdcdc00de3e139 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Sun, 12 Sep 2021 22:28:11 +0000 Subject: [PATCH 05/21] Order imports Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index f5838bc795..b880652e26 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -408,12 +408,12 @@ ConvertToMultiChannelBasedOnBratsClassesd, ConvertToMultiChannelBasedOnBratsClassesD, ConvertToMultiChannelBasedOnBratsClassesDict, - CuCIMd, - CuCIMD, - CuCIMDict, CopyItemsd, CopyItemsD, CopyItemsDict, + CuCIMd, + CuCIMD, + CuCIMDict, DataStatsd, DataStatsD, DataStatsDict, From 86767cead04d6df2429a070acb7688d61f27ef6a Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Mon, 13 Sep 2021 17:45:10 +0000 Subject: [PATCH 06/21] Update RandCuCIM and RandCuCIMd to be randomly applied Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 60 +++++++++++++++----- monai/transforms/utility/dictionary.py | 76 +++++++++++++++++++------- 2 files changed, 102 insertions(+), 34 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 59f809d982..c22f2652b9 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1108,30 +1108,64 @@ def __call__(self, img: torch.Tensor): return img.to(self.device, **self.kwargs) -class CuCIM: +class CuCIM(Transform): """ - Wrap cuCIM transform, defined based on the transform name and args. - CuCIM transforms only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. - Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + Wrap a non-randomized cuCIM transform, defined based on the transform name and args. + For randomized transforms (or randomly applying a transform) use :py:class:`monai.transforms.RandCuCIM`. + Args: + name: the transform name in CuCIM package + args: parameters for the CuCIM transform + kwargs: parameters for the CuCIM transform + + Note: + CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. """ def __init__(self, name: str, *args, **kwargs) -> None: - """ - Args: - name: The transform name in CuCIM package. - args: parameters for the CuCIM transform. - kwargs: parameters for the CuCIM transform. - - """ super().__init__() - transform, _ = optional_import("cucim.transforms", name=name) + transform, _ = optional_import("cucim.core.operations", name=name) self.trans = transform(*args, **kwargs) def __call__(self, data: cp_ndarray): """ Args: - img: CuPy array for the cuCIM transform. + img: CuPy array for the cuCIM transform """ return self.trans(data) + + +class RandCuCIM(CuCIM, RandomizableTransform): + """ + Wrap a randomized cuCIM transform, defined based on the transform name and args, + or randomly apply a non-randomized transform. + For deterministic non-randomized transforms use :py:class:`monai.transforms.CuCIM`. + + Args: + name: the transform name in CuCIM package. + prob: the probability to apply the transform (default=1.0) + args: parameters for the CuCIM transform. + kwargs: parameters for the CuCIM transform. + + Note: + - CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + - If the cuCIM transform is already randomized, the `prob` argument has nothing to do with + the randomness of the underlying cuCIM transform. + It defines if the transform (either randomized or non-randomized) being applied randomly with `prob=1.0`, + so it can apply non-randomized tranforms randomly but be careful when it is being used along with randomized transforms. + - If the random factor of the underlying cuCIM transform is not derived from `self.R`, + the results may not be deterministic. See Also: :py:class:`monai.transforms.Randomizable`. + """ + + def __init__(self, name: str, prob: float = 1.0, *args, **kwargs) -> None: + CuCIM.__init__(self, name, *args, **kwargs) + RandomizableTransform.__init__(self, prob=prob) + + def __call__(self, data: cp_ndarray): + self.randomize(data) + if not self._do_transform: + return data + return super().__call__(data) diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index 2699ca0a9d..9545095251 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -18,7 +18,7 @@ import copy import logging from copy import deepcopy -from typing import Any, Callable, Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Union import numpy as np import torch @@ -62,6 +62,11 @@ from monai.utils import convert_to_numpy, ensure_tuple, ensure_tuple_rep from monai.utils.enums import InverseKeys, TransformBackends +if TYPE_CHECKING: + from cupy import ndarray as cp_ndarray +else: + cp_ndarray, _ = optional_import("cupy", name="ndarray") + __all__ = [ "AddChannelD", "AddChannelDict", @@ -87,6 +92,9 @@ "CopyItemsD", "CopyItemsDict", "CopyItemsd", + "CuCIMd", + "CuCIMD", + "CuCIMDict", "DataStatsD", "DataStatsDict", "DataStatsd", @@ -117,6 +125,9 @@ "MapLabelValueD", "MapLabelValueDict", "MapLabelValued", + "RandCuCIMd", + "RandCuCIMD", + "RandCuCIMDict", "RandLambdaD", "RandLambdaDict", "RandLambdad", @@ -1433,6 +1444,14 @@ class CuCIMd(MapTransform): Dictionary-based wrapper of :py:class:`monai.transforms.CuCIM` for non-randomized transforms. For randomized transforms of CuCIM use :py:class:`monai.transforms.RandCuCIMd`. + Args: + keys: keys of the corresponding items to be transformed. + See also: :py:class:`monai.transforms.compose.MapTransform` + name: The transform name in CuCIM package. + allow_missing_keys: don't raise exception if key is missing. + args: parameters for the CuCIM transform. + kwargs: parameters for the CuCIM transform. + Note: CuCIM transforms only work with CuPy arrays, this transform expects input data to be `cupy.ndarray`. Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. @@ -1446,41 +1465,56 @@ def __init__( *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 CuCIM package. - allow_missing_keys: don't raise exception if key is missing. - args: parameters for the CuCIM transform. - kwargs: parameters for the CuCIM transform. - - """ - super().__init__(keys, allow_missing_keys) + super().__init__(keys=keys, allow_missing_keys=allow_missing_keys) self.trans = CuCIM(name, *args, **kwargs) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Mapping[Hashable, cp_ndarray]: d = dict(data) for key in self.key_iterator(d): d[key] = self.trans(d[key]) return d -class RandCuCIMd(Randomizable, CuCIMd): +class RandCuCIMd(CuCIMd, RandomizableTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.CuCIM` for randomized transforms. For deterministic non-randomized transforms of CuCIM use :py:class:`monai.transforms.CuCIMd`. - Note: - - CuCIM transforms only work with CuPy arrays, this transform expects input data to be `cupy.ndarray`. - Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. - - This class inherits the ``Randomizable`` purely to prevent any dataset caching to skip the transform - computation. If the random factor of the underlying cuCIM transform is not derived from `self.R`, - the results may not be deterministic. - See Also: :py:class:`monai.transforms.Randomizable`. + Args: + keys: keys of the corresponding items to be transformed. + See also: :py:class:`monai.transforms.compose.MapTransform` + name: The transform name in CuCIM package. + prob: the probability to apply the transform (default=1.0) + allow_missing_keys: don't raise exception if key is missing. + args: parameters for the CuCIM transform. + kwargs: parameters for the CuCIM transform. + Note: + - CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + - If the cuCIM transform is already randomized, the `prob` argument has nothing to do with + the randomness of the underlying cuCIM transform. + It defines if the transform (either randomized or non-randomized) being applied randomly with `prob=1.0`, + so it can apply non-randomized tranforms randomly but be careful when it is being used along with randomized transforms. + - If the random factor of the underlying cuCIM transform is not derived from `self.R`, + the results may not be deterministic. See Also: :py:class:`monai.transforms.Randomizable`. """ + def __init__( + self, + prob: float = 1.0, + *args, + **kwargs, + ) -> None: + CuCIMd.__init__(self, *args, **kwargs) + RandomizableTransform.__init__(self, prob=prob) + + def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Mapping[Hashable, cp_ndarray]: + self.randomize(data) + if not self._do_transform: + return data + return super().__call__(data) + IdentityD = IdentityDict = Identityd AsChannelFirstD = AsChannelFirstDict = AsChannelFirstd From c9b3198f67d5f5d529475a9e2333570981e3dfe1 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:05:28 +0000 Subject: [PATCH 07/21] Add optional_import Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/dictionary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index 9545095251..a156e88f88 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -25,7 +25,7 @@ from monai.config import DtypeLike, KeysCollection from monai.config.type_definitions import NdarrayOrTensor -from monai.data.utils import no_collation +from monai.data.utils import no_collation, optional_import from monai.transforms.inverse import InvertibleTransform from monai.transforms.transform import MapTransform, Randomizable, RandomizableTransform from monai.transforms.utility.array import ( From 64c11ee194e8c693520e5cb47c2c22616653a5d5 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:37:18 +0000 Subject: [PATCH 08/21] Update docs and docstring Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- docs/source/transforms.rst | 5 +++++ monai/transforms/utility/array.py | 7 ++++++- monai/transforms/utility/dictionary.py | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index ec2e589f1b..6b839a89ac 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -739,6 +739,11 @@ Utility :members: :special-members: __call__ +`RandCuCIM` +""""""" +.. autoclass:: RandCuCIM + :members: + :special-members: __call__ Dictionary Transforms --------------------- diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index c22f2652b9..fc865f9ba7 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1131,7 +1131,7 @@ def __init__(self, name: str, *args, **kwargs) -> None: def __call__(self, data: cp_ndarray): """ Args: - img: CuPy array for the cuCIM transform + img: a CuPy array (`cupy.ndarray`) for the cuCIM transform """ return self.trans(data) @@ -1165,6 +1165,11 @@ def __init__(self, name: str, prob: float = 1.0, *args, **kwargs) -> None: RandomizableTransform.__init__(self, prob=prob) def __call__(self, data: cp_ndarray): + """ + Args: + img: a CuPy array (`cupy.ndarray`) for the cuCIM transform + + """ self.randomize(data) if not self._do_transform: return data diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index a156e88f88..482dfc034b 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -1468,7 +1468,7 @@ def __init__( super().__init__(keys=keys, allow_missing_keys=allow_missing_keys) self.trans = CuCIM(name, *args, **kwargs) - def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Mapping[Hashable, cp_ndarray]: + def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Dict[Hashable, cp_ndarray]: d = dict(data) for key in self.key_iterator(d): d[key] = self.trans(d[key]) @@ -1509,10 +1509,10 @@ def __init__( CuCIMd.__init__(self, *args, **kwargs) RandomizableTransform.__init__(self, prob=prob) - def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Mapping[Hashable, cp_ndarray]: + def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Dict[Hashable, cp_ndarray]: self.randomize(data) if not self._do_transform: - return data + return dict(data) return super().__call__(data) From d433a22d0e904dc6a8a4f1f687310cecc5b884ab Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:42:40 +0000 Subject: [PATCH 09/21] Update cucim api for monai Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- 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 fc865f9ba7..091ff00147 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1125,7 +1125,7 @@ class CuCIM(Transform): def __init__(self, name: str, *args, **kwargs) -> None: super().__init__() - transform, _ = optional_import("cucim.core.operations", name=name) + transform, _ = optional_import("cucim.monai", name=name) self.trans = transform(*args, **kwargs) def __call__(self, data: cp_ndarray): From 2c260b973a5b20e9b0085cdc0b61825e0daffb1c Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:09:47 +0000 Subject: [PATCH 10/21] Fix docs Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- 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 6b839a89ac..b3937a82ac 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -740,7 +740,7 @@ Utility :special-members: __call__ `RandCuCIM` -""""""" +""""""""""" .. autoclass:: RandCuCIM :members: :special-members: __call__ From 4e7c1371a2387ec495b2ffa571b289599f5776a7 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Mon, 13 Sep 2021 21:06:43 +0000 Subject: [PATCH 11/21] Update imports Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- docs/source/transforms.rst | 1 + monai/transforms/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index b3937a82ac..0b0f9f0792 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -745,6 +745,7 @@ Utility :members: :special-members: __call__ + Dictionary Transforms --------------------- diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index b880652e26..63f33a0831 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -369,6 +369,7 @@ LabelToMask, Lambda, MapLabelValue, + RandCuCIM, RandLambda, RemoveRepeatedChannel, RepeatChannel, From 2500e09e3660095d52d86914baa7b69fd65ac29c Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Tue, 14 Sep 2021 15:06:20 +0000 Subject: [PATCH 12/21] Remove type checking for cupy.ndarray Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 17 ++++++++------- monai/transforms/utility/dictionary.py | 29 ++++++++++++++++++-------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 091ff00147..3cbb55d601 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -17,7 +17,7 @@ import sys import time import warnings -from typing import TYPE_CHECKING, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union import numpy as np import torch @@ -41,11 +41,6 @@ pil_image_fromarray, _ = optional_import("PIL.Image", name="fromarray") cp, has_cp = optional_import("cupy") -if TYPE_CHECKING: - from cupy import ndarray as cp_ndarray -else: - cp_ndarray, _ = optional_import("cupy", name="ndarray") - __all__ = [ "Identity", @@ -1128,11 +1123,14 @@ def __init__(self, name: str, *args, **kwargs) -> None: transform, _ = optional_import("cucim.monai", name=name) self.trans = transform(*args, **kwargs) - def __call__(self, data: cp_ndarray): + def __call__(self, data): """ Args: img: a CuPy array (`cupy.ndarray`) for the cuCIM transform + Returns: + `cupy.ndarray` + """ return self.trans(data) @@ -1164,11 +1162,14 @@ def __init__(self, name: str, prob: float = 1.0, *args, **kwargs) -> None: CuCIM.__init__(self, name, *args, **kwargs) RandomizableTransform.__init__(self, prob=prob) - def __call__(self, data: cp_ndarray): + def __call__(self, data): """ Args: img: a CuPy array (`cupy.ndarray`) for the cuCIM transform + Returns: + `cupy.ndarray` + """ self.randomize(data) if not self._do_transform: diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index 482dfc034b..f9bf78b21f 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -18,14 +18,14 @@ import copy import logging from copy import deepcopy -from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Any, Callable, Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Union import numpy as np import torch from monai.config import DtypeLike, KeysCollection from monai.config.type_definitions import NdarrayOrTensor -from monai.data.utils import no_collation, optional_import +from monai.data.utils import no_collation from monai.transforms.inverse import InvertibleTransform from monai.transforms.transform import MapTransform, Randomizable, RandomizableTransform from monai.transforms.utility.array import ( @@ -62,11 +62,6 @@ from monai.utils import convert_to_numpy, ensure_tuple, ensure_tuple_rep from monai.utils.enums import InverseKeys, TransformBackends -if TYPE_CHECKING: - from cupy import ndarray as cp_ndarray -else: - cp_ndarray, _ = optional_import("cupy", name="ndarray") - __all__ = [ "AddChannelD", "AddChannelDict", @@ -1468,7 +1463,15 @@ def __init__( super().__init__(keys=keys, allow_missing_keys=allow_missing_keys) self.trans = CuCIM(name, *args, **kwargs) - def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Dict[Hashable, cp_ndarray]: + def __call__(self, data): + """ + Args: + data: Dict[Hashable, `cupy.ndarray`] + + Returns: + Dict[Hashable, `cupy.ndarray`] + + """ d = dict(data) for key in self.key_iterator(d): d[key] = self.trans(d[key]) @@ -1509,7 +1512,15 @@ def __init__( CuCIMd.__init__(self, *args, **kwargs) RandomizableTransform.__init__(self, prob=prob) - def __call__(self, data: Mapping[Hashable, cp_ndarray]) -> Dict[Hashable, cp_ndarray]: + def __call__(self, data): + """ + Args: + data: Dict[Hashable, `cupy.ndarray`] + + Returns: + Dict[Hashable, `cupy.ndarray`] + + """ self.randomize(data) if not self._do_transform: return dict(data) From 1715212892b0bb067430c735c0a8a2b48a4aea42 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Tue, 14 Sep 2021 20:37:29 +0000 Subject: [PATCH 13/21] Update cuCIM exposed API Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- 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 63c535fcd5..e2e10acaa2 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1131,7 +1131,7 @@ class CuCIM(Transform): def __init__(self, name: str, *args, **kwargs) -> None: super().__init__() - transform, _ = optional_import("cucim.monai", name=name) + transform, _ = optional_import("cucim.core.operations.expose.transform", name=name) self.trans = transform(*args, **kwargs) def __call__(self, data): From 3ca5d88b49e575b37589edb7faf7a03f7229f36e Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Wed, 15 Sep 2021 02:41:37 +0000 Subject: [PATCH 14/21] Update cucim transfom calls Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index e2e10acaa2..55e9e4e93c 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1131,8 +1131,9 @@ class CuCIM(Transform): def __init__(self, name: str, *args, **kwargs) -> None: super().__init__() - transform, _ = optional_import("cucim.core.operations.expose.transform", name=name) - self.trans = transform(*args, **kwargs) + self.transform, _ = optional_import("cucim.core.operations.expose.transform", name=name) + self.args = args + self.kwargs = kwargs def __call__(self, data): """ @@ -1143,7 +1144,7 @@ def __call__(self, data): `cupy.ndarray` """ - return self.trans(data) + return self.transform(data, *self.args, **self.kwargs) class RandCuCIM(CuCIM, RandomizableTransform): From fca930d8f8456a5db7b5fd613fd28ec164d8b088 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Wed, 15 Sep 2021 03:41:46 +0000 Subject: [PATCH 15/21] fix typo Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 55e9e4e93c..732b396318 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1138,7 +1138,7 @@ def __init__(self, name: str, *args, **kwargs) -> None: def __call__(self, data): """ Args: - img: a CuPy array (`cupy.ndarray`) for the cuCIM transform + data: a CuPy array (`cupy.ndarray`) for the cuCIM transform Returns: `cupy.ndarray` @@ -1177,7 +1177,7 @@ def __init__(self, name: str, prob: float = 1.0, *args, **kwargs) -> None: def __call__(self, data): """ Args: - img: a CuPy array (`cupy.ndarray`) for the cuCIM transform + data: a CuPy array (`cupy.ndarray`) for the cuCIM transform Returns: `cupy.ndarray` From 22a3ec9b9e15dd1b45b9f9aa3e90278d93b194a2 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Wed, 15 Sep 2021 22:39:29 +0000 Subject: [PATCH 16/21] Rename prob to apply_prob Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 14 +++++++------- monai/transforms/utility/dictionary.py | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index e84ae50e7c..6df2833fcb 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1170,24 +1170,24 @@ class RandCuCIM(CuCIM, RandomizableTransform): Args: name: the transform name in CuCIM package. - prob: the probability to apply the transform (default=1.0) + apply_prob: the probability to apply the transform (default=1.0) args: parameters for the CuCIM transform. kwargs: parameters for the CuCIM transform. Note: - CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. - - If the cuCIM transform is already randomized, the `prob` argument has nothing to do with - the randomness of the underlying cuCIM transform. - It defines if the transform (either randomized or non-randomized) being applied randomly with `prob=1.0`, - so it can apply non-randomized tranforms randomly but be careful when it is being used along with randomized transforms. + - If the cuCIM transform is already randomized the `apply_prob` argument has nothing to do with + the randomness of the underlying cuCIM transform. `apply_prob` defines if the transform (either randomized + or non-randomized) being applied randomly, so it can apply non-randomized tranforms randomly but be careful + with setting `apply_prob` to anything than 1.0 when using along with cuCIM's randomized transforms. - If the random factor of the underlying cuCIM transform is not derived from `self.R`, the results may not be deterministic. See Also: :py:class:`monai.transforms.Randomizable`. """ - def __init__(self, name: str, prob: float = 1.0, *args, **kwargs) -> None: + def __init__(self, name: str, apply_prob: float = 1.0, *args, **kwargs) -> None: CuCIM.__init__(self, name, *args, **kwargs) - RandomizableTransform.__init__(self, prob=prob) + RandomizableTransform.__init__(self, prob=apply_prob) def __call__(self, data): """ diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index 3094b06e12..f1478d2c28 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -1525,7 +1525,7 @@ class RandCuCIMd(CuCIMd, RandomizableTransform): keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` name: The transform name in CuCIM package. - prob: the probability to apply the transform (default=1.0) + apply_prob: the probability to apply the transform (default=1.0) allow_missing_keys: don't raise exception if key is missing. args: parameters for the CuCIM transform. kwargs: parameters for the CuCIM transform. @@ -1533,22 +1533,22 @@ class RandCuCIMd(CuCIMd, RandomizableTransform): Note: - CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. - - If the cuCIM transform is already randomized, the `prob` argument has nothing to do with - the randomness of the underlying cuCIM transform. - It defines if the transform (either randomized or non-randomized) being applied randomly with `prob=1.0`, - so it can apply non-randomized tranforms randomly but be careful when it is being used along with randomized transforms. + - If the cuCIM transform is already randomized the `apply_prob` argument has nothing to do with + the randomness of the underlying cuCIM transform. `apply_prob` defines if the transform (either randomized + or non-randomized) being applied randomly, so it can apply non-randomized tranforms randomly but be careful + with setting `apply_prob` to anything than 1.0 when using along with cuCIM's randomized transforms. - If the random factor of the underlying cuCIM transform is not derived from `self.R`, the results may not be deterministic. See Also: :py:class:`monai.transforms.Randomizable`. """ def __init__( self, - prob: float = 1.0, + apply_prob: float = 1.0, *args, **kwargs, ) -> None: CuCIMd.__init__(self, *args, **kwargs) - RandomizableTransform.__init__(self, prob=prob) + RandomizableTransform.__init__(self, prob=apply_prob) def __call__(self, data): """ From b996567af34c4732b56bfb5bddc7bb1d81f72b3a Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Wed, 15 Sep 2021 23:12:11 +0000 Subject: [PATCH 17/21] Add unittests for CuCIM tranform Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- tests/test_cucim_transform.py | 296 ++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 tests/test_cucim_transform.py diff --git a/tests/test_cucim_transform.py b/tests/test_cucim_transform.py new file mode 100644 index 0000000000..c34ce1df9a --- /dev/null +++ b/tests/test_cucim_transform.py @@ -0,0 +1,296 @@ +# 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 numpy as np +from parameterized import parameterized + +from monai.transforms import CuCIM +from monai.utils import optional_import, set_determinism +from tests.utils import skip_if_no_cuda + +_, has_cut = optional_import("cucim.core.operations.expose.transform") +cp, has_cp = optional_import("cupy") + +set_determinism(seed=0) + +TEST_CASE_COLOR_JITTER_1 = [ + {"name": "color_jitter", "brightness": 0.0, "contrast": 0.0, "saturation": 0.0, "hue": 0.0}, + np.array( + [ + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_COLOR_JITTER_2 = [ + {"name": "color_jitter", "brightness": 0.0, "contrast": 0.0, "saturation": 0.0, "hue": 0.0}, + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + ], + dtype=np.uint8, + ), + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + ], + dtype=np.uint8, + ), +] + +TEST_CASE_FLIP_1 = [ + {"name": "image_flip", "spatial_axis": -1}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + + +TEST_CASE_ROTATE_1 = [ + {"name": "image_rotate_90", "k": 1, "spatial_axis": (-2, -1)}, + np.array( + [ + [[0.0, 1.0], [2.0, 3.0]], + [[0.0, 1.0], [2.0, 3.0]], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_SCALE_INTENSITY_1 = [ + {"name": "scale_intensity_range", "a_min": 0.0, "a_max": 4.0, "b_min": 0.0, "b_max": 1.0, "clip": False}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [[0.0, 1.0], [2.0, 3.0]], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_ZOOM_1 = [ + {"name": "zoom", "zoom_factor": (0.5, 0.5)}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.concatenate( + [ + np.ones((1, 3, 3), dtype=np.float32) * 1.0, + np.ones((1, 3, 3), dtype=np.float32) * 2.0, + ] + ), +] + + +@skip_if_no_cuda +@unittest.skipUnless(has_cp, "CuPy is required.") +@unittest.skipUnless(has_cut, "cuCIM transforms are required.") +class TestCuCIM(unittest.TestCase): + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_numpy_single(self, params, input, expected): + output = CuCIM(**params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_numpy_batch(self, params, input, expected): + input = input[cp.newaxis, ...] + expected = expected[cp.newaxis, ...] + output = CuCIM(**params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_cupy_single(self, params, input, expected): + input = cp.asarray(input) + expected = cp.asarray(expected) + output = CuCIM(**params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_cupy_batch(self, params, input, expected): + input = cp.asarray(input)[cp.newaxis, ...] + expected = cp.asarray(expected)[cp.newaxis, ...] + output = CuCIM(**params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + + +if __name__ == "__main__": + unittest.main() From 628b139067f6ecdc8347debd42ae3c07bddd1de6 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Wed, 15 Sep 2021 23:27:26 +0000 Subject: [PATCH 18/21] Add unittests for CuCIMDict transform Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- tests/test_cucim_dict_transform.py | 297 +++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 tests/test_cucim_dict_transform.py diff --git a/tests/test_cucim_dict_transform.py b/tests/test_cucim_dict_transform.py new file mode 100644 index 0000000000..4196e895b1 --- /dev/null +++ b/tests/test_cucim_dict_transform.py @@ -0,0 +1,297 @@ +# 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 numpy as np +from parameterized import parameterized + +from monai.transforms import CuCIMd +from monai.utils import optional_import, set_determinism +from tests.utils import skip_if_no_cuda + +_, has_cut = optional_import("cucim.core.operations.expose.transform") +cp, has_cp = optional_import("cupy") + +set_determinism(seed=0) + +TEST_CASE_COLOR_JITTER_1 = [ + {"name": "color_jitter", "brightness": 0.0, "contrast": 0.0, "saturation": 0.0, "hue": 0.0}, + np.array( + [ + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + [ + [0.0, 1.0], + [1.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_COLOR_JITTER_2 = [ + {"name": "color_jitter", "brightness": 0.0, "contrast": 0.0, "saturation": 0.0, "hue": 0.0}, + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + ], + dtype=np.uint8, + ), + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + [ + [0, 1], + [2, 3], + ], + ], + dtype=np.uint8, + ), +] + +TEST_CASE_FLIP_1 = [ + {"name": "image_flip", "spatial_axis": -1}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + + +TEST_CASE_ROTATE_1 = [ + {"name": "image_rotate_90", "k": 1, "spatial_axis": (-2, -1)}, + np.array( + [ + [[0.0, 1.0], [2.0, 3.0]], + [[0.0, 1.0], [2.0, 3.0]], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_SCALE_INTENSITY_1 = [ + {"name": "scale_intensity_range", "a_min": 0.0, "a_max": 4.0, "b_min": 0.0, "b_max": 1.0, "clip": False}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [[0.0, 1.0], [2.0, 3.0]], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_ZOOM_1 = [ + {"name": "zoom", "zoom_factor": (0.5, 0.5)}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.concatenate( + [ + np.ones((1, 3, 3), dtype=np.float32) * 1.0, + np.ones((1, 3, 3), dtype=np.float32) * 2.0, + ] + ), +] + + +@skip_if_no_cuda +@unittest.skipUnless(has_cp, "CuPy is required.") +@unittest.skipUnless(has_cut, "cuCIM transforms are required.") +class TestCuCIMDict(unittest.TestCase): + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_numpy_single(self, params, input, expected): + input = {"image": input} + output = CuCIMd(keys="image", **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_numpy_batch(self, params, input, expected): + input = {"image": input[cp.newaxis, ...]} + expected = expected[cp.newaxis, ...] + output = CuCIMd(keys="image", **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_cupy_single(self, params, input, expected): + input = {"image": cp.asarray(input)} + expected = cp.asarray(expected) + output = CuCIMd(keys="image", **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_COLOR_JITTER_2, + TEST_CASE_FLIP_1, + TEST_CASE_ROTATE_1, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + ] + ) + def test_tramsforms_cupy_batch(self, params, input, expected): + input = {"image": cp.asarray(input)[cp.newaxis, ...]} + expected = cp.asarray(expected)[cp.newaxis, ...] + output = CuCIMd(keys="image", **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + + +if __name__ == "__main__": + unittest.main() From e464c134184ce1fb610642101833ef09816daf16 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Thu, 16 Sep 2021 02:20:07 +0000 Subject: [PATCH 19/21] Add unittests for RandCuCIM transform Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- tests/test_rand_cucim_transform.py | 354 +++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 tests/test_rand_cucim_transform.py diff --git a/tests/test_rand_cucim_transform.py b/tests/test_rand_cucim_transform.py new file mode 100644 index 0000000000..3760f56283 --- /dev/null +++ b/tests/test_rand_cucim_transform.py @@ -0,0 +1,354 @@ +# 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 numpy as np +from parameterized import parameterized + +from monai.transforms import RandCuCIM +from monai.utils import optional_import, set_determinism +from tests.utils import skip_if_no_cuda + +_, has_cut = optional_import("cucim.core.operations.expose.transform") +cp, has_cp = optional_import("cupy") + +set_determinism(seed=0) + +TEST_CASE_COLOR_JITTER_1 = [ + {"name": "color_jitter", "brightness": 0.0, "contrast": 0.0, "saturation": 0.0, "hue": 0.0}, + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 10], + [20, 30], + ], + [ + [0, 50], + [100, 150], + ], + ], + dtype=np.uint8, + ), + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 10], + [20, 30], + ], + [ + [0, 50], + [100, 150], + ], + ], + dtype=np.uint8, + ), +] + +TEST_CASE_FLIP_1 = [ + {"name": "image_flip", "spatial_axis": -1}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_RAND_ROTATE_1 = [ + {"name": "rand_image_rotate_90", "prob": 1.0, "max_k": 1, "spatial_axis": (-2, -1)}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + + +TEST_CASE_RAND_ROTATE_2 = [ + {"name": "rand_image_rotate_90", "prob": 0.0, "max_k": 1, "spatial_axis": (-2, -1)}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_SCALE_INTENSITY_1 = [ + {"name": "scale_intensity_range", "a_min": 0.0, "a_max": 4.0, "b_min": 0.0, "b_max": 1.0, "clip": False}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_ZOOM_1 = [ + {"name": "zoom", "zoom_factor": (0.5, 0.5)}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.concatenate( + [ + np.ones((1, 3, 3), dtype=np.float32) * 1.0, + np.ones((1, 3, 3), dtype=np.float32) * 2.0, + ] + ), +] + +TEST_CASE_RAND_ZOOM_1 = [ + {"name": "rand_zoom", "prob": 1.0, "min_zoom": 0.5, "max_zoom": 0.5}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.concatenate( + [ + np.ones((1, 3, 3), dtype=np.float32) * 1.0, + np.ones((1, 3, 3), dtype=np.float32) * 2.0, + ] + ), +] + +TEST_CASE_RAND_ZOOM_2 = [ + {"name": "rand_zoom", "prob": 0.0, "min_zoom": 0.5, "max_zoom": 0.5}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.mgrid[:3, 1:4].astype(dtype=np.float32), +] + + +@skip_if_no_cuda +@unittest.skipUnless(has_cp, "CuPy is required.") +@unittest.skipUnless(has_cut, "cuCIM transforms are required.") +class TestRandCuCIM(unittest.TestCase): + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_numpy_single(self, params, input, expected): + # apply_prob=1.0 + output = RandCuCIM(apply_prob=1.0, **params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIM(apply_prob=0.0, **params)(input) + self.assertTrue(output.dtype == input.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, input) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_numpy_batch(self, params, input, expected): + input = input[cp.newaxis, ...] + expected = expected[cp.newaxis, ...] + # apply_prob=1.0 + output = RandCuCIM(apply_prob=1.0, **params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIM(apply_prob=0.0, **params)(input) + self.assertTrue(output.dtype == input.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, input) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_cupy_single(self, params, input, expected): + input = cp.asarray(input) + expected = cp.asarray(expected) + # apply_prob=1.0 + output = RandCuCIM(apply_prob=1.0, **params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIM(apply_prob=0.0, **params)(input) + self.assertTrue(output.dtype == input.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, input) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_cupy_batch(self, params, input, expected): + input = cp.asarray(input)[cp.newaxis, ...] + expected = cp.asarray(expected)[cp.newaxis, ...] + # apply_prob=1.0 + output = RandCuCIM(**params)(input) + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIM(apply_prob=0.0, **params)(input) + self.assertTrue(output.dtype == input.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, input) + + +if __name__ == "__main__": + unittest.main() From 41553ea4745cdddfccbec0ec79fe85985685a597 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Thu, 16 Sep 2021 02:24:34 +0000 Subject: [PATCH 20/21] Add unittests for RandCuCIMDict transform Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- tests/test_rand_cucim_dict_transform.py | 355 ++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 tests/test_rand_cucim_dict_transform.py diff --git a/tests/test_rand_cucim_dict_transform.py b/tests/test_rand_cucim_dict_transform.py new file mode 100644 index 0000000000..beaccaac05 --- /dev/null +++ b/tests/test_rand_cucim_dict_transform.py @@ -0,0 +1,355 @@ +# 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 numpy as np +from parameterized import parameterized + +from monai.transforms import RandCuCIMd +from monai.utils import optional_import, set_determinism +from tests.utils import skip_if_no_cuda + +_, has_cut = optional_import("cucim.core.operations.expose.transform") +cp, has_cp = optional_import("cupy") + +set_determinism(seed=0) + +TEST_CASE_COLOR_JITTER_1 = [ + {"name": "color_jitter", "brightness": 0.0, "contrast": 0.0, "saturation": 0.0, "hue": 0.0}, + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 10], + [20, 30], + ], + [ + [0, 50], + [100, 150], + ], + ], + dtype=np.uint8, + ), + np.array( + [ + [ + [0, 1], + [2, 3], + ], + [ + [0, 10], + [20, 30], + ], + [ + [0, 50], + [100, 150], + ], + ], + dtype=np.uint8, + ), +] + +TEST_CASE_FLIP_1 = [ + {"name": "image_flip", "spatial_axis": -1}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + [ + [1.0, 0.0], + [3.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_RAND_ROTATE_1 = [ + {"name": "rand_image_rotate_90", "prob": 1.0, "max_k": 1, "spatial_axis": (-2, -1)}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + [ + [1.0, 3.0], + [0.0, 2.0], + ], + ], + dtype=np.float32, + ), +] + + +TEST_CASE_RAND_ROTATE_2 = [ + {"name": "rand_image_rotate_90", "prob": 0.0, "max_k": 1, "spatial_axis": (-2, -1)}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_SCALE_INTENSITY_1 = [ + {"name": "scale_intensity_range", "a_min": 0.0, "a_max": 4.0, "b_min": 0.0, "b_max": 1.0, "clip": False}, + np.array( + [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [0.0, 1.0], + [2.0, 3.0], + ], + ], + dtype=np.float32, + ), + np.array( + [ + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + [ + [0.0, 0.25], + [0.5, 0.75], + ], + ], + dtype=np.float32, + ), +] + +TEST_CASE_ZOOM_1 = [ + {"name": "zoom", "zoom_factor": (0.5, 0.5)}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.concatenate( + [ + np.ones((1, 3, 3), dtype=np.float32) * 1.0, + np.ones((1, 3, 3), dtype=np.float32) * 2.0, + ] + ), +] + +TEST_CASE_RAND_ZOOM_1 = [ + {"name": "rand_zoom", "prob": 1.0, "min_zoom": 0.5, "max_zoom": 0.5}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.concatenate( + [ + np.ones((1, 3, 3), dtype=np.float32) * 1.0, + np.ones((1, 3, 3), dtype=np.float32) * 2.0, + ] + ), +] + +TEST_CASE_RAND_ZOOM_2 = [ + {"name": "rand_zoom", "prob": 0.0, "min_zoom": 0.5, "max_zoom": 0.5}, + np.mgrid[:3, 1:4].astype(dtype=np.float32), + np.mgrid[:3, 1:4].astype(dtype=np.float32), +] + + +@skip_if_no_cuda +@unittest.skipUnless(has_cp, "CuPy is required.") +@unittest.skipUnless(has_cut, "cuCIM transforms are required.") +class TestRandCuCIMDict(unittest.TestCase): + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_numpy_single(self, params, input, expected): + input = {"image": input} + # apply_prob=1.0 + output = RandCuCIMd(keys="image", apply_prob=1.0, **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIMd(keys="image", apply_prob=0.0, **params)(input)["image"] + self.assertTrue(output.dtype == input["image"].dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, input["image"]) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_numpy_batch(self, params, input, expected): + input = {"image": input[cp.newaxis, ...]} + expected = expected[cp.newaxis, ...] + # apply_prob=1.0 + output = RandCuCIMd(keys="image", apply_prob=1.0, **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIMd(keys="image", apply_prob=0.0, **params)(input)["image"] + self.assertTrue(output.dtype == input["image"].dtype) + self.assertTrue(isinstance(output, np.ndarray)) + cp.testing.assert_allclose(output, input["image"]) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_cupy_single(self, params, input, expected): + input = {"image": cp.asarray(input)} + expected = cp.asarray(expected) + # apply_prob=1.0 + output = RandCuCIMd(keys="image", apply_prob=1.0, **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIMd(keys="image", apply_prob=0.0, **params)(input)["image"] + self.assertTrue(output.dtype == input["image"].dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, input["image"]) + + @parameterized.expand( + [ + TEST_CASE_COLOR_JITTER_1, + TEST_CASE_FLIP_1, + TEST_CASE_RAND_ROTATE_1, + TEST_CASE_RAND_ROTATE_2, + TEST_CASE_SCALE_INTENSITY_1, + TEST_CASE_ZOOM_1, + TEST_CASE_RAND_ZOOM_1, + TEST_CASE_RAND_ZOOM_2, + ] + ) + def test_tramsforms_cupy_batch(self, params, input, expected): + input = {"image": cp.asarray(input)[cp.newaxis, ...]} + expected = cp.asarray(expected)[cp.newaxis, ...] + # apply_prob=1.0 + output = RandCuCIMd(keys="image", **params)(input)["image"] + self.assertTrue(output.dtype == expected.dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, expected) + # apply_prob=0.0 + output = RandCuCIMd(keys="image", apply_prob=0.0, **params)(input)["image"] + self.assertTrue(output.dtype == input["image"].dtype) + self.assertTrue(isinstance(output, cp.ndarray)) + cp.testing.assert_allclose(output, input["image"]) + + +if __name__ == "__main__": + unittest.main() From 1f6b6e27e684e560ecfd1d0c8bdc187ecd192b01 Mon Sep 17 00:00:00 2001 From: Behrooz <3968947+drbeh@users.noreply.github.com> Date: Thu, 16 Sep 2021 02:33:47 +0000 Subject: [PATCH 21/21] Fix docstrigs Signed-off-by: Behrooz <3968947+drbeh@users.noreply.github.com> --- monai/transforms/utility/array.py | 6 +++--- monai/transforms/utility/dictionary.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index c9a2c8d052..b2380f9207 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -1194,11 +1194,11 @@ class RandCuCIM(CuCIM, RandomizableTransform): Note: - CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. - Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. - If the cuCIM transform is already randomized the `apply_prob` argument has nothing to do with the randomness of the underlying cuCIM transform. `apply_prob` defines if the transform (either randomized - or non-randomized) being applied randomly, so it can apply non-randomized tranforms randomly but be careful - with setting `apply_prob` to anything than 1.0 when using along with cuCIM's randomized transforms. + or non-randomized) being applied randomly, so it can apply non-randomized tranforms randomly but be careful + with setting `apply_prob` to anything than 1.0 when using along with cuCIM's randomized transforms. - If the random factor of the underlying cuCIM transform is not derived from `self.R`, the results may not be deterministic. See Also: :py:class:`monai.transforms.Randomizable`. """ diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index e0892c15b3..695753a07d 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -1548,11 +1548,11 @@ class RandCuCIMd(CuCIMd, RandomizableTransform): Note: - CuCIM transform only work with CuPy arrays, so this transform expects input data to be `cupy.ndarray`. - Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. + Users can call `ToCuPy` transform to convert a numpy array or torch tensor to cupy array. - If the cuCIM transform is already randomized the `apply_prob` argument has nothing to do with the randomness of the underlying cuCIM transform. `apply_prob` defines if the transform (either randomized - or non-randomized) being applied randomly, so it can apply non-randomized tranforms randomly but be careful - with setting `apply_prob` to anything than 1.0 when using along with cuCIM's randomized transforms. + or non-randomized) being applied randomly, so it can apply non-randomized tranforms randomly but be careful + with setting `apply_prob` to anything than 1.0 when using along with cuCIM's randomized transforms. - If the random factor of the underlying cuCIM transform is not derived from `self.R`, the results may not be deterministic. See Also: :py:class:`monai.transforms.Randomizable`. """