From 06d5842fcfb31375ae1dc6e8de065816d52b278a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 27 Mar 2023 22:43:32 +0100 Subject: [PATCH 001/175] Initial commit Signed-off-by: Ben Murray --- monai/data/dataset.py | 62 ++++++++++++++++++-------- monai/transforms/compose.py | 32 +++---------- monai/transforms/lazy/functional.py | 25 ++++++++++- monai/transforms/transform.py | 36 +++++++++++++-- tests/test_integration_lazy_samples.py | 12 +++-- 5 files changed, 113 insertions(+), 54 deletions(-) diff --git a/monai/data/dataset.py b/monai/data/dataset.py index 5ef8d7e903..fe6700ebbd 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -45,6 +45,7 @@ convert_to_contiguous, reset_ops_id, ) +from monai.transforms.lazy.functional import execute_pending_transforms from monai.utils import MAX_SEED, get_seed, look_up_option, min_version, optional_import from monai.utils.misc import first @@ -322,9 +323,13 @@ def _pre_transform(self, item_transformed): break # this is to be consistent with CacheDataset even though it's not in a multi-thread situation. _xform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform - item_transformed = self.transform.evaluate_with_overrides(item_transformed, _xform) - item_transformed = apply_transform(_xform, item_transformed) - item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, _xform) + # item_transformed = apply_transform(_xform, item_transformed) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + item_transformed = apply_transform(_transform, item_transformed, + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) + item_transformed = execute_pending_transforms(item_transformed, self.transform.overrides) if self.reset_ops_id: reset_ops_id(item_transformed) return item_transformed @@ -350,9 +355,13 @@ def _post_transform(self, item_transformed): or not isinstance(_transform, Transform) ): start_post_randomize_run = True - item_transformed = self.transform.evaluate_with_overrides(item_transformed, _transform) - item_transformed = apply_transform(_transform, item_transformed) - item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, _transform) + # item_transformed = apply_transform(_transform, item_transformed) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + item_transformed = apply_transform(_transform, item_transformed, + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) + item_transformed = execute_pending_transforms(item_transformed, self.transform.overrides) return item_transformed def _cachecheck(self, item_transformed): @@ -500,9 +509,13 @@ def _pre_transform(self, item_transformed): if i == self.cache_n_trans: break _xform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform - item_transformed = self.transform.evaluate_with_overrides(item_transformed, _xform) - item_transformed = apply_transform(_xform, item_transformed) - item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, _xform) + # item_transformed = apply_transform(_xform, item_transformed) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + item_transformed = apply_transform(_transform, item_transformed, + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) + item_transformed = execute_pending_transforms(item_transformed, self.transform.overrides) reset_ops_id(item_transformed) return item_transformed @@ -520,9 +533,13 @@ def _post_transform(self, item_transformed): raise ValueError("transform must be an instance of monai.transforms.Compose.") for i, _transform in enumerate(self.transform.transforms): if i >= self.cache_n_trans: - item_transformed = self.transform.evaluate_with_overrides(item_transformed, item_transformed) - item_transformed = apply_transform(_transform, item_transformed) - item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, item_transformed) + # item_transformed = apply_transform(_transform, item_transformed) + # item_transformed = self.transform.evaluate_with_overrides(item_transformed, None) + item_transformed = apply_transform(_transform, item_transformed, + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) + item_transformed = execute_pending_transforms(item_transformed, self.transform.overrides) return item_transformed @@ -892,9 +909,13 @@ def _load_cache_item(self, idx: int): if isinstance(_transform, RandomizableTrait) or not isinstance(_transform, Transform): break _xform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform - item = self.transform.evaluate_with_overrides(item, _xform) - item = apply_transform(_xform, item) - item = self.transform.evaluate_with_overrides(item, None) + # item = self.transform.evaluate_with_overrides(item, _xform) + # item = apply_transform(_xform, item) + # item = self.transform.evaluate_with_overrides(item, None) + item = apply_transform(_transform, item, + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) + item = execute_pending_transforms(item, self.transform.overrides) if self.as_contiguous: item = convert_to_contiguous(item, memory_format=torch.contiguous_format) return item @@ -931,9 +952,14 @@ def _transform(self, index: int): start_run = True if self.copy_cache: data = deepcopy(data) - data = self.transform.evaluate_with_overrides(data, _transform) - data = apply_transform(_transform, data) - data = self.transform.evaluate_with_overrides(data, None) + # data = self.transform.evaluate_with_overrides(data, _transform) + # data = apply_transform(_transform, data) + # data = self.transform.evaluate_with_overrides(data, None) + + data = apply_transform(_transform, data, + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) + data = execute_pending_transforms(data, self.transform.overrides) return data diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 6cdd1b3d55..244f8b5e34 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -26,6 +26,7 @@ from monai.transforms.inverse import InvertibleTransform # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) +from monai.transforms.lazy.functional import execute_pending_transforms from monai.transforms.transform import ( # noqa: F401 LazyTransform, MapTransform, @@ -38,7 +39,7 @@ logger = get_logger(__name__) -__all__ = ["Compose", "OneOf", "RandomOrder", "evaluate_with_overrides", "SomeOf"] +__all__ = ["Compose", "OneOf", "RandomOrder", "SomeOf"] def evaluate_with_overrides( @@ -76,11 +77,7 @@ def evaluate_with_overrides( return data # eager evaluation overrides = (overrides or {}).copy() if isinstance(data, monai.data.MetaTensor): - if data.has_pending_operations and ( - (upcoming is None) - or (isinstance(upcoming, mt.Identity)) - or (isinstance(upcoming, mt.Identityd) and override_keys in upcoming.keys) - ): + if data.has_pending_operations and ((isinstance(upcoming, (mt.Identityd, mt.Identity))) or upcoming is None): data, _ = mt.apply_transforms(data, None, overrides=overrides) if verbose: next_name = "final output" if upcoming is None else f"'{upcoming.__class__.__name__}'" @@ -204,8 +201,6 @@ class Compose(Randomizable, InvertibleTransform): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - override_keys: this optional parameter specifies the keys to which ``overrides`` are to be applied. If - ``overrides`` is set, ``override_keys`` must also be set. verbose: whether to print debugging info when lazy_evaluation=True. """ @@ -278,26 +273,11 @@ def __len__(self): """Return number of transformations.""" return len(self.flatten().transforms) - def evaluate_with_overrides(self, input_, upcoming_xform): - """ - Args: - input_: input data to be transformed. - upcoming_xform: a transform used to determine whether to evaluate with override - """ - return evaluate_with_overrides( - input_, - upcoming_xform, - lazy_evaluation=self.lazy_evaluation, - overrides=self.overrides, - override_keys=self.override_keys, - verbose=self.verbose, - ) - def __call__(self, input_): for _transform in self.transforms: - input_ = self.evaluate_with_overrides(input_, _transform) - input_ = apply_transform(_transform, input_, self.map_items, self.unpack_items, self.log_stats) - input_ = self.evaluate_with_overrides(input_, None) + input_ = apply_transform(_transform, input_, self.map_items, self.unpack_items, self.log_stats, + lazy_evaluation=self.lazy_evaluation, overrides=self.overrides) + input_ = execute_pending_transforms(input_, self.overrides) return input_ def inverse(self, data): diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 334f271e05..23653dbdde 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -30,8 +30,29 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} +def execute_pending_transforms(data, overrides: dict = None, verbose: bool = False): + if isinstance(data, (tuple, list)): + return [execute_pending_transforms(d) for d in data] + + if isinstance(data, dict): + d = dict(data) + for k, v in d.items(): + if isinstance(v, MetaTensor) and v.has_pending_operations: + overrides_ = None if overrides is None else overrides[k] + d[k], _ = apply_transforms(d[k], overrides=overrides_, verbose=verbose) + return d + + if isinstance(data, MetaTensor) and data.has_pending_operations: + data, _ = apply_transforms(data, overrides=overrides, verbose=verbose) + return data + + def apply_transforms( - data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None, **kwargs: Any + data: torch.Tensor | MetaTensor, + pending: list | None = None, + overrides: dict | None = None, + verbose: bool | None = None, + # **kwargs: Any ): """ This method applies pending transforms to `data` tensors. @@ -65,7 +86,7 @@ def apply_transforms( """ overrides = (overrides or {}).copy() - overrides.update((kwargs or {}).copy()) + # overrides.update((kwargs or {}).copy()) for k in overrides: look_up_option(k, __override_keywords) # check existence of the key diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 3e66431bbc..fdc8e5c51e 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -25,6 +25,7 @@ from monai import config, transforms from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor +from monai.transforms.lazy.functional import execute_pending_transforms from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first from monai.utils.enums import TransformBackends @@ -44,7 +45,12 @@ def _apply_transform( - transform: Callable[..., ReturnType], parameters: Any, unpack_parameters: bool = False + transform: Callable[..., ReturnType], + parameters: Any, + unpack_parameters: bool = False, + lazy_evaluation: bool = False, + overrides: dict = None, + verbose: bool = False, ) -> ReturnType: """ Perform transformation `transform` with the provided parameters `parameters`. @@ -61,10 +67,28 @@ def _apply_transform( Returns: ReturnType: The return type of `transform`. """ + + if isinstance(parameters, tuple) and unpack_parameters: + data = parameters[0] + else: + data = parameters + + # For the 1.2 release, we are limited here to having executing transforms that + # are lazy but set to not be lazy _after_ we have applied the pending list. This + # is because the transform implementations for 1.2 don't have unified code paths for + # lazy and non-lazy operation, so it is not possible to pass a tensor with pending + # operations and have the transform handle them correctly. + # In order to have this functionality for 1.2, we need to provide lazy_evaluation + # overrides on __call__ methods for lazy array and dictionary transforms. + if not isinstance(transform, LazyTrait) or transform.lazy_evaluation is False: + # must evaluate outstanding pending transforms before we proceed + data = execute_pending_transforms(data, overrides, verbose) + if isinstance(parameters, tuple) and unpack_parameters: + parameters_ = (data,) + parameters[1:] return transform(*parameters) - return transform(parameters) + return transform(data) def apply_transform( @@ -73,6 +97,9 @@ def apply_transform( map_items: bool = True, unpack_items: bool = False, log_stats: bool = False, + lazy_evaluation: bool = False, + overrides: dict = {}, + verbose: bool = False, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -99,8 +126,9 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items) for item in data] - return _apply_transform(transform, data, unpack_items) + return [_apply_transform(transform, item, unpack_items, lazy_evaluation, overrides) + for item in data] + return _apply_transform(transform, data, unpack_items, lazy_evaluation, overrides) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint # appears where the exception was raised. diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index be1f81500a..37f0dae1e9 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -37,9 +37,13 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, num_workers = 0 if torch.cuda.is_available() else num_workers # define transforms for image and segmentation - lazy_kwargs = dict( - mode=("bilinear", 0), device=device, padding_mode=("border", "nearest"), dtype=(torch.float32, torch.uint8) - ) + # lazy_kwargs = dict( + # mode=("bilinear", 0), device=device, padding_mode=("border", "nearest"), dtype=(torch.float32, torch.uint8) + # ) + lazy_kwargs = { + "img": {"mode": "bilinear", "device": device, "padding_mode": "border", "dtype": torch.float32}, + "seg": {"mode": 0, "device": device, "padding_mode": "nearest", "dtype": torch.uint8} + } train_transforms = mt.Compose( [ mt.LoadImaged(keys=["img", "seg"], reader=readers[0], image_only=True), @@ -67,7 +71,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, ], lazy_evaluation=lazy, overrides=lazy_kwargs, - override_keys=("img", "seg"), + # override_keys=("img", "seg"), verbose=num_workers > 0, # testing both flags ) From 2507a0b32b762f321aea5741dea986cc7a4ceee8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 28 Mar 2023 19:20:58 +0100 Subject: [PATCH 002/175] Refactors and simplification of api / calls Signed-off-by: Ben Murray --- issue.txt | 65 +++++++++++++++++++++++++++++ monai/data/dataset.py | 4 +- monai/transforms/compose.py | 25 ++++------- monai/transforms/lazy/functional.py | 14 +++---- monai/transforms/transform.py | 23 +++++----- monai/utils/enums.py | 31 ++++++++++++++ 6 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 issue.txt diff --git a/issue.txt b/issue.txt new file mode 100644 index 0000000000..3f1debcedd --- /dev/null +++ b/issue.txt @@ -0,0 +1,65 @@ +# Lazy Evaluation + +The lazy_evaluation flag on Compose has three potential states, but only two potential code-paths: + . True -> True + . False -> False + . None -> False + +In terms of the code paths: + . False disables all lazy resampling + . True enables all lazy resampling (which involves overriding what the user has set + +Design-wise, it has always made sense to me that the user should be able to set flags on transforms. +Doing so allows a given transform to run in a non-lazy fashion of the user desires without having to +use some other mechanism to force non-lazy reevaluation. + +As such, the intention with the design has always been that, everything else being equal, lazy +resampling will honor what is set on the transforms, rather than automatically overriding them. + +There are two ways I see this working: + . Compose has a lazy_evaluation mode that tells it to do what the transforms want to do + . Transforms have a lazy_evaluation mode that means I'll do whatever compose says + + +## The compose way + +Compose has three lazy_evaluation states: 'OFF', 'ENABLED', 'ON' + +OFF means "no lazy_evalution is allowed, even if a transform has lazy_evaluation set to True" +ENABLED means "if a lazy transform has lazy_evaluation set to True, evaluate it lazily +ON means "override any lazy transforms so that lazy evaluation happens + +Implication: compose can always overrule a transform setting and evaluate it lazily + + +## The transform way + +Transforms that can evalate lazily have three states: False, None, True + +False means "I must not be lazily evaluated" +None means "I'm happy to be evaluated lazily or not depending on the compose" +True means "I must be lazily evaluated" + +Implication: transforms can always overrule compose + + +## Hybrid + +Both systems exist, with compose getting the final say if 'ON' set. + + +## Selecting the best design + +I think the compose way is the best way to implement this. It makes sense that the user can set the policy +primarily through compose. The user doesn't want to touch the setting on the individual transforms, they +are free not to, and 'ENABLE' & 'ON' will do the same thing. If the user sets flags on the transforms, +'ENABLE' allows for fine-grained settings, and 'ON' will override the transform. + +I don't think it is necessarily good to allow transforms to overrule compose. It should be easy for the +user to completely switch lazy resampling on or off, they can do so in a single place in the code if compose +can overrule transforms. + +The hybrid system may give more fine grained semantics, i.e. "transform trumps compose trumps transform" but +this is quite a cognitive load for the user, too much IMO. + + diff --git a/monai/data/dataset.py b/monai/data/dataset.py index fe6700ebbd..e4b5e4a95e 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -913,8 +913,8 @@ def _load_cache_item(self, idx: int): # item = apply_transform(_xform, item) # item = self.transform.evaluate_with_overrides(item, None) item = apply_transform(_transform, item, - lazy_evaluation=self.transform.lazy_evaluation, - overrides=self.transform.overrides) + lazy_evaluation=self.transform.lazy_evaluation, + overrides=self.transform.overrides) item = execute_pending_transforms(item, self.transform.overrides) if self.as_contiguous: item = convert_to_contiguous(item, memory_format=torch.contiguous_format) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 244f8b5e34..cd2751c143 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -36,6 +36,7 @@ apply_transform, ) from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed, to_tuple_of_dictionaries +from monai.utils.enums import LazyMode logger = get_logger(__name__) @@ -45,7 +46,7 @@ def evaluate_with_overrides( data, upcoming, - lazy_evaluation: bool | None = False, + lazy_evaluation: LazyMode = LazyMode.OFF, overrides: dict | None = None, override_keys: Sequence[str] | None = None, verbose: bool = False, @@ -186,9 +187,6 @@ class Compose(Randomizable, InvertibleTransform): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - log_stats: whether to log the detailed information of data and applied transform when error happened, - for NumPy array and PyTorch Tensor, log the data shape and value range, - for other metadata, log the values directly. default to `False`. lazy_evaluation: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. @@ -201,7 +199,6 @@ class Compose(Randomizable, InvertibleTransform): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - verbose: whether to print debugging info when lazy_evaluation=True. """ def __init__( @@ -209,29 +206,23 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool = False, lazy_evaluation: bool | None = None, overrides: dict | None = None, - override_keys: Sequence[str] | None = None, - verbose: bool = False, ) -> None: if transforms is None: transforms = [] self.transforms = ensure_tuple(transforms) self.map_items = map_items self.unpack_items = unpack_items - self.log_stats = log_stats self.set_random_state(seed=get_seed()) self.lazy_evaluation = lazy_evaluation self.overrides = overrides - self.override_keys = override_keys - self.verbose = verbose - if self.lazy_evaluation is not None: - for t in self.flatten().transforms: # TODO: test Compose of Compose/OneOf - if isinstance(t, LazyTransform): - t.lazy_evaluation = self.lazy_evaluation + # if self.lazy_evaluation is not None: + # for t in self.flatten().transforms: # TODO: test Compose of Compose/OneOf + # if isinstance(t, LazyTransform): + # t.lazy_evaluation = self.lazy_evaluation def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) @@ -275,7 +266,7 @@ def __len__(self): def __call__(self, input_): for _transform in self.transforms: - input_ = apply_transform(_transform, input_, self.map_items, self.unpack_items, self.log_stats, + input_ = apply_transform(_transform, input_, self.map_items, self.unpack_items, lazy_evaluation=self.lazy_evaluation, overrides=self.overrides) input_ = execute_pending_transforms(input_, self.overrides) return input_ @@ -287,7 +278,7 @@ def inverse(self, data): # loop backwards over transforms for t in reversed(invertible_transforms): - data = apply_transform(t.inverse, data, self.map_items, self.unpack_items, self.log_stats) + data = apply_transform(t.inverse, data, self.map_items, self.unpack_items) return data diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 23653dbdde..a11e1f9dbc 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -30,20 +30,23 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} -def execute_pending_transforms(data, overrides: dict = None, verbose: bool = False): - if isinstance(data, (tuple, list)): +def execute_pending_transforms(data, overrides: dict = None): + if isinstance(data, list): return [execute_pending_transforms(d) for d in data] + if isinstance(data, tuple): + return tuple(execute_pending_transforms(d) for d in data) + if isinstance(data, dict): d = dict(data) for k, v in d.items(): if isinstance(v, MetaTensor) and v.has_pending_operations: overrides_ = None if overrides is None else overrides[k] - d[k], _ = apply_transforms(d[k], overrides=overrides_, verbose=verbose) + d[k], _ = apply_transforms(d[k], overrides=overrides_) return d if isinstance(data, MetaTensor) and data.has_pending_operations: - data, _ = apply_transforms(data, overrides=overrides, verbose=verbose) + data, _ = apply_transforms(data, overrides=overrides) return data @@ -51,8 +54,6 @@ def apply_transforms( data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None, - verbose: bool | None = None, - # **kwargs: Any ): """ This method applies pending transforms to `data` tensors. @@ -86,7 +87,6 @@ def apply_transforms( """ overrides = (overrides or {}).copy() - # overrides.update((kwargs or {}).copy()) for k in overrides: look_up_option(k, __override_keywords) # check existence of the key diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index fdc8e5c51e..aab0d2aeb9 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -28,7 +28,7 @@ from monai.transforms.lazy.functional import execute_pending_transforms from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first -from monai.utils.enums import TransformBackends +from monai.utils.enums import TransformBackends, LazyMode from monai.utils.misc import MONAIEnvVars __all__ = [ @@ -48,9 +48,8 @@ def _apply_transform( transform: Callable[..., ReturnType], parameters: Any, unpack_parameters: bool = False, - lazy_evaluation: bool = False, + lazy_evaluation: bool = LazyMode.OFF, overrides: dict = None, - verbose: bool = False, ) -> ReturnType: """ Perform transformation `transform` with the provided parameters `parameters`. @@ -80,15 +79,17 @@ def _apply_transform( # operations and have the transform handle them correctly. # In order to have this functionality for 1.2, we need to provide lazy_evaluation # overrides on __call__ methods for lazy array and dictionary transforms. - if not isinstance(transform, LazyTrait) or transform.lazy_evaluation is False: + + lazy_tx = isinstance(transform, LazyTrait) + + if not lazy_tx or transform.lazy_evaluation is False: # must evaluate outstanding pending transforms before we proceed - data = execute_pending_transforms(data, overrides, verbose) + data = execute_pending_transforms(data, overrides) if isinstance(parameters, tuple) and unpack_parameters: - parameters_ = (data,) + parameters[1:] - return transform(*parameters) + return transform(*parameters, lazy_evaluation=bool(lazy_evaluation)) if lazy_tx else transform(*parameters) - return transform(data) + return transform(data, lazy_evaluation=bool(lazy_evaluation)) if lazy_tx else transform(parameters) def apply_transform( @@ -96,10 +97,8 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, - log_stats: bool = False, - lazy_evaluation: bool = False, + lazy_evaluation: LazyMode = LazyMode.OFF, overrides: dict = {}, - verbose: bool = False, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -134,7 +133,7 @@ def apply_transform( # appears where the exception was raised. if MONAIEnvVars.debug(): raise - if log_stats and not isinstance(transform, transforms.compose.Compose): + if not isinstance(transform, transforms.compose.Compose): # log the input data information of exact transform in the transform chain datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False) logger = logging.getLogger(datastats._logger_name) diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 6b01e43b47..1a043033dd 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -54,6 +54,7 @@ "HoVerNetMode", "HoVerNetBranch", "LazyAttr", + "LazyMode", "BundleProperty", "BundlePropertyConfig", ] @@ -643,6 +644,36 @@ class LazyAttr(StrEnum): RESAMPLE_MODE = "lazy_resample_mode" +class LazyMode(StrEnum): + """ + Lazy evaluation modes for executing processing pipelines (ie. Compose). These modes control how transforms + that can execute lazily are executed by the pipeline: + 'OFF' indicates that the pipeline should not be executed lazily + 'ENABLED' indicates that the pipeline can be executed lazily, but this will only be done for transforms + that have ``lazy_evaluation`` set to True + 'ON' indicates that all transforms capable of being executed lazily will be executed lazily + See: :py:class: monai.transforms.compose.Compose for more details. + """ + OFF = 'off' + ENABLED = 'enabled' + ON = 'on' + + + @classmethod + def __bool__(cls, lazy_mode): + if lazy_mode == LazyMode.OFF: + return False + + if lazy_mode == LazyMode.ON: + return True + + if lazy_mode == LazyMode.ENABLED: + return None + + raise ValueError("'lazy_mode' must be one of LazyMode.OFF, LazyMode.ENABLED or LazyMode.ON, " + f"but is {lazy_mode}") + + class BundleProperty(StrEnum): """ Bundle property fields: From 56b618ed4feec1366666efd0ed680fc2fc4327fa Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 28 Mar 2023 23:47:06 +0100 Subject: [PATCH 003/175] Refactoring functional spatial transform to support lazy_evaluation parameter, refactoring spatial arrays to support lazy_evaluation call time parameter, refactoring _apply_transform to pass call time lazy_evaluation flag Signed-off-by: Ben Murray --- monai/transforms/inverse.py | 2 +- monai/transforms/lazy/functional.py | 3 +- monai/transforms/spatial/array.py | 231 ++++++++++++++++++------- monai/transforms/spatial/functional.py | 76 ++++---- monai/transforms/transform.py | 13 +- monai/utils/enums.py | 4 +- tests/test_compose.py | 2 +- tests/test_integration_lazy_samples.py | 7 +- 8 files changed, 228 insertions(+), 110 deletions(-) diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index f2a88be481..d9bfe31009 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -93,7 +93,7 @@ def get_transform_info(self) -> dict: self.__class__.__name__, id(self), self.tracing, - self.lazy_evaluation if isinstance(self, LazyTransform) else False, + # self.lazy_evaluation if isinstance(self, LazyTransform) else False, self._do_transform if hasattr(self, "_do_transform") else True, ) return dict(zip(self.transform_info_keys(), vals)) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index a11e1f9dbc..18ff6b265a 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -47,7 +47,8 @@ def execute_pending_transforms(data, overrides: dict = None): if isinstance(data, MetaTensor) and data.has_pending_operations: data, _ = apply_transforms(data, overrides=overrides) - return data + + return data def apply_transforms( diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index c8dc12193a..849ad7a9f1 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -137,6 +137,7 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike = np.float64, + lazy_evaluation: bool = False, ): """ Args: @@ -156,6 +157,7 @@ def __init__( If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. """ + self.lazy_evaluation = lazy_evaluation self.mode = mode self.padding_mode = padding_mode self.align_corners = align_corners @@ -170,6 +172,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike = None, + lazy_evaluation: bool | None = None, ) -> torch.Tensor: """ Args: @@ -213,8 +216,11 @@ def __call__( align_corners = align_corners if align_corners is not None else self.align_corners mode = mode if mode is not None else self.mode padding_mode = padding_mode if padding_mode is not None else self.padding_mode + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation return spatial_resample( - img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, self.get_transform_info() + img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info() ) def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -246,6 +252,7 @@ def __call__( # type: ignore padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike = None, + lazy_evaluation: bool = False, ) -> torch.Tensor: """ Args: @@ -277,6 +284,7 @@ def __call__( # type: ignore if img_dst is None: raise RuntimeError("`img_dst` is missing.") dst_affine = img_dst.peek_pending_affine() if isinstance(img_dst, MetaTensor) else torch.eye(4) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation img = super().__call__( img=img, dst_affine=dst_affine, @@ -285,8 +293,9 @@ def __call__( # type: ignore padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation_ ) - if not self.lazy_evaluation: + if not lazy_evaluation_: if isinstance(img, MetaTensor): img.affine = dst_affine if isinstance(img_dst, MetaTensor): @@ -323,6 +332,7 @@ def __init__( recompute_affine: bool = False, min_pixdim: Sequence[float] | float | np.ndarray | None = None, max_pixdim: Sequence[float] | float | np.ndarray | None = None, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -377,6 +387,7 @@ def __init__( value of `pixdim`. Default to `None`. """ + self.lazy_evaluation = lazy_evaluation self.pixdim = np.array(ensure_tuple(pixdim), dtype=np.float64) self.min_pixdim = np.array(ensure_tuple(min_pixdim), dtype=np.float64) self.max_pixdim = np.array(ensure_tuple(max_pixdim), dtype=np.float64) @@ -389,7 +400,8 @@ def __init__( raise ValueError(f"min_pixdim {self.min_pixdim} must be positive, smaller than max {self.max_pixdim}.") self.sp_resample = SpatialResample( - mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype + mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation ) @LazyTransform.lazy_evaluation.setter # type: ignore @@ -408,6 +420,7 @@ def __call__( dtype: DtypeLike = None, scale_extent: bool | None = None, output_spatial_shape: Sequence[int] | np.ndarray | int | None = None, + lazy_evaluation: bool | None = None ) -> torch.Tensor: """ Args: @@ -487,6 +500,7 @@ def __call__( new_affine[:sr, -1] = offset[:sr] actual_shape = list(output_shape) if output_spatial_shape is None else output_spatial_shape + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation data_array = self.sp_resample( data_array, dst_affine=torch.as_tensor(new_affine), @@ -495,9 +509,10 @@ def __call__( padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation_ ) if self.recompute_affine and isinstance(data_array, MetaTensor): - if self.lazy_evaluation: + if lazy_evaluation_: raise NotImplementedError("recompute_affine is not supported with lazy evaluation.") a = scale_affine(original_spatial_shape, actual_shape) data_array.affine = convert_to_dst_type(a, affine_)[0] # type: ignore @@ -519,6 +534,7 @@ def __init__( axcodes: str | None = None, as_closest_canonical: bool = False, labels: Sequence[tuple[str, str]] | None = (("L", "R"), ("P", "A"), ("I", "S")), + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -538,6 +554,7 @@ def __init__( See Also: `nibabel.orientations.ornt2axcodes`. """ + self.lazy_evaluation = lazy_evaluation if axcodes is None and not as_closest_canonical: raise ValueError("Incompatible values: axcodes=None and as_closest_canonical=True.") if axcodes is not None and as_closest_canonical: @@ -546,7 +563,7 @@ def __init__( self.as_closest_canonical = as_closest_canonical self.labels = labels - def __call__(self, data_array: torch.Tensor) -> torch.Tensor: + def __call__(self, data_array: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: """ If input type is `MetaTensor`, original affine is extracted with `data_array.affine`. If input type is `torch.Tensor`, original affine is assumed to be identity. @@ -597,7 +614,10 @@ def __call__(self, data_array: torch.Tensor) -> torch.Tensor: f"axcodes must match data_array spatially, got axcodes={len(self.axcodes)}D data_array={sr}D" ) spatial_ornt = nib.orientations.ornt_transform(src, dst) - return orientation(data_array, affine_np, spatial_ornt, self.get_transform_info()) # type: ignore + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return orientation(data_array, affine_np, spatial_ornt, + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: transform = self.pop_transform(data) @@ -629,16 +649,20 @@ class Flip(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH] - def __init__(self, spatial_axis: Sequence[int] | int | None = None) -> None: + def __init__(self, spatial_axis: Sequence[int] | int | None = None, lazy_evaluation: bool | None = None) -> None: + self.lazy_evaluation = lazy_evaluation self.spatial_axis = spatial_axis - def __call__(self, img: torch.Tensor) -> torch.Tensor: + def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) """ img = convert_to_tensor(img, track_meta=get_track_meta()) - return flip(img, self.spatial_axis, transform_info=self.get_transform_info()) # type: ignore + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return flip(img, self.spatial_axis, + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: self.pop_transform(data) @@ -692,7 +716,9 @@ def __init__( anti_aliasing: bool = False, anti_aliasing_sigma: Sequence[float] | float | None = None, dtype: DtypeLike | torch.dtype = torch.float32, + lazy_evaluation: bool | None = None, ) -> None: + self.lazy_evaluation = lazy_evaluation self.size_mode = look_up_option(size_mode, ["all", "longest"]) self.spatial_size = spatial_size self.mode: InterpolateMode = look_up_option(mode, InterpolateMode) @@ -709,6 +735,7 @@ def __call__( anti_aliasing: bool | None = None, anti_aliasing_sigma: Sequence[float] | float | None = None, dtype: DtypeLike | torch.dtype = None, + lazy_evaluation: bool | None = None ) -> torch.Tensor: """ Args: @@ -762,6 +789,7 @@ def __call__( _mode = look_up_option(self.mode if mode is None else mode, InterpolateMode) _align_corners = self.align_corners if align_corners is None else align_corners _dtype = get_equivalent_dtype(dtype or self.dtype or img.dtype, torch.Tensor) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation return resize( # type: ignore img, sp_size, @@ -771,6 +799,7 @@ def __call__( input_ndim, anti_aliasing, anti_aliasing_sigma, + lazy_evaluation_, self.get_transform_info(), ) @@ -828,7 +857,9 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike | torch.dtype = torch.float32, + lazy_evaluation: bool | None = None ) -> None: + self.lazy_evaluation = lazy_evaluation self.angle = angle self.keep_size = keep_size self.mode: str = look_up_option(mode, GridSampleMode) @@ -843,6 +874,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, + lazy_evaluation: bool | None = None ) -> torch.Tensor: """ Args: @@ -872,8 +904,11 @@ def __call__( _align_corners = self.align_corners if align_corners is None else align_corners im_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] output_shape = im_shape if self.keep_size else None + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation return rotate( # type: ignore - img, self.angle, output_shape, _mode, _padding_mode, _align_corners, _dtype, self.get_transform_info() + img, self.angle, output_shape, _mode, _padding_mode, _align_corners, _dtype, + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info() ) def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -950,8 +985,10 @@ def __init__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = torch.float32, keep_size: bool = True, + lazy_evaluation: bool | None = None, **kwargs, ) -> None: + self.lazy_evaluation = lazy_evaluation self.zoom = zoom self.mode: InterpolateMode = InterpolateMode(mode) self.padding_mode = padding_mode @@ -967,6 +1004,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, + lazy_evaluation: bool | None = None, ) -> torch.Tensor: """ Args: @@ -995,8 +1033,11 @@ def __call__( _padding_mode = padding_mode or self.padding_mode _align_corners = self.align_corners if align_corners is None else align_corners _dtype = get_equivalent_dtype(dtype or self.dtype or img.dtype, torch.Tensor) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation return zoom( # type: ignore - img, _zoom, self.keep_size, _mode, _padding_mode, _align_corners, _dtype, self.get_transform_info() + img, _zoom, self.keep_size, _mode, _padding_mode, _align_corners, _dtype, + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info() ) def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1035,7 +1076,12 @@ class Rotate90(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH] - def __init__(self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1)) -> None: + def __init__( + self, + k: int = 1, + spatial_axes: tuple[int, int] = (0, 1), + lazy_evaluation: bool | None = None + ) -> None: """ Args: k: number of times to rotate by 90 degrees. @@ -1043,20 +1089,24 @@ def __init__(self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1)) -> None: Default: (0, 1), this is the first two axis in spatial dimensions. If axis is negative it counts from the last to the first axis. """ + self.lazy_evaluation = lazy_evaluation self.k = (4 + (k % 4)) % 4 # 0, 1, 2, 3 spatial_axes_: tuple[int, int] = ensure_tuple(spatial_axes) # type: ignore if len(spatial_axes_) != 2: raise ValueError(f"spatial_axes must be 2 numbers to define the plane to rotate, got {spatial_axes_}.") self.spatial_axes = spatial_axes_ - def __call__(self, img: torch.Tensor) -> torch.Tensor: + def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), """ img = convert_to_tensor(img, track_meta=get_track_meta()) axes = map_spatial_axes(img.ndim, self.spatial_axes) - return rotate90(img, axes, self.k, self.get_transform_info()) # type: ignore + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return rotate90(img, axes, self.k, + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: transform = self.pop_transform(data) @@ -1079,7 +1129,13 @@ class RandRotate90(RandomizableTransform, InvertibleTransform, LazyTransform): backend = Rotate90.backend - def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1)) -> None: + def __init__( + self, + prob: float = 0.1, + max_k: int = 3, + spatial_axes: tuple[int, int] = (0, 1), + lazy_evaluation: bool | None = None + ) -> None: """ Args: prob: probability of rotating. @@ -1089,6 +1145,7 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: tuple[int, i Default: (0, 1), this is the first two axis in spatial dimensions. """ RandomizableTransform.__init__(self, prob) + self.lazy_evaluation = lazy_evaluation self.max_k = max_k self.spatial_axes = spatial_axes @@ -1100,18 +1157,19 @@ def randomize(self, data: Any | None = None) -> None: return None self._rand_k = self.R.randint(self.max_k) + 1 - def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), randomize: whether to execute `randomize()` function first, default to True. """ + if randomize: self.randomize() if self._do_transform: - xform = Rotate90(self._rand_k, self.spatial_axes) - xform.lazy_evaluation = self.lazy_evaluation + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + xform = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) out = xform(img) else: out = convert_to_tensor(img, track_meta=get_track_meta()) @@ -1168,8 +1226,10 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike | torch.dtype = np.float32, + lazy_evaluation: bool | None = None, ) -> None: RandomizableTransform.__init__(self, prob) + self.lazy_evaluation = lazy_evaluation self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -1206,6 +1266,7 @@ def __call__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, randomize: bool = True, + lazy_evaluation: bool | None = None, ): """ Args: @@ -1228,6 +1289,7 @@ def __call__( if self._do_transform: ndim = len(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation rotator = Rotate( angle=self.x if ndim == 2 else (self.x, self.y, self.z), keep_size=self.keep_size, @@ -1235,8 +1297,8 @@ def __call__( padding_mode=look_up_option(padding_mode or self.padding_mode, GridSamplePadMode), align_corners=self.align_corners if align_corners is None else align_corners, dtype=dtype or self.dtype or img.dtype, + lazy_evaluation=lazy_evaluation_ ) - rotator.lazy_evaluation = self.lazy_evaluation out = rotator(img) else: out = convert_to_tensor(img, track_meta=get_track_meta(), dtype=torch.float32) @@ -1263,16 +1325,27 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): backend = Flip.backend - def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None) -> None: + def __init__( + self, + prob: float = 0.1, + spatial_axis: Sequence[int] | int | None = None, + lazy_evaluation: bool = False, + ) -> None: RandomizableTransform.__init__(self, prob) - self.flipper = Flip(spatial_axis=spatial_axis) + self.lazy_evaluation = lazy_evaluation + self.flipper = Flip(spatial_axis=spatial_axis, lazy_evaluation=lazy_evaluation) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.flipper.lazy_evaluation = val - self._lazy_evaluation = val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, val: bool): + # self.flipper.lazy_evaluation = val + # self._lazy_evaluation = val - def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: + def __call__( + self, + img: torch.Tensor, + randomize: bool = True, + lazy_evaluation: bool | None = None, + ) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), @@ -1280,7 +1353,8 @@ def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: """ if randomize: self.randomize(None) - out = self.flipper(img) if self._do_transform else img + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + out = self.flipper(img, lazy_evaluation=lazy_evaluation_) if self._do_transform else img out = convert_to_tensor(out, track_meta=get_track_meta()) self.push_transform(out, replace=True) return out @@ -1306,15 +1380,16 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform, LazyTransform): backend = Flip.backend - def __init__(self, prob: float = 0.1) -> None: + def __init__(self, prob: float = 0.1, lazy_evaluation: bool = False) -> None: RandomizableTransform.__init__(self, prob) + self.lazy_evaluation = lazy_evaluation self._axis: int | None = None self.flipper = Flip(spatial_axis=self._axis) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.flipper.lazy_evaluation = val - self._lazy_evaluation = val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, val: bool): + # self.flipper.lazy_evaluation = val + # self._lazy_evaluation = val def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) @@ -1322,7 +1397,12 @@ def randomize(self, data: NdarrayOrTensor) -> None: return None self._axis = self.R.randint(data.ndim - 1) - def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: + def __call__( + self, + img: torch.Tensor, + randomize: bool = True, + lazy_evaluation: bool | None = None, + ) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) @@ -1332,8 +1412,9 @@ def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: self.randomize(data=img) if self._do_transform: + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation self.flipper.spatial_axis = self._axis - out = self.flipper(img) + out = self.flipper(img, lazy_evaluation=lazy_evaluation_) else: out = convert_to_tensor(img, track_meta=get_track_meta()) self.push_transform(out, replace=True) @@ -1397,9 +1478,11 @@ def __init__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = torch.float32, keep_size: bool = True, + lazy_evaluation: bool = False, **kwargs, ) -> None: RandomizableTransform.__init__(self, prob) + self.lazy_evaluation = lazy_evaluation self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): @@ -1435,6 +1518,7 @@ def __call__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, randomize: bool = True, + lazy_evaluation: bool | None = None, ) -> torch.Tensor: """ Args: @@ -1461,6 +1545,7 @@ def __call__( if randomize: self.randomize(img=img) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation if not self._do_transform: out = convert_to_tensor(img, track_meta=get_track_meta(), dtype=torch.float32) else: @@ -1471,9 +1556,9 @@ def __call__( padding_mode=padding_mode or self.padding_mode, align_corners=self.align_corners if align_corners is None else align_corners, dtype=dtype or self.dtype, + lazy_evaluation=lazy_evaluation_ **self.kwargs, ) - xform.lazy_evaluation = self.lazy_evaluation out = xform(img) self.push_transform(out, replace=True) return out # type: ignore @@ -1529,7 +1614,9 @@ def __init__( dtype: DtypeLike = np.float32, align_corners: bool = False, affine: NdarrayOrTensor | None = None, + lazy_evaluation: bool = False, ) -> None: + self.lazy_evaluation = lazy_evaluation self.rotate_params = rotate_params self.shear_params = shear_params self.translate_params = translate_params @@ -1541,7 +1628,10 @@ def __init__( self.affine = affine def __call__( - self, spatial_size: Sequence[int] | None = None, grid: torch.Tensor | None = None + self, + spatial_size: Sequence[int] | None = None, + grid: torch.Tensor | None = None, + lazy_evaluation: bool | None = None, ) -> tuple[torch.Tensor | None, torch.Tensor]: """ The grid can be initialized with a `spatial_size` parameter, or provided directly as `grid`. @@ -1556,7 +1646,8 @@ def __call__( ValueError: When ``grid=None`` and ``spatial_size=None``. Incompatible values. """ - if not self.lazy_evaluation: + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + if not lazy_evaluation_: if grid is None: # create grid from spatial_size if spatial_size is None: raise ValueError("Incompatible values: grid=None and spatial_size=None.") @@ -1585,7 +1676,7 @@ def __call__( else: affine = self.affine # type: ignore affine = to_affine_nd(spatial_dims, affine) - if self.lazy_evaluation: + if lazy_evaluation_: return None, affine affine = convert_to_tensor(affine, device=grid_.device, dtype=grid_.dtype, track_meta=False) # type: ignore @@ -1616,6 +1707,7 @@ def __init__( scale_range: RandRange = None, device: torch.device | None = None, dtype: DtypeLike = np.float32, + lazy_evaluation: bool = None, ) -> None: """ Args: @@ -1652,6 +1744,7 @@ def __init__( - :py:meth:`monai.transforms.utils.create_scale` """ + self.lazy_evaluation = lazy_evaluation self.rotate_range = ensure_tuple(rotate_range) self.shear_range = ensure_tuple(shear_range) self.translate_range = ensure_tuple(translate_range) @@ -1684,7 +1777,11 @@ def randomize(self, data: Any | None = None) -> None: self.scale_params = self._get_rand_param(self.scale_range, 1.0) def __call__( - self, spatial_size: Sequence[int] | None = None, grid: NdarrayOrTensor | None = None, randomize: bool = True + self, + spatial_size: Sequence[int] | None = None, + grid: NdarrayOrTensor | None = None, + randomize: bool = True, + lazy_evaluation: bool | None = None, ) -> torch.Tensor: """ Args: @@ -1697,6 +1794,7 @@ def __call__( """ if randomize: self.randomize() + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation affine_grid = AffineGrid( rotate_params=self.rotate_params, shear_params=self.shear_params, @@ -1704,9 +1802,9 @@ def __call__( scale_params=self.scale_params, device=self.device, dtype=self.dtype, + lazy_evaluation=lazy_evaluation_ ) - affine_grid.lazy_evaluation = self.lazy_evaluation - if self.lazy_evaluation: # return the affine only, don't construct the grid + if lazy_evaluation_: # return the affine only, don't construct the grid self.affine = affine_grid(spatial_size, grid)[1] # type: ignore return None # type: ignore _grid: torch.Tensor @@ -1970,6 +2068,7 @@ def __init__( dtype: DtypeLike = np.float32, align_corners: bool = False, image_only: bool = False, + lazy_evaluation: bool = False, ) -> None: """ The affine transformations are applied in rotate, shear, translate, scale order. @@ -2035,7 +2134,9 @@ def __init__( dtype=dtype, align_corners=align_corners, device=device, + lazy_evaluation=lazy_evaluation, ) + self.lazy_evaluation = lazy_evaluation self.image_only = image_only self.norm_coord = not normalized self.resampler = Resample(norm_coords=self.norm_coord, device=device, dtype=dtype, align_corners=align_corners) @@ -2043,10 +2144,10 @@ def __init__( self.mode = mode self.padding_mode: str = padding_mode - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self.affine_grid.lazy_evaluation = val - self._lazy_evaluation = val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, val: bool) -> None: + # self.affine_grid.lazy_evaluation = val + # self._lazy_evaluation = val def __call__( self, @@ -2054,6 +2155,7 @@ def __call__( spatial_size: Sequence[int] | int | None = None, mode: str | int | None = None, padding_mode: str | None = None, + lazy_evaluation: bool | None = None, ) -> torch.Tensor | tuple[torch.Tensor, NdarrayOrTensor]: """ Args: @@ -2079,9 +2181,10 @@ def __call__( img = convert_to_tensor(img, track_meta=get_track_meta()) img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] sp_size = fall_back_tuple(self.spatial_size if spatial_size is None else spatial_size, img_size) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation _mode = mode if mode is not None else self.mode _padding_mode = padding_mode if padding_mode is not None else self.padding_mode - grid, affine = self.affine_grid(spatial_size=sp_size) + grid, affine = self.affine_grid(spatial_size=sp_size, lazy_evaluation=lazy_evaluation_) return affine_func( # type: ignore img, @@ -2093,7 +2196,8 @@ def __call__( _padding_mode, True, self.image_only, - self.get_transform_info(), + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info(), ) @classmethod @@ -2152,6 +2256,7 @@ def __init__( padding_mode: str = GridSamplePadMode.REFLECTION, cache_grid: bool = False, device: torch.device | None = None, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -2208,6 +2313,7 @@ def __init__( """ RandomizableTransform.__init__(self, prob) + self.lazy_evaluation = lazy_evaluation self.rand_affine_grid = RandAffineGrid( rotate_range=rotate_range, @@ -2215,25 +2321,26 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, + lazy_evaluation=lazy_evaluation, ) self.resampler = Resample(device=device) self.spatial_size = spatial_size self.cache_grid = cache_grid - self._cached_grid = self._init_identity_cache() + self._cached_grid = self._init_identity_cache(lazy_evaluation) self.mode = mode self.padding_mode: str = padding_mode - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.rand_affine_grid.lazy_evaluation = val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, val: bool) -> None: + # self._lazy_evaluation = val + # self.rand_affine_grid.lazy_evaluation = val - def _init_identity_cache(self): + def _init_identity_cache(self, lazy_evaluation: bool): """ Create cache of the identity grid if cache_grid=True and spatial_size is known. """ - if self.lazy_evaluation: + if lazy_evaluation: return None if self.spatial_size is None: if self.cache_grid: @@ -2253,14 +2360,14 @@ def _init_identity_cache(self): return None return create_grid(spatial_size=_sp_size, device=self.rand_affine_grid.device, backend="torch") - def get_identity_grid(self, spatial_size: Sequence[int]): + def get_identity_grid(self, spatial_size: Sequence[int], lazy_evaluation: bool): """ Return a cached or new identity grid depends on the availability. Args: spatial_size: non-dynamic spatial size """ - if self.lazy_evaluation: + if lazy_evaluation: return None ndim = len(spatial_size) if spatial_size != fall_back_tuple(spatial_size, [1] * ndim) or spatial_size != fall_back_tuple( @@ -2292,6 +2399,7 @@ def __call__( padding_mode: str | None = None, randomize: bool = True, grid=None, + lazy_evaluation: bool | None = None, ) -> torch.Tensor: """ Args: @@ -2326,17 +2434,19 @@ def __call__( do_resampling = self._do_transform or (sp_size != ensure_tuple(ori_size)) _mode = mode if mode is not None else self.mode _padding_mode = padding_mode if padding_mode is not None else self.padding_mode + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation img = convert_to_tensor(img, track_meta=get_track_meta()) - if self.lazy_evaluation: + if lazy_evaluation_: if self._do_transform: affine = self.rand_affine_grid.get_transformation_matrix() else: affine = convert_to_dst_type(torch.eye(len(sp_size) + 1), img, dtype=self.rand_affine_grid.dtype)[0] else: if grid is None: - grid = self.get_identity_grid(sp_size) + grid = self.get_identity_grid(sp_size, lazy_evaluation_) if self._do_transform: - grid = self.rand_affine_grid(grid=grid, randomize=randomize) + grid = self.rand_affine_grid(grid=grid, randomize=randomize, + lazy_evaluation=lazy_evaluation_) affine = self.rand_affine_grid.get_transformation_matrix() return affine_func( # type: ignore img, @@ -2348,7 +2458,8 @@ def __call__( _padding_mode, do_resampling, True, - self.get_transform_info(), + lazy_evaluation=lazy_evaluation_, + transform_info=self.get_transform_info(), ) def inverse(self, data: torch.Tensor) -> torch.Tensor: diff --git a/monai/transforms/spatial/functional.py b/monai/transforms/spatial/functional.py index e78ee75cb7..7177b9f308 100644 --- a/monai/transforms/spatial/functional.py +++ b/monai/transforms/spatial/functional.py @@ -66,12 +66,13 @@ def _maybe_new_metatensor(img, dtype=None, device=None): def spatial_resample( - img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, transform_info + img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, + lazy_evaluation, transform_info ) -> torch.Tensor: """ Functional implementation of resampling the input image to the specified ``dst_affine`` matrix and ``spatial_size``. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be resampled, assuming `img` is channel-first. @@ -92,6 +93,7 @@ def spatial_resample( align_corners: Geometrically, we consider the pixels of the input as squares rather than points. See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html dtype_pt: data `dtype` for resampling computation. + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ original_spatial_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -132,7 +134,6 @@ def spatial_resample( affine_unchanged = ( allclose(src_affine, dst_affine, atol=AFFINE_TOL) and allclose(spatial_size, in_spatial_size) ) or (allclose(xform, np.eye(len(xform)), atol=AFFINE_TOL) and allclose(spatial_size, in_spatial_size)) - lazy_evaluation = transform_info.get(TraceKeys.LAZY_EVALUATION, False) meta_info = TraceableTransform.track_transform_meta( img, sp_size=spatial_size, @@ -183,17 +184,18 @@ def spatial_resample( return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore -def orientation(img, original_affine, spatial_ornt, transform_info): +def orientation(img, original_affine, spatial_ornt, lazy_evaluation, transform_info): """ Functional implementation of changing the input image's orientation into the specified based on `spatial_ornt`. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. original_affine: original affine of the input image. spatial_ornt: orientations of the spatial axes, see also https://nipy.org/nibabel/reference/nibabel.orientations.html + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ spatial_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -216,10 +218,10 @@ def orientation(img, original_affine, spatial_ornt, transform_info): extra_info=extra_info, orig_size=spatial_shape, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = _maybe_new_metatensor(img) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info if axes: out = torch.flip(out, dims=axes) @@ -228,11 +230,11 @@ def orientation(img, original_affine, spatial_ornt, transform_info): return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def flip(img, sp_axes, transform_info): +def flip(img, sp_axes, lazy_evaluation, transform_info): """ Functional implementation of flip. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -241,6 +243,7 @@ def flip(img, sp_axes, transform_info): If axis is negative it counts from the last to the first axis. If axis is a tuple of ints, flipping is performed on all of the axes specified in the tuple. + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ sp_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -259,20 +262,21 @@ def flip(img, sp_axes, transform_info): affine=xform, extra_info=extra_info, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = _maybe_new_metatensor(img) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info out = torch.flip(out, axes) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, transform_info): +def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, + lazy_evaluation, transform_info): """ Functional implementation of resize. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -290,6 +294,7 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, the image to avoid aliasing artifacts. See also ``skimage.transform.resize`` anti_aliasing_sigma: {float, tuple of floats}, optional Standard deviation for Gaussian filtering used when anti-aliasing. + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ img = convert_to_tensor(img, track_meta=get_track_meta()) @@ -307,10 +312,10 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, extra_info=extra_info, orig_size=orig_size, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): - if anti_aliasing and transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: + if anti_aliasing and lazy_evaluation: warnings.warn("anti-aliasing is not compatible with lazy evaluation.") out = _maybe_new_metatensor(img) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info @@ -338,11 +343,12 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, transform_info): +def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, + lazy_evaluation, transform_info): """ Functional implementation of rotate. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -358,6 +364,7 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, t dtype: data type for resampling computation. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ @@ -391,10 +398,10 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, t extra_info=extra_info, orig_size=im_shape, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = _maybe_new_metatensor(img) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info xform = AffineTransform( normalized=False, mode=mode, padding_mode=padding_mode, align_corners=align_corners, reverse_indexing=True @@ -407,11 +414,12 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, t return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, transform_info): +def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, + lazy_evaluation, transform_info): """ Functional implementation of zoom. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -429,6 +437,7 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, dtype: data type for resampling computation. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ @@ -444,7 +453,7 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, } if keep_size: do_pad_crop = not np.allclose(output_size, im_shape) - if do_pad_crop and transform_info.get(TraceKeys.LAZY_EVALUATION, False): # update for lazy evaluation + if do_pad_crop and lazy_evaluation: # update for lazy evaluation _pad_crop = ResizeWithPadOrCrop(spatial_size=im_shape, mode=padding_mode) _pad_crop.lazy_evaluation = True _tmp_img = MetaTensor([], affine=torch.eye(len(output_size) + 1)) @@ -462,7 +471,7 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, extra_info=extra_info, orig_size=im_shape, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = _maybe_new_metatensor(img) if transform_info.get(TraceKeys.LAZY_EVALUATION, False): @@ -489,17 +498,18 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, return out -def rotate90(img, axes, k, transform_info): +def rotate90(img, axes, k, lazy_evaluation, transform_info): """ Functional implementation of rotate90. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. If axis is negative it counts from the last to the first axis. k: number of times to rotate by 90 degrees. + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ extra_info = {"axes": [d - 1 for d in axes], "k": k} @@ -529,20 +539,21 @@ def rotate90(img, axes, k, transform_info): extra_info=extra_info, orig_size=ori_shape, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = _maybe_new_metatensor(img) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info out = torch.rot90(out, k, axes) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, transform_info): +def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, + lazy_evaluation, transform_info): """ Functional implementation of affine. This function operates eagerly or lazily according to - ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + ``lazy_evaluation`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -566,6 +577,7 @@ def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_re do_resampling: whether to do the resampling, this is a flag for the use case of updating metadata but skipping the actual (potentially heavy) resampling operation. image_only: if True return only the image volume, otherwise return (image, affine). + lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ @@ -588,9 +600,9 @@ def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_re extra_info=extra_info, orig_size=img_size, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: out = _maybe_new_metatensor(img) out = out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info return out if image_only else (out, affine) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index aab0d2aeb9..ab1389fbbd 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -46,7 +46,7 @@ def _apply_transform( transform: Callable[..., ReturnType], - parameters: Any, + data: Any, unpack_parameters: bool = False, lazy_evaluation: bool = LazyMode.OFF, overrides: dict = None, @@ -67,11 +67,6 @@ def _apply_transform( ReturnType: The return type of `transform`. """ - if isinstance(parameters, tuple) and unpack_parameters: - data = parameters[0] - else: - data = parameters - # For the 1.2 release, we are limited here to having executing transforms that # are lazy but set to not be lazy _after_ we have applied the pending list. This # is because the transform implementations for 1.2 don't have unified code paths for @@ -86,10 +81,10 @@ def _apply_transform( # must evaluate outstanding pending transforms before we proceed data = execute_pending_transforms(data, overrides) - if isinstance(parameters, tuple) and unpack_parameters: - return transform(*parameters, lazy_evaluation=bool(lazy_evaluation)) if lazy_tx else transform(*parameters) + if isinstance(data, tuple) and unpack_parameters: + return transform(*data, lazy_evaluation=LazyMode.as_bool(lazy_evaluation)) if lazy_tx else transform(*data) - return transform(data, lazy_evaluation=bool(lazy_evaluation)) if lazy_tx else transform(parameters) + return transform(data, lazy_evaluation=LazyMode.as_bool(lazy_evaluation)) if lazy_tx else transform(data) def apply_transform( diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 1a043033dd..cf455e9254 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -659,8 +659,8 @@ class LazyMode(StrEnum): ON = 'on' - @classmethod - def __bool__(cls, lazy_mode): + @staticmethod + def as_bool(lazy_mode): if lazy_mode == LazyMode.OFF: return False diff --git a/tests/test_compose.py b/tests/test_compose.py index ddb7ce25d8..b9e2346920 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -156,7 +156,7 @@ def __call__(self, data): c.randomize() def test_err_msg(self): - transforms = Compose([abs, AddChannel(), round], log_stats=False) + transforms = Compose([abs, AddChannel(), round]) with self.assertRaisesRegex(Exception, "AddChannel"): transforms(42.1) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 37f0dae1e9..15bfcd2a58 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -25,6 +25,7 @@ import monai.transforms as mt from monai.data import create_test_image_3d from monai.utils import set_determinism +from monai.utils.enums import LazyMode from tests.utils import HAS_CUPY, DistTestCase, SkipIfBeforePyTorchVersion, skip_if_quick @@ -71,8 +72,6 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, ], lazy_evaluation=lazy, overrides=lazy_kwargs, - # override_keys=("img", "seg"), - verbose=num_workers > 0, # testing both flags ) # create a training data loader @@ -182,10 +181,10 @@ def train_and_infer(self, idx=0): _readers = ("itkreader", "nibabelreader") _w = 0 results = run_training_test( - self.data_dir, device=self.device, cachedataset=idx, readers=_readers, num_workers=_w, lazy=True + self.data_dir, device=self.device, cachedataset=idx, readers=_readers, num_workers=_w, lazy=LazyMode.ON ) results_expected = run_training_test( - self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=False + self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=LazyMode.OFF ) self.assertFalse(np.allclose(results, [0])) self.assertFalse(np.allclose(results_expected, [0])) From 315e5b766510b91187c2fb1bf3bf23b87a6444b6 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 29 Mar 2023 00:01:03 +0100 Subject: [PATCH 004/175] Making lazy_evaluation parameter consistent across spatial array lazy transforms Signed-off-by: Ben Murray --- monai/transforms/spatial/array.py | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 849ad7a9f1..958f916dee 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -157,11 +157,11 @@ def __init__( If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. """ - self.lazy_evaluation = lazy_evaluation self.mode = mode self.padding_mode = padding_mode self.align_corners = align_corners self.dtype = dtype + self.lazy_evaluation = lazy_evaluation def __call__( self, @@ -387,13 +387,13 @@ def __init__( value of `pixdim`. Default to `None`. """ - self.lazy_evaluation = lazy_evaluation self.pixdim = np.array(ensure_tuple(pixdim), dtype=np.float64) self.min_pixdim = np.array(ensure_tuple(min_pixdim), dtype=np.float64) self.max_pixdim = np.array(ensure_tuple(max_pixdim), dtype=np.float64) self.diagonal = diagonal self.scale_extent = scale_extent self.recompute_affine = recompute_affine + self.lazy_evaluation = lazy_evaluation for mn, mx in zip(self.min_pixdim, self.max_pixdim): if (not np.isnan(mn)) and (not np.isnan(mx)) and ((mx < mn) or (mn < 0)): @@ -404,10 +404,10 @@ def __init__( lazy_evaluation=lazy_evaluation ) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.sp_resample.lazy_evaluation = val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, val: bool) -> None: + # self._lazy_evaluation = val + # self.sp_resample.lazy_evaluation = val @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") def __call__( @@ -554,7 +554,6 @@ def __init__( See Also: `nibabel.orientations.ornt2axcodes`. """ - self.lazy_evaluation = lazy_evaluation if axcodes is None and not as_closest_canonical: raise ValueError("Incompatible values: axcodes=None and as_closest_canonical=True.") if axcodes is not None and as_closest_canonical: @@ -562,6 +561,7 @@ def __init__( self.axcodes = axcodes self.as_closest_canonical = as_closest_canonical self.labels = labels + self.lazy_evaluation = lazy_evaluation def __call__(self, data_array: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: """ @@ -649,9 +649,9 @@ class Flip(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH] - def __init__(self, spatial_axis: Sequence[int] | int | None = None, lazy_evaluation: bool | None = None) -> None: - self.lazy_evaluation = lazy_evaluation + def __init__(self, spatial_axis: Sequence[int] | int | None = None, lazy_evaluation: bool = False) -> None: self.spatial_axis = spatial_axis + self.lazy_evaluation = lazy_evaluation def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: """ @@ -716,9 +716,8 @@ def __init__( anti_aliasing: bool = False, anti_aliasing_sigma: Sequence[float] | float | None = None, dtype: DtypeLike | torch.dtype = torch.float32, - lazy_evaluation: bool | None = None, + lazy_evaluation: bool = False, ) -> None: - self.lazy_evaluation = lazy_evaluation self.size_mode = look_up_option(size_mode, ["all", "longest"]) self.spatial_size = spatial_size self.mode: InterpolateMode = look_up_option(mode, InterpolateMode) @@ -726,6 +725,7 @@ def __init__( self.anti_aliasing = anti_aliasing self.anti_aliasing_sigma = anti_aliasing_sigma self.dtype = dtype + self.lazy_evaluation = lazy_evaluation def __call__( self, @@ -857,15 +857,15 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike | torch.dtype = torch.float32, - lazy_evaluation: bool | None = None + lazy_evaluation: bool = False, ) -> None: - self.lazy_evaluation = lazy_evaluation self.angle = angle self.keep_size = keep_size self.mode: str = look_up_option(mode, GridSampleMode) self.padding_mode: str = look_up_option(padding_mode, GridSamplePadMode) self.align_corners = align_corners self.dtype = dtype + self.lazy_evaluation = lazy_evaluation def __call__( self, @@ -985,10 +985,9 @@ def __init__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = torch.float32, keep_size: bool = True, - lazy_evaluation: bool | None = None, + lazy_evaluation: bool = False, **kwargs, ) -> None: - self.lazy_evaluation = lazy_evaluation self.zoom = zoom self.mode: InterpolateMode = InterpolateMode(mode) self.padding_mode = padding_mode @@ -996,6 +995,7 @@ def __init__( self.dtype = dtype self.keep_size = keep_size self.kwargs = kwargs + self.lazy_evaluation = lazy_evaluation def __call__( self, @@ -1080,7 +1080,7 @@ def __init__( self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1), - lazy_evaluation: bool | None = None + lazy_evaluation: bool = False ) -> None: """ Args: @@ -1089,12 +1089,12 @@ def __init__( Default: (0, 1), this is the first two axis in spatial dimensions. If axis is negative it counts from the last to the first axis. """ - self.lazy_evaluation = lazy_evaluation self.k = (4 + (k % 4)) % 4 # 0, 1, 2, 3 spatial_axes_: tuple[int, int] = ensure_tuple(spatial_axes) # type: ignore if len(spatial_axes_) != 2: raise ValueError(f"spatial_axes must be 2 numbers to define the plane to rotate, got {spatial_axes_}.") self.spatial_axes = spatial_axes_ + self.lazy_evaluation = lazy_evaluation def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: """ @@ -1134,7 +1134,7 @@ def __init__( prob: float = 0.1, max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1), - lazy_evaluation: bool | None = None + lazy_evaluation: bool = False ) -> None: """ Args: @@ -1145,9 +1145,9 @@ def __init__( Default: (0, 1), this is the first two axis in spatial dimensions. """ RandomizableTransform.__init__(self, prob) - self.lazy_evaluation = lazy_evaluation self.max_k = max_k self.spatial_axes = spatial_axes + self.lazy_evaluation = lazy_evaluation self._rand_k = 0 @@ -1226,10 +1226,9 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike | torch.dtype = np.float32, - lazy_evaluation: bool | None = None, + lazy_evaluation: bool = False, ) -> None: RandomizableTransform.__init__(self, prob) - self.lazy_evaluation = lazy_evaluation self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -1245,6 +1244,7 @@ def __init__( self.padding_mode: str = look_up_option(padding_mode, GridSamplePadMode) self.align_corners = align_corners self.dtype = dtype + self.lazy_evaluation = lazy_evaluation self.x = 0.0 self.y = 0.0 @@ -1332,8 +1332,8 @@ def __init__( lazy_evaluation: bool = False, ) -> None: RandomizableTransform.__init__(self, prob) - self.lazy_evaluation = lazy_evaluation self.flipper = Flip(spatial_axis=spatial_axis, lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation # @LazyTransform.lazy_evaluation.setter # type: ignore # def lazy_evaluation(self, val: bool): @@ -1382,9 +1382,9 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform, LazyTransform): def __init__(self, prob: float = 0.1, lazy_evaluation: bool = False) -> None: RandomizableTransform.__init__(self, prob) - self.lazy_evaluation = lazy_evaluation self._axis: int | None = None self.flipper = Flip(spatial_axis=self._axis) + self.lazy_evaluation = lazy_evaluation # @LazyTransform.lazy_evaluation.setter # type: ignore # def lazy_evaluation(self, val: bool): @@ -1482,7 +1482,6 @@ def __init__( **kwargs, ) -> None: RandomizableTransform.__init__(self, prob) - self.lazy_evaluation = lazy_evaluation self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): @@ -1494,6 +1493,7 @@ def __init__( self.align_corners = align_corners self.dtype = dtype self.keep_size = keep_size + self.lazy_evaluation = lazy_evaluation self.kwargs = kwargs self._zoom: Sequence[float] = [1.0] @@ -1616,7 +1616,6 @@ def __init__( affine: NdarrayOrTensor | None = None, lazy_evaluation: bool = False, ) -> None: - self.lazy_evaluation = lazy_evaluation self.rotate_params = rotate_params self.shear_params = shear_params self.translate_params = translate_params @@ -1626,6 +1625,7 @@ def __init__( self.dtype = _dtype if _dtype in (torch.float16, torch.float64, None) else torch.float32 self.align_corners = align_corners self.affine = affine + self.lazy_evaluation = lazy_evaluation def __call__( self, @@ -1744,7 +1744,6 @@ def __init__( - :py:meth:`monai.transforms.utils.create_scale` """ - self.lazy_evaluation = lazy_evaluation self.rotate_range = ensure_tuple(rotate_range) self.shear_range = ensure_tuple(shear_range) self.translate_range = ensure_tuple(translate_range) @@ -1758,6 +1757,7 @@ def __init__( self.device = device self.dtype = dtype self.affine: torch.Tensor | None = torch.eye(4, dtype=torch.float64) + self.lazy_evaluation = lazy_evaluation def _get_rand_param(self, param_range, add_scalar: float = 0.0): out_param = [] @@ -2136,13 +2136,13 @@ def __init__( device=device, lazy_evaluation=lazy_evaluation, ) - self.lazy_evaluation = lazy_evaluation self.image_only = image_only self.norm_coord = not normalized self.resampler = Resample(norm_coords=self.norm_coord, device=device, dtype=dtype, align_corners=align_corners) self.spatial_size = spatial_size self.mode = mode self.padding_mode: str = padding_mode + self.lazy_evaluation = lazy_evaluation # @LazyTransform.lazy_evaluation.setter # type: ignore # def lazy_evaluation(self, val: bool) -> None: @@ -2313,7 +2313,6 @@ def __init__( """ RandomizableTransform.__init__(self, prob) - self.lazy_evaluation = lazy_evaluation self.rand_affine_grid = RandAffineGrid( rotate_range=rotate_range, @@ -2330,6 +2329,7 @@ def __init__( self._cached_grid = self._init_identity_cache(lazy_evaluation) self.mode = mode self.padding_mode: str = padding_mode + self.lazy_evaluation = lazy_evaluation # @LazyTransform.lazy_evaluation.setter # type: ignore # def lazy_evaluation(self, val: bool) -> None: From 4e693137b0777c06473500569c8fda64c2c66b95 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 29 Mar 2023 13:37:29 +0100 Subject: [PATCH 005/175] Updating apply and dictionary spatial transforms to support lazy_evaluation initing / calls Signed-off-by: Ben Murray --- monai/transforms/spatial/array.py | 127 ++++-- monai/transforms/spatial/dictionary.py | 529 +++++++++++++++++++------ 2 files changed, 495 insertions(+), 161 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 958f916dee..8a953c24fa 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -156,6 +156,8 @@ def __init__( dtype: data type for resampling computation. Defaults to ``float64`` for best precision. If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ self.mode = mode self.padding_mode = padding_mode @@ -204,7 +206,9 @@ def __call__( dtype: data type for resampling computation. Defaults to ``self.dtype`` or ``np.float64`` (for best precision). If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always `float32`. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. The spatial rank is determined by the smallest among ``img.ndim -1``, ``len(src_affine) - 1``, and ``3``. When both ``monai.config.USE_COMPILED`` and ``align_corners`` are set to ``True``, @@ -252,7 +256,7 @@ def __call__( # type: ignore padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike = None, - lazy_evaluation: bool = False, + lazy_evaluation: bool | None = None, ) -> torch.Tensor: """ Args: @@ -276,6 +280,10 @@ def __call__( # type: ignore dtype: data type for resampling computation. Defaults to ``self.dtype`` or ``np.float64`` (for best precision). If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always `float32`. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + Raises: ValueError: When the affine matrix of the source image is not invertible. Returns: @@ -385,7 +393,8 @@ def __init__( max_pixdim: maximal input spacing to be resampled. If provided, input image with a smaller spacing than this value will be kept in its original spacing (not be resampled to `pixdim`). Set it to `None` to use the value of `pixdim`. Default to `None`. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ self.pixdim = np.array(ensure_tuple(pixdim), dtype=np.float64) self.min_pixdim = np.array(ensure_tuple(min_pixdim), dtype=np.float64) @@ -404,11 +413,6 @@ def __init__( lazy_evaluation=lazy_evaluation ) - # @LazyTransform.lazy_evaluation.setter # type: ignore - # def lazy_evaluation(self, val: bool) -> None: - # self._lazy_evaluation = val - # self.sp_resample.lazy_evaluation = val - @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") def __call__( self, @@ -450,6 +454,9 @@ def __call__( output_spatial_shape: specify the shape of the output data_array. This is typically useful for the inverse of `Spacingd` where sometimes we could not compute the exact shape due to the quantization error with the affine. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. Raises: ValueError: When ``data_array`` has no spatial dimensions. @@ -547,6 +554,8 @@ def __init__( labels: optional, None or sequence of (2,) sequences (2,) sequences are labels for (beginning, end) of output axis. Defaults to ``(('L', 'R'), ('P', 'A'), ('I', 'S'))``. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False Raises: ValueError: When ``axcodes=None`` and ``as_closest_canonical=True``. Incompatible values. @@ -570,6 +579,9 @@ def __call__(self, data_array: torch.Tensor, lazy_evaluation: bool | None = None Args: data_array: in shape (num_channels, H[, W, ...]). + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. Raises: ValueError: When ``data_array`` has no spatial dimensions. @@ -644,6 +656,8 @@ class Flip(InvertibleTransform, LazyTransform): If axis is negative it counts from the last to the first axis. If axis is a tuple of ints, flipping is performed on all of the axes specified in the tuple. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ @@ -657,6 +671,9 @@ def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> to """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation @@ -703,6 +720,8 @@ class Resize(InvertibleTransform, LazyTransform): anti-aliasing is performed prior to rescaling. dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = [TransformBackends.TORCH] @@ -758,7 +777,9 @@ def __call__( anti-aliasing is performed prior to rescaling. dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. Raises: ValueError: When ``self.spatial_size`` length is less than ``img`` spatial dimensions. @@ -845,6 +866,8 @@ class Rotate(InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = [TransformBackends.TORCH] @@ -892,6 +915,9 @@ def __call__( dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. Raises: ValueError: When ``img`` spatially is not one of [2D, 3D]. @@ -970,9 +996,10 @@ class Zoom(InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. keep_size: Should keep original size (padding/slicing if needed), default is True. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. - """ backend = [TransformBackends.TORCH] @@ -1025,7 +1052,9 @@ def __call__( See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) _zoom = ensure_tuple_rep(self.zoom, img.ndim - 1) # match the spatial image dim @@ -1088,6 +1117,8 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. If axis is negative it counts from the last to the first axis. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ self.k = (4 + (k % 4)) % 4 # 0, 1, 2, 3 spatial_axes_: tuple[int, int] = ensure_tuple(spatial_axes) # type: ignore @@ -1100,6 +1131,9 @@ def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> to """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) axes = map_spatial_axes(img.ndim, self.spatial_axes) @@ -1143,6 +1177,8 @@ def __init__( max_k: number of rotations will be sampled from `np.random.randint(max_k) + 1`, (Default 3). spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ RandomizableTransform.__init__(self, prob) self.max_k = max_k @@ -1162,6 +1198,9 @@ def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: b Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), randomize: whether to execute `randomize()` function first, default to True. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ if randomize: @@ -1211,6 +1250,8 @@ class RandRotate(RandomizableTransform, InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Rotate.backend @@ -1283,6 +1324,9 @@ def __call__( If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. randomize: whether to execute `randomize()` function first, default to True. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ if randomize: self.randomize() @@ -1321,6 +1365,8 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): Args: prob: Probability of flipping. spatial_axis: Spatial axes along which to flip over. Default is None. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Flip.backend @@ -1335,11 +1381,6 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis, lazy_evaluation=lazy_evaluation) self.lazy_evaluation = lazy_evaluation - # @LazyTransform.lazy_evaluation.setter # type: ignore - # def lazy_evaluation(self, val: bool): - # self.flipper.lazy_evaluation = val - # self._lazy_evaluation = val - def __call__( self, img: torch.Tensor, @@ -1350,6 +1391,9 @@ def __call__( Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), randomize: whether to execute `randomize()` function first, default to True. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ if randomize: self.randomize(None) @@ -1375,7 +1419,8 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform, LazyTransform): Args: prob: Probability of flipping. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Flip.backend @@ -1386,11 +1431,6 @@ def __init__(self, prob: float = 0.1, lazy_evaluation: bool = False) -> None: self.flipper = Flip(spatial_axis=self._axis) self.lazy_evaluation = lazy_evaluation - # @LazyTransform.lazy_evaluation.setter # type: ignore - # def lazy_evaluation(self, val: bool): - # self.flipper.lazy_evaluation = val - # self._lazy_evaluation = val - def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) if not self._do_transform: @@ -1407,6 +1447,9 @@ def __call__( Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) randomize: whether to execute `randomize()` function first, default to True. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ if randomize: self.randomize(data=img) @@ -1461,6 +1504,8 @@ class RandZoom(RandomizableTransform, InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. keep_size: Should keep original size (pad if needed), default is True. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -1539,7 +1584,9 @@ def __call__( dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. randomize: whether to execute `randomize()` function first, default to True. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ # match the spatial image dim if randomize: @@ -1599,7 +1646,8 @@ class AffineGrid(LazyTransform): affine: If applied, ignore the params (`rotate_params`, etc.) and use the supplied matrix. Should be square with each side = num of image spatial dimensions + 1. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = [TransformBackends.TORCH] @@ -1641,7 +1689,9 @@ def __call__( Args: spatial_size: output grid size. grid: grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. Raises: ValueError: When ``grid=None`` and ``spatial_size=None``. Incompatible values. @@ -1736,6 +1786,8 @@ def __init__( device: device to store the output grid data. dtype: data type for the grid computation. Defaults to ``np.float32``. If ``None``, use the data type of input data (if `grid` is provided). + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False See also: - :py:meth:`monai.transforms.utils.create_rotate` @@ -1788,6 +1840,9 @@ def __call__( spatial_size: output grid size. grid: grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D. randomize: boolean as to whether the grid parameters governing the grid should be randomized. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. Returns: a 2D (3xHxW) or 3D (4xHxWxD) grid. @@ -2123,7 +2178,8 @@ def __init__( align_corners: Defaults to False. See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html image_only: if True return only the image volume, otherwise return (image, affine). - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ self.affine_grid = AffineGrid( rotate_params=rotate_params, @@ -2144,11 +2200,6 @@ def __init__( self.padding_mode: str = padding_mode self.lazy_evaluation = lazy_evaluation - # @LazyTransform.lazy_evaluation.setter # type: ignore - # def lazy_evaluation(self, val: bool) -> None: - # self.affine_grid.lazy_evaluation = val - # self._lazy_evaluation = val - def __call__( self, img: torch.Tensor, @@ -2177,6 +2228,9 @@ def __call__( When `mode` is an integer, using numpy/cupy backends, this argument accepts {'reflect', 'grid-mirror', 'constant', 'grid-constant', 'nearest', 'mirror', 'grid-wrap', 'wrap'}. See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -2306,6 +2360,8 @@ def __init__( If the spatial size is not dynamically defined by input image, enabling this option could accelerate the transform. device: device on which the tensor will be allocated. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False See also: - :py:class:`RandAffineGrid` for the random affine parameters configurations. @@ -2331,11 +2387,6 @@ def __init__( self.padding_mode: str = padding_mode self.lazy_evaluation = lazy_evaluation - # @LazyTransform.lazy_evaluation.setter # type: ignore - # def lazy_evaluation(self, val: bool) -> None: - # self._lazy_evaluation = val - # self.rand_affine_grid.lazy_evaluation = val - def _init_identity_cache(self, lazy_evaluation: bool): """ Create cache of the identity grid if cache_grid=True and spatial_size is known. @@ -2423,7 +2474,9 @@ def __call__( See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html randomize: whether to execute `randomize()` function first, default to True. grid: precomputed grid to be used (mainly to accelerate `RandAffined`). - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. """ if randomize: self.randomize() diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 36e86da903..832d640a3d 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -169,6 +169,7 @@ def __init__( dtype: Sequence[DtypeLike] | DtypeLike = np.float64, dst_keys: KeysCollection | None = "dst_affine", allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -196,21 +197,32 @@ def __init__( It also can be a sequence of dtypes, each element corresponds to a key in ``keys``. dst_keys: the key of the corresponding ``dst_affine`` in the metadata dictionary. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ super().__init__(keys, allow_missing_keys) - self.sp_transform = SpatialResample() + self.sp_transform = SpatialResample(lazy_evaluation=lazy_evaluation) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) + self.lazy_evaluation = lazy_evaluation - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.sp_transform.lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d: dict = dict(data) for key, mode, padding_mode, align_corners, dtype, dst_key in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype, self.dst_keys @@ -223,6 +235,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation_, ) return d @@ -247,6 +260,7 @@ def __init__( align_corners: Sequence[bool] | bool = False, dtype: Sequence[DtypeLike] | DtypeLike = np.float64, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ): """ Args: @@ -274,6 +288,8 @@ def __init__( the output data type is always ``float32``. It also can be a sequence of dtypes, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ super().__init__(keys, allow_missing_keys) self.key_dst = key_dst @@ -281,14 +297,23 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.resampler = ResampleToMatch() - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.resampler.lazy_evaluation = val + self.resampler = ResampleToMatch(lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d = dict(data) for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -300,6 +325,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation_, ) return d @@ -341,6 +367,7 @@ def __init__( max_pixdim: Sequence[float] | float | None = None, ensure_same_shape: bool = True, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -400,11 +427,13 @@ def __init__( ensure_same_shape: when the inputs have the same spatial shape, and almost the same pixdim, whether to ensure exactly the same output spatial shape. Default to True. allow_missing_keys: don't raise exception if key is missing. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ super().__init__(keys, allow_missing_keys) self.spacing_transform = Spacing( - pixdim, diagonal=diagonal, recompute_affine=recompute_affine, min_pixdim=min_pixdim, max_pixdim=max_pixdim + pixdim, diagonal=diagonal, recompute_affine=recompute_affine, min_pixdim=min_pixdim, max_pixdim=max_pixdim, + lazy_evaluation=lazy_evaluation ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) @@ -412,17 +441,26 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.scale_extent = ensure_tuple_rep(scale_extent, len(self.keys)) self.ensure_same_shape = ensure_same_shape + self.lazy_evaluation = lazy_evaluation - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.spacing_transform.lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d: dict = dict(data) _init_shape, _pixdim, should_match = None, None, False output_shape_k = None # tracking output shape + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key, mode, padding_mode, align_corners, dtype, scale_extent in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype, self.scale_extent @@ -442,6 +480,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc dtype=dtype, scale_extent=scale_extent, output_spatial_shape=output_shape_k if should_match else None, + lazy_evaluation=lazy_evaluation_, ) output_shape_k = d[key].peek_pending_shape() if isinstance(d[key], MetaTensor) else d[key].shape[1:] return d @@ -471,6 +510,7 @@ def __init__( as_closest_canonical: bool = False, labels: Sequence[tuple[str, str]] | None = (("L", "R"), ("P", "A"), ("I", "S")), allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -484,23 +524,34 @@ def __init__( (2,) sequences are labels for (beginning, end) of output axis. Defaults to ``(('L', 'R'), ('P', 'A'), ('I', 'S'))``. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False See Also: `nibabel.orientations.ornt2axcodes`. """ super().__init__(keys, allow_missing_keys) - self.ornt_transform = Orientation(axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels) - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.ornt_transform.lazy_evaluation = val + self.ornt_transform = Orientation( + axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy_evaluation=lazy_evaluation) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d: dict = dict(data) for key in self.key_iterator(d): - d[key] = self.ornt_transform(d[key]) + d[key] = self.ornt_transform(d[key], lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -518,7 +569,12 @@ class Rotate90d(MapTransform, InvertibleTransform, LazyTransform): backend = Rotate90.backend def __init__( - self, keys: KeysCollection, k: int = 1, spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False + self, + keys: KeysCollection, + k: int = 1, + spatial_axes: tuple[int, int] = (0, 1), + allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -526,19 +582,29 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ super().__init__(keys, allow_missing_keys) - self.rotator = Rotate90(k, spatial_axes) + self.rotator = Rotate90(k, spatial_axes, lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.rotator.lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) for key in self.key_iterator(d): - d[key] = self.rotator(d[key]) + d[key] = self.rotator(d[key], lazy_evaluation=lazy_evaluation) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -564,6 +630,7 @@ def __init__( max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -576,6 +643,8 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) @@ -584,21 +653,37 @@ def __init__( self.spatial_axes = spatial_axes self._rand_k = 0 + self.lazy_evaluation = lazy_evaluation def randomize(self, data: Any | None = None) -> None: self._rand_k = self.R.randint(self.max_k) + 1 super().randomize(None) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Mapping[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> Mapping[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ self.randomize() d = dict(data) # FIXME: here we didn't use array version `RandRotate90` transform as others, because we need # to be compatible with the random status of some previous integration tests - rotator = Rotate90(self._rand_k, self.spatial_axes) - rotator.lazy_evaluation = self.lazy_evaluation + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + rotator = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) for key in self.key_iterator(d): - d[key] = rotator(d[key]) if self._do_transform else convert_to_tensor(d[key], track_meta=get_track_meta()) + if self._do_transform: + d[key] = rotator(d[key]) + else: + convert_to_tensor(d[key], track_meta=get_track_meta()) self.push_transform(d[key], replace=True) return d @@ -649,6 +734,8 @@ class Resized(MapTransform, InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Resize.backend @@ -664,6 +751,7 @@ def __init__( anti_aliasing_sigma: Sequence[Sequence[float] | float | None] | Sequence[float] | float | None = None, dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.mode = ensure_tuple_rep(mode, len(self.keys)) @@ -671,14 +759,23 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.anti_aliasing = ensure_tuple_rep(anti_aliasing, len(self.keys)) self.anti_aliasing_sigma = ensure_tuple_rep(anti_aliasing_sigma, len(self.keys)) - self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode) - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.resizer.lazy_evaluation = val + self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d = dict(data) for key, mode, align_corners, anti_aliasing, anti_aliasing_sigma, dtype in self.key_iterator( d, self.mode, self.align_corners, self.anti_aliasing, self.anti_aliasing_sigma, self.dtype @@ -690,6 +787,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc anti_aliasing=anti_aliasing, anti_aliasing_sigma=anti_aliasing_sigma, dtype=dtype, + lazy_evaluation=lazy_evaluation_, ) return d @@ -722,6 +820,7 @@ def __init__( dtype: DtypeLike | torch.dtype = np.float32, align_corners: bool = False, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -772,6 +871,8 @@ def __init__( align_corners: Defaults to False. See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False See also: - :py:class:`monai.transforms.compose.MapTransform` @@ -789,19 +890,29 @@ def __init__( device=device, dtype=dtype, # type: ignore align_corners=align_corners, + lazy_evaluation=lazy_evaluation, ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) + self.lazy_evaluation = lazy_evaluation - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.affine.lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d = dict(data) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): - d[key], _ = self.affine(d[key], mode=mode, padding_mode=padding_mode) + d[key], _ = self.affine(d[key], mode=mode, padding_mode=padding_mode, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -832,6 +943,7 @@ def __init__( cache_grid: bool = False, device: torch.device | None = None, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -885,6 +997,8 @@ def __init__( accelerate the transform. device: device on which the tensor will be allocated. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False See also: - :py:class:`monai.transforms.compose.MapTransform` @@ -902,21 +1016,29 @@ def __init__( spatial_size=spatial_size, cache_grid=cache_grid, device=device, + lazy_evaluation=lazy_evaluation ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool) -> None: - self._lazy_evaluation = val - self.rand_affine.lazy_evaluation = val - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAffined: self.rand_affine.set_random_state(seed, state) super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy_evaluation: bool | None = None) -> dict[Hashable, NdarrayOrTensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): @@ -929,6 +1051,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, N item = d[first_key] spatial_size = item.peek_pending_shape() if isinstance(item, MetaTensor) else item.shape[1:] + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation sp_size = fall_back_tuple(self.rand_affine.spatial_size, spatial_size) # change image size or do random transform @@ -936,14 +1059,14 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, N # converting affine to tensor because the resampler currently only support torch backend grid = None if do_resampling: # need to prepare grid - grid = self.rand_affine.get_identity_grid(sp_size) + grid = self.rand_affine.get_identity_grid(sp_size, lazy_evaluation=lazy_evaluation_) if self._do_transform: # add some random factors - grid = self.rand_affine.rand_affine_grid(sp_size, grid=grid) + grid = self.rand_affine.rand_affine_grid(sp_size, grid=grid, lazy_evaluation=lazy_evaluation_) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): # do the transform if do_resampling: - d[key] = self.rand_affine(d[key], None, mode, padding_mode, True, grid) # type: ignore + d[key] = self.rand_affine(d[key], None, mode, padding_mode, True, grid, lazy_evaluation=lazy_evaluation_) # type: ignore else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) self._do_transform = do_resampling # TODO: unify self._do_transform and do_resampling @@ -1066,6 +1189,15 @@ def set_random_state(self, seed: int | None = None, state: np.random.RandomState return self def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) first_key: Hashable = self.first_key(d) @@ -1208,6 +1340,15 @@ def set_random_state(self, seed: int | None = None, state: np.random.RandomState return self def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) first_key: Hashable = self.first_key(d) @@ -1250,25 +1391,40 @@ class Flipd(MapTransform, InvertibleTransform, LazyTransform): keys: Keys to pick data for transformation. spatial_axis: Spatial axes along which to flip over. Default is None. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Flip.backend def __init__( - self, keys: KeysCollection, spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False + self, + keys: KeysCollection, + spatial_axis: Sequence[int] | int | None = None, + allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.flipper = Flip(spatial_axis=spatial_axis) + self.lazy_evaluation = lazy_evaluation - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.flipper.lazy_evaluation = val - self._lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): - d[key] = self.flipper(d[key]) + d[key] = self.flipper(d[key], lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1290,6 +1446,8 @@ class RandFlipd(RandomizableTransform, MapTransform, InvertibleTransform, LazyTr prob: Probability of flipping. spatial_axis: Spatial axes along which to flip over. Default is None. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Flip.backend @@ -1300,27 +1458,37 @@ def __init__( prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.flipper = Flip(spatial_axis=spatial_axis) - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.flipper.lazy_evaluation = val - self._lazy_evaluation = val + self.flipper = Flip(spatial_axis=spatial_axis, lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) self.randomize(None) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): if self._do_transform: - d[key] = self.flipper(d[key]) + d[key] = self.flipper(d[key], lazy_evaluation=lazy_evaluation_) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) self.push_transform(d[key], replace=True) @@ -1348,27 +1516,42 @@ class RandAxisFlipd(RandomizableTransform, MapTransform, InvertibleTransform, La keys: Keys to pick data for transformation. prob: Probability of flipping. allow_missing_keys: don't raise exception if key is missing. - + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = RandAxisFlip.backend - def __init__(self, keys: KeysCollection, prob: float = 0.1, allow_missing_keys: bool = False) -> None: + def __init__( + self, + keys: KeysCollection, + prob: float = 0.1, + allow_missing_keys: bool = False, + lazy_evaluation: bool = False, + ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.flipper = RandAxisFlip(prob=1.0) - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.flipper.lazy_evaluation = val - self._lazy_evaluation = val + self.flipper = RandAxisFlip(prob=1.0, lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) self.flipper.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): @@ -1379,9 +1562,10 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc # all the keys share the same random selected axis self.flipper.randomize(d[first_key]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): if self._do_transform: - d[key] = self.flipper(d[key], randomize=False) + d[key] = self.flipper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) self.push_transform(d[key], replace=True) @@ -1423,6 +1607,8 @@ class Rotated(MapTransform, InvertibleTransform, LazyTransform): the output data type is always ``float32``. It also can be a sequence of dtype or None, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = Rotate.backend @@ -1437,27 +1623,38 @@ def __init__( align_corners: Sequence[bool] | bool = False, dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) - self.rotator = Rotate(angle=angle, keep_size=keep_size) + self.rotator = Rotate(angle=angle, keep_size=keep_size, lazy_evaluation=lazy_evaluation) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) + self.lazy_evaluation = lazy_evaluation - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.rotator.lazy_evaluation = val - self._lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype ): d[key] = self.rotator( - d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype + d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation_ ) return d @@ -1501,6 +1698,8 @@ class RandRotated(RandomizableTransform, MapTransform, InvertibleTransform, Lazy the output data type is always ``float32``. It also can be a sequence of dtype or None, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = RandRotate.backend @@ -1518,31 +1717,43 @@ def __init__( align_corners: Sequence[bool] | bool = False, dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.rand_rotate = RandRotate(range_x=range_x, range_y=range_y, range_z=range_z, prob=1.0, keep_size=keep_size) + self.rand_rotate = RandRotate(range_x=range_x, range_y=range_y, range_z=range_z, prob=1.0, keep_size=keep_size, + lazy_evaluation=lazy_evaluation) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.rand_rotate.lazy_evaluation = val - self._lazy_evaluation = val + self.lazy_evaluation = lazy_evaluation def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) self.rand_rotate.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) self.randomize(None) # all the keys share the same random rotate angle self.rand_rotate.randomize() + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype ): @@ -1554,6 +1765,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc align_corners=align_corners, dtype=dtype, randomize=False, + lazy_evaluation=lazy_evaluation_, ) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) @@ -1598,6 +1810,8 @@ class Zoomd(MapTransform, InvertibleTransform, LazyTransform): If None, use the data type of input data. keep_size: Should keep original size (pad if needed), default is True. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -1615,6 +1829,7 @@ def __init__( dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, keep_size: bool = True, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, **kwargs, ) -> None: super().__init__(keys, allow_missing_keys) @@ -1622,19 +1837,28 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, **kwargs) + self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, lazy_evaluation=lazy_evaluation, **kwargs) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.zoomer.lazy_evaluation = val - self._lazy_evaluation = val - - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype ): - d[key] = self.zoomer(d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype) + d[key] = self.zoomer(d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, + lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1680,9 +1904,10 @@ class RandZoomd(RandomizableTransform, MapTransform, InvertibleTransform, LazyTr If None, use the data type of input data. keep_size: Should keep original size (pad if needed), default is True. allow_missing_keys: don't raise exception if key is missing. + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False kwargs: other args for `np.pad` API, note that `np.pad` treats channel dimension as the first dimension. more details: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html - """ backend = RandZoom.backend @@ -1699,27 +1924,37 @@ def __init__( dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, keep_size: bool = True, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, **kwargs, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.rand_zoom = RandZoom(prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, keep_size=keep_size, **kwargs) + self.rand_zoom = RandZoom(prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, keep_size=keep_size, + lazy_evaluation=lazy_evaluation, **kwargs) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.rand_zoom.lazy_evaluation = val - self._lazy_evaluation = val + self.lazy_evaluation = lazy_evaluation def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + during initialization for this call. Defaults to None. + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): @@ -1730,6 +1965,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc # all the keys share the same random zoom factor self.rand_zoom.randomize(d[first_key]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -1742,6 +1978,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc align_corners=align_corners, dtype=dtype, randomize=False, + lazy_evaluation=lazy_evaluation_ ) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) @@ -1798,7 +2035,6 @@ def __init__( It also can be a sequence, each element corresponds to a key in ``keys``. device: device on which the tensor will be allocated. allow_missing_keys: don't raise exception if key is missing. - """ super().__init__(keys, allow_missing_keys) self.grid_distortion = GridDistortion(num_cells=num_cells, distort_steps=distort_steps, device=device) @@ -1806,6 +2042,15 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): d[key] = self.grid_distortion(d[key], mode=mode, padding_mode=padding_mode) @@ -1872,6 +2117,15 @@ def set_random_state( return self def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) self.randomize(None) if not self._do_transform: @@ -1922,6 +2176,15 @@ def __init__( self.splitter = GridSplit(grid=grid) def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> list[dict[Hashable, NdarrayOrTensor]]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) n_outputs = np.prod(self.grid) output: list[dict[Hashable, NdarrayOrTensor]] = [dict(d) for _ in range(n_outputs)] @@ -1998,6 +2261,15 @@ def __init__( ) def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) for key in self.key_iterator(d): d[key] = self.patcher(d[key]) @@ -2081,6 +2353,15 @@ def set_random_state(self, seed: int | None = None, state: np.random.RandomState return self def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: + """ + Args: + data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified + in this dictionary must be tensor like arrays that are channel first and have at most + three spatial dimensions + + Returns: + a dictionary containing the transformed data, as well as any other data present in the dictionary + """ d = dict(data) # All the keys share the same random noise for key in self.key_iterator(d): From 99fa35b95fe8fafab4d1f5b141561950bb26a8f3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 29 Mar 2023 15:13:24 +0100 Subject: [PATCH 006/175] Missing changes to spatial dictionary Signed-off-by: Ben Murray --- monai/transforms/spatial/dictionary.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 832d640a3d..3e5df33805 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -534,6 +534,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.ornt_transform = Orientation( axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy_evaluation=lazy_evaluation) + self.lazy_evaluation = lazy_evaluation def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -548,8 +549,8 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d: dict = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): d[key] = self.ornt_transform(d[key], lazy_evaluation=lazy_evaluation_) return d @@ -603,8 +604,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool a dictionary containing the transformed data, as well as any other data present in the dictionary """ d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): - d[key] = self.rotator(d[key], lazy_evaluation=lazy_evaluation) + d[key] = self.rotator(d[key], lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -681,6 +683,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool rotator = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) for key in self.key_iterator(d): if self._do_transform: + # no need to override lazy_evaluation here as we already set it on the initializer d[key] = rotator(d[key]) else: convert_to_tensor(d[key], track_meta=get_track_meta()) @@ -775,8 +778,8 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key, mode, align_corners, anti_aliasing, anti_aliasing_sigma, dtype in self.key_iterator( d, self.mode, self.align_corners, self.anti_aliasing, self.anti_aliasing_sigma, self.dtype ): From a798e0ec47e769cf443a807a48551a206577ce12 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 29 Mar 2023 16:30:58 +0100 Subject: [PATCH 007/175] Enable call-time overriding of lazy_evaluation in croppad/functional and croppad/array Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 194 ++++++++++++++++--------- monai/transforms/croppad/functional.py | 18 ++- 2 files changed, 134 insertions(+), 78 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index aa13d54c51..88b418bf4b 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -106,8 +106,10 @@ class Pad(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] def __init__( - self, to_pad: tuple[tuple[int, int]] | None = None, mode: str = PytorchPadMode.CONSTANT, **kwargs + self, to_pad: tuple[tuple[int, int]] | None = None, mode: str = PytorchPadMode.CONSTANT, + lazy_evaluation: bool = False, **kwargs ) -> None: + LazyTransform.__init__(lazy_evaluation) self.to_pad = to_pad self.mode = mode self.kwargs = kwargs @@ -124,7 +126,8 @@ def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, in raise NotImplementedError(f"subclass {self.__class__.__name__} must implement this method.") def __call__( # type: ignore[override] - self, img: torch.Tensor, to_pad: tuple[tuple[int, int]] | None = None, mode: str | None = None, **kwargs + self, img: torch.Tensor, to_pad: tuple[tuple[int, int]] | None = None, mode: str | None = None, + lazy_evaluation: bool | None = None, **kwargs ) -> torch.Tensor: """ Args: @@ -150,7 +153,8 @@ def __call__( # type: ignore[override] kwargs_.update(kwargs) img_t = convert_to_tensor(data=img, track_meta=get_track_meta()) - return pad_func(img_t, to_pad_, mode_, self.get_transform_info(), kwargs_) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return pad_func(img_t, to_pad_, mode_, lazy_evaluation_, self.get_transform_info(), kwargs_) def inverse(self, data: MetaTensor) -> MetaTensor: transform = self.pop_transform(data) @@ -194,11 +198,12 @@ def __init__( spatial_size: Sequence[int] | int | tuple[tuple[int, ...] | int, ...], method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, + lazy_evaluation: bool = False, **kwargs, ) -> None: self.spatial_size = spatial_size self.method: Method = look_up_option(method, Method) - super().__init__(mode=mode, **kwargs) + super().__init__(mode=mode, lazy_evaluation=lazy_evaluation, **kwargs) def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, int]]: """ @@ -245,9 +250,12 @@ class BorderPad(Pad): """ - def __init__(self, spatial_border: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, **kwargs) -> None: + def __init__( + self, spatial_border: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, + lazy_evaluation: bool = False, **kwargs + ) -> None: self.spatial_border = spatial_border - super().__init__(mode=mode, **kwargs) + super().__init__(mode=mode, lazy_evaluation=lazy_evaluation, **kwargs) def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, int]]: spatial_border = ensure_tuple(self.spatial_border) @@ -279,7 +287,8 @@ class DivisiblePad(Pad): backend = SpatialPad.backend def __init__( - self, k: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, **kwargs + self, k: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, + lazy_evaluation: bool = False, **kwargs ) -> None: """ Args: @@ -301,7 +310,7 @@ def __init__( """ self.k = k self.method: Method = Method(method) - super().__init__(mode=mode, **kwargs) + super().__init__(mode=mode, lazy_evaluation=lazy_evaluation, **kwargs) def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, int]]: new_size = compute_divisible_spatial_size(spatial_shape=spatial_shape, k=self.k) @@ -313,10 +322,16 @@ class Crop(InvertibleTransform, LazyTransform): """ Perform crop operations on the input image. + Args: + lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + Defaults to False """ backend = [TransformBackends.TORCH] + def __init__(self, lazy_evaluation: bool = False): + LazyTransform.__init__(lazy_evaluation) + @staticmethod def compute_slices( roi_center: Sequence[int] | NdarrayOrTensor | None = None, @@ -370,7 +385,7 @@ def compute_slices( [slice(int(s), int(e)) for s, e in zip(roi_start_t.tolist(), roi_end_t.tolist())] ) - def __call__(self, img: torch.Tensor, slices: tuple[slice, ...]) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, slices: tuple[slice, ...], lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -384,7 +399,8 @@ def __call__(self, img: torch.Tensor, slices: tuple[slice, ...]) -> torch.Tensor slices_ = list([slice(None)] + slices_[:sd]) img_t: MetaTensor = convert_to_tensor(data=img, track_meta=get_track_meta()) - return crop_func(img_t, tuple(slices_), self.get_transform_info()) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return crop_func(img_t, tuple(slices_), lazy_evaluation_, self.get_transform_info()) def inverse(self, img: MetaTensor) -> MetaTensor: transform = self.pop_transform(img) @@ -417,6 +433,7 @@ def __init__( roi_start: Sequence[int] | NdarrayOrTensor | None = None, roi_end: Sequence[int] | NdarrayOrTensor | None = None, roi_slices: Sequence[slice] | None = None, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -428,17 +445,19 @@ def __init__( use the end coordinate of image. roi_slices: list of slices for each of the spatial dimensions. """ + super().__init__(lazy_evaluation) self.slices = self.compute_slices( roi_center=roi_center, roi_size=roi_size, roi_start=roi_start, roi_end=roi_end, roi_slices=roi_slices ) - def __call__(self, img: torch.Tensor) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy_evaluation: bool = False) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. """ - return super().__call__(img=img, slices=ensure_tuple(self.slices)) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return super().__call__(img=img, slices=ensure_tuple(self.slices), lazy_evaluation=lazy_evaluation_) class CenterSpatialCrop(Crop): @@ -456,7 +475,8 @@ class CenterSpatialCrop(Crop): the spatial size of output data will be [32, 40, 40]. """ - def __init__(self, roi_size: Sequence[int] | int) -> None: + def __init__(self, roi_size: Sequence[int] | int, lazy_evaluation: bool = False) -> None: + super().__init__(lazy_evaluation) self.roi_size = roi_size def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: ignore[override] @@ -464,15 +484,17 @@ def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: roi_center = [i // 2 for i in spatial_size] return super().compute_slices(roi_center=roi_center, roi_size=roi_size) - def __call__(self, img: torch.Tensor) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. """ + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation return super().__call__( img=img, slices=self.compute_slices(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]), + lazy_evaluation=lazy_evaluation_, ) @@ -486,15 +508,17 @@ class CenterScaleCrop(Crop): """ - def __init__(self, roi_scale: Sequence[float] | float): + def __init__(self, roi_scale: Sequence[float] | float, lazy_evaluation: bool = False): + super().__init__(lazy_evaluation) self.roi_scale = roi_scale - def __call__(self, img: torch.Tensor) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] ndim = len(img_size) roi_size = [ceil(r * s) for r, s in zip(ensure_tuple_rep(self.roi_scale, ndim), img_size)] - cropper = CenterSpatialCrop(roi_size=roi_size) - return super().__call__(img=img, slices=cropper.compute_slices(img_size)) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + cropper = CenterSpatialCrop(roi_size=roi_size, lazy_evaluation=lazy_evaluation_) + return super().__call__(img=img, slices=cropper.compute_slices(img_size), lazy_evaluation=lazy_evaluation_) class RandSpatialCrop(Randomizable, Crop): @@ -528,7 +552,9 @@ def __init__( max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, + lazy_evaluation: bool = False, ) -> None: + super().__init__(lazy_evaluation) self.roi_size = roi_size self.max_roi_size = max_roi_size self.random_center = random_center @@ -547,7 +573,7 @@ def randomize(self, img_size: Sequence[int]) -> None: valid_size = get_valid_patch_size(img_size, self._size) self._slices = get_random_patch(img_size, valid_size, self.R) - def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -558,10 +584,11 @@ def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: self.randomize(img_size) if self._size is None: raise RuntimeError("self._size not specified.") + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation if self.random_center: - return super().__call__(img=img, slices=self._slices) - cropper = CenterSpatialCrop(self._size) - return super().__call__(img=img, slices=cropper.compute_slices(img_size)) + return super().__call__(img=img, slices=self._slices, lazy_evaluation=lazy_evaluation_) + cropper = CenterSpatialCrop(self._size, lazy_evaluation=lazy_evaluation_) + return super().__call__(img=img, slices=cropper.compute_slices(img_size), lazy_evaluation=lazy_evaluation_) class RandScaleCrop(RandSpatialCrop): @@ -592,8 +619,12 @@ def __init__( max_roi_scale: Sequence[float] | float | None = None, random_center: bool = True, random_size: bool = True, + lazy_evaluation: bool = False, ) -> None: - super().__init__(roi_size=-1, max_roi_size=None, random_center=random_center, random_size=random_size) + super().__init__( + roi_size=-1, max_roi_size=None, random_center=random_center, random_size=random_size, + lazy_evaluation=lazy_evaluation + ) self.roi_scale = roi_scale self.max_roi_scale = max_roi_scale @@ -609,14 +640,15 @@ def randomize(self, img_size: Sequence[int]) -> None: self.get_max_roi_size(img_size) super().randomize(img_size) - def __call__(self, img: torch.Tensor, randomize: bool = True) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. """ self.get_max_roi_size(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]) - return super().__call__(img=img, randomize=randomize) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + return super().__call__(img=img, randomize=randomize, lazy_evaluation=lazy_evaluation_) class RandSpatialCropSamples(Randomizable, TraceableTransform, LazyTransform, MultiSampleTrait): @@ -660,11 +692,12 @@ def __init__( max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, + lazy_evaluation: bool = False, ) -> None: if num_samples < 1: raise ValueError(f"num_samples must be positive, got {num_samples}.") self.num_samples = num_samples - self.cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size) + self.cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size, lazy_evaluation) def set_random_state( self, seed: int | None = None, state: np.random.RandomState | None = None @@ -673,22 +706,23 @@ def set_random_state( self.cropper.set_random_state(seed, state) return self - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # self.cropper.lazy_evaluation = value def randomize(self, data: Any | None = None) -> None: pass - def __call__(self, img: torch.Tensor) -> list[torch.Tensor]: + def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> list[torch.Tensor]: """ Apply the transform to `img`, assuming `img` is channel-first and cropping doesn't change the channel dim. """ ret = [] + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for i in range(self.num_samples): - cropped = self.cropper(img) + cropped = self.cropper(img, lazy_evaluation=lazy_evaluation_) if get_track_meta(): cropped.meta[Key.PATCH_INDEX] = i # type: ignore self.push_transform(cropped, replace=True) # track as this class instead of RandSpatialCrop @@ -737,6 +771,7 @@ def __init__( return_coords: bool = False, k_divisible: Sequence[int] | int = 1, mode: str = PytorchPadMode.CONSTANT, + lazy_evaluation: bool = False, **pad_kwargs, ) -> None: """ @@ -761,18 +796,19 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ + LazyTransform.__init__(lazy_evaluation) self.select_fn = select_fn self.channel_indices = ensure_tuple(channel_indices) if channel_indices is not None else None self.margin = margin self.allow_smaller = allow_smaller self.return_coords = return_coords self.k_divisible = k_divisible - self.padder = Pad(mode=mode, **pad_kwargs) + self.padder = Pad(mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs) - @Crop.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, _val: bool): - self._lazy_evaluation = _val - self.padder.lazy_evaluation = _val + # @Crop.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, _val: bool): + # self._lazy_evaluation = _val + # self.padder.lazy_evaluation = _val def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ @@ -796,14 +832,15 @@ def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarra return box_start_, box_end_ def crop_pad( - self, img: torch.Tensor, box_start: np.ndarray, box_end: np.ndarray, mode: str | None = None, **pad_kwargs + self, img: torch.Tensor, box_start: np.ndarray, box_end: np.ndarray, mode: str | None = None, + lazy_evaluation: bool = False, **pad_kwargs ) -> torch.Tensor: """ Crop and pad based on the bounding box. """ slices = self.compute_slices(roi_start=box_start, roi_end=box_end) - cropped = super().__call__(img=img, slices=slices) + cropped = super().__call__(img=img, slices=slices, lazy_evaluation=lazy_evaluation) pad_to_start = np.maximum(-box_start, 0) pad_to_end = np.maximum( box_end - np.asarray(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]), 0 @@ -812,23 +849,26 @@ def crop_pad( pad_width = BorderPad(spatial_border=pad).compute_pad_width( cropped.peek_pending_shape() if isinstance(cropped, MetaTensor) else cropped.shape[1:] ) - ret = self.padder.__call__(img=cropped, to_pad=pad_width, mode=mode, **pad_kwargs) + ret = self.padder.__call__( + img=cropped, to_pad=pad_width, mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs + ) # combine the traced cropping and padding into one transformation # by taking the padded info and placing it in a key inside the crop info. if get_track_meta() and isinstance(ret, MetaTensor): - if not self.lazy_evaluation: + if not lazy_evaluation: ret.applied_operations[-1][TraceKeys.EXTRA_INFO]["pad_info"] = ret.applied_operations.pop() return ret def __call__( # type: ignore[override] - self, img: torch.Tensor, mode: str | None = None, **pad_kwargs + self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: bool | None = None, **pad_kwargs ) -> torch.Tensor: """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't change the channel dim. """ box_start, box_end = self.compute_bounding_box(img) - cropped = self.crop_pad(img, box_start, box_end, mode, **pad_kwargs) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + cropped = self.crop_pad(img, box_start, box_end, mode, lazy_evaluation=lazy_evaluation_, **pad_kwargs) if self.return_coords: return cropped, box_start, box_end # type: ignore[return-value] @@ -861,8 +901,10 @@ class RandWeightedCrop(Randomizable, TraceableTransform, LazyTransform, MultiSam backend = SpatialCrop.backend def __init__( - self, spatial_size: Sequence[int] | int, num_samples: int = 1, weight_map: NdarrayOrTensor | None = None + self, spatial_size: Sequence[int] | int, num_samples: int = 1, weight_map: NdarrayOrTensor | None = None, + lazy_evaluation: bool = False ): + LazyTransform.__init__(lazy_evaluation) self.spatial_size = ensure_tuple(spatial_size) self.num_samples = int(num_samples) self.weight_map = weight_map @@ -875,12 +917,13 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: spatial_size=self.spatial_size, w=weight_map[0], n_samples=self.num_samples, r_state=self.R ) # using only the first channel as weight map - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, _val: bool): - self._lazy_evaluation = _val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, _val: bool): + # self._lazy_evaluation = _val def __call__( - self, img: torch.Tensor, weight_map: NdarrayOrTensor | None = None, randomize: bool = True + self, img: torch.Tensor, weight_map: NdarrayOrTensor | None = None, randomize: bool = True, + lazy_evaluation: bool | None = None ) -> list[torch.Tensor]: """ Args: @@ -907,9 +950,9 @@ def __call__( _spatial_size = fall_back_tuple(self.spatial_size, img_shape) results: list[torch.Tensor] = [] + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for i, center in enumerate(self.centers): - cropper = SpatialCrop(roi_center=center, roi_size=_spatial_size) - cropper.lazy_evaluation = self.lazy_evaluation + cropper = SpatialCrop(roi_center=center, roi_size=_spatial_size, lazy_evaluation=lazy_evaluation_) cropped = cropper(img) if get_track_meta(): ret_: MetaTensor = cropped # type: ignore @@ -989,7 +1032,9 @@ def __init__( fg_indices: NdarrayOrTensor | None = None, bg_indices: NdarrayOrTensor | None = None, allow_smaller: bool = False, + lazy_evaluation: bool = False, ) -> None: + LazyTransform.__init__(lazy_evaluation) self.spatial_size = spatial_size self.label = label if pos < 0 or neg < 0: @@ -1040,9 +1085,9 @@ def randomize( self.allow_smaller, ) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, _val: bool): - self._lazy_evaluation = _val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, _val: bool): + # self._lazy_evaluation = _val def __call__( self, @@ -1052,6 +1097,7 @@ def __call__( fg_indices: NdarrayOrTensor | None = None, bg_indices: NdarrayOrTensor | None = None, randomize: bool = True, + lazy_evaluation: bool | None = None, ) -> list[torch.Tensor]: """ Args: @@ -1078,9 +1124,9 @@ def __call__( if self.centers is not None: img_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] roi_size = fall_back_tuple(self.spatial_size, default=img_shape) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for i, center in enumerate(self.centers): - cropper = SpatialCrop(roi_center=center, roi_size=roi_size) - cropper.lazy_evaluation = self.lazy_evaluation + cropper = SpatialCrop(roi_center=center, roi_size=roi_size, lazy_evaluation=lazy_evaluation_) cropped = cropper(img) if get_track_meta(): ret_: MetaTensor = cropped # type: ignore @@ -1170,7 +1216,9 @@ def __init__( indices: list[NdarrayOrTensor] | None = None, allow_smaller: bool = False, warn: bool = True, + lazy_evaluation: bool = False, ) -> None: + LazyTransform.__init__(lazy_evaluation) self.spatial_size = spatial_size self.ratios = ratios self.label = label @@ -1209,9 +1257,9 @@ def randomize( self.spatial_size, self.num_samples, _shape, indices_, self.ratios, self.R, self.allow_smaller, self.warn ) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, _val: bool): - self._lazy_evaluation = _val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, _val: bool): + # self._lazy_evaluation = _val def __call__( self, @@ -1220,6 +1268,7 @@ def __call__( image: torch.Tensor | None = None, indices: list[NdarrayOrTensor] | None = None, randomize: bool = True, + lazy_evaluation: bool | None = None, ) -> list[torch.Tensor]: """ Args: @@ -1242,9 +1291,9 @@ def __call__( if self.centers is not None: img_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] roi_size = fall_back_tuple(self.spatial_size, default=img_shape) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for i, center in enumerate(self.centers): - cropper = SpatialCrop(roi_center=tuple(center), roi_size=roi_size) - cropper.lazy_evaluation = self.lazy_evaluation + cropper = SpatialCrop(roi_center=tuple(center), roi_size=roi_size, lazy_evaluation=lazy_evaluation_) cropped = cropper(img) if get_track_meta(): ret_: MetaTensor = cropped # type: ignore @@ -1286,18 +1335,22 @@ def __init__( spatial_size: Sequence[int] | int, method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, + lazy_evaluation: bool = False, **pad_kwargs, ): - self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, **pad_kwargs) - self.cropper = CenterSpatialCrop(roi_size=spatial_size) + LazyTransform.__init__(lazy_evaluation) + self.padder = SpatialPad( + spatial_size=spatial_size, method=method, mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs + ) + self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy_evaluation=lazy_evaluation) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, val: bool): - self.padder.lazy_evaluation = val - self.cropper.lazy_evaluation = val - self._lazy_evaluation = val + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, val: bool): + # self.padder.lazy_evaluation = val + # self.cropper.lazy_evaluation = val + # self._lazy_evaluation = val - def __call__(self, img: torch.Tensor, mode: str | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: bool | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore """ Args: img: data to pad or crop, assuming `img` is channel-first and @@ -1312,11 +1365,12 @@ def __call__(self, img: torch.Tensor, mode: str | None = None, **pad_kwargs) -> note that `np.pad` treats channel dimension as the first dimension. """ - ret = self.padder(self.cropper(img), mode=mode, **pad_kwargs) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + ret = self.padder(self.cropper(img, lazy_evaluation_), mode=mode, lazy_evaluation=lazy_evaluation_, **pad_kwargs) # remove the individual info and combine if get_track_meta(): ret_: MetaTensor = ret # type: ignore - if not self.lazy_evaluation: + if not lazy_evaluation_: pad_info = ret_.applied_operations.pop() crop_info = ret_.applied_operations.pop() orig_size = crop_info.get(TraceKeys.ORIG_SIZE) diff --git a/monai/transforms/croppad/functional.py b/monai/transforms/croppad/functional.py index fa95958bd5..a61e3a65a7 100644 --- a/monai/transforms/croppad/functional.py +++ b/monai/transforms/croppad/functional.py @@ -148,11 +148,11 @@ def crop_or_pad_nd(img: torch.Tensor, translation_mat, spatial_size: tuple[int, def pad_func( - img: torch.Tensor, to_pad: tuple[tuple[int, int]], mode: str, transform_info: dict, kwargs + img: torch.Tensor, to_pad: tuple[tuple[int, int]], mode: str, lazy_evaluation: bool, transform_info: dict, kwargs ) -> torch.Tensor: """ Functional implementation of padding a MetaTensor. This function operates eagerly or lazily according - to ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + to ``lazy_evaluation`` (default ``False``). Args: img: data to be transformed, assuming `img` is channel-first and padding doesn't apply to the channel dim. @@ -164,6 +164,7 @@ def pad_func( One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html + lazy_evaluation: a flag indicating whether the operation should be performed in a lazy fashion or not. transform_info: a dictionary with the relevant information pertaining to an applied transform. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -189,24 +190,25 @@ def pad_func( extra_info=extra_info, orig_size=img_size, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = convert_to_tensor(img.as_tensor() if isinstance(img, MetaTensor) else img, track_meta=get_track_meta()) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info # type: ignore out = pad_nd(out, to_pad_list, mode, **kwargs) if do_pad else out out = convert_to_tensor(out, track_meta=get_track_meta()) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore -def crop_func(img: torch.Tensor, slices: tuple[slice, ...], transform_info: dict) -> torch.Tensor: +def crop_func(img: torch.Tensor, slices: tuple[slice, ...], lazy_evaluation: bool, transform_info: dict) -> torch.Tensor: """ Functional implementation of cropping a MetaTensor. This function operates eagerly or lazily according - to ``transform_info[TraceKeys.LAZY_EVALUATION]`` (default ``False``). + to ``lazy_evaluation`` (default ``False``). Args: img: data to be transformed, assuming `img` is channel-first and cropping doesn't apply to the channel dim. slices: the crop slices computed based on specified `center & size` or `start & end` or `slices`. + lazy_evaluation: a flag indicating whether the operation should be performed in a lazy fashion or not. transform_info: a dictionary with the relevant information pertaining to an applied transform. """ img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -227,10 +229,10 @@ def crop_func(img: torch.Tensor, slices: tuple[slice, ...], transform_info: dict extra_info=extra_info, orig_size=img_size, transform_info=transform_info, - lazy_evaluation=transform_info.get(TraceKeys.LAZY_EVALUATION, False), + lazy_evaluation=lazy_evaluation, ) out = convert_to_tensor(img.as_tensor() if isinstance(img, MetaTensor) else img, track_meta=get_track_meta()) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info # type: ignore out = out[slices] return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore From 114b8cc76eb689a7555961000d9817f9a8d56124 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 29 Mar 2023 17:26:04 +0100 Subject: [PATCH 008/175] Updating croppad dictionary and croppad array for call-time laziness Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 16 +- monai/transforms/croppad/dictionary.py | 211 ++++++++++++++++--------- monai/transforms/transform.py | 3 +- 3 files changed, 147 insertions(+), 83 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 88b418bf4b..cac1ca6c34 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -109,7 +109,7 @@ def __init__( self, to_pad: tuple[tuple[int, int]] | None = None, mode: str = PytorchPadMode.CONSTANT, lazy_evaluation: bool = False, **kwargs ) -> None: - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) self.to_pad = to_pad self.mode = mode self.kwargs = kwargs @@ -330,7 +330,7 @@ class Crop(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH] def __init__(self, lazy_evaluation: bool = False): - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) @staticmethod def compute_slices( @@ -509,7 +509,7 @@ class CenterScaleCrop(Crop): """ def __init__(self, roi_scale: Sequence[float] | float, lazy_evaluation: bool = False): - super().__init__(lazy_evaluation) + super().__init__(lazy_evaluation=lazy_evaluation) self.roi_scale = roi_scale def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] @@ -796,7 +796,7 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) self.select_fn = select_fn self.channel_indices = ensure_tuple(channel_indices) if channel_indices is not None else None self.margin = margin @@ -904,7 +904,7 @@ def __init__( self, spatial_size: Sequence[int] | int, num_samples: int = 1, weight_map: NdarrayOrTensor | None = None, lazy_evaluation: bool = False ): - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) self.spatial_size = ensure_tuple(spatial_size) self.num_samples = int(num_samples) self.weight_map = weight_map @@ -1034,7 +1034,7 @@ def __init__( allow_smaller: bool = False, lazy_evaluation: bool = False, ) -> None: - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) self.spatial_size = spatial_size self.label = label if pos < 0 or neg < 0: @@ -1218,7 +1218,7 @@ def __init__( warn: bool = True, lazy_evaluation: bool = False, ) -> None: - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) self.spatial_size = spatial_size self.ratios = ratios self.label = label @@ -1338,7 +1338,7 @@ def __init__( lazy_evaluation: bool = False, **pad_kwargs, ): - LazyTransform.__init__(lazy_evaluation) + LazyTransform.__init__(self, lazy_evaluation) self.padder = SpatialPad( spatial_size=spatial_size, method=method, mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs ) diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index ab4ce28941..350e13c7d8 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -47,7 +47,7 @@ SpatialPad, ) from monai.transforms.inverse import InvertibleTransform -from monai.transforms.traits import MultiSampleTrait +from monai.transforms.traits import MultiSampleTrait, LazyTrait from monai.transforms.transform import LazyTransform, MapTransform, Randomizable from monai.transforms.utils import is_positive from monai.utils import MAX_SEED, Method, PytorchPadMode, deprecated_arg_default, ensure_tuple_rep @@ -124,6 +124,7 @@ def __init__( padder: Pad, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -140,20 +141,31 @@ def __init__( allow_missing_keys: don't raise exception if key is missing. """ - super().__init__(keys, allow_missing_keys) + super().__init__(keys, allow_missing_keys, lazy_evaluation=lazy_evaluation) + if lazy_evaluation is True and not isinstance(padder, LazyTrait): + raise ValueError("'padder' must inherit LazyTrait if lazy_evaluation is True " + f"'padder' is of type({type(padder)})") self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - if isinstance(self.padder, LazyTransform): - self.padder.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # if isinstance(self.padder, LazyTransform): + # self.padder.lazy_evaluation = value - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + if lazy_evaluation_ is True and not isinstance(self.padder, LazyTrait): + raise ValueError("'self.padder' must inherit LazyTrait if lazy_evaluation is True " + f"'self.padder' is of type({type(self.padder)}") for key, m in self.key_iterator(d, self.mode): - d[key] = self.padder(d[key], mode=m) + if isinstance(self.padder, LazyTrait): + d[key] = self.padder(d[key], mode=m, lazy_evaluation=lazy_evaluation_) + else: + d[key] = self.padder(d[key], mode=m) + return d def inverse(self, data: Mapping[Hashable, MetaTensor]) -> dict[Hashable, MetaTensor]: @@ -177,6 +189,7 @@ def __init__( method: str = Method.SYMMETRIC, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, + lazy_evaluation: bool | None = None, **kwargs, ) -> None: """ @@ -202,8 +215,11 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - padder = SpatialPad(spatial_size, method, **kwargs) - super().__init__(keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + padder = SpatialPad(spatial_size, method, lazy_evaluation=lazy_evaluation_, **kwargs) + super().__init__( + keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation_ + ) class BorderPadd(Padd): @@ -220,6 +236,7 @@ def __init__( spatial_border: Sequence[int] | int, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, **kwargs, ) -> None: """ @@ -249,8 +266,11 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - padder = BorderPad(spatial_border=spatial_border, **kwargs) - super().__init__(keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + padder = BorderPad(spatial_border=spatial_border, lazy_evaluation=lazy_evaluation_, **kwargs) + super().__init__( + keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation_ + ) class DivisiblePadd(Padd): @@ -268,6 +288,7 @@ def __init__( mode: SequenceStr = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, **kwargs, ) -> None: """ @@ -293,8 +314,10 @@ def __init__( See also :py:class:`monai.transforms.SpatialPad` """ - padder = DivisiblePad(k=k, method=method, **kwargs) - super().__init__(keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + padder = DivisiblePad(k=k, method=method, lazy_evaluation=lazy_evaluation_, **kwargs) + super().__init__(keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys, + lazy_evaluation=lazy_evaluation_) class Cropd(MapTransform, InvertibleTransform, LazyTransform): @@ -311,20 +334,23 @@ class Cropd(MapTransform, InvertibleTransform, LazyTransform): backend = Crop.backend - def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False): - super().__init__(keys, allow_missing_keys) + def __init__( + self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy_evaluation: bool = False + ): + super().__init__(keys, allow_missing_keys, lazy_evaluation=lazy_evaluation) self.cropper = cropper - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - if isinstance(self.cropper, LazyTransform): - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # if isinstance(self.cropper, LazyTransform): + # self.cropper.lazy_evaluation = value - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): - d[key] = self.cropper(d[key]) # type: ignore + d[key] = self.cropper(d[key], lazy_evaluation=lazy_evaluation_) # type: ignore return d def inverse(self, data: Mapping[Hashable, MetaTensor]) -> dict[Hashable, MetaTensor]: @@ -348,8 +374,10 @@ class RandCropd(Cropd, Randomizable): backend = Crop.backend - def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False): - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + def __init__( + self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy_evaluation: bool = False + ): + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandCropd: super().set_random_state(seed, state) @@ -361,13 +389,19 @@ def randomize(self, img_size: Sequence[int]) -> None: if isinstance(self.cropper, Randomizable): self.cropper.randomize(img_size) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) # the first key must exist to execute random operations first_item = d[self.first_key(d)] self.randomize(first_item.peek_pending_shape() if isinstance(first_item, MetaTensor) else first_item.shape[1:]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + if lazy_evaluation_ is True and not isinstance(self.cropper, LazyTrait): + raise ValueError("'self.cropper' must inherit LazyTrait if lazy_evaluation is True " + f"'self.cropper' is of type({type(self.cropper)}") for key in self.key_iterator(d): kwargs = {"randomize": False} if isinstance(self.cropper, Randomizable) else {} + if isinstance(self.cropper, LazyTrait): + kwargs["lazy_evaluation"] = lazy_evaluation_ d[key] = self.cropper(d[key], **kwargs) # type: ignore return d @@ -396,6 +430,7 @@ def __init__( roi_end: Sequence[int] | None = None, roi_slices: Sequence[slice] | None = None, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: """ Args: @@ -411,8 +446,8 @@ def __init__( allow_missing_keys: don't raise exception if key is missing. """ - cropper = SpatialCrop(roi_center, roi_size, roi_start, roi_end, roi_slices) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + cropper = SpatialCrop(roi_center, roi_size, roi_start, roi_end, roi_slices, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) class CenterSpatialCropd(Cropd): @@ -433,9 +468,11 @@ class CenterSpatialCropd(Cropd): allow_missing_keys: don't raise exception if key is missing. """ - def __init__(self, keys: KeysCollection, roi_size: Sequence[int] | int, allow_missing_keys: bool = False) -> None: - cropper = CenterSpatialCrop(roi_size) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + def __init__( + self, keys: KeysCollection, roi_size: Sequence[int] | int, allow_missing_keys: bool = False, lazy_evaluation: bool = False + ) -> None: + cropper = CenterSpatialCrop(roi_size, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) class CenterScaleCropd(Cropd): @@ -453,10 +490,10 @@ class CenterScaleCropd(Cropd): """ def __init__( - self, keys: KeysCollection, roi_scale: Sequence[float] | float, allow_missing_keys: bool = False + self, keys: KeysCollection, roi_scale: Sequence[float] | float, allow_missing_keys: bool = False, lazy_evaluation: bool = False ) -> None: - cropper = CenterScaleCrop(roi_scale) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + cropper = CenterScaleCrop(roi_scale, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) class RandSpatialCropd(RandCropd): @@ -498,9 +535,10 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: - cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) class RandScaleCropd(RandCropd): @@ -537,9 +575,10 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, + lazy_evaluation: bool = False ) -> None: - cropper = RandScaleCrop(roi_scale, max_roi_scale, random_center, random_size) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + cropper = RandScaleCrop(roi_scale, max_roi_scale, random_center, random_size, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) class RandSpatialCropSamplesd(Randomizable, MapTransform, LazyTransform, MultiSampleTrait): @@ -590,19 +629,22 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - self.cropper = RandSpatialCropSamples(roi_size, num_samples, max_roi_size, random_center, random_size) + LazyTransform.__init__(self, lazy_evaluation) + self.cropper = RandSpatialCropSamples(roi_size, num_samples, max_roi_size, random_center, random_size, + lazy_evaluation=lazy_evaluation) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # self.cropper.lazy_evaluation = value def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: ret: list[dict[Hashable, torch.Tensor]] = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): @@ -611,9 +653,11 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, # for each key we reset the random state to ensure crops are the same self.randomize() + + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(dict(data)): self.cropper.set_random_state(seed=self.sub_seed) - for i, im in enumerate(self.cropper(data[key])): + for i, im in enumerate(self.cropper(data[key], lazy_evaluation=lazy_evaluation_)): ret[i][key] = im return ret @@ -644,6 +688,7 @@ def __init__( start_coord_key: str = "foreground_start_coord", end_coord_key: str = "foreground_end_coord", allow_missing_keys: bool = False, + lazy_evaluation: bool = False, **pad_kwargs, ) -> None: """ @@ -683,17 +728,18 @@ def __init__( margin=margin, allow_smaller=allow_smaller, k_divisible=k_divisible, + lazy_evaluation=lazy_evaluation, **pad_kwargs, ) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) self.mode = ensure_tuple_rep(mode, len(self.keys)) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # self.cropper.lazy_evaluation = value - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) self.cropper: CropForeground box_start, box_end = self.cropper.compute_bounding_box(img=d[self.source_key]) @@ -701,8 +747,11 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc d[self.start_coord_key] = box_start # type: ignore if self.end_coord_key is not None: d[self.end_coord_key] = box_end # type: ignore + + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key, m in self.key_iterator(d, self.mode): - d[key] = self.cropper.crop_pad(img=d[key], box_start=box_start, box_end=box_end, mode=m) + d[key] = self.cropper.crop_pad(img=d[key], box_start=box_start, box_end=box_end, mode=m, + lazy_evaluation=lazy_evaluation_) return d @@ -733,10 +782,12 @@ def __init__( spatial_size: Sequence[int] | int, num_samples: int = 1, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ): MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy_evaluation) self.w_key = w_key - self.cropper = RandWeightedCrop(spatial_size, num_samples) + self.cropper = RandWeightedCrop(spatial_size, num_samples, lazy_evaluation=lazy_evaluation) def set_random_state( self, seed: int | None = None, state: np.random.RandomState | None = None @@ -748,12 +799,12 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # self.cropper.lazy_evaluation = value - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: # output starts as empty list of dictionaries ret: list = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data @@ -762,8 +813,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, ret[i][key] = deepcopy(data[key]) self.randomize(weight_map=data[self.w_key]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(data): - for i, im in enumerate(self.cropper(data[key], randomize=False)): + for i, im in enumerate(self.cropper(data[key], randomize=False, lazy_evaluation=lazy_evaluation_)): ret[i][key] = im return ret @@ -836,8 +888,10 @@ def __init__( bg_indices_key: str | None = None, allow_smaller: bool = False, allow_missing_keys: bool = False, + lazy_evaluation: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy_evaluation) self.label_key = label_key self.image_key = image_key self.fg_indices_key = fg_indices_key @@ -849,6 +903,7 @@ def __init__( num_samples=num_samples, image_threshold=image_threshold, allow_smaller=allow_smaller, + lazy_evaluation=lazy_evaluation, ) def set_random_state( @@ -867,12 +922,12 @@ def randomize( ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # self.cropper.lazy_evaluation = value - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) fg_indices = d.pop(self.fg_indices_key, None) bg_indices = d.pop(self.bg_indices_key, None) @@ -886,8 +941,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, for key in set(d.keys()).difference(set(self.keys)): ret[i][key] = deepcopy(d[key]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): - for i, im in enumerate(self.cropper(d[key], randomize=False)): + for i, im in enumerate(self.cropper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_)): ret[i][key] = im return ret @@ -982,8 +1038,10 @@ def __init__( allow_smaller: bool = False, allow_missing_keys: bool = False, warn: bool = True, + lazy_evaluation: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy_evaluation) self.label_key = label_key self.image_key = image_key self.indices_key = indices_key @@ -995,6 +1053,7 @@ def __init__( image_threshold=image_threshold, allow_smaller=allow_smaller, warn=warn, + lazy_evaluation=lazy_evaluation ) def set_random_state( @@ -1009,12 +1068,12 @@ def randomize( ) -> None: self.cropper.randomize(label=label, indices=indices, image=image) - @LazyTransform.lazy_evaluation.setter # type: ignore - def lazy_evaluation(self, value: bool) -> None: - self._lazy_evaluation = value - self.cropper.lazy_evaluation = value + # @LazyTransform.lazy_evaluation.setter # type: ignore + # def lazy_evaluation(self, value: bool) -> None: + # self._lazy_evaluation = value + # self.cropper.lazy_evaluation = value - def __call__(self, data: Mapping[Hashable, Any]) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, Any], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) self.randomize(d.get(self.label_key), d.pop(self.indices_key, None), d.get(self.image_key)) # type: ignore @@ -1025,8 +1084,9 @@ def __call__(self, data: Mapping[Hashable, Any]) -> list[dict[Hashable, torch.Te for key in set(d.keys()).difference(set(self.keys)): ret[i][key] = deepcopy(d[key]) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation for key in self.key_iterator(d): - for i, im in enumerate(self.cropper(d[key], randomize=False)): + for i, im in enumerate(self.cropper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_)): ret[i][key] = im return ret @@ -1062,10 +1122,13 @@ def __init__( mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, method: str = Method.SYMMETRIC, + lazy_evaluation: bool = False, **pad_kwargs, ) -> None: - padcropper = ResizeWithPadOrCrop(spatial_size=spatial_size, method=method, **pad_kwargs) - super().__init__(keys, padder=padcropper, mode=mode, allow_missing_keys=allow_missing_keys) # type: ignore + padcropper = ResizeWithPadOrCrop(spatial_size=spatial_size, method=method, **pad_kwargs, lazy_evaluation=lazy_evaluation) + super().__init__( + keys, padder=padcropper, mode=mode, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation # type: ignore + ) class BoundingRectd(MapTransform): diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index ab1389fbbd..de261b37bb 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -276,7 +276,8 @@ class LazyTransform(Transform, LazyTrait): dictionary transforms to simplify implementation of new lazy transforms. """ - _lazy_evaluation: bool = False + def __init__(self, lazy_evaluation: bool = False): + self._lazy_evaluation = lazy_evaluation @property def lazy_evaluation(self): From 5df4261560b2c0d91b93a9efdac13f11c052817d Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 29 Mar 2023 23:12:43 +0100 Subject: [PATCH 009/175] Fix for push_transform; updating the lazy integration test Signed-off-by: Ben Murray --- monai/transforms/compose.py | 11 +++-------- monai/transforms/croppad/array.py | 14 ++++++++------ monai/transforms/croppad/dictionary.py | 3 ++- monai/transforms/inverse.py | 5 +++-- monai/transforms/lazy/functional.py | 2 +- monai/transforms/spatial/array.py | 26 +++++++++++++------------- monai/transforms/spatial/dictionary.py | 22 +++++++++------------- monai/transforms/transform.py | 7 ++++--- tests/test_integration_lazy_samples.py | 2 +- 9 files changed, 44 insertions(+), 48 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index cd2751c143..99f90c0abc 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -23,6 +23,7 @@ import monai import monai.transforms as mt from monai.apps.utils import get_logger +from monai.transforms.traits import LazyTrait from monai.transforms.inverse import InvertibleTransform # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) @@ -206,7 +207,7 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - lazy_evaluation: bool | None = None, + lazy_evaluation: str = LazyMode.OFF, overrides: dict | None = None, ) -> None: if transforms is None: @@ -215,15 +216,9 @@ def __init__( self.map_items = map_items self.unpack_items = unpack_items self.set_random_state(seed=get_seed()) - self.lazy_evaluation = lazy_evaluation self.overrides = overrides - # if self.lazy_evaluation is not None: - # for t in self.flatten().transforms: # TODO: test Compose of Compose/OneOf - # if isinstance(t, LazyTransform): - # t.lazy_evaluation = self.lazy_evaluation - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) for _transform in self.transforms: @@ -264,7 +259,7 @@ def __len__(self): """Return number of transformations.""" return len(self.flatten().transforms) - def __call__(self, input_): + def __call__(self, input_, lazy_evaluation: bool | None = None): for _transform in self.transforms: input_ = apply_transform(_transform, input_, self.map_items, self.unpack_items, lazy_evaluation=self.lazy_evaluation, overrides=self.overrides) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index cac1ca6c34..ae81e53bda 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -450,7 +450,7 @@ def __init__( roi_center=roi_center, roi_size=roi_size, roi_start=roi_start, roi_end=roi_end, roi_slices=roi_slices ) - def __call__(self, img: torch.Tensor, lazy_evaluation: bool = False) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -725,7 +725,7 @@ def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> li cropped = self.cropper(img, lazy_evaluation=lazy_evaluation_) if get_track_meta(): cropped.meta[Key.PATCH_INDEX] = i # type: ignore - self.push_transform(cropped, replace=True) # track as this class instead of RandSpatialCrop + self.push_transform(cropped, replace=True, lazy_evaluation=lazy_evaluation_) # track as this class instead of RandSpatialCrop ret.append(cropped) return ret @@ -958,7 +958,7 @@ def __call__( ret_: MetaTensor = cropped # type: ignore ret_.meta[Key.PATCH_INDEX] = i ret_.meta["crop_center"] = center - self.push_transform(ret_, replace=True) + self.push_transform(ret_, replace=True, lazy_evaluation=lazy_evaluation_) results.append(cropped) return results @@ -1132,7 +1132,7 @@ def __call__( ret_: MetaTensor = cropped # type: ignore ret_.meta[Key.PATCH_INDEX] = i ret_.meta["crop_center"] = center - self.push_transform(ret_, replace=True) + self.push_transform(ret_, replace=True, lazy_evaluation=lazy_evaluation_) results.append(cropped) return results @@ -1299,7 +1299,7 @@ def __call__( ret_: MetaTensor = cropped # type: ignore ret_.meta[Key.PATCH_INDEX] = i ret_.meta["crop_center"] = center - self.push_transform(ret_, replace=True) + self.push_transform(ret_, replace=True, lazy_evaluation=lazy_evaluation_) results.append(cropped) return results @@ -1375,7 +1375,8 @@ def __call__(self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: crop_info = ret_.applied_operations.pop() orig_size = crop_info.get(TraceKeys.ORIG_SIZE) self.push_transform( - ret_, orig_size=orig_size, extra_info={"pad_info": pad_info, "crop_info": crop_info} + ret_, orig_size=orig_size, extra_info={"pad_info": pad_info, "crop_info": crop_info}, + lazy_evaluation=lazy_evaluation_ ) else: pad_info = ret_.pending_operations.pop() @@ -1387,6 +1388,7 @@ def __call__(self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: sp_size=pad_info[LazyAttr.SHAPE], affine=crop_info[LazyAttr.AFFINE] @ pad_info[LazyAttr.AFFINE], extra_info={"pad_info": pad_info, "crop_info": crop_info}, + lazy_evaluation=lazy_evaluation_, ) return ret diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 350e13c7d8..367ce8fe25 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -141,7 +141,8 @@ def __init__( allow_missing_keys: don't raise exception if key is missing. """ - super().__init__(keys, allow_missing_keys, lazy_evaluation=lazy_evaluation) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy_evaluation) if lazy_evaluation is True and not isinstance(padder, LazyTrait): raise ValueError("'padder' must inherit LazyTrait if lazy_evaluation is True " f"'padder' is of type({type(padder)})") diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index d9bfe31009..1158ff0217 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -93,7 +93,7 @@ def get_transform_info(self) -> dict: self.__class__.__name__, id(self), self.tracing, - # self.lazy_evaluation if isinstance(self, LazyTransform) else False, + self.lazy_evaluation if isinstance(self, LazyTransform) else False, self._do_transform if hasattr(self, "_do_transform") else True, ) return dict(zip(self.transform_info_keys(), vals)) @@ -109,8 +109,9 @@ def push_transform(self, data, *args, **kwargs): set ``replace=True`` (default False) to rewrite the last transform infor in applied_operation/pending_operation based on ``self.get_transform_info()``. """ + lazy_eval = kwargs.get("lazy_evaluation", False) transform_info = self.get_transform_info() - lazy_eval = transform_info.get(TraceKeys.LAZY_EVALUATION, False) + # lazy_eval = transform_info.get(TraceKeys.LAZY_EVALUATION, False) do_transform = transform_info.get(TraceKeys.DO_TRANSFORM, True) kwargs = kwargs or {} replace = kwargs.pop("replace", False) # whether to rewrite the most recently pushed transform info diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 18ff6b265a..01ba17b0df 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -42,7 +42,7 @@ def execute_pending_transforms(data, overrides: dict = None): for k, v in d.items(): if isinstance(v, MetaTensor) and v.has_pending_operations: overrides_ = None if overrides is None else overrides[k] - d[k], _ = apply_transforms(d[k], overrides=overrides_) + d[k], _ = apply_transforms(v, overrides=overrides_) return d if isinstance(data, MetaTensor) and data.has_pending_operations: diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 8a953c24fa..6faeee8a6b 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -301,7 +301,7 @@ def __call__( # type: ignore padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, ) if not lazy_evaluation_: if isinstance(img, MetaTensor): @@ -516,7 +516,7 @@ def __call__( padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, ) if self.recompute_affine and isinstance(data_array, MetaTensor): if lazy_evaluation_: @@ -1206,14 +1206,14 @@ def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: b if randomize: self.randomize() + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation if self._do_transform: - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation xform = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) out = xform(img) else: out = convert_to_tensor(img, track_meta=get_track_meta()) - self.push_transform(out, replace=True) + self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1331,9 +1331,9 @@ def __call__( if randomize: self.randomize() + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation if self._do_transform: ndim = len(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation rotator = Rotate( angle=self.x if ndim == 2 else (self.x, self.y, self.z), keep_size=self.keep_size, @@ -1341,12 +1341,12 @@ def __call__( padding_mode=look_up_option(padding_mode or self.padding_mode, GridSamplePadMode), align_corners=self.align_corners if align_corners is None else align_corners, dtype=dtype or self.dtype or img.dtype, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, ) out = rotator(img) else: out = convert_to_tensor(img, track_meta=get_track_meta(), dtype=torch.float32) - self.push_transform(out, replace=True) + self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1400,7 +1400,7 @@ def __call__( lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation out = self.flipper(img, lazy_evaluation=lazy_evaluation_) if self._do_transform else img out = convert_to_tensor(out, track_meta=get_track_meta()) - self.push_transform(out, replace=True) + self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1454,13 +1454,13 @@ def __call__( if randomize: self.randomize(data=img) + lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation if self._do_transform: - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation self.flipper.spatial_axis = self._axis out = self.flipper(img, lazy_evaluation=lazy_evaluation_) else: out = convert_to_tensor(img, track_meta=get_track_meta()) - self.push_transform(out, replace=True) + self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1603,11 +1603,11 @@ def __call__( padding_mode=padding_mode or self.padding_mode, align_corners=self.align_corners if align_corners is None else align_corners, dtype=dtype or self.dtype, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, **self.kwargs, ) out = xform(img) - self.push_transform(out, replace=True) + self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) return out # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1857,7 +1857,7 @@ def __call__( scale_params=self.scale_params, device=self.device, dtype=self.dtype, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, ) if lazy_evaluation_: # return the affine only, don't construct the grid self.affine = affine_grid(spatial_size, grid)[1] # type: ignore diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 3e5df33805..4e8845b249 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -682,12 +682,8 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation rotator = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) for key in self.key_iterator(d): - if self._do_transform: - # no need to override lazy_evaluation here as we already set it on the initializer - d[key] = rotator(d[key]) - else: - convert_to_tensor(d[key], track_meta=get_track_meta()) - self.push_transform(d[key], replace=True) + d[key] = rotator(d[key]) if self._do_transform else convert_to_tensor(d[key], track_meta=get_track_meta()) + self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1073,7 +1069,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy_evaluation: bo else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) self._do_transform = do_resampling # TODO: unify self._do_transform and do_resampling - self.push_transform(d[key], replace=True) + self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: @@ -1494,7 +1490,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool d[key] = self.flipper(d[key], lazy_evaluation=lazy_evaluation_) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) - self.push_transform(d[key], replace=True) + self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1571,7 +1567,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool d[key] = self.flipper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) - self.push_transform(d[key], replace=True) + self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1657,7 +1653,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool ): d[key] = self.rotator( d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, ) return d @@ -1772,7 +1768,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool ) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) - self.push_transform(d[key], replace=True) + self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1981,11 +1977,11 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool align_corners=align_corners, dtype=dtype, randomize=False, - lazy_evaluation=lazy_evaluation_ + lazy_evaluation=lazy_evaluation_, ) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) - self.push_transform(d[key], replace=True) + self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index de261b37bb..5554158a70 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -48,7 +48,7 @@ def _apply_transform( transform: Callable[..., ReturnType], data: Any, unpack_parameters: bool = False, - lazy_evaluation: bool = LazyMode.OFF, + lazy_evaluation: str = LazyMode.OFF, overrides: dict = None, ) -> ReturnType: """ @@ -77,8 +77,9 @@ def _apply_transform( lazy_tx = isinstance(transform, LazyTrait) - if not lazy_tx or transform.lazy_evaluation is False: - # must evaluate outstanding pending transforms before we proceed + if lazy_tx is False or lazy_evaluation == LazyMode.OFF: + data = execute_pending_transforms(data, overrides) + elif lazy_evaluation is LazyMode.ENABLED and transform.lazy_evaluation is False: data = execute_pending_transforms(data, overrides) if isinstance(data, tuple) and unpack_parameters: diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 15bfcd2a58..9300d264d6 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -29,7 +29,7 @@ from tests.utils import HAS_CUPY, DistTestCase, SkipIfBeforePyTorchVersion, skip_if_quick -def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, None), num_workers=4, lazy=True): +def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, None), num_workers=4, lazy=LazyMode.ON): print(f"test case: {locals()}") images = sorted(glob(os.path.join(root_dir, "img*.nii.gz"))) segs = sorted(glob(os.path.join(root_dir, "seg*.nii.gz"))) From 84d65f76b903bf5a7b6a202d3dd9b417bac06a65 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 30 Mar 2023 10:00:43 +0100 Subject: [PATCH 010/175] removing issue.txt Signed-off-by: Ben Murray --- issue.txt | 65 ------------------------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 issue.txt diff --git a/issue.txt b/issue.txt deleted file mode 100644 index 3f1debcedd..0000000000 --- a/issue.txt +++ /dev/null @@ -1,65 +0,0 @@ -# Lazy Evaluation - -The lazy_evaluation flag on Compose has three potential states, but only two potential code-paths: - . True -> True - . False -> False - . None -> False - -In terms of the code paths: - . False disables all lazy resampling - . True enables all lazy resampling (which involves overriding what the user has set - -Design-wise, it has always made sense to me that the user should be able to set flags on transforms. -Doing so allows a given transform to run in a non-lazy fashion of the user desires without having to -use some other mechanism to force non-lazy reevaluation. - -As such, the intention with the design has always been that, everything else being equal, lazy -resampling will honor what is set on the transforms, rather than automatically overriding them. - -There are two ways I see this working: - . Compose has a lazy_evaluation mode that tells it to do what the transforms want to do - . Transforms have a lazy_evaluation mode that means I'll do whatever compose says - - -## The compose way - -Compose has three lazy_evaluation states: 'OFF', 'ENABLED', 'ON' - -OFF means "no lazy_evalution is allowed, even if a transform has lazy_evaluation set to True" -ENABLED means "if a lazy transform has lazy_evaluation set to True, evaluate it lazily -ON means "override any lazy transforms so that lazy evaluation happens - -Implication: compose can always overrule a transform setting and evaluate it lazily - - -## The transform way - -Transforms that can evalate lazily have three states: False, None, True - -False means "I must not be lazily evaluated" -None means "I'm happy to be evaluated lazily or not depending on the compose" -True means "I must be lazily evaluated" - -Implication: transforms can always overrule compose - - -## Hybrid - -Both systems exist, with compose getting the final say if 'ON' set. - - -## Selecting the best design - -I think the compose way is the best way to implement this. It makes sense that the user can set the policy -primarily through compose. The user doesn't want to touch the setting on the individual transforms, they -are free not to, and 'ENABLE' & 'ON' will do the same thing. If the user sets flags on the transforms, -'ENABLE' allows for fine-grained settings, and 'ON' will override the transform. - -I don't think it is necessarily good to allow transforms to overrule compose. It should be easy for the -user to completely switch lazy resampling on or off, they can do so in a single place in the code if compose -can overrule transforms. - -The hybrid system may give more fine grained semantics, i.e. "transform trumps compose trumps transform" but -this is quite a cognitive load for the user, too much IMO. - - From 7ec442f962de5f44574a5600bf1ae9fff7d29e63 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 31 Mar 2023 13:35:56 +0100 Subject: [PATCH 011/175] . Renaming apply_transform to apply_pending as it is more semantically meaningful . Adding ApplyPending and ApplyPendingd transforms Signed-off-by: Ben Murray --- monai/transforms/__init__.py | 2 +- monai/transforms/compose.py | 2 +- monai/transforms/lazy/__init__.py | 2 +- monai/transforms/lazy/array.py | 25 +++++++++++++++++++++++ monai/transforms/lazy/dictionary.py | 24 ++++++++++++++++++++++ monai/transforms/lazy/functional.py | 9 ++++---- tests/croppers.py | 6 +++--- tests/lazy_transforms_utils.py | 4 ++-- tests/padders.py | 6 +++--- tests/test_affine.py | 6 +++--- tests/test_apply.py | 8 ++++---- tests/test_crop_foreground.py | 6 +++--- tests/test_crop_foregroundd.py | 4 ++-- tests/test_rand_crop_by_label_classes.py | 4 ++-- tests/test_rand_crop_by_label_classesd.py | 4 ++-- tests/test_rand_crop_by_pos_neg_label.py | 4 ++-- tests/test_rand_crop_by_pos_neg_labeld.py | 6 +++--- tests/test_rand_spatial_crop.py | 4 ++-- tests/test_rand_spatial_crop_samples.py | 4 ++-- tests/test_rand_spatial_crop_samplesd.py | 6 +++--- tests/test_rand_spatial_cropd.py | 4 ++-- tests/test_rand_weighted_crop.py | 4 ++-- tests/test_rand_weighted_cropd.py | 4 ++-- tests/test_resize_with_pad_or_crop.py | 4 ++-- tests/test_resize_with_pad_or_cropd.py | 4 ++-- tests/test_rotate90.py | 6 +++--- tests/test_spatial_combine_transforms.py | 4 ++-- tests/test_zoom.py | 4 ++-- 28 files changed, 110 insertions(+), 60 deletions(-) create mode 100644 monai/transforms/lazy/array.py create mode 100644 monai/transforms/lazy/dictionary.py diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index 11790e639b..adbefda2ac 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -230,7 +230,7 @@ from .inverse_batch_transform import BatchInverseTransform, Decollated, DecollateD, DecollateDict from .io.array import SUPPORTED_READERS, LoadImage, SaveImage from .io.dictionary import LoadImaged, LoadImageD, LoadImageDict, SaveImaged, SaveImageD, SaveImageDict -from .lazy.functional import apply_transforms +from .lazy.functional import apply_pending from .lazy.utils import combine_transforms, resample from .meta_utility.dictionary import ( FromMetaTensord, diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 99f90c0abc..c006fd4567 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -80,7 +80,7 @@ def evaluate_with_overrides( overrides = (overrides or {}).copy() if isinstance(data, monai.data.MetaTensor): if data.has_pending_operations and ((isinstance(upcoming, (mt.Identityd, mt.Identity))) or upcoming is None): - data, _ = mt.apply_transforms(data, None, overrides=overrides) + data, _ = mt.apply_pending(data, None, overrides=overrides) if verbose: next_name = "final output" if upcoming is None else f"'{upcoming.__class__.__name__}'" logger.info(f"Evaluated - '{override_keys}' - up-to-date for - {next_name}") diff --git a/monai/transforms/lazy/__init__.py b/monai/transforms/lazy/__init__.py index 02349dd0f2..e6304549f4 100644 --- a/monai/transforms/lazy/__init__.py +++ b/monai/transforms/lazy/__init__.py @@ -11,5 +11,5 @@ from __future__ import annotations -from .functional import apply_transforms +from .functional import apply_pending from .utils import combine_transforms, resample diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py new file mode 100644 index 0000000000..a568632351 --- /dev/null +++ b/monai/transforms/lazy/array.py @@ -0,0 +1,25 @@ +from monai.data.meta_tensor import MetaTensor +from monai.transforms.inverse import InvertibleTransform +from monai.transforms.lazy.functional import apply_pending + + +class ApplyPending(InvertibleTransform): + """ + Apply wraps the apply_pending method and can function as a Transform in an array-based pipeline. + """ + + def __init__(self): + super().__init__() + + def __call__(self, data, *args, **kwargs): + if isinstance(data, dict): + rd = dict(data) + for k, v in data.items(): + if isinstance(v, MetaTensor): + rd[k] = apply_pending(v, *args, **kwargs) + return rd + + return apply_pending(data, *args, **kwargs) + + def inverse(self, data): + return self(data) diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py new file mode 100644 index 0000000000..4b0c94aef6 --- /dev/null +++ b/monai/transforms/lazy/dictionary.py @@ -0,0 +1,24 @@ +from monai.transforms.inverse import InvertibleTransform + + +class ApplyPendingd(InvertibleTransform): + """ + Apply wraps the apply method and can function as a Transform in either array or dictionary + mode. + """ + + def __init__(self, keys): + super().__init__() + self.keys = keys + + def __call__(self, data, **kwargs): + rd = dict(data) + for k in self.keys: + rd[k] = apply_pending(rd[k], **kwargs) + return rd + + # return apply_transforms(data, *args, **kwargs) + + def inverse(self, data): + return self(data) + \ No newline at end of file diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 01ba17b0df..bab2d6fa04 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -25,7 +25,7 @@ ) from monai.utils import LazyAttr, look_up_option -__all__ = ["apply_transforms"] +__all__ = ["apply_pending"] __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} @@ -42,19 +42,20 @@ def execute_pending_transforms(data, overrides: dict = None): for k, v in d.items(): if isinstance(v, MetaTensor) and v.has_pending_operations: overrides_ = None if overrides is None else overrides[k] - d[k], _ = apply_transforms(v, overrides=overrides_) + d[k], _ = apply_pending(v, overrides=overrides_) return d if isinstance(data, MetaTensor) and data.has_pending_operations: - data, _ = apply_transforms(data, overrides=overrides) + data, _ = apply_pending(data, overrides=overrides) return data -def apply_transforms( +def apply_pending( data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None, + **kwargs ): """ This method applies pending transforms to `data` tensors. diff --git a/tests/croppers.py b/tests/croppers.py index 6b5933458e..acc21307d4 100644 --- a/tests/croppers.py +++ b/tests/croppers.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import Randomizable -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from monai.transforms.transform import MapTransform from tests.utils import TEST_NDARRAYS_ALL, assert_allclose @@ -124,7 +124,7 @@ def crop_test_pending_ops(self, input_param, input_shape, align_corners=False): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms(pending_result, mode="nearest", align_corners=align_corners)[0] + result = apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -159,7 +159,7 @@ def crop_test_combine_ops(self, funcs, input_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - result = apply_transforms(pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/lazy_transforms_utils.py b/tests/lazy_transforms_utils.py index 012b39dceb..3751306b16 100644 --- a/tests/lazy_transforms_utils.py +++ b/tests/lazy_transforms_utils.py @@ -13,7 +13,7 @@ from monai.data import set_track_meta from monai.transforms import Randomizable -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import assert_allclose apply_transforms_kwargs = ("pending", "mode", "padding_mode", "dtype", "align_corners") @@ -73,5 +73,5 @@ def test_resampler_lazy( if not skip_shape_check: assert_allclose(lazy_out.peek_pending_shape(), non_lazy_out.shape[1:4]) apply_param = get_apply_param(init_param, call_param) - lazy_out = apply_transforms(lazy_out, **apply_param)[0] + lazy_out = apply_pending(lazy_out, **apply_param)[0] assert_allclose(lazy_out, non_lazy_out, rtol=rtol, atol=atol) diff --git a/tests/padders.py b/tests/padders.py index ded427e5a1..b0c2f23d0a 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import Compose -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from monai.transforms.transform import MapTransform from monai.utils.enums import NumpyPadMode, PytorchPadMode from tests.utils import TEST_NDARRAYS_ALL, assert_allclose @@ -134,7 +134,7 @@ def pad_test_pending_ops(self, input_param, input_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - result = apply_transforms(pending_result, mode="nearest", padding_mode=mode[1], align_corners=False)[0] + result = apply_pending(pending_result, mode="nearest", padding_mode=mode[1], align_corners=False)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -163,6 +163,6 @@ def pad_test_combine_ops(self, funcs, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - result = apply_transforms(pending_result, mode="nearest", padding_mode=mode[1], align_corners=False)[0] + result = apply_pending(pending_result, mode="nearest", padding_mode=mode[1], align_corners=False)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_affine.py b/tests/test_affine.py index e8f7f33b17..85eb1def6c 100644 --- a/tests/test_affine.py +++ b/tests/test_affine.py @@ -20,7 +20,7 @@ from monai.data import MetaTensor, set_track_meta from monai.transforms import Affine, Resize -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from monai.utils import optional_import from tests.lazy_transforms_utils import test_resampler_lazy from tests.utils import TEST_NDARRAYS_ALL, assert_allclose, test_local_inversion @@ -210,14 +210,14 @@ def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=sp_size) xform.lazy_evaluation = True out = xform(im) - out = apply_transforms(out, padding_mode="border", align_corners=ac)[0] + out = apply_pending(out, padding_mode="border", align_corners=ac)[0] return out def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=sp_size) xform.lazy_evaluation = True out = xform(im) - out = apply_transforms(out, mode=1, padding_mode="nearest", align_corners=ac)[0] + out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] return out def method_2(im, ac): diff --git a/tests/test_apply.py b/tests/test_apply.py index cf74721267..4784d46413 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -16,7 +16,7 @@ import numpy as np import torch -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from monai.transforms.utils import create_rotate from monai.utils import LazyAttr, convert_to_tensor from tests.utils import get_arange_img @@ -40,20 +40,20 @@ def single_2d_transform_cases(): class TestApply(unittest.TestCase): def _test_apply_impl(self, tensor, pending_transforms, expected_shape): - result = apply_transforms(tensor, pending_transforms) + result = apply_pending(tensor, pending_transforms) self.assertListEqual(result[1], pending_transforms) self.assertEqual(result[0].shape, expected_shape) def _test_apply_metatensor_impl(self, tensor, pending_transforms, expected_shape, pending_as_parameter): tensor_ = convert_to_tensor(tensor, track_meta=True) if pending_as_parameter: - result, transforms = apply_transforms(tensor_, pending_transforms) + result, transforms = apply_pending(tensor_, pending_transforms) else: for p in pending_transforms: tensor_.push_pending_operation(p) if not isinstance(p, dict): return - result, transforms = apply_transforms(tensor_) + result, transforms = apply_pending(tensor_) self.assertEqual(result.shape, expected_shape) SINGLE_TRANSFORM_CASES = single_2d_transform_cases() diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index e70d5f268e..9a123622a2 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -19,7 +19,7 @@ from monai.config import USE_COMPILED from monai.data.meta_tensor import MetaTensor from monai.transforms import CropForeground -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TEST_COORDS, TESTS, TEST_LAZY_ERROR = [], [], [] @@ -132,7 +132,7 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms(pending_result, mode="nearest", align_corners=align_corners)[0] + result = apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -144,7 +144,7 @@ def test_lazy_error(self, input_param, image, _expected_data, align_corners): # lazy crop_fn.lazy_evaluation = True pending_result = crop_fn(image) - return apply_transforms(pending_result, mode="nearest", align_corners=align_corners)[0] + return apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] if __name__ == "__main__": diff --git a/tests/test_crop_foregroundd.py b/tests/test_crop_foregroundd.py index d2604ef9cf..9e810319f7 100644 --- a/tests/test_crop_foregroundd.py +++ b/tests/test_crop_foregroundd.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import CropForegroundd -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TEST_POSITION, TESTS = [], [] @@ -195,7 +195,7 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms(pending_result, mode="nearest", align_corners=align_corners)[0] + result = apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_crop_by_label_classes.py b/tests/test_rand_crop_by_label_classes.py index 6723dfc4c6..c16e0c89f4 100644 --- a/tests/test_rand_crop_by_label_classes.py +++ b/tests/test_rand_crop_by_label_classes.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import ClassesToIndices, RandCropByLabelClasses -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TESTS_INDICES, TESTS_SHAPE = [], [] @@ -161,7 +161,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_transforms(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_crop_by_label_classesd.py b/tests/test_rand_crop_by_label_classesd.py index 77221e67cd..c883e18fd2 100644 --- a/tests/test_rand_crop_by_label_classesd.py +++ b/tests/test_rand_crop_by_label_classesd.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import ClassesToIndicesd, RandCropByLabelClassesd -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TESTS = [] @@ -149,7 +149,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result = apply_transforms(_pending_result["img"], mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result["img"], mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected[i]["img"], rtol=1e-5) diff --git a/tests/test_rand_crop_by_pos_neg_label.py b/tests/test_rand_crop_by_pos_neg_label.py index e1c4cdff58..b8371c1411 100644 --- a/tests/test_rand_crop_by_pos_neg_label.py +++ b/tests/test_rand_crop_by_pos_neg_label.py @@ -19,7 +19,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import RandCropByPosNegLabel -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TESTS = [ @@ -143,7 +143,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_transforms(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_crop_by_pos_neg_labeld.py b/tests/test_rand_crop_by_pos_neg_labeld.py index 11b7960617..4132714aa9 100644 --- a/tests/test_rand_crop_by_pos_neg_labeld.py +++ b/tests/test_rand_crop_by_pos_neg_labeld.py @@ -19,7 +19,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import RandCropByPosNegLabeld -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TESTS = [ @@ -160,8 +160,8 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): assert_allclose(_pending_result["image"].peek_pending_affine(), expected[i]["image"].affine) assert_allclose(_pending_result["image"].peek_pending_shape(), expected[i]["image"].shape[1:]) # only support nearest - result_image = apply_transforms(_pending_result["image"], mode="nearest", align_corners=False)[0] - result_extra = apply_transforms(_pending_result["extra"], mode="nearest", align_corners=False)[0] + result_image = apply_pending(_pending_result["image"], mode="nearest", align_corners=False)[0] + result_extra = apply_pending(_pending_result["extra"], mode="nearest", align_corners=False)[0] # compare assert_allclose(result_image, expected[i]["image"], rtol=1e-5) assert_allclose(result_extra, expected[i]["extra"], rtol=1e-5) diff --git a/tests/test_rand_spatial_crop.py b/tests/test_rand_spatial_crop.py index a0d56bcaf3..a4eb22c549 100644 --- a/tests/test_rand_spatial_crop.py +++ b/tests/test_rand_spatial_crop.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import RandScaleCrop, RandSpatialCrop -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.croppers import CropTest from tests.utils import TEST_NDARRAYS_ALL, assert_allclose @@ -90,7 +90,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms(pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_spatial_crop_samples.py b/tests/test_rand_spatial_crop_samples.py index 69d2e5af5d..c385fc9c47 100644 --- a/tests/test_rand_spatial_crop_samples.py +++ b/tests/test_rand_spatial_crop_samples.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import RandSpatialCropSamples -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.croppers import CropTest from tests.utils import TEST_NDARRAYS_ALL, assert_allclose @@ -119,7 +119,7 @@ def test_pending_ops(self, input_param, input_shape, _expected_shape, _expected_ assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_transforms(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_spatial_crop_samplesd.py b/tests/test_rand_spatial_crop_samplesd.py index fc6e6c8c43..7b629cba72 100644 --- a/tests/test_rand_spatial_crop_samplesd.py +++ b/tests/test_rand_spatial_crop_samplesd.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import Compose, DivisiblePadd, RandSpatialCropSamplesd -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose TEST_CASE_1 = [ @@ -129,8 +129,8 @@ def test_pending_ops(self, input_param, input_data, _expected_shape, _expected_l assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result_img = apply_transforms(_pending_result["img"], mode="nearest", align_corners=False)[0] - result_seg = apply_transforms(_pending_result["seg"], mode="nearest", align_corners=False)[0] + result_img = apply_pending(_pending_result["img"], mode="nearest", align_corners=False)[0] + result_seg = apply_pending(_pending_result["seg"], mode="nearest", align_corners=False)[0] # compare assert_allclose(result_img, expected[i]["img"], rtol=1e-5) assert_allclose(result_seg, expected[i]["seg"], rtol=1e-5) diff --git a/tests/test_rand_spatial_cropd.py b/tests/test_rand_spatial_cropd.py index 5114a45159..a45e3f3bbe 100644 --- a/tests/test_rand_spatial_cropd.py +++ b/tests/test_rand_spatial_cropd.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import RandScaleCropd, RandSpatialCropd -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.croppers import CropTest from tests.utils import TEST_NDARRAYS_ALL, assert_allclose @@ -95,7 +95,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms(pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_weighted_crop.py b/tests/test_rand_weighted_crop.py index e279f29f68..9b96656d5e 100644 --- a/tests/test_rand_weighted_crop.py +++ b/tests/test_rand_weighted_crop.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms.croppad.array import RandWeightedCrop -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.croppers import CropTest from tests.utils import TEST_NDARRAYS_ALL, NumpyImageTestCase2D, NumpyImageTestCase3D, assert_allclose @@ -185,7 +185,7 @@ def test_pending_ops(self, _, input_param, img, weight, expected_shape, expected assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_transforms(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_weighted_cropd.py b/tests/test_rand_weighted_cropd.py index 51e1b15c2c..159e07c61f 100644 --- a/tests/test_rand_weighted_cropd.py +++ b/tests/test_rand_weighted_cropd.py @@ -18,7 +18,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms.croppad.dictionary import RandWeightedCropd -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, NumpyImageTestCase2D, NumpyImageTestCase3D, assert_allclose @@ -173,7 +173,7 @@ def test_pending_ops(self, _, input_param, input_data, expected_shape, expected_ assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result = apply_transforms(_pending_result["img"], mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result["img"], mode="nearest", align_corners=False)[0] # compare assert_allclose(result, expected[i]["img"], rtol=1e-5) diff --git a/tests/test_resize_with_pad_or_crop.py b/tests/test_resize_with_pad_or_crop.py index 5529ec698a..2d543686d9 100644 --- a/tests/test_resize_with_pad_or_crop.py +++ b/tests/test_resize_with_pad_or_crop.py @@ -19,7 +19,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import ResizeWithPadOrCrop -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import TEST_NDARRAYS_ALL, assert_allclose, pytorch_after TEST_CASES = [ @@ -85,7 +85,7 @@ def test_pending_ops(self, input_param, input_shape, _expected_data, align_corne assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms( + result = apply_pending( pending_result, mode="nearest", padding_mode=TESTS_PENDING_MODE[input_param["mode"]], diff --git a/tests/test_resize_with_pad_or_cropd.py b/tests/test_resize_with_pad_or_cropd.py index a71652375b..f44bbe7686 100644 --- a/tests/test_resize_with_pad_or_cropd.py +++ b/tests/test_resize_with_pad_or_cropd.py @@ -20,7 +20,7 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms import ResizeWithPadOrCropd -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.test_resize_with_pad_or_crop import TESTS_PENDING_MODE from tests.utils import TEST_NDARRAYS_ALL, assert_allclose, pytorch_after @@ -80,7 +80,7 @@ def test_pending_ops(self, input_param, input_data, _expected_data): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_transforms( + result = apply_pending( pending_result, mode="nearest", padding_mode=TESTS_PENDING_MODE[input_param["mode"]], align_corners=True )[0] # compare diff --git a/tests/test_rotate90.py b/tests/test_rotate90.py index fd54e7639f..01e93870fa 100644 --- a/tests/test_rotate90.py +++ b/tests/test_rotate90.py @@ -18,7 +18,7 @@ from monai.data import MetaTensor, set_track_meta from monai.transforms import Affine, Rotate90 -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from monai.utils import optional_import from tests.lazy_transforms_utils import test_resampler_lazy from tests.utils import ( @@ -179,14 +179,14 @@ def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) xform.lazy_evaluation = True out = xform(im) - out = apply_transforms(out, padding_mode="border", align_corners=ac)[0] + out = apply_pending(out, padding_mode="border", align_corners=ac)[0] return out def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) xform.lazy_evaluation = True out = xform(im) - out = apply_transforms(out, mode=1, padding_mode="nearest", align_corners=ac)[0] + out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] return out def method_2(im, ac): diff --git a/tests/test_spatial_combine_transforms.py b/tests/test_spatial_combine_transforms.py index 74c03fc4ff..6c554d469a 100644 --- a/tests/test_spatial_combine_transforms.py +++ b/tests/test_spatial_combine_transforms.py @@ -20,7 +20,7 @@ import monai.transforms as mt from monai.data import create_test_image_2d, create_test_image_3d from monai.data.meta_tensor import MetaTensor -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from monai.transforms.transform import MapTransform from monai.utils import set_determinism from tests.lazy_transforms_utils import get_apply_param @@ -175,7 +175,7 @@ def test_combine_transforms(self, input_shape, funcs): init_param = funcs[-1][1] call_param = {} apply_param = get_apply_param(init_param, call_param) - result = apply_transforms(pending_result, **apply_param)[0] + result = apply_pending(pending_result, **apply_param)[0] match_ratio = np.sum(np.isclose(result.array, expected.array, atol=5e-1)) / np.prod(result.shape) self.assertGreater(match_ratio, 0.5) # at least half of the images are very close diff --git a/tests/test_zoom.py b/tests/test_zoom.py index cb25abc29f..6aa59c449c 100644 --- a/tests/test_zoom.py +++ b/tests/test_zoom.py @@ -20,7 +20,7 @@ from monai.data import MetaTensor, set_track_meta from monai.transforms import Zoom -from monai.transforms.lazy.functional import apply_transforms +from monai.transforms.lazy.functional import apply_pending from tests.utils import ( DEFAULT_TEST_AFFINE, TEST_NDARRAYS_ALL, @@ -57,7 +57,7 @@ def test_pending_ops(self, zoom, mode, align_corners=False, keep_size=False): self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) - result = apply_transforms(pending_result, mode="bilinear", dtype=np.float64, align_corners=align_corners)[0] + result = apply_pending(pending_result, mode="bilinear", dtype=np.float64, align_corners=align_corners)[0] # compare match_ratio = np.sum(np.isclose(result, expected)) / np.prod(result.shape) self.assertGreater(match_ratio, 0.95) From 8f6277609c093e325e7fba741ffc5c034f9d0b4d Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 31 Mar 2023 14:01:29 +0100 Subject: [PATCH 012/175] Tweaked array ApplyPending and ApplyPendingd for array and dict data respectively Signed-off-by: Ben Murray --- monai/transforms/lazy/array.py | 7 ------- monai/transforms/lazy/dictionary.py | 9 ++++++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index a568632351..954141ea52 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -12,13 +12,6 @@ def __init__(self): super().__init__() def __call__(self, data, *args, **kwargs): - if isinstance(data, dict): - rd = dict(data) - for k, v in data.items(): - if isinstance(v, MetaTensor): - rd[k] = apply_pending(v, *args, **kwargs) - return rd - return apply_pending(data, *args, **kwargs) def inverse(self, data): diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 4b0c94aef6..35e424d4f7 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -12,13 +12,16 @@ def __init__(self, keys): self.keys = keys def __call__(self, data, **kwargs): + if not isinstance(data, dict): + raise ValueError("'data' must be of type dict but is '{type(data)}'") + rd = dict(data) for k in self.keys: rd[k] = apply_pending(rd[k], **kwargs) return rd - # return apply_transforms(data, *args, **kwargs) - def inverse(self, data): + if not isinstance(data, dict): + raise ValueError("'data' must be of type dict but is '{type(data)}'") + return self(data) - \ No newline at end of file From 2492223c7dbfc14d1210c157457f274c636b17d6 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 31 Mar 2023 14:21:18 +0100 Subject: [PATCH 013/175] Added missing license text. Filter for metatensors in ApplyPending / ApplyPendingd classes Signed-off-by: Ben Murray --- monai/transforms/lazy/array.py | 18 +++++++++++++++++- monai/transforms/lazy/dictionary.py | 16 +++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index 954141ea52..7598016653 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -1,3 +1,15 @@ +# Copyright (c) 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. + + from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform from monai.transforms.lazy.functional import apply_pending @@ -12,7 +24,11 @@ def __init__(self): super().__init__() def __call__(self, data, *args, **kwargs): - return apply_pending(data, *args, **kwargs) + + if isinstance(data, MetaTensor): + return apply_pending(data, *args, **kwargs) + + return data def inverse(self, data): return self(data) diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 35e424d4f7..ea23b08696 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -1,3 +1,16 @@ +# Copyright (c) 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. + + +from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform @@ -17,7 +30,8 @@ def __call__(self, data, **kwargs): rd = dict(data) for k in self.keys: - rd[k] = apply_pending(rd[k], **kwargs) + if isinstance(rd[k], MetaTensor): + rd[k] = apply_pending(rd[k], **kwargs) return rd def inverse(self, data): From 006432651987ea2a0c7af21f0b6a70dbcddc900d Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 31 Mar 2023 14:25:51 +0100 Subject: [PATCH 014/175] Missing import for apply_pending Signed-off-by: Ben Murray --- monai/transforms/lazy/dictionary.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index ea23b08696..ee8f4404bc 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -12,6 +12,8 @@ from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform +from monai.transforms.lazy.functional import apply_pending + class ApplyPendingd(InvertibleTransform): From 05eb3f97bdf58d4d842e2cef5311b436807f3a3e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:56:19 +0000 Subject: [PATCH 015/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/data/dataset.py | 1 - monai/transforms/compose.py | 1 - monai/transforms/lazy/array.py | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/monai/data/dataset.py b/monai/data/dataset.py index 89b5d0f60c..675c452c28 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -44,7 +44,6 @@ convert_to_contiguous, reset_ops_id, ) -from monai.transforms.lazy.functional import execute_pending_transforms from monai.utils import MAX_SEED, get_seed, look_up_option, min_version, optional_import from monai.utils.misc import first diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index dade43cd56..2692e9d8fb 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -24,7 +24,6 @@ import monai import monai.transforms as mt from monai.apps.utils import get_logger -from monai.transforms.traits import LazyTrait from monai.transforms.inverse import InvertibleTransform from monai.transforms.traits import ThreadUnsafe diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index 7598016653..7b059f0048 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -27,7 +27,7 @@ def __call__(self, data, *args, **kwargs): if isinstance(data, MetaTensor): return apply_pending(data, *args, **kwargs) - + return data def inverse(self, data): From 788cd748593835c27d5a17b526c3c9f6addbac27 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 14 Apr 2023 18:07:11 +0100 Subject: [PATCH 016/175] Fixing syntax error created by merge of dev to PR Signed-off-by: Ben Murray --- monai/data/dataset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monai/data/dataset.py b/monai/data/dataset.py index 675c452c28..7df19d88d3 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -352,8 +352,7 @@ def _post_transform(self, item_transformed): ) if first_random is not None: item_transformed = self.transform(item_transformed, start=first_random) - - return item_transformed + return item_transformed def _cachecheck(self, item_transformed): """ From 4bd22f8141472331b99b22f53039ccfdeb8e4d84 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 14 Apr 2023 18:41:38 +0100 Subject: [PATCH 017/175] Fixing issues with pad_func post merge Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 2 +- monai/transforms/croppad/functional.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 31a4d7af9b..48bde1e31a 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -154,7 +154,7 @@ def __call__( # type: ignore[override] img_t = convert_to_tensor(data=img, track_meta=get_track_meta()) lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - return pad_func(img_t, to_pad_, mode_, lazy_evaluation_, self.get_transform_info(), kwargs_) + return pad_func(img_t, to_pad_, self.get_transform_info(), mode_, lazy_evaluation_, **kwargs_) def inverse(self, data: MetaTensor) -> MetaTensor: transform = self.pop_transform(data) diff --git a/monai/transforms/croppad/functional.py b/monai/transforms/croppad/functional.py index 9730237fc0..dbb2fb77f9 100644 --- a/monai/transforms/croppad/functional.py +++ b/monai/transforms/croppad/functional.py @@ -157,7 +157,12 @@ def crop_or_pad_nd(img: torch.Tensor, translation_mat, spatial_size: tuple[int, def pad_func( - img: torch.Tensor, to_pad: tuple[tuple[int, int]], mode: str = PytorchPadMode.CONSTANT, lazy_evaluation: bool, transform_info: dict, **kwargs + img: torch.Tensor, + to_pad: tuple[tuple[int, int]], + transform_info: dict, + mode: str = PytorchPadMode.CONSTANT, + lazy_evaluation: bool = False, + **kwargs ) -> torch.Tensor: """ Functional implementation of padding a MetaTensor. This function operates eagerly or lazily according From 1e0510c50f31b4b25775c071e3efd5709fda8a03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 17:42:51 +0000 Subject: [PATCH 018/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/croppad/functional.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/transforms/croppad/functional.py b/monai/transforms/croppad/functional.py index dbb2fb77f9..7dfd9e9a61 100644 --- a/monai/transforms/croppad/functional.py +++ b/monai/transforms/croppad/functional.py @@ -29,7 +29,6 @@ from monai.transforms.utils import convert_pad_mode, create_translate from monai.utils import ( PytorchPadMode, - TraceKeys, convert_to_dst_type, convert_to_numpy, convert_to_tensor, From c36b69f01a807f1f739e4cd6de9bb16e49a27646 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 14 Apr 2023 18:57:56 +0100 Subject: [PATCH 019/175] Fixing issues in crop while running unit tests Signed-off-by: Ben Murray --- monai/transforms/compose.py | 11 +++-------- monai/transforms/croppad/array.py | 1 + monai/transforms/croppad/dictionary.py | 3 ++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 2692e9d8fb..117b6256aa 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -24,6 +24,7 @@ import monai import monai.transforms as mt from monai.apps.utils import get_logger +from monai.config import NdarrayOrTensor from monai.transforms.inverse import InvertibleTransform from monai.transforms.traits import ThreadUnsafe @@ -413,8 +414,6 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy_evaluation: lazy_evaluation=self.lazy_evaluation, # type: ignore overrides=self.overrides, threading=threading, - log_stats=self.log_stats, - verbose=self.verbose ) def inverse(self, data): @@ -530,9 +529,7 @@ def __call__(self, data, start=0, end=None, threading=False): unpack_items=self.unpack_items, lazy_evaluation=self.lazy_evaluation, # type: ignore overrides=self.overrides, - threading=threading, - log_stats=self.log_stats, - verbose=self.verbose + threading=threading ) # if the data is a mapping (dictionary), append the OneOf transform to the end @@ -627,9 +624,7 @@ def __call__(self, input_, start=0, end=None, threading=False): map_items=self.map_items, unpack_items=self.unpack_items, lazy_evaluation=self.lazy_evaluation, - threading=threading, - log_stats=self.log_stats, - verbose=self.verbose + threading=threading ) # if the data is a mapping (dictionary), append the RandomOrder transform to the end diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 48bde1e31a..3bf408ecd9 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -694,6 +694,7 @@ def __init__( random_size: bool = True, lazy_evaluation: bool = False, ) -> None: + LazyTransform.__init__(self, lazy_evaluation) if num_samples < 1: raise ValueError(f"num_samples must be positive, got {num_samples}.") self.num_samples = num_samples diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index de8b412826..5c8e6c4a6e 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -332,7 +332,8 @@ class Cropd(MapTransform, InvertibleTransform, LazyTransform): def __init__( self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy_evaluation: bool = False ): - super().__init__(keys, allow_missing_keys, lazy_evaluation=lazy_evaluation) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy_evaluation) self.cropper = cropper def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: From 3c69776f9120ae45a8f7fcedc4fc8d439ea672e3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 14 Apr 2023 21:23:31 +0100 Subject: [PATCH 020/175] Fixing issues with OneOf/SomeOf/RandomOrder after dev merge Signed-off-by: Ben Murray --- monai/transforms/compose.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 117b6256aa..491c2e664e 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -466,15 +466,10 @@ def __init__( weights: Sequence[float] | float | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool = False, lazy_evaluation: bool | None = None, overrides: dict | None = None, - override_keys: Sequence[str] | None = None, - verbose: bool = False, ) -> None: - super().__init__( - transforms, map_items, unpack_items, log_stats, lazy_evaluation, overrides, override_keys, verbose - ) + super().__init__(transforms, map_items, unpack_items, lazy_evaluation, overrides) if len(self.transforms) == 0: weights = [] elif weights is None or isinstance(weights, float): @@ -600,15 +595,10 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool = False, lazy_evaluation: bool | None = None, overrides: dict | None = None, - override_keys: Sequence[str] | None = None, - verbose: bool = False, ) -> None: - super().__init__( - transforms, map_items, unpack_items, log_stats, lazy_evaluation, overrides, override_keys, verbose - ) + super().__init__(transforms, map_items, unpack_items, lazy_evaluation, overrides) def __call__(self, input_, start=0, end=None, threading=False): if len(self.transforms) == 0: @@ -659,7 +649,7 @@ def inverse(self, data): for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): data = apply_transform( - self.transforms[o].inverse, data, self.map_items, self.unpack_items, self.log_stats + self.transforms[o].inverse, data, self.map_items, self.unpack_items ) return data @@ -773,8 +763,6 @@ def __call__(self, data, start=0, end=None, threading=False): lazy_evaluation=self.lazy_evaluation, overrides=self.overrides, threading=threading, - log_stats=self.log_stats, - verbose=self.verbose ) if isinstance(data, monai.data.MetaTensor): self.push_transform(data, extra_info={"applied_order": applied_order}) @@ -809,8 +797,6 @@ def inverse(self, data): for o in reversed(applied_order): transform = self.transforms[o] if isinstance(transform, InvertibleTransform): - data = apply_transform( - self.transforms[o].inverse, data, self.map_items, self.unpack_items, self.log_stats - ) + data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) return data From 6b94aa15bdfb0073eac21c2a1c2828a0f18a45cb Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 14 Apr 2023 21:33:08 +0100 Subject: [PATCH 021/175] More croppad fixes post dev merge Signed-off-by: Ben Murray --- monai/transforms/croppad/dictionary.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 5c8e6c4a6e..66908e873e 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -184,7 +184,7 @@ def __init__( method: str = Method.SYMMETRIC, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, - lazy_evaluation: bool | None = None, + lazy_evaluation: bool = False, **kwargs, ) -> None: """ @@ -210,10 +210,10 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - padder = SpatialPad(spatial_size, method, lazy_evaluation=lazy_evaluation_, **kwargs) - super().__init__( - keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation_ + LazyTransform.__init__(self, lazy_evaluation) + padder = SpatialPad(spatial_size, method, lazy_evaluation=lazy_evaluation, **kwargs) + Padd.__init__( + self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys ) @@ -261,10 +261,10 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - padder = BorderPad(spatial_border=spatial_border, lazy_evaluation=lazy_evaluation_, **kwargs) - super().__init__( - keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation_ + LazyTransform.__init__(self, lazy_evaluation) + padder = BorderPad(spatial_border=spatial_border, lazy_evaluation=lazy_evaluation, **kwargs) + Padd.__init__( + self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys ) @@ -309,10 +309,9 @@ def __init__( See also :py:class:`monai.transforms.SpatialPad` """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - padder = DivisiblePad(k=k, method=method, lazy_evaluation=lazy_evaluation_, **kwargs) - super().__init__(keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys, - lazy_evaluation=lazy_evaluation_) + LazyTransform.__init__(self, lazy_evaluation) + padder = DivisiblePad(k=k, method=method, lazy_evaluation=lazy_evaluation, **kwargs) + Padd.__init__(self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) class Cropd(MapTransform, InvertibleTransform, LazyTransform): From bf1c01a39b2340a2f80633e1c47a22edc59e6f13 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 14 Apr 2023 21:37:21 +0100 Subject: [PATCH 022/175] Fixing test_nvtx_decorator after def merge Signed-off-by: Ben Murray --- tests/test_nvtx_decorator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_nvtx_decorator.py b/tests/test_nvtx_decorator.py index 1e7bea17d4..be09ff9aa3 100644 --- a/tests/test_nvtx_decorator.py +++ b/tests/test_nvtx_decorator.py @@ -66,7 +66,7 @@ [ ToNumpy(), Flip(), - OneOf([RandAdjustContrast(prob=0.0), RandFlip(prob=1.0)], weights=[0, 1], log_stats=True), + OneOf([RandAdjustContrast(prob=0.0), RandFlip(prob=1.0)], weights=[0, 1]), ToTensor(), ] ), From 19745f95cdbb3361aef4e2c080815bd8fec57eb2 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sun, 23 Apr 2023 15:46:59 +0100 Subject: [PATCH 023/175] Removed use of **kwargs parameter in tests as it isn't supported by apply_pending. Fixing various tests by moving to use of 'overrides' on apply_pending Signed-off-by: Ben Murray --- monai/transforms/inverse.py | 3 ++- monai/transforms/spatial/dictionary.py | 4 +++- monai/transforms/spatial/functional.py | 2 +- tests/lazy_transforms_utils.py | 2 +- tests/padders.py | 3 ++- tests/test_spatial_pad.py | 24 ++++++++++++------------ tests/test_zoom.py | 3 ++- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 1158ff0217..1e9b3d6c4b 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -23,6 +23,7 @@ from monai.data.meta_obj import MetaObj, get_track_meta from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd +from monai.transforms.traits import LazyTrait from monai.transforms.transform import LazyTransform, Transform from monai.utils import LazyAttr, MetaKeys, TraceKeys, convert_to_dst_type, convert_to_numpy, convert_to_tensor @@ -93,7 +94,7 @@ def get_transform_info(self) -> dict: self.__class__.__name__, id(self), self.tracing, - self.lazy_evaluation if isinstance(self, LazyTransform) else False, + self.lazy_evaluation if isinstance(self, LazyTrait) else False, self._do_transform if hasattr(self, "_do_transform") else True, ) return dict(zip(self.transform_info_keys(), vals)) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index f822b8ed6e..7fb0eb6e2b 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -1831,7 +1831,9 @@ def __init__( lazy_evaluation: bool = False, **kwargs, ) -> None: - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy_evaluation) + self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) diff --git a/monai/transforms/spatial/functional.py b/monai/transforms/spatial/functional.py index 7177b9f308..73e98f161e 100644 --- a/monai/transforms/spatial/functional.py +++ b/monai/transforms/spatial/functional.py @@ -474,7 +474,7 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, lazy_evaluation=lazy_evaluation, ) out = _maybe_new_metatensor(img) - if transform_info.get(TraceKeys.LAZY_EVALUATION, False): + if lazy_evaluation: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info img_t = out.to(dtype) zoomed: NdarrayOrTensor = torch.nn.functional.interpolate( diff --git a/tests/lazy_transforms_utils.py b/tests/lazy_transforms_utils.py index 3751306b16..cf64079c0f 100644 --- a/tests/lazy_transforms_utils.py +++ b/tests/lazy_transforms_utils.py @@ -73,5 +73,5 @@ def test_resampler_lazy( if not skip_shape_check: assert_allclose(lazy_out.peek_pending_shape(), non_lazy_out.shape[1:4]) apply_param = get_apply_param(init_param, call_param) - lazy_out = apply_pending(lazy_out, **apply_param)[0] + lazy_out = apply_pending(lazy_out, overrides=apply_param)[0] assert_allclose(lazy_out, non_lazy_out, rtol=rtol, atol=atol) diff --git a/tests/padders.py b/tests/padders.py index b0c2f23d0a..3415d68fa0 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -134,7 +134,8 @@ def pad_test_pending_ops(self, input_param, input_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - result = apply_pending(pending_result, mode="nearest", padding_mode=mode[1], align_corners=False)[0] + overrides = {'mode': "nearest", 'padding_mode': mode[1], 'align_corners': False} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_spatial_pad.py b/tests/test_spatial_pad.py index 978059760e..dbf30593c1 100644 --- a/tests/test_spatial_pad.py +++ b/tests/test_spatial_pad.py @@ -35,18 +35,18 @@ class TestSpatialPad(PadTest): Padder = SpatialPad - @parameterized.expand(TESTS) - def test_pad(self, input_param, input_shape, expected_shape): - self.pad_test(input_param, input_shape, expected_shape) - - def test_pad_kwargs(self): - kwargs = {"spatial_size": [15, 8], "method": "end", "mode": "constant"} - unchanged_slices = [slice(None), slice(None, 8), slice(None, 4)] - self.pad_test_kwargs(unchanged_slices, **kwargs) - - @parameterized.expand(TESTS) - def test_pending_ops(self, input_param, input_shape, _): - self.pad_test_pending_ops(input_param, input_shape) + # @parameterized.expand(TESTS) + # def test_pad(self, input_param, input_shape, expected_shape): + # self.pad_test(input_param, input_shape, expected_shape) + # + # def test_pad_kwargs(self): + # kwargs = {"spatial_size": [15, 8], "method": "end", "mode": "constant"} + # unchanged_slices = [slice(None), slice(None, 8), slice(None, 4)] + # self.pad_test_kwargs(unchanged_slices, **kwargs) + # + # @parameterized.expand(TESTS) + # def test_pending_ops(self, input_param, input_shape, _): + # self.pad_test_pending_ops(input_param, input_shape) @parameterized.expand(TESTS_COMBINE) def test_combine_ops(self, funcs, input_shape, expected_shape): diff --git a/tests/test_zoom.py b/tests/test_zoom.py index 6aa59c449c..0f2eca888e 100644 --- a/tests/test_zoom.py +++ b/tests/test_zoom.py @@ -57,7 +57,8 @@ def test_pending_ops(self, zoom, mode, align_corners=False, keep_size=False): self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) - result = apply_pending(pending_result, mode="bilinear", dtype=np.float64, align_corners=align_corners)[0] + overrides = {'mode': "bilinear", 'dtype': np.float64, 'align_corners': align_corners} + result = apply_pending(pending_result, overrides=overrides)[0] # compare match_ratio = np.sum(np.isclose(result, expected)) / np.prod(result.shape) self.assertGreater(match_ratio, 0.95) From b219f13d4e5b1ecb27b371bdfe03737bf11ad4bd Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sun, 23 Apr 2023 16:31:50 +0100 Subject: [PATCH 024/175] Setting use of lazy_evaluation to False for Rand2D/Rand3DElastic Signed-off-by: Ben Murray --- monai/transforms/spatial/array.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 1c59b8c65d..020478e002 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -2627,6 +2627,7 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, + lazy_evaluation=False ) self.resampler = Resample(device=device) @@ -2794,6 +2795,7 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, + lazy_evaluation=False ) self.resampler = Resample(device=device) From 6af8f24acbd940a9d776d335d39e17523f6922ad Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sun, 23 Apr 2023 18:06:54 +0100 Subject: [PATCH 025/175] Fixed test_spatial_pad Signed-off-by: Ben Murray --- monai/transforms/spatial/dictionary.py | 1 + tests/padders.py | 3 ++- tests/test_spatial_pad.py | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 7fb0eb6e2b..acb9d8c661 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -1006,6 +1006,7 @@ def __init__( """ MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy_evaluation) self.rand_affine = RandAffine( prob=1.0, # because probability handled in this class rotate_range=rotate_range, diff --git a/tests/padders.py b/tests/padders.py index 3415d68fa0..419831f30c 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -164,6 +164,7 @@ def pad_test_combine_ops(self, funcs, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - result = apply_pending(pending_result, mode="nearest", padding_mode=mode[1], align_corners=False)[0] + overrides = {'mode': "nearest", 'padding_mode': mode[1], 'align_corners': False} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_spatial_pad.py b/tests/test_spatial_pad.py index dbf30593c1..978059760e 100644 --- a/tests/test_spatial_pad.py +++ b/tests/test_spatial_pad.py @@ -35,18 +35,18 @@ class TestSpatialPad(PadTest): Padder = SpatialPad - # @parameterized.expand(TESTS) - # def test_pad(self, input_param, input_shape, expected_shape): - # self.pad_test(input_param, input_shape, expected_shape) - # - # def test_pad_kwargs(self): - # kwargs = {"spatial_size": [15, 8], "method": "end", "mode": "constant"} - # unchanged_slices = [slice(None), slice(None, 8), slice(None, 4)] - # self.pad_test_kwargs(unchanged_slices, **kwargs) - # - # @parameterized.expand(TESTS) - # def test_pending_ops(self, input_param, input_shape, _): - # self.pad_test_pending_ops(input_param, input_shape) + @parameterized.expand(TESTS) + def test_pad(self, input_param, input_shape, expected_shape): + self.pad_test(input_param, input_shape, expected_shape) + + def test_pad_kwargs(self): + kwargs = {"spatial_size": [15, 8], "method": "end", "mode": "constant"} + unchanged_slices = [slice(None), slice(None, 8), slice(None, 4)] + self.pad_test_kwargs(unchanged_slices, **kwargs) + + @parameterized.expand(TESTS) + def test_pending_ops(self, input_param, input_shape, _): + self.pad_test_pending_ops(input_param, input_shape) @parameterized.expand(TESTS_COMBINE) def test_combine_ops(self, funcs, input_shape, expected_shape): From 173c1615708ce94581ae26be01809a40d24ca53d Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sun, 23 Apr 2023 18:49:47 +0100 Subject: [PATCH 026/175] Renaming lazy_evaluation to lazy Signed-off-by: Ben Murray --- monai/transforms/compose.py | 70 ++--- monai/transforms/croppad/array.py | 180 ++++++------ monai/transforms/croppad/dictionary.py | 156 +++++----- monai/transforms/croppad/functional.py | 20 +- monai/transforms/inverse.py | 22 +- monai/transforms/lazy/utils.py | 4 +- monai/transforms/spatial/array.py | 330 +++++++++++----------- monai/transforms/spatial/dictionary.py | 300 ++++++++++---------- monai/transforms/spatial/functional.py | 88 +++--- monai/transforms/traits.py | 10 +- monai/transforms/transform.py | 38 +-- monai/utils/enums.py | 4 +- tests/croppers.py | 4 +- tests/lazy_transforms_utils.py | 2 +- tests/padders.py | 4 +- tests/test_affine.py | 12 +- tests/test_crop_foreground.py | 4 +- tests/test_crop_foregroundd.py | 2 +- tests/test_integration_lazy_samples.py | 2 +- tests/test_rand_affined.py | 2 +- tests/test_rand_axis_flip.py | 2 +- tests/test_rand_axis_flipd.py | 2 +- tests/test_rand_crop_by_label_classes.py | 2 +- tests/test_rand_crop_by_label_classesd.py | 2 +- tests/test_rand_crop_by_pos_neg_label.py | 2 +- tests/test_rand_crop_by_pos_neg_labeld.py | 2 +- tests/test_rand_flipd.py | 2 +- tests/test_rand_rotate.py | 4 +- tests/test_rand_rotate90.py | 8 +- tests/test_rand_rotate90d.py | 8 +- tests/test_rand_spatial_crop.py | 2 +- tests/test_rand_spatial_crop_samples.py | 2 +- tests/test_rand_spatial_crop_samplesd.py | 2 +- tests/test_rand_spatial_cropd.py | 2 +- tests/test_rand_weighted_crop.py | 2 +- tests/test_rand_weighted_cropd.py | 2 +- tests/test_rand_zoomd.py | 2 +- tests/test_resize_with_pad_or_crop.py | 2 +- tests/test_resize_with_pad_or_cropd.py | 2 +- tests/test_rotate90.py | 20 +- tests/test_rotate90d.py | 8 +- tests/test_spatial_combine_transforms.py | 2 +- tests/test_zoom.py | 2 +- tests/test_zoomd.py | 2 +- 44 files changed, 673 insertions(+), 667 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 491c2e664e..9d1f5b26a2 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -49,7 +49,7 @@ def evaluate_with_overrides( data, upcoming, - lazy_evaluation: LazyMode = LazyMode.OFF, + lazy: LazyMode = LazyMode.OFF, overrides: dict | None = None, override_keys: Sequence[str] | None = None, verbose: bool = False, @@ -62,7 +62,7 @@ def evaluate_with_overrides( Currently, the conditions for evaluation are: - - ``lazy_evaluation`` is ``True``, AND + - ``lazy`` is ``True``, AND - the data is a ``MetaTensor`` and has pending operations, AND - the upcoming transform is an instance of ``Identity`` or ``IdentityD`` or ``None``. @@ -71,13 +71,13 @@ def evaluate_with_overrides( Args: data: data to be evaluated. upcoming: the upcoming transform. - lazy_evaluation: whether to evaluate the pending operations. + lazy: whether to evaluate the pending operations. override: keyword arguments to apply transforms. override_keys: to which the override arguments are used when apply transforms. verbose: whether to print debugging info when evaluate MetaTensor with pending operations. """ - if not lazy_evaluation: + if not lazy: return data # eager evaluation overrides = (overrides or {}).copy() if isinstance(data, monai.data.MetaTensor): @@ -107,12 +107,12 @@ def evaluate_with_overrides( for k in data: if k in keys_to_override: dict_for_key = dict_overrides[override_keys.index(k)] - data[k] = evaluate_with_overrides(data[k], upcoming, lazy_evaluation, dict_for_key, k, verbose) + data[k] = evaluate_with_overrides(data[k], upcoming, lazy, dict_for_key, k, verbose) else: - data[k] = evaluate_with_overrides(data[k], upcoming, lazy_evaluation, None, k, verbose) + data[k] = evaluate_with_overrides(data[k], upcoming, lazy, None, k, verbose) if isinstance(data, (list, tuple)): - return [evaluate_with_overrides(v, upcoming, lazy_evaluation, overrides, override_keys, verbose) for v in data] + return [evaluate_with_overrides(v, upcoming, lazy, overrides, override_keys, verbose) for v in data] return data @@ -123,7 +123,7 @@ def execute_compose( unpack_items: bool = False, start: int = 0, end: int | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, overrides: dict | None = None, threading: bool = False, log_stats: bool = False, @@ -145,15 +145,15 @@ def execute_compose( start: the index of the first transform to be executed. If not set, this defaults to 0 end: the index after the last transform to be exectued. If set, the transform at index-1 is the last transform that is executed. If this is not set, it defaults to len(transforms) - lazy_evaluation: whether to enable lazy evaluation for lazy transforms. If False, transforms will be + lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy_evaluation - is True. If lazy_evaluation is False they are ignored. + to that transform before it is executed. Note that overrides are currently only applied when lazy + is True. If lazy is False they are ignored. currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. @@ -164,7 +164,7 @@ def execute_compose( log_stats: whether to log the detailed information of data and applied transform when error happened, for NumPy array and PyTorch Tensor, log the data shape and value range, for other metadata, log the values directly. default to `False`. - verbose: whether to print debugging info when lazy_evaluation=True. + verbose: whether to print debugging info when lazy=True. Returns: A tensorlike, sequence of tensorlikes or dict of tensorlists containing the result of running @@ -186,7 +186,7 @@ def execute_compose( if threading: _transform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform data = apply_transform( - _transform, data, map_items, unpack_items, lazy_evaluation=lazy_evaluation, overrides=overrides + _transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides ) data = execute_pending_transforms(data, overrides) return data @@ -297,15 +297,15 @@ class Compose(Randomizable, InvertibleTransform): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - lazy_evaluation: whether to enable lazy evaluation for lazy transforms. If False, transforms will be + lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy_evaluation - is True. If lazy_evaluation is False they are ignored. + to that transform before it is executed. Note that overrides are currently only applied when lazy + is True. If lazy is False they are ignored. currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. @@ -316,7 +316,7 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - lazy_evaluation: str = LazyMode.OFF, + lazy: str = LazyMode.OFF, overrides: dict | None = None, ) -> None: if transforms is None: @@ -325,7 +325,7 @@ def __init__( self.map_items = map_items self.unpack_items = unpack_items self.set_random_state(seed=get_seed()) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy self.overrides = overrides def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: @@ -403,7 +403,7 @@ def __len__(self): """Return number of transformations.""" return len(self.flatten().transforms) - def __call__(self, input_, start=0, end=None, threading=False, lazy_evaluation: bool | None = None): + def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): return execute_compose( input_, self.transforms, @@ -411,7 +411,7 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy_evaluation: end=end, map_items=self.map_items, unpack_items=self.unpack_items, - lazy_evaluation=self.lazy_evaluation, # type: ignore + lazy=self.lazy, # type: ignore overrides=self.overrides, threading=threading, ) @@ -443,21 +443,21 @@ class OneOf(Compose): log_stats: whether to log the detailed information of data and applied transform when error happened, for NumPy array and PyTorch Tensor, log the data shape and value range, for other metadata, log the values directly. default to `False`. - lazy_evaluation: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will + lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be carried out on a transform by transform basis. A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy_evaluation - is True. If lazy_evaluation is False they are ignored. + to that transform before it is executed. Note that overrides are currently only applied when lazy + is True. If lazy is False they are ignored. currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. override_keys: this optional parameter specifies the keys to which ``overrides`` are to be applied. If ``overrides`` is set, ``override_keys`` must also be set. - verbose: whether to print debugging info when lazy_evaluation=True. + verbose: whether to print debugging info when lazy=True. """ def __init__( @@ -466,10 +466,10 @@ def __init__( weights: Sequence[float] | float | None = None, map_items: bool = True, unpack_items: bool = False, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, overrides: dict | None = None, ) -> None: - super().__init__(transforms, map_items, unpack_items, lazy_evaluation, overrides) + super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: weights = [] elif weights is None or isinstance(weights, float): @@ -522,7 +522,7 @@ def __call__(self, data, start=0, end=None, threading=False): end=end, map_items=self.map_items, unpack_items=self.unpack_items, - lazy_evaluation=self.lazy_evaluation, # type: ignore + lazy=self.lazy, # type: ignore overrides=self.overrides, threading=threading ) @@ -573,21 +573,21 @@ class RandomOrder(Compose): log_stats: whether to log the detailed information of data and applied transform when error happened, for NumPy array and PyTorch Tensor, log the data shape and value range, for other metadata, log the values directly. default to `False`. - lazy_evaluation: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will + lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be carried out on a transform by transform basis. A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy_evaluation - is True. If lazy_evaluation is False they are ignored. + to that transform before it is executed. Note that overrides are currently only applied when lazy + is True. If lazy is False they are ignored. currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. override_keys: this optional parameter specifies the keys to which ``overrides`` are to be applied. If ``overrides`` is set, ``override_keys`` must also be set. - verbose: whether to print debugging info when lazy_evaluation=True. + verbose: whether to print debugging info when lazy=True. """ def __init__( @@ -595,10 +595,10 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, overrides: dict | None = None, ) -> None: - super().__init__(transforms, map_items, unpack_items, lazy_evaluation, overrides) + super().__init__(transforms, map_items, unpack_items, lazy, overrides) def __call__(self, input_, start=0, end=None, threading=False): if len(self.transforms) == 0: @@ -613,7 +613,7 @@ def __call__(self, input_, start=0, end=None, threading=False): end=end, map_items=self.map_items, unpack_items=self.unpack_items, - lazy_evaluation=self.lazy_evaluation, + lazy=self.lazy, threading=threading ) @@ -760,7 +760,7 @@ def __call__(self, data, start=0, end=None, threading=False): end=end, map_items=self.map_items, unpack_items=self.unpack_items, - lazy_evaluation=self.lazy_evaluation, + lazy=self.lazy, overrides=self.overrides, threading=threading, ) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 3bf408ecd9..42f768868d 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -107,9 +107,9 @@ class Pad(InvertibleTransform, LazyTransform): def __init__( self, to_pad: tuple[tuple[int, int]] | None = None, mode: str = PytorchPadMode.CONSTANT, - lazy_evaluation: bool = False, **kwargs + lazy: bool = False, **kwargs ) -> None: - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.to_pad = to_pad self.mode = mode self.kwargs = kwargs @@ -127,7 +127,7 @@ def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, in def __call__( # type: ignore[override] self, img: torch.Tensor, to_pad: tuple[tuple[int, int]] | None = None, mode: str | None = None, - lazy_evaluation: bool | None = None, **kwargs + lazy: bool | None = None, **kwargs ) -> torch.Tensor: """ Args: @@ -153,8 +153,8 @@ def __call__( # type: ignore[override] kwargs_.update(kwargs) img_t = convert_to_tensor(data=img, track_meta=get_track_meta()) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - return pad_func(img_t, to_pad_, self.get_transform_info(), mode_, lazy_evaluation_, **kwargs_) + lazy_ = self.lazy if lazy is None else lazy + return pad_func(img_t, to_pad_, self.get_transform_info(), mode_, lazy_, **kwargs_) def inverse(self, data: MetaTensor) -> MetaTensor: transform = self.pop_transform(data) @@ -198,12 +198,12 @@ def __init__( spatial_size: Sequence[int] | int | tuple[tuple[int, ...] | int, ...], method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: self.spatial_size = spatial_size self.method: Method = look_up_option(method, Method) - super().__init__(mode=mode, lazy_evaluation=lazy_evaluation, **kwargs) + super().__init__(mode=mode, lazy=lazy, **kwargs) def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, int]]: """ @@ -252,10 +252,10 @@ class BorderPad(Pad): def __init__( self, spatial_border: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, - lazy_evaluation: bool = False, **kwargs + lazy: bool = False, **kwargs ) -> None: self.spatial_border = spatial_border - super().__init__(mode=mode, lazy_evaluation=lazy_evaluation, **kwargs) + super().__init__(mode=mode, lazy=lazy, **kwargs) def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, int]]: spatial_border = ensure_tuple(self.spatial_border) @@ -288,7 +288,7 @@ class DivisiblePad(Pad): def __init__( self, k: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, - lazy_evaluation: bool = False, **kwargs + lazy: bool = False, **kwargs ) -> None: """ Args: @@ -310,7 +310,7 @@ def __init__( """ self.k = k self.method: Method = Method(method) - super().__init__(mode=mode, lazy_evaluation=lazy_evaluation, **kwargs) + super().__init__(mode=mode, lazy=lazy, **kwargs) def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, int]]: new_size = compute_divisible_spatial_size(spatial_shape=spatial_shape, k=self.k) @@ -323,14 +323,14 @@ class Crop(InvertibleTransform, LazyTransform): Perform crop operations on the input image. Args: - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ backend = [TransformBackends.TORCH] - def __init__(self, lazy_evaluation: bool = False): - LazyTransform.__init__(self, lazy_evaluation) + def __init__(self, lazy: bool = False): + LazyTransform.__init__(self, lazy) @staticmethod def compute_slices( @@ -385,7 +385,7 @@ def compute_slices( [slice(int(s), int(e)) for s, e in zip(roi_start_t.tolist(), roi_end_t.tolist())] ) - def __call__(self, img: torch.Tensor, slices: tuple[slice, ...], lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, slices: tuple[slice, ...], lazy: bool | None = None) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -399,8 +399,8 @@ def __call__(self, img: torch.Tensor, slices: tuple[slice, ...], lazy_evaluation slices_ = list([slice(None)] + slices_[:sd]) img_t: MetaTensor = convert_to_tensor(data=img, track_meta=get_track_meta()) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - return crop_func(img_t, tuple(slices_), lazy_evaluation_, self.get_transform_info()) + lazy_ = self.lazy if lazy is None else lazy + return crop_func(img_t, tuple(slices_), lazy_, self.get_transform_info()) def inverse(self, img: MetaTensor) -> MetaTensor: transform = self.pop_transform(img) @@ -433,7 +433,7 @@ def __init__( roi_start: Sequence[int] | NdarrayOrTensor | None = None, roi_end: Sequence[int] | NdarrayOrTensor | None = None, roi_slices: Sequence[slice] | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -445,19 +445,19 @@ def __init__( use the end coordinate of image. roi_slices: list of slices for each of the spatial dimensions. """ - super().__init__(lazy_evaluation) + super().__init__(lazy) self.slices = self.compute_slices( roi_center=roi_center, roi_size=roi_size, roi_start=roi_start, roi_end=roi_end, roi_slices=roi_slices ) - def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - return super().__call__(img=img, slices=ensure_tuple(self.slices), lazy_evaluation=lazy_evaluation_) + lazy_ = self.lazy if lazy is None else lazy + return super().__call__(img=img, slices=ensure_tuple(self.slices), lazy=lazy_) class CenterSpatialCrop(Crop): @@ -475,8 +475,8 @@ class CenterSpatialCrop(Crop): the spatial size of output data will be [32, 40, 40]. """ - def __init__(self, roi_size: Sequence[int] | int, lazy_evaluation: bool = False) -> None: - super().__init__(lazy_evaluation) + def __init__(self, roi_size: Sequence[int] | int, lazy: bool = False) -> None: + super().__init__(lazy) self.roi_size = roi_size def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: ignore[override] @@ -484,17 +484,17 @@ def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: roi_center = [i // 2 for i in spatial_size] return super().compute_slices(roi_center=roi_center, roi_size=roi_size) - def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return super().__call__( img=img, slices=self.compute_slices(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]), - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) @@ -508,17 +508,17 @@ class CenterScaleCrop(Crop): """ - def __init__(self, roi_scale: Sequence[float] | float, lazy_evaluation: bool = False): - super().__init__(lazy_evaluation=lazy_evaluation) + def __init__(self, roi_scale: Sequence[float] | float, lazy: bool = False): + super().__init__(lazy=lazy) self.roi_scale = roi_scale - def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore[override] + def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: # type: ignore[override] img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] ndim = len(img_size) roi_size = [ceil(r * s) for r, s in zip(ensure_tuple_rep(self.roi_scale, ndim), img_size)] - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - cropper = CenterSpatialCrop(roi_size=roi_size, lazy_evaluation=lazy_evaluation_) - return super().__call__(img=img, slices=cropper.compute_slices(img_size), lazy_evaluation=lazy_evaluation_) + lazy_ = self.lazy if lazy is None else lazy + cropper = CenterSpatialCrop(roi_size=roi_size, lazy=lazy_) + return super().__call__(img=img, slices=cropper.compute_slices(img_size), lazy=lazy_) class RandSpatialCrop(Randomizable, Crop): @@ -552,9 +552,9 @@ def __init__( max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: - super().__init__(lazy_evaluation) + super().__init__(lazy) self.roi_size = roi_size self.max_roi_size = max_roi_size self.random_center = random_center @@ -573,7 +573,7 @@ def randomize(self, img_size: Sequence[int]) -> None: valid_size = get_valid_patch_size(img_size, self._size) self._slices = get_random_patch(img_size, valid_size, self.R) - def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: # type: ignore """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -584,11 +584,11 @@ def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: b self.randomize(img_size) if self._size is None: raise RuntimeError("self._size not specified.") - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy if self.random_center: - return super().__call__(img=img, slices=self._slices, lazy_evaluation=lazy_evaluation_) - cropper = CenterSpatialCrop(self._size, lazy_evaluation=lazy_evaluation_) - return super().__call__(img=img, slices=cropper.compute_slices(img_size), lazy_evaluation=lazy_evaluation_) + return super().__call__(img=img, slices=self._slices, lazy=lazy_) + cropper = CenterSpatialCrop(self._size, lazy=lazy_) + return super().__call__(img=img, slices=cropper.compute_slices(img_size), lazy=lazy_) class RandScaleCrop(RandSpatialCrop): @@ -619,11 +619,11 @@ def __init__( max_roi_scale: Sequence[float] | float | None = None, random_center: bool = True, random_size: bool = True, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: super().__init__( roi_size=-1, max_roi_size=None, random_center=random_center, random_size=random_size, - lazy_evaluation=lazy_evaluation + lazy=lazy ) self.roi_scale = roi_scale self.max_roi_scale = max_roi_scale @@ -640,15 +640,15 @@ def randomize(self, img_size: Sequence[int]) -> None: self.get_max_roi_size(img_size) super().randomize(img_size) - def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: bool | None = None) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: # type: ignore """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. """ self.get_max_roi_size(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - return super().__call__(img=img, randomize=randomize, lazy_evaluation=lazy_evaluation_) + lazy_ = self.lazy if lazy is None else lazy + return super().__call__(img=img, randomize=randomize, lazy=lazy_) class RandSpatialCropSamples(Randomizable, TraceableTransform, LazyTransform, MultiSampleTrait): @@ -692,13 +692,13 @@ def __init__( max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) if num_samples < 1: raise ValueError(f"num_samples must be positive, got {num_samples}.") self.num_samples = num_samples - self.cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size, lazy_evaluation) + self.cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size, lazy) def set_random_state( self, seed: int | None = None, state: np.random.RandomState | None = None @@ -710,18 +710,18 @@ def set_random_state( def randomize(self, data: Any | None = None) -> None: pass - def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> list[torch.Tensor]: + def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> list[torch.Tensor]: """ Apply the transform to `img`, assuming `img` is channel-first and cropping doesn't change the channel dim. """ ret = [] - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for i in range(self.num_samples): - cropped = self.cropper(img, lazy_evaluation=lazy_evaluation_) + cropped = self.cropper(img, lazy=lazy_) if get_track_meta(): cropped.meta[Key.PATCH_INDEX] = i # type: ignore - self.push_transform(cropped, replace=True, lazy_evaluation=lazy_evaluation_) # track as this class instead of RandSpatialCrop + self.push_transform(cropped, replace=True, lazy=lazy_) # track as this class instead of RandSpatialCrop ret.append(cropped) return ret @@ -767,7 +767,7 @@ def __init__( return_coords: bool = False, k_divisible: Sequence[int] | int = 1, mode: str = PytorchPadMode.CONSTANT, - lazy_evaluation: bool = False, + lazy: bool = False, **pad_kwargs, ) -> None: """ @@ -792,14 +792,14 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.select_fn = select_fn self.channel_indices = ensure_tuple(channel_indices) if channel_indices is not None else None self.margin = margin self.allow_smaller = allow_smaller self.return_coords = return_coords self.k_divisible = k_divisible - self.padder = Pad(mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs) + self.padder = Pad(mode=mode, lazy=lazy, **pad_kwargs) def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ @@ -822,14 +822,14 @@ def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarra def crop_pad( self, img: torch.Tensor, box_start: np.ndarray, box_end: np.ndarray, mode: str | None = None, - lazy_evaluation: bool = False, **pad_kwargs + lazy: bool = False, **pad_kwargs ) -> torch.Tensor: """ Crop and pad based on the bounding box. """ slices = self.compute_slices(roi_start=box_start, roi_end=box_end) - cropped = super().__call__(img=img, slices=slices, lazy_evaluation=lazy_evaluation) + cropped = super().__call__(img=img, slices=slices, lazy=lazy) pad_to_start = np.maximum(-box_start, 0) pad_to_end = np.maximum( box_end - np.asarray(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]), 0 @@ -839,25 +839,25 @@ def crop_pad( cropped.peek_pending_shape() if isinstance(cropped, MetaTensor) else cropped.shape[1:] ) ret = self.padder.__call__( - img=cropped, to_pad=pad_width, mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs + img=cropped, to_pad=pad_width, mode=mode, lazy=lazy, **pad_kwargs ) # combine the traced cropping and padding into one transformation # by taking the padded info and placing it in a key inside the crop info. if get_track_meta() and isinstance(ret, MetaTensor): - if not lazy_evaluation: + if not lazy: ret.applied_operations[-1][TraceKeys.EXTRA_INFO]["pad_info"] = ret.applied_operations.pop() return ret def __call__( # type: ignore[override] - self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: bool | None = None, **pad_kwargs + self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs ) -> torch.Tensor: """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't change the channel dim. """ box_start, box_end = self.compute_bounding_box(img) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - cropped = self.crop_pad(img, box_start, box_end, mode, lazy_evaluation=lazy_evaluation_, **pad_kwargs) + lazy_ = self.lazy if lazy is None else lazy + cropped = self.crop_pad(img, box_start, box_end, mode, lazy=lazy_, **pad_kwargs) if self.return_coords: return cropped, box_start, box_end # type: ignore[return-value] @@ -891,9 +891,9 @@ class RandWeightedCrop(Randomizable, TraceableTransform, LazyTransform, MultiSam def __init__( self, spatial_size: Sequence[int] | int, num_samples: int = 1, weight_map: NdarrayOrTensor | None = None, - lazy_evaluation: bool = False + lazy: bool = False ): - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.spatial_size = ensure_tuple(spatial_size) self.num_samples = int(num_samples) self.weight_map = weight_map @@ -906,7 +906,7 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: def __call__( self, img: torch.Tensor, weight_map: NdarrayOrTensor | None = None, randomize: bool = True, - lazy_evaluation: bool | None = None + lazy: bool | None = None ) -> list[torch.Tensor]: """ Args: @@ -933,15 +933,15 @@ def __call__( _spatial_size = fall_back_tuple(self.spatial_size, img_shape) results: list[torch.Tensor] = [] - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for i, center in enumerate(self.centers): - cropper = SpatialCrop(roi_center=center, roi_size=_spatial_size, lazy_evaluation=lazy_evaluation_) + cropper = SpatialCrop(roi_center=center, roi_size=_spatial_size, lazy=lazy_) cropped = cropper(img) if get_track_meta(): ret_: MetaTensor = cropped # type: ignore ret_.meta[Key.PATCH_INDEX] = i ret_.meta["crop_center"] = center - self.push_transform(ret_, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(ret_, replace=True, lazy=lazy_) results.append(cropped) return results @@ -1015,9 +1015,9 @@ def __init__( fg_indices: NdarrayOrTensor | None = None, bg_indices: NdarrayOrTensor | None = None, allow_smaller: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.label = label if pos < 0 or neg < 0: @@ -1072,7 +1072,7 @@ def __call__( fg_indices: NdarrayOrTensor | None = None, bg_indices: NdarrayOrTensor | None = None, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> list[torch.Tensor]: """ Args: @@ -1099,15 +1099,15 @@ def __call__( if self.centers is not None: img_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] roi_size = fall_back_tuple(self.spatial_size, default=img_shape) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for i, center in enumerate(self.centers): - cropper = SpatialCrop(roi_center=center, roi_size=roi_size, lazy_evaluation=lazy_evaluation_) + cropper = SpatialCrop(roi_center=center, roi_size=roi_size, lazy=lazy_) cropped = cropper(img) if get_track_meta(): ret_: MetaTensor = cropped # type: ignore ret_.meta[Key.PATCH_INDEX] = i ret_.meta["crop_center"] = center - self.push_transform(ret_, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(ret_, replace=True, lazy=lazy_) results.append(cropped) return results @@ -1194,9 +1194,9 @@ def __init__( allow_smaller: bool = False, warn: bool = True, max_samples_per_class: int | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.ratios = ratios self.label = label @@ -1241,7 +1241,7 @@ def __call__( image: torch.Tensor | None = None, indices: list[NdarrayOrTensor] | None = None, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> list[torch.Tensor]: """ Args: @@ -1264,15 +1264,15 @@ def __call__( if self.centers is not None: img_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] roi_size = fall_back_tuple(self.spatial_size, default=img_shape) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for i, center in enumerate(self.centers): - cropper = SpatialCrop(roi_center=tuple(center), roi_size=roi_size, lazy_evaluation=lazy_evaluation_) + cropper = SpatialCrop(roi_center=tuple(center), roi_size=roi_size, lazy=lazy_) cropped = cropper(img) if get_track_meta(): ret_: MetaTensor = cropped # type: ignore ret_.meta[Key.PATCH_INDEX] = i ret_.meta["crop_center"] = center - self.push_transform(ret_, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(ret_, replace=True, lazy=lazy_) results.append(cropped) return results @@ -1308,16 +1308,16 @@ def __init__( spatial_size: Sequence[int] | int, method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, - lazy_evaluation: bool = False, + lazy: bool = False, **pad_kwargs, ): - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.padder = SpatialPad( - spatial_size=spatial_size, method=method, mode=mode, lazy_evaluation=lazy_evaluation, **pad_kwargs + spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs ) - self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy_evaluation=lazy_evaluation) + self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) - def __call__(self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: bool | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore """ Args: img: data to pad or crop, assuming `img` is channel-first and @@ -1332,18 +1332,18 @@ def __call__(self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: note that `np.pad` treats channel dimension as the first dimension. """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - ret = self.padder(self.cropper(img, lazy_evaluation_), mode=mode, lazy_evaluation=lazy_evaluation_, **pad_kwargs) + lazy_ = self.lazy if lazy is None else lazy + ret = self.padder(self.cropper(img, lazy_), mode=mode, lazy=lazy_, **pad_kwargs) # remove the individual info and combine if get_track_meta(): ret_: MetaTensor = ret # type: ignore - if not lazy_evaluation_: + if not lazy_: pad_info = ret_.applied_operations.pop() crop_info = ret_.applied_operations.pop() orig_size = crop_info.get(TraceKeys.ORIG_SIZE) self.push_transform( ret_, orig_size=orig_size, extra_info={"pad_info": pad_info, "crop_info": crop_info}, - lazy_evaluation=lazy_evaluation_ + lazy=lazy_ ) else: pad_info = ret_.pending_operations.pop() @@ -1355,7 +1355,7 @@ def __call__(self, img: torch.Tensor, mode: str | None = None, lazy_evaluation: sp_size=pad_info[LazyAttr.SHAPE], affine=crop_info[LazyAttr.AFFINE] @ pad_info[LazyAttr.AFFINE], extra_info={"pad_info": pad_info, "crop_info": crop_info}, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) return ret diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 66908e873e..e8ba7f1b4a 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -124,7 +124,7 @@ def __init__( padder: Pad, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -142,22 +142,22 @@ def __init__( """ MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) - if lazy_evaluation is True and not isinstance(padder, LazyTrait): - raise ValueError("'padder' must inherit LazyTrait if lazy_evaluation is True " + LazyTransform.__init__(self, lazy) + if lazy is True and not isinstance(padder, LazyTrait): + raise ValueError("'padder' must inherit LazyTrait if lazy is True " f"'padder' is of type({type(padder)})") self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - if lazy_evaluation_ is True and not isinstance(self.padder, LazyTrait): - raise ValueError("'self.padder' must inherit LazyTrait if lazy_evaluation is True " + lazy_ = self.lazy if lazy is None else lazy + if lazy_ is True and not isinstance(self.padder, LazyTrait): + raise ValueError("'self.padder' must inherit LazyTrait if lazy is True " f"'self.padder' is of type({type(self.padder)}") for key, m in self.key_iterator(d, self.mode): if isinstance(self.padder, LazyTrait): - d[key] = self.padder(d[key], mode=m, lazy_evaluation=lazy_evaluation_) + d[key] = self.padder(d[key], mode=m, lazy=lazy_) else: d[key] = self.padder(d[key], mode=m) @@ -184,7 +184,7 @@ def __init__( method: str = Method.SYMMETRIC, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: """ @@ -210,8 +210,8 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - LazyTransform.__init__(self, lazy_evaluation) - padder = SpatialPad(spatial_size, method, lazy_evaluation=lazy_evaluation, **kwargs) + LazyTransform.__init__(self, lazy) + padder = SpatialPad(spatial_size, method, lazy=lazy, **kwargs) Padd.__init__( self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys ) @@ -231,7 +231,7 @@ def __init__( spatial_border: Sequence[int] | int, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: """ @@ -261,8 +261,8 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - LazyTransform.__init__(self, lazy_evaluation) - padder = BorderPad(spatial_border=spatial_border, lazy_evaluation=lazy_evaluation, **kwargs) + LazyTransform.__init__(self, lazy) + padder = BorderPad(spatial_border=spatial_border, lazy=lazy, **kwargs) Padd.__init__( self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys ) @@ -283,7 +283,7 @@ def __init__( mode: SequenceStr = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: """ @@ -309,8 +309,8 @@ def __init__( See also :py:class:`monai.transforms.SpatialPad` """ - LazyTransform.__init__(self, lazy_evaluation) - padder = DivisiblePad(k=k, method=method, lazy_evaluation=lazy_evaluation, **kwargs) + LazyTransform.__init__(self, lazy) + padder = DivisiblePad(k=k, method=method, lazy=lazy, **kwargs) Padd.__init__(self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) @@ -329,17 +329,17 @@ class Cropd(MapTransform, InvertibleTransform, LazyTransform): backend = Crop.backend def __init__( - self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy_evaluation: bool = False + self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy: bool = False ): MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.cropper = cropper - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): - d[key] = self.cropper(d[key], lazy_evaluation=lazy_evaluation_) # type: ignore + d[key] = self.cropper(d[key], lazy=lazy_) # type: ignore return d def inverse(self, data: Mapping[Hashable, MetaTensor]) -> dict[Hashable, MetaTensor]: @@ -364,9 +364,9 @@ class RandCropd(Cropd, Randomizable): backend = Crop.backend def __init__( - self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy_evaluation: bool = False + self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy: bool = False ): - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandCropd: super().set_random_state(seed, state) @@ -378,19 +378,19 @@ def randomize(self, img_size: Sequence[int]) -> None: if isinstance(self.cropper, Randomizable): self.cropper.randomize(img_size) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) # the first key must exist to execute random operations first_item = d[self.first_key(d)] self.randomize(first_item.peek_pending_shape() if isinstance(first_item, MetaTensor) else first_item.shape[1:]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - if lazy_evaluation_ is True and not isinstance(self.cropper, LazyTrait): - raise ValueError("'self.cropper' must inherit LazyTrait if lazy_evaluation is True " + lazy_ = self.lazy if lazy is None else lazy + if lazy_ is True and not isinstance(self.cropper, LazyTrait): + raise ValueError("'self.cropper' must inherit LazyTrait if lazy is True " f"'self.cropper' is of type({type(self.cropper)}") for key in self.key_iterator(d): kwargs = {"randomize": False} if isinstance(self.cropper, Randomizable) else {} if isinstance(self.cropper, LazyTrait): - kwargs["lazy_evaluation"] = lazy_evaluation_ + kwargs["lazy"] = lazy_ d[key] = self.cropper(d[key], **kwargs) # type: ignore return d @@ -419,7 +419,7 @@ def __init__( roi_end: Sequence[int] | None = None, roi_slices: Sequence[slice] | None = None, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -435,8 +435,8 @@ def __init__( allow_missing_keys: don't raise exception if key is missing. """ - cropper = SpatialCrop(roi_center, roi_size, roi_start, roi_end, roi_slices, lazy_evaluation=lazy_evaluation) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + cropper = SpatialCrop(roi_center, roi_size, roi_start, roi_end, roi_slices, lazy=lazy) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) class CenterSpatialCropd(Cropd): @@ -458,10 +458,10 @@ class CenterSpatialCropd(Cropd): """ def __init__( - self, keys: KeysCollection, roi_size: Sequence[int] | int, allow_missing_keys: bool = False, lazy_evaluation: bool = False + self, keys: KeysCollection, roi_size: Sequence[int] | int, allow_missing_keys: bool = False, lazy: bool = False ) -> None: - cropper = CenterSpatialCrop(roi_size, lazy_evaluation=lazy_evaluation) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + cropper = CenterSpatialCrop(roi_size, lazy=lazy) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) class CenterScaleCropd(Cropd): @@ -479,10 +479,10 @@ class CenterScaleCropd(Cropd): """ def __init__( - self, keys: KeysCollection, roi_scale: Sequence[float] | float, allow_missing_keys: bool = False, lazy_evaluation: bool = False + self, keys: KeysCollection, roi_scale: Sequence[float] | float, allow_missing_keys: bool = False, lazy: bool = False ) -> None: - cropper = CenterScaleCrop(roi_scale, lazy_evaluation=lazy_evaluation) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + cropper = CenterScaleCrop(roi_scale, lazy=lazy) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) class RandSpatialCropd(RandCropd): @@ -524,10 +524,10 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: - cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size, lazy_evaluation=lazy_evaluation) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size, lazy=lazy) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) class RandScaleCropd(RandCropd): @@ -564,10 +564,10 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, - lazy_evaluation: bool = False + lazy: bool = False ) -> None: - cropper = RandScaleCrop(roi_scale, max_roi_scale, random_center, random_size, lazy_evaluation=lazy_evaluation) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + cropper = RandScaleCrop(roi_scale, max_roi_scale, random_center, random_size, lazy=lazy) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) class RandSpatialCropSamplesd(Randomizable, MapTransform, LazyTransform, MultiSampleTrait): @@ -618,17 +618,17 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.cropper = RandSpatialCropSamples(roi_size, num_samples, max_roi_size, random_center, random_size, - lazy_evaluation=lazy_evaluation) + lazy=lazy) def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: ret: list[dict[Hashable, torch.Tensor]] = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): @@ -638,10 +638,10 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool # for each key we reset the random state to ensure crops are the same self.randomize() - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(dict(data)): self.cropper.set_random_state(seed=self.sub_seed) - for i, im in enumerate(self.cropper(data[key], lazy_evaluation=lazy_evaluation_)): + for i, im in enumerate(self.cropper(data[key], lazy=lazy_)): ret[i][key] = im return ret @@ -672,7 +672,7 @@ def __init__( start_coord_key: str = "foreground_start_coord", end_coord_key: str = "foreground_end_coord", allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, **pad_kwargs, ) -> None: """ @@ -712,13 +712,13 @@ def __init__( margin=margin, allow_smaller=allow_smaller, k_divisible=k_divisible, - lazy_evaluation=lazy_evaluation, + lazy=lazy, **pad_kwargs, ) - super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation) + super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) self.cropper: CropForeground box_start, box_end = self.cropper.compute_bounding_box(img=d[self.source_key]) @@ -727,10 +727,10 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool if self.end_coord_key is not None: d[self.end_coord_key] = box_end # type: ignore - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, m in self.key_iterator(d, self.mode): d[key] = self.cropper.crop_pad(img=d[key], box_start=box_start, box_end=box_end, mode=m, - lazy_evaluation=lazy_evaluation_) + lazy=lazy_) return d @@ -761,12 +761,12 @@ def __init__( spatial_size: Sequence[int] | int, num_samples: int = 1, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ): MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.w_key = w_key - self.cropper = RandWeightedCrop(spatial_size, num_samples, lazy_evaluation=lazy_evaluation) + self.cropper = RandWeightedCrop(spatial_size, num_samples, lazy=lazy) def set_random_state( self, seed: int | None = None, state: np.random.RandomState | None = None @@ -778,7 +778,7 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: # output starts as empty list of dictionaries ret: list = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data @@ -787,9 +787,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool ret[i][key] = deepcopy(data[key]) self.randomize(weight_map=data[self.w_key]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(data): - for i, im in enumerate(self.cropper(data[key], randomize=False, lazy_evaluation=lazy_evaluation_)): + for i, im in enumerate(self.cropper(data[key], randomize=False, lazy=lazy_)): ret[i][key] = im return ret @@ -862,10 +862,10 @@ def __init__( bg_indices_key: str | None = None, allow_smaller: bool = False, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key self.fg_indices_key = fg_indices_key @@ -877,7 +877,7 @@ def __init__( num_samples=num_samples, image_threshold=image_threshold, allow_smaller=allow_smaller, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) def set_random_state( @@ -896,7 +896,7 @@ def randomize( ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) fg_indices = d.pop(self.fg_indices_key, None) bg_indices = d.pop(self.bg_indices_key, None) @@ -910,9 +910,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool for key in set(d.keys()).difference(set(self.keys)): ret[i][key] = deepcopy(d[key]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): - for i, im in enumerate(self.cropper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_)): + for i, im in enumerate(self.cropper(d[key], randomize=False, lazy=lazy_)): ret[i][key] = im return ret @@ -1009,10 +1009,10 @@ def __init__( allow_missing_keys: bool = False, warn: bool = True, max_samples_per_class: int | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key self.indices_key = indices_key @@ -1025,7 +1025,7 @@ def __init__( allow_smaller=allow_smaller, warn=warn, max_samples_per_class=max_samples_per_class, - lazy_evaluation=lazy_evaluation + lazy=lazy ) def set_random_state( @@ -1040,7 +1040,7 @@ def randomize( ) -> None: self.cropper.randomize(label=label, indices=indices, image=image) - def __call__(self, data: Mapping[Hashable, Any], lazy_evaluation: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) self.randomize(d.get(self.label_key), d.pop(self.indices_key, None), d.get(self.image_key)) # type: ignore @@ -1051,9 +1051,9 @@ def __call__(self, data: Mapping[Hashable, Any], lazy_evaluation: bool | None = for key in set(d.keys()).difference(set(self.keys)): ret[i][key] = deepcopy(d[key]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): - for i, im in enumerate(self.cropper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_)): + for i, im in enumerate(self.cropper(d[key], randomize=False, lazy=lazy_)): ret[i][key] = im return ret @@ -1089,12 +1089,12 @@ def __init__( mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, method: str = Method.SYMMETRIC, - lazy_evaluation: bool = False, + lazy: bool = False, **pad_kwargs, ) -> None: - padcropper = ResizeWithPadOrCrop(spatial_size=spatial_size, method=method, **pad_kwargs, lazy_evaluation=lazy_evaluation) + padcropper = ResizeWithPadOrCrop(spatial_size=spatial_size, method=method, **pad_kwargs, lazy=lazy) super().__init__( - keys, padder=padcropper, mode=mode, allow_missing_keys=allow_missing_keys, lazy_evaluation=lazy_evaluation # type: ignore + keys, padder=padcropper, mode=mode, allow_missing_keys=allow_missing_keys, lazy=lazy # type: ignore ) diff --git a/monai/transforms/croppad/functional.py b/monai/transforms/croppad/functional.py index 7dfd9e9a61..3460127c8a 100644 --- a/monai/transforms/croppad/functional.py +++ b/monai/transforms/croppad/functional.py @@ -160,12 +160,12 @@ def pad_func( to_pad: tuple[tuple[int, int]], transform_info: dict, mode: str = PytorchPadMode.CONSTANT, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs ) -> torch.Tensor: """ Functional implementation of padding a MetaTensor. This function operates eagerly or lazily according - to ``lazy_evaluation`` (default ``False``). + to ``lazy`` (default ``False``). `torch.nn.functional.pad` is used unless the mode or kwargs are not available in torch, in which case `np.pad` will be used. @@ -181,7 +181,7 @@ def pad_func( One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html - lazy_evaluation: a flag indicating whether the operation should be performed in a lazy fashion or not. + lazy: a flag indicating whether the operation should be performed in a lazy fashion or not. transform_info: a dictionary with the relevant information pertaining to an applied transform. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -207,25 +207,25 @@ def pad_func( extra_info=extra_info, orig_size=img_size, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = convert_to_tensor(img.as_tensor() if isinstance(img, MetaTensor) else img, track_meta=get_track_meta()) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info # type: ignore out = pad_nd(out, to_pad_list, mode, **kwargs) if do_pad else out out = convert_to_tensor(out, track_meta=get_track_meta()) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore -def crop_func(img: torch.Tensor, slices: tuple[slice, ...], lazy_evaluation: bool, transform_info: dict) -> torch.Tensor: +def crop_func(img: torch.Tensor, slices: tuple[slice, ...], lazy: bool, transform_info: dict) -> torch.Tensor: """ Functional implementation of cropping a MetaTensor. This function operates eagerly or lazily according - to ``lazy_evaluation`` (default ``False``). + to ``lazy`` (default ``False``). Args: img: data to be transformed, assuming `img` is channel-first and cropping doesn't apply to the channel dim. slices: the crop slices computed based on specified `center & size` or `start & end` or `slices`. - lazy_evaluation: a flag indicating whether the operation should be performed in a lazy fashion or not. + lazy: a flag indicating whether the operation should be performed in a lazy fashion or not. transform_info: a dictionary with the relevant information pertaining to an applied transform. """ img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -246,10 +246,10 @@ def crop_func(img: torch.Tensor, slices: tuple[slice, ...], lazy_evaluation: boo extra_info=extra_info, orig_size=img_size, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = convert_to_tensor(img.as_tensor() if isinstance(img, MetaTensor) else img, track_meta=get_track_meta()) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info # type: ignore out = out[slices] return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 1e9b3d6c4b..5aaa8309ef 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -82,7 +82,7 @@ def transform_info_keys(): TraceKeys.CLASS_NAME, TraceKeys.ID, TraceKeys.TRACING, - TraceKeys.LAZY_EVALUATION, + TraceKeys.LAZY, TraceKeys.DO_TRANSFORM, ) @@ -94,7 +94,7 @@ def get_transform_info(self) -> dict: self.__class__.__name__, id(self), self.tracing, - self.lazy_evaluation if isinstance(self, LazyTrait) else False, + self.lazy if isinstance(self, LazyTrait) else False, self._do_transform if hasattr(self, "_do_transform") else True, ) return dict(zip(self.transform_info_keys(), vals)) @@ -110,9 +110,9 @@ def push_transform(self, data, *args, **kwargs): set ``replace=True`` (default False) to rewrite the last transform infor in applied_operation/pending_operation based on ``self.get_transform_info()``. """ - lazy_eval = kwargs.get("lazy_evaluation", False) + lazy_eval = kwargs.get("lazy", False) transform_info = self.get_transform_info() - # lazy_eval = transform_info.get(TraceKeys.LAZY_EVALUATION, False) + # lazy_eval = transform_info.get(TraceKeys.lazy, False) do_transform = transform_info.get(TraceKeys.DO_TRANSFORM, True) kwargs = kwargs or {} replace = kwargs.pop("replace", False) # whether to rewrite the most recently pushed transform info @@ -125,10 +125,10 @@ def push_transform(self, data, *args, **kwargs): xform = data.pending_operations.pop() extra = xform.copy() xform.update(transform_info) - meta_obj = self.push_transform(data, transform_info=xform, lazy_evaluation=lazy_eval, extra_info=extra) + meta_obj = self.push_transform(data, transform_info=xform, lazy=lazy_eval, extra_info=extra) return data.copy_meta_from(meta_obj) return data - kwargs["lazy_evaluation"] = lazy_eval + kwargs["lazy"] = lazy_eval if "transform_info" in kwargs and isinstance(kwargs["transform_info"], dict): kwargs["transform_info"].update(transform_info) else: @@ -146,7 +146,7 @@ def track_transform_meta( extra_info: dict | None = None, orig_size: tuple | None = None, transform_info=None, - lazy_evaluation=False, + lazy=False, ): """ Update a stack of applied/pending transforms metadata of ``data``. @@ -164,7 +164,7 @@ def track_transform_meta( orig_size: sometimes during the inverse it is useful to know what the size of the original image was, in which case it can be supplied here. transform_info: info from self.get_transform_info(). - lazy_evaluation: whether to push the transform to pending_operations or applied_operations. + lazy: whether to push the transform to pending_operations or applied_operations. Returns: @@ -177,10 +177,10 @@ def track_transform_meta( if isinstance(data_t, MetaTensor): out_obj.copy_meta_from(data_t, keys=out_obj.__dict__.keys()) - if lazy_evaluation and (not get_track_meta()): + if lazy and (not get_track_meta()): warnings.warn("metadata is not tracked, please call 'set_track_meta(True)' if doing lazy evaluation.") - if not lazy_evaluation and affine is not None and isinstance(data_t, MetaTensor): + if not lazy and affine is not None and isinstance(data_t, MetaTensor): # not lazy evaluation, directly update the metatensor affine (don't push to the stack) orig_affine = data_t.peek_pending_affine() orig_affine = convert_to_dst_type(orig_affine, affine, dtype=torch.float64)[0] @@ -210,7 +210,7 @@ def track_transform_meta( info[TraceKeys.EXTRA_INFO] = extra_info # push the transform info to the applied_operation or pending_operation stack - if lazy_evaluation: + if lazy: if sp_size is None: if LazyAttr.SHAPE not in info: warnings.warn("spatial size is None in push transform.") diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index fa1bb6d48e..d9c8404cdb 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -223,7 +223,9 @@ def resample(data: torch.Tensor, matrix: NdarrayOrTensor, kwargs: dict | None = img.affine = call_kwargs["dst_affine"] return img + # TODO: lazy evaluation - no need to separately set lazy to False + # resampler = monai.transforms.SpatialResample(lazy=False, **init_kwargs) resampler = monai.transforms.SpatialResample(**init_kwargs) - resampler.lazy_evaluation = False # resampler is a lazytransform + resampler.lazy = False # resampler is a lazytransform with resampler.trace_transform(False): # don't track this transform in `img` return resampler(img=img, **call_kwargs) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 020478e002..c040a21607 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -136,7 +136,7 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike = np.float64, - lazy_evaluation: bool = False, + lazy: bool = False, ): """ Args: @@ -155,14 +155,14 @@ def __init__( dtype: data type for resampling computation. Defaults to ``float64`` for best precision. If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ self.mode = mode self.padding_mode = padding_mode self.align_corners = align_corners self.dtype = dtype - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def __call__( self, @@ -173,7 +173,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike = None, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -205,8 +205,8 @@ def __call__( dtype: data type for resampling computation. Defaults to ``self.dtype`` or ``np.float64`` (for best precision). If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always `float32`. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. The spatial rank is determined by the smallest among ``img.ndim -1``, ``len(src_affine) - 1``, and ``3``. @@ -219,10 +219,10 @@ def __call__( align_corners = align_corners if align_corners is not None else self.align_corners mode = mode if mode is not None else self.mode padding_mode = padding_mode if padding_mode is not None else self.padding_mode - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return spatial_resample( img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info() ) @@ -256,7 +256,7 @@ def __call__( # type: ignore padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike = None, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -280,8 +280,8 @@ def __call__( # type: ignore dtype: data type for resampling computation. Defaults to ``self.dtype`` or ``np.float64`` (for best precision). If ``None``, use the data type of input data. To be compatible with other modules, the output data type is always `float32`. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Raises: @@ -292,7 +292,7 @@ def __call__( # type: ignore if img_dst is None: raise RuntimeError("`img_dst` is missing.") dst_affine = img_dst.peek_pending_affine() if isinstance(img_dst, MetaTensor) else torch.eye(4) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy img = super().__call__( img=img, dst_affine=dst_affine, @@ -301,9 +301,9 @@ def __call__( # type: ignore padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) - if not lazy_evaluation_: + if not lazy_: if isinstance(img, MetaTensor): img.affine = dst_affine if isinstance(img_dst, MetaTensor): @@ -340,7 +340,7 @@ def __init__( recompute_affine: bool = False, min_pixdim: Sequence[float] | float | np.ndarray | None = None, max_pixdim: Sequence[float] | float | np.ndarray | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -393,7 +393,7 @@ def __init__( max_pixdim: maximal input spacing to be resampled. If provided, input image with a smaller spacing than this value will be kept in its original spacing (not be resampled to `pixdim`). Set it to `None` to use the value of `pixdim`. Default to `None`. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ self.pixdim = np.array(ensure_tuple(pixdim), dtype=np.float64) @@ -402,7 +402,7 @@ def __init__( self.diagonal = diagonal self.scale_extent = scale_extent self.recompute_affine = recompute_affine - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy for mn, mx in zip(self.min_pixdim, self.max_pixdim): if (not np.isnan(mn)) and (not np.isnan(mx)) and ((mx < mn) or (mn < 0)): @@ -410,7 +410,7 @@ def __init__( self.sp_resample = SpatialResample( mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation + lazy=lazy ) @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") @@ -424,7 +424,7 @@ def __call__( dtype: DtypeLike = None, scale_extent: bool | None = None, output_spatial_shape: Sequence[int] | np.ndarray | int | None = None, - lazy_evaluation: bool | None = None + lazy: bool | None = None ) -> torch.Tensor: """ Args: @@ -454,8 +454,8 @@ def __call__( output_spatial_shape: specify the shape of the output data_array. This is typically useful for the inverse of `Spacingd` where sometimes we could not compute the exact shape due to the quantization error with the affine. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Raises: @@ -507,7 +507,7 @@ def __call__( new_affine[:sr, -1] = offset[:sr] actual_shape = list(output_shape) if output_spatial_shape is None else output_spatial_shape - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy data_array = self.sp_resample( data_array, dst_affine=torch.as_tensor(new_affine), @@ -516,10 +516,10 @@ def __call__( padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) if self.recompute_affine and isinstance(data_array, MetaTensor): - if lazy_evaluation_: + if lazy_: raise NotImplementedError("recompute_affine is not supported with lazy evaluation.") a = scale_affine(original_spatial_shape, actual_shape) data_array.affine = convert_to_dst_type(a, affine_)[0] # type: ignore @@ -541,7 +541,7 @@ def __init__( axcodes: str | None = None, as_closest_canonical: bool = False, labels: Sequence[tuple[str, str]] | None = (("L", "R"), ("P", "A"), ("I", "S")), - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -554,7 +554,7 @@ def __init__( labels: optional, None or sequence of (2,) sequences (2,) sequences are labels for (beginning, end) of output axis. Defaults to ``(('L', 'R'), ('P', 'A'), ('I', 'S'))``. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False Raises: @@ -570,17 +570,17 @@ def __init__( self.axcodes = axcodes self.as_closest_canonical = as_closest_canonical self.labels = labels - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, data_array: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: + def __call__(self, data_array: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ If input type is `MetaTensor`, original affine is extracted with `data_array.affine`. If input type is `torch.Tensor`, original affine is assumed to be identity. Args: data_array: in shape (num_channels, H[, W, ...]). - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Raises: @@ -626,9 +626,9 @@ def __call__(self, data_array: torch.Tensor, lazy_evaluation: bool | None = None f"axcodes must match data_array spatially, got axcodes={len(self.axcodes)}D data_array={sr}D" ) spatial_ornt = nib.orientations.ornt_transform(src, dst) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return orientation(data_array, affine_np, spatial_ornt, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -656,29 +656,29 @@ class Flip(InvertibleTransform, LazyTransform): If axis is negative it counts from the last to the first axis. If axis is a tuple of ints, flipping is performed on all of the axes specified in the tuple. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ backend = [TransformBackends.TORCH] - def __init__(self, spatial_axis: Sequence[int] | int | None = None, lazy_evaluation: bool = False) -> None: + def __init__(self, spatial_axis: Sequence[int] | int | None = None, lazy: bool = False) -> None: self.spatial_axis = spatial_axis - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: + def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return flip(img, self.spatial_axis, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -720,7 +720,7 @@ class Resize(InvertibleTransform, LazyTransform): anti-aliasing is performed prior to rescaling. dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -735,7 +735,7 @@ def __init__( anti_aliasing: bool = False, anti_aliasing_sigma: Sequence[float] | float | None = None, dtype: DtypeLike | torch.dtype = torch.float32, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: self.size_mode = look_up_option(size_mode, ["all", "longest"]) self.spatial_size = spatial_size @@ -744,7 +744,7 @@ def __init__( self.anti_aliasing = anti_aliasing self.anti_aliasing_sigma = anti_aliasing_sigma self.dtype = dtype - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def __call__( self, @@ -754,7 +754,7 @@ def __call__( anti_aliasing: bool | None = None, anti_aliasing_sigma: Sequence[float] | float | None = None, dtype: DtypeLike | torch.dtype = None, - lazy_evaluation: bool | None = None + lazy: bool | None = None ) -> torch.Tensor: """ Args: @@ -777,8 +777,8 @@ def __call__( anti-aliasing is performed prior to rescaling. dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Raises: ValueError: When ``self.spatial_size`` length is less than ``img`` spatial dimensions. @@ -810,7 +810,7 @@ def __call__( _mode = look_up_option(self.mode if mode is None else mode, InterpolateMode) _align_corners = self.align_corners if align_corners is None else align_corners _dtype = get_equivalent_dtype(dtype or self.dtype or img.dtype, torch.Tensor) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return resize( # type: ignore img, sp_size, @@ -820,7 +820,7 @@ def __call__( input_ndim, anti_aliasing, anti_aliasing_sigma, - lazy_evaluation_, + lazy_, self.get_transform_info(), ) @@ -866,7 +866,7 @@ class Rotate(InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -880,7 +880,7 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike | torch.dtype = torch.float32, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: self.angle = angle self.keep_size = keep_size @@ -888,7 +888,7 @@ def __init__( self.padding_mode: str = look_up_option(padding_mode, GridSamplePadMode) self.align_corners = align_corners self.dtype = dtype - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def __call__( self, @@ -897,7 +897,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, - lazy_evaluation: bool | None = None + lazy: bool | None = None ) -> torch.Tensor: """ Args: @@ -915,8 +915,8 @@ def __call__( dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Raises: @@ -930,10 +930,10 @@ def __call__( _align_corners = self.align_corners if align_corners is None else align_corners im_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] output_shape = im_shape if self.keep_size else None - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return rotate( # type: ignore img, self.angle, output_shape, _mode, _padding_mode, _align_corners, _dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info() ) @@ -996,7 +996,7 @@ class Zoom(InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. keep_size: Should keep original size (padding/slicing if needed), default is True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -1012,7 +1012,7 @@ def __init__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = torch.float32, keep_size: bool = True, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: self.zoom = zoom @@ -1022,7 +1022,7 @@ def __init__( self.dtype = dtype self.keep_size = keep_size self.kwargs = kwargs - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def __call__( self, @@ -1031,7 +1031,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -1052,8 +1052,8 @@ def __call__( See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) @@ -1062,10 +1062,10 @@ def __call__( _padding_mode = padding_mode or self.padding_mode _align_corners = self.align_corners if align_corners is None else align_corners _dtype = get_equivalent_dtype(dtype or self.dtype or img.dtype, torch.Tensor) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return zoom( # type: ignore img, _zoom, self.keep_size, _mode, _padding_mode, _align_corners, _dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info() ) @@ -1109,7 +1109,7 @@ def __init__( self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1), - lazy_evaluation: bool = False + lazy: bool = False ) -> None: """ Args: @@ -1117,7 +1117,7 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. If axis is negative it counts from the last to the first axis. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ self.k = (4 + (k % 4)) % 4 # 0, 1, 2, 3 @@ -1125,21 +1125,21 @@ def __init__( if len(spatial_axes_) != 2: raise ValueError(f"spatial_axes must be 2 numbers to define the plane to rotate, got {spatial_axes_}.") self.spatial_axes = spatial_axes_ - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, img: torch.Tensor, lazy_evaluation: bool | None = None) -> torch.Tensor: + def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) axes = map_spatial_axes(img.ndim, self.spatial_axes) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy return rotate90(img, axes, self.k, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1168,7 +1168,7 @@ def __init__( prob: float = 0.1, max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1), - lazy_evaluation: bool = False + lazy: bool = False ) -> None: """ Args: @@ -1177,13 +1177,13 @@ def __init__( max_k: number of rotations will be sampled from `np.random.randint(max_k) + 1`, (Default 3). spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ RandomizableTransform.__init__(self, prob) self.max_k = max_k self.spatial_axes = spatial_axes - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy self._rand_k = 0 @@ -1193,27 +1193,27 @@ def randomize(self, data: Any | None = None) -> None: return None self._rand_k = self.R.randint(self.max_k) + 1 - def __call__(self, img: torch.Tensor, randomize: bool = True, lazy_evaluation: bool | None = None) -> torch.Tensor: + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), randomize: whether to execute `randomize()` function first, default to True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ if randomize: self.randomize() - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy if self._do_transform: - xform = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) + xform = Rotate90(self._rand_k, self.spatial_axes, lazy=lazy_) out = xform(img) else: out = convert_to_tensor(img, track_meta=get_track_meta()) - self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(out, replace=True, lazy=lazy_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1250,7 +1250,7 @@ class RandRotate(RandomizableTransform, InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1267,7 +1267,7 @@ def __init__( padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike | torch.dtype = np.float32, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: RandomizableTransform.__init__(self, prob) self.range_x = ensure_tuple(range_x) @@ -1285,7 +1285,7 @@ def __init__( self.padding_mode: str = look_up_option(padding_mode, GridSamplePadMode) self.align_corners = align_corners self.dtype = dtype - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy self.x = 0.0 self.y = 0.0 @@ -1307,7 +1307,7 @@ def __call__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ): """ Args: @@ -1324,14 +1324,14 @@ def __call__( If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. randomize: whether to execute `randomize()` function first, default to True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ if randomize: self.randomize() - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy if self._do_transform: ndim = len(img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:]) rotator = Rotate( @@ -1341,12 +1341,12 @@ def __call__( padding_mode=look_up_option(padding_mode or self.padding_mode, GridSamplePadMode), align_corners=self.align_corners if align_corners is None else align_corners, dtype=dtype or self.dtype or img.dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) out = rotator(img) else: out = convert_to_tensor(img, track_meta=get_track_meta(), dtype=torch.float32) - self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(out, replace=True, lazy=lazy_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1365,7 +1365,7 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): Args: prob: Probability of flipping. spatial_axis: Spatial axes along which to flip over. Default is None. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1375,32 +1375,32 @@ def __init__( self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: RandomizableTransform.__init__(self, prob) - self.flipper = Flip(spatial_axis=spatial_axis, lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) + self.lazy = lazy def __call__( self, img: torch.Tensor, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), randomize: whether to execute `randomize()` function first, default to True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ if randomize: self.randomize(None) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - out = self.flipper(img, lazy_evaluation=lazy_evaluation_) if self._do_transform else img + lazy_ = self.lazy if lazy is None else lazy + out = self.flipper(img, lazy=lazy_) if self._do_transform else img out = convert_to_tensor(out, track_meta=get_track_meta()) - self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(out, replace=True, lazy=lazy_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1419,17 +1419,17 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform, LazyTransform): Args: prob: Probability of flipping. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ backend = Flip.backend - def __init__(self, prob: float = 0.1, lazy_evaluation: bool = False) -> None: + def __init__(self, prob: float = 0.1, lazy: bool = False) -> None: RandomizableTransform.__init__(self, prob) self._axis: int | None = None self.flipper = Flip(spatial_axis=self._axis) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) @@ -1441,26 +1441,26 @@ def __call__( self, img: torch.Tensor, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) randomize: whether to execute `randomize()` function first, default to True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ if randomize: self.randomize(data=img) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy if self._do_transform: self.flipper.spatial_axis = self._axis - out = self.flipper(img, lazy_evaluation=lazy_evaluation_) + out = self.flipper(img, lazy=lazy_) else: out = convert_to_tensor(img, track_meta=get_track_meta()) - self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(out, replace=True, lazy=lazy_) return out def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1504,7 +1504,7 @@ class RandZoom(RandomizableTransform, InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. keep_size: Should keep original size (pad if needed), default is True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -1523,7 +1523,7 @@ def __init__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = torch.float32, keep_size: bool = True, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: RandomizableTransform.__init__(self, prob) @@ -1538,7 +1538,7 @@ def __init__( self.align_corners = align_corners self.dtype = dtype self.keep_size = keep_size - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy self.kwargs = kwargs self._zoom: Sequence[float] = [1.0] @@ -1563,7 +1563,7 @@ def __call__( align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -1584,15 +1584,15 @@ def __call__( dtype: data type for resampling computation. Defaults to ``self.dtype``. If None, use the data type of input data. randomize: whether to execute `randomize()` function first, default to True. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ # match the spatial image dim if randomize: self.randomize(img=img) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy if not self._do_transform: out = convert_to_tensor(img, track_meta=get_track_meta(), dtype=torch.float32) else: @@ -1603,11 +1603,11 @@ def __call__( padding_mode=padding_mode or self.padding_mode, align_corners=self.align_corners if align_corners is None else align_corners, dtype=dtype or self.dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, **self.kwargs, ) out = xform(img) - self.push_transform(out, replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(out, replace=True, lazy=lazy_) return out # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1646,7 +1646,7 @@ class AffineGrid(LazyTransform): affine: If applied, ignore the params (`rotate_params`, etc.) and use the supplied matrix. Should be square with each side = num of image spatial dimensions + 1. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1662,7 +1662,7 @@ def __init__( dtype: DtypeLike = np.float32, align_corners: bool = False, affine: NdarrayOrTensor | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: self.rotate_params = rotate_params self.shear_params = shear_params @@ -1673,13 +1673,13 @@ def __init__( self.dtype = _dtype if _dtype in (torch.float16, torch.float64, None) else torch.float32 self.align_corners = align_corners self.affine = affine - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def __call__( self, spatial_size: Sequence[int] | None = None, grid: torch.Tensor | None = None, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> tuple[torch.Tensor | None, torch.Tensor]: """ The grid can be initialized with a `spatial_size` parameter, or provided directly as `grid`. @@ -1689,15 +1689,15 @@ def __call__( Args: spatial_size: output grid size. grid: grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Raises: ValueError: When ``grid=None`` and ``spatial_size=None``. Incompatible values. """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - if not lazy_evaluation_: + lazy_ = self.lazy if lazy is None else lazy + if not lazy_: if grid is None: # create grid from spatial_size if spatial_size is None: raise ValueError("Incompatible values: grid=None and spatial_size=None.") @@ -1726,7 +1726,7 @@ def __call__( else: affine = self.affine # type: ignore affine = to_affine_nd(spatial_dims, affine) - if lazy_evaluation_: + if lazy_: return None, affine affine = convert_to_tensor(affine, device=grid_.device, dtype=grid_.dtype, track_meta=False) # type: ignore @@ -1757,7 +1757,7 @@ def __init__( scale_range: RandRange = None, device: torch.device | None = None, dtype: DtypeLike = np.float32, - lazy_evaluation: bool = None, + lazy: bool = None, ) -> None: """ Args: @@ -1786,7 +1786,7 @@ def __init__( device: device to store the output grid data. dtype: data type for the grid computation. Defaults to ``np.float32``. If ``None``, use the data type of input data (if `grid` is provided). - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False See also: @@ -1809,7 +1809,7 @@ def __init__( self.device = device self.dtype = dtype self.affine: torch.Tensor | None = torch.eye(4, dtype=torch.float64) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def _get_rand_param(self, param_range, add_scalar: float = 0.0): out_param = [] @@ -1833,15 +1833,15 @@ def __call__( spatial_size: Sequence[int] | None = None, grid: NdarrayOrTensor | None = None, randomize: bool = True, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: spatial_size: output grid size. grid: grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D. randomize: boolean as to whether the grid parameters governing the grid should be randomized. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -1849,7 +1849,7 @@ def __call__( """ if randomize: self.randomize() - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy affine_grid = AffineGrid( rotate_params=self.rotate_params, shear_params=self.shear_params, @@ -1857,9 +1857,9 @@ def __call__( scale_params=self.scale_params, device=self.device, dtype=self.dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) - if lazy_evaluation_: # return the affine only, don't construct the grid + if lazy_: # return the affine only, don't construct the grid self.affine = affine_grid(spatial_size, grid)[1] # type: ignore return None # type: ignore _grid: torch.Tensor @@ -2123,7 +2123,7 @@ def __init__( dtype: DtypeLike = np.float32, align_corners: bool = False, image_only: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ The affine transformations are applied in rotate, shear, translate, scale order. @@ -2178,7 +2178,7 @@ def __init__( align_corners: Defaults to False. See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html image_only: if True return only the image volume, otherwise return (image, affine). - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ self.affine_grid = AffineGrid( @@ -2190,7 +2190,7 @@ def __init__( dtype=dtype, align_corners=align_corners, device=device, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) self.image_only = image_only self.norm_coord = not normalized @@ -2198,7 +2198,7 @@ def __init__( self.spatial_size = spatial_size self.mode = mode self.padding_mode: str = padding_mode - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def __call__( self, @@ -2206,7 +2206,7 @@ def __call__( spatial_size: Sequence[int] | int | None = None, mode: str | int | None = None, padding_mode: str | None = None, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor | tuple[torch.Tensor, NdarrayOrTensor]: """ Args: @@ -2228,17 +2228,17 @@ def __call__( When `mode` is an integer, using numpy/cupy backends, this argument accepts {'reflect', 'grid-mirror', 'constant', 'grid-constant', 'nearest', 'mirror', 'grid-wrap', 'wrap'}. See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ img = convert_to_tensor(img, track_meta=get_track_meta()) img_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] sp_size = fall_back_tuple(self.spatial_size if spatial_size is None else spatial_size, img_size) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy _mode = mode if mode is not None else self.mode _padding_mode = padding_mode if padding_mode is not None else self.padding_mode - grid, affine = self.affine_grid(spatial_size=sp_size, lazy_evaluation=lazy_evaluation_) + grid, affine = self.affine_grid(spatial_size=sp_size, lazy=lazy_) return affine_func( # type: ignore img, @@ -2250,7 +2250,7 @@ def __call__( _padding_mode, True, self.image_only, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info(), ) @@ -2310,7 +2310,7 @@ def __init__( padding_mode: str = GridSamplePadMode.REFLECTION, cache_grid: bool = False, device: torch.device | None = None, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -2360,7 +2360,7 @@ def __init__( If the spatial size is not dynamically defined by input image, enabling this option could accelerate the transform. device: device on which the tensor will be allocated. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False See also: @@ -2376,22 +2376,22 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) self.resampler = Resample(device=device) self.spatial_size = spatial_size self.cache_grid = cache_grid - self._cached_grid = self._init_identity_cache(lazy_evaluation) + self._cached_grid = self._init_identity_cache(lazy) self.mode = mode self.padding_mode: str = padding_mode - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def _init_identity_cache(self, lazy_evaluation: bool): + def _init_identity_cache(self, lazy: bool): """ Create cache of the identity grid if cache_grid=True and spatial_size is known. """ - if lazy_evaluation: + if lazy: return None if self.spatial_size is None: if self.cache_grid: @@ -2411,14 +2411,14 @@ def _init_identity_cache(self, lazy_evaluation: bool): return None return create_grid(spatial_size=_sp_size, device=self.rand_affine_grid.device, backend="torch") - def get_identity_grid(self, spatial_size: Sequence[int], lazy_evaluation: bool): + def get_identity_grid(self, spatial_size: Sequence[int], lazy: bool): """ Return a cached or new identity grid depends on the availability. Args: spatial_size: non-dynamic spatial size """ - if lazy_evaluation: + if lazy: return None ndim = len(spatial_size) if spatial_size != fall_back_tuple(spatial_size, [1] * ndim) or spatial_size != fall_back_tuple( @@ -2450,7 +2450,7 @@ def __call__( padding_mode: str | None = None, randomize: bool = True, grid=None, - lazy_evaluation: bool | None = None, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -2474,8 +2474,8 @@ def __call__( See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html randomize: whether to execute `randomize()` function first, default to True. grid: precomputed grid to be used (mainly to accelerate `RandAffined`). - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. """ if randomize: @@ -2487,19 +2487,19 @@ def __call__( do_resampling = self._do_transform or (sp_size != ensure_tuple(ori_size)) _mode = mode if mode is not None else self.mode _padding_mode = padding_mode if padding_mode is not None else self.padding_mode - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy img = convert_to_tensor(img, track_meta=get_track_meta()) - if lazy_evaluation_: + if lazy_: if self._do_transform: affine = self.rand_affine_grid.get_transformation_matrix() else: affine = convert_to_dst_type(torch.eye(len(sp_size) + 1), img, dtype=self.rand_affine_grid.dtype)[0] else: if grid is None: - grid = self.get_identity_grid(sp_size, lazy_evaluation_) + grid = self.get_identity_grid(sp_size, lazy_) if self._do_transform: grid = self.rand_affine_grid(grid=grid, randomize=randomize, - lazy_evaluation=lazy_evaluation_) + lazy=lazy_) affine = self.rand_affine_grid.get_transformation_matrix() return affine_func( # type: ignore img, @@ -2511,7 +2511,7 @@ def __call__( _padding_mode, do_resampling, True, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, transform_info=self.get_transform_info(), ) @@ -2627,7 +2627,7 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, - lazy_evaluation=False + lazy=False ) self.resampler = Resample(device=device) @@ -2795,7 +2795,7 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, - lazy_evaluation=False + lazy=False ) self.resampler = Resample(device=device) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index acb9d8c661..a4aaa800b5 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -169,7 +169,7 @@ def __init__( dtype: Sequence[DtypeLike] | DtypeLike = np.float64, dst_keys: KeysCollection | None = "dst_affine", allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -197,32 +197,32 @@ def __init__( It also can be a sequence of dtypes, each element corresponds to a key in ``keys``. dst_keys: the key of the corresponding ``dst_affine`` in the metadata dictionary. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ super().__init__(keys, allow_missing_keys) - self.sp_transform = SpatialResample(lazy_evaluation=lazy_evaluation) + self.sp_transform = SpatialResample(lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy d: dict = dict(data) for key, mode, padding_mode, align_corners, dtype, dst_key in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype, self.dst_keys @@ -235,7 +235,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) return d @@ -260,7 +260,7 @@ def __init__( align_corners: Sequence[bool] | bool = False, dtype: Sequence[DtypeLike] | DtypeLike = np.float64, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ): """ Args: @@ -288,7 +288,7 @@ def __init__( the output data type is always ``float32``. It also can be a sequence of dtypes, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ super().__init__(keys, allow_missing_keys) @@ -297,23 +297,23 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.resampler = ResampleToMatch(lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + self.resampler = ResampleToMatch(lazy=lazy) + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy d = dict(data) for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -325,7 +325,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) return d @@ -367,7 +367,7 @@ def __init__( max_pixdim: Sequence[float] | float | None = None, ensure_same_shape: bool = True, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -427,13 +427,13 @@ def __init__( ensure_same_shape: when the inputs have the same spatial shape, and almost the same pixdim, whether to ensure exactly the same output spatial shape. Default to True. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ super().__init__(keys, allow_missing_keys) self.spacing_transform = Spacing( pixdim, diagonal=diagonal, recompute_affine=recompute_affine, min_pixdim=min_pixdim, max_pixdim=max_pixdim, - lazy_evaluation=lazy_evaluation + lazy=lazy ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) @@ -441,16 +441,16 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.scale_extent = ensure_tuple_rep(scale_extent, len(self.keys)) self.ensure_same_shape = ensure_same_shape - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -460,7 +460,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool _init_shape, _pixdim, should_match = None, None, False output_shape_k = None # tracking output shape - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, mode, padding_mode, align_corners, dtype, scale_extent in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype, self.scale_extent @@ -480,7 +480,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool dtype=dtype, scale_extent=scale_extent, output_spatial_shape=output_shape_k if should_match else None, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) output_shape_k = d[key].peek_pending_shape() if isinstance(d[key], MetaTensor) else d[key].shape[1:] return d @@ -510,7 +510,7 @@ def __init__( as_closest_canonical: bool = False, labels: Sequence[tuple[str, str]] | None = (("L", "R"), ("P", "A"), ("I", "S")), allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -524,7 +524,7 @@ def __init__( (2,) sequences are labels for (beginning, end) of output axis. Defaults to ``(('L', 'R'), ('P', 'A'), ('I', 'S'))``. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False See Also: @@ -533,26 +533,26 @@ def __init__( """ super().__init__(keys, allow_missing_keys) self.ornt_transform = Orientation( - axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy=lazy) + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ d: dict = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): - d[key] = self.ornt_transform(d[key], lazy_evaluation=lazy_evaluation_) + d[key] = self.ornt_transform(d[key], lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -575,7 +575,7 @@ def __init__( k: int = 1, spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -583,30 +583,30 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ super().__init__(keys, allow_missing_keys) - self.rotator = Rotate90(k, spatial_axes, lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + self.rotator = Rotate90(k, spatial_axes, lazy=lazy) + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): - d[key] = self.rotator(d[key], lazy_evaluation=lazy_evaluation_) + d[key] = self.rotator(d[key], lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -632,7 +632,7 @@ def __init__( max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -645,7 +645,7 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ MapTransform.__init__(self, keys, allow_missing_keys) @@ -655,20 +655,20 @@ def __init__( self.spatial_axes = spatial_axes self._rand_k = 0 - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def randomize(self, data: Any | None = None) -> None: self._rand_k = self.R.randint(self.max_k) + 1 super().randomize(None) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> Mapping[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> Mapping[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -679,11 +679,11 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool # FIXME: here we didn't use array version `RandRotate90` transform as others, because we need # to be compatible with the random status of some previous integration tests - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation - rotator = Rotate90(self._rand_k, self.spatial_axes, lazy_evaluation=lazy_evaluation_) + lazy_ = self.lazy if lazy is None else lazy + rotator = Rotate90(self._rand_k, self.spatial_axes, lazy=lazy_) for key in self.key_iterator(d): d[key] = rotator(d[key]) if self._do_transform else convert_to_tensor(d[key], track_meta=get_track_meta()) - self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(d[key], replace=True, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -733,7 +733,7 @@ class Resized(MapTransform, InvertibleTransform, LazyTransform): dtype: data type for resampling computation. Defaults to ``float32``. If None, use the data type of input data. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -750,7 +750,7 @@ def __init__( anti_aliasing_sigma: Sequence[Sequence[float] | float | None] | Sequence[float] | float | None = None, dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.mode = ensure_tuple_rep(mode, len(self.keys)) @@ -758,24 +758,24 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.anti_aliasing = ensure_tuple_rep(anti_aliasing, len(self.keys)) self.anti_aliasing_sigma = ensure_tuple_rep(anti_aliasing_sigma, len(self.keys)) - self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy=lazy) + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, mode, align_corners, anti_aliasing, anti_aliasing_sigma, dtype in self.key_iterator( d, self.mode, self.align_corners, self.anti_aliasing, self.anti_aliasing_sigma, self.dtype ): @@ -786,7 +786,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool anti_aliasing=anti_aliasing, anti_aliasing_sigma=anti_aliasing_sigma, dtype=dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) return d @@ -819,7 +819,7 @@ def __init__( dtype: DtypeLike | torch.dtype = np.float32, align_corners: bool = False, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -870,7 +870,7 @@ def __init__( align_corners: Defaults to False. See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False See also: @@ -889,29 +889,29 @@ def __init__( device=device, dtype=dtype, # type: ignore align_corners=align_corners, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy d = dict(data) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): - d[key], _ = self.affine(d[key], mode=mode, padding_mode=padding_mode, lazy_evaluation=lazy_evaluation_) + d[key], _ = self.affine(d[key], mode=mode, padding_mode=padding_mode, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -942,7 +942,7 @@ def __init__( cache_grid: bool = False, device: torch.device | None = None, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -996,7 +996,7 @@ def __init__( accelerate the transform. device: device on which the tensor will be allocated. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False See also: @@ -1006,7 +1006,7 @@ def __init__( """ MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.rand_affine = RandAffine( prob=1.0, # because probability handled in this class rotate_range=rotate_range, @@ -1016,7 +1016,7 @@ def __init__( spatial_size=spatial_size, cache_grid=cache_grid, device=device, - lazy_evaluation=lazy_evaluation + lazy=lazy ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) @@ -1026,14 +1026,14 @@ def set_random_state(self, seed: int | None = None, state: np.random.RandomState super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy_evaluation: bool | None = None) -> dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy: bool | None = None) -> dict[Hashable, NdarrayOrTensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -1051,7 +1051,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy_evaluation: bo item = d[first_key] spatial_size = item.peek_pending_shape() if isinstance(item, MetaTensor) else item.shape[1:] - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy sp_size = fall_back_tuple(self.rand_affine.spatial_size, spatial_size) # change image size or do random transform @@ -1059,18 +1059,18 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy_evaluation: bo # converting affine to tensor because the resampler currently only support torch backend grid = None if do_resampling: # need to prepare grid - grid = self.rand_affine.get_identity_grid(sp_size, lazy_evaluation=lazy_evaluation_) + grid = self.rand_affine.get_identity_grid(sp_size, lazy=lazy_) if self._do_transform: # add some random factors - grid = self.rand_affine.rand_affine_grid(sp_size, grid=grid, lazy_evaluation=lazy_evaluation_) + grid = self.rand_affine.rand_affine_grid(sp_size, grid=grid, lazy=lazy_) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): # do the transform if do_resampling: - d[key] = self.rand_affine(d[key], None, mode, padding_mode, True, grid, lazy_evaluation=lazy_evaluation_) # type: ignore + d[key] = self.rand_affine(d[key], None, mode, padding_mode, True, grid, lazy=lazy_) # type: ignore else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) self._do_transform = do_resampling # TODO: unify self._do_transform and do_resampling - self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(d[key], replace=True, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: @@ -1391,7 +1391,7 @@ class Flipd(MapTransform, InvertibleTransform, LazyTransform): keys: Keys to pick data for transformation. spatial_axis: Spatial axes along which to flip over. Default is None. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1402,29 +1402,29 @@ def __init__( keys: KeysCollection, spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.flipper = Flip(spatial_axis=spatial_axis) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): - d[key] = self.flipper(d[key], lazy_evaluation=lazy_evaluation_) + d[key] = self.flipper(d[key], lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1446,7 +1446,7 @@ class RandFlipd(RandomizableTransform, MapTransform, InvertibleTransform, LazyTr prob: Probability of flipping. spatial_axis: Spatial axes along which to flip over. Default is None. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1458,25 +1458,25 @@ def __init__( prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.flipper = Flip(spatial_axis=spatial_axis, lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) + self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -1485,13 +1485,13 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool d = dict(data) self.randomize(None) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): if self._do_transform: - d[key] = self.flipper(d[key], lazy_evaluation=lazy_evaluation_) + d[key] = self.flipper(d[key], lazy=lazy_) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) - self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(d[key], replace=True, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1516,7 +1516,7 @@ class RandAxisFlipd(RandomizableTransform, MapTransform, InvertibleTransform, La keys: Keys to pick data for transformation. prob: Probability of flipping. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1527,26 +1527,26 @@ def __init__( keys: KeysCollection, prob: float = 0.1, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.flipper = RandAxisFlip(prob=1.0, lazy_evaluation=lazy_evaluation) - self.lazy_evaluation = lazy_evaluation + self.flipper = RandAxisFlip(prob=1.0, lazy=lazy) + self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) self.flipper.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -1562,13 +1562,13 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool # all the keys share the same random selected axis self.flipper.randomize(d[first_key]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key in self.key_iterator(d): if self._do_transform: - d[key] = self.flipper(d[key], randomize=False, lazy_evaluation=lazy_evaluation_) + d[key] = self.flipper(d[key], randomize=False, lazy=lazy_) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) - self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(d[key], replace=True, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1607,7 +1607,7 @@ class Rotated(MapTransform, InvertibleTransform, LazyTransform): the output data type is always ``float32``. It also can be a sequence of dtype or None, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1623,38 +1623,38 @@ def __init__( align_corners: Sequence[bool] | bool = False, dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) - self.rotator = Rotate(angle=angle, keep_size=keep_size, lazy_evaluation=lazy_evaluation) + self.rotator = Rotate(angle=angle, keep_size=keep_size, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype ): d[key] = self.rotator( d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) return d @@ -1698,7 +1698,7 @@ class RandRotated(RandomizableTransform, MapTransform, InvertibleTransform, Lazy the output data type is always ``float32``. It also can be a sequence of dtype or None, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ @@ -1717,31 +1717,31 @@ def __init__( align_corners: Sequence[bool] | bool = False, dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) self.rand_rotate = RandRotate(range_x=range_x, range_y=range_y, range_z=range_z, prob=1.0, keep_size=keep_size, - lazy_evaluation=lazy_evaluation) + lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) self.rand_rotate.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -1752,7 +1752,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool # all the keys share the same random rotate angle self.rand_rotate.randomize() - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -1765,11 +1765,11 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool align_corners=align_corners, dtype=dtype, randomize=False, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) - self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(d[key], replace=True, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1810,7 +1810,7 @@ class Zoomd(MapTransform, InvertibleTransform, LazyTransform): If None, use the data type of input data. keep_size: Should keep original size (pad if needed), default is True. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -1829,38 +1829,38 @@ def __init__( dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, keep_size: bool = True, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy_evaluation) + LazyTransform.__init__(self, lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, lazy_evaluation=lazy_evaluation, **kwargs) + self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, lazy=lazy, **kwargs) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: a dictionary containing the transformed data, as well as any other data present in the dictionary """ d = dict(data) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype ): d[key] = self.zoomer(d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy_evaluation=lazy_evaluation_) + lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1906,7 +1906,7 @@ class RandZoomd(RandomizableTransform, MapTransform, InvertibleTransform, LazyTr If None, use the data type of input data. keep_size: Should keep original size (pad if needed), default is True. allow_missing_keys: don't raise exception if key is missing. - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False kwargs: other args for `np.pad` API, note that `np.pad` treats channel dimension as the first dimension. more details: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html @@ -1926,32 +1926,32 @@ def __init__( dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, keep_size: bool = True, allow_missing_keys: bool = False, - lazy_evaluation: bool = False, + lazy: bool = False, **kwargs, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) self.rand_zoom = RandZoom(prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, keep_size=keep_size, - lazy_evaluation=lazy_evaluation, **kwargs) + lazy=lazy, **kwargs) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.lazy_evaluation = lazy_evaluation + self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool | None = None) -> dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified in this dictionary must be tensor like arrays that are channel first and have at most three spatial dimensions - lazy_evaluation: a flag to indicate whether this transform should execute lazily or not - during this call. Setting this to False or True overrides the ``lazy_evaluation`` flag set + lazy: a flag to indicate whether this transform should execute lazily or not + during this call. Setting this to False or True overrides the ``lazy`` flag set during initialization for this call. Defaults to None. Returns: @@ -1967,7 +1967,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool # all the keys share the same random zoom factor self.rand_zoom.randomize(d[first_key]) - lazy_evaluation_ = self.lazy_evaluation if lazy_evaluation is None else lazy_evaluation + lazy_ = self.lazy if lazy is None else lazy for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -1980,11 +1980,11 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy_evaluation: bool align_corners=align_corners, dtype=dtype, randomize=False, - lazy_evaluation=lazy_evaluation_, + lazy=lazy_, ) else: d[key] = convert_to_tensor(d[key], track_meta=get_track_meta(), dtype=torch.float32) - self.push_transform(d[key], replace=True, lazy_evaluation=lazy_evaluation_) + self.push_transform(d[key], replace=True, lazy=lazy_) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: diff --git a/monai/transforms/spatial/functional.py b/monai/transforms/spatial/functional.py index 73e98f161e..50e5f6ba1c 100644 --- a/monai/transforms/spatial/functional.py +++ b/monai/transforms/spatial/functional.py @@ -67,12 +67,12 @@ def _maybe_new_metatensor(img, dtype=None, device=None): def spatial_resample( img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, - lazy_evaluation, transform_info + lazy, transform_info ) -> torch.Tensor: """ Functional implementation of resampling the input image to the specified ``dst_affine`` matrix and ``spatial_size``. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be resampled, assuming `img` is channel-first. @@ -93,7 +93,7 @@ def spatial_resample( align_corners: Geometrically, we consider the pixels of the input as squares rather than points. See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html dtype_pt: data `dtype` for resampling computation. - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ original_spatial_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -137,13 +137,13 @@ def spatial_resample( meta_info = TraceableTransform.track_transform_meta( img, sp_size=spatial_size, - affine=None if affine_unchanged and not lazy_evaluation else xform, + affine=None if affine_unchanged and not lazy else xform, extra_info=extra_info, orig_size=original_spatial_shape, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) - if lazy_evaluation: + if lazy: out = _maybe_new_metatensor(img) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info # type: ignore if affine_unchanged: @@ -184,18 +184,18 @@ def spatial_resample( return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore -def orientation(img, original_affine, spatial_ornt, lazy_evaluation, transform_info): +def orientation(img, original_affine, spatial_ornt, lazy, transform_info): """ Functional implementation of changing the input image's orientation into the specified based on `spatial_ornt`. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. original_affine: original affine of the input image. spatial_ornt: orientations of the spatial axes, see also https://nipy.org/nibabel/reference/nibabel.orientations.html - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ spatial_shape = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -218,10 +218,10 @@ def orientation(img, original_affine, spatial_ornt, lazy_evaluation, transform_i extra_info=extra_info, orig_size=spatial_shape, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = _maybe_new_metatensor(img) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info if axes: out = torch.flip(out, dims=axes) @@ -230,11 +230,11 @@ def orientation(img, original_affine, spatial_ornt, lazy_evaluation, transform_i return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def flip(img, sp_axes, lazy_evaluation, transform_info): +def flip(img, sp_axes, lazy, transform_info): """ Functional implementation of flip. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -243,7 +243,7 @@ def flip(img, sp_axes, lazy_evaluation, transform_info): If axis is negative it counts from the last to the first axis. If axis is a tuple of ints, flipping is performed on all of the axes specified in the tuple. - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ sp_size = img.peek_pending_shape() if isinstance(img, MetaTensor) else img.shape[1:] @@ -262,21 +262,21 @@ def flip(img, sp_axes, lazy_evaluation, transform_info): affine=xform, extra_info=extra_info, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = _maybe_new_metatensor(img) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info out = torch.flip(out, axes) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, - lazy_evaluation, transform_info): + lazy, transform_info): """ Functional implementation of resize. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -294,7 +294,7 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, the image to avoid aliasing artifacts. See also ``skimage.transform.resize`` anti_aliasing_sigma: {float, tuple of floats}, optional Standard deviation for Gaussian filtering used when anti-aliasing. - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ img = convert_to_tensor(img, track_meta=get_track_meta()) @@ -312,10 +312,10 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, extra_info=extra_info, orig_size=orig_size, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) - if lazy_evaluation: - if anti_aliasing and lazy_evaluation: + if lazy: + if anti_aliasing and lazy: warnings.warn("anti-aliasing is not compatible with lazy evaluation.") out = _maybe_new_metatensor(img) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info @@ -344,11 +344,11 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, - lazy_evaluation, transform_info): + lazy, transform_info): """ Functional implementation of rotate. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -364,7 +364,7 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, dtype: data type for resampling computation. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ @@ -398,10 +398,10 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, extra_info=extra_info, orig_size=im_shape, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = _maybe_new_metatensor(img) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info xform = AffineTransform( normalized=False, mode=mode, padding_mode=padding_mode, align_corners=align_corners, reverse_indexing=True @@ -415,11 +415,11 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, - lazy_evaluation, transform_info): + lazy, transform_info): """ Functional implementation of zoom. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -437,7 +437,7 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, dtype: data type for resampling computation. If None, use the data type of input data. To be compatible with other modules, the output data type is always ``float32``. - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ @@ -453,9 +453,9 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, } if keep_size: do_pad_crop = not np.allclose(output_size, im_shape) - if do_pad_crop and lazy_evaluation: # update for lazy evaluation + if do_pad_crop and lazy: # update for lazy evaluation _pad_crop = ResizeWithPadOrCrop(spatial_size=im_shape, mode=padding_mode) - _pad_crop.lazy_evaluation = True + _pad_crop.lazy = True _tmp_img = MetaTensor([], affine=torch.eye(len(output_size) + 1)) _tmp_img.push_pending_operation({LazyAttr.SHAPE: list(output_size), LazyAttr.AFFINE: xform}) lazy_cropped = _pad_crop(_tmp_img) @@ -471,10 +471,10 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, extra_info=extra_info, orig_size=im_shape, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = _maybe_new_metatensor(img) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info img_t = out.to(dtype) zoomed: NdarrayOrTensor = torch.nn.functional.interpolate( @@ -498,18 +498,18 @@ def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, return out -def rotate90(img, axes, k, lazy_evaluation, transform_info): +def rotate90(img, axes, k, lazy, transform_info): """ Functional implementation of rotate90. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. If axis is negative it counts from the last to the first axis. k: number of times to rotate by 90 degrees. - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ extra_info = {"axes": [d - 1 for d in axes], "k": k} @@ -539,21 +539,21 @@ def rotate90(img, axes, k, lazy_evaluation, transform_info): extra_info=extra_info, orig_size=ori_shape, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) out = _maybe_new_metatensor(img) - if lazy_evaluation: + if lazy: return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info out = torch.rot90(out, k, axes) return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, - lazy_evaluation, transform_info): + lazy, transform_info): """ Functional implementation of affine. This function operates eagerly or lazily according to - ``lazy_evaluation`` (default ``False``). + ``lazy`` (default ``False``). Args: img: data to be changed, assuming `img` is channel-first. @@ -577,7 +577,7 @@ def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_re do_resampling: whether to do the resampling, this is a flag for the use case of updating metadata but skipping the actual (potentially heavy) resampling operation. image_only: if True return only the image volume, otherwise return (image, affine). - lazy_evaluation: a flag that indicates whether the operation should be performed lazily or not + lazy: a flag that indicates whether the operation should be performed lazily or not transform_info: a dictionary with the relevant information pertaining to an applied transform. """ @@ -600,9 +600,9 @@ def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_re extra_info=extra_info, orig_size=img_size, transform_info=transform_info, - lazy_evaluation=lazy_evaluation, + lazy=lazy, ) - if lazy_evaluation: + if lazy: out = _maybe_new_metatensor(img) out = out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info return out if image_only else (out, affine) diff --git a/monai/transforms/traits.py b/monai/transforms/traits.py index 0193065562..1d8436a1f2 100644 --- a/monai/transforms/traits.py +++ b/monai/transforms/traits.py @@ -27,18 +27,18 @@ class LazyTrait: """ @property - def lazy_evaluation(self): + def lazy(self): """ - Get whether lazy_evaluation is enabled for this transform instance. + Get whether lazy evaluation is enabled for this transform instance. Returns: True if the transform is operating in a lazy fashion, False if not. """ raise NotImplementedError() - @lazy_evaluation.setter - def lazy_evaluation(self, enabled: bool): + @lazy.setter + def lazy(self, enabled: bool): """ - Set whether lazy_evaluation is enabled for this transform instance. + Set whether lazy evaluation is enabled for this transform instance. Args: enabled: True if the transform should operate in a lazy fashion, False if not. """ diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 5554158a70..8bea2575ed 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -48,7 +48,7 @@ def _apply_transform( transform: Callable[..., ReturnType], data: Any, unpack_parameters: bool = False, - lazy_evaluation: str = LazyMode.OFF, + lazy: str = LazyMode.OFF, overrides: dict = None, ) -> ReturnType: """ @@ -72,20 +72,20 @@ def _apply_transform( # is because the transform implementations for 1.2 don't have unified code paths for # lazy and non-lazy operation, so it is not possible to pass a tensor with pending # operations and have the transform handle them correctly. - # In order to have this functionality for 1.2, we need to provide lazy_evaluation + # In order to have this functionality for 1.2, we need to provide lazy # overrides on __call__ methods for lazy array and dictionary transforms. lazy_tx = isinstance(transform, LazyTrait) - if lazy_tx is False or lazy_evaluation == LazyMode.OFF: + if lazy_tx is False or lazy == LazyMode.OFF: data = execute_pending_transforms(data, overrides) - elif lazy_evaluation is LazyMode.ENABLED and transform.lazy_evaluation is False: + elif lazy is LazyMode.ENABLED and transform.lazy is False: data = execute_pending_transforms(data, overrides) if isinstance(data, tuple) and unpack_parameters: - return transform(*data, lazy_evaluation=LazyMode.as_bool(lazy_evaluation)) if lazy_tx else transform(*data) + return transform(*data, lazy=LazyMode.as_bool(lazy)) if lazy_tx else transform(*data) - return transform(data, lazy_evaluation=LazyMode.as_bool(lazy_evaluation)) if lazy_tx else transform(data) + return transform(data, lazy=LazyMode.as_bool(lazy)) if lazy_tx else transform(data) def apply_transform( @@ -93,7 +93,7 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, - lazy_evaluation: LazyMode = LazyMode.OFF, + lazy: LazyMode = LazyMode.OFF, overrides: dict = {}, ) -> list[ReturnType] | ReturnType: """ @@ -121,9 +121,9 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items, lazy_evaluation, overrides) + return [_apply_transform(transform, item, unpack_items, lazy, overrides) for item in data] - return _apply_transform(transform, data, unpack_items, lazy_evaluation, overrides) + return _apply_transform(transform, data, unpack_items, lazy, overrides) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint # appears where the exception was raised. @@ -277,18 +277,18 @@ class LazyTransform(Transform, LazyTrait): dictionary transforms to simplify implementation of new lazy transforms. """ - def __init__(self, lazy_evaluation: bool = False): - self._lazy_evaluation = lazy_evaluation + def __init__(self, lazy: bool = False): + self._lazy = lazy @property - def lazy_evaluation(self): - return self._lazy_evaluation - - @lazy_evaluation.setter - def lazy_evaluation(self, lazy_evaluation: bool): - if not isinstance(lazy_evaluation, bool): - raise TypeError(f"lazy_evaluation must be a bool but is of type {type(lazy_evaluation)}") - self._lazy_evaluation = lazy_evaluation + def lazy(self): + return self._lazy + + @lazy.setter + def lazy(self, lazy: bool): + if not isinstance(lazy, bool): + raise TypeError(f"lazy must be a bool but is of type {type(lazy)}") + self._lazy = lazy class RandomizableTransform(Randomizable, Transform): diff --git a/monai/utils/enums.py b/monai/utils/enums.py index bee79c5c73..17e963b687 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -317,7 +317,7 @@ class TraceKeys(StrEnum): KEY_SUFFIX: str = "_transforms" NONE: str = "none" TRACING: str = "tracing" - LAZY_EVALUATION: str = "lazy_evaluation" + LAZY: str = "lazy" class CommonKeys(StrEnum): @@ -654,7 +654,7 @@ class LazyMode(StrEnum): that can execute lazily are executed by the pipeline: 'OFF' indicates that the pipeline should not be executed lazily 'ENABLED' indicates that the pipeline can be executed lazily, but this will only be done for transforms - that have ``lazy_evaluation`` set to True + that have ``lazy`` set to True 'ON' indicates that all transforms capable of being executed lazily will be executed lazily See: :py:class: monai.transforms.compose.Compose for more details. """ diff --git a/tests/croppers.py b/tests/croppers.py index acc21307d4..9c3a5085a2 100644 --- a/tests/croppers.py +++ b/tests/croppers.py @@ -117,7 +117,7 @@ def crop_test_pending_ops(self, input_param, input_shape, align_corners=False): expected = result_non_lazy["img"] if is_map else result_non_lazy self.assertIsInstance(expected, MetaTensor) # lazy - crop_fn.lazy_evaluation = True + crop_fn.lazy = True pending_result = crop_fn(input_data) pending_result = pending_result["img"] if is_map else pending_result self.assertIsInstance(pending_result, MetaTensor) @@ -150,7 +150,7 @@ def crop_test_combine_ops(self, funcs, input_shape): # lazy pending_result = input_data for _func in _funcs: - _func.lazy_evaluation = True + _func.lazy = True if isinstance(_func, Randomizable): _func.set_random_state(seed=123) pending_result = _func(pending_result) diff --git a/tests/lazy_transforms_utils.py b/tests/lazy_transforms_utils.py index cf64079c0f..cadb33b1b0 100644 --- a/tests/lazy_transforms_utils.py +++ b/tests/lazy_transforms_utils.py @@ -61,7 +61,7 @@ def test_resampler_lazy( if isinstance(resampler, Randomizable): resampler.set_random_state(seed=seed) set_track_meta(True) - resampler.lazy_evaluation = True + resampler.lazy = True pending_output = resampler(**call_param) if output_idx is not None: expected_output, pending_output = expected_output[output_idx], pending_output[output_idx] diff --git a/tests/padders.py b/tests/padders.py index 419831f30c..61bd4b28d0 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -127,7 +127,7 @@ def pad_test_pending_ops(self, input_param, input_shape): expected = result_non_lazy["img"] if is_map else result_non_lazy self.assertIsInstance(expected, MetaTensor) # lazy - pad_fn.lazy_evaluation = True + pad_fn.lazy = True pending_result = pad_fn(input_data) pending_result = pending_result["img"] if is_map else pending_result self.assertIsInstance(pending_result, MetaTensor) @@ -157,7 +157,7 @@ def pad_test_combine_ops(self, funcs, input_shape, expected_shape): # lazy pending_result = input_data for _func in _funcs: - _func.lazy_evaluation = True + _func.lazy = True pending_result = _func(pending_result) pending_result = pending_result["img"] if is_map else pending_result self.assertIsInstance(pending_result, MetaTensor) diff --git a/tests/test_affine.py b/tests/test_affine.py index 85eb1def6c..a824df8f2b 100644 --- a/tests/test_affine.py +++ b/tests/test_affine.py @@ -208,16 +208,20 @@ def test_affine_resize(self, s): def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=sp_size) - xform.lazy_evaluation = True + xform.lazy = True out = xform(im) - out = apply_pending(out, padding_mode="border", align_corners=ac)[0] + overrides = {'padding_mode': "border", 'align_corners': ac} + out = apply_pending(out, overrides=overrides)[0] + # out = apply_pending(out, padding_mode="border", align_corners=ac)[0] return out def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=sp_size) - xform.lazy_evaluation = True + xform.lazy = True out = xform(im) - out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] + overrides = {'mode': 1, 'padding_mode': "nearest", 'align_corners': ac} + out = apply_pending(out, overrides=overrides)[0] + # out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] return out def method_2(im, ac): diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index 9a123622a2..beca2fb820 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -126,7 +126,7 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): expected = crop_fn(image) self.assertIsInstance(expected, MetaTensor) # lazy - crop_fn.lazy_evaluation = True + crop_fn.lazy = True pending_result = crop_fn(image) self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) @@ -142,7 +142,7 @@ def test_lazy_error(self, input_param, image, _expected_data, align_corners): with self.assertRaises(ValueError): crop_fn = CropForeground(**input_param) # lazy - crop_fn.lazy_evaluation = True + crop_fn.lazy = True pending_result = crop_fn(image) return apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] diff --git a/tests/test_crop_foregroundd.py b/tests/test_crop_foregroundd.py index 9e810319f7..e17321a1a1 100644 --- a/tests/test_crop_foregroundd.py +++ b/tests/test_crop_foregroundd.py @@ -189,7 +189,7 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): expected = crop_fn(image)["img"] self.assertIsInstance(expected, MetaTensor) # lazy - crop_fn.lazy_evaluation = True + crop_fn.lazy = True pending_result = crop_fn(image)["img"] self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 9300d264d6..136aad2111 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -70,7 +70,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, mt.ResizeWithPadOrCropD(keys=["img", "seg"], spatial_size=[80, 72, 80]), mt.Rotated(keys=["img", "seg"], angle=[np.pi / 2, np.pi / 2, 0], mode="nearest", keep_size=False), ], - lazy_evaluation=lazy, + lazy=lazy, overrides=lazy_kwargs, ) diff --git a/tests/test_rand_affined.py b/tests/test_rand_affined.py index 5c1e2359e8..a607029c1a 100644 --- a/tests/test_rand_affined.py +++ b/tests/test_rand_affined.py @@ -234,7 +234,7 @@ def test_rand_affined(self, input_param, input_data, expected_val, track_meta): resampler = RandAffined(**lazy_init_param).set_random_state(123) expected_output = resampler(**call_param) test_resampler_lazy(resampler, expected_output, lazy_init_param, call_param, seed=123, output_key=key) - resampler.lazy_evaluation = False + resampler.lazy = False if input_param.get("cache_grid", False): self.assertTrue(g.rand_affine._cached_grid is not None) diff --git a/tests/test_rand_axis_flip.py b/tests/test_rand_axis_flip.py index 457617fc19..81e42372db 100644 --- a/tests/test_rand_axis_flip.py +++ b/tests/test_rand_axis_flip.py @@ -33,7 +33,7 @@ def test_correct_results(self): # test lazy test_resampler_lazy(flip, result, call_param=call_param, seed=321) - flip.lazy_evaluation = False + flip.lazy = False expected = [np.flip(channel, flip._axis) for channel in self.imt[0]] assert_allclose(result, p(np.stack(expected)), type_test="tensor") diff --git a/tests/test_rand_axis_flipd.py b/tests/test_rand_axis_flipd.py index e6fac5637f..75357b23e1 100644 --- a/tests/test_rand_axis_flipd.py +++ b/tests/test_rand_axis_flipd.py @@ -33,7 +33,7 @@ def test_correct_results(self): # test lazy test_resampler_lazy(flip, result, call_param=call_param, output_key="img", seed=1234) - flip.lazy_evaluation = False + flip.lazy = False test_local_inversion(flip, result, {"img": im}, "img") expected = [np.flip(channel, flip.flipper._axis) for channel in self.imt[0]] diff --git a/tests/test_rand_crop_by_label_classes.py b/tests/test_rand_crop_by_label_classes.py index c16e0c89f4..4613b36b34 100644 --- a/tests/test_rand_crop_by_label_classes.py +++ b/tests/test_rand_crop_by_label_classes.py @@ -154,7 +154,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh self.assertIsInstance(expected[0], MetaTensor) # lazy cropper.set_random_state(0) - cropper.lazy_evaluation = True + cropper.lazy = True pending_result = cropper(**input_data) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result, MetaTensor) diff --git a/tests/test_rand_crop_by_label_classesd.py b/tests/test_rand_crop_by_label_classesd.py index 11b0228274..d4590fe26e 100644 --- a/tests/test_rand_crop_by_label_classesd.py +++ b/tests/test_rand_crop_by_label_classesd.py @@ -143,7 +143,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh self.assertIsInstance(expected[0]["img"], MetaTensor) # lazy cropper.set_random_state(0) - cropper.lazy_evaluation = True + cropper.lazy = True pending_result = cropper(input_data) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result["img"], MetaTensor) diff --git a/tests/test_rand_crop_by_pos_neg_label.py b/tests/test_rand_crop_by_pos_neg_label.py index b8371c1411..633c2b40ca 100644 --- a/tests/test_rand_crop_by_pos_neg_label.py +++ b/tests/test_rand_crop_by_pos_neg_label.py @@ -136,7 +136,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): self.assertIsInstance(expected[0], MetaTensor) # lazy cropper.set_random_state(0) - cropper.lazy_evaluation = True + cropper.lazy = True pending_result = cropper(**input_data_mod) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result, MetaTensor) diff --git a/tests/test_rand_crop_by_pos_neg_labeld.py b/tests/test_rand_crop_by_pos_neg_labeld.py index 4132714aa9..120ce88f8d 100644 --- a/tests/test_rand_crop_by_pos_neg_labeld.py +++ b/tests/test_rand_crop_by_pos_neg_labeld.py @@ -153,7 +153,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): self.assertIsInstance(expected[0]["image"], MetaTensor) # lazy cropper.set_random_state(0) - cropper.lazy_evaluation = True + cropper.lazy = True pending_result = cropper(input_data_mod) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result["image"], MetaTensor) diff --git a/tests/test_rand_flipd.py b/tests/test_rand_flipd.py index d67b4ca31b..be5394c172 100644 --- a/tests/test_rand_flipd.py +++ b/tests/test_rand_flipd.py @@ -37,7 +37,7 @@ def test_correct_results(self, _, spatial_axis): # test lazy test_resampler_lazy(flip, result, init_param, call_param, output_key="img") - flip.lazy_evaluation = False + flip.lazy = False expected = [np.flip(channel, spatial_axis) for channel in self.imt[0]] expected = np.stack(expected) diff --git a/tests/test_rand_rotate.py b/tests/test_rand_rotate.py index 8bd697efe5..ca3eda3b12 100644 --- a/tests/test_rand_rotate.py +++ b/tests/test_rand_rotate.py @@ -91,7 +91,7 @@ def test_correct_results(self, im_type, degrees, keep_size, mode, padding_mode, # test lazy test_resampler_lazy(rotate_fn, rotated, init_param=init_param, call_param=call_param, seed=243) - rotate_fn.lazy_evaluation = False + rotate_fn.lazy = False _order = 0 if mode == "nearest" else 1 if mode == "border": @@ -133,7 +133,7 @@ def test_correct_results(self, im_type, x, y, z, keep_size, mode, padding_mode, # test lazy test_resampler_lazy(rotate_fn, rotated, init_param=init_param, call_param=call_param, seed=243) - rotate_fn.lazy_evaluation = False + rotate_fn.lazy = False assert_allclose(rotated.shape, expected, rtol=1e-7, atol=0) test_local_inversion(rotate_fn, rotated, im) diff --git a/tests/test_rand_rotate90.py b/tests/test_rand_rotate90.py index 2504c0f01b..88f88bf422 100644 --- a/tests/test_rand_rotate90.py +++ b/tests/test_rand_rotate90.py @@ -37,7 +37,7 @@ def test_default(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=123) - rotate.lazy_evaluation = False + rotate.lazy = False def test_k(self): init_param = {"max_k": 2} @@ -60,7 +60,7 @@ def test_k(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=123) - rotate.lazy_evaluation = False + rotate.lazy = False def test_spatial_axes(self): rotate = RandRotate90(spatial_axes=(0, 1), prob=1.0) @@ -71,7 +71,7 @@ def test_spatial_axes(self): rotated = rotate(**call_param) # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=1234) - rotate.lazy_evaluation = False + rotate.lazy = False self.assertEqual(len(rotated.applied_operations), 1) expected = [np.rot90(channel, rotate._rand_k, (0, 1)) for channel in self.imt[0]] @@ -88,7 +88,7 @@ def test_prob_k_spatial_axes(self): rotated = rotate(**call_param) # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=234) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 1, (0, 1)) for channel in self.imt[0]] diff --git a/tests/test_rand_rotate90d.py b/tests/test_rand_rotate90d.py index f811f1a6a6..23e9025c08 100644 --- a/tests/test_rand_rotate90d.py +++ b/tests/test_rand_rotate90d.py @@ -34,7 +34,7 @@ def test_default(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=1323, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im, key) expected = [np.rot90(channel, 0, (0, 1)) for channel in self.imt[0]] @@ -58,7 +58,7 @@ def test_k(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=234, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im, key) expected = [np.rot90(channel, 0, (0, 1)) for channel in self.imt[0]] @@ -76,7 +76,7 @@ def test_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=234, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im, key) expected = [np.rot90(channel, 0, (0, 1)) for channel in self.imt[0]] @@ -94,7 +94,7 @@ def test_prob_k_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, seed=234, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False expected = [np.rot90(channel, 1, (0, 1)) for channel in self.imt[0]] expected = np.stack(expected) diff --git a/tests/test_rand_spatial_crop.py b/tests/test_rand_spatial_crop.py index a4eb22c549..1fe19a0797 100644 --- a/tests/test_rand_spatial_crop.py +++ b/tests/test_rand_spatial_crop.py @@ -84,7 +84,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): # lazy # reset random seed to ensure the same results cropper.set_random_state(seed=123) - cropper.lazy_evaluation = True + cropper.lazy = True pending_result = cropper(input_data) self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) diff --git a/tests/test_rand_spatial_crop_samples.py b/tests/test_rand_spatial_crop_samples.py index c385fc9c47..4b4cb35902 100644 --- a/tests/test_rand_spatial_crop_samples.py +++ b/tests/test_rand_spatial_crop_samples.py @@ -112,7 +112,7 @@ def test_pending_ops(self, input_param, input_shape, _expected_shape, _expected_ self.assertIsInstance(expected[0], MetaTensor) # lazy xform.set_random_state(1234) - xform.lazy_evaluation = True + xform.lazy = True pending_result = xform(image) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result, MetaTensor) diff --git a/tests/test_rand_spatial_crop_samplesd.py b/tests/test_rand_spatial_crop_samplesd.py index 7b629cba72..287a0c62a4 100644 --- a/tests/test_rand_spatial_crop_samplesd.py +++ b/tests/test_rand_spatial_crop_samplesd.py @@ -122,7 +122,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape, _expected_l # lazy xform.set_random_state(1234) - xform.lazy_evaluation = True + xform.lazy = True pending_result = xform(input_data) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result["img"], MetaTensor) diff --git a/tests/test_rand_spatial_cropd.py b/tests/test_rand_spatial_cropd.py index a45e3f3bbe..6fe0a68087 100644 --- a/tests/test_rand_spatial_cropd.py +++ b/tests/test_rand_spatial_cropd.py @@ -89,7 +89,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): # lazy # reset random seed to ensure the same results cropper.set_random_state(seed=123) - cropper.lazy_evaluation = True + cropper.lazy = True pending_result = cropper(input_data)["img"] self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) diff --git a/tests/test_rand_weighted_crop.py b/tests/test_rand_weighted_crop.py index 9b96656d5e..a8bd050a41 100644 --- a/tests/test_rand_weighted_crop.py +++ b/tests/test_rand_weighted_crop.py @@ -178,7 +178,7 @@ def test_pending_ops(self, _, input_param, img, weight, expected_shape, expected self.assertIsInstance(expected[0], MetaTensor) # lazy crop.set_random_state(10) - crop.lazy_evaluation = True + crop.lazy = True pending_result = crop(img, weight) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result, MetaTensor) diff --git a/tests/test_rand_weighted_cropd.py b/tests/test_rand_weighted_cropd.py index 159e07c61f..af6c886d76 100644 --- a/tests/test_rand_weighted_cropd.py +++ b/tests/test_rand_weighted_cropd.py @@ -166,7 +166,7 @@ def test_pending_ops(self, _, input_param, input_data, expected_shape, expected_ self.assertIsInstance(expected[0]["img"], MetaTensor) # lazy crop.set_random_state(10) - crop.lazy_evaluation = True + crop.lazy = True pending_result = crop(input_data) for i, _pending_result in enumerate(pending_result): self.assertIsInstance(_pending_result["img"], MetaTensor) diff --git a/tests/test_rand_zoomd.py b/tests/test_rand_zoomd.py index f080056b63..bb0495c793 100644 --- a/tests/test_rand_zoomd.py +++ b/tests/test_rand_zoomd.py @@ -58,7 +58,7 @@ def test_correct_results(self, min_zoom, max_zoom, mode, align_corners, keep_siz test_resampler_lazy( random_zoom, zoomed, init_param, call_param, key, seed=1234, atol=1e-4 if USE_COMPILED else 1e-6 ) - random_zoom.lazy_evaluation = False + random_zoom.lazy = False test_local_inversion(random_zoom, zoomed, {key: im}, key) expected = [ diff --git a/tests/test_resize_with_pad_or_crop.py b/tests/test_resize_with_pad_or_crop.py index 2d543686d9..0652799142 100644 --- a/tests/test_resize_with_pad_or_crop.py +++ b/tests/test_resize_with_pad_or_crop.py @@ -79,7 +79,7 @@ def test_pending_ops(self, input_param, input_shape, _expected_data, align_corne expected = padcropper(image) self.assertIsInstance(expected, MetaTensor) # lazy - padcropper.lazy_evaluation = True + padcropper.lazy = True pending_result = padcropper(image) self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) diff --git a/tests/test_resize_with_pad_or_cropd.py b/tests/test_resize_with_pad_or_cropd.py index f44bbe7686..2a7b9f6252 100644 --- a/tests/test_resize_with_pad_or_cropd.py +++ b/tests/test_resize_with_pad_or_cropd.py @@ -74,7 +74,7 @@ def test_pending_ops(self, input_param, input_data, _expected_data): expected = padcropper(input_data)["img"] self.assertIsInstance(expected, MetaTensor) # lazy - padcropper.lazy_evaluation = True + padcropper.lazy = True pending_result = padcropper(input_data)["img"] self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) diff --git a/tests/test_rotate90.py b/tests/test_rotate90.py index 01e93870fa..62dcfc37c9 100644 --- a/tests/test_rotate90.py +++ b/tests/test_rotate90.py @@ -41,7 +41,7 @@ def test_rotate90_default(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 1, (0, 1)) for channel in self.imt[0]] @@ -61,7 +61,7 @@ def test_k(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 2, (0, 1)) for channel in self.imt[0]] @@ -77,7 +77,7 @@ def test_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 1, (0, -1)) for channel in self.imt[0]] @@ -93,7 +93,7 @@ def test_prob_k_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 2, (0, 1)) for channel in self.imt[0]] @@ -111,7 +111,7 @@ def test_rotate90_default(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 1, (0, 1)) for channel in self.imt[0]] @@ -127,7 +127,7 @@ def test_k(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 2, (0, 1)) for channel in self.imt[0]] @@ -143,7 +143,7 @@ def test_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 1, (0, -1)) for channel in self.imt[0]] @@ -159,7 +159,7 @@ def test_prob_k_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, im) expected = [np.rot90(channel, 2, (0, 1)) for channel in self.imt[0]] @@ -177,14 +177,14 @@ def test_affine_rot90(self, s): def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) - xform.lazy_evaluation = True + xform.lazy = True out = xform(im) out = apply_pending(out, padding_mode="border", align_corners=ac)[0] return out def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) - xform.lazy_evaluation = True + xform.lazy = True out = xform(im) out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] return out diff --git a/tests/test_rotate90d.py b/tests/test_rotate90d.py index 95d475d480..08d3a97498 100644 --- a/tests/test_rotate90d.py +++ b/tests/test_rotate90d.py @@ -33,7 +33,7 @@ def test_rotate90_default(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, {key: im}, key) expected = [np.rot90(channel, 1, (0, 1)) for channel in self.imt[0]] @@ -54,7 +54,7 @@ def test_k(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, {key: im}, key) expected = [np.rot90(channel, 2, (0, 1)) for channel in self.imt[0]] @@ -71,7 +71,7 @@ def test_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, {key: im}, key) expected = [np.rot90(channel, 1, (0, 1)) for channel in self.imt[0]] @@ -88,7 +88,7 @@ def test_prob_k_spatial_axes(self): # test lazy test_resampler_lazy(rotate, rotated, call_param=call_param, output_key=key) - rotate.lazy_evaluation = False + rotate.lazy = False test_local_inversion(rotate, rotated, {key: im}, key) expected = [np.rot90(channel, 2, (0, 1)) for channel in self.imt[0]] diff --git a/tests/test_spatial_combine_transforms.py b/tests/test_spatial_combine_transforms.py index 6c554d469a..1b9bfa04fa 100644 --- a/tests/test_spatial_combine_transforms.py +++ b/tests/test_spatial_combine_transforms.py @@ -162,7 +162,7 @@ def test_combine_transforms(self, input_shape, funcs): # lazy pending_result = input_data for _func in _funcs: - _func.lazy_evaluation = True + _func.lazy = True if isinstance(_func, mt.Randomizable): _func.set_random_state(seed=seed) pending_result = _func(pending_result) diff --git a/tests/test_zoom.py b/tests/test_zoom.py index 0f2eca888e..43a756d829 100644 --- a/tests/test_zoom.py +++ b/tests/test_zoom.py @@ -52,7 +52,7 @@ def test_pending_ops(self, zoom, mode, align_corners=False, keep_size=False): expected = zoom_fn(im) self.assertIsInstance(expected, MetaTensor) # lazy - zoom_fn.lazy_evaluation = True + zoom_fn.lazy = True pending_result = zoom_fn(im) self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) diff --git a/tests/test_zoomd.py b/tests/test_zoomd.py index a76e43a6b4..1dcbf98572 100644 --- a/tests/test_zoomd.py +++ b/tests/test_zoomd.py @@ -57,7 +57,7 @@ def test_correct_results(self, zoom, mode, keep_size, align_corners=None): test_resampler_lazy( zoom_fn, zoomed, init_param, call_param, output_key=key, atol=1e-4 if USE_COMPILED else 1e-6 ) - zoom_fn.lazy_evaluation = False + zoom_fn.lazy = False test_local_inversion(zoom_fn, zoomed, {key: im}, key) _order = 0 From 2262ba93389d9db25e3523528839e4e9bda18abc Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sun, 23 Apr 2023 19:34:40 +0100 Subject: [PATCH 027/175] Fixed all outstanding usages of apply_pending in tests. Removed the **kwargs parameter from apply_pending Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 1 - tests/croppers.py | 6 ++++-- tests/test_affine.py | 2 -- tests/test_crop_foreground.py | 6 ++++-- tests/test_crop_foregroundd.py | 3 ++- tests/test_rand_crop_by_label_classes.py | 2 +- tests/test_rand_crop_by_label_classesd.py | 2 +- tests/test_rand_crop_by_pos_neg_label.py | 2 +- tests/test_rand_crop_by_pos_neg_labeld.py | 5 +++-- tests/test_rand_spatial_crop.py | 2 +- tests/test_rand_spatial_crop_samples.py | 2 +- tests/test_rand_spatial_crop_samplesd.py | 5 +++-- tests/test_rand_spatial_cropd.py | 2 +- tests/test_rand_weighted_crop.py | 2 +- tests/test_rand_weighted_cropd.py | 2 +- tests/test_resize_with_pad_or_crop.py | 9 +++------ tests/test_resize_with_pad_or_cropd.py | 5 ++--- tests/test_rotate90.py | 4 ++-- tests/test_spatial_combine_transforms.py | 2 +- 19 files changed, 32 insertions(+), 32 deletions(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index bab2d6fa04..ab9399e12c 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -55,7 +55,6 @@ def apply_pending( data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None, - **kwargs ): """ This method applies pending transforms to `data` tensors. diff --git a/tests/croppers.py b/tests/croppers.py index 9c3a5085a2..17cd448940 100644 --- a/tests/croppers.py +++ b/tests/croppers.py @@ -124,7 +124,8 @@ def crop_test_pending_ops(self, input_param, input_shape, align_corners=False): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] + overrides = {'mode': "nearest", 'align_corners': align_corners} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -159,7 +160,8 @@ def crop_test_combine_ops(self, funcs, input_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - result = apply_pending(pending_result, mode="nearest", align_corners=False)[0] + overrides = {'mode': "nearest", 'align_corners': False} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_affine.py b/tests/test_affine.py index a824df8f2b..f3b65feaa2 100644 --- a/tests/test_affine.py +++ b/tests/test_affine.py @@ -212,7 +212,6 @@ def method_0(im, ac): out = xform(im) overrides = {'padding_mode': "border", 'align_corners': ac} out = apply_pending(out, overrides=overrides)[0] - # out = apply_pending(out, padding_mode="border", align_corners=ac)[0] return out def method_1(im, ac): @@ -221,7 +220,6 @@ def method_1(im, ac): out = xform(im) overrides = {'mode': 1, 'padding_mode': "nearest", 'align_corners': ac} out = apply_pending(out, overrides=overrides)[0] - # out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] return out def method_2(im, ac): diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index beca2fb820..35d18d9c86 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -132,7 +132,8 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] + overrides = {'mode': "nearest", 'align_corners': align_corners} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -144,7 +145,8 @@ def test_lazy_error(self, input_param, image, _expected_data, align_corners): # lazy crop_fn.lazy = True pending_result = crop_fn(image) - return apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] + overrides = {'mode': "nearest", 'align_corners': align_corners} + return apply_pending(pending_result, overrides=overrides)[0] if __name__ == "__main__": diff --git a/tests/test_crop_foregroundd.py b/tests/test_crop_foregroundd.py index e17321a1a1..b2f59b2da9 100644 --- a/tests/test_crop_foregroundd.py +++ b/tests/test_crop_foregroundd.py @@ -195,7 +195,8 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, mode="nearest", align_corners=align_corners)[0] + overrides = {'mode': "nearest", 'align_corners': align_corners} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_crop_by_label_classes.py b/tests/test_rand_crop_by_label_classes.py index 4613b36b34..a17977d1ea 100644 --- a/tests/test_rand_crop_by_label_classes.py +++ b/tests/test_rand_crop_by_label_classes.py @@ -161,7 +161,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_crop_by_label_classesd.py b/tests/test_rand_crop_by_label_classesd.py index d4590fe26e..21eb7095d1 100644 --- a/tests/test_rand_crop_by_label_classesd.py +++ b/tests/test_rand_crop_by_label_classesd.py @@ -150,7 +150,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result = apply_pending(_pending_result["img"], mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result["img"], overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected[i]["img"], rtol=1e-5) diff --git a/tests/test_rand_crop_by_pos_neg_label.py b/tests/test_rand_crop_by_pos_neg_label.py index 633c2b40ca..b0b285500a 100644 --- a/tests/test_rand_crop_by_pos_neg_label.py +++ b/tests/test_rand_crop_by_pos_neg_label.py @@ -143,7 +143,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_crop_by_pos_neg_labeld.py b/tests/test_rand_crop_by_pos_neg_labeld.py index 120ce88f8d..8ec184ca46 100644 --- a/tests/test_rand_crop_by_pos_neg_labeld.py +++ b/tests/test_rand_crop_by_pos_neg_labeld.py @@ -160,8 +160,9 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): assert_allclose(_pending_result["image"].peek_pending_affine(), expected[i]["image"].affine) assert_allclose(_pending_result["image"].peek_pending_shape(), expected[i]["image"].shape[1:]) # only support nearest - result_image = apply_pending(_pending_result["image"], mode="nearest", align_corners=False)[0] - result_extra = apply_pending(_pending_result["extra"], mode="nearest", align_corners=False)[0] + overrides = {'mode': "nearest", 'align_corners': False} + result_image = apply_pending(_pending_result["image"], overrides=overrides)[0] + result_extra = apply_pending(_pending_result["extra"], overrides=overrides)[0] # compare assert_allclose(result_image, expected[i]["image"], rtol=1e-5) assert_allclose(result_extra, expected[i]["extra"], rtol=1e-5) diff --git a/tests/test_rand_spatial_crop.py b/tests/test_rand_spatial_crop.py index 1fe19a0797..a11aca9f6f 100644 --- a/tests/test_rand_spatial_crop.py +++ b/tests/test_rand_spatial_crop.py @@ -90,7 +90,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_spatial_crop_samples.py b/tests/test_rand_spatial_crop_samples.py index 4b4cb35902..d9b21a9926 100644 --- a/tests/test_rand_spatial_crop_samples.py +++ b/tests/test_rand_spatial_crop_samples.py @@ -119,7 +119,7 @@ def test_pending_ops(self, input_param, input_shape, _expected_shape, _expected_ assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_spatial_crop_samplesd.py b/tests/test_rand_spatial_crop_samplesd.py index 287a0c62a4..c0051f684e 100644 --- a/tests/test_rand_spatial_crop_samplesd.py +++ b/tests/test_rand_spatial_crop_samplesd.py @@ -129,8 +129,9 @@ def test_pending_ops(self, input_param, input_data, _expected_shape, _expected_l assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result_img = apply_pending(_pending_result["img"], mode="nearest", align_corners=False)[0] - result_seg = apply_pending(_pending_result["seg"], mode="nearest", align_corners=False)[0] + overrides = {'mode': "nearest", 'align_corners': False} + result_img = apply_pending(_pending_result["img"], overrides=overrides)[0] + result_seg = apply_pending(_pending_result["seg"], overrides=overrides)[0] # compare assert_allclose(result_img, expected[i]["img"], rtol=1e-5) assert_allclose(result_seg, expected[i]["seg"], rtol=1e-5) diff --git a/tests/test_rand_spatial_cropd.py b/tests/test_rand_spatial_cropd.py index 6fe0a68087..3abc7e8683 100644 --- a/tests/test_rand_spatial_cropd.py +++ b/tests/test_rand_spatial_cropd.py @@ -95,7 +95,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_weighted_crop.py b/tests/test_rand_weighted_crop.py index a8bd050a41..4a4526ff94 100644 --- a/tests/test_rand_weighted_crop.py +++ b/tests/test_rand_weighted_crop.py @@ -185,7 +185,7 @@ def test_pending_ops(self, _, input_param, img, weight, expected_shape, expected assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_weighted_cropd.py b/tests/test_rand_weighted_cropd.py index af6c886d76..f16715d213 100644 --- a/tests/test_rand_weighted_cropd.py +++ b/tests/test_rand_weighted_cropd.py @@ -173,7 +173,7 @@ def test_pending_ops(self, _, input_param, input_data, expected_shape, expected_ assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result = apply_pending(_pending_result["img"], mode="nearest", align_corners=False)[0] + result = apply_pending(_pending_result["img"], overrides={'mode': "nearest", 'align_corners': False})[0] # compare assert_allclose(result, expected[i]["img"], rtol=1e-5) diff --git a/tests/test_resize_with_pad_or_crop.py b/tests/test_resize_with_pad_or_crop.py index 0652799142..313f7298ac 100644 --- a/tests/test_resize_with_pad_or_crop.py +++ b/tests/test_resize_with_pad_or_crop.py @@ -85,12 +85,9 @@ def test_pending_ops(self, input_param, input_shape, _expected_data, align_corne assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending( - pending_result, - mode="nearest", - padding_mode=TESTS_PENDING_MODE[input_param["mode"]], - align_corners=align_corners, - )[0] + overrides = {'mode': "nearest", 'padding_mode': TESTS_PENDING_MODE[input_param["mode"]], + 'align_corners': align_corners} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_resize_with_pad_or_cropd.py b/tests/test_resize_with_pad_or_cropd.py index 2a7b9f6252..f0bd712b6a 100644 --- a/tests/test_resize_with_pad_or_cropd.py +++ b/tests/test_resize_with_pad_or_cropd.py @@ -80,9 +80,8 @@ def test_pending_ops(self, input_param, input_data, _expected_data): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending( - pending_result, mode="nearest", padding_mode=TESTS_PENDING_MODE[input_param["mode"]], align_corners=True - )[0] + overrides = {'mode': "nearest", 'padding_mode': TESTS_PENDING_MODE[input_param["mode"]], 'align_corners': True} + result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rotate90.py b/tests/test_rotate90.py index 62dcfc37c9..d8113b4d37 100644 --- a/tests/test_rotate90.py +++ b/tests/test_rotate90.py @@ -179,14 +179,14 @@ def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) xform.lazy = True out = xform(im) - out = apply_pending(out, padding_mode="border", align_corners=ac)[0] + out = apply_pending(out, overrides={'padding_mode': "border", 'align_corners': ac})[0] return out def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) xform.lazy = True out = xform(im) - out = apply_pending(out, mode=1, padding_mode="nearest", align_corners=ac)[0] + out = apply_pending(out, overrides={'mode': 1, 'padding_mode': "nearest", 'align_corners': ac})[0] return out def method_2(im, ac): diff --git a/tests/test_spatial_combine_transforms.py b/tests/test_spatial_combine_transforms.py index 1b9bfa04fa..8594daed16 100644 --- a/tests/test_spatial_combine_transforms.py +++ b/tests/test_spatial_combine_transforms.py @@ -175,7 +175,7 @@ def test_combine_transforms(self, input_shape, funcs): init_param = funcs[-1][1] call_param = {} apply_param = get_apply_param(init_param, call_param) - result = apply_pending(pending_result, **apply_param)[0] + result = apply_pending(pending_result, overrides=apply_param)[0] match_ratio = np.sum(np.isclose(result.array, expected.array, atol=5e-1)) / np.prod(result.shape) self.assertGreater(match_ratio, 0.5) # at least half of the images are very close From 088d14e6dbce0ec96fdc38dec8ad1a64c0127dcd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:15:12 +0000 Subject: [PATCH 028/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/inverse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index c308216658..9af526804f 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -24,7 +24,7 @@ from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd from monai.transforms.traits import LazyTrait -from monai.transforms.transform import LazyTransform, Transform +from monai.transforms.transform import Transform from monai.utils import LazyAttr, MetaKeys, TraceKeys, convert_to_dst_type, convert_to_numpy, convert_to_tensor __all__ = ["TraceableTransform", "InvertibleTransform"] From 3e6a637abc1c6fa74bf9f2eb5234e4617adbcf28 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 13:09:18 +0100 Subject: [PATCH 029/175] Accepting formatting auto-fixes Signed-off-by: Ben Murray --- monai/transforms/compose.py | 14 +-- monai/transforms/croppad/array.py | 63 ++++++++----- monai/transforms/croppad/dictionary.py | 64 +++++++------ monai/transforms/croppad/functional.py | 20 ++-- monai/transforms/inverse.py | 8 +- monai/transforms/lazy/array.py | 3 +- monai/transforms/lazy/dictionary.py | 3 +- monai/transforms/lazy/functional.py | 6 +- monai/transforms/spatial/array.py | 109 ++++++++++------------ monai/transforms/spatial/dictionary.py | 67 +++++++------ monai/transforms/spatial/functional.py | 26 ++---- monai/transforms/transform.py | 15 ++- monai/utils/enums.py | 11 ++- tests/croppers.py | 4 +- tests/padders.py | 4 +- tests/test_affine.py | 4 +- tests/test_crop_foreground.py | 4 +- tests/test_crop_foregroundd.py | 2 +- tests/test_integration_lazy_samples.py | 2 +- tests/test_nvtx_decorator.py | 9 +- tests/test_rand_crop_by_label_classes.py | 2 +- tests/test_rand_crop_by_label_classesd.py | 2 +- tests/test_rand_crop_by_pos_neg_label.py | 2 +- tests/test_rand_crop_by_pos_neg_labeld.py | 2 +- tests/test_rand_spatial_crop.py | 2 +- tests/test_rand_spatial_crop_samples.py | 2 +- tests/test_rand_spatial_crop_samplesd.py | 2 +- tests/test_rand_spatial_cropd.py | 2 +- tests/test_rand_weighted_crop.py | 2 +- tests/test_rand_weighted_cropd.py | 2 +- tests/test_resize_with_pad_or_crop.py | 7 +- tests/test_resize_with_pad_or_cropd.py | 6 +- tests/test_rotate90.py | 4 +- tests/test_zoom.py | 2 +- 34 files changed, 232 insertions(+), 245 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index e1bfae3610..9dfe03c33d 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -26,10 +26,10 @@ from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor from monai.transforms.inverse import InvertibleTransform -from monai.transforms.traits import ThreadUnsafe # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) from monai.transforms.lazy.functional import execute_pending_transforms +from monai.transforms.traits import ThreadUnsafe from monai.transforms.transform import ( # noqa: F401 LazyTransform, MapTransform, @@ -185,9 +185,7 @@ def execute_compose( for _transform in transforms[start:end]: if threading: _transform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform - data = apply_transform( - _transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides - ) + data = apply_transform(_transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides) data = execute_pending_transforms(data, overrides) return data @@ -529,7 +527,7 @@ def __call__(self, data, start=0, end=None, threading=False): unpack_items=self.unpack_items, lazy=self.lazy, # type: ignore overrides=self.overrides, - threading=threading + threading=threading, ) # if the data is a mapping (dictionary), append the OneOf transform to the end @@ -619,7 +617,7 @@ def __call__(self, input_, start=0, end=None, threading=False): map_items=self.map_items, unpack_items=self.unpack_items, lazy=self.lazy, - threading=threading + threading=threading, ) # if the data is a mapping (dictionary), append the RandomOrder transform to the end @@ -653,9 +651,7 @@ def inverse(self, data): # loop backwards over transforms for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): - data = apply_transform( - self.transforms[o].inverse, data, self.map_items, self.unpack_items - ) + data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) return data diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 39865ab454..4feff9a33a 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -106,8 +106,11 @@ class Pad(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] def __init__( - self, to_pad: tuple[tuple[int, int]] | None = None, mode: str = PytorchPadMode.CONSTANT, - lazy: bool = False, **kwargs + self, + to_pad: tuple[tuple[int, int]] | None = None, + mode: str = PytorchPadMode.CONSTANT, + lazy: bool = False, + **kwargs, ) -> None: LazyTransform.__init__(self, lazy) self.to_pad = to_pad @@ -126,8 +129,12 @@ def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, in raise NotImplementedError(f"subclass {self.__class__.__name__} must implement this method.") def __call__( # type: ignore[override] - self, img: torch.Tensor, to_pad: tuple[tuple[int, int]] | None = None, mode: str | None = None, - lazy: bool | None = None, **kwargs + self, + img: torch.Tensor, + to_pad: tuple[tuple[int, int]] | None = None, + mode: str | None = None, + lazy: bool | None = None, + **kwargs, ) -> torch.Tensor: """ Args: @@ -251,8 +258,7 @@ class BorderPad(Pad): """ def __init__( - self, spatial_border: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, - lazy: bool = False, **kwargs + self, spatial_border: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, lazy: bool = False, **kwargs ) -> None: self.spatial_border = spatial_border super().__init__(mode=mode, lazy=lazy, **kwargs) @@ -287,8 +293,12 @@ class DivisiblePad(Pad): backend = SpatialPad.backend def __init__( - self, k: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, - lazy: bool = False, **kwargs + self, + k: Sequence[int] | int, + mode: str = PytorchPadMode.CONSTANT, + method: str = Method.SYMMETRIC, + lazy: bool = False, + **kwargs, ) -> None: """ Args: @@ -622,8 +632,7 @@ def __init__( lazy: bool = False, ) -> None: super().__init__( - roi_size=-1, max_roi_size=None, random_center=random_center, random_size=random_size, - lazy=lazy + roi_size=-1, max_roi_size=None, random_center=random_center, random_size=random_size, lazy=lazy ) self.roi_scale = roi_scale self.max_roi_scale = max_roi_scale @@ -821,8 +830,13 @@ def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarra return box_start_, box_end_ def crop_pad( - self, img: torch.Tensor, box_start: np.ndarray, box_end: np.ndarray, mode: str | None = None, - lazy: bool = False, **pad_kwargs + self, + img: torch.Tensor, + box_start: np.ndarray, + box_end: np.ndarray, + mode: str | None = None, + lazy: bool = False, + **pad_kwargs, ) -> torch.Tensor: """ Crop and pad based on the bounding box. @@ -838,9 +852,7 @@ def crop_pad( pad_width = BorderPad(spatial_border=pad).compute_pad_width( cropped.peek_pending_shape() if isinstance(cropped, MetaTensor) else cropped.shape[1:] ) - ret = self.padder.__call__( - img=cropped, to_pad=pad_width, mode=mode, lazy=lazy, **pad_kwargs - ) + ret = self.padder.__call__(img=cropped, to_pad=pad_width, mode=mode, lazy=lazy, **pad_kwargs) # combine the traced cropping and padding into one transformation # by taking the padded info and placing it in a key inside the crop info. if get_track_meta() and isinstance(ret, MetaTensor): @@ -902,8 +914,11 @@ class RandWeightedCrop(Randomizable, TraceableTransform, LazyTransform, MultiSam backend = SpatialCrop.backend def __init__( - self, spatial_size: Sequence[int] | int, num_samples: int = 1, weight_map: NdarrayOrTensor | None = None, - lazy: bool = False + self, + spatial_size: Sequence[int] | int, + num_samples: int = 1, + weight_map: NdarrayOrTensor | None = None, + lazy: bool = False, ): LazyTransform.__init__(self, lazy) self.spatial_size = ensure_tuple(spatial_size) @@ -917,8 +932,11 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: ) # using only the first channel as weight map def __call__( - self, img: torch.Tensor, weight_map: NdarrayOrTensor | None = None, randomize: bool = True, - lazy: bool | None = None + self, + img: torch.Tensor, + weight_map: NdarrayOrTensor | None = None, + randomize: bool = True, + lazy: bool | None = None, ) -> list[torch.Tensor]: """ Args: @@ -1324,9 +1342,7 @@ def __init__( **pad_kwargs, ): LazyTransform.__init__(self, lazy) - self.padder = SpatialPad( - spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs - ) + self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) def __call__(self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore @@ -1354,8 +1370,7 @@ def __call__(self, img: torch.Tensor, mode: str | None = None, lazy: bool | None crop_info = ret_.applied_operations.pop() orig_size = crop_info.get(TraceKeys.ORIG_SIZE) self.push_transform( - ret_, orig_size=orig_size, extra_info={"pad_info": pad_info, "crop_info": crop_info}, - lazy=lazy_ + ret_, orig_size=orig_size, extra_info={"pad_info": pad_info, "crop_info": crop_info}, lazy=lazy_ ) else: pad_info = ret_.pending_operations.pop() diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index e8ba7f1b4a..6dd2e139af 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -47,7 +47,7 @@ SpatialPad, ) from monai.transforms.inverse import InvertibleTransform -from monai.transforms.traits import MultiSampleTrait, LazyTrait +from monai.transforms.traits import LazyTrait, MultiSampleTrait from monai.transforms.transform import LazyTransform, MapTransform, Randomizable from monai.transforms.utils import is_positive from monai.utils import MAX_SEED, Method, PytorchPadMode, deprecated_arg_default, ensure_tuple_rep @@ -144,8 +144,7 @@ def __init__( MapTransform.__init__(self, keys, allow_missing_keys) LazyTransform.__init__(self, lazy) if lazy is True and not isinstance(padder, LazyTrait): - raise ValueError("'padder' must inherit LazyTrait if lazy is True " - f"'padder' is of type({type(padder)})") + raise ValueError("'padder' must inherit LazyTrait if lazy is True " f"'padder' is of type({type(padder)})") self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) @@ -153,8 +152,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No d = dict(data) lazy_ = self.lazy if lazy is None else lazy if lazy_ is True and not isinstance(self.padder, LazyTrait): - raise ValueError("'self.padder' must inherit LazyTrait if lazy is True " - f"'self.padder' is of type({type(self.padder)}") + raise ValueError( + "'self.padder' must inherit LazyTrait if lazy is True " f"'self.padder' is of type({type(self.padder)}" + ) for key, m in self.key_iterator(d, self.mode): if isinstance(self.padder, LazyTrait): d[key] = self.padder(d[key], mode=m, lazy=lazy_) @@ -212,9 +212,7 @@ def __init__( """ LazyTransform.__init__(self, lazy) padder = SpatialPad(spatial_size, method, lazy=lazy, **kwargs) - Padd.__init__( - self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys - ) + Padd.__init__(self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) class BorderPadd(Padd): @@ -263,9 +261,7 @@ def __init__( """ LazyTransform.__init__(self, lazy) padder = BorderPad(spatial_border=spatial_border, lazy=lazy, **kwargs) - Padd.__init__( - self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys - ) + Padd.__init__(self, keys, padder=padder, mode=mode, allow_missing_keys=allow_missing_keys) class DivisiblePadd(Padd): @@ -328,9 +324,7 @@ class Cropd(MapTransform, InvertibleTransform, LazyTransform): backend = Crop.backend - def __init__( - self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy: bool = False - ): + def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy: bool = False): MapTransform.__init__(self, keys, allow_missing_keys) LazyTransform.__init__(self, lazy) self.cropper = cropper @@ -363,9 +357,7 @@ class RandCropd(Cropd, Randomizable): backend = Crop.backend - def __init__( - self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy: bool = False - ): + def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False, lazy: bool = False): super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandCropd: @@ -385,8 +377,10 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No self.randomize(first_item.peek_pending_shape() if isinstance(first_item, MetaTensor) else first_item.shape[1:]) lazy_ = self.lazy if lazy is None else lazy if lazy_ is True and not isinstance(self.cropper, LazyTrait): - raise ValueError("'self.cropper' must inherit LazyTrait if lazy is True " - f"'self.cropper' is of type({type(self.cropper)}") + raise ValueError( + "'self.cropper' must inherit LazyTrait if lazy is True " + f"'self.cropper' is of type({type(self.cropper)}" + ) for key in self.key_iterator(d): kwargs = {"randomize": False} if isinstance(self.cropper, Randomizable) else {} if isinstance(self.cropper, LazyTrait): @@ -479,7 +473,11 @@ class CenterScaleCropd(Cropd): """ def __init__( - self, keys: KeysCollection, roi_scale: Sequence[float] | float, allow_missing_keys: bool = False, lazy: bool = False + self, + keys: KeysCollection, + roi_scale: Sequence[float] | float, + allow_missing_keys: bool = False, + lazy: bool = False, ) -> None: cropper = CenterScaleCrop(roi_scale, lazy=lazy) super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) @@ -564,7 +562,7 @@ def __init__( random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, - lazy: bool = False + lazy: bool = False, ) -> None: cropper = RandScaleCrop(roi_scale, max_roi_scale, random_center, random_size, lazy=lazy) super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) @@ -622,13 +620,16 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) LazyTransform.__init__(self, lazy) - self.cropper = RandSpatialCropSamples(roi_size, num_samples, max_roi_size, random_center, random_size, - lazy=lazy) + self.cropper = RandSpatialCropSamples( + roi_size, num_samples, max_roi_size, random_center, random_size, lazy=lazy + ) def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__( + self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None + ) -> list[dict[Hashable, torch.Tensor]]: ret: list[dict[Hashable, torch.Tensor]] = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): @@ -729,8 +730,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No lazy_ = self.lazy if lazy is None else lazy for key, m in self.key_iterator(d, self.mode): - d[key] = self.cropper.crop_pad(img=d[key], box_start=box_start, box_end=box_end, mode=m, - lazy=lazy_) + d[key] = self.cropper.crop_pad(img=d[key], box_start=box_start, box_end=box_end, mode=m, lazy=lazy_) return d @@ -778,7 +778,9 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__( + self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None + ) -> list[dict[Hashable, torch.Tensor]]: # output starts as empty list of dictionaries ret: list = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data @@ -896,7 +898,9 @@ def randomize( ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + def __call__( + self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None + ) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) fg_indices = d.pop(self.fg_indices_key, None) bg_indices = d.pop(self.bg_indices_key, None) @@ -1025,7 +1029,7 @@ def __init__( allow_smaller=allow_smaller, warn=warn, max_samples_per_class=max_samples_per_class, - lazy=lazy + lazy=lazy, ) def set_random_state( @@ -1094,7 +1098,7 @@ def __init__( ) -> None: padcropper = ResizeWithPadOrCrop(spatial_size=spatial_size, method=method, **pad_kwargs, lazy=lazy) super().__init__( - keys, padder=padcropper, mode=mode, allow_missing_keys=allow_missing_keys, lazy=lazy # type: ignore + keys, padder=padcropper, mode=mode, allow_missing_keys=allow_missing_keys, lazy=lazy # type: ignore ) diff --git a/monai/transforms/croppad/functional.py b/monai/transforms/croppad/functional.py index 3460127c8a..783635e467 100644 --- a/monai/transforms/croppad/functional.py +++ b/monai/transforms/croppad/functional.py @@ -27,13 +27,7 @@ from monai.data.utils import to_affine_nd from monai.transforms.inverse import TraceableTransform from monai.transforms.utils import convert_pad_mode, create_translate -from monai.utils import ( - PytorchPadMode, - convert_to_dst_type, - convert_to_numpy, - convert_to_tensor, - ensure_tuple, -) +from monai.utils import PytorchPadMode, convert_to_dst_type, convert_to_numpy, convert_to_tensor, ensure_tuple __all__ = ["pad_nd", "pad_func", "crop_func", "crop_or_pad_nd"] @@ -156,12 +150,12 @@ def crop_or_pad_nd(img: torch.Tensor, translation_mat, spatial_size: tuple[int, def pad_func( - img: torch.Tensor, - to_pad: tuple[tuple[int, int]], - transform_info: dict, - mode: str = PytorchPadMode.CONSTANT, - lazy: bool = False, - **kwargs + img: torch.Tensor, + to_pad: tuple[tuple[int, int]], + transform_info: dict, + mode: str = PytorchPadMode.CONSTANT, + lazy: bool = False, + **kwargs, ) -> torch.Tensor: """ Functional implementation of padding a MetaTensor. This function operates eagerly or lazily according diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 9af526804f..9a172d3f17 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -78,13 +78,7 @@ def trace_key(key: Hashable = None): @staticmethod def transform_info_keys(): """The keys to store necessary info of an applied transform.""" - return ( - TraceKeys.CLASS_NAME, - TraceKeys.ID, - TraceKeys.TRACING, - TraceKeys.LAZY, - TraceKeys.DO_TRANSFORM, - ) + return (TraceKeys.CLASS_NAME, TraceKeys.ID, TraceKeys.TRACING, TraceKeys.LAZY, TraceKeys.DO_TRANSFORM) def get_transform_info(self) -> dict: """ diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index 7b059f0048..d5bfd27a8e 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -10,6 +10,8 @@ # limitations under the License. +from __future__ import annotations + from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform from monai.transforms.lazy.functional import apply_pending @@ -24,7 +26,6 @@ def __init__(self): super().__init__() def __call__(self, data, *args, **kwargs): - if isinstance(data, MetaTensor): return apply_pending(data, *args, **kwargs) diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index ee8f4404bc..f30061d7bb 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -10,12 +10,13 @@ # limitations under the License. +from __future__ import annotations + from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform from monai.transforms.lazy.functional import apply_pending - class ApplyPendingd(InvertibleTransform): """ Apply wraps the apply method and can function as a Transform in either array or dictionary diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index ab9399e12c..1c3c5ff6f3 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -51,11 +51,7 @@ def execute_pending_transforms(data, overrides: dict = None): return data -def apply_pending( - data: torch.Tensor | MetaTensor, - pending: list | None = None, - overrides: dict | None = None, -): +def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None): """ This method applies pending transforms to `data` tensors. Currently, only 2d and 3d input are supported. diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index c040a21607..47c62b31a0 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -221,9 +221,15 @@ def __call__( padding_mode = padding_mode if padding_mode is not None else self.padding_mode lazy_ = self.lazy if lazy is None else lazy return spatial_resample( - img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, + img, + dst_affine, + spatial_size, + mode, + padding_mode, + align_corners, + dtype_pt, lazy=lazy_, - transform_info=self.get_transform_info() + transform_info=self.get_transform_info(), ) def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -409,8 +415,7 @@ def __init__( raise ValueError(f"min_pixdim {self.min_pixdim} must be positive, smaller than max {self.max_pixdim}.") self.sp_resample = SpatialResample( - mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy=lazy + mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, lazy=lazy ) @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") @@ -424,7 +429,7 @@ def __call__( dtype: DtypeLike = None, scale_extent: bool | None = None, output_spatial_shape: Sequence[int] | np.ndarray | int | None = None, - lazy: bool | None = None + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -627,9 +632,9 @@ def __call__(self, data_array: torch.Tensor, lazy: bool | None = None) -> torch. ) spatial_ornt = nib.orientations.ornt_transform(src, dst) lazy_ = self.lazy if lazy is None else lazy - return orientation(data_array, affine_np, spatial_ornt, - lazy=lazy_, - transform_info=self.get_transform_info()) # type: ignore + return orientation( + data_array, affine_np, spatial_ornt, lazy=lazy_, transform_info=self.get_transform_info() + ) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: transform = self.pop_transform(data) @@ -677,9 +682,7 @@ def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ img = convert_to_tensor(img, track_meta=get_track_meta()) lazy_ = self.lazy if lazy is None else lazy - return flip(img, self.spatial_axis, - lazy=lazy_, - transform_info=self.get_transform_info()) # type: ignore + return flip(img, self.spatial_axis, lazy=lazy_, transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: self.pop_transform(data) @@ -754,7 +757,7 @@ def __call__( anti_aliasing: bool | None = None, anti_aliasing_sigma: Sequence[float] | float | None = None, dtype: DtypeLike | torch.dtype = None, - lazy: bool | None = None + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -897,7 +900,7 @@ def __call__( padding_mode: str | None = None, align_corners: bool | None = None, dtype: DtypeLike | torch.dtype = None, - lazy: bool | None = None + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -932,9 +935,15 @@ def __call__( output_shape = im_shape if self.keep_size else None lazy_ = self.lazy if lazy is None else lazy return rotate( # type: ignore - img, self.angle, output_shape, _mode, _padding_mode, _align_corners, _dtype, + img, + self.angle, + output_shape, + _mode, + _padding_mode, + _align_corners, + _dtype, lazy=lazy_, - transform_info=self.get_transform_info() + transform_info=self.get_transform_info(), ) def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1064,9 +1073,15 @@ def __call__( _dtype = get_equivalent_dtype(dtype or self.dtype or img.dtype, torch.Tensor) lazy_ = self.lazy if lazy is None else lazy return zoom( # type: ignore - img, _zoom, self.keep_size, _mode, _padding_mode, _align_corners, _dtype, + img, + _zoom, + self.keep_size, + _mode, + _padding_mode, + _align_corners, + _dtype, lazy=lazy_, - transform_info=self.get_transform_info() + transform_info=self.get_transform_info(), ) def inverse(self, data: torch.Tensor) -> torch.Tensor: @@ -1105,12 +1120,7 @@ class Rotate90(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH] - def __init__( - self, - k: int = 1, - spatial_axes: tuple[int, int] = (0, 1), - lazy: bool = False - ) -> None: + def __init__(self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1), lazy: bool = False) -> None: """ Args: k: number of times to rotate by 90 degrees. @@ -1138,9 +1148,7 @@ def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: img = convert_to_tensor(img, track_meta=get_track_meta()) axes = map_spatial_axes(img.ndim, self.spatial_axes) lazy_ = self.lazy if lazy is None else lazy - return rotate90(img, axes, self.k, - lazy=lazy_, - transform_info=self.get_transform_info()) # type: ignore + return rotate90(img, axes, self.k, lazy=lazy_, transform_info=self.get_transform_info()) # type: ignore def inverse(self, data: torch.Tensor) -> torch.Tensor: transform = self.pop_transform(data) @@ -1164,11 +1172,7 @@ class RandRotate90(RandomizableTransform, InvertibleTransform, LazyTransform): backend = Rotate90.backend def __init__( - self, - prob: float = 0.1, - max_k: int = 3, - spatial_axes: tuple[int, int] = (0, 1), - lazy: bool = False + self, prob: float = 0.1, max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1), lazy: bool = False ) -> None: """ Args: @@ -1371,22 +1375,12 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): backend = Flip.backend - def __init__( - self, - prob: float = 0.1, - spatial_axis: Sequence[int] | int | None = None, - lazy: bool = False, - ) -> None: + def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, lazy: bool = False) -> None: RandomizableTransform.__init__(self, prob) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) self.lazy = lazy - def __call__( - self, - img: torch.Tensor, - randomize: bool = True, - lazy: bool | None = None, - ) -> torch.Tensor: + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), @@ -1437,12 +1431,7 @@ def randomize(self, data: NdarrayOrTensor) -> None: return None self._axis = self.R.randint(data.ndim - 1) - def __call__( - self, - img: torch.Tensor, - randomize: bool = True, - lazy: bool | None = None, - ) -> torch.Tensor: + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]) @@ -1676,10 +1665,7 @@ def __init__( self.lazy = lazy def __call__( - self, - spatial_size: Sequence[int] | None = None, - grid: torch.Tensor | None = None, - lazy: bool | None = None, + self, spatial_size: Sequence[int] | None = None, grid: torch.Tensor | None = None, lazy: bool | None = None ) -> tuple[torch.Tensor | None, torch.Tensor]: """ The grid can be initialized with a `spatial_size` parameter, or provided directly as `grid`. @@ -1829,11 +1815,11 @@ def randomize(self, data: Any | None = None) -> None: self.scale_params = self._get_rand_param(self.scale_range, 1.0) def __call__( - self, - spatial_size: Sequence[int] | None = None, - grid: NdarrayOrTensor | None = None, - randomize: bool = True, - lazy: bool | None = None, + self, + spatial_size: Sequence[int] | None = None, + grid: NdarrayOrTensor | None = None, + randomize: bool = True, + lazy: bool | None = None, ) -> torch.Tensor: """ Args: @@ -2498,8 +2484,7 @@ def __call__( if grid is None: grid = self.get_identity_grid(sp_size, lazy_) if self._do_transform: - grid = self.rand_affine_grid(grid=grid, randomize=randomize, - lazy=lazy_) + grid = self.rand_affine_grid(grid=grid, randomize=randomize, lazy=lazy_) affine = self.rand_affine_grid.get_transformation_matrix() return affine_func( # type: ignore img, @@ -2627,7 +2612,7 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, - lazy=False + lazy=False, ) self.resampler = Resample(device=device) @@ -2795,7 +2780,7 @@ def __init__( translate_range=translate_range, scale_range=scale_range, device=device, - lazy=False + lazy=False, ) self.resampler = Resample(device=device) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index a4aaa800b5..ec182d3bc8 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -432,8 +432,12 @@ def __init__( """ super().__init__(keys, allow_missing_keys) self.spacing_transform = Spacing( - pixdim, diagonal=diagonal, recompute_affine=recompute_affine, min_pixdim=min_pixdim, max_pixdim=max_pixdim, - lazy=lazy + pixdim, + diagonal=diagonal, + recompute_affine=recompute_affine, + min_pixdim=min_pixdim, + max_pixdim=max_pixdim, + lazy=lazy, ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) @@ -533,7 +537,8 @@ def __init__( """ super().__init__(keys, allow_missing_keys) self.ornt_transform = Orientation( - axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy=lazy) + axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy=lazy + ) self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: @@ -570,12 +575,12 @@ class Rotate90d(MapTransform, InvertibleTransform, LazyTransform): backend = Rotate90.backend def __init__( - self, - keys: KeysCollection, - k: int = 1, - spatial_axes: tuple[int, int] = (0, 1), - allow_missing_keys: bool = False, - lazy: bool = False, + self, + keys: KeysCollection, + k: int = 1, + spatial_axes: tuple[int, int] = (0, 1), + allow_missing_keys: bool = False, + lazy: bool = False, ) -> None: """ Args: @@ -661,7 +666,9 @@ def randomize(self, data: Any | None = None) -> None: self._rand_k = self.R.randint(self.max_k) + 1 super().randomize(None) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> Mapping[Hashable, torch.Tensor]: + def __call__( + self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None + ) -> Mapping[Hashable, torch.Tensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified @@ -1016,7 +1023,7 @@ def __init__( spatial_size=spatial_size, cache_grid=cache_grid, device=device, - lazy=lazy + lazy=lazy, ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) @@ -1026,7 +1033,9 @@ def set_random_state(self, seed: int | None = None, state: np.random.RandomState super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor], lazy: bool | None = None) -> dict[Hashable, NdarrayOrTensor]: + def __call__( + self, data: Mapping[Hashable, NdarrayOrTensor], lazy: bool | None = None + ) -> dict[Hashable, NdarrayOrTensor]: """ Args: data: a dictionary containing the tensor-like data to be processed. The ``keys`` specified @@ -1398,11 +1407,11 @@ class Flipd(MapTransform, InvertibleTransform, LazyTransform): backend = Flip.backend def __init__( - self, - keys: KeysCollection, - spatial_axis: Sequence[int] | int | None = None, - allow_missing_keys: bool = False, - lazy: bool = False, + self, + keys: KeysCollection, + spatial_axis: Sequence[int] | int | None = None, + allow_missing_keys: bool = False, + lazy: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.flipper = Flip(spatial_axis=spatial_axis) @@ -1523,11 +1532,7 @@ class RandAxisFlipd(RandomizableTransform, MapTransform, InvertibleTransform, La backend = RandAxisFlip.backend def __init__( - self, - keys: KeysCollection, - prob: float = 0.1, - allow_missing_keys: bool = False, - lazy: bool = False, + self, keys: KeysCollection, prob: float = 0.1, allow_missing_keys: bool = False, lazy: bool = False ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) @@ -1653,8 +1658,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No d, self.mode, self.padding_mode, self.align_corners, self.dtype ): d[key] = self.rotator( - d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy=lazy_, + d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, lazy=lazy_ ) return d @@ -1721,8 +1725,9 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.rand_rotate = RandRotate(range_x=range_x, range_y=range_y, range_z=range_z, prob=1.0, keep_size=keep_size, - lazy=lazy) + self.rand_rotate = RandRotate( + range_x=range_x, range_y=range_y, range_z=range_z, prob=1.0, keep_size=keep_size, lazy=lazy + ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) @@ -1859,8 +1864,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype ): - d[key] = self.zoomer(d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, - lazy=lazy_) + d[key] = self.zoomer( + d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, lazy=lazy_ + ) return d def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: @@ -1931,8 +1937,9 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - self.rand_zoom = RandZoom(prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, keep_size=keep_size, - lazy=lazy, **kwargs) + self.rand_zoom = RandZoom( + prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, keep_size=keep_size, lazy=lazy, **kwargs + ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) diff --git a/monai/transforms/spatial/functional.py b/monai/transforms/spatial/functional.py index 50e5f6ba1c..a739b3e1fc 100644 --- a/monai/transforms/spatial/functional.py +++ b/monai/transforms/spatial/functional.py @@ -66,8 +66,7 @@ def _maybe_new_metatensor(img, dtype=None, device=None): def spatial_resample( - img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, - lazy, transform_info + img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, lazy, transform_info ) -> torch.Tensor: """ Functional implementation of resampling the input image to the specified ``dst_affine`` matrix and ``spatial_size``. @@ -257,12 +256,7 @@ def flip(img, sp_axes, lazy, transform_info): sp = axis - 1 xform[sp, sp], xform[sp, -1] = xform[sp, sp] * -1, sp_size[sp] - 1 meta_info = TraceableTransform.track_transform_meta( - img, - sp_size=sp_size, - affine=xform, - extra_info=extra_info, - transform_info=transform_info, - lazy=lazy, + img, sp_size=sp_size, affine=xform, extra_info=extra_info, transform_info=transform_info, lazy=lazy ) out = _maybe_new_metatensor(img) if lazy: @@ -271,8 +265,9 @@ def flip(img, sp_axes, lazy, transform_info): return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, - lazy, transform_info): +def resize( + img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, lazy, transform_info +): """ Functional implementation of resize. This function operates eagerly or lazily according to @@ -343,8 +338,7 @@ def resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, - lazy, transform_info): +def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, lazy, transform_info): """ Functional implementation of rotate. This function operates eagerly or lazily according to @@ -414,8 +408,7 @@ def rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, - lazy, transform_info): +def zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, lazy, transform_info): """ Functional implementation of zoom. This function operates eagerly or lazily according to @@ -548,8 +541,9 @@ def rotate90(img, axes, k, lazy, transform_info): return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out -def affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, - lazy, transform_info): +def affine_func( + img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, lazy, transform_info +): """ Functional implementation of affine. This function operates eagerly or lazily according to diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 8bea2575ed..2a0cee55d7 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -28,7 +28,7 @@ from monai.transforms.lazy.functional import execute_pending_transforms from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first -from monai.utils.enums import TransformBackends, LazyMode +from monai.utils.enums import LazyMode, TransformBackends from monai.utils.misc import MONAIEnvVars __all__ = [ @@ -45,11 +45,11 @@ def _apply_transform( - transform: Callable[..., ReturnType], - data: Any, - unpack_parameters: bool = False, - lazy: str = LazyMode.OFF, - overrides: dict = None, + transform: Callable[..., ReturnType], + data: Any, + unpack_parameters: bool = False, + lazy: str = LazyMode.OFF, + overrides: dict = None, ) -> ReturnType: """ Perform transformation `transform` with the provided parameters `parameters`. @@ -121,8 +121,7 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items, lazy, overrides) - for item in data] + return [_apply_transform(transform, item, unpack_items, lazy, overrides) for item in data] return _apply_transform(transform, data, unpack_items, lazy, overrides) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 17e963b687..e7d5237698 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -658,10 +658,10 @@ class LazyMode(StrEnum): 'ON' indicates that all transforms capable of being executed lazily will be executed lazily See: :py:class: monai.transforms.compose.Compose for more details. """ - OFF = 'off' - ENABLED = 'enabled' - ON = 'on' + OFF = "off" + ENABLED = "enabled" + ON = "on" @staticmethod def as_bool(lazy_mode): @@ -674,8 +674,9 @@ def as_bool(lazy_mode): if lazy_mode == LazyMode.ENABLED: return None - raise ValueError("'lazy_mode' must be one of LazyMode.OFF, LazyMode.ENABLED or LazyMode.ON, " - f"but is {lazy_mode}") + raise ValueError( + "'lazy_mode' must be one of LazyMode.OFF, LazyMode.ENABLED or LazyMode.ON, " f"but is {lazy_mode}" + ) class BundleProperty(StrEnum): diff --git a/tests/croppers.py b/tests/croppers.py index 29596e7e03..36ee3cc9c4 100644 --- a/tests/croppers.py +++ b/tests/croppers.py @@ -124,7 +124,7 @@ def crop_test_pending_ops(self, input_param, input_shape, align_corners=False): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'align_corners': align_corners} + overrides = {"mode": "nearest", "align_corners": align_corners} result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -165,7 +165,7 @@ def crop_test_combine_ops(self, funcs, input_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - overrides = {'mode': "nearest", 'align_corners': False} + overrides = {"mode": "nearest", "align_corners": False} result = apply_pending(pending_result, overrides=overrides)[0] # compare diff --git a/tests/padders.py b/tests/padders.py index 8524f64610..f3983343e3 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -134,7 +134,7 @@ def pad_test_pending_ops(self, input_param, input_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - overrides = {'mode': "nearest", 'padding_mode': mode[1], 'align_corners': False} + overrides = {"mode": "nearest", "padding_mode": mode[1], "align_corners": False} result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -169,7 +169,7 @@ def pad_test_combine_ops(self, funcs, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # TODO: mode="bilinear" may report error - overrides = {'mode': "nearest", 'padding_mode': mode[1], 'align_corners': False} + overrides = {"mode": "nearest", "padding_mode": mode[1], "align_corners": False} result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_affine.py b/tests/test_affine.py index f3b65feaa2..9c2f4197a6 100644 --- a/tests/test_affine.py +++ b/tests/test_affine.py @@ -210,7 +210,7 @@ def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=sp_size) xform.lazy = True out = xform(im) - overrides = {'padding_mode': "border", 'align_corners': ac} + overrides = {"padding_mode": "border", "align_corners": ac} out = apply_pending(out, overrides=overrides)[0] return out @@ -218,7 +218,7 @@ def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=sp_size) xform.lazy = True out = xform(im) - overrides = {'mode': 1, 'padding_mode': "nearest", 'align_corners': ac} + overrides = {"mode": 1, "padding_mode": "nearest", "align_corners": ac} out = apply_pending(out, overrides=overrides)[0] return out diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index 5f99e99f1a..5bbc1bbd3a 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -132,7 +132,7 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'align_corners': align_corners} + overrides = {"mode": "nearest", "align_corners": align_corners} result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) @@ -145,7 +145,7 @@ def test_lazy_error(self, input_param, image, _expected_data, align_corners): # lazy crop_fn.lazy = True pending_result = crop_fn(image) - overrides = {'mode': "nearest", 'align_corners': align_corners} + overrides = {"mode": "nearest", "align_corners": align_corners} return apply_pending(pending_result, overrides=overrides)[0] @parameterized.expand(TEST_COORDS + TESTS) diff --git a/tests/test_crop_foregroundd.py b/tests/test_crop_foregroundd.py index b2f59b2da9..776776f6c5 100644 --- a/tests/test_crop_foregroundd.py +++ b/tests/test_crop_foregroundd.py @@ -195,7 +195,7 @@ def test_pending_ops(self, input_param, image, _expected_data, align_corners): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'align_corners': align_corners} + overrides = {"mode": "nearest", "align_corners": align_corners} result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 377b49d1d4..018a175a74 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -47,7 +47,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, # ) lazy_kwargs = { "img": {"mode": "bilinear", "device": device, "padding_mode": "border", "dtype": torch.float32}, - "seg": {"mode": 0, "device": device, "padding_mode": "nearest", "dtype": torch.uint8} + "seg": {"mode": 0, "device": device, "padding_mode": "nearest", "dtype": torch.uint8}, } train_transforms = mt.Compose( [ diff --git a/tests/test_nvtx_decorator.py b/tests/test_nvtx_decorator.py index be09ff9aa3..00294fc186 100644 --- a/tests/test_nvtx_decorator.py +++ b/tests/test_nvtx_decorator.py @@ -62,14 +62,7 @@ ] TEST_CASE_RECURSIVE_2 = [ torch.randn(3, 3), - Compose( - [ - ToNumpy(), - Flip(), - OneOf([RandAdjustContrast(prob=0.0), RandFlip(prob=1.0)], weights=[0, 1]), - ToTensor(), - ] - ), + Compose([ToNumpy(), Flip(), OneOf([RandAdjustContrast(prob=0.0), RandFlip(prob=1.0)], weights=[0, 1]), ToTensor()]), ] TEST_CASE_RECURSIVE_LIST = [ torch.randn(3, 3), diff --git a/tests/test_rand_crop_by_label_classes.py b/tests/test_rand_crop_by_label_classes.py index a17977d1ea..88d2631ca5 100644 --- a/tests/test_rand_crop_by_label_classes.py +++ b/tests/test_rand_crop_by_label_classes.py @@ -161,7 +161,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(_pending_result, overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_crop_by_label_classesd.py b/tests/test_rand_crop_by_label_classesd.py index 21eb7095d1..748f26f1ff 100644 --- a/tests/test_rand_crop_by_label_classesd.py +++ b/tests/test_rand_crop_by_label_classesd.py @@ -150,7 +150,7 @@ def test_pending_ops(self, input_param, input_data, _expected_type, _expected_sh assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result = apply_pending(_pending_result["img"], overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(_pending_result["img"], overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected[i]["img"], rtol=1e-5) diff --git a/tests/test_rand_crop_by_pos_neg_label.py b/tests/test_rand_crop_by_pos_neg_label.py index b0b285500a..98af6b0b5e 100644 --- a/tests/test_rand_crop_by_pos_neg_label.py +++ b/tests/test_rand_crop_by_pos_neg_label.py @@ -143,7 +143,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(_pending_result, overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_crop_by_pos_neg_labeld.py b/tests/test_rand_crop_by_pos_neg_labeld.py index 8ec184ca46..1b57548d12 100644 --- a/tests/test_rand_crop_by_pos_neg_labeld.py +++ b/tests/test_rand_crop_by_pos_neg_labeld.py @@ -160,7 +160,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape): assert_allclose(_pending_result["image"].peek_pending_affine(), expected[i]["image"].affine) assert_allclose(_pending_result["image"].peek_pending_shape(), expected[i]["image"].shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'align_corners': False} + overrides = {"mode": "nearest", "align_corners": False} result_image = apply_pending(_pending_result["image"], overrides=overrides)[0] result_extra = apply_pending(_pending_result["extra"], overrides=overrides)[0] # compare diff --git a/tests/test_rand_spatial_crop.py b/tests/test_rand_spatial_crop.py index a11aca9f6f..df121e2220 100644 --- a/tests/test_rand_spatial_crop.py +++ b/tests/test_rand_spatial_crop.py @@ -90,7 +90,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(pending_result, overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_spatial_crop_samples.py b/tests/test_rand_spatial_crop_samples.py index d9b21a9926..92f0f9d9be 100644 --- a/tests/test_rand_spatial_crop_samples.py +++ b/tests/test_rand_spatial_crop_samples.py @@ -119,7 +119,7 @@ def test_pending_ops(self, input_param, input_shape, _expected_shape, _expected_ assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(_pending_result, overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_spatial_crop_samplesd.py b/tests/test_rand_spatial_crop_samplesd.py index c0051f684e..ec0d63cc50 100644 --- a/tests/test_rand_spatial_crop_samplesd.py +++ b/tests/test_rand_spatial_crop_samplesd.py @@ -129,7 +129,7 @@ def test_pending_ops(self, input_param, input_data, _expected_shape, _expected_l assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'align_corners': False} + overrides = {"mode": "nearest", "align_corners": False} result_img = apply_pending(_pending_result["img"], overrides=overrides)[0] result_seg = apply_pending(_pending_result["seg"], overrides=overrides)[0] # compare diff --git a/tests/test_rand_spatial_cropd.py b/tests/test_rand_spatial_cropd.py index 3abc7e8683..123459235f 100644 --- a/tests/test_rand_spatial_cropd.py +++ b/tests/test_rand_spatial_cropd.py @@ -95,7 +95,7 @@ def test_random_shape(self, input_param, input_shape, expected_shape): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - result = apply_pending(pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(pending_result, overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rand_weighted_crop.py b/tests/test_rand_weighted_crop.py index 4a4526ff94..47a8f3bfa2 100644 --- a/tests/test_rand_weighted_crop.py +++ b/tests/test_rand_weighted_crop.py @@ -185,7 +185,7 @@ def test_pending_ops(self, _, input_param, img, weight, expected_shape, expected assert_allclose(_pending_result.peek_pending_affine(), expected[i].affine) assert_allclose(_pending_result.peek_pending_shape(), expected[i].shape[1:]) # only support nearest - result = apply_pending(_pending_result, overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(_pending_result, overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected[i], rtol=1e-5) diff --git a/tests/test_rand_weighted_cropd.py b/tests/test_rand_weighted_cropd.py index f16715d213..9d37779613 100644 --- a/tests/test_rand_weighted_cropd.py +++ b/tests/test_rand_weighted_cropd.py @@ -173,7 +173,7 @@ def test_pending_ops(self, _, input_param, input_data, expected_shape, expected_ assert_allclose(_pending_result["img"].peek_pending_affine(), expected[i]["img"].affine) assert_allclose(_pending_result["img"].peek_pending_shape(), expected[i]["img"].shape[1:]) # only support nearest - result = apply_pending(_pending_result["img"], overrides={'mode': "nearest", 'align_corners': False})[0] + result = apply_pending(_pending_result["img"], overrides={"mode": "nearest", "align_corners": False})[0] # compare assert_allclose(result, expected[i]["img"], rtol=1e-5) diff --git a/tests/test_resize_with_pad_or_crop.py b/tests/test_resize_with_pad_or_crop.py index 3b147cd249..287df039b8 100644 --- a/tests/test_resize_with_pad_or_crop.py +++ b/tests/test_resize_with_pad_or_crop.py @@ -85,8 +85,11 @@ def test_pending_ops(self, input_param, input_shape, _expected_data, align_corne assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'padding_mode': TESTS_PENDING_MODE[input_param["mode"]], - 'align_corners': align_corners} + overrides = { + "mode": "nearest", + "padding_mode": TESTS_PENDING_MODE[input_param["mode"]], + "align_corners": align_corners, + } result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_resize_with_pad_or_cropd.py b/tests/test_resize_with_pad_or_cropd.py index f0bd712b6a..471144a609 100644 --- a/tests/test_resize_with_pad_or_cropd.py +++ b/tests/test_resize_with_pad_or_cropd.py @@ -80,7 +80,11 @@ def test_pending_ops(self, input_param, input_data, _expected_data): assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) # only support nearest - overrides = {'mode': "nearest", 'padding_mode': TESTS_PENDING_MODE[input_param["mode"]], 'align_corners': True} + overrides = { + "mode": "nearest", + "padding_mode": TESTS_PENDING_MODE[input_param["mode"]], + "align_corners": True, + } result = apply_pending(pending_result, overrides=overrides)[0] # compare assert_allclose(result, expected, rtol=1e-5) diff --git a/tests/test_rotate90.py b/tests/test_rotate90.py index d8113b4d37..0948469df9 100644 --- a/tests/test_rotate90.py +++ b/tests/test_rotate90.py @@ -179,14 +179,14 @@ def method_0(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) xform.lazy = True out = xform(im) - out = apply_pending(out, overrides={'padding_mode': "border", 'align_corners': ac})[0] + out = apply_pending(out, overrides={"padding_mode": "border", "align_corners": ac})[0] return out def method_1(im, ac): xform = Affine(align_corners=ac, affine=mat, image_only=True, spatial_size=s) xform.lazy = True out = xform(im) - out = apply_pending(out, overrides={'mode': 1, 'padding_mode': "nearest", 'align_corners': ac})[0] + out = apply_pending(out, overrides={"mode": 1, "padding_mode": "nearest", "align_corners": ac})[0] return out def method_2(im, ac): diff --git a/tests/test_zoom.py b/tests/test_zoom.py index 43a756d829..15c5b9b905 100644 --- a/tests/test_zoom.py +++ b/tests/test_zoom.py @@ -57,7 +57,7 @@ def test_pending_ops(self, zoom, mode, align_corners=False, keep_size=False): self.assertIsInstance(pending_result, MetaTensor) assert_allclose(pending_result.peek_pending_affine(), expected.affine) assert_allclose(pending_result.peek_pending_shape(), expected.shape[1:]) - overrides = {'mode': "bilinear", 'dtype': np.float64, 'align_corners': align_corners} + overrides = {"mode": "bilinear", "dtype": np.float64, "align_corners": align_corners} result = apply_pending(pending_result, overrides=overrides)[0] # compare match_ratio = np.sum(np.isclose(result, expected)) / np.prod(result.shape) From 98a53bf54aa66dcac9a12d5d43a8e5b5cb17a9d3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 14:10:08 +0100 Subject: [PATCH 030/175] Renaming surviving lazy_evaluation instances to lazy. Fix for _apply_transform when being called with bool for lazy Added lazy parameter to crop_pad call to TraceableTransform.push_transform Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- monai/transforms/croppad/array.py | 1 + monai/transforms/transform.py | 5 +++-- tests/test_crop_foreground.py | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 9dfe03c33d..06c81d54f2 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -421,7 +421,7 @@ def inverse(self, data): # loop backwards over transforms for t in reversed(invertible_transforms): - if isinstance(t, LazyTransform) and t.lazy_evaluation: + if isinstance(t, LazyTrait) and t.lazy: warnings.warn( f"inversing {t.__class__.__name__} lazily may not implemented" "please set `lazy_evaluation=False` before calling inverse." diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 4feff9a33a..77f10906bf 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -868,6 +868,7 @@ def crop_pad( orig_size=crop_info.get(TraceKeys.ORIG_SIZE), sp_size=pad_info[LazyAttr.SHAPE], affine=crop_info[LazyAttr.AFFINE] @ pad_info[LazyAttr.AFFINE], + lazy=lazy, extra_info=extra, ) return ret diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 2a0cee55d7..f3f524abcc 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -82,10 +82,11 @@ def _apply_transform( elif lazy is LazyMode.ENABLED and transform.lazy is False: data = execute_pending_transforms(data, overrides) + lazy_mode = lazy if isinstance(lazy, bool) else LazyMode.as_bool(lazy) if isinstance(data, tuple) and unpack_parameters: - return transform(*data, lazy=LazyMode.as_bool(lazy)) if lazy_tx else transform(*data) + return transform(*data, lazy=lazy_mode) if lazy_tx else transform(*data) - return transform(data, lazy=LazyMode.as_bool(lazy)) if lazy_tx else transform(data) + return transform(data, lazy=lazy_mode) if lazy_tx else transform(data) def apply_transform( diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index 5bbc1bbd3a..55a94ca917 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -151,10 +151,10 @@ def test_lazy_error(self, input_param, image, _expected_data, align_corners): @parameterized.expand(TEST_COORDS + TESTS) def test_inverse_pending_ops(self, input_param, image, _expected_data, align_corners): crop_fn = CropForeground(**input_param) - crop_fn.lazy_evaluation = True + crop_fn.lazy = True pending_result = crop_fn(image) self.assertIsInstance(pending_result, MetaTensor) - result = apply_transforms(pending_result, mode="nearest", align_corners=align_corners)[0] + result = apply_pending(pending_result, overrides={'mode': "nearest", 'align_corners': align_corners})[0] inverted = crop_fn.inverse(result) self.assertEqual(image.shape, inverted.shape) self.assertTrue((not inverted.applied_operations) and (not inverted.pending_operations)) From 28a0175c108493389696c4bebab8461bff6f88d8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 14:14:19 +0100 Subject: [PATCH 031/175] Renamed the last errant 'lazy_evaluation' instances to 'lazy' Signed-off-by: Ben Murray --- monai/transforms/compose.py | 4 ++-- tests/croppers.py | 2 +- tests/lazy_transforms_utils.py | 4 ++-- tests/padders.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 06c81d54f2..b20815e4ee 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -29,7 +29,7 @@ # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) from monai.transforms.lazy.functional import execute_pending_transforms -from monai.transforms.traits import ThreadUnsafe +from monai.transforms.traits import ThreadUnsafe, LazyTrait from monai.transforms.transform import ( # noqa: F401 LazyTransform, MapTransform, @@ -424,7 +424,7 @@ def inverse(self, data): if isinstance(t, LazyTrait) and t.lazy: warnings.warn( f"inversing {t.__class__.__name__} lazily may not implemented" - "please set `lazy_evaluation=False` before calling inverse." + "please set `lazy=False` before calling inverse." ) data = apply_transform(t.inverse, data, self.map_items, self.unpack_items) return data diff --git a/tests/croppers.py b/tests/croppers.py index 36ee3cc9c4..8c9b43bf0a 100644 --- a/tests/croppers.py +++ b/tests/croppers.py @@ -129,7 +129,7 @@ def crop_test_pending_ops(self, input_param, input_shape, align_corners=False): # compare assert_allclose(result, expected, rtol=1e-5) if isinstance(result, MetaTensor) and not isinstance(crop_fn, MapTransform): - crop_fn.lazy_evaluation = False + crop_fn.lazy = False inverted = crop_fn.inverse(result) self.assertTrue((not inverted.applied_operations) and (not inverted.pending_operations)) self.assertEqual(inverted.shape, im.shape) diff --git a/tests/lazy_transforms_utils.py b/tests/lazy_transforms_utils.py index 6cd8cf76ac..1681e26037 100644 --- a/tests/lazy_transforms_utils.py +++ b/tests/lazy_transforms_utils.py @@ -82,10 +82,10 @@ def test_resampler_lazy( and isinstance(non_lazy_out, MetaTensor) and non_lazy_out.applied_operations ): - resampler.lazy_evaluation = False + resampler.lazy = False out = resampler.inverse(lazy_out.clone()) ref = resampler.inverse(non_lazy_out.clone()) assert_allclose(out.applied_operations, []) assert_allclose(out.pending_operations, []) assert_allclose(ref, out, type_test=False, rtol=1e-3, atol=1e-3) - resampler.lazy_evaluation = True + resampler.lazy = True diff --git a/tests/padders.py b/tests/padders.py index f3983343e3..02d7b40af6 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -139,7 +139,7 @@ def pad_test_pending_ops(self, input_param, input_shape): # compare assert_allclose(result, expected, rtol=1e-5) if isinstance(result, MetaTensor) and not isinstance(pad_fn, MapTransform): - pad_fn.lazy_evaluation = False + pad_fn.lazy = False inverted = pad_fn.inverse(result) self.assertTrue((not inverted.pending_operations) and (not inverted.applied_operations)) self.assertEqual(inverted.shape, im.shape) From 4e9b6c605e2fe2e3c4ee76b79a2127607f53731a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 14:19:14 +0100 Subject: [PATCH 032/175] Fixing ruff errors for over-long line lengths Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 77f10906bf..e0fea4b6f9 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -395,7 +395,12 @@ def compute_slices( [slice(int(s), int(e)) for s, e in zip(roi_start_t.tolist(), roi_end_t.tolist())] ) - def __call__(self, img: torch.Tensor, slices: tuple[slice, ...], lazy: bool | None = None) -> torch.Tensor: # type: ignore[override] + def __call__( + self, + img: torch.Tensor, + slices: tuple[slice, ...], + lazy: bool | None = None + ) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -1346,7 +1351,13 @@ def __init__( self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) - def __call__(self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore + def __call__( + self, + img: torch.Tensor, + mode: str | None = None, + lazy: bool | None = None, + **pad_kwargs + ) -> torch.Tensor: # type: ignore """ Args: img: data to pad or crop, assuming `img` is channel-first and From ad23bea7a305abfc364a14fce6d800330ebf94e3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 15:20:06 +0100 Subject: [PATCH 033/175] Fix for issue when the 'lazy' property is set to None Signed-off-by: Ben Murray --- monai/transforms/compose.py | 31 +++++++++++++++++++++---------- monai/transforms/transform.py | 20 ++++++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index b20815e4ee..031d2c6686 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -126,8 +126,6 @@ def execute_compose( lazy: bool = False, overrides: dict | None = None, threading: bool = False, - log_stats: bool = False, - verbose: bool = False, ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -161,10 +159,6 @@ def execute_compose( ``overrides`` is set, ``override_keys`` must also be set. threading: whether executing is happening in a threaded environment. If set, copies are made of transforms that have the ``RandomizedTrait`` interface. - log_stats: whether to log the detailed information of data and applied transform when error happened, - for NumPy array and PyTorch Tensor, log the data shape and value range, - for other metadata, log the values directly. default to `False`. - verbose: whether to print debugging info when lazy=True. Returns: A tensorlike, sequence of tensorlikes or dict of tensorlists containing the result of running @@ -278,6 +272,22 @@ class Compose(Randomizable, InvertibleTransform): should ensure that you fully execute the part of the pipeline that generates the data to be cached before caching it. This is quite simply done however, as shown by the following example. + Lazy resampling can be enabled or disabled through the ``lazy`` parameter. This can be specified either as + the LazyMode enum or as an optional boolean. The modes are as follows: + . LazyMode.OFF / False (default): Don't perform any lazy resampling + . LazyMode.ENABLED / None: Perform lazy resampling based on the 'lazy' properties of the transform instances. + . LazyMode.ON / True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties + of the transform instances + + If you only want some of the pipeline to be executed lazily, there are two ways to achieve this. + + The first way is to set LazyMode.ENABLED on your Compose instance and specify for each transform whether you + want it to be lazily executed or not. + + The second way is to set LazyMode.ON on your Compose instance and add ``ApplyPending`` or `ApplyPendingd` + transforms after the final transform in a sequence that you want to execute lazily. This can be done at multiple + points in the pipeline. + Example: # run the part of the pipeline that needs to be cached data = self.transform(data, end=self.post_cache_index) @@ -295,10 +305,11 @@ class Compose(Randomizable, InvertibleTransform): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be - carried out on a transform by transform basis. If True, all lazy transforms will + lazy: whether to enable lazy evaluation for lazy transforms. This can be either string values from the enum + ``LazyMode`` or an optional bool. If LazyMode.OFF, lazy execution is disabled and transforms will be + carried out on a transform by transform basis. If LazyMode.ON, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. - A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of + A `monai.transforms.ApplyPending[d]` transform in the pipeline will trigger the evaluation of the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied @@ -314,7 +325,7 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - lazy: str = LazyMode.OFF, + lazy: str | bool | None = LazyMode.OFF, overrides: dict | None = None, ) -> None: if transforms is None: diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index f3f524abcc..d674552a24 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -94,7 +94,7 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, - lazy: LazyMode = LazyMode.OFF, + lazy: LazyMode | bool | None = LazyMode.OFF, overrides: dict = {}, ) -> list[ReturnType] | ReturnType: """ @@ -110,9 +110,9 @@ def apply_transform( map_items: whether to apply transform to each item in `data`, if `data` is a list or tuple. Defaults to True. unpack_items: whether to unpack parameters using `*`. Defaults to False. - log_stats: whether to log the detailed information of data and applied transform when error happened, - for NumPy array and PyTorch Tensor, log the data shape and value range, - for other metadata, log the values directly. default to `False`. + lazy: whether to execute in lazy mode or not. See ``Compose`` for more information about lazy resampling. + overrides: optional overrides to apply to transform parameters. This parameter is ignored unless transforms + are being executed lazily. Raises: Exception: When ``transform`` raises an exception. @@ -277,7 +277,10 @@ class LazyTransform(Transform, LazyTrait): dictionary transforms to simplify implementation of new lazy transforms. """ - def __init__(self, lazy: bool = False): + def __init__(self, lazy: bool | None = False): + if lazy is not None: + if not isinstance(lazy, bool): + raise TypeError(f"lazy must be a bool but is of type {type(lazy)}") self._lazy = lazy @property @@ -285,9 +288,10 @@ def lazy(self): return self._lazy @lazy.setter - def lazy(self, lazy: bool): - if not isinstance(lazy, bool): - raise TypeError(f"lazy must be a bool but is of type {type(lazy)}") + def lazy(self, lazy: bool | None): + if lazy is not None: + if not isinstance(lazy, bool): + raise TypeError(f"lazy must be a bool but is of type {type(lazy)}") self._lazy = lazy From 81549e2a85ab74876a955ff3503e77f4ae0a4a45 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 15:32:42 +0100 Subject: [PATCH 034/175] Fix for _apply_transform when lazy is None, post merge tidy up Signed-off-by: Ben Murray --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index d674552a24..834a8e88d5 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -82,7 +82,7 @@ def _apply_transform( elif lazy is LazyMode.ENABLED and transform.lazy is False: data = execute_pending_transforms(data, overrides) - lazy_mode = lazy if isinstance(lazy, bool) else LazyMode.as_bool(lazy) + lazy_mode = lazy if isinstance(lazy, bool) or lazy is None else LazyMode.as_bool(lazy) if isinstance(data, tuple) and unpack_parameters: return transform(*data, lazy=lazy_mode) if lazy_tx else transform(*data) From 77eff5b554a4840b3b324622fce2c7f9e930d5ed Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 15:44:48 +0100 Subject: [PATCH 035/175] Removing the redundant check for log_stats from tests.test_nvtx_decorator.TestNVTXRangeDecorator Signed-off-by: Ben Murray --- tests/test_nvtx_decorator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_nvtx_decorator.py b/tests/test_nvtx_decorator.py index 00294fc186..574fd49592 100644 --- a/tests/test_nvtx_decorator.py +++ b/tests/test_nvtx_decorator.py @@ -160,7 +160,6 @@ def test_recursive_tranforms(self, input, transforms): # Check the outputs self.assertEqual(transforms.map_items, transforms_range.map_items) self.assertEqual(transforms.unpack_items, transforms_range.unpack_items) - self.assertEqual(transforms.log_stats, transforms_range.log_stats) np.testing.assert_equal(output.numpy(), output_r.numpy()) @parameterized.expand([TEST_CASE_RECURSIVE_LIST]) From 7c53d2621b7a1b9be7c8313c57665d55fe932e6a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 15:46:17 +0100 Subject: [PATCH 036/175] Further formatting auto-fixes Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- monai/transforms/croppad/array.py | 11 ++--------- tests/test_crop_foreground.py | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 031d2c6686..a8d15d345c 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -29,7 +29,7 @@ # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) from monai.transforms.lazy.functional import execute_pending_transforms -from monai.transforms.traits import ThreadUnsafe, LazyTrait +from monai.transforms.traits import LazyTrait, ThreadUnsafe from monai.transforms.transform import ( # noqa: F401 LazyTransform, MapTransform, diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index e0fea4b6f9..bbbcdd1dad 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -396,10 +396,7 @@ def compute_slices( ) def __call__( - self, - img: torch.Tensor, - slices: tuple[slice, ...], - lazy: bool | None = None + self, img: torch.Tensor, slices: tuple[slice, ...], lazy: bool | None = None ) -> torch.Tensor: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and @@ -1352,11 +1349,7 @@ def __init__( self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) def __call__( - self, - img: torch.Tensor, - mode: str | None = None, - lazy: bool | None = None, - **pad_kwargs + self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs ) -> torch.Tensor: # type: ignore """ Args: diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index 55a94ca917..4435b128ba 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -154,7 +154,7 @@ def test_inverse_pending_ops(self, input_param, image, _expected_data, align_cor crop_fn.lazy = True pending_result = crop_fn(image) self.assertIsInstance(pending_result, MetaTensor) - result = apply_pending(pending_result, overrides={'mode': "nearest", 'align_corners': align_corners})[0] + result = apply_pending(pending_result, overrides={"mode": "nearest", "align_corners": align_corners})[0] inverted = crop_fn.inverse(result) self.assertEqual(image.shape, inverted.shape) self.assertTrue((not inverted.applied_operations) and (not inverted.pending_operations)) From ee5378fe5db9d550b61de3d360387f3152b31718 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 15:58:06 +0100 Subject: [PATCH 037/175] Making default for overrides be None instead of {}. Signed-off-by: Ben Murray --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 834a8e88d5..7014615f94 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -95,7 +95,7 @@ def apply_transform( map_items: bool = True, unpack_items: bool = False, lazy: LazyMode | bool | None = LazyMode.OFF, - overrides: dict = {}, + overrides: dict = None, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. From 28c05d08d466e20fafe2cb66121b5fae0b24e847 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 17:25:43 +0100 Subject: [PATCH 038/175] Changed execute_pending_transforms to not make a shallow copy of dictionary 'data' objects Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 1c3c5ff6f3..36f9793b4f 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -38,7 +38,7 @@ def execute_pending_transforms(data, overrides: dict = None): return tuple(execute_pending_transforms(d) for d in data) if isinstance(data, dict): - d = dict(data) + d = data for k, v in d.items(): if isinstance(v, MetaTensor) and v.has_pending_operations: overrides_ = None if overrides is None else overrides[k] From d96fef09e7228726687a9b818823cd3f693294b4 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 17:35:45 +0100 Subject: [PATCH 039/175] Fixing docstring for Compose Signed-off-by: Ben Murray --- monai/transforms/compose.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index a8d15d345c..1f7f235862 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -274,9 +274,10 @@ class Compose(Randomizable, InvertibleTransform): Lazy resampling can be enabled or disabled through the ``lazy`` parameter. This can be specified either as the LazyMode enum or as an optional boolean. The modes are as follows: - . LazyMode.OFF / False (default): Don't perform any lazy resampling - . LazyMode.ENABLED / None: Perform lazy resampling based on the 'lazy' properties of the transform instances. - . LazyMode.ON / True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties + + * LazyMode.OFF / False (default): Don't perform any lazy resampling + * LazyMode.ENABLED / None: Perform lazy resampling based on the 'lazy' properties of the transform instances. + * LazyMode.ON / True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties of the transform instances If you only want some of the pipeline to be executed lazily, there are two ways to achieve this. From 654cf48e0ee37ccd038d341ca060a3da5ba20090 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 24 Apr 2023 20:07:30 +0100 Subject: [PATCH 040/175] Fixing type complaints Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 2 +- monai/transforms/transform.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 36f9793b4f..556b644969 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -30,7 +30,7 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} -def execute_pending_transforms(data, overrides: dict = None): +def execute_pending_transforms(data, overrides: dict | None = None): if isinstance(data, list): return [execute_pending_transforms(d) for d in data] diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 7014615f94..9186b989af 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -49,7 +49,7 @@ def _apply_transform( data: Any, unpack_parameters: bool = False, lazy: str = LazyMode.OFF, - overrides: dict = None, + overrides: dict | None = None, ) -> ReturnType: """ Perform transformation `transform` with the provided parameters `parameters`. @@ -95,7 +95,7 @@ def apply_transform( map_items: bool = True, unpack_items: bool = False, lazy: LazyMode | bool | None = LazyMode.OFF, - overrides: dict = None, + overrides: dict | None = None, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. From 64ec242386de65f9a379424c480ac47f87799167 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 11:08:52 +0100 Subject: [PATCH 041/175] Fix for tests/tests_integration_lazy_samples. The modified lazy evaluation fixes the bug that allowed pending operations to be present on a metatensor when they should already have been evaluated, so the lazy test handles the lambda without issues Signed-off-by: Ben Murray --- tests/test_integration_lazy_samples.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 018a175a74..d3c0a5b04c 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -152,16 +152,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, saver(item) # just testing the saving saver(in_img) saver(in_seg) - if lazy: - inverted = 0 - try: - inverted = [inverter(b_data) for b_data in decollate_batch(batch_data)] - except RuntimeError as e: - if "Lambda" in str(e): - inverted = None - assert inverted is None, "invert LambdaD + lazy is not supported" - else: - [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error + [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error return ops From f38eb9b9bdbaae2580365a748ce3b4687d9af7a5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 11:21:38 +0100 Subject: [PATCH 042/175] Flake8 fix Signed-off-by: Ben Murray --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 9186b989af..d8618778b6 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -48,7 +48,7 @@ def _apply_transform( transform: Callable[..., ReturnType], data: Any, unpack_parameters: bool = False, - lazy: str = LazyMode.OFF, + lazy: str | bool | None = LazyMode.OFF, overrides: dict | None = None, ) -> ReturnType: """ From 14349ffd26b38657094fe337ec175863d4652cba Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 12:02:06 +0100 Subject: [PATCH 043/175] Flake8 fixes Signed-off-by: Ben Murray --- monai/apps/detection/transforms/dictionary.py | 8 +++++++- monai/apps/reconstruction/transforms/dictionary.py | 9 +++++++-- monai/transforms/compose.py | 2 +- monai/transforms/spatial/array.py | 2 +- monai/transforms/transform.py | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index a692a42369..cada9b84a4 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -17,6 +17,8 @@ from __future__ import annotations +import warnings + from collections.abc import Hashable, Mapping, Sequence from copy import deepcopy from typing import Any @@ -1307,6 +1309,7 @@ class RandRotateBox90d(RandRotate90d): spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. allow_missing_keys: don't raise exception if key is missing. + lazy: """ backend = RotateBox90.backend @@ -1326,7 +1329,10 @@ def __init__( super().__init__(self.image_keys + self.box_keys, prob, max_k, spatial_axes, allow_missing_keys) self.box_ref_image_keys = ensure_tuple_rep(box_ref_image_keys, len(self.box_keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Mapping[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> Mapping[Hashable, torch.Tensor]: + if lazy is True: + warnings.warn(f"RandRotateBox90d cannot be executed lazily; ignoring lazy=True") + self.randomize() d = dict(data) diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 11454b0b6b..94497a4600 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -11,6 +11,8 @@ from __future__ import annotations +import warnings + from collections.abc import Hashable, Mapping, Sequence import numpy as np @@ -213,10 +215,10 @@ class ReferenceBasedSpatialCropd(Cropd): """ def __init__(self, keys: KeysCollection, ref_key: str, allow_missing_keys: bool = False) -> None: - super().__init__(keys, cropper=None, allow_missing_keys=allow_missing_keys) # type: ignore + super().__init__(keys, cropper=None, allow_missing_keys=allow_missing_keys, lazy=False) # type: ignore self.ref_key = ref_key - def __call__(self, data: Mapping[Hashable, Tensor]) -> dict[Hashable, Tensor]: + def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None) -> dict[Hashable, Tensor]: """ This transform can support to crop ND spatial (channel-first) data. It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D @@ -229,6 +231,9 @@ def __call__(self, data: Mapping[Hashable, Tensor]) -> dict[Hashable, Tensor]: Returns: the new data dictionary """ + if lazy is True: + warnings.warn(f"RandRotateBox90d cannot be executed lazily; ignoring lazy=True") + d = dict(data) # compute roi_size according to self.ref_key diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 1f7f235862..69d6aac084 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -123,7 +123,7 @@ def execute_compose( unpack_items: bool = False, start: int = 0, end: int | None = None, - lazy: bool = False, + lazy: str | bool | None = False, overrides: dict | None = None, threading: bool = False, ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 47c62b31a0..5f3cd37d1b 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -1743,7 +1743,7 @@ def __init__( scale_range: RandRange = None, device: torch.device | None = None, dtype: DtypeLike = np.float32, - lazy: bool = None, + lazy: bool = False, ) -> None: """ Args: diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index d8618778b6..fa36aceef6 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -94,7 +94,7 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, - lazy: LazyMode | bool | None = LazyMode.OFF, + lazy: str | bool | None = LazyMode.OFF, overrides: dict | None = None, ) -> list[ReturnType] | ReturnType: """ From 51fda453d2b92216b3ddcefd2fea05a71a24be52 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 11:03:39 +0000 Subject: [PATCH 044/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/apps/detection/transforms/dictionary.py | 2 +- monai/apps/reconstruction/transforms/dictionary.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index cada9b84a4..5e2c500717 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -1331,7 +1331,7 @@ def __init__( def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> Mapping[Hashable, torch.Tensor]: if lazy is True: - warnings.warn(f"RandRotateBox90d cannot be executed lazily; ignoring lazy=True") + warnings.warn("RandRotateBox90d cannot be executed lazily; ignoring lazy=True") self.randomize() d = dict(data) diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 94497a4600..7a4ce8cee0 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -232,7 +232,7 @@ def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None) -> dict[H the new data dictionary """ if lazy is True: - warnings.warn(f"RandRotateBox90d cannot be executed lazily; ignoring lazy=True") + warnings.warn("RandRotateBox90d cannot be executed lazily; ignoring lazy=True") d = dict(data) From c5742c0921490356553ea94dbebcca75de0ec8f8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 12:15:28 +0100 Subject: [PATCH 045/175] Autoformat style fixes Signed-off-by: Ben Murray --- monai/apps/detection/transforms/dictionary.py | 5 +++-- monai/apps/reconstruction/transforms/dictionary.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index cada9b84a4..f574d9486a 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -18,7 +18,6 @@ from __future__ import annotations import warnings - from collections.abc import Hashable, Mapping, Sequence from copy import deepcopy from typing import Any @@ -1329,7 +1328,9 @@ def __init__( super().__init__(self.image_keys + self.box_keys, prob, max_k, spatial_axes, allow_missing_keys) self.box_ref_image_keys = ensure_tuple_rep(box_ref_image_keys, len(self.box_keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> Mapping[Hashable, torch.Tensor]: + def __call__( + self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None + ) -> Mapping[Hashable, torch.Tensor]: if lazy is True: warnings.warn(f"RandRotateBox90d cannot be executed lazily; ignoring lazy=True") diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 94497a4600..20c3c72067 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -12,7 +12,6 @@ from __future__ import annotations import warnings - from collections.abc import Hashable, Mapping, Sequence import numpy as np From 916b0a9f50be03cf57f2bb3d8b11b3e4148619e7 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 12:27:17 +0100 Subject: [PATCH 046/175] Fixing bug introduced by flake8 fix Signed-off-by: Ben Murray --- monai/apps/reconstruction/transforms/dictionary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index a2c7e90279..94b1a44ee7 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -217,7 +217,9 @@ def __init__(self, keys: KeysCollection, ref_key: str, allow_missing_keys: bool super().__init__(keys, cropper=None, allow_missing_keys=allow_missing_keys, lazy=False) # type: ignore self.ref_key = ref_key - def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None) -> dict[Hashable, Tensor]: + def __call__( + self, data: Mapping[Hashable, Tensor], lazy: bool | None = None + ) -> dict[Hashable, Tensor]: """ This transform can support to crop ND spatial (channel-first) data. It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D From c94341f68181e4f21875a585faa3bedc516a4073 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 13:43:08 +0100 Subject: [PATCH 047/175] Removal of obsolete lazy eval code Signed-off-by: Ben Murray --- monai/transforms/compose.py | 68 ------------------------------------- 1 file changed, 68 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 69d6aac084..87ea8af8f3 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -46,74 +46,6 @@ __all__ = ["Compose", "OneOf", "RandomOrder", "SomeOf"] -def evaluate_with_overrides( - data, - upcoming, - lazy: LazyMode = LazyMode.OFF, - overrides: dict | None = None, - override_keys: Sequence[str] | None = None, - verbose: bool = False, -): - """ - The previously applied transform may have been lazily applied to MetaTensor `data` and - made `data.has_pending_operations` equals to True. Given the upcoming transform ``upcoming``, - this function determines whether `data.pending_operations` should be evaluated. If so, it will - evaluate the lazily applied transforms. - - Currently, the conditions for evaluation are: - - - ``lazy`` is ``True``, AND - - the data is a ``MetaTensor`` and has pending operations, AND - - the upcoming transform is an instance of ``Identity`` or ``IdentityD`` or ``None``. - - The returned `data` will then be ready for the ``upcoming`` transform. - - Args: - data: data to be evaluated. - upcoming: the upcoming transform. - lazy: whether to evaluate the pending operations. - override: keyword arguments to apply transforms. - override_keys: to which the override arguments are used when apply transforms. - verbose: whether to print debugging info when evaluate MetaTensor with pending operations. - - """ - if not lazy: - return data # eager evaluation - overrides = (overrides or {}).copy() - if isinstance(data, monai.data.MetaTensor): - if data.has_pending_operations and ((isinstance(upcoming, (mt.Identityd, mt.Identity))) or upcoming is None): - data, _ = mt.apply_pending(data, None, overrides=overrides) - if verbose: - next_name = "final output" if upcoming is None else f"'{upcoming.__class__.__name__}'" - logger.info(f"Evaluated - '{override_keys}' - up-to-date for - {next_name}") - elif verbose: - logger.info( - f"Lazy - '{override_keys}' - upcoming: '{upcoming.__class__.__name__}'" - f"- pending {len(data.pending_operations)}" - ) - return data - override_keys = ensure_tuple(override_keys) - if isinstance(data, dict): - if isinstance(upcoming, MapTransform): - applied_keys = {k for k in data if k in upcoming.keys} - if not applied_keys: - return data - else: - applied_keys = set(data.keys()) - - keys_to_override = {k for k in applied_keys if k in override_keys} - # generate a list of dictionaries with the appropriate override value per key - dict_overrides = to_tuple_of_dictionaries(overrides, override_keys) - for k in data: - if k in keys_to_override: - dict_for_key = dict_overrides[override_keys.index(k)] - data[k] = evaluate_with_overrides(data[k], upcoming, lazy, dict_for_key, k, verbose) - else: - data[k] = evaluate_with_overrides(data[k], upcoming, lazy, None, k, verbose) - - if isinstance(data, (list, tuple)): - return [evaluate_with_overrides(v, upcoming, lazy, overrides, override_keys, verbose) for v in data] - return data def execute_compose( From cc51f2fbaad8351112fc49dbbcaa72fad354654e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 13:45:07 +0100 Subject: [PATCH 048/175] Autoformat shuffle Signed-off-by: Ben Murray --- monai/apps/reconstruction/transforms/dictionary.py | 4 +--- monai/transforms/compose.py | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 94b1a44ee7..5524f5fefa 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -217,9 +217,7 @@ def __init__(self, keys: KeysCollection, ref_key: str, allow_missing_keys: bool super().__init__(keys, cropper=None, allow_missing_keys=allow_missing_keys, lazy=False) # type: ignore self.ref_key = ref_key - def __call__( - self, data: Mapping[Hashable, Tensor], lazy: bool | None = None - ) -> dict[Hashable, Tensor]: + def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None = None) -> dict[Hashable, Tensor]: """ This transform can support to crop ND spatial (channel-first) data. It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 87ea8af8f3..b3ccd55e00 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -46,8 +46,6 @@ __all__ = ["Compose", "OneOf", "RandomOrder", "SomeOf"] - - def execute_compose( data: NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], transforms: Sequence[Any], From 06aad014252c3d2e5c097356b36d92e53541c93c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:45:43 +0000 Subject: [PATCH 049/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/compose.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index b3ccd55e00..2c345ea974 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -22,7 +22,6 @@ import numpy as np import monai -import monai.transforms as mt from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor from monai.transforms.inverse import InvertibleTransform @@ -38,7 +37,7 @@ Transform, apply_transform, ) -from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed, to_tuple_of_dictionaries +from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed from monai.utils.enums import LazyMode logger = get_logger(__name__) From 3ae46e921b9e28e81b7c7038e7e492ed93a52f62 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 14:06:40 +0100 Subject: [PATCH 050/175] Resolving mypy issues Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 4 ++-- monai/transforms/transform.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index bbbcdd1dad..fa38e6d25d 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -397,7 +397,7 @@ def compute_slices( def __call__( self, img: torch.Tensor, slices: tuple[slice, ...], lazy: bool | None = None - ) -> torch.Tensor: # type: ignore[override] + ) -> Any: # type: ignore[override] """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -1350,7 +1350,7 @@ def __init__( def __call__( self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs - ) -> torch.Tensor: # type: ignore + ) -> Any: # type: ignore """ Args: img: data to pad or crop, assuming `img` is channel-first and diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index fa36aceef6..a4083cac56 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -79,7 +79,7 @@ def _apply_transform( if lazy_tx is False or lazy == LazyMode.OFF: data = execute_pending_transforms(data, overrides) - elif lazy is LazyMode.ENABLED and transform.lazy is False: + elif lazy is LazyMode.ENABLED and transform.lazy is False: # ignore[attr-defined] data = execute_pending_transforms(data, overrides) lazy_mode = lazy if isinstance(lazy, bool) or lazy is None else LazyMode.as_bool(lazy) From 81559fa2b9d1427d163d0106fb1acba1fe1b3e23 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 14:43:08 +0100 Subject: [PATCH 051/175] Finally got to grips with mypy troubles Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 8 ++++---- monai/transforms/spatial/array.py | 6 ++++-- monai/transforms/spatial/functional.py | 6 +++--- monai/transforms/transform.py | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index fa38e6d25d..39e48a8988 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -395,9 +395,9 @@ def compute_slices( [slice(int(s), int(e)) for s, e in zip(roi_start_t.tolist(), roi_end_t.tolist())] ) - def __call__( + def __call__( # type: ignore[override] self, img: torch.Tensor, slices: tuple[slice, ...], lazy: bool | None = None - ) -> Any: # type: ignore[override] + ) -> torch.Tensor: """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -1348,9 +1348,9 @@ def __init__( self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) - def __call__( + def __call__( # type: ignore[override] self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs - ) -> Any: # type: ignore + ) -> torch.Tensor: """ Args: img: data to pad or crop, assuming `img` is channel-first and diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 5f3cd37d1b..8b2d8155a2 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -577,7 +577,9 @@ def __init__( self.labels = labels self.lazy = lazy - def __call__(self, data_array: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: + def __call__( + self, data_array: torch.Tensor, lazy: bool | None = None + ) -> torch.Tensor: """ If input type is `MetaTensor`, original affine is extracted with `data_array.affine`. If input type is `torch.Tensor`, original affine is assumed to be identity. @@ -634,7 +636,7 @@ def __call__(self, data_array: torch.Tensor, lazy: bool | None = None) -> torch. lazy_ = self.lazy if lazy is None else lazy return orientation( data_array, affine_np, spatial_ornt, lazy=lazy_, transform_info=self.get_transform_info() - ) # type: ignore + ) # type: ignore[no-any-return] def inverse(self, data: torch.Tensor) -> torch.Tensor: transform = self.pop_transform(data) diff --git a/monai/transforms/spatial/functional.py b/monai/transforms/spatial/functional.py index a739b3e1fc..a52b5d8269 100644 --- a/monai/transforms/spatial/functional.py +++ b/monai/transforms/spatial/functional.py @@ -183,7 +183,7 @@ def spatial_resample( return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore -def orientation(img, original_affine, spatial_ornt, lazy, transform_info): +def orientation(img, original_affine, spatial_ornt, lazy, transform_info) -> torch.Tensor: """ Functional implementation of changing the input image's orientation into the specified based on `spatial_ornt`. This function operates eagerly or lazily according to @@ -221,12 +221,12 @@ def orientation(img, original_affine, spatial_ornt, lazy, transform_info): ) out = _maybe_new_metatensor(img) if lazy: - return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info + return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else meta_info # type: ignore if axes: out = torch.flip(out, dims=axes) if not np.all(full_transpose == np.arange(len(out.shape))): out = out.permute(full_transpose.tolist()) - return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out + return out.copy_meta_from(meta_info) if isinstance(out, MetaTensor) else out # type: ignore def flip(img, sp_axes, lazy, transform_info): diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index a4083cac56..a0cd94ece3 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -79,7 +79,7 @@ def _apply_transform( if lazy_tx is False or lazy == LazyMode.OFF: data = execute_pending_transforms(data, overrides) - elif lazy is LazyMode.ENABLED and transform.lazy is False: # ignore[attr-defined] + elif lazy is LazyMode.ENABLED and transform.lazy is False: # type: ignore[attr-defined] data = execute_pending_transforms(data, overrides) lazy_mode = lazy if isinstance(lazy, bool) or lazy is None else LazyMode.as_bool(lazy) From af9cad14a5b74c85739ef29fe0116ac540f46897 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 14:52:40 +0100 Subject: [PATCH 052/175] Final mypy complaints Signed-off-by: Ben Murray --- monai/apps/auto3dseg/data_analyzer.py | 2 +- tests/test_flip.py | 2 +- tests/test_orientation.py | 2 +- tests/test_orientationd.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/monai/apps/auto3dseg/data_analyzer.py b/monai/apps/auto3dseg/data_analyzer.py index 5252568576..e19db0027c 100644 --- a/monai/apps/auto3dseg/data_analyzer.py +++ b/monai/apps/auto3dseg/data_analyzer.py @@ -212,7 +212,7 @@ def get_all_case_stats(self, key="training", transform_list=None): manager_list = manager.list() processes = [] for rank in range(nprocs): - p = tmp_ctx.Process( + p = tmp_ctx.Process( # type: ignore[attr-defined] target=self._get_all_case_stats, args=(rank, nprocs, manager_list, key, transform_list) ) processes.append(p) diff --git a/tests/test_flip.py b/tests/test_flip.py index 287852c2c1..d7df55fde0 100644 --- a/tests/test_flip.py +++ b/tests/test_flip.py @@ -61,7 +61,7 @@ def test_torch(self, spatial_axis, img: torch.Tensor, track_meta: bool, device): init_param = {"spatial_axis": spatial_axis} xform = Flip(**init_param) call_param = {"img": img} - res = xform(**call_param) + res = xform(**call_param) # type: ignore[arg-type] self.assertEqual(img.shape, res.shape) if track_meta: test_resampler_lazy(xform, res, init_param, call_param) diff --git a/tests/test_orientation.py b/tests/test_orientation.py index 6e89d085d2..aa1c326bdf 100644 --- a/tests/test_orientation.py +++ b/tests/test_orientation.py @@ -190,7 +190,7 @@ def test_ornt_meta( img = MetaTensor(img, affine=affine).to(device) ornt = Orientation(**init_param) call_param = {"data_array": img} - res = ornt(**call_param) + res = ornt(**call_param) # type: ignore[arg-type] if img.ndim in (3, 4): test_resampler_lazy(ornt, res, init_param, call_param) diff --git a/tests/test_orientationd.py b/tests/test_orientationd.py index ddb5dc3e98..cf4eb23d42 100644 --- a/tests/test_orientationd.py +++ b/tests/test_orientationd.py @@ -74,7 +74,7 @@ def test_orntd( img = MetaTensor(img, affine=affine) img = img.to(device) call_param = {"data": {k: img.clone() for k in ornt.keys}} - res = ornt(**call_param) + res = ornt(**call_param) # type: ignore[arg-type] for k in ornt.keys: if img.ndim in (3, 4): test_resampler_lazy(ornt, res, init_param, call_param, output_key=k) @@ -92,7 +92,7 @@ def test_orntd_torch(self, init_param, img: torch.Tensor, track_meta: bool, devi expected_shape = img.shape expected_code = ornt.ornt_transform.axcodes call_param = {"data": {k: img.clone() for k in ornt.keys}} - res = ornt(**call_param) + res = ornt(**call_param) # type: ignore[arg-type] for k in ornt.keys: _im = res[k] np.testing.assert_allclose(_im.shape, expected_shape) From a9c8849ddc4b899d7a67c0bd59046417dba99b8e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 14:56:37 +0100 Subject: [PATCH 053/175] Chasing autofixes again Signed-off-by: Ben Murray --- monai/transforms/spatial/array.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 8b2d8155a2..b058281c55 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -577,9 +577,7 @@ def __init__( self.labels = labels self.lazy = lazy - def __call__( - self, data_array: torch.Tensor, lazy: bool | None = None - ) -> torch.Tensor: + def __call__(self, data_array: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ If input type is `MetaTensor`, original affine is extracted with `data_array.affine`. If input type is `torch.Tensor`, original affine is assumed to be identity. From aadbae3d58dbe5abce10231f001a9519dd028844 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 16:13:29 +0100 Subject: [PATCH 054/175] test_invert_warn_pending is now obsolete due to this no longer being an issue. Signed-off-by: Ben Murray --- tests/test_invert.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_invert.py b/tests/test_invert.py index 0d53b4bf61..7ccc11d36c 100644 --- a/tests/test_invert.py +++ b/tests/test_invert.py @@ -90,16 +90,16 @@ def test_invert(self): set_determinism(seed=None) def test_invert_warn_pending(self): + # this test shouldn't raise a warning or error any more as that issue was fixed + # by https://github.com/Project-MONAI/MONAI/pull/6257 set_determinism(seed=0) im_fname = make_nifti_image(create_test_image_3d(101, 100, 107, noise_max=100)[1]) # label image, discrete transform = Compose( [LoadImage(image_only=True), EnsureChannelFirst(), Orientation("RPS"), Lambda(func=lambda x: x)], - lazy_evaluation=True, + lazy=True, ) output = transform([im_fname for _ in range(2)]) - with self.assertRaises(RuntimeError): # transform id mismatch because of lambda - with self.assertWarns(Warning): # warning of wrong ordering lazy + nonlazy_invertible - transform.inverse(output) + transform.inverse(output) if __name__ == "__main__": From 1cccf9bb9675e9fe4ae74423ac5b9f631aa0af52 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 16:38:03 +0100 Subject: [PATCH 055/175] Adding documentation for ApplyPending/ApplyPendingd. Adding more warnings and lazy overrides for classes which can't yet execute lazily Signed-off-by: Ben Murray --- monai/apps/detection/transforms/dictionary.py | 3 +-- .../reconstruction/transforms/dictionary.py | 2 +- monai/transforms/croppad/array.py | 26 ++++++++++++++++++- monai/transforms/croppad/dictionary.py | 25 ++++++++++++++++++ monai/transforms/lazy/array.py | 8 +++++- monai/transforms/lazy/dictionary.py | 9 +++++-- 6 files changed, 66 insertions(+), 7 deletions(-) diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index 576022034b..0476d4e8f1 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -1332,8 +1332,7 @@ def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> Mapping[Hashable, torch.Tensor]: if lazy is True: - warnings.warn("RandRotateBox90d cannot be executed lazily; ignoring lazy=True") - + warnings.warn("RandRotateBox90d cannot currently execute lazily; ignoring lazy=True") self.randomize() d = dict(data) diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 5524f5fefa..6b96f10cf9 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -231,7 +231,7 @@ def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None = None) -> the new data dictionary """ if lazy is True: - warnings.warn("RandRotateBox90d cannot be executed lazily; ignoring lazy=True") + warnings.warn("ReferenceBasedSpatialCropd cannot currently execute lazily; ignoring lazy=True") d = dict(data) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 39e48a8988..80f1983bfd 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -803,6 +803,10 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ + if lazy is True: + warnings.warn("CropForeground cannot currently execute lazily; " + "ignoring lazy=True set during initialization") + lazy = False LazyTransform.__init__(self, lazy) self.select_fn = select_fn self.channel_indices = ensure_tuple(channel_indices) if channel_indices is not None else None @@ -810,7 +814,7 @@ def __init__( self.allow_smaller = allow_smaller self.return_coords = return_coords self.k_divisible = k_divisible - self.padder = Pad(mode=mode, lazy=lazy, **pad_kwargs) + self.padder = Pad(mode=mode, lazy=False, **pad_kwargs) def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ @@ -882,6 +886,10 @@ def __call__( # type: ignore[override] Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't change the channel dim. """ + if lazy is True: + warnings.warn("CropForeground cannot currently execute lazily; ignoring lazy=True") + lazy = False + box_start, box_end = self.compute_bounding_box(img) lazy_ = self.lazy if lazy is None else lazy cropped = self.crop_pad(img, box_start, box_end, mode, lazy=lazy_, **pad_kwargs) @@ -1050,6 +1058,10 @@ def __init__( allow_smaller: bool = False, lazy: bool = False, ) -> None: + if lazy is True: + warnings.warn("RandCropByPosNegLabel cannot currently execute lazily; " + "ignoring lazy=True set during initialization") + lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.label = label @@ -1122,6 +1134,10 @@ def __call__( randomize: whether to execute the random operations, default to `True`. """ + if lazy is True: + warnings.warn("RandCropByPosNegLabel cannot currently execute lazily; ignoring lazy=True") + lazy = False + if image is None: image = self.image if randomize: @@ -1229,6 +1245,10 @@ def __init__( max_samples_per_class: int | None = None, lazy: bool = False, ) -> None: + if lazy is True: + warnings.warn("RandCropByLabelClasses cannot currently execute lazily; " + "ignoring lazy=True set during initialization") + lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.ratios = ratios @@ -1287,6 +1307,10 @@ def __call__( randomize: whether to execute the random operations, default to `True`. """ + if lazy is True: + warnings.warn("RandCropByLabelClasses cannot currently execute lazily; ignoring lazy=True") + lazy = False + if image is None: image = self.image if randomize: diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 6dd2e139af..2af5476a96 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -17,6 +17,7 @@ from __future__ import annotations +import warnings from collections.abc import Callable, Hashable, Mapping, Sequence from copy import deepcopy from typing import Any @@ -704,6 +705,11 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ + if lazy is True: + warnings.warn("CropForegroundd cannot currently execute lazily; " + "ignoring lazy=True set during initialization") + lazy = False + self.source_key = source_key self.start_coord_key = start_coord_key self.end_coord_key = end_coord_key @@ -720,6 +726,10 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: + if lazy is True: + warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") + lazy = False + d = dict(data) self.cropper: CropForeground box_start, box_end = self.cropper.compute_bounding_box(img=d[self.source_key]) @@ -867,6 +877,10 @@ def __init__( lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) + if lazy is True: + warnings.warn("RandCropByPosNegLabeld cannot currently execute lazily; " + "ignoring lazy=True set during initialization") + lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key @@ -901,6 +915,10 @@ def randomize( def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: + if lazy is True: + warnings.warn("RandCropByPosNegLabeld cannot currently execute lazily; ignoring lazy=True") + lazy = False + d = dict(data) fg_indices = d.pop(self.fg_indices_key, None) bg_indices = d.pop(self.bg_indices_key, None) @@ -1016,6 +1034,10 @@ def __init__( lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) + if lazy is True: + warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; " + "ignoring lazy=True set during initialization") + lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key @@ -1045,6 +1067,9 @@ def randomize( self.cropper.randomize(label=label, indices=indices, image=image) def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: + if lazy is True: + warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") + lazy = False d = dict(data) self.randomize(d.get(self.label_key), d.pop(self.indices_key, None), d.get(self.image_key)) # type: ignore diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index d5bfd27a8e..57e4d9a9b4 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -17,9 +17,15 @@ from monai.transforms.lazy.functional import apply_pending +__all__ = ["ApplyPending"] + class ApplyPending(InvertibleTransform): """ - Apply wraps the apply_pending method and can function as a Transform in an array-based pipeline. + ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure + resampling happens before the next transform. If passed a ``MetaTensor`` that has pending + transforms, it executes those pending transforms and returns the resampled ``MetaTensor`` instance. + + See ``Compose`` for a detailed explanation of the lazy resampling feature. """ def __init__(self): diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index f30061d7bb..0e926b9c1e 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -19,8 +19,13 @@ class ApplyPendingd(InvertibleTransform): """ - Apply wraps the apply method and can function as a Transform in either array or dictionary - mode. + ApplyPendingd can be inserted into a pipeline that is being executed lazily in order + to ensure resampling happens before the next transform. When called, it will check the + keys specified by `self.keys` and, if the value at that key is a ``MetaTensor`` instance, + it will execute all pending transforms on that value, inserting the transformed ``MetaTensor`` + at that key location. + + See ``Compose`` for a detailed explanation of the lazy resampling feature. """ def __init__(self, keys): From 9ae1140f30d5c0add6bdaf453bbfdd4cdcd804cb Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 16:45:52 +0100 Subject: [PATCH 056/175] More autofix Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 16 ++++++++++------ monai/transforms/croppad/dictionary.py | 17 +++++++++++------ monai/transforms/lazy/array.py | 2 +- tests/test_invert.py | 3 +-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 80f1983bfd..f8625b758f 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -804,8 +804,9 @@ def __init__( """ if lazy is True: - warnings.warn("CropForeground cannot currently execute lazily; " - "ignoring lazy=True set during initialization") + warnings.warn( + "CropForeground cannot currently execute lazily; " "ignoring lazy=True set during initialization" + ) lazy = False LazyTransform.__init__(self, lazy) self.select_fn = select_fn @@ -1059,8 +1060,9 @@ def __init__( lazy: bool = False, ) -> None: if lazy is True: - warnings.warn("RandCropByPosNegLabel cannot currently execute lazily; " - "ignoring lazy=True set during initialization") + warnings.warn( + "RandCropByPosNegLabel cannot currently execute lazily; " "ignoring lazy=True set during initialization" + ) lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size @@ -1246,8 +1248,10 @@ def __init__( lazy: bool = False, ) -> None: if lazy is True: - warnings.warn("RandCropByLabelClasses cannot currently execute lazily; " - "ignoring lazy=True set during initialization") + warnings.warn( + "RandCropByLabelClasses cannot currently execute lazily; " + "ignoring lazy=True set during initialization" + ) lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 2af5476a96..688e431e78 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -706,8 +706,9 @@ def __init__( """ if lazy is True: - warnings.warn("CropForegroundd cannot currently execute lazily; " - "ignoring lazy=True set during initialization") + warnings.warn( + "CropForegroundd cannot currently execute lazily; " "ignoring lazy=True set during initialization" + ) lazy = False self.source_key = source_key @@ -878,8 +879,10 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) if lazy is True: - warnings.warn("RandCropByPosNegLabeld cannot currently execute lazily; " - "ignoring lazy=True set during initialization") + warnings.warn( + "RandCropByPosNegLabeld cannot currently execute lazily; " + "ignoring lazy=True set during initialization" + ) lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key @@ -1035,8 +1038,10 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) if lazy is True: - warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; " - "ignoring lazy=True set during initialization") + warnings.warn( + "RandCropByLabelClassesd cannot currently execute lazily; " + "ignoring lazy=True set during initialization" + ) lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index 57e4d9a9b4..25de8a8d7e 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -16,9 +16,9 @@ from monai.transforms.inverse import InvertibleTransform from monai.transforms.lazy.functional import apply_pending - __all__ = ["ApplyPending"] + class ApplyPending(InvertibleTransform): """ ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure diff --git a/tests/test_invert.py b/tests/test_invert.py index 7ccc11d36c..b7c11362ce 100644 --- a/tests/test_invert.py +++ b/tests/test_invert.py @@ -95,8 +95,7 @@ def test_invert_warn_pending(self): set_determinism(seed=0) im_fname = make_nifti_image(create_test_image_3d(101, 100, 107, noise_max=100)[1]) # label image, discrete transform = Compose( - [LoadImage(image_only=True), EnsureChannelFirst(), Orientation("RPS"), Lambda(func=lambda x: x)], - lazy=True, + [LoadImage(image_only=True), EnsureChannelFirst(), Orientation("RPS"), Lambda(func=lambda x: x)], lazy=True ) output = transform([im_fname for _ in range(2)]) transform.inverse(output) From bb2ad0f551507a4752de4823fb6c09dce408518d Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 25 Apr 2023 23:31:53 +0100 Subject: [PATCH 057/175] Restoring lazy.setters for croppad and spatial lazy transforms Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 30 ++++++++++- monai/transforms/croppad/dictionary.py | 37 +++++++++++++ monai/transforms/spatial/array.py | 27 +++++++++- monai/transforms/spatial/dictionary.py | 75 ++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 2 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index f8625b758f..824bd6210c 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -488,7 +488,7 @@ class CenterSpatialCrop(Crop): """ def __init__(self, roi_size: Sequence[int] | int, lazy: bool = False) -> None: - super().__init__(lazy) + super().__init__(lazy=lazy) self.roi_size = roi_size def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: ignore[override] @@ -718,6 +718,11 @@ def set_random_state( self.cropper.set_random_state(seed, state) return self + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def randomize(self, data: Any | None = None) -> None: pass @@ -817,6 +822,11 @@ def __init__( self.k_divisible = k_divisible self.padder = Pad(mode=mode, lazy=False, **pad_kwargs) + @Crop.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + self.padder.lazy = _val + def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ Compute the start points and end points of bounding box to crop. @@ -943,6 +953,10 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: spatial_size=self.spatial_size, w=weight_map[0], n_samples=self.num_samples, r_state=self.R ) # using only the first channel as weight map + @LazyTransform.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + def __call__( self, img: torch.Tensor, @@ -1111,6 +1125,10 @@ def randomize( self.allow_smaller, ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + def __call__( self, img: torch.Tensor, @@ -1291,6 +1309,10 @@ def randomize( self.spatial_size, self.num_samples, _shape, indices_, self.ratios, self.R, self.allow_smaller, self.warn ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + def __call__( self, img: torch.Tensor, @@ -1376,6 +1398,12 @@ def __init__( self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.padder.lazy = val + self.cropper.lazy = val + self._lazy = val + def __call__( # type: ignore[override] self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs ) -> torch.Tensor: diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 688e431e78..734b97d524 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -149,6 +149,12 @@ def __init__( self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + if isinstance(self.padder, LazyTransform): + self.padder.lazy = value + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) lazy_ = self.lazy if lazy is None else lazy @@ -330,6 +336,12 @@ def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool LazyTransform.__init__(self, lazy) self.cropper = cropper + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + if isinstance(self.cropper, LazyTransform): + self.cropper.lazy = value + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) lazy_ = self.lazy if lazy is None else lazy @@ -625,6 +637,11 @@ def __init__( roi_size, num_samples, max_roi_size, random_center, random_size, lazy=lazy ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") @@ -726,6 +743,11 @@ def __init__( super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: if lazy is True: warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") @@ -789,6 +811,11 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: @@ -915,6 +942,11 @@ def randomize( ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: @@ -1071,6 +1103,11 @@ def randomize( ) -> None: self.cropper.randomize(label=label, indices=indices, image=image) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: if lazy is True: warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index b058281c55..c91d51b092 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -418,6 +418,11 @@ def __init__( mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, lazy=lazy ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.sp_resample.lazy = val + @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") def __call__( self, @@ -1377,8 +1382,13 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, lazy: bool = False) -> None: RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) - self.lazy = lazy + + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ @@ -1425,6 +1435,11 @@ def __init__(self, prob: float = 0.1, lazy: bool = False) -> None: self.flipper = Flip(spatial_axis=self._axis) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) if not self._do_transform: @@ -2186,6 +2201,11 @@ def __init__( self.padding_mode: str = padding_mode self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self.affine_grid.lazy = val + self._lazy = val + def __call__( self, img: torch.Tensor, @@ -2373,6 +2393,11 @@ def __init__( self.padding_mode: str = padding_mode self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.rand_affine_grid.lazy = val + def _init_identity_cache(self, lazy: bool): """ Create cache of the identity grid if cache_grid=True and spatial_size is known. diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index ec182d3bc8..d417f41a34 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -209,6 +209,11 @@ def __init__( self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.sp_transform.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -300,6 +305,11 @@ def __init__( self.resampler = ResampleToMatch(lazy=lazy) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.resampler.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -447,6 +457,11 @@ def __init__( self.ensure_same_shape = ensure_same_shape self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.spacing_transform.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -541,6 +556,11 @@ def __init__( ) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.ornt_transform.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -595,6 +615,11 @@ def __init__( self.rotator = Rotate90(k, spatial_axes, lazy=lazy) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.rotator.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -768,6 +793,11 @@ def __init__( self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy=lazy) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.resizer.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -902,6 +932,11 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.affine.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1028,6 +1063,11 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.rand_affine.lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAffined: self.rand_affine.set_random_state(seed, state) super().set_random_state(seed, state) @@ -1417,6 +1457,11 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1474,6 +1519,11 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) return self @@ -1539,6 +1589,11 @@ def __init__( self.flipper = RandAxisFlip(prob=1.0, lazy=lazy) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) self.flipper.set_random_state(seed, state) @@ -1639,6 +1694,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.rotator.lazy = val + self._lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1734,6 +1794,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.rand_rotate.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) self.rand_rotate.set_random_state(seed, state) @@ -1846,6 +1911,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, lazy=lazy, **kwargs) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.zoomer.lazy = val + self._lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1946,6 +2016,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.lazy = lazy + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.rand_zoom.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) From 5abe2941392146b2ae9e8b7fa57575cf9479136a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 00:31:18 +0100 Subject: [PATCH 058/175] Undoing the previous commit until I get to the bottom of the randaffined test failures Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 30 +---------- monai/transforms/croppad/dictionary.py | 37 ------------- monai/transforms/spatial/array.py | 27 +--------- monai/transforms/spatial/dictionary.py | 75 -------------------------- 4 files changed, 2 insertions(+), 167 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 824bd6210c..f8625b758f 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -488,7 +488,7 @@ class CenterSpatialCrop(Crop): """ def __init__(self, roi_size: Sequence[int] | int, lazy: bool = False) -> None: - super().__init__(lazy=lazy) + super().__init__(lazy) self.roi_size = roi_size def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: ignore[override] @@ -718,11 +718,6 @@ def set_random_state( self.cropper.set_random_state(seed, state) return self - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - self.cropper.lazy = value - def randomize(self, data: Any | None = None) -> None: pass @@ -822,11 +817,6 @@ def __init__( self.k_divisible = k_divisible self.padder = Pad(mode=mode, lazy=False, **pad_kwargs) - @Crop.lazy.setter # type: ignore - def lazy(self, _val: bool): - self._lazy = _val - self.padder.lazy = _val - def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ Compute the start points and end points of bounding box to crop. @@ -953,10 +943,6 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: spatial_size=self.spatial_size, w=weight_map[0], n_samples=self.num_samples, r_state=self.R ) # using only the first channel as weight map - @LazyTransform.lazy.setter # type: ignore - def lazy(self, _val: bool): - self._lazy = _val - def __call__( self, img: torch.Tensor, @@ -1125,10 +1111,6 @@ def randomize( self.allow_smaller, ) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, _val: bool): - self._lazy = _val - def __call__( self, img: torch.Tensor, @@ -1309,10 +1291,6 @@ def randomize( self.spatial_size, self.num_samples, _shape, indices_, self.ratios, self.R, self.allow_smaller, self.warn ) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, _val: bool): - self._lazy = _val - def __call__( self, img: torch.Tensor, @@ -1398,12 +1376,6 @@ def __init__( self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.padder.lazy = val - self.cropper.lazy = val - self._lazy = val - def __call__( # type: ignore[override] self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs ) -> torch.Tensor: diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 734b97d524..688e431e78 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -149,12 +149,6 @@ def __init__( self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - if isinstance(self.padder, LazyTransform): - self.padder.lazy = value - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) lazy_ = self.lazy if lazy is None else lazy @@ -336,12 +330,6 @@ def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool LazyTransform.__init__(self, lazy) self.cropper = cropper - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - if isinstance(self.cropper, LazyTransform): - self.cropper.lazy = value - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) lazy_ = self.lazy if lazy is None else lazy @@ -637,11 +625,6 @@ def __init__( roi_size, num_samples, max_roi_size, random_center, random_size, lazy=lazy ) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - self.cropper.lazy = value - def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") @@ -743,11 +726,6 @@ def __init__( super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - self.cropper.lazy = value - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: if lazy is True: warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") @@ -811,11 +789,6 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - self.cropper.lazy = value - def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: @@ -942,11 +915,6 @@ def randomize( ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - self.cropper.lazy = value - def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: @@ -1103,11 +1071,6 @@ def randomize( ) -> None: self.cropper.randomize(label=label, indices=indices, image=image) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, value: bool) -> None: - self._lazy = value - self.cropper.lazy = value - def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: if lazy is True: warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index c91d51b092..b058281c55 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -418,11 +418,6 @@ def __init__( mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, lazy=lazy ) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.sp_resample.lazy = val - @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") def __call__( self, @@ -1382,13 +1377,8 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, lazy: bool = False) -> None: RandomizableTransform.__init__(self, prob) - LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) - - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.flipper.lazy = val - self._lazy = val + self.lazy = lazy def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ @@ -1435,11 +1425,6 @@ def __init__(self, prob: float = 0.1, lazy: bool = False) -> None: self.flipper = Flip(spatial_axis=self._axis) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.flipper.lazy = val - self._lazy = val - def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) if not self._do_transform: @@ -2201,11 +2186,6 @@ def __init__( self.padding_mode: str = padding_mode self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self.affine_grid.lazy = val - self._lazy = val - def __call__( self, img: torch.Tensor, @@ -2393,11 +2373,6 @@ def __init__( self.padding_mode: str = padding_mode self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.rand_affine_grid.lazy = val - def _init_identity_cache(self, lazy: bool): """ Create cache of the identity grid if cache_grid=True and spatial_size is known. diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index d417f41a34..ec182d3bc8 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -209,11 +209,6 @@ def __init__( self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.sp_transform.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -305,11 +300,6 @@ def __init__( self.resampler = ResampleToMatch(lazy=lazy) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.resampler.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -457,11 +447,6 @@ def __init__( self.ensure_same_shape = ensure_same_shape self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.spacing_transform.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -556,11 +541,6 @@ def __init__( ) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.ornt_transform.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -615,11 +595,6 @@ def __init__( self.rotator = Rotate90(k, spatial_axes, lazy=lazy) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.rotator.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -793,11 +768,6 @@ def __init__( self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy=lazy) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.resizer.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -932,11 +902,6 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.affine.lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1063,11 +1028,6 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool) -> None: - self._lazy = val - self.rand_affine.lazy = val - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAffined: self.rand_affine.set_random_state(seed, state) super().set_random_state(seed, state) @@ -1457,11 +1417,6 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.flipper.lazy = val - self._lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1519,11 +1474,6 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.flipper.lazy = val - self._lazy = val - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) return self @@ -1589,11 +1539,6 @@ def __init__( self.flipper = RandAxisFlip(prob=1.0, lazy=lazy) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.flipper.lazy = val - self._lazy = val - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) self.flipper.set_random_state(seed, state) @@ -1694,11 +1639,6 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.rotator.lazy = val - self._lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1794,11 +1734,6 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.rand_rotate.lazy = val - self._lazy = val - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) self.rand_rotate.set_random_state(seed, state) @@ -1911,11 +1846,6 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, lazy=lazy, **kwargs) - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.zoomer.lazy = val - self._lazy = val - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -2016,11 +1946,6 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.lazy = lazy - @LazyTransform.lazy.setter # type: ignore - def lazy(self, val: bool): - self.rand_zoom.lazy = val - self._lazy = val - def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) From c2bf4a752f55f517bdf588c108c620d920979eea Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 09:37:13 +0100 Subject: [PATCH 059/175] Making apis for OneOf/SomeOf/RandomOrder consistent with Compose Adding logger_name to Compose Enhancing testing around Compose API Signed-off-by: Ben Murray --- monai/transforms/compose.py | 56 ++++++++++++++-------- monai/transforms/transform.py | 3 +- tests/test_compose.py | 88 +++++++++++++++++++++++++++++++++-- tests/test_one_of.py | 38 +++++++++++++++ tests/test_random_order.py | 42 +++++++++++++++++ tests/test_some_of.py | 42 +++++++++++++++++ 6 files changed, 244 insertions(+), 25 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 2c345ea974..8c44214f3e 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -55,6 +55,7 @@ def execute_compose( lazy: str | bool | None = False, overrides: dict | None = None, threading: bool = False, + logger_name: str = "monai.transforms.compose.execute_compose", ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -96,6 +97,8 @@ def execute_compose( end_ = len(transforms) if end is None else end if start is None: raise ValueError(f"'start' ({start}) cannot be None") + if start < 0: + raise ValueError(f"'start' ({start}) cannot be less than 0") if start > end_: raise ValueError(f"'start' ({start}) must be less than 'end' ({end_})") if end_ > len(transforms): @@ -257,6 +260,7 @@ def __init__( unpack_items: bool = False, lazy: str | bool | None = LazyMode.OFF, overrides: dict | None = None, + logger_name: str = "monai.transforms.compose.Compose", ) -> None: if transforms is None: transforms = [] @@ -266,6 +270,7 @@ def __init__( self.set_random_state(seed=get_seed()) self.lazy = lazy self.overrides = overrides + self.logger_name = logger_name def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) @@ -353,6 +358,7 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None lazy=self.lazy, # type: ignore overrides=self.overrides, threading=threading, + logger_name=self.logger_name, ) def inverse(self, data): @@ -384,9 +390,6 @@ class OneOf(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - log_stats: whether to log the detailed information of data and applied transform when error happened, - for NumPy array and PyTorch Tensor, log the data shape and value range, - for other metadata, log the values directly. default to `False`. lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be carried out on a transform by transform basis. @@ -399,9 +402,7 @@ class OneOf(Compose): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - override_keys: this optional parameter specifies the keys to which ``overrides`` are to be applied. If - ``overrides`` is set, ``override_keys`` must also be set. - verbose: whether to print debugging info when lazy=True. + logger_name: the name of the logger to use when logging output. """ def __init__( @@ -412,6 +413,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, + logger_name: str = "monai.transforms.compose.OneOf", ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: @@ -424,6 +426,7 @@ def __init__( f"got {len(weights)} and {len(self.transforms)}." ) self.weights = ensure_tuple(self._normalize_probabilities(weights)) + self.logger_name = logger_name def _normalize_probabilities(self, weights): if len(weights) == 0: @@ -452,7 +455,12 @@ def flatten(self): weights.append(w) return OneOf(transforms, weights, self.map_items, self.unpack_items) - def __call__(self, data, start=0, end=None, threading=False): + def __call__(self, data, start=0, end=None, threading=False, lazy: str | bool | None = None): + if start != 0: + raise ValueError(f"OneOf requires 'start' parameter to be 0 (start set to {start})") + if end is not None: + raise ValueError(f"OneOf requires 'end' parameter to be None (end set to {end}") + if len(self.transforms) == 0: return data @@ -469,6 +477,7 @@ def __call__(self, data, start=0, end=None, threading=False): lazy=self.lazy, # type: ignore overrides=self.overrides, threading=threading, + logger_name=self.logger_name, ) # if the data is a mapping (dictionary), append the OneOf transform to the end @@ -514,9 +523,6 @@ class RandomOrder(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - log_stats: whether to log the detailed information of data and applied transform when error happened, - for NumPy array and PyTorch Tensor, log the data shape and value range, - for other metadata, log the values directly. default to `False`. lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be carried out on a transform by transform basis. @@ -529,9 +535,7 @@ class RandomOrder(Compose): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - override_keys: this optional parameter specifies the keys to which ``overrides`` are to be applied. If - ``overrides`` is set, ``override_keys`` must also be set. - verbose: whether to print debugging info when lazy=True. + logger_name: the name of the logger to use when logging output. """ def __init__( @@ -541,12 +545,19 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, + logger_name: str = "monai.transforms.compose.RandomOrder", ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) - def __call__(self, input_, start=0, end=None, threading=False): + def __call__(self, input_, start=0, end=None, threading=False, lazy: str | bool | None = None): + if start != 0: + raise ValueError(f"RandomOrder requires 'start' parameter to be 0 (start set to {start})") + if end is not None: + raise ValueError(f"RandomOrder requires 'end' parameter to be None (end set to {end}") + if len(self.transforms) == 0: return input_ + num = len(self.transforms) applied_order = self.R.permutation(range(num)) @@ -559,6 +570,7 @@ def __call__(self, input_, start=0, end=None, threading=False): unpack_items=self.unpack_items, lazy=self.lazy, threading=threading, + logger_name=self.logger_name, ) # if the data is a mapping (dictionary), append the RandomOrder transform to the end @@ -610,14 +622,12 @@ class SomeOf(Compose): Defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. Defaults to `False`. - log_stats: whether to log the detailed information of data and applied transform when error happened, - for NumPy array and PyTorch Tensor, log the data shape and value range, - for other metadata, log the values directly. Default to `False`. num_transforms: a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. None sets it to `len(transforms)`. Default to `None`. replace: whether to sample with replacement. Defaults to `False`. weights: weights to use in for sampling transforms. Will be normalized to 1. Default: None (uniform). + logger_name: the name of the logger to use when logging output. """ def __init__( @@ -625,13 +635,13 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool = False, *, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, + logger_name: str = "monai.transforms.compose.SomeOf" ) -> None: - super().__init__(transforms, map_items, unpack_items, log_stats) + super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) @@ -688,7 +698,12 @@ def _normalize_probabilities(self, weights): return ensure_tuple(list(weights)) - def __call__(self, data, start=0, end=None, threading=False): + def __call__(self, data, start=0, end=None, threading=False, lazy: str | bool | None = None): + if start != 0: + raise ValueError(f"SomeOf requires 'start' parameter to be 0 (start set to {start})") + if end is not None: + raise ValueError(f"SomeOf requires 'end' parameter to be None (end set to {end}") + if len(self.transforms) == 0: return data @@ -705,6 +720,7 @@ def __call__(self, data, start=0, end=None, threading=False): lazy=self.lazy, overrides=self.overrides, threading=threading, + logger_name=self.logger_name, ) if isinstance(data, monai.data.MetaTensor): self.push_transform(data, extra_info={"applied_order": applied_order}) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index a0cd94ece3..33af65d6b0 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -96,6 +96,7 @@ def apply_transform( unpack_items: bool = False, lazy: str | bool | None = LazyMode.OFF, overrides: dict | None = None, + logger_name: str = "monai.compose.transform.apply_transform", ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -131,7 +132,7 @@ def apply_transform( raise if not isinstance(transform, transforms.compose.Compose): # log the input data information of exact transform in the transform chain - datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False) + datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False, name=logger_name) logger = logging.getLogger(datastats._logger_name) logger.info(f"\n=== Transform input info -- {type(transform).__name__} ===") if isinstance(data, (list, tuple)): diff --git a/tests/test_compose.py b/tests/test_compose.py index e62cd16d7b..f630d8b3c1 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -22,6 +22,8 @@ from monai.data import DataLoader, Dataset from monai.transforms import AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Zoom from monai.transforms.compose import execute_compose +import monai.transforms.croppad.array as ca +import monai.transforms.spatial.array as sa from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -255,14 +257,20 @@ def test_backwards_compatible_imports(self): class TestComposeExecute(unittest.TestCase): - @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) - def test_compose_execute_equivalence(self, keys, pipeline): + + @staticmethod + def data_from_keys(keys): if keys is None: - data = torch.unsqueeze(torch.tensor(np.arange(24 * 32).reshape(24, 32)), axis=0) + data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) else: data = {} for i_k, k in enumerate(keys): - data[k] = torch.unsqueeze(torch.tensor(np.arange(24 * 32)).reshape(24, 32) + i_k * 768, axis=0) + data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) + return data + + @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) + def test_compose_execute_equivalence(self, keys, pipeline): + data = self.data_from_keys(keys) expected = Compose(deepcopy(pipeline))(data) @@ -283,6 +291,63 @@ def test_compose_execute_equivalence(self, keys, pipeline): else: self.assertTrue(torch.allclose(expected, actual)) + @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) + def test_compose_execute_bad_start_param(self, keys, pipeline): + data = self.data_from_keys(keys) + + with self.assertRaises(ValueError): + c = Compose(deepcopy(pipeline)) + c(data, start=None) + + with self.assertRaises(ValueError): + execute_compose(data, deepcopy(pipeline), start=None) + + with self.assertRaises(ValueError): + c = Compose(deepcopy(pipeline)) + c(data, start=-1) + + with self.assertRaises(ValueError): + execute_compose(data, deepcopy(pipeline), start=-1) + + @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) + def test_compose_execute_negative_range(self, keys, pipeline): + data = self.data_from_keys(keys) + + with self.assertRaises(ValueError): + c = Compose(deepcopy(pipeline)) + c(data, start=2, end=1) + + with self.assertRaises(ValueError): + execute_compose(data, deepcopy(pipeline), start=2, end=1) + + @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) + def test_compose_execute_bad_end_param(self, keys, pipeline): + data = self.data_from_keys(keys) + + with self.assertRaises(ValueError): + c = Compose(deepcopy(pipeline)) + c(data, end=len(pipeline)+1) + + with self.assertRaises(ValueError): + execute_compose(data, deepcopy(pipeline), end=len(pipeline)+1) + + + @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) + def test_compose_execute_empty_range(self, keys, pipeline): + data = self.data_from_keys(keys) + + c = Compose(deepcopy(pipeline)) + for i in range(len(pipeline)): + result = c(data, start=i, end=i) + self.assertIs(data, result) + + @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) + def test_compose_with_logger_name(self, keys, pipeline): + data = self.data_from_keys(keys) + + c = Compose(deepcopy(pipeline), logger_name="a_logger_name") + result = c(data) + class TestOps: @staticmethod @@ -318,6 +383,16 @@ def _inner(data1, data2): class TestComposeExecuteWithFlags(unittest.TestCase): @parameterized.expand(TEST_COMPOSE_EXECUTE_FLAG_TEST_CASES) def test_compose_execute_equivalence_with_flags(self, flags, data, pipeline): + @staticmethod + def data_from_keys(keys): + if keys is None: + data = torch.unsqueeze(torch.tensor(np.arange(24 * 32).reshape(24, 32)), dim=0) + else: + data = {} + for i_k, k in enumerate(keys): + data[k] = torch.unsqueeze(torch.tensor(np.arange(24 * 32)).reshape(24, 32) + i_k * 768, dim=0) + return data + expected = Compose(pipeline, **flags)(data) for cutoff in range(len(pipeline)): @@ -337,6 +412,11 @@ def test_compose_execute_equivalence_with_flags(self, flags, data, pipeline): else: self.assertTrue(expected, actual) +# TEST_COMPOSE_LAZY_FLAG_CASES = [ +# [sa.Spacing(), sa.Flip(), sa.Flip(), sa.Rotate90(), ca.ResizeWithPadOrCro()], +# [], +# ] + if __name__ == "__main__": unittest.main() diff --git a/tests/test_one_of.py b/tests/test_one_of.py index 36980c23a7..d38e49bce4 100644 --- a/tests/test_one_of.py +++ b/tests/test_one_of.py @@ -17,6 +17,8 @@ import numpy as np from parameterized import parameterized +import torch + from monai.data import MetaTensor from monai.transforms import ( InvertibleTransform, @@ -29,6 +31,9 @@ Resized, Transform, ) +import monai.transforms.intensity.array as ia +import monai.transforms.spatial.array as sa +import monai.transforms.spatial.dictionary as sd from monai.transforms.compose import Compose from monai.transforms.transform import MapTransform from monai.utils.enums import TraceKeys @@ -227,5 +232,38 @@ def test_one_of(self): self.assertAlmostEqual(counts[2] / 10000, 0.25, delta=1.0) +TEST_ONEOF_EXTENDED_TEST_CASES = [ + [None, tuple()], + [None, (sa.Rotate(np.pi / 8),)], + [None, (sa.Flip(0), sa.Flip(1), sa.Rotate90(1), sa.Zoom(0.8), ia.NormalizeIntensity())], + [("a",), (sd.Rotated(("a",), np.pi / 8),)], +] + + +class TestOneOfAPITests(unittest.TestCase): + + @staticmethod + def data_from_keys(keys): + if keys is None: + data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) + else: + data = {} + for i_k, k in enumerate(keys): + data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) + return data + + @parameterized.expand(TEST_ONEOF_EXTENDED_TEST_CASES) + def test_execute_change_start_end(self, keys, pipeline): + data = self.data_from_keys(keys) + + with self.assertRaises(ValueError): + c = OneOf(deepcopy(pipeline)) + c(data, start=1) + + with self.assertRaises(ValueError): + c = OneOf(deepcopy(pipeline)) + c(data, end=1) + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_random_order.py b/tests/test_random_order.py index 9ed22d30ae..12f72614b0 100644 --- a/tests/test_random_order.py +++ b/tests/test_random_order.py @@ -13,11 +13,20 @@ import unittest +from copy import deepcopy + from parameterized import parameterized +import numpy as np + +import torch + from monai.data import MetaTensor from monai.transforms import RandomOrder from monai.transforms.compose import Compose +import monai.transforms.intensity.array as ia +import monai.transforms.spatial.array as sa +import monai.transforms.spatial.dictionary as sd from monai.utils import set_determinism from monai.utils.enums import TraceKeys from tests.test_one_of import A, B, C, Inv, NonInv, X, Y @@ -98,5 +107,38 @@ def test_inverse(self, transform, invertible, use_metatensor): self.assertDictEqual(fwd_data[i], _fwd_inv_data) +TEST_RANDOM_ORDER_EXTENDED_TEST_CASES = [ + [None, tuple()], + [None, (sa.Rotate(np.pi / 8),)], + [None, (sa.Flip(0), sa.Flip(1), sa.Rotate90(1), sa.Zoom(0.8), ia.NormalizeIntensity())], + [("a",), (sd.Rotated(("a",), np.pi / 8),)], +] + + +class TestRandomOrderAPITests(unittest.TestCase): + + @staticmethod + def data_from_keys(keys): + if keys is None: + data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) + else: + data = {} + for i_k, k in enumerate(keys): + data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) + return data + + @parameterized.expand(TEST_RANDOM_ORDER_EXTENDED_TEST_CASES) + def test_execute_change_start_end(self, keys, pipeline): + data = self.data_from_keys(keys) + + with self.assertRaises(ValueError): + c = RandomOrder(deepcopy(pipeline)) + c(data, start=1) + + with self.assertRaises(ValueError): + c = RandomOrder(deepcopy(pipeline)) + c(data, end=1) + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_some_of.py b/tests/test_some_of.py index 0cc903bb2d..d934fcab2e 100644 --- a/tests/test_some_of.py +++ b/tests/test_some_of.py @@ -13,11 +13,20 @@ import unittest +from copy import deepcopy + from parameterized import parameterized +import numpy as np + +import torch + from monai.data import MetaTensor from monai.transforms import TraceableTransform, Transform from monai.transforms.compose import Compose, SomeOf +import monai.transforms.intensity.array as ia +import monai.transforms.spatial.array as sa +import monai.transforms.spatial.dictionary as sd from monai.utils import set_determinism from monai.utils.enums import TraceKeys from tests.test_one_of import NonInv @@ -206,5 +215,38 @@ def test_bad_num_transforms(self): self.assertRaises(ValueError, SomeOf, (A(), B(), C()), num_transforms=("a", 1)) +TEST_SOMEOF_EXTENDED_TEST_CASES = [ + [None, tuple()], + [None, (sa.Rotate(np.pi / 8),)], + [None, (sa.Flip(0), sa.Flip(1), sa.Rotate90(1), sa.Zoom(0.8), ia.NormalizeIntensity())], + [("a",), (sd.Rotated(("a",), np.pi / 8),)], +] + + +class TestSomeOfAPITests(unittest.TestCase): + + @staticmethod + def data_from_keys(keys): + if keys is None: + data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) + else: + data = {} + for i_k, k in enumerate(keys): + data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) + return data + + @parameterized.expand(TEST_SOMEOF_EXTENDED_TEST_CASES) + def test_execute_change_start_end(self, keys, pipeline): + data = self.data_from_keys(keys) + + with self.assertRaises(ValueError): + c = SomeOf(deepcopy(pipeline)) + c(data, start=1) + + with self.assertRaises(ValueError): + c = SomeOf(deepcopy(pipeline)) + c(data, end=1) + + if __name__ == "__main__": unittest.main() From b16a87fce2ecf51a10efe28ca9eb9bfbf2aa89ba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:38:25 +0000 Subject: [PATCH 060/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_compose.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index f630d8b3c1..29452d8e84 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -22,8 +22,6 @@ from monai.data import DataLoader, Dataset from monai.transforms import AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Zoom from monai.transforms.compose import execute_compose -import monai.transforms.croppad.array as ca -import monai.transforms.spatial.array as sa from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -346,7 +344,7 @@ def test_compose_with_logger_name(self, keys, pipeline): data = self.data_from_keys(keys) c = Compose(deepcopy(pipeline), logger_name="a_logger_name") - result = c(data) + c(data) class TestOps: From 816666e807b48410e0bc5334e5d1e5fc03bd7162 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 10:42:26 +0100 Subject: [PATCH 061/175] Removing partial docstring for 'lazy', which isn't on the init parameter list anyway Signed-off-by: Ben Murray --- monai/apps/detection/transforms/dictionary.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index 0476d4e8f1..853971adc9 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -1308,7 +1308,6 @@ class RandRotateBox90d(RandRotate90d): spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. allow_missing_keys: don't raise exception if key is missing. - lazy: """ backend = RotateBox90.backend From 0e10c24ac712d05c0695599c178a9b9178bc099f Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 10:45:14 +0100 Subject: [PATCH 062/175] Adding execute_pending_transforms to lazy/functional __all__ Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 556b644969..edefc108e7 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -25,7 +25,7 @@ ) from monai.utils import LazyAttr, look_up_option -__all__ = ["apply_pending"] +__all__ = ["apply_pending", "execute_pending_transforms"] __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} From f323ff97ae780b6fbbf48981de946bef38231633 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 13:39:59 +0100 Subject: [PATCH 063/175] Removing LazyMode; lazy is now True / False / None Adding 'logger_name' to Compose and related Signed-off-by: Ben Murray --- monai/transforms/compose.py | 33 ++++++++++++++------------ monai/transforms/lazy/functional.py | 19 ++++++++++----- monai/transforms/transform.py | 15 ++++++------ monai/utils/enums.py | 31 ------------------------ tests/test_integration_lazy_samples.py | 5 ++-- 5 files changed, 40 insertions(+), 63 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 8c44214f3e..1214d00db6 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -38,7 +38,7 @@ apply_transform, ) from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed -from monai.utils.enums import LazyMode + logger = get_logger(__name__) @@ -111,8 +111,10 @@ def execute_compose( for _transform in transforms[start:end]: if threading: _transform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform - data = apply_transform(_transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides) - data = execute_pending_transforms(data, overrides) + data = apply_transform( + _transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides, logger_name=logger_name + ) + data = execute_pending_transforms(data, overrides, logger_name=logger_name) return data @@ -204,20 +206,20 @@ class Compose(Randomizable, InvertibleTransform): should ensure that you fully execute the part of the pipeline that generates the data to be cached before caching it. This is quite simply done however, as shown by the following example. - Lazy resampling can be enabled or disabled through the ``lazy`` parameter. This can be specified either as - the LazyMode enum or as an optional boolean. The modes are as follows: + Lazy resampling can be enabled or disabled through the ``lazy`` parameter. This is specified as an + optional boolean parameter. - * LazyMode.OFF / False (default): Don't perform any lazy resampling - * LazyMode.ENABLED / None: Perform lazy resampling based on the 'lazy' properties of the transform instances. - * LazyMode.ON / True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties + * False (default): Don't perform any lazy resampling + * None: Perform lazy resampling based on the 'lazy' properties of the transform instances. + * True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties of the transform instances If you only want some of the pipeline to be executed lazily, there are two ways to achieve this. - The first way is to set LazyMode.ENABLED on your Compose instance and specify for each transform whether you + The first way is to set lazy=True on your Compose instance and specify for each transform whether you want it to be lazily executed or not. - The second way is to set LazyMode.ON on your Compose instance and add ``ApplyPending`` or `ApplyPendingd` + The second way is to set lazy=True on your Compose instance and add ``ApplyPending`` or `ApplyPendingd` transforms after the final transform in a sequence that you want to execute lazily. This can be done at multiple points in the pipeline. @@ -238,10 +240,11 @@ class Compose(Randomizable, InvertibleTransform): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - lazy: whether to enable lazy evaluation for lazy transforms. This can be either string values from the enum - ``LazyMode`` or an optional bool. If LazyMode.OFF, lazy execution is disabled and transforms will be - carried out on a transform by transform basis. If LazyMode.ON, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. + lazy: whether to enable lazy evaluation for lazy transforms. This is an optional bool that can take + the following values. If lazy=False, lazy execution is disabled and transforms will be + carried out on a transform by transform basis. If lazy=True, all lazy transforms will + be executed by accumulating changes and resampling as few times as possible. If lazy is None, + Compose will perform lazy execution on lazy transforms that have their lazy flag set to True. A `monai.transforms.ApplyPending[d]` transform in the pipeline will trigger the evaluation of the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden @@ -258,7 +261,7 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - lazy: str | bool | None = LazyMode.OFF, + lazy: bool | None = False, overrides: dict | None = None, logger_name: str = "monai.transforms.compose.Compose", ) -> None: diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index edefc108e7..ee41683e14 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -30,28 +30,35 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} -def execute_pending_transforms(data, overrides: dict | None = None): +def execute_pending_transforms( + data, overrides: dict | None = None, logger_name: str = "monai.lazy.functional.execute_pending_transforms" + ): if isinstance(data, list): - return [execute_pending_transforms(d) for d in data] + return [execute_pending_transforms(d, logger_name=logger_name) for d in data] if isinstance(data, tuple): - return tuple(execute_pending_transforms(d) for d in data) + return tuple(execute_pending_transforms(d, logger_name=logger_name) for d in data) if isinstance(data, dict): d = data for k, v in d.items(): if isinstance(v, MetaTensor) and v.has_pending_operations: overrides_ = None if overrides is None else overrides[k] - d[k], _ = apply_pending(v, overrides=overrides_) + d[k], _ = apply_pending(v, overrides=overrides_, logger_name=logger_name) return d if isinstance(data, MetaTensor) and data.has_pending_operations: - data, _ = apply_pending(data, overrides=overrides) + data, _ = apply_pending(data, overrides=overrides, logger_name=logger_name) return data -def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None): +def apply_pending( + data: torch.Tensor | MetaTensor, + pending: list | None = None, + overrides: dict | None = None, + logger_name: str = "monai.lazy.functional.apply_pending", +): """ This method applies pending transforms to `data` tensors. Currently, only 2d and 3d input are supported. diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 33af65d6b0..0bdb812fe1 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -28,7 +28,7 @@ from monai.transforms.lazy.functional import execute_pending_transforms from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first -from monai.utils.enums import LazyMode, TransformBackends +from monai.utils.enums import TransformBackends from monai.utils.misc import MONAIEnvVars __all__ = [ @@ -48,7 +48,7 @@ def _apply_transform( transform: Callable[..., ReturnType], data: Any, unpack_parameters: bool = False, - lazy: str | bool | None = LazyMode.OFF, + lazy: bool | None = False, overrides: dict | None = None, ) -> ReturnType: """ @@ -77,16 +77,15 @@ def _apply_transform( lazy_tx = isinstance(transform, LazyTrait) - if lazy_tx is False or lazy == LazyMode.OFF: + if lazy_tx is False or lazy == False: data = execute_pending_transforms(data, overrides) - elif lazy is LazyMode.ENABLED and transform.lazy is False: # type: ignore[attr-defined] + elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] data = execute_pending_transforms(data, overrides) - lazy_mode = lazy if isinstance(lazy, bool) or lazy is None else LazyMode.as_bool(lazy) if isinstance(data, tuple) and unpack_parameters: - return transform(*data, lazy=lazy_mode) if lazy_tx else transform(*data) + return transform(*data, lazy=lazy) if lazy_tx else transform(*data) - return transform(data, lazy=lazy_mode) if lazy_tx else transform(data) + return transform(data, lazy=lazy) if lazy_tx else transform(data) def apply_transform( @@ -94,7 +93,7 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, - lazy: str | bool | None = LazyMode.OFF, + lazy: bool | None = False, overrides: dict | None = None, logger_name: str = "monai.compose.transform.apply_transform", ) -> list[ReturnType] | ReturnType: diff --git a/monai/utils/enums.py b/monai/utils/enums.py index e7d5237698..71839ae384 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -648,37 +648,6 @@ class LazyAttr(StrEnum): RESAMPLE_MODE = "lazy_resample_mode" -class LazyMode(StrEnum): - """ - Lazy evaluation modes for executing processing pipelines (ie. Compose). These modes control how transforms - that can execute lazily are executed by the pipeline: - 'OFF' indicates that the pipeline should not be executed lazily - 'ENABLED' indicates that the pipeline can be executed lazily, but this will only be done for transforms - that have ``lazy`` set to True - 'ON' indicates that all transforms capable of being executed lazily will be executed lazily - See: :py:class: monai.transforms.compose.Compose for more details. - """ - - OFF = "off" - ENABLED = "enabled" - ON = "on" - - @staticmethod - def as_bool(lazy_mode): - if lazy_mode == LazyMode.OFF: - return False - - if lazy_mode == LazyMode.ON: - return True - - if lazy_mode == LazyMode.ENABLED: - return None - - raise ValueError( - "'lazy_mode' must be one of LazyMode.OFF, LazyMode.ENABLED or LazyMode.ON, " f"but is {lazy_mode}" - ) - - class BundleProperty(StrEnum): """ Bundle property fields: diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index d3c0a5b04c..8dbf76b6a1 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -25,7 +25,6 @@ import monai.transforms as mt from monai.data import create_test_image_3d, decollate_batch from monai.utils import set_determinism -from monai.utils.enums import LazyMode from tests.utils import HAS_CUPY, DistTestCase, SkipIfBeforePyTorchVersion, skip_if_quick @@ -188,10 +187,10 @@ def train_and_infer(self, idx=0): _readers = ("itkreader", "nibabelreader") _w = 0 results = run_training_test( - self.data_dir, device=self.device, cachedataset=idx, readers=_readers, num_workers=_w, lazy=LazyMode.ON + self.data_dir, device=self.device, cachedataset=idx, readers=_readers, num_workers=_w, lazy=True ) results_expected = run_training_test( - self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=LazyMode.OFF + self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=False ) self.assertFalse(np.allclose(results, [0])) self.assertFalse(np.allclose(results_expected, [0])) From 4b69ec6f2ae01c2716a3473bbf8a963d25eed0d7 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 15:05:27 +0100 Subject: [PATCH 064/175] Renamed execute_pending_transforms to apply_pending_transforms. Added documentation to apply_pending_transforms and apply_pending Signed-off-by: Ben Murray --- monai/transforms/compose.py | 12 ++++--- monai/transforms/lazy/functional.py | 53 +++++++++++++++++++++++++---- monai/transforms/transform.py | 6 ++-- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 1214d00db6..916b5d2915 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -27,7 +27,7 @@ from monai.transforms.inverse import InvertibleTransform # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) -from monai.transforms.lazy.functional import execute_pending_transforms +from monai.transforms.lazy.functional import apply_pending_transforms from monai.transforms.traits import LazyTrait, ThreadUnsafe from monai.transforms.transform import ( # noqa: F401 LazyTransform, @@ -55,7 +55,7 @@ def execute_compose( lazy: str | bool | None = False, overrides: dict | None = None, threading: bool = False, - logger_name: str = "monai.transforms.compose.execute_compose", + logger_name: str | None = None, ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -114,7 +114,7 @@ def execute_compose( data = apply_transform( _transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides, logger_name=logger_name ) - data = execute_pending_transforms(data, overrides, logger_name=logger_name) + data = apply_pending_transforms(data, overrides, logger_name=logger_name) return data @@ -254,6 +254,8 @@ class Compose(Randomizable, InvertibleTransform): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. + logger_name: this optional parameter allows you to specify a logger by name. If this is not set + it defaults to 'Compose' """ def __init__( @@ -263,7 +265,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: str = "monai.transforms.compose.Compose", + logger_name: str | None = None, ) -> None: if transforms is None: transforms = [] @@ -273,7 +275,7 @@ def __init__( self.set_random_state(seed=get_seed()) self.lazy = lazy self.overrides = overrides - self.logger_name = logger_name + self.logger_name = self.__class__.__name__ if logger_name is None else logger_name def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index ee41683e14..0a49ca2d58 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -25,19 +25,40 @@ ) from monai.utils import LazyAttr, look_up_option -__all__ = ["apply_pending", "execute_pending_transforms"] +__all__ = ["apply_pending", "apply_pending_transforms"] __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} -def execute_pending_transforms( - data, overrides: dict | None = None, logger_name: str = "monai.lazy.functional.execute_pending_transforms" +def apply_pending_transforms( + data, overrides: dict | None = None, logger_name: str = "monai.lazy.functional.apply_pending_transforms" ): + """ + apply_pending_transforms iterates over a tuple, list, or dictionary of data, recursively calling itself + to get a single tensor. If that tensor is a MetaTensor with pending lazy transforms, it then calls + ``apply_pending_to_tensor`` on each element to perform the executing of the pending transforms. + + This method optionally takes a set of overrides that can be used to change specific parameters on the + transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used + to override the default logger, to provide telemetry during the execution of pending transforms. + + This method is intended primarily for use by ``execute_compose`` and other methods that handle the + underlying execution of transform pipelines. You should not need to use it in the general case, unless + you are developing functionality to perform such operations. + + Args: + data: a ``torch.Tensor`` or ``MetaTensor``, or list, tuple or dictionary of tensors. + overrides: An optional dictionary that specifies parameters that can be used to override transform + arguments when they are called + logger_name: An optional name for a logger to be used instead of the default logger. + Returns: + + """ if isinstance(data, list): - return [execute_pending_transforms(d, logger_name=logger_name) for d in data] + return [apply_pending_transforms(d, logger_name=logger_name) for d in data] if isinstance(data, tuple): - return tuple(execute_pending_transforms(d, logger_name=logger_name) for d in data) + return tuple(apply_pending_transforms(d, logger_name=logger_name) for d in data) if isinstance(data, dict): d = data @@ -61,7 +82,27 @@ def apply_pending( ): """ This method applies pending transforms to `data` tensors. - Currently, only 2d and 3d input are supported. + Currently, only 2d and 3d inputs are supported. + + This method is designed to be called by ``apply_pending_transforms`` and other methods / classes + that are part of the implementation of lazy resampling. In general, you should not need to tall + this method unless you are directly developing custom lazy execution strategies. + + It works by calculating the overall effect of the accumulated pending transforms. When it runs + out of pending transforms or when it finds incompatibilities between the accumulated pending + transform and the next pending transform, it then applies the accumulated transform in a call to + '`resample``. + + Pending transforms are incompatible with each other if one or more of the arguments in the pending + transforms differ. These are parameters such as 'mode', 'padding_mode', 'dtype' and so forth. If + a pending transform doesn't have a given parameter, it is considered compatible with the + accumulated transform. If a subsequent transform has a parameter that is incompatible with + the accumulated transform (e.g. 'mode' of 'bilinear' vs. 'mode' of 'nearest'), an intermediate + resample will be performed and the accumulated transform reset to its starting state. + + After resampling, the pending transforms are pushed to the ``applied_transforms`` field of the + resulting MetaTensor. Note, if a torch.tensor is passed to this method along with a list of + pending transforms, the resampled tensor will be wrapped in a MetaTensor before being returned. Args: data: A torch Tensor or a monai MetaTensor. diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 0bdb812fe1..ba88a9ebf2 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -25,7 +25,7 @@ from monai import config, transforms from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor -from monai.transforms.lazy.functional import execute_pending_transforms +from monai.transforms.lazy.functional import apply_pending_transforms from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first from monai.utils.enums import TransformBackends @@ -78,9 +78,9 @@ def _apply_transform( lazy_tx = isinstance(transform, LazyTrait) if lazy_tx is False or lazy == False: - data = execute_pending_transforms(data, overrides) + data = apply_pending_transforms(data, overrides) elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] - data = execute_pending_transforms(data, overrides) + data = apply_pending_transforms(data, overrides) if isinstance(data, tuple) and unpack_parameters: return transform(*data, lazy=lazy) if lazy_tx else transform(*data) From 29ce2b71c3e336040733076243774e56e672c0d1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:06:48 +0000 Subject: [PATCH 065/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index ba88a9ebf2..6adf6ded34 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -77,7 +77,7 @@ def _apply_transform( lazy_tx = isinstance(transform, LazyTrait) - if lazy_tx is False or lazy == False: + if lazy_tx is False or lazy is False: data = apply_pending_transforms(data, overrides) elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] data = apply_pending_transforms(data, overrides) From 6774aaa839a86ed5d6da95c7577238bd15012a93 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 15:08:12 +0100 Subject: [PATCH 066/175] Removed "LazyMode" from enums.py __all__ Signed-off-by: Ben Murray --- monai/utils/enums.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 71839ae384..be546a903b 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -56,7 +56,6 @@ "HoVerNetMode", "HoVerNetBranch", "LazyAttr", - "LazyMode", "BundleProperty", "BundlePropertyConfig", "AlgoKeys", From 1c7d172d8f2dfc2c480f1bcfd261700e0bc211bb Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 15:50:12 +0100 Subject: [PATCH 067/175] Modified ApplyPending/ApplyPendingd in the light of the override issue More work on loggin_name for Compose-based classes Signed-off-by: Ben Murray --- monai/transforms/compose.py | 11 ++++++----- monai/transforms/lazy/array.py | 17 ++++------------ monai/transforms/lazy/dictionary.py | 30 ++++++++++++++--------------- monai/transforms/lazy/functional.py | 12 ++++++------ tests/test_compose.py | 7 +++---- tests/test_one_of.py | 10 ++++------ tests/test_random_order.py | 12 ++++-------- tests/test_some_of.py | 12 ++++-------- 8 files changed, 46 insertions(+), 65 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 916b5d2915..805d820c19 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -39,7 +39,6 @@ ) from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed - logger = get_logger(__name__) __all__ = ["Compose", "OneOf", "RandomOrder", "SomeOf"] @@ -418,7 +417,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str = "monai.transforms.compose.OneOf", + logger_name: str | None = None, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: @@ -431,7 +430,7 @@ def __init__( f"got {len(weights)} and {len(self.transforms)}." ) self.weights = ensure_tuple(self._normalize_probabilities(weights)) - self.logger_name = logger_name + self.logger_name = self.__class__.__name__ if logger_name is None else logger_name def _normalize_probabilities(self, weights): if len(weights) == 0: @@ -550,9 +549,10 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str = "monai.transforms.compose.RandomOrder", + logger_name: str | None = None, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) + self.logger_name = self.__class__.__name__ if logger_name is None else logger_name def __call__(self, input_, start=0, end=None, threading=False, lazy: str | bool | None = None): if start != 0: @@ -644,12 +644,13 @@ def __init__( num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, - logger_name: str = "monai.transforms.compose.SomeOf" + logger_name: str | None = None, ) -> None: super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) + self.logger_name = self.__class__.__name__ if logger_name is None else logger_name def _ensure_valid_num_transforms(self, num_transforms: int | tuple[int, int] | None) -> tuple: if ( diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index 25de8a8d7e..c4e873af73 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -9,12 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. - from __future__ import annotations -from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform -from monai.transforms.lazy.functional import apply_pending __all__ = ["ApplyPending"] @@ -22,20 +19,14 @@ class ApplyPending(InvertibleTransform): """ ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure - resampling happens before the next transform. If passed a ``MetaTensor`` that has pending - transforms, it executes those pending transforms and returns the resampled ``MetaTensor`` instance. + resampling happens before the next transform. It doesn't do anything itself, but its presence + causes the pipeline to be executed as ApplyPending doesn't implement ```LazyTrait``. See ``Compose`` for a detailed explanation of the lazy resampling feature. """ - def __init__(self): - super().__init__() - - def __call__(self, data, *args, **kwargs): - if isinstance(data, MetaTensor): - return apply_pending(data, *args, **kwargs) - + def __call__(self, data): return data def inverse(self, data): - return self(data) + return data diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 0e926b9c1e..e58cf7f8ca 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -12,38 +12,38 @@ from __future__ import annotations +from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor +from monai.transforms import MapTransform from monai.transforms.inverse import InvertibleTransform from monai.transforms.lazy.functional import apply_pending +__all__ = ["ApplyPendingd"] -class ApplyPendingd(InvertibleTransform): + +class ApplyPendingd(InvertibleTransform, MapTransform): """ ApplyPendingd can be inserted into a pipeline that is being executed lazily in order - to ensure resampling happens before the next transform. When called, it will check the - keys specified by `self.keys` and, if the value at that key is a ``MetaTensor`` instance, - it will execute all pending transforms on that value, inserting the transformed ``MetaTensor`` - at that key location. + to ensure resampling happens before the next transform. It doesn't do anything itself, + but its presence causes the pipeline to be executed as it doesn't implement ``LazyTrait`` See ``Compose`` for a detailed explanation of the lazy resampling feature. """ - def __init__(self, keys): - super().__init__() - self.keys = keys + def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False): + super().__init__(keys, allow_missing_keys) - def __call__(self, data, **kwargs): + def __call__(self, data): if not isinstance(data, dict): raise ValueError("'data' must be of type dict but is '{type(data)}'") - rd = dict(data) - for k in self.keys: - if isinstance(rd[k], MetaTensor): - rd[k] = apply_pending(rd[k], **kwargs) - return rd + return data def inverse(self, data): if not isinstance(data, dict): raise ValueError("'data' must be of type dict but is '{type(data)}'") - return self(data) + return data + + +ApplyPendingD = ApplyPendingDict = ApplyPendingd diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 0a49ca2d58..f01dfbe11b 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -31,8 +31,8 @@ def apply_pending_transforms( - data, overrides: dict | None = None, logger_name: str = "monai.lazy.functional.apply_pending_transforms" - ): + data, overrides: dict | None = None, logger_name: str = "monai.lazy.functional.apply_pending_transforms" +): """ apply_pending_transforms iterates over a tuple, list, or dictionary of data, recursively calling itself to get a single tensor. If that tensor is a MetaTensor with pending lazy transforms, it then calls @@ -75,10 +75,10 @@ def apply_pending_transforms( def apply_pending( - data: torch.Tensor | MetaTensor, - pending: list | None = None, - overrides: dict | None = None, - logger_name: str = "monai.lazy.functional.apply_pending", + data: torch.Tensor | MetaTensor, + pending: list | None = None, + overrides: dict | None = None, + logger_name: str = "monai.lazy.functional.apply_pending", ): """ This method applies pending transforms to `data` tensors. diff --git a/tests/test_compose.py b/tests/test_compose.py index 29452d8e84..710f9513b0 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -255,7 +255,6 @@ def test_backwards_compatible_imports(self): class TestComposeExecute(unittest.TestCase): - @staticmethod def data_from_keys(keys): if keys is None: @@ -324,11 +323,10 @@ def test_compose_execute_bad_end_param(self, keys, pipeline): with self.assertRaises(ValueError): c = Compose(deepcopy(pipeline)) - c(data, end=len(pipeline)+1) + c(data, end=len(pipeline) + 1) with self.assertRaises(ValueError): - execute_compose(data, deepcopy(pipeline), end=len(pipeline)+1) - + execute_compose(data, deepcopy(pipeline), end=len(pipeline) + 1) @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_empty_range(self, keys, pipeline): @@ -410,6 +408,7 @@ def data_from_keys(keys): else: self.assertTrue(expected, actual) + # TEST_COMPOSE_LAZY_FLAG_CASES = [ # [sa.Spacing(), sa.Flip(), sa.Flip(), sa.Rotate90(), ca.ResizeWithPadOrCro()], # [], diff --git a/tests/test_one_of.py b/tests/test_one_of.py index d38e49bce4..2977b141ce 100644 --- a/tests/test_one_of.py +++ b/tests/test_one_of.py @@ -15,10 +15,12 @@ from copy import deepcopy import numpy as np -from parameterized import parameterized - import torch +from parameterized import parameterized +import monai.transforms.intensity.array as ia +import monai.transforms.spatial.array as sa +import monai.transforms.spatial.dictionary as sd from monai.data import MetaTensor from monai.transforms import ( InvertibleTransform, @@ -31,9 +33,6 @@ Resized, Transform, ) -import monai.transforms.intensity.array as ia -import monai.transforms.spatial.array as sa -import monai.transforms.spatial.dictionary as sd from monai.transforms.compose import Compose from monai.transforms.transform import MapTransform from monai.utils.enums import TraceKeys @@ -241,7 +240,6 @@ def test_one_of(self): class TestOneOfAPITests(unittest.TestCase): - @staticmethod def data_from_keys(keys): if keys is None: diff --git a/tests/test_random_order.py b/tests/test_random_order.py index 12f72614b0..5eadedb58a 100644 --- a/tests/test_random_order.py +++ b/tests/test_random_order.py @@ -12,21 +12,18 @@ from __future__ import annotations import unittest - from copy import deepcopy -from parameterized import parameterized - import numpy as np - import torch +from parameterized import parameterized -from monai.data import MetaTensor -from monai.transforms import RandomOrder -from monai.transforms.compose import Compose import monai.transforms.intensity.array as ia import monai.transforms.spatial.array as sa import monai.transforms.spatial.dictionary as sd +from monai.data import MetaTensor +from monai.transforms import RandomOrder +from monai.transforms.compose import Compose from monai.utils import set_determinism from monai.utils.enums import TraceKeys from tests.test_one_of import A, B, C, Inv, NonInv, X, Y @@ -116,7 +113,6 @@ def test_inverse(self, transform, invertible, use_metatensor): class TestRandomOrderAPITests(unittest.TestCase): - @staticmethod def data_from_keys(keys): if keys is None: diff --git a/tests/test_some_of.py b/tests/test_some_of.py index d934fcab2e..cba2c8a464 100644 --- a/tests/test_some_of.py +++ b/tests/test_some_of.py @@ -12,21 +12,18 @@ from __future__ import annotations import unittest - from copy import deepcopy -from parameterized import parameterized - import numpy as np - import torch +from parameterized import parameterized -from monai.data import MetaTensor -from monai.transforms import TraceableTransform, Transform -from monai.transforms.compose import Compose, SomeOf import monai.transforms.intensity.array as ia import monai.transforms.spatial.array as sa import monai.transforms.spatial.dictionary as sd +from monai.data import MetaTensor +from monai.transforms import TraceableTransform, Transform +from monai.transforms.compose import Compose, SomeOf from monai.utils import set_determinism from monai.utils.enums import TraceKeys from tests.test_one_of import NonInv @@ -224,7 +221,6 @@ def test_bad_num_transforms(self): class TestSomeOfAPITests(unittest.TestCase): - @staticmethod def data_from_keys(keys): if keys is None: From bb22c139e16d59251ecccd21e83be9daf2010b99 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:53:41 +0000 Subject: [PATCH 068/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/lazy/dictionary.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index e58cf7f8ca..39f3d035b6 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -13,10 +13,8 @@ from __future__ import annotations from monai.config import KeysCollection -from monai.data.meta_tensor import MetaTensor from monai.transforms import MapTransform from monai.transforms.inverse import InvertibleTransform -from monai.transforms.lazy.functional import apply_pending __all__ = ["ApplyPendingd"] From 2159ce87b57f37ea3075589c745e793832255338 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 16:03:57 +0100 Subject: [PATCH 069/175] Correction to type info for execute_compose lazy parameter. Removal of obsolete docstrings from execute_compose Signed-off-by: Ben Murray --- monai/transforms/compose.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 805d820c19..a73e5a0d92 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -51,7 +51,7 @@ def execute_compose( unpack_items: bool = False, start: int = 0, end: int | None = None, - lazy: str | bool | None = False, + lazy: bool | None = False, overrides: dict | None = None, threading: bool = False, logger_name: str | None = None, @@ -83,9 +83,7 @@ def execute_compose( is True. If lazy is False they are ignored. currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, - please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - override_keys: this optional parameter specifies the keys to which ``overrides`` are to be applied. If - ``overrides`` is set, ``override_keys`` must also be set. + please see also :py:func:`monai.transforms.lazy.apply_pending` and ``Compose`` for more details. threading: whether executing is happening in a threaded environment. If set, copies are made of transforms that have the ``RandomizedTrait`` interface. From 2c3172d8f67d6f506895edfd56c35a7a100918d0 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 16:07:42 +0100 Subject: [PATCH 070/175] Removal of comment in docstring about IdentityD being used to cause application of pending transforms Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index a73e5a0d92..c92b676df8 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -75,8 +75,6 @@ def execute_compose( lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. - A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of - the pending operations and make the primary data up-to-date. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when lazy From 5a7820cc8bf8006597cd27f4f6734140888ba598 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 20:01:23 +0100 Subject: [PATCH 071/175] Logging refactored with pending pipeline logging enabled Fix for transforms that provide 3x3 matrices in their pending info Signed-off-by: Ben Murray --- monai/transforms/compose.py | 26 ++++++---- monai/transforms/lazy/functional.py | 17 +++++-- monai/transforms/transform.py | 55 +++++++++++++++++--- tests/test_compose.py | 79 ++++++++++++++++++++++++++++- 4 files changed, 154 insertions(+), 23 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index c92b676df8..8e140d08fc 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -84,6 +84,8 @@ def execute_compose( please see also :py:func:`monai.transforms.lazy.apply_pending` and ``Compose`` for more details. threading: whether executing is happening in a threaded environment. If set, copies are made of transforms that have the ``RandomizedTrait`` interface. + logger_name: The name of the logger that should be used during transform execution. If None, logging is + suppressed. Returns: A tensorlike, sequence of tensorlikes or dict of tensorlists containing the result of running @@ -250,7 +252,7 @@ class Compose(Randomizable, InvertibleTransform): {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. logger_name: this optional parameter allows you to specify a logger by name. If this is not set - it defaults to 'Compose' + it defaults to 'Compose'. You can also suppress logging by setting this to None. """ def __init__( @@ -260,7 +262,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: str | None = None, + logger_name: str | None = "Compose", ) -> None: if transforms is None: transforms = [] @@ -270,7 +272,7 @@ def __init__( self.set_random_state(seed=get_seed()) self.lazy = lazy self.overrides = overrides - self.logger_name = self.__class__.__name__ if logger_name is None else logger_name + self.logger_name = logger_name def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) @@ -402,7 +404,8 @@ class OneOf(Compose): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - logger_name: the name of the logger to use when logging output. + logger_name: this optional parameter allows you to specify a logger by name. If this is not set + it defaults to 'OneOf'. You can also suppress logging by setting this to None. """ def __init__( @@ -413,7 +416,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str | None = None, + logger_name: str | None = "OneOf", ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: @@ -426,7 +429,7 @@ def __init__( f"got {len(weights)} and {len(self.transforms)}." ) self.weights = ensure_tuple(self._normalize_probabilities(weights)) - self.logger_name = self.__class__.__name__ if logger_name is None else logger_name + self.logger_name = logger_name def _normalize_probabilities(self, weights): if len(weights) == 0: @@ -535,7 +538,8 @@ class RandomOrder(Compose): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - logger_name: the name of the logger to use when logging output. + logger_name: this optional parameter allows you to specify a logger by name. If this is not set + it defaults to 'Compose'. You can also suppress logging by setting this to None. """ def __init__( @@ -545,10 +549,10 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str | None = None, + logger_name: str | None = "RandomOrder", ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) - self.logger_name = self.__class__.__name__ if logger_name is None else logger_name + self.logger_name = logger_name def __call__(self, input_, start=0, end=None, threading=False, lazy: str | bool | None = None): if start != 0: @@ -640,13 +644,13 @@ def __init__( num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, - logger_name: str | None = None, + logger_name: str | None = "SomeOf", ) -> None: super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) - self.logger_name = self.__class__.__name__ if logger_name is None else logger_name + self.logger_name = logger_name def _ensure_valid_num_transforms(self, num_transforms: int | tuple[int, int] | None) -> tuple: if ( diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index f01dfbe11b..f5b41e2daa 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -15,6 +15,7 @@ import torch +from monai.data.utils import to_affine_nd from monai.data.meta_tensor import MetaTensor from monai.transforms.lazy.utils import ( affine_from_pending, @@ -31,7 +32,7 @@ def apply_pending_transforms( - data, overrides: dict | None = None, logger_name: str = "monai.lazy.functional.apply_pending_transforms" + data, overrides: dict | None = None, logger_name: str | None = None ): """ apply_pending_transforms iterates over a tuple, list, or dictionary of data, recursively calling itself @@ -50,7 +51,8 @@ def apply_pending_transforms( data: a ``torch.Tensor`` or ``MetaTensor``, or list, tuple or dictionary of tensors. overrides: An optional dictionary that specifies parameters that can be used to override transform arguments when they are called - logger_name: An optional name for a logger to be used instead of the default logger. + logger_name: An optional name for a logger to be used when applying pending transforms. If None, + logging is suppressed. Returns: """ @@ -78,7 +80,7 @@ def apply_pending( data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None, - logger_name: str = "monai.lazy.functional.apply_pending", + logger_name: str | None = None, ): """ This method applies pending transforms to `data` tensors. @@ -129,6 +131,8 @@ def apply_pending( - device: device for resampling computation. Defaults to ``None``. - resample_mode: the mode of resampling, currently support ``"auto"``. Setting to other values will use the :py:class:`monai.transforms.SpatialResample` for resampling (instead of potentially crop/pad). + logger_name: A logger name that is used to log output generated while applying pending transforms. You can + suppress logging by setting this to None (default). """ overrides = (overrides or {}).copy() @@ -144,6 +148,9 @@ def apply_pending( return data, [] cumulative_xform = affine_from_pending(pending[0]) + if cumulative_xform.shape[0] == 3: + cumulative_xform = to_affine_nd(3, cumulative_xform) + cur_kwargs = kwargs_from_pending(pending[0]) override_kwargs: dict[str, Any] = {} if "mode" in overrides: @@ -165,7 +172,11 @@ def apply_pending( _cur_kwargs = cur_kwargs.copy() _cur_kwargs.update(override_kwargs) data = resample(data.to(device), cumulative_xform, _cur_kwargs) + next_matrix = affine_from_pending(p) + if next_matrix.shape[0] == 3: + next_matrix = to_affine_nd(3, next_matrix) + cumulative_xform = combine_transforms(cumulative_xform, next_matrix) cur_kwargs.update(new_kwargs) cur_kwargs.update(override_kwargs) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 6adf6ded34..95b2e4b498 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -44,12 +44,38 @@ ReturnType = TypeVar("ReturnType") +def _log_pending_info( + logger: Any | None, data: Any, transform: Any, activity: str, lazy: bool | None = None, +): + if logger is None: + return + + if isinstance(transform, LazyTrait): + if lazy is not None and lazy != transform.lazy: + tlazy = f", transform.lazy: {transform.lazy} (overridden)" + else: + tlazy = f", transform.lazy: {transform.lazy}" + else: + tlazy = ", transform is not lazy" + + if isinstance(transform, MapTransform): + for k in transform.keys: + pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 + logger.info(f"{activity} - lazy mode: {lazy}, key: '{k}', " + f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}") + else: + pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 + logger.info(f"{activity} - lazy: {lazy}, " + f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}") + + def _apply_transform( transform: Callable[..., ReturnType], data: Any, unpack_parameters: bool = False, lazy: bool | None = False, overrides: dict | None = None, + logger_name: str | None = "_apply_transform", ) -> ReturnType: """ Perform transformation `transform` with the provided parameters `parameters`. @@ -58,6 +84,19 @@ def _apply_transform( as arguments to `transform`. Otherwise `parameters` is considered as single argument to `transform`. + For the 1.2 release, we are limited here to having executing transforms that + are lazy but set to not be lazy _after_ we have applied the pending list. This + is because the transform implementations for 1.2 don't have unified code paths for + lazy and non-lazy operation, so it is not possible to pass a tensor with pending + operations and have the transform handle them correctly. + In order to have this functionality for 1.2, we need to provide lazy + overrides on __call__ methods for lazy array and dictionary transforms, and we need + pure lazy transforms. See ``Compose`` for more information about lazy resampling. + + Please note, this class is function is designed to be called by ``apply_transform``. + In general, you should not need to make specific use of it unless you are implementing + pipeline execution mechanisms. + Args: transform: a callable to be used to transform `data`. parameters: parameters for the `transform`. @@ -67,20 +106,20 @@ def _apply_transform( ReturnType: The return type of `transform`. """ - # For the 1.2 release, we are limited here to having executing transforms that - # are lazy but set to not be lazy _after_ we have applied the pending list. This - # is because the transform implementations for 1.2 don't have unified code paths for - # lazy and non-lazy operation, so it is not possible to pass a tensor with pending - # operations and have the transform handle them correctly. - # In order to have this functionality for 1.2, we need to provide lazy - # overrides on __call__ methods for lazy array and dictionary transforms. + logger = None + if logger_name is not None: + logger = logging.getLogger(logger_name) lazy_tx = isinstance(transform, LazyTrait) if lazy_tx is False or lazy is False: + _log_pending_info(logger, data, transform, "Apply pending transforms", lazy) data = apply_pending_transforms(data, overrides) elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] + _log_pending_info(logger, data, transform, "Apply pending transforms", lazy) data = apply_pending_transforms(data, overrides) + else: + _log_pending_info(logger, data, transform, "Accumulate pending transforms", lazy) if isinstance(data, tuple) and unpack_parameters: return transform(*data, lazy=lazy) if lazy_tx else transform(*data) @@ -123,7 +162,7 @@ def apply_transform( try: if isinstance(data, (list, tuple)) and map_items: return [_apply_transform(transform, item, unpack_items, lazy, overrides) for item in data] - return _apply_transform(transform, data, unpack_items, lazy, overrides) + return _apply_transform(transform, data, unpack_items, lazy, overrides, logger_name) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint # appears where the exception was raised. diff --git a/tests/test_compose.py b/tests/test_compose.py index 710f9513b0..2b13aa076c 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -11,16 +11,21 @@ from __future__ import annotations +import logging import sys import unittest from copy import deepcopy +from io import StringIO import numpy as np import torch from parameterized import parameterized from monai.data import DataLoader, Dataset -from monai.transforms import AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Zoom +from monai.transforms import ( + AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Spacing, Zoom, +) +import monai.transforms.spatial.array as sa from monai.transforms.compose import execute_compose from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -345,6 +350,78 @@ def test_compose_with_logger_name(self, keys, pipeline): c(data) +TEST_COMPOSE_EXECUTE_LOGGING_TEST_CASES = [ + [ + None, + (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + False, + ("Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Spacing', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Rotate90', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Zoom', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'NormalizeIntensity', transform is not lazy\n") + ], + [ + None, + (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + True, + ("Accumulate pending transforms - lazy: True, pending: 0, upcoming 'Flip', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 1, upcoming 'Spacing', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 2, upcoming 'Flip', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 3, upcoming 'Rotate90', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 4, upcoming 'Zoom', transform.lazy: False (overridden)\n" + "Apply pending transforms - lazy: True, pending: 5, upcoming 'NormalizeIntensity', transform is not lazy\n") + ] +] + + +class TestComposeExecuteWithLogging(unittest.TestCase): + # def setUp(self): + # self.stream = StringIO() + # self.handler = logging.StreamHandler(self.stream) + # self.log = logging.getLogger('mylogger') + # self.log.setLevel(logging.INFO) + # for handler in self.log.handlers: + # self.log.removeHandler(handler) + # self.log.addHandler(self.handler) + + @staticmethod + def data_from_keys(keys): + if keys is None: + data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) + else: + data = {} + for i_k, k in enumerate(keys): + data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) + return data + + @parameterized.expand(TEST_COMPOSE_EXECUTE_LOGGING_TEST_CASES) + def test_compose_with_logging(self, keys, pipeline, lazy, expected): + # stream = StringIO() + # logging.basicConfig(handlers=[logging.StreamHandler(stream)], level=logging.NOTSET) + # log_handler = logging.StreamHandler(stream) + # log_handler.setLevel(logging.INFO) + # print(stream.getvalue()) + stream = StringIO() + handler = logging.StreamHandler(stream) + logger = logging.getLogger("a_logger_name") + logger.setLevel(logging.INFO) + while len(logger.handlers) > 0: + logger.removeHandler(logger.handlers[-1]) + logger.addHandler(handler) + + data = self.data_from_keys(keys) + c = Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") + result = c(data) + + # logger = logging.getLogger("a_logger_name") + handler.flush() + actual = stream.getvalue() + print(f"stream = '{actual}'") + self.assertEqual(actual, expected) + + class TestOps: @staticmethod def concat(value): From 503db854394bd544141f102e1ce9bbb0699e8d1d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 19:04:34 +0000 Subject: [PATCH 072/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_compose.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 2b13aa076c..bfb61fc921 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -25,7 +25,6 @@ from monai.transforms import ( AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Spacing, Zoom, ) -import monai.transforms.spatial.array as sa from monai.transforms.compose import execute_compose from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -413,7 +412,7 @@ def test_compose_with_logging(self, keys, pipeline, lazy, expected): data = self.data_from_keys(keys) c = Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") - result = c(data) + c(data) # logger = logging.getLogger("a_logger_name") handler.flush() From 1af35fd1e03c81cb6d745518ac30fa75996551b7 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 20:45:15 +0100 Subject: [PATCH 073/175] All logging_names are now None by default to prevent massive spam in certain tests. Cleaned up noisy logging test, Exceptions logged during apply_transform are now errors rather than info Signed-off-by: Ben Murray --- monai/transforms/compose.py | 8 ++++---- monai/transforms/transform.py | 6 +++--- tests/test_compose.py | 15 --------------- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 8e140d08fc..e1f2fa1c11 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -262,7 +262,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: str | None = "Compose", + logger_name: str | None = None, ) -> None: if transforms is None: transforms = [] @@ -416,7 +416,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str | None = "OneOf", + logger_name: str | None = None, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: @@ -549,7 +549,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str | None = "RandomOrder", + logger_name: str | None = None, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) self.logger_name = logger_name @@ -644,7 +644,7 @@ def __init__( num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, - logger_name: str | None = "SomeOf", + logger_name: str | None = None, ) -> None: super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 95b2e4b498..d7bfdfcdd8 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -75,7 +75,7 @@ def _apply_transform( unpack_parameters: bool = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: str | None = "_apply_transform", + logger_name: str | None = None, ) -> ReturnType: """ Perform transformation `transform` with the provided parameters `parameters`. @@ -168,11 +168,11 @@ def apply_transform( # appears where the exception was raised. if MONAIEnvVars.debug(): raise - if not isinstance(transform, transforms.compose.Compose): + if logger_name is not None and not isinstance(transform, transforms.compose.Compose): # log the input data information of exact transform in the transform chain datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False, name=logger_name) logger = logging.getLogger(datastats._logger_name) - logger.info(f"\n=== Transform input info -- {type(transform).__name__} ===") + logger.error(f"\n=== Transform input info -- {type(transform).__name__} ===") if isinstance(data, (list, tuple)): data = data[0] diff --git a/tests/test_compose.py b/tests/test_compose.py index 2b13aa076c..442e0ec9b7 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -377,14 +377,6 @@ def test_compose_with_logger_name(self, keys, pipeline): class TestComposeExecuteWithLogging(unittest.TestCase): - # def setUp(self): - # self.stream = StringIO() - # self.handler = logging.StreamHandler(self.stream) - # self.log = logging.getLogger('mylogger') - # self.log.setLevel(logging.INFO) - # for handler in self.log.handlers: - # self.log.removeHandler(handler) - # self.log.addHandler(self.handler) @staticmethod def data_from_keys(keys): @@ -398,11 +390,6 @@ def data_from_keys(keys): @parameterized.expand(TEST_COMPOSE_EXECUTE_LOGGING_TEST_CASES) def test_compose_with_logging(self, keys, pipeline, lazy, expected): - # stream = StringIO() - # logging.basicConfig(handlers=[logging.StreamHandler(stream)], level=logging.NOTSET) - # log_handler = logging.StreamHandler(stream) - # log_handler.setLevel(logging.INFO) - # print(stream.getvalue()) stream = StringIO() handler = logging.StreamHandler(stream) logger = logging.getLogger("a_logger_name") @@ -415,10 +402,8 @@ def test_compose_with_logging(self, keys, pipeline, lazy, expected): c = Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") result = c(data) - # logger = logging.getLogger("a_logger_name") handler.flush() actual = stream.getvalue() - print(f"stream = '{actual}'") self.assertEqual(actual, expected) From af6cd62e1ec3cbf70da7b5fd1437307d019cbed8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 20:48:58 +0100 Subject: [PATCH 074/175] More autofixes Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 6 ++--- monai/transforms/transform.py | 15 +++++++------ tests/test_compose.py | 35 +++++++++++++++-------------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index f5b41e2daa..dfcb0c995b 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -15,8 +15,8 @@ import torch -from monai.data.utils import to_affine_nd from monai.data.meta_tensor import MetaTensor +from monai.data.utils import to_affine_nd from monai.transforms.lazy.utils import ( affine_from_pending, combine_transforms, @@ -31,9 +31,7 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} -def apply_pending_transforms( - data, overrides: dict | None = None, logger_name: str | None = None -): +def apply_pending_transforms(data, overrides: dict | None = None, logger_name: str | None = None): """ apply_pending_transforms iterates over a tuple, list, or dictionary of data, recursively calling itself to get a single tensor. If that tensor is a MetaTensor with pending lazy transforms, it then calls diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index d7bfdfcdd8..9f0c55112f 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -44,9 +44,7 @@ ReturnType = TypeVar("ReturnType") -def _log_pending_info( - logger: Any | None, data: Any, transform: Any, activity: str, lazy: bool | None = None, -): +def _log_pending_info(logger: Any | None, data: Any, transform: Any, activity: str, lazy: bool | None = None): if logger is None: return @@ -61,12 +59,15 @@ def _log_pending_info( if isinstance(transform, MapTransform): for k in transform.keys: pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 - logger.info(f"{activity} - lazy mode: {lazy}, key: '{k}', " - f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}") + logger.info( + f"{activity} - lazy mode: {lazy}, key: '{k}', " + f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) else: pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 - logger.info(f"{activity} - lazy: {lazy}, " - f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}") + logger.info( + f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) def _apply_transform( diff --git a/tests/test_compose.py b/tests/test_compose.py index 51719eb030..bd1b9dbde6 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -22,9 +22,7 @@ from parameterized import parameterized from monai.data import DataLoader, Dataset -from monai.transforms import ( - AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Spacing, Zoom, -) +from monai.transforms import AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Spacing, Zoom from monai.transforms.compose import execute_compose from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -354,29 +352,32 @@ def test_compose_with_logger_name(self, keys, pipeline): None, (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), False, - ("Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Spacing', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Rotate90', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Zoom', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'NormalizeIntensity', transform is not lazy\n") + ( + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Spacing', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Rotate90', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'Zoom', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, upcoming 'NormalizeIntensity', transform is not lazy\n" + ), ], [ None, (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), True, - ("Accumulate pending transforms - lazy: True, pending: 0, upcoming 'Flip', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 1, upcoming 'Spacing', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 2, upcoming 'Flip', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 3, upcoming 'Rotate90', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 4, upcoming 'Zoom', transform.lazy: False (overridden)\n" - "Apply pending transforms - lazy: True, pending: 5, upcoming 'NormalizeIntensity', transform is not lazy\n") - ] + ( + "Accumulate pending transforms - lazy: True, pending: 0, upcoming 'Flip', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 1, upcoming 'Spacing', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 2, upcoming 'Flip', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 3, upcoming 'Rotate90', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 4, upcoming 'Zoom', transform.lazy: False (overridden)\n" + "Apply pending transforms - lazy: True, pending: 5, upcoming 'NormalizeIntensity', transform is not lazy\n" + ), + ], ] class TestComposeExecuteWithLogging(unittest.TestCase): - @staticmethod def data_from_keys(keys): if keys is None: From 1403d777f0ac87fd42ffddb2fa443dc00fcd397a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 21:01:48 +0100 Subject: [PATCH 075/175] Making apply_transform logger_name None by default Signed-off-by: Ben Murray --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 9f0c55112f..e74190ac47 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -135,7 +135,7 @@ def apply_transform( unpack_items: bool = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: str = "monai.compose.transform.apply_transform", + logger_name: str | None = None, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. From 5c4c10aed5ba03fa3a53b4e4d6f83ed110eb90f6 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 26 Apr 2023 22:56:09 +0100 Subject: [PATCH 076/175] Added a dictionary test to the compose logging test suite Signed-off-by: Ben Murray --- tests/test_compose.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_compose.py b/tests/test_compose.py index bd1b9dbde6..3cd7520318 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -23,7 +23,9 @@ from monai.data import DataLoader, Dataset from monai.transforms import AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Spacing, Zoom +from monai.transforms import NormalizeIntensityd from monai.transforms.compose import execute_compose +from monai.transforms.spatial.dictionary import Flipd, Rotate90d, Spacingd from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -374,6 +376,21 @@ def test_compose_with_logger_name(self, keys, pipeline): "Apply pending transforms - lazy: True, pending: 5, upcoming 'NormalizeIntensity', transform is not lazy\n" ), ], + [ + ('a', 'b'), + (Flipd(('a', 'b'), 0), Spacingd(('a', 'b'), 1.2), Rotate90d(('a', 'b'), 1), NormalizeIntensityd(('a',))), + True, + ( + "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, upcoming 'Flipd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, upcoming 'Flipd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, upcoming 'Spacingd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, upcoming 'Spacingd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" + "Apply pending transforms - lazy mode: True, key: 'a', pending: 3, upcoming 'NormalizeIntensityd', transform is not lazy\n" + "" + ) + ] ] From 5755a652422d34c2d81087c17ec3d0c09719076e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 07:24:19 +0100 Subject: [PATCH 077/175] Updated docstring for _apply_transform Signed-off-by: Ben Murray --- monai/transforms/transform.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index e74190ac47..95f44a8e1b 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -79,20 +79,16 @@ def _apply_transform( logger_name: str | None = None, ) -> ReturnType: """ - Perform transformation `transform` with the provided parameters `parameters`. - - If `parameters` is a tuple and `unpack_items` is True, each parameter of `parameters` is unpacked - as arguments to `transform`. - Otherwise `parameters` is considered as single argument to `transform`. - - For the 1.2 release, we are limited here to having executing transforms that - are lazy but set to not be lazy _after_ we have applied the pending list. This - is because the transform implementations for 1.2 don't have unified code paths for - lazy and non-lazy operation, so it is not possible to pass a tensor with pending - operations and have the transform handle them correctly. - In order to have this functionality for 1.2, we need to provide lazy - overrides on __call__ methods for lazy array and dictionary transforms, and we need - pure lazy transforms. See ``Compose`` for more information about lazy resampling. + Perform a transform 'transform' on 'data', according to the other parameters specified. + + If `data` is a tuple and `unpack_parameters` is True, each parameter of `data` is unpacked + as arguments to `transform`. Otherwise `data` is considered as single argument to `transform`. + + If 'lazy' is True, this method first checks whether it can execute this method lazily. If it + can't, it will ensure that all pending lazy transforms on 'data' are applied before applying + this 'transform' to it. If 'lazy' is True, and 'overrides' are provided, those overrides will + be applied to the pending operations on 'data'. See ``Compose`` for more details on lazy + resampling, which is an experimental feature for 1.2. Please note, this class is function is designed to be called by ``apply_transform``. In general, you should not need to make specific use of it unless you are implementing From ca0905a9fa215804cddfdca42f1341c4f29cf7ca Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 07:28:02 +0100 Subject: [PATCH 078/175] Removed spurious empty string at the end of one of the logging test case expected outputs. Signed-off-by: Ben Murray --- tests/test_compose.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 3cd7520318..45040b60ea 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -388,7 +388,6 @@ def test_compose_with_logger_name(self, keys, pipeline): "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" "Apply pending transforms - lazy mode: True, key: 'a', pending: 3, upcoming 'NormalizeIntensityd', transform is not lazy\n" - "" ) ] ] From e8fdf5a63816712711c78f2edbd51aa1c07b4216 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 08:24:17 +0100 Subject: [PATCH 079/175] All spatial transforms now initialize LazyTransform correctly Signed-off-by: Ben Murray --- monai/transforms/spatial/array.py | 34 +++++++++--------- monai/transforms/spatial/dictionary.py | 48 +++++++++++++------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index b3dd1f2bc7..478eebed61 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -156,11 +156,11 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ + LazyTransform.__init__(self, lazy=lazy) self.mode = mode self.padding_mode = padding_mode self.align_corners = align_corners self.dtype = dtype - self.lazy = lazy def __call__( self, @@ -400,13 +400,13 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ + LazyTransform.__init__(self, lazy=lazy) self.pixdim = np.array(ensure_tuple(pixdim), dtype=np.float64) self.min_pixdim = np.array(ensure_tuple(min_pixdim), dtype=np.float64) self.max_pixdim = np.array(ensure_tuple(max_pixdim), dtype=np.float64) self.diagonal = diagonal self.scale_extent = scale_extent self.recompute_affine = recompute_affine - self.lazy = lazy for mn, mx in zip(self.min_pixdim, self.max_pixdim): if (not np.isnan(mn)) and (not np.isnan(mx)) and ((mx < mn) or (mn < 0)): @@ -566,6 +566,7 @@ def __init__( See Also: `nibabel.orientations.ornt2axcodes`. """ + LazyTransform.__init__(self, lazy=lazy) if axcodes is None and not as_closest_canonical: raise ValueError("Incompatible values: axcodes=None and as_closest_canonical=True.") if axcodes is not None and as_closest_canonical: @@ -573,7 +574,6 @@ def __init__( self.axcodes = axcodes self.as_closest_canonical = as_closest_canonical self.labels = labels - self.lazy = lazy def __call__(self, data_array: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ @@ -667,8 +667,8 @@ class Flip(InvertibleTransform, LazyTransform): backend = [TransformBackends.TORCH] def __init__(self, spatial_axis: Sequence[int] | int | None = None, lazy: bool = False) -> None: + LazyTransform.__init__(self, lazy=lazy) self.spatial_axis = spatial_axis - self.lazy = lazy def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ @@ -738,6 +738,7 @@ def __init__( dtype: DtypeLike | torch.dtype = torch.float32, lazy: bool = False, ) -> None: + LazyTransform.__init__(self, lazy=lazy) self.size_mode = look_up_option(size_mode, ["all", "longest"]) self.spatial_size = spatial_size self.mode = mode @@ -745,7 +746,6 @@ def __init__( self.anti_aliasing = anti_aliasing self.anti_aliasing_sigma = anti_aliasing_sigma self.dtype = dtype - self.lazy = lazy def __call__( self, @@ -883,13 +883,13 @@ def __init__( dtype: DtypeLike | torch.dtype = torch.float32, lazy: bool = False, ) -> None: + LazyTransform.__init__(self, lazy=lazy) self.angle = angle self.keep_size = keep_size self.mode: str = mode self.padding_mode: str = padding_mode self.align_corners = align_corners self.dtype = dtype - self.lazy = lazy def __call__( self, @@ -1023,6 +1023,7 @@ def __init__( lazy: bool = False, **kwargs, ) -> None: + LazyTransform.__init__(self, lazy=lazy) self.zoom = zoom self.mode = mode self.padding_mode = padding_mode @@ -1030,7 +1031,6 @@ def __init__( self.dtype = dtype self.keep_size = keep_size self.kwargs = kwargs - self.lazy = lazy def __call__( self, @@ -1129,12 +1129,12 @@ def __init__(self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1), lazy: boo lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ + LazyTransform.__init__(self, lazy=lazy) self.k = (4 + (k % 4)) % 4 # 0, 1, 2, 3 spatial_axes_: tuple[int, int] = ensure_tuple(spatial_axes) # type: ignore if len(spatial_axes_) != 2: raise ValueError(f"spatial_axes must be 2 numbers to define the plane to rotate, got {spatial_axes_}.") self.spatial_axes = spatial_axes_ - self.lazy = lazy def __call__(self, img: torch.Tensor, lazy: bool | None = None) -> torch.Tensor: """ @@ -1184,9 +1184,9 @@ def __init__( Defaults to False """ RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.max_k = max_k self.spatial_axes = spatial_axes - self.lazy = lazy self._rand_k = 0 @@ -1273,6 +1273,7 @@ def __init__( lazy: bool = False, ) -> None: RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -1288,7 +1289,6 @@ def __init__( self.padding_mode: str = padding_mode self.align_corners = align_corners self.dtype = dtype - self.lazy = lazy self.x = 0.0 self.y = 0.0 @@ -1376,8 +1376,8 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None, lazy: bool = False) -> None: RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) - self.lazy = lazy def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ @@ -1420,9 +1420,9 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform, LazyTransform): def __init__(self, prob: float = 0.1, lazy: bool = False) -> None: RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self._axis: int | None = None self.flipper = Flip(spatial_axis=self._axis) - self.lazy = lazy def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) @@ -1515,6 +1515,7 @@ def __init__( **kwargs, ) -> None: RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): @@ -1526,7 +1527,6 @@ def __init__( self.align_corners = align_corners self.dtype = dtype self.keep_size = keep_size - self.lazy = lazy self.kwargs = kwargs self._zoom: Sequence[float] = [1.0] @@ -1652,6 +1652,7 @@ def __init__( affine: NdarrayOrTensor | None = None, lazy: bool = False, ) -> None: + LazyTransform.__init__(self, lazy=lazy) self.rotate_params = rotate_params self.shear_params = shear_params self.translate_params = translate_params @@ -1661,7 +1662,6 @@ def __init__( self.dtype = _dtype if _dtype in (torch.float16, torch.float64, None) else torch.float32 self.align_corners = align_corners self.affine = affine - self.lazy = lazy def __call__( self, spatial_size: Sequence[int] | None = None, grid: torch.Tensor | None = None, lazy: bool | None = None @@ -1781,6 +1781,7 @@ def __init__( - :py:meth:`monai.transforms.utils.create_scale` """ + LazyTransform.__init__(self, lazy=lazy) self.rotate_range = ensure_tuple(rotate_range) self.shear_range = ensure_tuple(shear_range) self.translate_range = ensure_tuple(translate_range) @@ -1794,7 +1795,6 @@ def __init__( self.device = device self.dtype = dtype self.affine: torch.Tensor | None = torch.eye(4, dtype=torch.float64) - self.lazy = lazy def _get_rand_param(self, param_range, add_scalar: float = 0.0): out_param = [] @@ -2140,6 +2140,7 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ + LazyTransform.__init__(self, lazy=lazy) self.affine_grid = AffineGrid( rotate_params=rotate_params, shear_params=shear_params, @@ -2157,7 +2158,6 @@ def __init__( self.spatial_size = spatial_size self.mode = mode self.padding_mode: str = padding_mode - self.lazy = lazy def __call__( self, @@ -2328,6 +2328,7 @@ def __init__( """ RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.rand_affine_grid = RandAffineGrid( rotate_range=rotate_range, @@ -2344,7 +2345,6 @@ def __init__( self._cached_grid = self._init_identity_cache(lazy) self.mode = mode self.padding_mode: str = padding_mode - self.lazy = lazy def _init_identity_cache(self, lazy: bool): """ diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index ec182d3bc8..8e5a16c0d3 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -200,14 +200,14 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.sp_transform = SpatialResample(lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -291,14 +291,14 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.key_dst = key_dst self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.resampler = ResampleToMatch(lazy=lazy) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -430,7 +430,8 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.spacing_transform = Spacing( pixdim, diagonal=diagonal, @@ -445,7 +446,6 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.scale_extent = ensure_tuple_rep(scale_extent, len(self.keys)) self.ensure_same_shape = ensure_same_shape - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -535,11 +535,11 @@ def __init__( `nibabel.orientations.ornt2axcodes`. """ - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.ornt_transform = Orientation( axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy=lazy ) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -591,9 +591,9 @@ def __init__( lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False """ - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.rotator = Rotate90(k, spatial_axes, lazy=lazy) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -655,12 +655,12 @@ def __init__( """ MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.max_k = max_k self.spatial_axes = spatial_axes self._rand_k = 0 - self.lazy = lazy def randomize(self, data: Any | None = None) -> None: self._rand_k = self.R.randint(self.max_k) + 1 @@ -759,14 +759,14 @@ def __init__( allow_missing_keys: bool = False, lazy: bool = False, ) -> None: - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.anti_aliasing = ensure_tuple_rep(anti_aliasing, len(self.keys)) self.anti_aliasing_sigma = ensure_tuple_rep(anti_aliasing_sigma, len(self.keys)) self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy=lazy) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -886,6 +886,7 @@ def __init__( """ MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.affine = Affine( rotate_params=rotate_params, shear_params=shear_params, @@ -900,7 +901,6 @@ def __init__( ) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -1013,7 +1013,7 @@ def __init__( """ MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) - LazyTransform.__init__(self, lazy) + LazyTransform.__init__(self, lazy=lazy) self.rand_affine = RandAffine( prob=1.0, # because probability handled in this class rotate_range=rotate_range, @@ -1413,9 +1413,9 @@ def __init__( allow_missing_keys: bool = False, lazy: bool = False, ) -> None: - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -1471,8 +1471,8 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) - self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) @@ -1536,8 +1536,8 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.flipper = RandAxisFlip(prob=1.0, lazy=lazy) - self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) @@ -1630,14 +1630,14 @@ def __init__( allow_missing_keys: bool = False, lazy: bool = False, ) -> None: - super().__init__(keys, allow_missing_keys) + MapTransform.__init__(self, keys, allow_missing_keys) + LazyTransform.__init__(self, lazy=lazy) self.rotator = Rotate(angle=angle, keep_size=keep_size, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.lazy = lazy def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ @@ -1725,6 +1725,7 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.rand_rotate = RandRotate( range_x=range_x, range_y=range_y, range_z=range_z, prob=1.0, keep_size=keep_size, lazy=lazy ) @@ -1732,7 +1733,6 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) @@ -1838,7 +1838,7 @@ def __init__( **kwargs, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - LazyTransform.__init__(self, lazy) + LazyTransform.__init__(self, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) @@ -1937,6 +1937,7 @@ def __init__( ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) + LazyTransform.__init__(self, lazy=lazy) self.rand_zoom = RandZoom( prob=1.0, min_zoom=min_zoom, max_zoom=max_zoom, keep_size=keep_size, lazy=lazy, **kwargs ) @@ -1944,7 +1945,6 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self.lazy = lazy def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) From 70d351727144d158ca5c8f755367bfb8d8e81e3c Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 09:07:55 +0100 Subject: [PATCH 080/175] Adding setters for lazy back in. Autoformat fix Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 30 ++++++++++- monai/transforms/croppad/dictionary.py | 37 +++++++++++++ monai/transforms/spatial/array.py | 26 ++++++++- monai/transforms/spatial/dictionary.py | 75 ++++++++++++++++++++++++++ tests/test_compose.py | 22 +++++--- 5 files changed, 182 insertions(+), 8 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index f8625b758f..824bd6210c 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -488,7 +488,7 @@ class CenterSpatialCrop(Crop): """ def __init__(self, roi_size: Sequence[int] | int, lazy: bool = False) -> None: - super().__init__(lazy) + super().__init__(lazy=lazy) self.roi_size = roi_size def compute_slices(self, spatial_size: Sequence[int]) -> tuple[slice]: # type: ignore[override] @@ -718,6 +718,11 @@ def set_random_state( self.cropper.set_random_state(seed, state) return self + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def randomize(self, data: Any | None = None) -> None: pass @@ -817,6 +822,11 @@ def __init__( self.k_divisible = k_divisible self.padder = Pad(mode=mode, lazy=False, **pad_kwargs) + @Crop.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + self.padder.lazy = _val + def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ Compute the start points and end points of bounding box to crop. @@ -943,6 +953,10 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: spatial_size=self.spatial_size, w=weight_map[0], n_samples=self.num_samples, r_state=self.R ) # using only the first channel as weight map + @LazyTransform.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + def __call__( self, img: torch.Tensor, @@ -1111,6 +1125,10 @@ def randomize( self.allow_smaller, ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + def __call__( self, img: torch.Tensor, @@ -1291,6 +1309,10 @@ def randomize( self.spatial_size, self.num_samples, _shape, indices_, self.ratios, self.R, self.allow_smaller, self.warn ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, _val: bool): + self._lazy = _val + def __call__( self, img: torch.Tensor, @@ -1376,6 +1398,12 @@ def __init__( self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, lazy=lazy, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.padder.lazy = val + self.cropper.lazy = val + self._lazy = val + def __call__( # type: ignore[override] self, img: torch.Tensor, mode: str | None = None, lazy: bool | None = None, **pad_kwargs ) -> torch.Tensor: diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 688e431e78..734b97d524 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -149,6 +149,12 @@ def __init__( self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + if isinstance(self.padder, LazyTransform): + self.padder.lazy = value + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) lazy_ = self.lazy if lazy is None else lazy @@ -330,6 +336,12 @@ def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool LazyTransform.__init__(self, lazy) self.cropper = cropper + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + if isinstance(self.cropper, LazyTransform): + self.cropper.lazy = value + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) lazy_ = self.lazy if lazy is None else lazy @@ -625,6 +637,11 @@ def __init__( roi_size, num_samples, max_roi_size, random_center, random_size, lazy=lazy ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") @@ -726,6 +743,11 @@ def __init__( super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) self.mode = ensure_tuple_rep(mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: if lazy is True: warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") @@ -789,6 +811,11 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: @@ -915,6 +942,11 @@ def randomize( ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: @@ -1071,6 +1103,11 @@ def randomize( ) -> None: self.cropper.randomize(label=label, indices=indices, image=image) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, value: bool) -> None: + self._lazy = value + self.cropper.lazy = value + def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: if lazy is True: warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 478eebed61..eb7f273e4c 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -416,6 +416,11 @@ def __init__( mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype, lazy=lazy ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.sp_resample.lazy = val + @deprecated_arg(name="affine", since="0.9", msg_suffix="Not needed, input should be `MetaTensor`.") def __call__( self, @@ -1379,6 +1384,11 @@ def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def __call__(self, img: torch.Tensor, randomize: bool = True, lazy: bool | None = None) -> torch.Tensor: """ Args: @@ -1424,6 +1434,11 @@ def __init__(self, prob: float = 0.1, lazy: bool = False) -> None: self._axis: int | None = None self.flipper = Flip(spatial_axis=self._axis) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def randomize(self, data: NdarrayOrTensor) -> None: super().randomize(None) if not self._do_transform: @@ -2159,6 +2174,11 @@ def __init__( self.mode = mode self.padding_mode: str = padding_mode + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self.affine_grid.lazy = val + self._lazy = val + def __call__( self, img: torch.Tensor, @@ -2329,7 +2349,6 @@ def __init__( """ RandomizableTransform.__init__(self, prob) LazyTransform.__init__(self, lazy=lazy) - self.rand_affine_grid = RandAffineGrid( rotate_range=rotate_range, shear_range=shear_range, @@ -2346,6 +2365,11 @@ def __init__( self.mode = mode self.padding_mode: str = padding_mode + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.rand_affine_grid.lazy = val + def _init_identity_cache(self, lazy: bool): """ Create cache of the identity grid if cache_grid=True and spatial_size is known. diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 8e5a16c0d3..b0fd53d3cd 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -209,6 +209,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.sp_transform.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -300,6 +305,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.resampler = ResampleToMatch(lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.resampler.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -447,6 +457,11 @@ def __init__( self.scale_extent = ensure_tuple_rep(scale_extent, len(self.keys)) self.ensure_same_shape = ensure_same_shape + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.spacing_transform.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -541,6 +556,11 @@ def __init__( axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels, lazy=lazy ) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.ornt_transform.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -595,6 +615,11 @@ def __init__( LazyTransform.__init__(self, lazy=lazy) self.rotator = Rotate90(k, spatial_axes, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.rotator.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -768,6 +793,11 @@ def __init__( self.anti_aliasing_sigma = ensure_tuple_rep(anti_aliasing_sigma, len(self.keys)) self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.resizer.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -902,6 +932,11 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.affine.lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1028,6 +1063,11 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool) -> None: + self._lazy = val + self.rand_affine.lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAffined: self.rand_affine.set_random_state(seed, state) super().set_random_state(seed, state) @@ -1417,6 +1457,11 @@ def __init__( LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1474,6 +1519,11 @@ def __init__( LazyTransform.__init__(self, lazy=lazy) self.flipper = Flip(spatial_axis=spatial_axis, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) return self @@ -1539,6 +1589,11 @@ def __init__( LazyTransform.__init__(self, lazy=lazy) self.flipper = RandAxisFlip(prob=1.0, lazy=lazy) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.flipper.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) self.flipper.set_random_state(seed, state) @@ -1639,6 +1694,11 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.rotator.lazy = val + self._lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1734,6 +1794,11 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.rand_rotate.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) self.rand_rotate.set_random_state(seed, state) @@ -1846,6 +1911,11 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, lazy=lazy, **kwargs) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.zoomer.lazy = val + self._lazy = val + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: """ Args: @@ -1946,6 +2016,11 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) + @LazyTransform.lazy.setter # type: ignore + def lazy(self, val: bool): + self.rand_zoom.lazy = val + self._lazy = val + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) diff --git a/tests/test_compose.py b/tests/test_compose.py index 45040b60ea..60151f86ff 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -22,8 +22,18 @@ from parameterized import parameterized from monai.data import DataLoader, Dataset -from monai.transforms import AddChannel, Compose, Flip, NormalizeIntensity, Rotate, Rotate90, Rotated, Spacing, Zoom -from monai.transforms import NormalizeIntensityd +from monai.transforms import ( + AddChannel, + Compose, + Flip, + NormalizeIntensity, + NormalizeIntensityd, + Rotate, + Rotate90, + Rotated, + Spacing, + Zoom, +) from monai.transforms.compose import execute_compose from monai.transforms.spatial.dictionary import Flipd, Rotate90d, Spacingd from monai.transforms.transform import Randomizable @@ -377,8 +387,8 @@ def test_compose_with_logger_name(self, keys, pipeline): ), ], [ - ('a', 'b'), - (Flipd(('a', 'b'), 0), Spacingd(('a', 'b'), 1.2), Rotate90d(('a', 'b'), 1), NormalizeIntensityd(('a',))), + ("a", "b"), + (Flipd(("a", "b"), 0), Spacingd(("a", "b"), 1.2), Rotate90d(("a", "b"), 1), NormalizeIntensityd(("a",))), True, ( "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, upcoming 'Flipd', transform.lazy: False (overridden)\n" @@ -388,8 +398,8 @@ def test_compose_with_logger_name(self, keys, pipeline): "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" "Apply pending transforms - lazy mode: True, key: 'a', pending: 3, upcoming 'NormalizeIntensityd', transform is not lazy\n" - ) - ] + ), + ], ] From a6ad51ec6bf651b8df2c16d403c1baccddd59dad Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 09:18:30 +0100 Subject: [PATCH 081/175] Sorting out line length complaint in compose logging tests Signed-off-by: Ben Murray --- tests/test_compose.py | 57 ++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 60151f86ff..b1bb58cb77 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -365,12 +365,18 @@ def test_compose_with_logger_name(self, keys, pipeline): (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), False, ( - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Spacing', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Flip', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Rotate90', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'Zoom', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, upcoming 'NormalizeIntensity', transform is not lazy\n" + "Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Flip', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Spacing', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Flip', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Rotate90', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Zoom', transform.lazy: False\n" + "Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'NormalizeIntensity', transform is not lazy\n" ), ], [ @@ -378,12 +384,18 @@ def test_compose_with_logger_name(self, keys, pipeline): (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), True, ( - "Accumulate pending transforms - lazy: True, pending: 0, upcoming 'Flip', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 1, upcoming 'Spacing', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 2, upcoming 'Flip', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 3, upcoming 'Rotate90', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 4, upcoming 'Zoom', transform.lazy: False (overridden)\n" - "Apply pending transforms - lazy: True, pending: 5, upcoming 'NormalizeIntensity', transform is not lazy\n" + "Accumulate pending transforms - lazy: True, pending: 0, " + "upcoming 'Flip', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 1, " + "upcoming 'Spacing', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 2, " + "upcoming 'Flip', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 3, " + "upcoming 'Rotate90', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy: True, pending: 4, " + "upcoming 'Zoom', transform.lazy: False (overridden)\n" + "Apply pending transforms - lazy: True, pending: 5, " + "upcoming 'NormalizeIntensity', transform is not lazy\n" ), ], [ @@ -391,13 +403,20 @@ def test_compose_with_logger_name(self, keys, pipeline): (Flipd(("a", "b"), 0), Spacingd(("a", "b"), 1.2), Rotate90d(("a", "b"), 1), NormalizeIntensityd(("a",))), True, ( - "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, upcoming 'Flipd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, upcoming 'Flipd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, upcoming 'Spacingd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, upcoming 'Spacingd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "Apply pending transforms - lazy mode: True, key: 'a', pending: 3, upcoming 'NormalizeIntensityd', transform is not lazy\n" + "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "upcoming 'Flipd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "upcoming 'Flipd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "upcoming 'Spacingd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, " + "upcoming 'Spacingd', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, " + "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" + "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, " + "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" + "Apply pending transforms - lazy mode: True, key: 'a', pending: 3, " + "upcoming 'NormalizeIntensityd', transform is not lazy\n" ), ], ] From 305b88e7724ca6f72ee05f8f8edc237ccd473b35 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 09:35:18 +0100 Subject: [PATCH 082/175] Fix for old lazy_evaluation name creeping in during merge Signed-off-by: Ben Murray --- tests/test_compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 5f6505db2a..5b44473542 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -522,7 +522,7 @@ class TestLazyComposePipelineFixes(unittest.TestCase): @parameterized.expand(TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES) def test_lazy_compose_pipeline_fixes(self, pipeline): data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) - c = Compose(deepcopy(pipeline), lazy_evaluation=True) + c = Compose(deepcopy(pipeline), lazy=True) _ = c(data) From 2aa0ada6cf85847e810000123a9958e9e251bca3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 09:46:02 +0100 Subject: [PATCH 083/175] Autofix Signed-off-by: Ben Murray --- tests/test_compose.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_compose.py b/tests/test_compose.py index 5b44473542..aa2236b828 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -515,6 +515,7 @@ def data_from_keys(keys): else: self.assertTrue(expected, actual) + TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES = [[(Flip(0), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity())]] From 62431ff810f86c09ed672ad6dba78afd7126dd5c Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 27 Apr 2023 18:02:16 +0100 Subject: [PATCH 084/175] Using get_logger so that named loggers all get set up correctly Signed-off-by: Ben Murray --- monai/transforms/transform.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 95f44a8e1b..8dc3df2600 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -23,6 +23,7 @@ import torch from monai import config, transforms +from monai.apps.utils import get_logger from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor from monai.transforms.lazy.functional import apply_pending_transforms @@ -44,9 +45,12 @@ ReturnType = TypeVar("ReturnType") -def _log_pending_info(logger: Any | None, data: Any, transform: Any, activity: str, lazy: bool | None = None): - if logger is None: +def _log_pending_info( + data: Any, transform: Any, activity: str, lazy: bool | None = None, logger_name: str | None = None +): + if logger_name is None: return + logger = get_logger(logger_name) if isinstance(transform, LazyTrait): if lazy is not None and lazy != transform.lazy: @@ -103,20 +107,16 @@ def _apply_transform( ReturnType: The return type of `transform`. """ - logger = None - if logger_name is not None: - logger = logging.getLogger(logger_name) - lazy_tx = isinstance(transform, LazyTrait) if lazy_tx is False or lazy is False: - _log_pending_info(logger, data, transform, "Apply pending transforms", lazy) + _log_pending_info(data, transform, "Apply pending transforms", lazy, logger_name) data = apply_pending_transforms(data, overrides) elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] - _log_pending_info(logger, data, transform, "Apply pending transforms", lazy) + _log_pending_info(data, transform, "Apply pending transforms", lazy, logger_name) data = apply_pending_transforms(data, overrides) else: - _log_pending_info(logger, data, transform, "Accumulate pending transforms", lazy) + _log_pending_info(data, transform, "Accumulate pending transforms", lazy, logger_name) if isinstance(data, tuple) and unpack_parameters: return transform(*data, lazy=lazy) if lazy_tx else transform(*data) From 2ea89c130894d6d56d9ad94c6e8424bd5ec8f07c Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 28 Apr 2023 14:00:04 +0100 Subject: [PATCH 085/175] Fixes for logging; additional compose logging tests; missing imports ApplyPending[d], added docstring for ApplyPending[d] Signed-off-by: Ben Murray --- monai/transforms/__init__.py | 2 + monai/transforms/compose.py | 2 +- monai/transforms/lazy/__init__.py | 5 - monai/transforms/lazy/dictionary.py | 16 ++- monai/transforms/lazy/functional.py | 35 +++--- monai/transforms/transform.py | 15 +-- tests/test_compose.py | 174 ++++++++++++++++++++++++---- 7 files changed, 197 insertions(+), 52 deletions(-) diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index b6aa95a48f..84817d17b0 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -230,6 +230,8 @@ from .inverse_batch_transform import BatchInverseTransform, Decollated, DecollateD, DecollateDict from .io.array import SUPPORTED_READERS, LoadImage, SaveImage from .io.dictionary import LoadImaged, LoadImageD, LoadImageDict, SaveImaged, SaveImageD, SaveImageDict +from .lazy.array import ApplyPending +from .lazy.dictionary import ApplyPendingd, ApplyPendingD, ApplyPendingDict from .lazy.functional import apply_pending from .lazy.utils import combine_transforms, resample from .meta_utility.dictionary import ( diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index e1f2fa1c11..1701119743 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -41,7 +41,7 @@ logger = get_logger(__name__) -__all__ = ["Compose", "OneOf", "RandomOrder", "SomeOf"] +__all__ = ["Compose", "OneOf", "RandomOrder", "SomeOf", "execute_compose"] def execute_compose( diff --git a/monai/transforms/lazy/__init__.py b/monai/transforms/lazy/__init__.py index e6304549f4..1e97f89407 100644 --- a/monai/transforms/lazy/__init__.py +++ b/monai/transforms/lazy/__init__.py @@ -8,8 +8,3 @@ # 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. - -from __future__ import annotations - -from .functional import apply_pending -from .utils import combine_transforms, resample diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 39f3d035b6..955bb655df 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -13,10 +13,10 @@ from __future__ import annotations from monai.config import KeysCollection -from monai.transforms import MapTransform from monai.transforms.inverse import InvertibleTransform +from monai.transforms.transform import MapTransform -__all__ = ["ApplyPendingd"] +__all__ = ["ApplyPendingd", "ApplyPendingD", "ApplyPendingDict"] class ApplyPendingd(InvertibleTransform, MapTransform): @@ -26,20 +26,24 @@ class ApplyPendingd(InvertibleTransform, MapTransform): but its presence causes the pipeline to be executed as it doesn't implement ``LazyTrait`` See ``Compose`` for a detailed explanation of the lazy resampling feature. + + Args: + keys: the keys on which the transform operates. As of 1.2, this field must be set + but it doesn't alter the behaviour of lazy resampling. """ - def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False): - super().__init__(keys, allow_missing_keys) + def __init__(self, keys: KeysCollection): + super().__init__(keys, True) def __call__(self, data): if not isinstance(data, dict): - raise ValueError("'data' must be of type dict but is '{type(data)}'") + raise ValueError(f"'data' must be of type dict but is '{type(data)}'") return data def inverse(self, data): if not isinstance(data, dict): - raise ValueError("'data' must be of type dict but is '{type(data)}'") + raise ValueError(f"'data' must be of type dict but is '{type(data)}'") return data diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index dfcb0c995b..f167a0ba6d 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -15,6 +15,7 @@ import torch +from monai.apps.utils import get_logger from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd from monai.transforms.lazy.utils import ( @@ -31,6 +32,15 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} +def _log_applied_info(data: Any, key=None, logger_name: str | None = None): + if logger_name is None: + return + logger = get_logger(logger_name) + + key_str = "" if key is None else f"key: '{key}', " + logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") + + def apply_pending_transforms(data, overrides: dict | None = None, logger_name: str | None = None): """ apply_pending_transforms iterates over a tuple, list, or dictionary of data, recursively calling itself @@ -61,25 +71,24 @@ def apply_pending_transforms(data, overrides: dict | None = None, logger_name: s return tuple(apply_pending_transforms(d, logger_name=logger_name) for d in data) if isinstance(data, dict): - d = data - for k, v in d.items(): - if isinstance(v, MetaTensor) and v.has_pending_operations: - overrides_ = None if overrides is None else overrides[k] - d[k], _ = apply_pending(v, overrides=overrides_, logger_name=logger_name) - return d + needs_apply_pending = any(isinstance(v, MetaTensor) and v.has_pending_operations for k, v in data.items()) + if needs_apply_pending: + d = dict(data) + for k, v in d.items(): + if isinstance(v, MetaTensor) and v.has_pending_operations: + overrides_ = None if overrides is None else overrides[k] + d[k], _ = apply_pending(v, overrides=overrides_) + _log_applied_info(d[k], key=k, logger_name=logger_name) + return d if isinstance(data, MetaTensor) and data.has_pending_operations: - data, _ = apply_pending(data, overrides=overrides, logger_name=logger_name) + data, _ = apply_pending(data, overrides=overrides) + _log_applied_info(data, logger_name=logger_name) return data -def apply_pending( - data: torch.Tensor | MetaTensor, - pending: list | None = None, - overrides: dict | None = None, - logger_name: str | None = None, -): +def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None): """ This method applies pending transforms to `data` tensors. Currently, only 2d and 3d inputs are supported. diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 8dc3df2600..4a1409f195 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -62,11 +62,12 @@ def _log_pending_info( if isinstance(transform, MapTransform): for k in transform.keys: - pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 - logger.info( - f"{activity} - lazy mode: {lazy}, key: '{k}', " - f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) + if k in data: + pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 + logger.info( + f"{activity} - lazy mode: {lazy}, key: '{k}', " + f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) else: pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 logger.info( @@ -111,10 +112,10 @@ def _apply_transform( if lazy_tx is False or lazy is False: _log_pending_info(data, transform, "Apply pending transforms", lazy, logger_name) - data = apply_pending_transforms(data, overrides) + data = apply_pending_transforms(data, overrides, logger_name) elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] _log_pending_info(data, transform, "Apply pending transforms", lazy, logger_name) - data = apply_pending_transforms(data, overrides) + data = apply_pending_transforms(data, overrides, logger_name) else: _log_pending_info(data, transform, "Accumulate pending transforms", lazy, logger_name) diff --git a/tests/test_compose.py b/tests/test_compose.py index aa2236b828..0055bc93bf 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -24,6 +24,8 @@ from monai.data import DataLoader, Dataset from monai.transforms import ( AddChannel, + ApplyPending, + ApplyPendingd, Compose, Flip, NormalizeIntensity, @@ -35,7 +37,7 @@ Zoom, ) from monai.transforms.compose import execute_compose -from monai.transforms.spatial.dictionary import Flipd, Rotate90d, Spacingd +from monai.transforms.spatial.dictionary import Flipd, Rotate90d, Spacingd, Zoomd from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -365,37 +367,66 @@ def test_compose_with_logger_name(self, keys, pipeline): (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), False, ( - "Apply pending transforms - lazy: False, pending: 0, " + "INFO - Apply pending transforms - lazy: False, pending: 0, " "upcoming 'Flip', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, " + "INFO - Apply pending transforms - lazy: False, pending: 0, " "upcoming 'Spacing', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, " + "INFO - Apply pending transforms - lazy: False, pending: 0, " "upcoming 'Flip', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, " + "INFO - Apply pending transforms - lazy: False, pending: 0, " "upcoming 'Rotate90', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, " + "INFO - Apply pending transforms - lazy: False, pending: 0, " "upcoming 'Zoom', transform.lazy: False\n" - "Apply pending transforms - lazy: False, pending: 0, " + "INFO - Apply pending transforms - lazy: False, pending: 0, " "upcoming 'NormalizeIntensity', transform is not lazy\n" ), ], + [ + None, + ( + Flip(0, lazy=True), + Spacing((1.2, 1.2), lazy=True), + Flip(1, lazy=True), + Rotate90(1), + Zoom(0.8, lazy=True), + NormalizeIntensity(), + ), + None, + ( + "INFO - Accumulate pending transforms - lazy: None, pending: 0, " + "upcoming 'Flip', transform.lazy: True\n" + "INFO - Accumulate pending transforms - lazy: None, pending: 1, " + "upcoming 'Spacing', transform.lazy: True\n" + "INFO - Accumulate pending transforms - lazy: None, pending: 2, " + "upcoming 'Flip', transform.lazy: True\n" + "INFO - Apply pending transforms - lazy: None, pending: 3, " + "upcoming 'Rotate90', transform.lazy: False\n" + "INFO - Pending transforms applied: applied_operations: 3\n" + "INFO - Accumulate pending transforms - lazy: None, pending: 0, " + "upcoming 'Zoom', transform.lazy: True\n" + "INFO - Apply pending transforms - lazy: None, pending: 1, " + "upcoming 'NormalizeIntensity', transform is not lazy\n" + "INFO - Pending transforms applied: applied_operations: 5\n" + ), + ], [ None, (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), True, ( - "Accumulate pending transforms - lazy: True, pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, pending: 0, " "upcoming 'Flip', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 1, " + "INFO - Accumulate pending transforms - lazy: True, pending: 1, " "upcoming 'Spacing', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 2, " + "INFO - Accumulate pending transforms - lazy: True, pending: 2, " "upcoming 'Flip', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 3, " + "INFO - Accumulate pending transforms - lazy: True, pending: 3, " "upcoming 'Rotate90', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy: True, pending: 4, " + "INFO - Accumulate pending transforms - lazy: True, pending: 4, " "upcoming 'Zoom', transform.lazy: False (overridden)\n" - "Apply pending transforms - lazy: True, pending: 5, " + "INFO - Apply pending transforms - lazy: True, pending: 5, " "upcoming 'NormalizeIntensity', transform is not lazy\n" + "INFO - Pending transforms applied: applied_operations: 5\n" ), ], [ @@ -403,20 +434,121 @@ def test_compose_with_logger_name(self, keys, pipeline): (Flipd(("a", "b"), 0), Spacingd(("a", "b"), 1.2), Rotate90d(("a", "b"), 1), NormalizeIntensityd(("a",))), True, ( - "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " "upcoming 'Flipd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " "upcoming 'Flipd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " "upcoming 'Spacingd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, " + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, " "upcoming 'Spacingd', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, " + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, " "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, " + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, " "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "Apply pending transforms - lazy mode: True, key: 'a', pending: 3, " + "INFO - Apply pending transforms - lazy mode: True, key: 'a', pending: 3, " "upcoming 'NormalizeIntensityd', transform is not lazy\n" + "INFO - Pending transforms applied: key: 'a', applied_operations: 3\n" + "INFO - Pending transforms applied: key: 'b', applied_operations: 3\n" + ), + ], + [ + ("a", "b"), + ( + Flipd(keys="a", spatial_axis=0), + Rotate90d(keys="b", k=1, allow_missing_keys=True), + Zoomd(keys=("a", "b"), zoom=0.8, allow_missing_keys=True), + Spacingd(keys="a", pixdim=1.2), + ), + True, + ( + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "upcoming 'Flipd', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "upcoming 'Zoomd', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, " + "upcoming 'Zoomd', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, " + "upcoming 'Spacingd', transform.lazy: False (overridden)\n" + "INFO - Pending transforms applied: key: 'a', applied_operations: 3\n" + "INFO - Pending transforms applied: key: 'b', applied_operations: 2\n" + ), + ], + [ + None, + (Flip(0), Spacing((1.2, 1.2)), Flip(1), ApplyPending(), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + False, + ( + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Flip', transform.lazy: False\n" + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Spacing', transform.lazy: False\n" + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Flip', transform.lazy: False\n" + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'ApplyPending', transform is not lazy\n" + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Rotate90', transform.lazy: False\n" + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'Zoom', transform.lazy: False\n" + "INFO - Apply pending transforms - lazy: False, pending: 0, " + "upcoming 'NormalizeIntensity', transform is not lazy\n" + ), + ], + [ + None, + (Flip(0), Spacing((1.2, 1.2)), Flip(1), ApplyPending(), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + True, + ( + "INFO - Accumulate pending transforms - lazy: True, pending: 0, " + "upcoming 'Flip', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy: True, pending: 1, " + "upcoming 'Spacing', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy: True, pending: 2, " + "upcoming 'Flip', transform.lazy: False (overridden)\n" + "INFO - Apply pending transforms - lazy: True, pending: 3, " + "upcoming 'ApplyPending', transform is not lazy\n" + "INFO - Pending transforms applied: applied_operations: 3\n" + "INFO - Accumulate pending transforms - lazy: True, pending: 0, " + "upcoming 'Rotate90', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy: True, pending: 1, " + "upcoming 'Zoom', transform.lazy: False (overridden)\n" + "INFO - Apply pending transforms - lazy: True, pending: 2, " + "upcoming 'NormalizeIntensity', transform is not lazy\n" + "INFO - Pending transforms applied: applied_operations: 5\n" + ), + ], + [ + ("a", "b"), + ( + Flipd(keys="a", spatial_axis=0), + Rotate90d(keys="b", k=1, allow_missing_keys=True), + ApplyPendingd(keys=("a", "b")), + Zoomd(keys=("a", "b"), zoom=0.8, allow_missing_keys=True), + Spacingd(keys="a", pixdim=1.2), + ), + True, + ( + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "upcoming 'Flipd', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" + "INFO - Apply pending transforms - lazy mode: True, key: 'a', pending: 1, " + "upcoming 'ApplyPendingd', transform is not lazy\n" + "INFO - Apply pending transforms - lazy mode: True, key: 'b', pending: 1, " + "upcoming 'ApplyPendingd', transform is not lazy\n" + "INFO - Pending transforms applied: key: 'a', applied_operations: 1\n" + "INFO - Pending transforms applied: key: 'b', applied_operations: 1\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "upcoming 'Zoomd', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "upcoming 'Zoomd', transform.lazy: False (overridden)\n" + "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "upcoming 'Spacingd', transform.lazy: False (overridden)\n" + "INFO - Pending transforms applied: key: 'a', applied_operations: 3\n" + "INFO - Pending transforms applied: key: 'b', applied_operations: 2\n" ), ], ] @@ -437,6 +569,8 @@ def data_from_keys(keys): def test_compose_with_logging(self, keys, pipeline, lazy, expected): stream = StringIO() handler = logging.StreamHandler(stream) + formatter = logging.Formatter("%(levelname)s - %(message)s") + handler.setFormatter(formatter) logger = logging.getLogger("a_logger_name") logger.setLevel(logging.INFO) while len(logger.handlers) > 0: From 628a7f8e6649204f0f172f250f44c958aeeb8ee3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 28 Apr 2023 14:44:23 +0100 Subject: [PATCH 086/175] Adding ApplyPending and ApplyPendingd to transforms.rst Signed-off-by: Ben Murray --- docs/source/transforms.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index e045a7e741..378b771750 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -958,6 +958,17 @@ MRI Transforms :special-members: __call__ +Lazy +^^^^ + +`ApplyPending` +"""""""""""""" + +.. autoclass:: ApplyPending + :members: + :special-members: __call__ + + Utility ^^^^^^^ @@ -1912,6 +1923,17 @@ Smooth Field (Dict) :special-members: __call__ +Lazy (Dict) +^^^^^^^^^^^ + +`ApplyPendingd` +"""""""""""""" + +.. autoclass:: ApplyPendingd + :members: + :special-members: __call__ + + Utility (Dict) ^^^^^^^^^^^^^^ From 5b347557de58fea7edf489bbc0ef118c5a196ee5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 28 Apr 2023 15:25:48 +0100 Subject: [PATCH 087/175] Fixed transforms.rst modifications Signed-off-by: Ben Murray --- docs/source/transforms.rst | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index 378b771750..fe17fa4efe 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -1927,7 +1927,7 @@ Lazy (Dict) ^^^^^^^^^^^ `ApplyPendingd` -"""""""""""""" +""""""""""""""" .. autoclass:: ApplyPendingd :members: @@ -2233,9 +2233,3 @@ Utilities .. automodule:: monai.transforms.utils_pytorch_numpy_unification :members: - -Lazy ----- -.. automodule:: monai.transforms.lazy - :members: - :imported-members: From d547b81eed9c588348d58a7bb2ffa178e5e8ddb2 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 2 May 2023 10:04:52 +0100 Subject: [PATCH 088/175] Refactor of lazy to provide in_order and out_of_order lazy execution Signed-off-by: Ben Murray --- docs/source/transforms.rst | 2 +- monai/transforms/compose.py | 229 ++++++++++++++++++-- monai/transforms/inverse.py | 4 +- monai/transforms/lazy/array.py | 4 +- monai/transforms/lazy/dictionary.py | 12 +- monai/transforms/lazy/executors.py | 202 ++++++++++++++++++ monai/transforms/lazy/functional.py | 64 +----- monai/transforms/traits.py | 24 ++- monai/transforms/transform.py | 82 +++----- tests/test_compose.py | 275 +++++++++++++++++++------ tests/test_integration_lazy_samples.py | 2 +- 11 files changed, 697 insertions(+), 203 deletions(-) create mode 100644 monai/transforms/lazy/executors.py diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index 378b771750..fd557e67fb 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -1927,7 +1927,7 @@ Lazy (Dict) ^^^^^^^^^^^ `ApplyPendingd` -"""""""""""""" +""""""""""""""" .. autoclass:: ApplyPendingd :members: diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 1701119743..f69cc71938 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -24,10 +24,13 @@ import monai from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor +from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform +from monai.transforms.lazy.array import ApplyPending +from monai.transforms.lazy.dictionary import ApplyPendingd # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) -from monai.transforms.lazy.functional import apply_pending_transforms +from monai.transforms.lazy.executors import apply_pending_transforms from monai.transforms.traits import LazyTrait, ThreadUnsafe from monai.transforms.transform import ( # noqa: F401 LazyTransform, @@ -52,6 +55,7 @@ def execute_compose( start: int = 0, end: int | None = None, lazy: bool | None = False, + lazy_strategy: str = "in_order", overrides: dict | None = None, threading: bool = False, logger_name: str | None = None, @@ -75,6 +79,9 @@ def execute_compose( lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. + lazy_strategy: this field controls how execution occurs when processing data lazily. Permitted + options are "in_order", "out_of_order". Please see `Compose`_ for more details of what these + options mean. In general, you should not need to change this from its default. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when lazy @@ -109,12 +116,138 @@ def execute_compose( if threading: _transform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform data = apply_transform( - _transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides, logger_name=logger_name + _transform, + data, + map_items, + unpack_items, + lazy=lazy, + lazy_strategy=lazy_strategy, + overrides=overrides, + logger_name=logger_name, ) - data = apply_pending_transforms(data, overrides, logger_name=logger_name) + data = apply_pending_transforms(data, None, overrides, logger_name=logger_name) return data +class ComposeCompiler: + """ + The ComposeCompiler is an implementation class that is required to parse options for Compose. It should currently + be considered an implementation detail that should not be interacted with directly by users of MONAI, although that + may change in subsequent releases. Its job is to parse options provided to `Compose.__call__`_ to set execution + modes for lazy resampling. + + See `Compose`_ for a detailed explanation of lazy resampling. + """ + + def __init__(self): + # construct the list of options + options = {"reorder": {"lazy_last": self.reorder_lazy_last, "lazy_last_nosync": self.reorder_lazy_last_nosync}} + self.options = options + + def __call__(self, transforms, lazy: bool | None, options: dict | None = None): + """ + Get a policy object that controls the way `Compose`_ executes a list of transforms. + + Args: + transforms: a list of transforms to be executed + lazy: the current lazy mode (False, None, or True) + options: the options that determine the execution policy + + Returns: + a dictionary specifying the execution policy + + """ + if lazy is False or options is None: + return ComposeCompiler.generate_policy() + + if len(options.keys()) > 1: + raise ValueError("Only one option can currently be set") + + for k, v in options.items(): + if k not in self.options.keys(): + raise KeyError( + f"'{k}' is not a valid option key. Valid options are " f"{tuple(k for k in self.options.keys())}" + ) + + option = self.options[k] + if v not in option.keys(): + raise KeyError(f"'{v}' is not a valid option value. Value options for " f"'{k}' are {option.keys()}") + + action = option[v] + + return action(transforms=transforms, lazy=lazy) + + @classmethod + def reorder_lazy_last(cls, *, transforms: list, lazy: bool | None, **kwargs): + subsections = list() + subsection_starts = list() + # pass 1: split the transform list into subsections + i_s = 0 + for i_t in range(len(transforms)): + if isinstance(transforms[i_t], (ApplyPending, ApplyPendingd)): + # this subsection ends and is added to the subsection list + if i_s < i_t: + subsections.append(transforms[i_s:i_t]) + subsection_starts.append(i_s) + # add apply pending in its own list + subsections.append([transforms[i_t]]) + subsection_starts.append(i_t) + i_s = i_t + 1 + + if i_s != len(transforms): + subsections.append(transforms[i_s:]) + subsection_starts.append(i_s) + + # pass 2: calculate the permuted indices + permuted_indices = list() + for sub_start, subsection in zip(subsection_starts, subsections): + for i_s, s in enumerate(subsection): + if not cls._executing_lazily(s, lazy): + permuted_indices.append(i_s + sub_start) + for i_s, s in enumerate(subsection): + if cls._executing_lazily(s, lazy): + permuted_indices.append(i_s + sub_start) + + # pass 2: sort the subsections + reordered = list() + for subsection in subsections: + # non-lazy, lazy + subsection = [t for t in subsection if not cls._executing_lazily(t, lazy)] + [ + t for t in subsection if cls._executing_lazily(t, lazy) + ] + reordered.extend(subsection) + + return ComposeCompiler.generate_policy({"indices": permuted_indices}) + + @classmethod + def reorder_lazy_last_nosync(cls, *, transforms: list, **_): + """ + 'reorder: lazy_last_nosync' is implemented through use of the 'out_of_order' execution + policy. See 'Compose'_ for details of this policy. + Args: + transforms: Not used by this method + + Returns: + + """ + return cls.generate_policy({"can_invert": False, "lazy_policy": "out_of_order"}) + + @staticmethod + def generate_policy(overrides: dict | None = None): + default_policy = {"indices": None, "transforms": None, "can_invert": True, "lazy_policy": "in_order"} + if overrides is not None: + for k, v in overrides.items(): + default_policy[k] = v + return default_policy + + @staticmethod + def _executing_lazily(t, lazy_policy): + if isinstance(t, LazyTrait): + lazy_ = t.lazy if lazy_policy is None else lazy_policy + return lazy_ + return False + + class Compose(Randomizable, InvertibleTransform): """ ``Compose`` provides the ability to chain a series of callables together in @@ -262,6 +395,7 @@ def __init__( unpack_items: bool = False, lazy: bool | None = False, overrides: dict | None = None, + options: dict | None = None, logger_name: str | None = None, ) -> None: if transforms is None: @@ -272,6 +406,7 @@ def __init__( self.set_random_state(seed=get_seed()) self.lazy = lazy self.overrides = overrides + self.options = options self.logger_name = logger_name def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: @@ -350,33 +485,94 @@ def __len__(self): return len(self.flatten().transforms) def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): - return execute_compose( + lazy_ = self.lazy if lazy is None else lazy + policy = ComposeCompiler()(self.transforms, lazy_) + lazy_strategy = policy["lazy_policy"] + indices = policy["indices"] + + # permute the transforms if required + transforms = self.transforms if indices is None else [self.transforms[i] for i in indices] + + result = execute_compose( input_, - self.transforms, + transforms, start=start, end=end, map_items=self.map_items, unpack_items=self.unpack_items, lazy=self.lazy, # type: ignore + lazy_strategy=lazy_strategy, overrides=self.overrides, threading=threading, logger_name=self.logger_name, ) + # if the transforms were permuted, record it in the metadata for inversion + if indices is not None: + if isinstance(result, monai.data.MetaTensor): + self.push_transform(result, extra_info={"applied_order": indices}) + elif isinstance(result, Mapping): + for key in result: # dictionary not change size during iteration + if isinstance(result[key], monai.data.MetaTensor) or self.trace_key(key) in result: + self.push_transform(result, key, extra_info={"applied_order": indices}) + + return result + def inverse(self, data): - invertible_transforms = [t for t in self.flatten().transforms if isinstance(t, InvertibleTransform)] - if not invertible_transforms: - warnings.warn("inverse has been called but no invertible transforms have been supplied") + if self.options is not None: + compiler = ComposeCompiler() + policy = compiler(self.transforms, self.lazy, self.options) + else: + policy = ComposeCompiler().generate_policy() + + indices = policy["indices"] + can_invert = policy["can_invert"] + if can_invert is False: + raise ValueError("'inverse' is not supported with options {self.options}") + + data_ = deepcopy(data) + + if indices is not None: + applied_order = None + if isinstance(data_, monai.data.MetaTensor): + applied_order = self.pop_transform(data_)[TraceKeys.EXTRA_INFO]["applied_order"] + elif isinstance(data_, Mapping): + for key in data_: + if isinstance(data_[key], monai.data.MetaTensor) or self.trace_key(key) in data_: + applied_order = self.pop_transform(data_, key)[TraceKeys.EXTRA_INFO]["applied_order"] + else: + raise RuntimeError( + f"Inverse only implemented for Mapping (dictionary) or MetaTensor data, got type {type(data)}." + ) + if applied_order is None: + # no invertible transforms have been applied + return data_ + + # loop backwards over transforms + for o in reversed(applied_order): + if isinstance(self.transforms[o], InvertibleTransform): + data_ = apply_transform(self.transforms[o].inverse, data_, self.map_items, self.unpack_items) + else: + invertible_transforms = [t for t in self.flatten().transforms if isinstance(t, InvertibleTransform)] + if not invertible_transforms: + warnings.warn("inverse has been called but no invertible transforms have been supplied") - # loop backwards over transforms - for t in reversed(invertible_transforms): - if isinstance(t, LazyTrait) and t.lazy: + if self.lazy is not False: warnings.warn( - f"inversing {t.__class__.__name__} lazily may not implemented" - "please set `lazy=False` before calling inverse." + f"'lazy' is set to {self.lazy} but lazy execution is not supported when inverting. " + f"'lazy' has been overridden to False for the call to inverse" ) - data = apply_transform(t.inverse, data, self.map_items, self.unpack_items) - return data + # loop backwards over transforms + for t in reversed(invertible_transforms): + # if isinstance(t, LazyTrait) and t.lazy: + # warnings.warn( + # f"inversing {t.__class__.__name__} lazily may not implemented" + # "please set `lazy=False` before calling inverse." + # ) + data_ = apply_transform( + t.inverse, data_, self.map_items, self.unpack_items, lazy=False, logger_name=self.logger_name + ) + return data_ class OneOf(Compose): @@ -759,8 +955,7 @@ def inverse(self, data): # loop backwards over transforms for o in reversed(applied_order): - transform = self.transforms[o] - if isinstance(transform, InvertibleTransform): + if isinstance(self.transforms[o], InvertibleTransform): data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) return data diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 563c0502f6..9f3db86b89 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -23,7 +23,7 @@ from monai.data.meta_obj import MetaObj, get_track_meta from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd -from monai.transforms.traits import LazyTrait +from monai.transforms.traits import InvertibleTrait, LazyTrait from monai.transforms.transform import Transform from monai.utils import LazyAttr, MetaKeys, TraceKeys, convert_to_dst_type, convert_to_numpy, convert_to_tensor @@ -325,7 +325,7 @@ def trace_transform(self, to_trace: bool): self.tracing = prev -class InvertibleTransform(TraceableTransform): +class InvertibleTransform(TraceableTransform, InvertibleTrait): """Classes for invertible transforms. This class exists so that an ``invert`` method can be implemented. This allows, for diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index c4e873af73..aa635eeda9 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -11,12 +11,12 @@ from __future__ import annotations -from monai.transforms.inverse import InvertibleTransform +from monai.transforms.traits import InvertibleTrait __all__ = ["ApplyPending"] -class ApplyPending(InvertibleTransform): +class ApplyPending(InvertibleTrait): """ ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure resampling happens before the next transform. It doesn't do anything itself, but its presence diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 955bb655df..cc98026f1b 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -13,13 +13,12 @@ from __future__ import annotations from monai.config import KeysCollection -from monai.transforms.inverse import InvertibleTransform -from monai.transforms.transform import MapTransform +from monai.transforms.traits import InvertibleTrait, MapTrait __all__ = ["ApplyPendingd", "ApplyPendingD", "ApplyPendingDict"] -class ApplyPendingd(InvertibleTransform, MapTransform): +class ApplyPendingd(InvertibleTrait, MapTrait): """ ApplyPendingd can be inserted into a pipeline that is being executed lazily in order to ensure resampling happens before the next transform. It doesn't do anything itself, @@ -28,12 +27,11 @@ class ApplyPendingd(InvertibleTransform, MapTransform): See ``Compose`` for a detailed explanation of the lazy resampling feature. Args: - keys: the keys on which the transform operates. As of 1.2, this field must be set - but it doesn't alter the behaviour of lazy resampling. + keys: the keys for tensors that should have their pending transforms executed """ - def __init__(self, keys: KeysCollection): - super().__init__(keys, True) + def __init__(self, keys: KeysCollection | None): + self.keys = keys def __call__(self, data): if not isinstance(data, dict): diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py new file mode 100644 index 0000000000..99576ee22f --- /dev/null +++ b/monai/transforms/lazy/executors.py @@ -0,0 +1,202 @@ +# Copyright (c) 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. + +from __future__ import annotations + +from typing import Any + +from monai.apps.utils import get_logger +from monai.data.meta_tensor import MetaTensor +from monai.transforms.lazy.array import ApplyPending +from monai.transforms.lazy.dictionary import ApplyPendingd +from monai.transforms.lazy.functional import apply_pending +from monai.transforms.traits import LazyTrait, MapTrait + +__all__ = ["apply_pending_transforms"] + + +def _log_pending_info( + transform: Any, data: Any, activity: str, lazy: bool | None = None, logger_name: str | None = None +): + if logger_name is None: + return + logger = get_logger(logger_name) + + if isinstance(transform, LazyTrait): + if lazy is not None and lazy != transform.lazy: + tlazy = f", transform.lazy: {transform.lazy} (overridden)" + else: + tlazy = f", transform.lazy: {transform.lazy}" + else: + tlazy = ", transform is not lazy" + + if isinstance(transform, MapTrait): + for k in transform.keys: + if k in data: + pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 + logger.info( + f"{activity} - lazy mode: {lazy}, key: '{k}', " + f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) + else: + pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 + logger.info( + f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) + + +def _log_applied_info(data: Any, key=None, logger_name: str | None = None): + if logger_name is None: + return + logger = get_logger(logger_name) + + key_str = "" if key is None else f"key: '{key}', " + logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") + + +def apply_pending_transforms( + data: dict, keys: tuple | None, overrides: dict | None = None, logger_name: str | None = None +): + """ + apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain + tensors. + + When operating on a dictionary of tensors, the 'keys' parameter determines what tensors should be checked. + If 'keys' is not set, all keys of 'data' are considered. + + This method optionally takes a set of overrides that can be used to change specific parameters on the + transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used + to override the default logger, to provide telemetry during the execution of pending transforms. + + This method is intended primarily for use by ``execute_compose`` and other methods that handle the + underlying execution of transform pipelines. You should not need to use it in the general case, unless + you are developing functionality to perform such operations. + + Args: + data: a ``torch.Tensor`` or ``MetaTensor``, or list, tuple or dictionary of tensors. + keys: an optional tuple of keys that filters the keys on 'data' if it is a dict + overrides: An optional dictionary that specifies parameters that can be used to override transform + arguments when they are called. When 'data' is a dict, this dictionary should contain a dictionary + of overrides for each key that needs them + logger_name: An optional name for a logger to be used when applying pending transforms. If None, + logging is suppressed. + Returns: + an object of the same type as data if pending transforms were applied, or 'data' if they were not + """ + if isinstance(data, dict): + # get the keys from 'data' for metatensors with pending operations. If 'keys' is set, select + # only data keys that are in 'keys' + active_keys = [k for k in data.keys() if keys is None or k in keys] + keys_to_update = [k for k in active_keys if isinstance(data[k], MetaTensor) and data[k].has_pending_operations] + + if len(keys_to_update) > 0: + rdata = dict(data) + + for k in keys_to_update: + overrides_ = None if overrides is None else overrides.get(k, None) + rdata[k], _ = apply_pending(data[k], overrides=overrides_) + _log_applied_info(rdata[k], key=k, logger_name=logger_name) + + return rdata + else: + if isinstance(data, MetaTensor) and data.has_pending_operations: + rdata, _ = apply_pending(data, overrides=overrides) + _log_applied_info(rdata, logger_name=logger_name) + return rdata + + return data + + +def apply_pending_transforms_in_order( + transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: str | None = None +): + """ + This method causes "out of order" processing of pending transforms to occur. + + Out of order processing for lazy resampling only causes pending transforms to be processed when + an `ApplyPending`_ or `ApplyPendingd`_ transform is encountered in the pipeline. + + This method is designed to be used only in the context of implementing lazy resampling functionality. In general + you should not need to interact with or use this method directly. + Args: + transform: a transform that should be evaluated to determine whether pending transforms should be applied + data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may + need to be applied + lazy: The lazy mode that is being applied (this can be False, True or None) + overrides: An optional dictionary containing overrides to be applied to the pending transforms when they + are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that + needs them + logger_name: An optional name for a logger to be used when applying pending transforms. If None, + logging is suppressed. + Returns: + an object of the same type as data if pending transforms were applied, or 'data' if they were not + + """ + if lazy is False: + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, None, overrides, logger_name) + + lazy_ = transform.lazy if isinstance(transform, LazyTrait) and lazy is None else lazy + if not isinstance(transform, LazyTrait) or lazy_ is False: + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, None, overrides, logger_name) + + if isinstance(transform, ApplyPendingd): + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, transform.keys, overrides, logger_name) + + if isinstance(transform, ApplyPending): + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, None, overrides, logger_name) + + _log_pending_info(transform, data, "Accumulate pending transforms", lazy, logger_name) + + return data + + +def apply_pending_transforms_out_of_order( + transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: str | None = None +): + """ + This method causes "out of order" processing of pending transforms to occur. + + Out of order processing for lazy resampling only causes pending transforms to be processed when + an `ApplyPending`_ or `ApplyPendingd`_ transform is encountered in the pipeline. + + This method is designed to be used only in the context of implementing lazy resampling functionality. In general + you should not need to interact with or use this method directly. + Args: + transform: a transform that should be evaluated to determine whether pending transforms should be applied + data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may + need to be applied + lazy: The lazy mode that is being applied (this can be False, True or None) + overrides: An optional dictionary containing overrides to be applied to the pending transforms when they + are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that + needs them + logger_name: An optional name for a logger to be used when applying pending transforms. If None, + logging is suppressed. + Returns: + an object of the same type as data if pending transforms were applied, or 'data' if they were not + + """ + if lazy is False: + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, None, overrides, logger_name) + + if isinstance(transform, ApplyPendingd): + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, transform.keys, overrides, logger_name) + + if isinstance(transform, ApplyPending): + _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + return apply_pending_transforms(data, None, overrides, logger_name) + + _log_pending_info(transform, data, "Accumulate pending transforms", lazy, logger_name) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index f167a0ba6d..2b351f27ca 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -15,9 +15,13 @@ import torch -from monai.apps.utils import get_logger +# from monai.apps.utils import get_logger from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd + +# from monai.transforms.lazy.array import ApplyPending +# from monai.transforms.lazy.dictionary import ApplyPendingd +# from monai.transforms.traits import LazyTrait from monai.transforms.lazy.utils import ( affine_from_pending, combine_transforms, @@ -25,6 +29,8 @@ kwargs_from_pending, resample, ) + +# from monai.transforms.traits import MapTrait from monai.utils import LazyAttr, look_up_option __all__ = ["apply_pending", "apply_pending_transforms"] @@ -32,62 +38,6 @@ __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} -def _log_applied_info(data: Any, key=None, logger_name: str | None = None): - if logger_name is None: - return - logger = get_logger(logger_name) - - key_str = "" if key is None else f"key: '{key}', " - logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") - - -def apply_pending_transforms(data, overrides: dict | None = None, logger_name: str | None = None): - """ - apply_pending_transforms iterates over a tuple, list, or dictionary of data, recursively calling itself - to get a single tensor. If that tensor is a MetaTensor with pending lazy transforms, it then calls - ``apply_pending_to_tensor`` on each element to perform the executing of the pending transforms. - - This method optionally takes a set of overrides that can be used to change specific parameters on the - transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used - to override the default logger, to provide telemetry during the execution of pending transforms. - - This method is intended primarily for use by ``execute_compose`` and other methods that handle the - underlying execution of transform pipelines. You should not need to use it in the general case, unless - you are developing functionality to perform such operations. - - Args: - data: a ``torch.Tensor`` or ``MetaTensor``, or list, tuple or dictionary of tensors. - overrides: An optional dictionary that specifies parameters that can be used to override transform - arguments when they are called - logger_name: An optional name for a logger to be used when applying pending transforms. If None, - logging is suppressed. - Returns: - - """ - if isinstance(data, list): - return [apply_pending_transforms(d, logger_name=logger_name) for d in data] - - if isinstance(data, tuple): - return tuple(apply_pending_transforms(d, logger_name=logger_name) for d in data) - - if isinstance(data, dict): - needs_apply_pending = any(isinstance(v, MetaTensor) and v.has_pending_operations for k, v in data.items()) - if needs_apply_pending: - d = dict(data) - for k, v in d.items(): - if isinstance(v, MetaTensor) and v.has_pending_operations: - overrides_ = None if overrides is None else overrides[k] - d[k], _ = apply_pending(v, overrides=overrides_) - _log_applied_info(d[k], key=k, logger_name=logger_name) - return d - - if isinstance(data, MetaTensor) and data.has_pending_operations: - data, _ = apply_pending(data, overrides=overrides) - _log_applied_info(data, logger_name=logger_name) - - return data - - def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None): """ This method applies pending transforms to `data` tensors. diff --git a/monai/transforms/traits.py b/monai/transforms/traits.py index 1d8436a1f2..013c52aefb 100644 --- a/monai/transforms/traits.py +++ b/monai/transforms/traits.py @@ -14,7 +14,9 @@ from __future__ import annotations -__all__ = ["LazyTrait", "RandomizableTrait", "MultiSampleTrait", "ThreadUnsafe"] +__all__ = ["LazyTrait", "InvertibleTrait", "MapTrait", "RandomizableTrait", "MultiSampleTrait", "ThreadUnsafe"] + +from typing import Any class LazyTrait: @@ -45,6 +47,26 @@ def lazy(self, enabled: bool): raise NotImplementedError() +class InvertibleTrait: + """ + An interface to indicate that the transform can be inverted, i.e. undone by performing + the inverse of the operation performed during `__call__`. + """ + + def inverse(self, data: Any) -> Any: + raise NotImplementedError() + + +class MapTrait: + """ + An interface to indicate that the transform has the capability to execute on dictionaries + of tensors rather than individual tensors. + """ + + def __init__(self): + self.keys = None + + class RandomizableTrait: """ An interface to indicate that the transform has the capability to perform diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 4a1409f195..bc33532953 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -23,11 +23,10 @@ import torch from monai import config, transforms -from monai.apps.utils import get_logger from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor -from monai.transforms.lazy.functional import apply_pending_transforms -from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe +from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order +from monai.transforms.traits import LazyTrait, MapTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first from monai.utils.enums import TransformBackends from monai.utils.misc import MONAIEnvVars @@ -45,41 +44,12 @@ ReturnType = TypeVar("ReturnType") -def _log_pending_info( - data: Any, transform: Any, activity: str, lazy: bool | None = None, logger_name: str | None = None -): - if logger_name is None: - return - logger = get_logger(logger_name) - - if isinstance(transform, LazyTrait): - if lazy is not None and lazy != transform.lazy: - tlazy = f", transform.lazy: {transform.lazy} (overridden)" - else: - tlazy = f", transform.lazy: {transform.lazy}" - else: - tlazy = ", transform is not lazy" - - if isinstance(transform, MapTransform): - for k in transform.keys: - if k in data: - pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 - logger.info( - f"{activity} - lazy mode: {lazy}, key: '{k}', " - f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) - else: - pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 - logger.info( - f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) - - def _apply_transform( transform: Callable[..., ReturnType], data: Any, unpack_parameters: bool = False, lazy: bool | None = False, + lazy_strategy: str | None = "in_order", overrides: dict | None = None, logger_name: str | None = None, ) -> ReturnType: @@ -101,28 +71,39 @@ def _apply_transform( Args: transform: a callable to be used to transform `data`. - parameters: parameters for the `transform`. + data: the tensorlike or dictionary of tensorlikes to be executed on unpack_parameters: whether to unpack parameters for `transform`. Defaults to False. + lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be + carried out on a transform by transform basis. If True, all lazy transforms will + be executed by accumulating changes and resampling as few times as possible. + lazy_strategy: this field controls how execution occurs when processing data lazily. Permitted + options are "in_order", "out_of_order". Please see `Compose`_ for more details of what these + options mean. In general, you should not need to change this from its default. + overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden + when executing a pipeline. These each parameter that is compatible with a given transform is then applied + to that transform before it is executed. Note that overrides are currently only applied when lazy + is True. If lazy is False they are ignored. + currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, + please see also :py:func:`monai.transforms.lazy.apply_pending` and ``Compose`` for more details. + logger_name: The name of the logger that should be used during transform execution. If None, logging is + suppressed. Returns: ReturnType: The return type of `transform`. """ - lazy_tx = isinstance(transform, LazyTrait) - - if lazy_tx is False or lazy is False: - _log_pending_info(data, transform, "Apply pending transforms", lazy, logger_name) - data = apply_pending_transforms(data, overrides, logger_name) - elif lazy is None and transform.lazy is False: # type: ignore[attr-defined] - _log_pending_info(data, transform, "Apply pending transforms", lazy, logger_name) - data = apply_pending_transforms(data, overrides, logger_name) + if lazy_strategy == "in_order": + data = apply_pending_transforms_in_order(transform, data, lazy, overrides, logger_name) + elif lazy_strategy == "out_of_order": + data = apply_pending_transforms_out_of_order(transform, data, lazy, overrides, logger_name) else: - _log_pending_info(data, transform, "Accumulate pending transforms", lazy, logger_name) + raise ValueError(f"'lazy_strategy' must be one of {('in_order', 'out_of_order')} but is '{lazy_strategy}") if isinstance(data, tuple) and unpack_parameters: - return transform(*data, lazy=lazy) if lazy_tx else transform(*data) + return transform(*data, lazy=lazy) if isinstance(transform, LazyTrait) else transform(*data) - return transform(data, lazy=lazy) if lazy_tx else transform(data) + return transform(data, lazy=lazy) if isinstance(transform, LazyTrait) else transform(data) def apply_transform( @@ -131,6 +112,7 @@ def apply_transform( map_items: bool = True, unpack_items: bool = False, lazy: bool | None = False, + lazy_strategy: str = "in_order", overrides: dict | None = None, logger_name: str | None = None, ) -> list[ReturnType] | ReturnType: @@ -148,6 +130,9 @@ def apply_transform( if `data` is a list or tuple. Defaults to True. unpack_items: whether to unpack parameters using `*`. Defaults to False. lazy: whether to execute in lazy mode or not. See ``Compose`` for more information about lazy resampling. + lazy_strategy: this field controls how execution occurs when processing data lazily. Permitted + options are "in_order", "out_of_order". Please see `Compose`_ for more details of what these + options mean. In general, you should not need to change this from its default. overrides: optional overrides to apply to transform parameters. This parameter is ignored unless transforms are being executed lazily. @@ -159,8 +144,8 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items, lazy, overrides) for item in data] - return _apply_transform(transform, data, unpack_items, lazy, overrides, logger_name) + return [_apply_transform(transform, item, unpack_items, lazy, lazy_strategy, overrides) for item in data] + return _apply_transform(transform, data, unpack_items, lazy, lazy_strategy, overrides, logger_name) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint # appears where the exception was raised. @@ -376,7 +361,7 @@ def randomize(self, data: Any) -> None: self._do_transform = self.R.rand() < self.prob -class MapTransform(Transform): +class MapTransform(Transform, MapTrait): """ A subclass of :py:class:`monai.transforms.Transform` with an assumption that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``. @@ -412,6 +397,7 @@ def __new__(cls, *args, **kwargs): return Transform.__new__(cls) def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False) -> None: + super().__init__() self.keys: tuple[Hashable, ...] = ensure_tuple(keys) self.allow_missing_keys = allow_missing_keys if not self.keys: diff --git a/tests/test_compose.py b/tests/test_compose.py index 0055bc93bf..11ccabb6c7 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -21,22 +21,24 @@ import torch from parameterized import parameterized +import monai.transforms as mt from monai.data import DataLoader, Dataset -from monai.transforms import ( - AddChannel, - ApplyPending, - ApplyPendingd, - Compose, - Flip, - NormalizeIntensity, - NormalizeIntensityd, - Rotate, - Rotate90, - Rotated, - Spacing, - Zoom, -) -from monai.transforms.compose import execute_compose + +# from monai.transforms import ( +# AddChannel, +# ApplyPending, +# ApplyPendingd, +# Compose, +# Flip, +# NormalizeIntensity, +# NormalizeIntensityd, +# Rotate, +# Rotate90, +# Rotated, +# Spacing, +# Zoom, +# ) +from monai.transforms.compose import ComposeCompiler, execute_compose from monai.transforms.spatial.dictionary import Flipd, Rotate90d, Spacingd, Zoomd from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -53,7 +55,7 @@ def __call__(self, __unused): class TestCompose(unittest.TestCase): def test_empty_compose(self): - c = Compose() + c = mt.Compose() i = 1 self.assertEqual(c(i), 1) @@ -64,7 +66,7 @@ def a(i): def b(i): return i + "b" - c = Compose([a, b, a, b]) + c = mt.Compose([a, b, a, b]) self.assertEqual(c(""), "abab") def test_dict_compose(self): @@ -82,7 +84,7 @@ def b(d): data = {"a": 0, "b": 0} expected = {"a": 3, "b": 2} - self.assertDictEqual(Compose(transforms)(data), expected) + self.assertDictEqual(mt.Compose(transforms)(data), expected) self.assertDictEqual(execute_compose(data, transforms), expected) def test_list_dict_compose(self): @@ -105,7 +107,7 @@ def c(d): # transform to handle dict data transforms = [a, a, b, c, c] data = {"a": 0, "b": 0, "c": 0} expected = {"a": 2, "b": 1, "c": 2} - value = Compose(transforms)(data) + value = mt.Compose(transforms)(data) for item in value: self.assertDictEqual(item, expected) value = execute_compose(data, transforms) @@ -122,7 +124,7 @@ def b(i, i2): transforms = [a, b, a, b] data = ("", "") expected = ("abab", "a2b2a2b2") - self.assertEqual(Compose(transforms, map_items=False, unpack_items=True)(data), expected) + self.assertEqual(mt.Compose(transforms, map_items=False, unpack_items=True)(data), expected) self.assertEqual(execute_compose(data, transforms, map_items=False, unpack_items=True), expected) def test_list_non_dict_compose_with_unpack(self): @@ -135,7 +137,7 @@ def b(i, i2): transforms = [a, b, a, b] data = [("", ""), ("t", "t")] expected = [("abab", "a2b2a2b2"), ("tabab", "ta2b2a2b2")] - self.assertEqual(Compose(transforms, unpack_items=True)(data), expected) + self.assertEqual(mt.Compose(transforms, unpack_items=True)(data), expected) self.assertEqual(execute_compose(data, transforms, unpack_items=True), expected) def test_list_dict_compose_no_map(self): @@ -159,7 +161,7 @@ def c(d): # transform to handle dict data transforms = [a, a, b, c, c] data = {"a": 0, "b": 0, "c": 0} expected = {"a": 2, "b": 1, "c": 2} - value = Compose(transforms, map_items=False)(data) + value = mt.Compose(transforms, map_items=False)(data) for item in value: self.assertDictEqual(item, expected) value = execute_compose(data, transforms, map_items=False) @@ -177,7 +179,7 @@ def __call__(self, data): self.randomize() return self.rand + data - c = Compose([_Acc(), _Acc()]) + c = mt.Compose([_Acc(), _Acc()]) self.assertNotAlmostEqual(c(0), c(0)) c.set_random_state(123) self.assertAlmostEqual(c(1), 1.61381597) @@ -193,17 +195,17 @@ def randomize(self, foo1, foo2): def __call__(self, data): pass - c = Compose([_RandomClass(), _RandomClass()]) + c = mt.Compose([_RandomClass(), _RandomClass()]) with self.assertWarns(Warning): c.randomize() def test_err_msg(self): - transforms = Compose([abs, AddChannel(), round]) + transforms = mt.Compose([abs, mt.AddChannel(), round]) with self.assertRaisesRegex(Exception, "AddChannel"): transforms(42.1) def test_data_loader(self): - xform_1 = Compose([_RandXform()]) + xform_1 = mt.Compose([_RandXform()]) train_ds = Dataset([1], transform=xform_1) xform_1.set_random_state(123) @@ -227,7 +229,7 @@ def test_data_loader(self): def test_data_loader_2(self): set_determinism(seed=123) - xform_2 = Compose([_RandXform(), _RandXform()]) + xform_2 = mt.Compose([_RandXform(), _RandXform()]) train_ds = Dataset([1], transform=xform_2) out_2 = train_ds[0] @@ -248,25 +250,25 @@ def test_data_loader_2(self): set_determinism(None) def test_flatten_and_len(self): - x = AddChannel() - t1 = Compose([x, x, x, x, Compose([Compose([x, x]), x, x])]) + x = mt.AddChannel() + t1 = mt.Compose([x, x, x, x, mt.Compose([mt.Compose([x, x]), x, x])]) t2 = t1.flatten() for t in t2.transforms: - self.assertNotIsInstance(t, Compose) + self.assertNotIsInstance(t, mt.Compose) # test len self.assertEqual(len(t1), 8) def test_backwards_compatible_imports(self): - from monai.transforms.compose import MapTransform, RandomizableTransform, Transform # noqa: F401 + from monai.transforms.transform import MapTransform, RandomizableTransform, Transform # noqa: F401 TEST_COMPOSE_EXECUTE_TEST_CASES = [ [None, tuple()], - [None, (Rotate(np.pi / 8),)], - [None, (Flip(0), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity())], - [("a",), (Rotated(("a",), np.pi / 8),)], + [None, (mt.Rotate(np.pi / 8),)], + [None, (mt.Flip(0), mt.Flip(1), mt.Rotate90(1), mt.Zoom(0.8), mt.NormalizeIntensity())], + [("a",), (mt.Rotated(("a",), np.pi / 8),)], ] @@ -285,10 +287,10 @@ def data_from_keys(keys): def test_compose_execute_equivalence(self, keys, pipeline): data = self.data_from_keys(keys) - expected = Compose(deepcopy(pipeline))(data) + expected = mt.Compose(deepcopy(pipeline))(data) for cutoff in range(len(pipeline)): - c = Compose(deepcopy(pipeline)) + c = mt.Compose(deepcopy(pipeline)) actual = c(c(data, end=cutoff), start=cutoff) if isinstance(actual, dict): for k in actual.keys(): @@ -309,14 +311,14 @@ def test_compose_execute_bad_start_param(self, keys, pipeline): data = self.data_from_keys(keys) with self.assertRaises(ValueError): - c = Compose(deepcopy(pipeline)) + c = mt.Compose(deepcopy(pipeline)) c(data, start=None) with self.assertRaises(ValueError): execute_compose(data, deepcopy(pipeline), start=None) with self.assertRaises(ValueError): - c = Compose(deepcopy(pipeline)) + c = mt.Compose(deepcopy(pipeline)) c(data, start=-1) with self.assertRaises(ValueError): @@ -327,7 +329,7 @@ def test_compose_execute_negative_range(self, keys, pipeline): data = self.data_from_keys(keys) with self.assertRaises(ValueError): - c = Compose(deepcopy(pipeline)) + c = mt.Compose(deepcopy(pipeline)) c(data, start=2, end=1) with self.assertRaises(ValueError): @@ -338,7 +340,7 @@ def test_compose_execute_bad_end_param(self, keys, pipeline): data = self.data_from_keys(keys) with self.assertRaises(ValueError): - c = Compose(deepcopy(pipeline)) + c = mt.Compose(deepcopy(pipeline)) c(data, end=len(pipeline) + 1) with self.assertRaises(ValueError): @@ -348,7 +350,7 @@ def test_compose_execute_bad_end_param(self, keys, pipeline): def test_compose_execute_empty_range(self, keys, pipeline): data = self.data_from_keys(keys) - c = Compose(deepcopy(pipeline)) + c = mt.Compose(deepcopy(pipeline)) for i in range(len(pipeline)): result = c(data, start=i, end=i) self.assertIs(data, result) @@ -357,14 +359,14 @@ def test_compose_execute_empty_range(self, keys, pipeline): def test_compose_with_logger_name(self, keys, pipeline): data = self.data_from_keys(keys) - c = Compose(deepcopy(pipeline), logger_name="a_logger_name") + c = mt.Compose(deepcopy(pipeline), logger_name="a_logger_name") c(data) TEST_COMPOSE_EXECUTE_LOGGING_TEST_CASES = [ [ None, - (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + (mt.Flip(0), mt.Spacing((1.2, 1.2)), mt.Flip(1), mt.Rotate90(1), mt.Zoom(0.8), mt.NormalizeIntensity()), False, ( "INFO - Apply pending transforms - lazy: False, pending: 0, " @@ -384,12 +386,12 @@ def test_compose_with_logger_name(self, keys, pipeline): [ None, ( - Flip(0, lazy=True), - Spacing((1.2, 1.2), lazy=True), - Flip(1, lazy=True), - Rotate90(1), - Zoom(0.8, lazy=True), - NormalizeIntensity(), + mt.Flip(0, lazy=True), + mt.Spacing((1.2, 1.2), lazy=True), + mt.Flip(1, lazy=True), + mt.Rotate90(1), + mt.Zoom(0.8, lazy=True), + mt.NormalizeIntensity(), ), None, ( @@ -411,7 +413,7 @@ def test_compose_with_logger_name(self, keys, pipeline): ], [ None, - (Flip(0), Spacing((1.2, 1.2)), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + (mt.Flip(0), mt.Spacing((1.2, 1.2)), mt.Flip(1), mt.Rotate90(1), mt.Zoom(0.8), mt.NormalizeIntensity()), True, ( "INFO - Accumulate pending transforms - lazy: True, pending: 0, " @@ -431,7 +433,12 @@ def test_compose_with_logger_name(self, keys, pipeline): ], [ ("a", "b"), - (Flipd(("a", "b"), 0), Spacingd(("a", "b"), 1.2), Rotate90d(("a", "b"), 1), NormalizeIntensityd(("a",))), + ( + mt.Flipd(("a", "b"), 0), + mt.Spacingd(("a", "b"), 1.2), + mt.Rotate90d(("a", "b"), 1), + mt.NormalizeIntensityd(("a",)), + ), True, ( "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " @@ -455,10 +462,10 @@ def test_compose_with_logger_name(self, keys, pipeline): [ ("a", "b"), ( - Flipd(keys="a", spatial_axis=0), - Rotate90d(keys="b", k=1, allow_missing_keys=True), - Zoomd(keys=("a", "b"), zoom=0.8, allow_missing_keys=True), - Spacingd(keys="a", pixdim=1.2), + mt.Flipd(keys="a", spatial_axis=0), + mt.Rotate90d(keys="b", k=1, allow_missing_keys=True), + mt.Zoomd(keys=("a", "b"), zoom=0.8, allow_missing_keys=True), + mt.Spacingd(keys="a", pixdim=1.2), ), True, ( @@ -478,7 +485,15 @@ def test_compose_with_logger_name(self, keys, pipeline): ], [ None, - (Flip(0), Spacing((1.2, 1.2)), Flip(1), ApplyPending(), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + ( + mt.Flip(0), + mt.Spacing((1.2, 1.2)), + mt.Flip(1), + mt.ApplyPending(), + mt.Rotate90(1), + mt.Zoom(0.8), + mt.NormalizeIntensity(), + ), False, ( "INFO - Apply pending transforms - lazy: False, pending: 0, " @@ -499,7 +514,15 @@ def test_compose_with_logger_name(self, keys, pipeline): ], [ None, - (Flip(0), Spacing((1.2, 1.2)), Flip(1), ApplyPending(), Rotate90(1), Zoom(0.8), NormalizeIntensity()), + ( + mt.Flip(0), + mt.Spacing((1.2, 1.2)), + mt.Flip(1), + mt.ApplyPending(), + mt.Rotate90(1), + mt.Zoom(0.8), + mt.NormalizeIntensity(), + ), True, ( "INFO - Accumulate pending transforms - lazy: True, pending: 0, " @@ -523,11 +546,11 @@ def test_compose_with_logger_name(self, keys, pipeline): [ ("a", "b"), ( - Flipd(keys="a", spatial_axis=0), - Rotate90d(keys="b", k=1, allow_missing_keys=True), - ApplyPendingd(keys=("a", "b")), - Zoomd(keys=("a", "b"), zoom=0.8, allow_missing_keys=True), - Spacingd(keys="a", pixdim=1.2), + mt.Flipd(keys="a", spatial_axis=0), + mt.Rotate90d(keys="b", k=1, allow_missing_keys=True), + mt.ApplyPendingd(keys=("a", "b")), + mt.Zoomd(keys=("a", "b"), zoom=0.8, allow_missing_keys=True), + mt.Spacingd(keys="a", pixdim=1.2), ), True, ( @@ -578,7 +601,7 @@ def test_compose_with_logging(self, keys, pipeline, lazy, expected): logger.addHandler(handler) data = self.data_from_keys(keys) - c = Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") + c = mt.Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") c(data) handler.flush() @@ -630,10 +653,10 @@ def data_from_keys(keys): data[k] = torch.unsqueeze(torch.tensor(np.arange(24 * 32)).reshape(24, 32) + i_k * 768, dim=0) return data - expected = Compose(pipeline, **flags)(data) + expected = mt.Compose(pipeline, **flags)(data) for cutoff in range(len(pipeline)): - c = Compose(deepcopy(pipeline), **flags) + c = mt.Compose(deepcopy(pipeline), **flags) actual = c(c(data, end=cutoff), start=cutoff) if isinstance(actual, dict): for k in actual.keys(): @@ -650,16 +673,134 @@ def data_from_keys(keys): self.assertTrue(expected, actual) -TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES = [[(Flip(0), Flip(1), Rotate90(1), Zoom(0.8), NormalizeIntensity())]] +TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES = [ + [(mt.Flip(0), mt.Flip(1), mt.Rotate90(1), mt.Zoom(0.8), mt.NormalizeIntensity())] +] class TestLazyComposePipelineFixes(unittest.TestCase): @parameterized.expand(TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES) def test_lazy_compose_pipeline_fixes(self, pipeline): data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) - c = Compose(deepcopy(pipeline), lazy=True) + c = mt.Compose(deepcopy(pipeline), lazy=True) _ = c(data) +class TNonLazy(mt.Transform): + def __init__(self, tag): + self.tag = tag + + def __call__(self, data): + return data + + +class TLazy(mt.LazyTransform): + def __init__(self, tag, lazy): + super().__init__(lazy) + self.tag = tag + + def __call__(self, data): + return data + + +class TApplyPending(mt.ApplyPending): + def __init__(self, tag): + self.tag = tag + + +TRANSFORM_REORDERING_TEST_CASES = [ + ( + [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["a", "lb", "lc", "ad", "f", "le"], + ["a", "lb", "lc", "ad", "f", "le"], + ), + ( + [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", False), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["a", "lb", "lc", "ad", "le", "f"], + ["a", "lb", "lc", "ad", "f", "le"], + ), + ( + [TLazy("la", True), TNonLazy("b"), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["b", "la", "lc", "ad", "f", "le"], + ["b", "la", "lc", "ad", "f", "le"], + ), + ( + [TLazy("la", False), TNonLazy("b"), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["la", "b", "lc", "ad", "f", "le"], + ["b", "la", "lc", "ad", "f", "le"], + ), + ( + [TLazy("la", True), TNonLazy("b"), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["b", "la", "lc", "ad", "f", "le"], + ["b", "la", "lc", "ad", "f", "le"], + ), + ( + [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", False), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["a", "lb", "lc", "ad", "le", "f"], + ["a", "lb", "lc", "ad", "f", "le"], + ), + ( + [TLazy("la", True), TLazy("lb", True), TNonLazy("c"), TApplyPending("ad"), TApplyPending("ae"), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["c", "la", "lb", "ad", "ae", "f"], + ["c", "la", "lb", "ad", "ae", "f"], + ), + ( + [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], + {"reorder": "lazy_last"}, + ["a", "lb", "lc", "ad", "f", "le"], + ["a", "lb", "lc", "ad", "f", "le"], + ), + ( + [ + TNonLazy("a"), + TLazy("lb", True), + TLazy("lc", True), + TApplyPending("ad"), + TLazy("le", True), + TApplyPending("af"), + TApplyPending("ag"), + ], + {"reorder": "lazy_last"}, + ["a", "lb", "lc", "ad", "le", "af", "ag"], + ["a", "lb", "lc", "ad", "le", "af", "ag"], + ), + ( + [TLazy("la", True), TLazy("lb", True), TNonLazy("c"), TLazy("ld", True)], + {"reorder": "lazy_last"}, + ["c", "la", "lb", "ld"], + ["c", "la", "lb", "ld"], + ), + ( + [TLazy("la", True), TLazy("lb", False), TNonLazy("c"), TLazy("ld", True)], + {"reorder": "lazy_last"}, + ["lb", "c", "la", "ld"], + ["c", "la", "lb", "ld"], + ), +] + + +class TestTransformReordering(unittest.TestCase): + @parameterized.expand(TRANSFORM_REORDERING_TEST_CASES) + def test_transform_reordering_test_cases(self, transforms, options, lazy_enabled_expected, lazy_on_expected): + with self.subTest("enable lazy"): + c = ComposeCompiler()(transforms, lazy=None, options={"reorder": "lazy_last"}) + reordered = [transforms[i] for i in c["indices"]] + actual = [t.tag for t in reordered] + self.assertListEqual(actual, lazy_enabled_expected) + + with self.subTest("force lazy"): + c = ComposeCompiler()(transforms, lazy=True, options={"reorder": "lazy_last"}) + reordered = [transforms[i] for i in c["indices"]] + actual = [t.tag for t in reordered] + self.assertListEqual(actual, lazy_on_expected) + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 8dbf76b6a1..41a0d56c0d 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -62,7 +62,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, mt.Orientationd(keys=["img", "seg"], axcodes="ARS"), mt.RandRotate90d(keys=["img", "seg"], prob=1.0, spatial_axes=(1, 2)), mt.ScaleIntensityd(keys="img"), - mt.IdentityD(keys=["seg"]), + mt.ApplyPendingd(keys=["seg"]), mt.RandCropByPosNegLabeld( keys=["img", "seg"], label_key="seg", spatial_size=[76, 82, 80], pos=1, neg=1, num_samples=4 ), From d2fa52146884348bdb9fd8564f54d8b572ebd56e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 09:05:57 +0000 Subject: [PATCH 089/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/compose.py | 1 - tests/test_compose.py | 1 - 2 files changed, 2 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index f69cc71938..8fa29c9c1e 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -24,7 +24,6 @@ import monai from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor -from monai.data.meta_tensor import MetaTensor from monai.transforms.inverse import InvertibleTransform from monai.transforms.lazy.array import ApplyPending from monai.transforms.lazy.dictionary import ApplyPendingd diff --git a/tests/test_compose.py b/tests/test_compose.py index 11ccabb6c7..14110f2c6d 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -39,7 +39,6 @@ # Zoom, # ) from monai.transforms.compose import ComposeCompiler, execute_compose -from monai.transforms.spatial.dictionary import Flipd, Rotate90d, Spacingd, Zoomd from monai.transforms.transform import Randomizable from monai.utils import set_determinism From eb641c6006637b70dc6bd87671cf563c97a81e4b Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 2 May 2023 10:10:09 +0100 Subject: [PATCH 090/175] Tidied up __all__ issues caused by refactor Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 2 +- monai/transforms/lazy/functional.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 99576ee22f..48100d0964 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -20,7 +20,7 @@ from monai.transforms.lazy.functional import apply_pending from monai.transforms.traits import LazyTrait, MapTrait -__all__ = ["apply_pending_transforms"] +__all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order", "apply_pending_transforms_out_of_order"] def _log_pending_info( diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 2b351f27ca..e989716d04 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -33,7 +33,7 @@ # from monai.transforms.traits import MapTrait from monai.utils import LazyAttr, look_up_option -__all__ = ["apply_pending", "apply_pending_transforms"] +__all__ = ["apply_pending"] __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} From 29b37bf5debe9cb2e2f4ead35a1b6a1fca565718 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 2 May 2023 20:23:35 +0100 Subject: [PATCH 091/175] Removed can_invert flag from compose compiler policy Added invert_disabled flag that can be interrogated to determine if inversion is possible and why Signed-off-by: Ben Murray --- monai/transforms/compose.py | 35 ++++++++++++++++++++++++----- monai/transforms/inverse.py | 17 +++++++------- monai/transforms/lazy/dictionary.py | 7 +++--- monai/transforms/lazy/executors.py | 8 +++++-- monai/transforms/lazy/functional.py | 1 - monai/transforms/traits.py | 12 +--------- monai/transforms/transform.py | 7 +++--- monai/utils/enums.py | 1 + tests/test_compose.py | 1 + 9 files changed, 54 insertions(+), 35 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 8fa29c9c1e..4e457f4e77 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -229,11 +229,11 @@ def reorder_lazy_last_nosync(cls, *, transforms: list, **_): Returns: """ - return cls.generate_policy({"can_invert": False, "lazy_policy": "out_of_order"}) + return cls.generate_policy({"lazy_policy": "out_of_order"}) @staticmethod def generate_policy(overrides: dict | None = None): - default_policy = {"indices": None, "transforms": None, "can_invert": True, "lazy_policy": "in_order"} + default_policy = {"indices": None, "transforms": None, "lazy_policy": "in_order"} if overrides is not None: for k, v in overrides.items(): default_policy[k] = v @@ -485,7 +485,7 @@ def __len__(self): def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): lazy_ = self.lazy if lazy is None else lazy - policy = ComposeCompiler()(self.transforms, lazy_) + policy = ComposeCompiler()(self.transforms, lazy_, self.options) lazy_strategy = policy["lazy_policy"] indices = policy["indices"] @@ -525,9 +525,8 @@ def inverse(self, data): policy = ComposeCompiler().generate_policy() indices = policy["indices"] - can_invert = policy["can_invert"] - if can_invert is False: - raise ValueError("'inverse' is not supported with options {self.options}") + + self._raise_if_not_invertible(data) data_ = deepcopy(data) @@ -573,6 +572,30 @@ def inverse(self, data): ) return data_ + def _check_invertible(self, data: Any): + invert_disabled_reasons = list() + if isinstance(data, monai.data.MetaTensor): + for op in data.applied_operations: + reasons = op.get(TraceKeys.INVERT_DISABLED, None) + if reasons is not None: + invert_disabled_reasons.extend(reasons) + elif isinstance(data, dict): + for k, d in data.items(): + for op in d.applied_operations: + reasons = op.get(TraceKeys.INVERT_DISABLED, None) + if reasons is not None: + invert_disabled_reasons.extend(reasons) + + if len(invert_disabled_reasons) > 0: + return False, invert_disabled_reasons + + def _raise_if_not_invertible(self, data: Any): + invertible, reasons = self._check_invertible(data) + if invertible is False: + raise RuntimeError( + "Unable to run inverse on 'data' for the following reasons: " + f"{', '.join(reasons)}") + class OneOf(Compose): """ diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 9f3db86b89..145a2c9b79 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -223,17 +223,16 @@ def track_transform_meta( if out_obj.pending_operations: transform_name = info.get(TraceKeys.CLASS_NAME, "") if isinstance(info, dict) else "" msg = ( - f"Applying transform {transform_name} to a MetaTensor with pending operations " - "is not supported (as this eventually changes the ordering of applied_operations when the pending " - f"operations are executed). Please clear the pending operations before transform {transform_name}." - f"\nPending operations: {[x.get(TraceKeys.CLASS_NAME) for x in out_obj.pending_operations]}." + f"Transform {transform_name} has been applied to a MetaTensor with pending operations: " + f"{[x.get(TraceKeys.CLASS_NAME) for x in out_obj.pending_operations]}" ) + if key is not None: + msg += f" for key {key}" + pend = out_obj.pending_operations[-1] - if not isinstance(pend.get(TraceKeys.EXTRA_INFO), dict): - pend[TraceKeys.EXTRA_INFO] = dict(pend.get(TraceKeys.EXTRA_INFO, {})) - if not isinstance(info.get(TraceKeys.EXTRA_INFO), dict): - info[TraceKeys.EXTRA_INFO] = dict(info.get(TraceKeys.EXTRA_INFO, {})) - info[TraceKeys.EXTRA_INFO]["warn"] = pend[TraceKeys.EXTRA_INFO]["warn"] = msg + reasons = pend.get(TraceKeys.INVERT_DISABLED, list()) + reasons.append(msg) + info[TraceKeys.INVERT_DISABLED] = reasons out_obj.push_applied_operation(info) if isinstance(data, Mapping): if not isinstance(data, dict): diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index cc98026f1b..1aa71b4133 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -13,12 +13,13 @@ from __future__ import annotations from monai.config import KeysCollection -from monai.transforms.traits import InvertibleTrait, MapTrait +from monai.transforms.traits import InvertibleTrait +from monai.transforms.transform import MapTransform __all__ = ["ApplyPendingd", "ApplyPendingD", "ApplyPendingDict"] -class ApplyPendingd(InvertibleTrait, MapTrait): +class ApplyPendingd(InvertibleTrait, MapTransform): """ ApplyPendingd can be inserted into a pipeline that is being executed lazily in order to ensure resampling happens before the next transform. It doesn't do anything itself, @@ -30,7 +31,7 @@ class ApplyPendingd(InvertibleTrait, MapTrait): keys: the keys for tensors that should have their pending transforms executed """ - def __init__(self, keys: KeysCollection | None): + def __init__(self, keys: KeysCollection): self.keys = keys def __call__(self, data): diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 48100d0964..bc27ab8b9d 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -18,7 +18,9 @@ from monai.transforms.lazy.array import ApplyPending from monai.transforms.lazy.dictionary import ApplyPendingd from monai.transforms.lazy.functional import apply_pending -from monai.transforms.traits import LazyTrait, MapTrait +from monai.transforms.traits import LazyTrait +from monai.transforms.transform import MapTransform + __all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order", "apply_pending_transforms_out_of_order"] @@ -38,7 +40,7 @@ def _log_pending_info( else: tlazy = ", transform is not lazy" - if isinstance(transform, MapTrait): + if isinstance(transform, MapTransform): for k in transform.keys: if k in data: pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 @@ -200,3 +202,5 @@ def apply_pending_transforms_out_of_order( return apply_pending_transforms(data, None, overrides, logger_name) _log_pending_info(transform, data, "Accumulate pending transforms", lazy, logger_name) + + return data diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index e989716d04..9db365fae5 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -30,7 +30,6 @@ resample, ) -# from monai.transforms.traits import MapTrait from monai.utils import LazyAttr, look_up_option __all__ = ["apply_pending"] diff --git a/monai/transforms/traits.py b/monai/transforms/traits.py index 013c52aefb..8ba39f8667 100644 --- a/monai/transforms/traits.py +++ b/monai/transforms/traits.py @@ -14,7 +14,7 @@ from __future__ import annotations -__all__ = ["LazyTrait", "InvertibleTrait", "MapTrait", "RandomizableTrait", "MultiSampleTrait", "ThreadUnsafe"] +__all__ = ["LazyTrait", "InvertibleTrait", "RandomizableTrait", "MultiSampleTrait", "ThreadUnsafe"] from typing import Any @@ -57,16 +57,6 @@ def inverse(self, data: Any) -> Any: raise NotImplementedError() -class MapTrait: - """ - An interface to indicate that the transform has the capability to execute on dictionaries - of tensors rather than individual tensors. - """ - - def __init__(self): - self.keys = None - - class RandomizableTrait: """ An interface to indicate that the transform has the capability to perform diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index bc33532953..f08e1e6f93 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -25,8 +25,8 @@ from monai import config, transforms from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor -from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order -from monai.transforms.traits import LazyTrait, MapTrait, RandomizableTrait, ThreadUnsafe +# from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order +from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first from monai.utils.enums import TransformBackends from monai.utils.misc import MONAIEnvVars @@ -92,6 +92,7 @@ def _apply_transform( Returns: ReturnType: The return type of `transform`. """ + from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order if lazy_strategy == "in_order": data = apply_pending_transforms_in_order(transform, data, lazy, overrides, logger_name) @@ -361,7 +362,7 @@ def randomize(self, data: Any) -> None: self._do_transform = self.R.rand() < self.prob -class MapTransform(Transform, MapTrait): +class MapTransform(Transform): """ A subclass of :py:class:`monai.transforms.Transform` with an assumption that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``. diff --git a/monai/utils/enums.py b/monai/utils/enums.py index be546a903b..863a1e1557 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -316,6 +316,7 @@ class TraceKeys(StrEnum): KEY_SUFFIX: str = "_transforms" NONE: str = "none" TRACING: str = "tracing" + INVERT_DISABLED: str = "invert_disabled" LAZY: str = "lazy" diff --git a/tests/test_compose.py b/tests/test_compose.py index 14110f2c6d..cf8e825941 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -704,6 +704,7 @@ def __call__(self, data): class TApplyPending(mt.ApplyPending): def __init__(self, tag): + super().__init__() self.tag = tag From 3194980b9142f4c38f35b64b7ab6a024c5ada2b5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 3 May 2023 09:25:00 +0100 Subject: [PATCH 092/175] Moved to use of a TraceKey.STATUS flag. This can be used to track statuses such as apply_operation being pushed while there are pending operations Signed-off-by: Ben Murray --- monai/transforms/compose.py | 36 ++++++++++++------------------------ monai/transforms/inverse.py | 18 ++++++++++++++---- monai/transforms/utils.py | 30 +++++++++++++++++++++++++++--- monai/utils/__init__.py | 1 + monai/utils/enums.py | 9 ++++++++- 5 files changed, 62 insertions(+), 32 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 4e457f4e77..9b1ccf8016 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -39,7 +39,8 @@ Transform, apply_transform, ) -from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed +from monai.transforms.utils import is_tensor_invertible +from monai.utils import MAX_SEED, TraceKeys, TraceStatusKeys, ensure_tuple, get_seed logger = get_logger(__name__) @@ -526,7 +527,7 @@ def inverse(self, data): indices = policy["indices"] - self._raise_if_not_invertible(data) + self._raise_if_tensor_is_not_invertible(data) data_ = deepcopy(data) @@ -572,29 +573,16 @@ def inverse(self, data): ) return data_ - def _check_invertible(self, data: Any): - invert_disabled_reasons = list() - if isinstance(data, monai.data.MetaTensor): - for op in data.applied_operations: - reasons = op.get(TraceKeys.INVERT_DISABLED, None) - if reasons is not None: - invert_disabled_reasons.extend(reasons) - elif isinstance(data, dict): - for k, d in data.items(): - for op in d.applied_operations: - reasons = op.get(TraceKeys.INVERT_DISABLED, None) - if reasons is not None: - invert_disabled_reasons.extend(reasons) - - if len(invert_disabled_reasons) > 0: - return False, invert_disabled_reasons - - def _raise_if_not_invertible(self, data: Any): - invertible, reasons = self._check_invertible(data) + @staticmethod + def _raise_if_tensor_is_not_invertible(data: Any): + invertible, reasons = is_tensor_invertible(data) + if invertible is False: - raise RuntimeError( - "Unable to run inverse on 'data' for the following reasons: " - f"{', '.join(reasons)}") + if reasons is not None: + reason_text = '\n'.join(reasons) + raise RuntimeError(f"Unable to run inverse on 'data' for the following reasons:\n{reason_text}") + else: + raise RuntimeError(f"Unable to run inverse on 'data'; no reason logged in trace data") class OneOf(Compose): diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 145a2c9b79..5defa6c167 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -25,7 +25,15 @@ from monai.data.utils import to_affine_nd from monai.transforms.traits import InvertibleTrait, LazyTrait from monai.transforms.transform import Transform -from monai.utils import LazyAttr, MetaKeys, TraceKeys, convert_to_dst_type, convert_to_numpy, convert_to_tensor +from monai.utils import ( + LazyAttr, + MetaKeys, + TraceKeys, + TraceStatusKeys, + convert_to_dst_type, + convert_to_numpy, + convert_to_tensor, +) __all__ = ["TraceableTransform", "InvertibleTransform"] @@ -230,9 +238,11 @@ def track_transform_meta( msg += f" for key {key}" pend = out_obj.pending_operations[-1] - reasons = pend.get(TraceKeys.INVERT_DISABLED, list()) - reasons.append(msg) - info[TraceKeys.INVERT_DISABLED] = reasons + statuses = pend.get(TraceKeys.STATUSES, dict()) + messages = statuses.get(TraceStatusKeys.PENDING_DURING_APPLY, list()) + messages.append(msg) + statuses[TraceStatusKeys.PENDING_DURING_APPLY] = messages + info[TraceKeys.STATUSES] = statuses out_obj.push_applied_operation(info) if isinstance(data, Mapping): if not isinstance(data, dict): diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 6dc75141af..50cd16aced 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -28,7 +28,7 @@ from monai.config.type_definitions import NdarrayOrTensor, NdarrayTensor from monai.networks.layers import GaussianFilter from monai.networks.utils import meshgrid_ij -from monai.transforms.compose import Compose, OneOf +# from monai.transforms.compose import Compose, OneOf from monai.transforms.transform import MapTransform, Transform, apply_transform from monai.transforms.utils_pytorch_numpy_unification import ( any_np_pt, @@ -52,6 +52,7 @@ PytorchPadMode, SplineMode, TraceKeys, + TraceStatusKeys, ensure_tuple, ensure_tuple_rep, ensure_tuple_size, @@ -121,6 +122,7 @@ "sync_meta_info", "reset_ops_id", "resolves_modes", + "is_tensor_invertible", ] @@ -1324,7 +1326,7 @@ def map_spatial_axes( @contextmanager -def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransform] | tuple[Compose]): +def allow_missing_keys_mode(transform: MapTransform | "Compose" | tuple[MapTransform] | tuple["Compose"]): """Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states. Args: @@ -1340,6 +1342,8 @@ def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransfo with allow_missing_keys_mode(t): _ = t(data) # OK! """ + from monai.transforms.compose import Compose + # If given a sequence of transforms, Compose them to get a single list if issequenceiterable(transform): transform = Compose(transform) @@ -1536,7 +1540,7 @@ def inv_shift_fourier(k: NdarrayOrTensor, spatial_dims: int, n_dims: int | None return out -def get_number_image_type_conversions(transform: Compose, test_data: Any, key: Hashable | None = None) -> int: +def get_number_image_type_conversions(transform: "Compose", test_data: Any, key: Hashable | None = None) -> int: """ Get the number of times that the data need to be converted (e.g., numpy to torch). Conversions between different devices are also counted (e.g., CPU to GPU). @@ -1546,6 +1550,7 @@ def get_number_image_type_conversions(transform: Compose, test_data: Any, key: H test_data: data to be used to count the number of conversions key: if using dictionary transforms, this key will be used to check the number of conversions. """ + from monai.transforms.compose import OneOf def _get_data(obj, key): return obj if key is None else obj[key] @@ -1967,5 +1972,24 @@ def resolves_modes( return backend, _interp_mode, _padding_mode, _kwargs +def is_tensor_invertible(data: Any): + invert_disabled_reasons = list() + if isinstance(data, monai.data.MetaTensor): + for op in data.applied_operations: + if TraceKeys.STATUSES in op: + if TraceStatusKeys.PENDING_DURING_APPLY in op[TraceKeys.STATUSES]: + reason = op[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] + invert_disabled_reasons.extend(["PENDING DURING APPLY"] if reason is None else reason) + elif isinstance(data, dict): + for k, d in data.items(): + _, reasons = is_tensor_invertible(d) + if reasons is not None: + invert_disabled_reasons.extend(reasons) + + if len(invert_disabled_reasons) > 0: + return False, invert_disabled_reasons + return True, None + + if __name__ == "__main__": print_transform_backends() diff --git a/monai/utils/__init__.py b/monai/utils/__init__.py index 834e4866d7..4a8e439f0a 100644 --- a/monai/utils/__init__.py +++ b/monai/utils/__init__.py @@ -55,6 +55,7 @@ SplineMode, StrEnum, TraceKeys, + TraceStatusKeys, TransformBackends, UpsampleMode, Weight, diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 863a1e1557..25c747ed90 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -36,6 +36,7 @@ "SkipMode", "Method", "TraceKeys", + "TraceStatusKeys", "CommonKeys", "GanKeys", "PostFix", @@ -316,10 +317,16 @@ class TraceKeys(StrEnum): KEY_SUFFIX: str = "_transforms" NONE: str = "none" TRACING: str = "tracing" - INVERT_DISABLED: str = "invert_disabled" + STATUSES: str = "statuses" LAZY: str = "lazy" +class TraceStatusKeys(StrEnum): + """Enumerable status keys for the TraceKeys.STATUS flag""" + + PENDING_DURING_APPLY = "pending_during_apply" + + class CommonKeys(StrEnum): """ A set of common keys for dictionary based supervised training process. From 5ff516d21855de2a9d95cd02aa0c97a4e3db450e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 May 2023 08:27:29 +0000 Subject: [PATCH 093/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/compose.py | 4 ++-- monai/transforms/utils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 9b1ccf8016..347277d506 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -40,7 +40,7 @@ apply_transform, ) from monai.transforms.utils import is_tensor_invertible -from monai.utils import MAX_SEED, TraceKeys, TraceStatusKeys, ensure_tuple, get_seed +from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed logger = get_logger(__name__) @@ -582,7 +582,7 @@ def _raise_if_tensor_is_not_invertible(data: Any): reason_text = '\n'.join(reasons) raise RuntimeError(f"Unable to run inverse on 'data' for the following reasons:\n{reason_text}") else: - raise RuntimeError(f"Unable to run inverse on 'data'; no reason logged in trace data") + raise RuntimeError("Unable to run inverse on 'data'; no reason logged in trace data") class OneOf(Compose): diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 50cd16aced..1fd1791239 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -1326,7 +1326,7 @@ def map_spatial_axes( @contextmanager -def allow_missing_keys_mode(transform: MapTransform | "Compose" | tuple[MapTransform] | tuple["Compose"]): +def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransform] | tuple[Compose]): """Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states. Args: @@ -1540,7 +1540,7 @@ def inv_shift_fourier(k: NdarrayOrTensor, spatial_dims: int, n_dims: int | None return out -def get_number_image_type_conversions(transform: "Compose", test_data: Any, key: Hashable | None = None) -> int: +def get_number_image_type_conversions(transform: Compose, test_data: Any, key: Hashable | None = None) -> int: """ Get the number of times that the data need to be converted (e.g., numpy to torch). Conversions between different devices are also counted (e.g., CPU to GPU). From eebb3de1936fcdc975c39d914d49574fec32587b Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 3 May 2023 09:49:00 +0100 Subject: [PATCH 094/175] Formatting autofixes Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- monai/transforms/lazy/executors.py | 1 - monai/transforms/lazy/functional.py | 1 - monai/transforms/transform.py | 1 + monai/transforms/utils.py | 1 + 5 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 9b1ccf8016..709732298e 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -579,7 +579,7 @@ def _raise_if_tensor_is_not_invertible(data: Any): if invertible is False: if reasons is not None: - reason_text = '\n'.join(reasons) + reason_text = "\n".join(reasons) raise RuntimeError(f"Unable to run inverse on 'data' for the following reasons:\n{reason_text}") else: raise RuntimeError(f"Unable to run inverse on 'data'; no reason logged in trace data") diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index bc27ab8b9d..3a36a31b46 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -21,7 +21,6 @@ from monai.transforms.traits import LazyTrait from monai.transforms.transform import MapTransform - __all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order", "apply_pending_transforms_out_of_order"] diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 9db365fae5..5324fa7058 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -29,7 +29,6 @@ kwargs_from_pending, resample, ) - from monai.utils import LazyAttr, look_up_option __all__ = ["apply_pending"] diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index f08e1e6f93..4051470997 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -25,6 +25,7 @@ from monai import config, transforms from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor + # from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 50cd16aced..af4db699aa 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -28,6 +28,7 @@ from monai.config.type_definitions import NdarrayOrTensor, NdarrayTensor from monai.networks.layers import GaussianFilter from monai.networks.utils import meshgrid_ij + # from monai.transforms.compose import Compose, OneOf from monai.transforms.transform import MapTransform, Transform, apply_transform from monai.transforms.utils_pytorch_numpy_unification import ( From e39e0a3171ed61ed3cca2bec30caf82f9773f779 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 3 May 2023 13:11:14 +0100 Subject: [PATCH 095/175] Lazy {reorder: lazy_last_nosync} now passes test_integration_lazy_samples Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 + monai/transforms/croppad/array.py | 52 +++++++++---------- monai/transforms/croppad/dictionary.py | 52 +++++++++---------- monai/transforms/lazy/executors.py | 71 +++++++++++++++++++++----- monai/transforms/utils.py | 34 +++++++++--- tests/test_integration_lazy_samples.py | 58 +++++++++++++-------- 6 files changed, 177 insertions(+), 92 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index f978f9ac48..3933e6fee8 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -125,6 +125,8 @@ def execute_compose( overrides=overrides, logger_name=logger_name, ) + if isinstance(data, (list, tuple)) and map_items: + data = [apply_pending_transforms(d, None, overrides, logger_name=logger_name) for d in data] data = apply_pending_transforms(data, None, overrides, logger_name=logger_name) return data diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 824bd6210c..25bd1ae4cd 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -808,11 +808,11 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - if lazy is True: - warnings.warn( - "CropForeground cannot currently execute lazily; " "ignoring lazy=True set during initialization" - ) - lazy = False + # if lazy is True: + # warnings.warn( + # "CropForeground cannot currently execute lazily; " "ignoring lazy=True set during initialization" + # ) + # lazy = False LazyTransform.__init__(self, lazy) self.select_fn = select_fn self.channel_indices = ensure_tuple(channel_indices) if channel_indices is not None else None @@ -820,7 +820,7 @@ def __init__( self.allow_smaller = allow_smaller self.return_coords = return_coords self.k_divisible = k_divisible - self.padder = Pad(mode=mode, lazy=False, **pad_kwargs) + self.padder = Pad(mode=mode, lazy=lazy, **pad_kwargs) @Crop.lazy.setter # type: ignore def lazy(self, _val: bool): @@ -897,9 +897,9 @@ def __call__( # type: ignore[override] Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't change the channel dim. """ - if lazy is True: - warnings.warn("CropForeground cannot currently execute lazily; ignoring lazy=True") - lazy = False + # if lazy is True: + # warnings.warn("CropForeground cannot currently execute lazily; ignoring lazy=True") + # lazy = False box_start, box_end = self.compute_bounding_box(img) lazy_ = self.lazy if lazy is None else lazy @@ -1073,11 +1073,11 @@ def __init__( allow_smaller: bool = False, lazy: bool = False, ) -> None: - if lazy is True: - warnings.warn( - "RandCropByPosNegLabel cannot currently execute lazily; " "ignoring lazy=True set during initialization" - ) - lazy = False + # if lazy is True: + # warnings.warn( + # "RandCropByPosNegLabel cannot currently execute lazily; " "ignoring lazy=True set during initialization" + # ) + # lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.label = label @@ -1154,9 +1154,9 @@ def __call__( randomize: whether to execute the random operations, default to `True`. """ - if lazy is True: - warnings.warn("RandCropByPosNegLabel cannot currently execute lazily; ignoring lazy=True") - lazy = False + # if lazy is True: + # warnings.warn("RandCropByPosNegLabel cannot currently execute lazily; ignoring lazy=True") + # lazy = False if image is None: image = self.image @@ -1265,12 +1265,12 @@ def __init__( max_samples_per_class: int | None = None, lazy: bool = False, ) -> None: - if lazy is True: - warnings.warn( - "RandCropByLabelClasses cannot currently execute lazily; " - "ignoring lazy=True set during initialization" - ) - lazy = False + # if lazy is True: + # warnings.warn( + # "RandCropByLabelClasses cannot currently execute lazily; " + # "ignoring lazy=True set during initialization" + # ) + # lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.ratios = ratios @@ -1333,9 +1333,9 @@ def __call__( randomize: whether to execute the random operations, default to `True`. """ - if lazy is True: - warnings.warn("RandCropByLabelClasses cannot currently execute lazily; ignoring lazy=True") - lazy = False + # if lazy is True: + # warnings.warn("RandCropByLabelClasses cannot currently execute lazily; ignoring lazy=True") + # lazy = False if image is None: image = self.image diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 734b97d524..ed07c8ffb8 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -722,11 +722,11 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - if lazy is True: - warnings.warn( - "CropForegroundd cannot currently execute lazily; " "ignoring lazy=True set during initialization" - ) - lazy = False + # if lazy is True: + # warnings.warn( + # "CropForegroundd cannot currently execute lazily; " "ignoring lazy=True set during initialization" + # ) + # lazy = False self.source_key = source_key self.start_coord_key = start_coord_key @@ -749,9 +749,9 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: - if lazy is True: - warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") - lazy = False + # if lazy is True: + # warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") + # lazy = False d = dict(data) self.cropper: CropForeground @@ -905,12 +905,12 @@ def __init__( lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - if lazy is True: - warnings.warn( - "RandCropByPosNegLabeld cannot currently execute lazily; " - "ignoring lazy=True set during initialization" - ) - lazy = False + # if lazy is True: + # warnings.warn( + # "RandCropByPosNegLabeld cannot currently execute lazily; " + # "ignoring lazy=True set during initialization" + # ) + # lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key @@ -950,9 +950,9 @@ def lazy(self, value: bool) -> None: def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: - if lazy is True: - warnings.warn("RandCropByPosNegLabeld cannot currently execute lazily; ignoring lazy=True") - lazy = False + # if lazy is True: + # warnings.warn("RandCropByPosNegLabeld cannot currently execute lazily; ignoring lazy=True") + # lazy = False d = dict(data) fg_indices = d.pop(self.fg_indices_key, None) @@ -1069,12 +1069,12 @@ def __init__( lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - if lazy is True: - warnings.warn( - "RandCropByLabelClassesd cannot currently execute lazily; " - "ignoring lazy=True set during initialization" - ) - lazy = False + # if lazy is True: + # warnings.warn( + # "RandCropByLabelClassesd cannot currently execute lazily; " + # "ignoring lazy=True set during initialization" + # ) + # lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key @@ -1109,9 +1109,9 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: - if lazy is True: - warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") - lazy = False + # if lazy is True: + # warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") + # lazy = False d = dict(data) self.randomize(d.get(self.label_key), d.pop(self.indices_key, None), d.get(self.image_key)) # type: ignore diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 3a36a31b46..cec716ae16 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -25,7 +25,13 @@ def _log_pending_info( - transform: Any, data: Any, activity: str, lazy: bool | None = None, logger_name: str | None = None + transform: Any, + data: Any, + activity: str, + *, + lazy: bool | None = None, + key: str | None = None, + logger_name: str | None = None, ): if logger_name is None: return @@ -40,7 +46,8 @@ def _log_pending_info( tlazy = ", transform is not lazy" if isinstance(transform, MapTransform): - for k in transform.keys: + transform_keys = transform.keys if key is None else (key,) + for k in transform_keys: if k in data: pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 logger.info( @@ -49,9 +56,15 @@ def _log_pending_info( ) else: pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 - logger.info( - f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) + if key is None: + logger.info( + f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) + else: + logger.info( + f"{activity} - lazy mode: {lazy}, key: '{key}', " + f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + ) def _log_applied_info(data: Any, key=None, logger_name: str | None = None): @@ -63,6 +76,33 @@ def _log_applied_info(data: Any, key=None, logger_name: str | None = None): logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") +def patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overrides, logger_name): + from monai.transforms.croppad.array import CropForeground, RandCropByLabelClasses, RandCropByPosNegLabel + from monai.transforms.croppad.dictionary import CropForegroundd, RandCropByLabelClassesd, RandCropByPosNegLabeld + + if isinstance(data, dict): + if isinstance(transform, CropForegroundd): + k = transform.source_key + elif isinstance(transform, (RandCropByLabelClassesd, RandCropByPosNegLabeld)): + k = transform.label_key + else: + return data + + if isinstance(data[k], MetaTensor) and data[k].has_pending_operations: + d = dict(data) + k = transform.source_key + _log_pending_info(transform, data, "Apply prior to executing", key=k, lazy=lazy, logger_name=logger_name) + d[k] = apply_pending(data[k], overrides=overrides.get(k, None)) + return d + elif isinstance(data, MetaTensor) and data.has_pending_operations: + if isinstance(transform, CropForeground, RandCropByLabelClasses, RandCropByPosNegLabel): + _log_pending_info(transform, data, "Apply prior to executing", lazy=lazy, logger_name=logger_name) + data = apply_pending(data, overrides) + return data + + return data + + def apply_pending_transforms( data: dict, keys: tuple | None, overrides: dict | None = None, logger_name: str | None = None ): @@ -142,23 +182,26 @@ def apply_pending_transforms_in_order( """ if lazy is False: - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, None, overrides, logger_name) lazy_ = transform.lazy if isinstance(transform, LazyTrait) and lazy is None else lazy if not isinstance(transform, LazyTrait) or lazy_ is False: - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, None, overrides, logger_name) if isinstance(transform, ApplyPendingd): - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, transform.keys, overrides, logger_name) if isinstance(transform, ApplyPending): - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, None, overrides, logger_name) - _log_pending_info(transform, data, "Accumulate pending transforms", lazy, logger_name) + if lazy is not False: + patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overrides, logger_name) + + _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) return data @@ -189,17 +232,17 @@ def apply_pending_transforms_out_of_order( """ if lazy is False: - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, None, overrides, logger_name) if isinstance(transform, ApplyPendingd): - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, transform.keys, overrides, logger_name) if isinstance(transform, ApplyPending): - _log_pending_info(transform, data, "Apply pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, None, overrides, logger_name) - _log_pending_info(transform, data, "Accumulate pending transforms", lazy, logger_name) + _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) return data diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index d663c5817f..5314ce4e9a 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -1327,7 +1327,7 @@ def map_spatial_axes( @contextmanager -def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransform] | tuple[Compose]): +def allow_missing_keys_mode(transform: MapTransform | "Compose" | tuple[MapTransform] | tuple["Compose"]): """Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states. Args: @@ -1974,13 +1974,35 @@ def resolves_modes( def is_tensor_invertible(data: Any): + def check_applied_operations(entry): + if isinstance(entry, list): + results = list() + for sub_entry in entry: + results.extend(check_applied_operations(sub_entry)) + return results + else: + if TraceKeys.STATUSES in entry: + if TraceStatusKeys.PENDING_DURING_APPLY in entry[TraceKeys.STATUSES]: + reason = entry[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] + if reason is None: + return ["Pending operations while applying an operation"] + return reason if isinstance(reason, list) else [reason] + return [] + invert_disabled_reasons = list() - if isinstance(data, monai.data.MetaTensor): + if isinstance(data, (list, tuple)): + for d in data: + _, reasons = is_tensor_invertible(d) + if reasons is not None: + invert_disabled_reasons.extend(reasons) + elif isinstance(data, monai.data.MetaTensor): for op in data.applied_operations: - if TraceKeys.STATUSES in op: - if TraceStatusKeys.PENDING_DURING_APPLY in op[TraceKeys.STATUSES]: - reason = op[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] - invert_disabled_reasons.extend(["PENDING DURING APPLY"] if reason is None else reason) + invert_disabled_reasons.extend(check_applied_operations(op)) + # if op + # if TraceKeys.STATUSES in op: + # if TraceStatusKeys.PENDING_DURING_APPLY in op[TraceKeys.STATUSES]: + # reason = op[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] + # invert_disabled_reasons.extend(["PENDING DURING APPLY"] if reason is None else reason) elif isinstance(data, dict): for k, d in data.items(): _, reasons = is_tensor_invertible(d) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 41a0d56c0d..ac5620dbe8 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -24,6 +24,7 @@ import monai import monai.transforms as mt from monai.data import create_test_image_3d, decollate_batch +from monai.transforms.utils import is_tensor_invertible from monai.utils import set_determinism from tests.utils import HAS_CUPY, DistTestCase, SkipIfBeforePyTorchVersion, skip_if_quick @@ -32,7 +33,9 @@ def _no_op(x): return x -def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, None), num_workers=4, lazy=True): +def run_training_test( + root_dir, device="cuda:0", cachedataset=0, readers=(None, None), num_workers=4, lazy=True, options=None +): print(f"test case: {locals()}") images = sorted(glob(os.path.join(root_dir, "img*.nii.gz"))) segs = sorted(glob(os.path.join(root_dir, "seg*.nii.gz"))) @@ -75,6 +78,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, mt.Lambdad(keys=["img"], func=_no_op), ], lazy=lazy, + options=options, overrides=lazy_kwargs, ) @@ -117,6 +121,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, train_ds, batch_size=1, shuffle=True, num_workers=num_workers, generator=_g, persistent_workers=num_workers > 0 ) all_coords = set() + batch_data = None for epoch in range(5): print("-" * 10) print(f"Epoch {epoch + 1}/5") @@ -151,7 +156,13 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, saver(item) # just testing the saving saver(in_img) saver(in_seg) - [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error + invertible, reasons = is_tensor_invertible(batch_data) + if options == {"reorder": "lazy_last_nosync"}: + assert invertible is False, f"the output of this pipeline with options {options} should not be invertible" + else: + assert invertible is True, f"the output of this pipeline with options {options} should be invertible" + inverted = [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error + return ops @@ -186,27 +197,34 @@ def train_and_infer(self, idx=0): elif idx == 2: _readers = ("itkreader", "nibabelreader") _w = 0 - results = run_training_test( - self.data_dir, device=self.device, cachedataset=idx, readers=_readers, num_workers=_w, lazy=True - ) + results_expected = run_training_test( self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=False ) - self.assertFalse(np.allclose(results, [0])) - self.assertFalse(np.allclose(results_expected, [0])) - np.testing.assert_allclose(results, results_expected) - lazy_files = glob(os.path.join(self.data_dir, "output", "*_True_*.nii.gz")) - regular_files = glob(os.path.join(self.data_dir, "output", "*_False_*.nii.gz")) - diffs = [] - for a, b in zip(sorted(lazy_files), sorted(regular_files)): - img_lazy = mt.LoadImage(image_only=True)(a) - img_regular = mt.LoadImage(image_only=True)(b) - diff = np.size(img_lazy) - np.sum(np.isclose(img_lazy, img_regular, atol=1e-4)) - diff_rate = diff / np.size(img_lazy) - diffs.append(diff_rate) - np.testing.assert_allclose(diff_rate, 0.0, atol=0.03) - print("volume diff:", diffs) - return results + for options in ({"reorder": "lazy_last_nosync"},): + results = run_training_test( + self.data_dir, + device=self.device, + cachedataset=idx, + readers=_readers, + num_workers=_w, + lazy=True, + options=options, + ) + self.assertFalse(np.allclose(results, [0])) + self.assertFalse(np.allclose(results_expected, [0])) + np.testing.assert_allclose(results, results_expected) + lazy_files = glob(os.path.join(self.data_dir, "output", "*_True_*.nii.gz")) + regular_files = glob(os.path.join(self.data_dir, "output", "*_False_*.nii.gz")) + diffs = [] + for a, b in zip(sorted(lazy_files), sorted(regular_files)): + img_lazy = mt.LoadImage(image_only=True)(a) + img_regular = mt.LoadImage(image_only=True)(b) + diff = np.size(img_lazy) - np.sum(np.isclose(img_lazy, img_regular, atol=1e-4)) + diff_rate = diff / np.size(img_lazy) + diffs.append(diff_rate) + np.testing.assert_allclose(diff_rate, 0.0, atol=0.03) + print("volume diff:", diffs) def test_training(self): for i in range(4): From cf58b9ffca4d70fedf9edbe5d5374b9ac9d12d6e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 May 2023 12:16:53 +0000 Subject: [PATCH 096/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/croppad/dictionary.py | 1 - monai/transforms/utils.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index ed07c8ffb8..385a7bf794 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -17,7 +17,6 @@ from __future__ import annotations -import warnings from collections.abc import Callable, Hashable, Mapping, Sequence from copy import deepcopy from typing import Any diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 5314ce4e9a..f6c0c77fe0 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -1327,7 +1327,7 @@ def map_spatial_axes( @contextmanager -def allow_missing_keys_mode(transform: MapTransform | "Compose" | tuple[MapTransform] | tuple["Compose"]): +def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransform] | tuple[Compose]): """Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states. Args: From 883f8c5794bc2f945387874d1a7fa97273043ecf Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 09:11:52 +0100 Subject: [PATCH 097/175] Fixed broken call to isinstance in lazy executors Made imports of Compose/OneOf local in transforms/utils.py Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 2 +- monai/transforms/utils.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index cec716ae16..bf84e878b5 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -95,7 +95,7 @@ def patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overr d[k] = apply_pending(data[k], overrides=overrides.get(k, None)) return d elif isinstance(data, MetaTensor) and data.has_pending_operations: - if isinstance(transform, CropForeground, RandCropByLabelClasses, RandCropByPosNegLabel): + if isinstance(transform, (CropForeground, RandCropByLabelClasses, RandCropByPosNegLabel)): _log_pending_info(transform, data, "Apply prior to executing", lazy=lazy, logger_name=logger_name) data = apply_pending(data, overrides) return data diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index f6c0c77fe0..623ee4456b 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -18,7 +18,7 @@ from contextlib import contextmanager from functools import lru_cache, wraps from inspect import getmembers, isclass -from typing import Any +from typing import Any, TYPE_CHECKING import numpy as np import torch @@ -68,6 +68,9 @@ from monai.utils.enums import TransformBackends from monai.utils.type_conversion import convert_data_type, convert_to_cupy, convert_to_dst_type, convert_to_tensor +if TYPE_CHECKING: + from monai.transforms.compose import Compose + measure, has_measure = optional_import("skimage.measure", "0.14.2", min_version) morphology, has_morphology = optional_import("skimage.morphology") ndimage, _ = optional_import("scipy.ndimage") From e81f359b47b94f05255479a22257ff432d4cd055 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 09:30:05 +0100 Subject: [PATCH 098/175] Autofix Signed-off-by: Ben Murray --- monai/transforms/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 623ee4456b..7d801aa194 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -18,7 +18,7 @@ from contextlib import contextmanager from functools import lru_cache, wraps from inspect import getmembers, isclass -from typing import Any, TYPE_CHECKING +from typing import TYPE_CHECKING, Any import numpy as np import torch From db1486bfc7be170f1a6517b3194b84e448e696ba Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 09:45:12 +0100 Subject: [PATCH 099/175] Resolving lint issue Signed-off-by: Ben Murray --- monai/transforms/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 7d801aa194..bc765abe04 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -2007,7 +2007,7 @@ def check_applied_operations(entry): # reason = op[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] # invert_disabled_reasons.extend(["PENDING DURING APPLY"] if reason is None else reason) elif isinstance(data, dict): - for k, d in data.items(): + for d in data.values(): _, reasons = is_tensor_invertible(d) if reasons is not None: invert_disabled_reasons.extend(reasons) From b15160722db9fcc47c6d1c67aa2767bba62d7736 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 12:37:01 +0100 Subject: [PATCH 100/175] Missing passing logger_name through to _apply_transform when mapping over sequence: Signed-off-by: Ben Murray --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 4051470997..8648ca6c5a 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -146,7 +146,7 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items, lazy, lazy_strategy, overrides) for item in data] + return [_apply_transform(transform, item, unpack_items, lazy, lazy_strategy, overrides, logger_name) for item in data] return _apply_transform(transform, data, unpack_items, lazy, lazy_strategy, overrides, logger_name) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint From 24286f15a18d8266858de29f9b3fc815c59a19c6 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 13:11:49 +0100 Subject: [PATCH 101/175] Autofix Signed-off-by: Ben Murray --- monai/transforms/transform.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 8648ca6c5a..5f7bb29f30 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -146,7 +146,10 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items, lazy, lazy_strategy, overrides, logger_name) for item in data] + return [ + _apply_transform(transform, item, unpack_items, lazy, lazy_strategy, overrides, logger_name) + for item in data + ] return _apply_transform(transform, data, unpack_items, lazy, lazy_strategy, overrides, logger_name) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint From 43f7bc0a0a84bca9184bc798611d6518c05458fb Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 14:03:48 +0100 Subject: [PATCH 102/175] Added missing lazy=None option from integration tests for lazy Was removed by mistake Signed-off-by: Ben Murray --- tests/test_integration_lazy_samples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index ac5620dbe8..3f335aefd9 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -201,7 +201,7 @@ def train_and_infer(self, idx=0): results_expected = run_training_test( self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=False ) - for options in ({"reorder": "lazy_last_nosync"},): + for options in (None, {"reorder": "lazy_last_nosync"},): results = run_training_test( self.data_dir, device=self.device, From b217dbe7f62912b9905cf16cfb8e9fb7b71a0d50 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 4 May 2023 15:01:03 +0100 Subject: [PATCH 103/175] Autofix Signed-off-by: Ben Murray --- tests/test_integration_lazy_samples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 3f335aefd9..6afb756748 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -201,7 +201,7 @@ def train_and_infer(self, idx=0): results_expected = run_training_test( self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=False ) - for options in (None, {"reorder": "lazy_last_nosync"},): + for options in (None, {"reorder": "lazy_last_nosync"}): results = run_training_test( self.data_dir, device=self.device, From ed1d9523020b16763c8a859516e26bec2c65a630 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 5 May 2023 07:39:36 +0100 Subject: [PATCH 104/175] Fixing lint issues Signed-off-by: Ben Murray --- monai/transforms/compose.py | 25 ++++++++++++++++++++++++- monai/transforms/lazy/dictionary.py | 2 +- monai/transforms/lazy/executors.py | 11 +++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 3933e6fee8..a66b760d27 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -148,7 +148,9 @@ def __init__(self): def __call__(self, transforms, lazy: bool | None, options: dict | None = None): """ - Get a policy object that controls the way `Compose`_ executes a list of transforms. + Get a policy object that controls the way `Compose`_ executes a list of transforms. At present, the user can + only specify a single flag, but this design will be extended in future releases to allow multiple flags to + control different aspects of transform execution. Args: transforms: a list of transforms to be executed @@ -181,6 +183,27 @@ def __call__(self, transforms, lazy: bool | None, options: dict | None = None): @classmethod def reorder_lazy_last(cls, *, transforms: list, lazy: bool | None, **kwargs): + """ + 'reorder_lazy_last` effectively reorders a sequence of transforms so that lazy transforms are grouped together + after non-lazy ones. This operation can significantly change the behaviour of your pipeline and so should only + be used once you are clear about its behaviour. + + Example:: + + transforms = [LoadImage, Flip, GaussianNoise, Rotate90, ApplyPending, Zoom, Rotate] + + # ApplyPending effectively splits the pipeline up into two subranges. No transform can move after or before + # an ApplyPending instance, so we end up with transforms before and after ApplyPending + + sub_ranges = [[LoadImage, Flip, GaussianNoise, Rotate90], [ApplyPending], [Zoom, Rotate]] + + # Each subrange is then sorted so that non-lazy transform stay in their relative order but go before the + # lazy transforms (which also stay in their relative order) + + sub_ranges = [[LoadImage, GaussianNoise, Flip, Rotate90], [Apply + + :: + """ subsections = list() subsection_starts = list() # pass 1: split the transform list into subsections diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 1aa71b4133..7abb0ea026 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -32,7 +32,7 @@ class ApplyPendingd(InvertibleTrait, MapTransform): """ def __init__(self, keys: KeysCollection): - self.keys = keys + super().__init__(keys) def __call__(self, data): if not isinstance(data, dict): diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index bf84e878b5..3a073ca8a5 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -11,9 +11,10 @@ from __future__ import annotations -from typing import Any +from typing import Any, Sequence, Mapping from monai.apps.utils import get_logger +from monai.config import NdarrayOrTensor from monai.data.meta_tensor import MetaTensor from monai.transforms.lazy.array import ApplyPending from monai.transforms.lazy.dictionary import ApplyPendingd @@ -104,7 +105,10 @@ def patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overr def apply_pending_transforms( - data: dict, keys: tuple | None, overrides: dict | None = None, logger_name: str | None = None + data: NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], + keys: tuple | None, + overrides: dict | None = None, + logger_name: str | None = None ): """ apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain @@ -198,8 +202,7 @@ def apply_pending_transforms_in_order( _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) return apply_pending_transforms(data, None, overrides, logger_name) - if lazy is not False: - patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overrides, logger_name) + patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overrides, logger_name) _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) From e7f3a071df697d6459d108949699388d99015387 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 5 May 2023 08:28:26 +0100 Subject: [PATCH 105/175] Autofixes Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 3a073ca8a5..7fea34cf7b 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -11,7 +11,7 @@ from __future__ import annotations -from typing import Any, Sequence, Mapping +from typing import Any, Mapping, Sequence from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor @@ -108,7 +108,7 @@ def apply_pending_transforms( data: NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], keys: tuple | None, overrides: dict | None = None, - logger_name: str | None = None + logger_name: str | None = None, ): """ apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain From 0751c52babd00806e3f64a3806b43392324fec60 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 5 May 2023 08:50:51 +0100 Subject: [PATCH 106/175] lint fix Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index a66b760d27..3371b6febb 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -179,7 +179,7 @@ def __call__(self, transforms, lazy: bool | None, options: dict | None = None): action = option[v] - return action(transforms=transforms, lazy=lazy) + return action(transforms=transforms, lazy=lazy) # type: ignore[operator] @classmethod def reorder_lazy_last(cls, *, transforms: list, lazy: bool | None, **kwargs): From 6114a6742519d07e4c1629005fe07f66dbcb171a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 5 May 2023 12:36:03 +0100 Subject: [PATCH 107/175] started on the lazy resampling topic page Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/source/lazy_resampling.rst diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst new file mode 100644 index 0000000000..a9c5a65bf7 --- /dev/null +++ b/docs/source/lazy_resampling.rst @@ -0,0 +1,102 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +Lazy Resampling +=============== + +.. toctree:: + : maxdepth: 2 + + mb_specification + config_syntax.md + +Introduction +^^^^^^^^^^^^ + +Lazy Resampling is a new feature for MONAI 1.2. This feature is still experimental at this time and it is possible that +behaviour and APIs will change in upcoming releases. + +Lazy resampling is a feature that can be used to improve preprocessing pipelines in the following ways: + * it can improve pipeline execution time + * it can improve pipeline memory usage + * it can improve image and segmentation quality by reducing incidental noise caused by resampling + +How Lazy Resampling changes preprocessing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to understand how lazy resampling changes preprocessing, we'll first discuss standard processing pipeline +behaviour, and then compare it with the way lazy resampling works. + +Traditional resampling pipelines +++++++++++++++++++++++++++++++++ + +With traditional resampling, found both in MONAI and many other preprocessing libraries, you typically define a sequence +of transforms and pass them to a ``Compose`` object, such as `monai.transforms.compose.Compose`_. + +Example:: + + transforms = [ + LoadImaged(keys=["img", "seg"], ...), + EnsureChannelFirstd(keys=["img", "seg"], ...), + Spacingd(keys=["img", "seg"], ...), + Orientationd(keys=["img", "seg"], ...), + RandSpatialCropd(keys=["img", "seg"], ...), + RandRotate90d(keys=["img", "seg"], ...), + RandRotated(keys=["img", "seg"], ...), + RandZoomd(keys=["img", "seg"], ...), + RandGaussianNoised(keys="img", ...), + ] + compose = Compose(transforms) + + # elsewhere this will be called many times (such as in a Dataset instance) + outputs = compose(inputs) +:: + +The following will then happen when we call ``compose(inputs)``: + +1. ``LoadImaged`` is called with its inputs (a dictionary of strings containing file locations). This loads and + returns a dictionary of the corresponding data samples +2. ``EnsureChannelFirstd`` is called with the dictionary of data samples and adds a channel so that they have the + appropriate shape for the rest of the pipeline +3. ``Spacingd`` is called and reinterpolates the data samples +4. ``Orientationd`` permutes the data samples so that their spatial dimensions are reorganised +5. ``RandSpatialCropd`` crops a random patch of the data samples, throwing away the rest of the data in the process +6. ``RandRotate90d`` has a chance of performing a tensor-based rotation of the data samples +7. ``RandRotated`` has a chance of performing a full resample of the data samples +8. ``RandZoomd`` has a chance of performing a reinterpolation of the data samples +9. ``RandGaussianNoised`` has a chance of adding noise to ``img`` + +Overall, there are up to three occasions where the data is either interpolated or resampled through spatial transforms. +Furthermore, the crop that occurs means that the output data samples might contain pixels for which there is data but +that show padding values, because the data was thrown away by ``RandSpatialCrop``. + +Each of these operations takes time and memory, but, as we can see in the example above, also creates resampling +artifacts and can even destroy data in the resulting data samples (see `lazy resampling best practices`_ for examples). + +Lazy resampling pipelines +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Lazy resampling works very differently. When you execute the same pipeline with `lazy=True`, the following happens: + +1. ``LoadImaged`` behaves identically +2. ``EnsureChannelFirstd`` behaves identically +3. ``Spacingd`` is executing lazily. It puts a description of the operation that it wants to perform onto a list of + pending operations +4. ``Orientationd`` is executing lazily. It adds a description of its own operation to the pending operation list so + now there are 2 pending operations +5. ``RandSpatialCropd`` is executing lazily. It adds a description of its own operation to the pending operation list + so now there are 3 pending operations +6. ``RandRotate90d`` is executing lazily. It adds a description of its own operation to the pending operation list + so now there are 4 pending operations +7. ``RandRotated`` is executing lazily. It adds a description of its own operation to the pending operation list + so now there are 5 pending operations +8. ``RandZoomd`` is executing lazily. It adds a description of its own operation to the pending operation list + so now there are 6 pending operations + 1. ``[Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, RandZoomd]`` are all on the pending + operations list but have yet to be carried out on the data +9. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their + descriptions are mathematically composited together, to determine the operation that results from all of them + being carried out. This is then applied in a single resample operation. Once that is done, ``RandGaussianNoised`` + operates on the resulting data + +The single resampling operation has less noise induced by resampling, as it only occurs once in this pipeline rather +than three times in the traditional pipeline. More importantly, although \ No newline at end of file From 23ff89c6b8273d866e848de810a8297ec93ec350 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 12:32:14 +0000 Subject: [PATCH 108/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/lazy_resampling.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index a9c5a65bf7..8dfbfceeb1 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -99,4 +99,4 @@ Lazy resampling works very differently. When you execute the same pipeline with operates on the resulting data The single resampling operation has less noise induced by resampling, as it only occurs once in this pipeline rather -than three times in the traditional pipeline. More importantly, although \ No newline at end of file +than three times in the traditional pipeline. More importantly, although From 5e1b81dcb5b13c2f6675718a0a1a8fb57325e7c3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sat, 6 May 2023 13:51:26 +0100 Subject: [PATCH 109/175] Removing unused Sequence import from lazy/executors Signed-off-by: Ben Murray --- monai/transforms/compose.py | 41 +++++++++++++----------------- monai/transforms/lazy/executors.py | 6 ++--- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 3371b6febb..38033d739f 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -131,9 +131,9 @@ def execute_compose( return data -class ComposeCompiler: +class ExecutionOptions: """ - The ComposeCompiler is an implementation class that is required to parse options for Compose. It should currently + ExecutionOptions is an implementation class that is required to parse options for Compose. It should currently be considered an implementation detail that should not be interacted with directly by users of MONAI, although that may change in subsequent releases. Its job is to parse options provided to `Compose.__call__`_ to set execution modes for lazy resampling. @@ -162,7 +162,7 @@ def __call__(self, transforms, lazy: bool | None, options: dict | None = None): """ if lazy is False or options is None: - return ComposeCompiler.generate_policy() + return ExecutionOptions.generate_policy() if len(options.keys()) > 1: raise ValueError("Only one option can currently be set") @@ -242,7 +242,7 @@ def reorder_lazy_last(cls, *, transforms: list, lazy: bool | None, **kwargs): ] reordered.extend(subsection) - return ComposeCompiler.generate_policy({"indices": permuted_indices}) + return ExecutionOptions.generate_policy({"indices": permuted_indices}) @classmethod def reorder_lazy_last_nosync(cls, *, transforms: list, **_): @@ -511,7 +511,7 @@ def __len__(self): def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): lazy_ = self.lazy if lazy is None else lazy - policy = ComposeCompiler()(self.transforms, lazy_, self.options) + policy = ExecutionOptions()(self.transforms, lazy_, self.options) lazy_strategy = policy["lazy_policy"] indices = policy["indices"] @@ -544,38 +544,31 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None return result def inverse(self, data): - if self.options is not None: - compiler = ComposeCompiler() - policy = compiler(self.transforms, self.lazy, self.options) - else: - policy = ComposeCompiler().generate_policy() - + policy = ExecutionOptions()(self.transforms, self.lazy, self.options) indices = policy["indices"] self._raise_if_tensor_is_not_invertible(data) - data_ = deepcopy(data) - if indices is not None: applied_order = None - if isinstance(data_, monai.data.MetaTensor): - applied_order = self.pop_transform(data_)[TraceKeys.EXTRA_INFO]["applied_order"] - elif isinstance(data_, Mapping): - for key in data_: - if isinstance(data_[key], monai.data.MetaTensor) or self.trace_key(key) in data_: - applied_order = self.pop_transform(data_, key)[TraceKeys.EXTRA_INFO]["applied_order"] + if isinstance(data, monai.data.MetaTensor): + applied_order = self.pop_transform(data)[TraceKeys.EXTRA_INFO]["applied_order"] + elif isinstance(data, Mapping): + for key in data: + if isinstance(data[key], monai.data.MetaTensor) or self.trace_key(key) in data: + applied_order = self.pop_transform(data, key)[TraceKeys.EXTRA_INFO]["applied_order"] else: raise RuntimeError( f"Inverse only implemented for Mapping (dictionary) or MetaTensor data, got type {type(data)}." ) if applied_order is None: # no invertible transforms have been applied - return data_ + return data # loop backwards over transforms for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): - data_ = apply_transform(self.transforms[o].inverse, data_, self.map_items, self.unpack_items) + data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) else: invertible_transforms = [t for t in self.flatten().transforms if isinstance(t, InvertibleTransform)] if not invertible_transforms: @@ -593,10 +586,10 @@ def inverse(self, data): # f"inversing {t.__class__.__name__} lazily may not implemented" # "please set `lazy=False` before calling inverse." # ) - data_ = apply_transform( - t.inverse, data_, self.map_items, self.unpack_items, lazy=False, logger_name=self.logger_name + data = apply_transform( + t.inverse, data, self.map_items, self.unpack_items, lazy=False, logger_name=self.logger_name ) - return data_ + return data @staticmethod def _raise_if_tensor_is_not_invertible(data: Any): diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 7fea34cf7b..1feef5351b 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -11,7 +11,7 @@ from __future__ import annotations -from typing import Any, Mapping, Sequence +from typing import Any, Mapping from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor @@ -105,7 +105,7 @@ def patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overr def apply_pending_transforms( - data: NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], + data: NdarrayOrTensor | Mapping[Any, NdarrayOrTensor], keys: tuple | None, overrides: dict | None = None, logger_name: str | None = None, @@ -126,7 +126,7 @@ def apply_pending_transforms( you are developing functionality to perform such operations. Args: - data: a ``torch.Tensor`` or ``MetaTensor``, or list, tuple or dictionary of tensors. + data: a ``torch.Tensor`` or ``MetaTensor``, or dictionary of tensors. keys: an optional tuple of keys that filters the keys on 'data' if it is a dict overrides: An optional dictionary that specifies parameters that can be used to override transform arguments when they are called. When 'data' is a dict, this dictionary should contain a dictionary From 5dd61137386c07a3a0922e83961a00022e76a2b4 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sat, 6 May 2023 14:28:29 +0100 Subject: [PATCH 110/175] Renaming ComposeCompiler to ExecuteOptions in test_compose Signed-off-by: Ben Murray --- tests/test_compose.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index cf8e825941..8bf87b3cba 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -23,22 +23,7 @@ import monai.transforms as mt from monai.data import DataLoader, Dataset - -# from monai.transforms import ( -# AddChannel, -# ApplyPending, -# ApplyPendingd, -# Compose, -# Flip, -# NormalizeIntensity, -# NormalizeIntensityd, -# Rotate, -# Rotate90, -# Rotated, -# Spacing, -# Zoom, -# ) -from monai.transforms.compose import ComposeCompiler, execute_compose +from monai.transforms.compose import ExecutionOptions, execute_compose from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -790,13 +775,13 @@ class TestTransformReordering(unittest.TestCase): @parameterized.expand(TRANSFORM_REORDERING_TEST_CASES) def test_transform_reordering_test_cases(self, transforms, options, lazy_enabled_expected, lazy_on_expected): with self.subTest("enable lazy"): - c = ComposeCompiler()(transforms, lazy=None, options={"reorder": "lazy_last"}) + c = ExecutionOptions()(transforms, lazy=None, options={"reorder": "lazy_last"}) reordered = [transforms[i] for i in c["indices"]] actual = [t.tag for t in reordered] self.assertListEqual(actual, lazy_enabled_expected) with self.subTest("force lazy"): - c = ComposeCompiler()(transforms, lazy=True, options={"reorder": "lazy_last"}) + c = ExecutionOptions()(transforms, lazy=True, options={"reorder": "lazy_last"}) reordered = [transforms[i] for i in c["indices"]] actual = [t.tag for t in reordered] self.assertListEqual(actual, lazy_on_expected) From 74bed34b71f2bafcae51ea3c62257f65db80a400 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 10 May 2023 12:12:06 +0100 Subject: [PATCH 111/175] Update tests/test_compose.py Cleaner tensor ops for test_compose Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- tests/test_compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index cf8e825941..c2dc978aad 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -580,7 +580,7 @@ class TestComposeExecuteWithLogging(unittest.TestCase): @staticmethod def data_from_keys(keys): if keys is None: - data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) + data = torch.arange(12 * 16).reshape(1, 12, 16) else: data = {} for i_k, k in enumerate(keys): From 76e4b85aef2eaf6603a3521e73bfee13f5cb789e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 10 May 2023 12:40:44 +0100 Subject: [PATCH 112/175] Execution options is no longer a poltergeist. Refactored is_tensor_invertible. Removed LAZY from default transform_info(). It is now added during 'track_transform_meta' Signed-off-by: Ben Murray --- monai/transforms/compose.py | 7 ++-- monai/transforms/inverse.py | 7 +++- monai/transforms/utils.py | 76 +++++++++++++++++++++++++++---------- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 38033d739f..b7ccd10dfd 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -136,7 +136,7 @@ class ExecutionOptions: ExecutionOptions is an implementation class that is required to parse options for Compose. It should currently be considered an implementation detail that should not be interacted with directly by users of MONAI, although that may change in subsequent releases. Its job is to parse options provided to `Compose.__call__`_ to set execution - modes for lazy resampling. + modes for executing the pipeline. See `Compose`_ for a detailed explanation of lazy resampling. """ @@ -433,6 +433,7 @@ def __init__( self.overrides = overrides self.options = options self.logger_name = logger_name + self.execution_options = ExecutionOptions() def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) @@ -511,7 +512,7 @@ def __len__(self): def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): lazy_ = self.lazy if lazy is None else lazy - policy = ExecutionOptions()(self.transforms, lazy_, self.options) + policy = self.execution_options(self.transforms, lazy_, self.options) lazy_strategy = policy["lazy_policy"] indices = policy["indices"] @@ -544,7 +545,7 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None return result def inverse(self, data): - policy = ExecutionOptions()(self.transforms, self.lazy, self.options) + policy = self.execution_options(self.transforms, self.lazy, self.options) indices = policy["indices"] self._raise_if_tensor_is_not_invertible(data) diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index 5defa6c167..eea02eb8e0 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -86,7 +86,7 @@ def trace_key(key: Hashable = None): @staticmethod def transform_info_keys(): """The keys to store necessary info of an applied transform.""" - return (TraceKeys.CLASS_NAME, TraceKeys.ID, TraceKeys.TRACING, TraceKeys.LAZY, TraceKeys.DO_TRANSFORM) + return (TraceKeys.CLASS_NAME, TraceKeys.ID, TraceKeys.TRACING, TraceKeys.DO_TRANSFORM) def get_transform_info(self) -> dict: """ @@ -96,7 +96,6 @@ def get_transform_info(self) -> dict: self.__class__.__name__, id(self), self.tracing, - self.lazy if isinstance(self, LazyTrait) else False, self._do_transform if hasattr(self, "_do_transform") else True, ) return dict(zip(self.transform_info_keys(), vals)) @@ -206,6 +205,10 @@ def track_transform_meta( info[TraceKeys.ORIG_SIZE] = data_t.peek_pending_shape() elif hasattr(data_t, "shape"): info[TraceKeys.ORIG_SIZE] = data_t.shape[1:] + + # add lazy status to the transform info + info[TraceKeys.LAZY] = lazy + # include extra_info if extra_info is not None: extra_info.pop(LazyAttr.SHAPE, None) diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index bc765abe04..4999d22bac 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -1976,22 +1976,57 @@ def resolves_modes( return backend, _interp_mode, _padding_mode, _kwargs -def is_tensor_invertible(data: Any): - def check_applied_operations(entry): - if isinstance(entry, list): - results = list() - for sub_entry in entry: - results.extend(check_applied_operations(sub_entry)) - return results - else: - if TraceKeys.STATUSES in entry: - if TraceStatusKeys.PENDING_DURING_APPLY in entry[TraceKeys.STATUSES]: - reason = entry[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] - if reason is None: - return ["Pending operations while applying an operation"] - return reason if isinstance(reason, list) else [reason] - return [] +def check_applied_operations(entry: list | dict, status_key: str, default_message: str = "No message provided"): + """ + Check the operations of a MetaTensor to determine whether there are any statuses + Args: + entry: a dictionary that may contain TraceKey.STATUS entries, or a list of such dictionaries + status_key: the status key to search for. This must be an entry in `TraceStatusKeys`_ + default_message: The message to provide if no messages are provided for the given status key entry + + Returns: + A list of status messages matching the providing status key + + """ + if isinstance(entry, list): + results = list() + for sub_entry in entry: + results.extend(check_applied_operations(sub_entry, status_key, default_message)) + return results + else: + status_key_ = TraceStatusKeys(status_key) + if TraceKeys.STATUSES in entry: + if status_key_ in entry[TraceKeys.STATUSES]: + reason = entry[TraceKeys.STATUSES][status_key_] + if reason is None: + return [default_message] + return reason if isinstance(reason, list) else [reason] + return [] + + +def is_tensor_invertible(data: torch.Tensor): + """ + Checks whether a given tensor is invertible. The rules are as follows: + 1. If the tensor is not a MetaTensor, it is not invertible + 2. If the tensor is a MetaTensor but it has `TraceStatusKeys.PENDING_DURING_APPLY` in the `TraceKeys.STATUS` of any + of its `applied_operations` it is not invertible + 3. Otherwise, it is invertible + + This function also accepts: + * dictionaries of tensors + * lists or tuples of tensors + * list or tuples of dictionaries of tensors + In any of the above scenarios, it iterates through the collections and executes itself recursively until it is + operating on tensors. + + Args: + data: a `torch.Tensor` or `MetaTensor` or collections of torch.Tensor or MetaTensor, as described above + Returns: + A tuple. The first entry is `False` or `True`. The second entry is the status messages that can be used for the + user to help debug their pipelines. + + """ invert_disabled_reasons = list() if isinstance(data, (list, tuple)): for d in data: @@ -2000,12 +2035,11 @@ def check_applied_operations(entry): invert_disabled_reasons.extend(reasons) elif isinstance(data, monai.data.MetaTensor): for op in data.applied_operations: - invert_disabled_reasons.extend(check_applied_operations(op)) - # if op - # if TraceKeys.STATUSES in op: - # if TraceStatusKeys.PENDING_DURING_APPLY in op[TraceKeys.STATUSES]: - # reason = op[TraceKeys.STATUSES][TraceStatusKeys.PENDING_DURING_APPLY] - # invert_disabled_reasons.extend(["PENDING DURING APPLY"] if reason is None else reason) + invert_disabled_reasons.extend( + check_applied_operations( + op, TraceStatusKeys.PENDING_DURING_APPLY, "Pending operations while applying an operation" + ) + ) elif isinstance(data, dict): for d in data.values(): _, reasons = is_tensor_invertible(d) From 8dde40dea410507a968f9a1a25058001113a3d69 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 11:41:25 +0000 Subject: [PATCH 113/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/inverse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index eea02eb8e0..f010aa9de9 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -23,7 +23,7 @@ from monai.data.meta_obj import MetaObj, get_track_meta from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd -from monai.transforms.traits import InvertibleTrait, LazyTrait +from monai.transforms.traits import InvertibleTrait from monai.transforms.transform import Transform from monai.utils import ( LazyAttr, From 7fd32e232360411e678d9b84b2912016ac0974d5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 10 May 2023 18:10:05 +0100 Subject: [PATCH 114/175] Removing unnecessary active_key property Signed-off-by: Ben Murray --- monai/transforms/croppad/array.py | 40 ++++++----------- monai/transforms/croppad/dictionary.py | 42 ++++++------------ monai/transforms/lazy/executors.py | 60 +++++++------------------- monai/transforms/traits.py | 11 +++++ monai/transforms/transform.py | 4 ++ 5 files changed, 56 insertions(+), 101 deletions(-) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 25bd1ae4cd..5e1acc2f9f 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -808,11 +808,6 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - # if lazy is True: - # warnings.warn( - # "CropForeground cannot currently execute lazily; " "ignoring lazy=True set during initialization" - # ) - # lazy = False LazyTransform.__init__(self, lazy) self.select_fn = select_fn self.channel_indices = ensure_tuple(channel_indices) if channel_indices is not None else None @@ -827,6 +822,10 @@ def lazy(self, _val: bool): self._lazy = _val self.padder.lazy = _val + @property + def partially_lazy(self): + return False + def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: """ Compute the start points and end points of bounding box to crop. @@ -897,10 +896,6 @@ def __call__( # type: ignore[override] Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't change the channel dim. """ - # if lazy is True: - # warnings.warn("CropForeground cannot currently execute lazily; ignoring lazy=True") - # lazy = False - box_start, box_end = self.compute_bounding_box(img) lazy_ = self.lazy if lazy is None else lazy cropped = self.crop_pad(img, box_start, box_end, mode, lazy=lazy_, **pad_kwargs) @@ -1073,11 +1068,6 @@ def __init__( allow_smaller: bool = False, lazy: bool = False, ) -> None: - # if lazy is True: - # warnings.warn( - # "RandCropByPosNegLabel cannot currently execute lazily; " "ignoring lazy=True set during initialization" - # ) - # lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.label = label @@ -1129,6 +1119,10 @@ def randomize( def lazy(self, _val: bool): self._lazy = _val + @property + def partially_lazy(self): + return False + def __call__( self, img: torch.Tensor, @@ -1154,10 +1148,6 @@ def __call__( randomize: whether to execute the random operations, default to `True`. """ - # if lazy is True: - # warnings.warn("RandCropByPosNegLabel cannot currently execute lazily; ignoring lazy=True") - # lazy = False - if image is None: image = self.image if randomize: @@ -1265,12 +1255,6 @@ def __init__( max_samples_per_class: int | None = None, lazy: bool = False, ) -> None: - # if lazy is True: - # warnings.warn( - # "RandCropByLabelClasses cannot currently execute lazily; " - # "ignoring lazy=True set during initialization" - # ) - # lazy = False LazyTransform.__init__(self, lazy) self.spatial_size = spatial_size self.ratios = ratios @@ -1313,6 +1297,10 @@ def randomize( def lazy(self, _val: bool): self._lazy = _val + @property + def partially_lazy(self): + return False + def __call__( self, img: torch.Tensor, @@ -1333,10 +1321,6 @@ def __call__( randomize: whether to execute the random operations, default to `True`. """ - # if lazy is True: - # warnings.warn("RandCropByLabelClasses cannot currently execute lazily; ignoring lazy=True") - # lazy = False - if image is None: image = self.image if randomize: diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 385a7bf794..49f22576ae 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -47,6 +47,7 @@ SpatialPad, ) from monai.transforms.inverse import InvertibleTransform +from monai.transforms.lazy.executors import apply_pending_transforms from monai.transforms.traits import LazyTrait, MultiSampleTrait from monai.transforms.transform import LazyTransform, MapTransform, Randomizable from monai.transforms.utils import is_positive @@ -721,12 +722,6 @@ def __init__( note that `np.pad` treats channel dimension as the first dimension. """ - # if lazy is True: - # warnings.warn( - # "CropForegroundd cannot currently execute lazily; " "ignoring lazy=True set during initialization" - # ) - # lazy = False - self.source_key = source_key self.start_coord_key = start_coord_key self.end_coord_key = end_coord_key @@ -747,11 +742,11 @@ def lazy(self, value: bool) -> None: self._lazy = value self.cropper.lazy = value - def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: - # if lazy is True: - # warnings.warn("CropForegroundd cannot currently execute lazily; ignoring lazy=True") - # lazy = False + @property + def partially_lazy(self): + return True + def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: d = dict(data) self.cropper: CropForeground box_start, box_end = self.cropper.compute_bounding_box(img=d[self.source_key]) @@ -904,12 +899,6 @@ def __init__( lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - # if lazy is True: - # warnings.warn( - # "RandCropByPosNegLabeld cannot currently execute lazily; " - # "ignoring lazy=True set during initialization" - # ) - # lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key @@ -946,13 +935,13 @@ def lazy(self, value: bool) -> None: self._lazy = value self.cropper.lazy = value + @property + def partially_lazy(self): + return True + def __call__( self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None ) -> list[dict[Hashable, torch.Tensor]]: - # if lazy is True: - # warnings.warn("RandCropByPosNegLabeld cannot currently execute lazily; ignoring lazy=True") - # lazy = False - d = dict(data) fg_indices = d.pop(self.fg_indices_key, None) bg_indices = d.pop(self.bg_indices_key, None) @@ -1068,12 +1057,6 @@ def __init__( lazy: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) - # if lazy is True: - # warnings.warn( - # "RandCropByLabelClassesd cannot currently execute lazily; " - # "ignoring lazy=True set during initialization" - # ) - # lazy = False LazyTransform.__init__(self, lazy) self.label_key = label_key self.image_key = image_key @@ -1107,10 +1090,11 @@ def lazy(self, value: bool) -> None: self._lazy = value self.cropper.lazy = value + @property + def partially_lazy(self): + return True + def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: - # if lazy is True: - # warnings.warn("RandCropByLabelClassesd cannot currently execute lazily; ignoring lazy=True") - # lazy = False d = dict(data) self.randomize(d.get(self.label_key), d.pop(self.indices_key, None), d.get(self.image_key)) # type: ignore diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 1feef5351b..90700c9f27 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -77,33 +77,6 @@ def _log_applied_info(data: Any, key=None, logger_name: str | None = None): logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") -def patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overrides, logger_name): - from monai.transforms.croppad.array import CropForeground, RandCropByLabelClasses, RandCropByPosNegLabel - from monai.transforms.croppad.dictionary import CropForegroundd, RandCropByLabelClassesd, RandCropByPosNegLabeld - - if isinstance(data, dict): - if isinstance(transform, CropForegroundd): - k = transform.source_key - elif isinstance(transform, (RandCropByLabelClassesd, RandCropByPosNegLabeld)): - k = transform.label_key - else: - return data - - if isinstance(data[k], MetaTensor) and data[k].has_pending_operations: - d = dict(data) - k = transform.source_key - _log_pending_info(transform, data, "Apply prior to executing", key=k, lazy=lazy, logger_name=logger_name) - d[k] = apply_pending(data[k], overrides=overrides.get(k, None)) - return d - elif isinstance(data, MetaTensor) and data.has_pending_operations: - if isinstance(transform, (CropForeground, RandCropByLabelClasses, RandCropByPosNegLabel)): - _log_pending_info(transform, data, "Apply prior to executing", lazy=lazy, logger_name=logger_name) - data = apply_pending(data, overrides) - return data - - return data - - def apply_pending_transforms( data: NdarrayOrTensor | Mapping[Any, NdarrayOrTensor], keys: tuple | None, @@ -185,27 +158,26 @@ def apply_pending_transforms_in_order( an object of the same type as data if pending transforms were applied, or 'data' if they were not """ - if lazy is False: - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, None, overrides, logger_name) - - lazy_ = transform.lazy if isinstance(transform, LazyTrait) and lazy is None else lazy - if not isinstance(transform, LazyTrait) or lazy_ is False: - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, None, overrides, logger_name) - - if isinstance(transform, ApplyPendingd): - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, transform.keys, overrides, logger_name) + apply_pending = False + keys = None + if isinstance(transform, LazyTrait): + if transform.partially_lazy: + apply_pending = True + else: + apply_pending = not (transform.lazy if lazy is None else lazy) + elif isinstance(transform, ApplyPending): + apply_pending = True + elif isinstance(transform, ApplyPendingd): + apply_pending = True + keys = transform.keys + else: + apply_pending = True - if isinstance(transform, ApplyPending): + if apply_pending is True: _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, None, overrides, logger_name) - - patch_for_in_order_needs_implicit_apply_pending(transform, data, lazy, overrides, logger_name) + return apply_pending_transforms(data, keys, overrides, logger_name) _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) - return data diff --git a/monai/transforms/traits.py b/monai/transforms/traits.py index 8ba39f8667..805fc61f18 100644 --- a/monai/transforms/traits.py +++ b/monai/transforms/traits.py @@ -46,6 +46,17 @@ def lazy(self, enabled: bool): """ raise NotImplementedError() + @property + def partially_lazy(self): + """ + Get whether the transform is "partially lazy" or not. A partially lazy transform + requires that all of the pending operations on its input transforms are up to date + before it is executed, but it can still execute lazily by adding pending operations + to the input tensors. + Returns: + True if the transform is partially lazy and False if it is not + """ + class InvertibleTrait: """ diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 5f7bb29f30..59847e23b2 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -321,6 +321,10 @@ def lazy(self, lazy: bool | None): raise TypeError(f"lazy must be a bool but is of type {type(lazy)}") self._lazy = lazy + @property + def partially_lazy(self): + return False + class RandomizableTransform(Randomizable, Transform): """ From 0d6a847066a4404ae04e9bca91d64995d2527d71 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 17:13:10 +0000 Subject: [PATCH 115/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/croppad/dictionary.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 49f22576ae..05ea25981f 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -47,7 +47,6 @@ SpatialPad, ) from monai.transforms.inverse import InvertibleTransform -from monai.transforms.lazy.executors import apply_pending_transforms from monai.transforms.traits import LazyTrait, MultiSampleTrait from monai.transforms.transform import LazyTransform, MapTransform, Randomizable from monai.transforms.utils import is_positive From 1ceeb1a6318e3147c9aca9c9b2d568d48d19df48 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 11 May 2023 10:01:54 +0100 Subject: [PATCH 116/175] Fix for multi-sampled data for transforms that aceept *args; refactoring out of order execution for brevity Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 -- monai/transforms/lazy/executors.py | 28 +++++++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index b7ccd10dfd..a610692952 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -125,8 +125,6 @@ def execute_compose( overrides=overrides, logger_name=logger_name, ) - if isinstance(data, (list, tuple)) and map_items: - data = [apply_pending_transforms(d, None, overrides, logger_name=logger_name) for d in data] data = apply_pending_transforms(data, None, overrides, logger_name=logger_name) return data diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 90700c9f27..3d427a6895 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -11,7 +11,7 @@ from __future__ import annotations -from typing import Any, Mapping +from typing import Any, Mapping, Sequence from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor @@ -78,7 +78,7 @@ def _log_applied_info(data: Any, key=None, logger_name: str | None = None): def apply_pending_transforms( - data: NdarrayOrTensor | Mapping[Any, NdarrayOrTensor], + data: NdarrayOrTensor | Sequence[Any, NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], keys: tuple | None, overrides: dict | None = None, logger_name: str | None = None, @@ -109,6 +109,11 @@ def apply_pending_transforms( Returns: an object of the same type as data if pending transforms were applied, or 'data' if they were not """ + if isinstance(data, list): + return [apply_pending_transforms(d, keys, overrides, logger_name) for d in data] + if isinstance(data, tuple): + return tuple(apply_pending_transforms(d, keys, overrides, logger_name) for d in data) + if isinstance(data, dict): # get the keys from 'data' for metatensors with pending operations. If 'keys' is set, select # only data keys that are in 'keys' @@ -206,18 +211,19 @@ def apply_pending_transforms_out_of_order( an object of the same type as data if pending transforms were applied, or 'data' if they were not """ + apply_pending = False + keys = None if lazy is False: - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, None, overrides, logger_name) - - if isinstance(transform, ApplyPendingd): - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, transform.keys, overrides, logger_name) + apply_pending = True + elif isinstance(transform, ApplyPending): + apply_pending = True + elif isinstance(transform, ApplyPendingd): + apply_pending = True + keys = transform.keys - if isinstance(transform, ApplyPending): + if apply_pending is True: _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, None, overrides, logger_name) + return apply_pending_transforms(data, keys, overrides, logger_name) _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) - return data From 6e0cfd9d7382069215f3dd0f470663fd5c97bc70 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Sun, 21 May 2023 18:22:59 +0100 Subject: [PATCH 117/175] Renaming LazyTrait partially_lazy -> checks_data Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 4 +++- monai/transforms/croppad/array.py | 6 +++--- monai/transforms/croppad/dictionary.py | 6 +++--- monai/transforms/lazy/executors.py | 2 +- monai/transforms/traits.py | 12 ++++++------ monai/transforms/transform.py | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index 8dfbfceeb1..f2e9911e3f 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -99,4 +99,6 @@ Lazy resampling works very differently. When you execute the same pipeline with operates on the resulting data The single resampling operation has less noise induced by resampling, as it only occurs once in this pipeline rather -than three times in the traditional pipeline. More importantly, although +than three times in the traditional pipeline. More importantly, although the crop describes an operation to keep only a +subset of the data sample, the crop is not performed until after the spatial transforms are completed, which means that +all of the data sample that is within bounds is preserved and is part of the resulting output. diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index 5e1acc2f9f..a0243cd237 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -823,7 +823,7 @@ def lazy(self, _val: bool): self.padder.lazy = _val @property - def partially_lazy(self): + def checks_data(self): return False def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: @@ -1120,7 +1120,7 @@ def lazy(self, _val: bool): self._lazy = _val @property - def partially_lazy(self): + def checks_data(self): return False def __call__( @@ -1298,7 +1298,7 @@ def lazy(self, _val: bool): self._lazy = _val @property - def partially_lazy(self): + def checks_data(self): return False def __call__( diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 05ea25981f..0ea6dc3442 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -742,7 +742,7 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value @property - def partially_lazy(self): + def checks_data(self): return True def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: @@ -935,7 +935,7 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value @property - def partially_lazy(self): + def checks_data(self): return True def __call__( @@ -1090,7 +1090,7 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value @property - def partially_lazy(self): + def checks_data(self): return True def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 3d427a6895..1adec4c6a9 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -166,7 +166,7 @@ def apply_pending_transforms_in_order( apply_pending = False keys = None if isinstance(transform, LazyTrait): - if transform.partially_lazy: + if transform.checks_data: apply_pending = True else: apply_pending = not (transform.lazy if lazy is None else lazy) diff --git a/monai/transforms/traits.py b/monai/transforms/traits.py index 805fc61f18..a47fc87e79 100644 --- a/monai/transforms/traits.py +++ b/monai/transforms/traits.py @@ -47,14 +47,14 @@ def lazy(self, enabled: bool): raise NotImplementedError() @property - def partially_lazy(self): + def checks_data(self): """ - Get whether the transform is "partially lazy" or not. A partially lazy transform - requires that all of the pending operations on its input transforms are up to date - before it is executed, but it can still execute lazily by adding pending operations - to the input tensors. + Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its + operation. A transform that checks data requires that all of the pending operations on its input + transforms are up to date before it is executed, but it can still execute lazily by adding pending + operations to the input tensors. Returns: - True if the transform is partially lazy and False if it is not + True if the transform checks data and False if it does not """ diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 59847e23b2..8b4795f7e6 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -322,7 +322,7 @@ def lazy(self, lazy: bool | None): self._lazy = lazy @property - def partially_lazy(self): + def checks_data(self): return False From 155ad906e7e1ad2b2f552cb841bc35238cba97e5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 08:41:32 +0100 Subject: [PATCH 118/175] Initial commit of reduced lazy resampling functionality for 1.2 Signed-off-by: Ben Murray --- monai/transforms/compose.py | 271 +++++-------------------- monai/transforms/lazy/executors.py | 73 ++----- monai/transforms/transform.py | 30 ++- tests/test_compose.py | 132 +----------- tests/test_integration_lazy_samples.py | 52 ++--- 5 files changed, 105 insertions(+), 453 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index a610692952..af56531d7d 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -55,10 +55,10 @@ def execute_compose( start: int = 0, end: int | None = None, lazy: bool | None = False, - lazy_strategy: str = "in_order", overrides: dict | None = None, threading: bool = False, - logger_name: str | None = None, + log_stats: bool | str | None = False, + logger_name: bool | str | None = False, ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -79,9 +79,6 @@ def execute_compose( lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. - lazy_strategy: this field controls how execution occurs when processing data lazily. Permitted - options are "in_order", "out_of_order". Please see `Compose`_ for more details of what these - options mean. In general, you should not need to change this from its default. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when lazy @@ -121,156 +118,14 @@ def execute_compose( map_items, unpack_items, lazy=lazy, - lazy_strategy=lazy_strategy, overrides=overrides, + log_stats=log_stats, logger_name=logger_name, ) data = apply_pending_transforms(data, None, overrides, logger_name=logger_name) return data -class ExecutionOptions: - """ - ExecutionOptions is an implementation class that is required to parse options for Compose. It should currently - be considered an implementation detail that should not be interacted with directly by users of MONAI, although that - may change in subsequent releases. Its job is to parse options provided to `Compose.__call__`_ to set execution - modes for executing the pipeline. - - See `Compose`_ for a detailed explanation of lazy resampling. - """ - - def __init__(self): - # construct the list of options - options = {"reorder": {"lazy_last": self.reorder_lazy_last, "lazy_last_nosync": self.reorder_lazy_last_nosync}} - self.options = options - - def __call__(self, transforms, lazy: bool | None, options: dict | None = None): - """ - Get a policy object that controls the way `Compose`_ executes a list of transforms. At present, the user can - only specify a single flag, but this design will be extended in future releases to allow multiple flags to - control different aspects of transform execution. - - Args: - transforms: a list of transforms to be executed - lazy: the current lazy mode (False, None, or True) - options: the options that determine the execution policy - - Returns: - a dictionary specifying the execution policy - - """ - if lazy is False or options is None: - return ExecutionOptions.generate_policy() - - if len(options.keys()) > 1: - raise ValueError("Only one option can currently be set") - - for k, v in options.items(): - if k not in self.options.keys(): - raise KeyError( - f"'{k}' is not a valid option key. Valid options are " f"{tuple(k for k in self.options.keys())}" - ) - - option = self.options[k] - if v not in option.keys(): - raise KeyError(f"'{v}' is not a valid option value. Value options for " f"'{k}' are {option.keys()}") - - action = option[v] - - return action(transforms=transforms, lazy=lazy) # type: ignore[operator] - - @classmethod - def reorder_lazy_last(cls, *, transforms: list, lazy: bool | None, **kwargs): - """ - 'reorder_lazy_last` effectively reorders a sequence of transforms so that lazy transforms are grouped together - after non-lazy ones. This operation can significantly change the behaviour of your pipeline and so should only - be used once you are clear about its behaviour. - - Example:: - - transforms = [LoadImage, Flip, GaussianNoise, Rotate90, ApplyPending, Zoom, Rotate] - - # ApplyPending effectively splits the pipeline up into two subranges. No transform can move after or before - # an ApplyPending instance, so we end up with transforms before and after ApplyPending - - sub_ranges = [[LoadImage, Flip, GaussianNoise, Rotate90], [ApplyPending], [Zoom, Rotate]] - - # Each subrange is then sorted so that non-lazy transform stay in their relative order but go before the - # lazy transforms (which also stay in their relative order) - - sub_ranges = [[LoadImage, GaussianNoise, Flip, Rotate90], [Apply - - :: - """ - subsections = list() - subsection_starts = list() - # pass 1: split the transform list into subsections - i_s = 0 - for i_t in range(len(transforms)): - if isinstance(transforms[i_t], (ApplyPending, ApplyPendingd)): - # this subsection ends and is added to the subsection list - if i_s < i_t: - subsections.append(transforms[i_s:i_t]) - subsection_starts.append(i_s) - # add apply pending in its own list - subsections.append([transforms[i_t]]) - subsection_starts.append(i_t) - i_s = i_t + 1 - - if i_s != len(transforms): - subsections.append(transforms[i_s:]) - subsection_starts.append(i_s) - - # pass 2: calculate the permuted indices - permuted_indices = list() - for sub_start, subsection in zip(subsection_starts, subsections): - for i_s, s in enumerate(subsection): - if not cls._executing_lazily(s, lazy): - permuted_indices.append(i_s + sub_start) - for i_s, s in enumerate(subsection): - if cls._executing_lazily(s, lazy): - permuted_indices.append(i_s + sub_start) - - # pass 2: sort the subsections - reordered = list() - for subsection in subsections: - # non-lazy, lazy - subsection = [t for t in subsection if not cls._executing_lazily(t, lazy)] + [ - t for t in subsection if cls._executing_lazily(t, lazy) - ] - reordered.extend(subsection) - - return ExecutionOptions.generate_policy({"indices": permuted_indices}) - - @classmethod - def reorder_lazy_last_nosync(cls, *, transforms: list, **_): - """ - 'reorder: lazy_last_nosync' is implemented through use of the 'out_of_order' execution - policy. See 'Compose'_ for details of this policy. - Args: - transforms: Not used by this method - - Returns: - - """ - return cls.generate_policy({"lazy_policy": "out_of_order"}) - - @staticmethod - def generate_policy(overrides: dict | None = None): - default_policy = {"indices": None, "transforms": None, "lazy_policy": "in_order"} - if overrides is not None: - for k, v in overrides.items(): - default_policy[k] = v - return default_policy - - @staticmethod - def _executing_lazily(t, lazy_policy): - if isinstance(t, LazyTrait): - lazy_ = t.lazy if lazy_policy is None else lazy_policy - return lazy_ - return False - - class Compose(Randomizable, InvertibleTransform): """ ``Compose`` provides the ability to chain a series of callables together in @@ -416,22 +271,21 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, + log_stats: bool | str | None = False, lazy: bool | None = False, overrides: dict | None = None, - options: dict | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ) -> None: if transforms is None: transforms = [] self.transforms = ensure_tuple(transforms) self.map_items = map_items self.unpack_items = unpack_items + self.log_stats = log_stats self.set_random_state(seed=get_seed()) self.lazy = lazy self.overrides = overrides - self.options = options self.logger_name = logger_name - self.execution_options = ExecutionOptions() def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) @@ -510,84 +364,46 @@ def __len__(self): def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): lazy_ = self.lazy if lazy is None else lazy - policy = self.execution_options(self.transforms, lazy_, self.options) - lazy_strategy = policy["lazy_policy"] - indices = policy["indices"] - - # permute the transforms if required - transforms = self.transforms if indices is None else [self.transforms[i] for i in indices] result = execute_compose( input_, - transforms, + transforms=self.transforms, start=start, end=end, map_items=self.map_items, unpack_items=self.unpack_items, lazy=self.lazy, # type: ignore - lazy_strategy=lazy_strategy, overrides=self.overrides, threading=threading, + log_stats=self.log_stats, logger_name=self.logger_name, ) - # if the transforms were permuted, record it in the metadata for inversion - if indices is not None: - if isinstance(result, monai.data.MetaTensor): - self.push_transform(result, extra_info={"applied_order": indices}) - elif isinstance(result, Mapping): - for key in result: # dictionary not change size during iteration - if isinstance(result[key], monai.data.MetaTensor) or self.trace_key(key) in result: - self.push_transform(result, key, extra_info={"applied_order": indices}) - return result def inverse(self, data): - policy = self.execution_options(self.transforms, self.lazy, self.options) - indices = policy["indices"] - self._raise_if_tensor_is_not_invertible(data) - if indices is not None: - applied_order = None - if isinstance(data, monai.data.MetaTensor): - applied_order = self.pop_transform(data)[TraceKeys.EXTRA_INFO]["applied_order"] - elif isinstance(data, Mapping): - for key in data: - if isinstance(data[key], monai.data.MetaTensor) or self.trace_key(key) in data: - applied_order = self.pop_transform(data, key)[TraceKeys.EXTRA_INFO]["applied_order"] - else: - raise RuntimeError( - f"Inverse only implemented for Mapping (dictionary) or MetaTensor data, got type {type(data)}." - ) - if applied_order is None: - # no invertible transforms have been applied - return data - - # loop backwards over transforms - for o in reversed(applied_order): - if isinstance(self.transforms[o], InvertibleTransform): - data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) - else: - invertible_transforms = [t for t in self.flatten().transforms if isinstance(t, InvertibleTransform)] - if not invertible_transforms: - warnings.warn("inverse has been called but no invertible transforms have been supplied") + invertible_transforms = [t for t in self.flatten().transforms if isinstance(t, InvertibleTransform)] + if not invertible_transforms: + warnings.warn("inverse has been called but no invertible transforms have been supplied") - if self.lazy is not False: - warnings.warn( - f"'lazy' is set to {self.lazy} but lazy execution is not supported when inverting. " - f"'lazy' has been overridden to False for the call to inverse" - ) - # loop backwards over transforms - for t in reversed(invertible_transforms): - # if isinstance(t, LazyTrait) and t.lazy: - # warnings.warn( - # f"inversing {t.__class__.__name__} lazily may not implemented" - # "please set `lazy=False` before calling inverse." - # ) - data = apply_transform( - t.inverse, data, self.map_items, self.unpack_items, lazy=False, logger_name=self.logger_name - ) + if self.lazy is not False: + warnings.warn( + f"'lazy' is set to {self.lazy} but lazy execution is not supported when inverting. " + f"'lazy' has been overridden to False for the call to inverse" + ) + # loop backwards over transforms + for t in reversed(invertible_transforms): + data = apply_transform( + t.inverse, + data, + self.map_items, + self.unpack_items, + lazy=False, + log_stats=self.log_stats, + logger_name=self.logger_name, + ) return data @staticmethod @@ -637,9 +453,10 @@ def __init__( weights: Sequence[float] | float | None = None, map_items: bool = True, unpack_items: bool = False, + log_stats: bool | str | None = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: @@ -652,6 +469,7 @@ def __init__( f"got {len(weights)} and {len(self.transforms)}." ) self.weights = ensure_tuple(self._normalize_probabilities(weights)) + self.log_stats = log_stats self.logger_name = logger_name def _normalize_probabilities(self, weights): @@ -703,6 +521,7 @@ def __call__(self, data, start=0, end=None, threading=False, lazy: str | bool | lazy=self.lazy, # type: ignore overrides=self.overrides, threading=threading, + log_stats=self.log_stats, logger_name=self.logger_name, ) @@ -770,11 +589,13 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, + log_stats: bool | str | None = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) + self.log_stats = log_stats self.logger_name = logger_name def __call__(self, input_, start=0, end=None, threading=False, lazy: str | bool | None = None): @@ -798,6 +619,7 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: str | bool unpack_items=self.unpack_items, lazy=self.lazy, threading=threading, + log_stats=self.log_stats, logger_name=self.logger_name, ) @@ -832,7 +654,14 @@ def inverse(self, data): # loop backwards over transforms for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): - data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) + data = apply_transform( + self.transforms[o].inverse, + data, + self.map_items, + self.unpack_items, + log_stats=self.log_stats, + logger_name=self.logger_name, + ) return data @@ -863,16 +692,18 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, + log_stats: bool | str | None = False, *, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ) -> None: super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) + self.log_stats = log_stats self.logger_name = logger_name def _ensure_valid_num_transforms(self, num_transforms: int | tuple[int, int] | None) -> tuple: @@ -949,6 +780,7 @@ def __call__(self, data, start=0, end=None, threading=False, lazy: str | bool | lazy=self.lazy, overrides=self.overrides, threading=threading, + log_stats=self.log_stats, logger_name=self.logger_name, ) if isinstance(data, monai.data.MetaTensor): @@ -983,6 +815,13 @@ def inverse(self, data): # loop backwards over transforms for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): - data = apply_transform(self.transforms[o].inverse, data, self.map_items, self.unpack_items) + data = apply_transform( + self.transforms[o].inverse, + data, + self.map_items, + self.unpack_items, + log_stats=self.log_stats, + logger_name=self.logger_name, + ) return data diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 1adec4c6a9..86b5ffde3f 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -22,7 +22,7 @@ from monai.transforms.traits import LazyTrait from monai.transforms.transform import MapTransform -__all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order", "apply_pending_transforms_out_of_order"] +__all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order"] def _log_pending_info( @@ -32,10 +32,11 @@ def _log_pending_info( *, lazy: bool | None = None, key: str | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ): - if logger_name is None: + if logger_name is False: return + logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" logger = get_logger(logger_name) if isinstance(transform, LazyTrait): @@ -68,9 +69,10 @@ def _log_pending_info( ) -def _log_applied_info(data: Any, key=None, logger_name: str | None = None): - if logger_name is None: +def _log_applied_info(data: Any, key=None, logger_name: bool | str | None = False): + if logger_name is False: return + logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" logger = get_logger(logger_name) key_str = "" if key is None else f"key: '{key}', " @@ -81,7 +83,7 @@ def apply_pending_transforms( data: NdarrayOrTensor | Sequence[Any, NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], keys: tuple | None, overrides: dict | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ): """ apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain @@ -139,16 +141,20 @@ def apply_pending_transforms( def apply_pending_transforms_in_order( - transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: str | None = None + transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str | None = False ): """ - This method causes "out of order" processing of pending transforms to occur. - - Out of order processing for lazy resampling only causes pending transforms to be processed when - an `ApplyPending`_ or `ApplyPendingd`_ transform is encountered in the pipeline. + This method causes "in order" processing of pending transforms to occur. + "in order" processing of pending transforms ensures that all pending transforms have been applied to the + tensor before a non-lazy transform (or lazy transform that is executing non-lazily) is carried out. + It ensures that no operations will be added to a metatensors's apply_operations while there are outstanding + pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this + is expected to change in future releases. This method is designed to be used only in the context of implementing lazy resampling functionality. In general - you should not need to interact with or use this method directly. + you should not need to interact with or use this method directly, and its API may change without warning between + releases. + Args: transform: a transform that should be evaluated to determine whether pending transforms should be applied data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may @@ -184,46 +190,3 @@ def apply_pending_transforms_in_order( _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) return data - - -def apply_pending_transforms_out_of_order( - transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: str | None = None -): - """ - This method causes "out of order" processing of pending transforms to occur. - - Out of order processing for lazy resampling only causes pending transforms to be processed when - an `ApplyPending`_ or `ApplyPendingd`_ transform is encountered in the pipeline. - - This method is designed to be used only in the context of implementing lazy resampling functionality. In general - you should not need to interact with or use this method directly. - Args: - transform: a transform that should be evaluated to determine whether pending transforms should be applied - data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may - need to be applied - lazy: The lazy mode that is being applied (this can be False, True or None) - overrides: An optional dictionary containing overrides to be applied to the pending transforms when they - are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that - needs them - logger_name: An optional name for a logger to be used when applying pending transforms. If None, - logging is suppressed. - Returns: - an object of the same type as data if pending transforms were applied, or 'data' if they were not - - """ - apply_pending = False - keys = None - if lazy is False: - apply_pending = True - elif isinstance(transform, ApplyPending): - apply_pending = True - elif isinstance(transform, ApplyPendingd): - apply_pending = True - keys = transform.keys - - if apply_pending is True: - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, keys, overrides, logger_name) - - _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) - return data diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 8b4795f7e6..d780eb3269 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -50,9 +50,8 @@ def _apply_transform( data: Any, unpack_parameters: bool = False, lazy: bool | None = False, - lazy_strategy: str | None = "in_order", overrides: dict | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ) -> ReturnType: """ Perform a transform 'transform' on 'data', according to the other parameters specified. @@ -93,14 +92,9 @@ def _apply_transform( Returns: ReturnType: The return type of `transform`. """ - from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order + from monai.transforms.lazy.executors import apply_pending_transforms_in_order - if lazy_strategy == "in_order": - data = apply_pending_transforms_in_order(transform, data, lazy, overrides, logger_name) - elif lazy_strategy == "out_of_order": - data = apply_pending_transforms_out_of_order(transform, data, lazy, overrides, logger_name) - else: - raise ValueError(f"'lazy_strategy' must be one of {('in_order', 'out_of_order')} but is '{lazy_strategy}") + data = apply_pending_transforms_in_order(transform, data, lazy, overrides, logger_name) if isinstance(data, tuple) and unpack_parameters: return transform(*data, lazy=lazy) if isinstance(transform, LazyTrait) else transform(*data) @@ -113,10 +107,10 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, + log_stats: bool | str | None = False, lazy: bool | None = False, - lazy_strategy: str = "in_order", overrides: dict | None = None, - logger_name: str | None = None, + logger_name: bool | str | None = False, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -146,19 +140,19 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [ - _apply_transform(transform, item, unpack_items, lazy, lazy_strategy, overrides, logger_name) - for item in data - ] - return _apply_transform(transform, data, unpack_items, lazy, lazy_strategy, overrides, logger_name) + return [_apply_transform(transform, item, unpack_items, lazy, overrides, logger_name) for item in data] + return _apply_transform(transform, data, unpack_items, lazy, overrides, logger_name) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint # appears where the exception was raised. if MONAIEnvVars.debug(): raise - if logger_name is not None and not isinstance(transform, transforms.compose.Compose): + if log_stats is not False and not isinstance(transform, transforms.compose.Compose): # log the input data information of exact transform in the transform chain - datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False, name=logger_name) + log_stats_logger_name = log_stats if isinstance(log_stats, str) else None + datastats = transforms.utility.array.DataStats( + data_shape=False, value_range=False, name=log_stats_logger_name + ) logger = logging.getLogger(datastats._logger_name) logger.error(f"\n=== Transform input info -- {type(transform).__name__} ===") if isinstance(data, (list, tuple)): diff --git a/tests/test_compose.py b/tests/test_compose.py index 132d399fa6..10c97f9e61 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -23,7 +23,7 @@ import monai.transforms as mt from monai.data import DataLoader, Dataset -from monai.transforms.compose import ExecutionOptions, execute_compose +from monai.transforms.compose import execute_compose from monai.transforms.transform import Randomizable from monai.utils import set_determinism @@ -657,135 +657,5 @@ def data_from_keys(keys): self.assertTrue(expected, actual) -TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES = [ - [(mt.Flip(0), mt.Flip(1), mt.Rotate90(1), mt.Zoom(0.8), mt.NormalizeIntensity())] -] - - -class TestLazyComposePipelineFixes(unittest.TestCase): - @parameterized.expand(TEST_LAZY_COMPOSE_PIPELINE_FIX_CASES) - def test_lazy_compose_pipeline_fixes(self, pipeline): - data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) - c = mt.Compose(deepcopy(pipeline), lazy=True) - _ = c(data) - - -class TNonLazy(mt.Transform): - def __init__(self, tag): - self.tag = tag - - def __call__(self, data): - return data - - -class TLazy(mt.LazyTransform): - def __init__(self, tag, lazy): - super().__init__(lazy) - self.tag = tag - - def __call__(self, data): - return data - - -class TApplyPending(mt.ApplyPending): - def __init__(self, tag): - super().__init__() - self.tag = tag - - -TRANSFORM_REORDERING_TEST_CASES = [ - ( - [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["a", "lb", "lc", "ad", "f", "le"], - ["a", "lb", "lc", "ad", "f", "le"], - ), - ( - [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", False), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["a", "lb", "lc", "ad", "le", "f"], - ["a", "lb", "lc", "ad", "f", "le"], - ), - ( - [TLazy("la", True), TNonLazy("b"), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["b", "la", "lc", "ad", "f", "le"], - ["b", "la", "lc", "ad", "f", "le"], - ), - ( - [TLazy("la", False), TNonLazy("b"), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["la", "b", "lc", "ad", "f", "le"], - ["b", "la", "lc", "ad", "f", "le"], - ), - ( - [TLazy("la", True), TNonLazy("b"), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["b", "la", "lc", "ad", "f", "le"], - ["b", "la", "lc", "ad", "f", "le"], - ), - ( - [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", False), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["a", "lb", "lc", "ad", "le", "f"], - ["a", "lb", "lc", "ad", "f", "le"], - ), - ( - [TLazy("la", True), TLazy("lb", True), TNonLazy("c"), TApplyPending("ad"), TApplyPending("ae"), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["c", "la", "lb", "ad", "ae", "f"], - ["c", "la", "lb", "ad", "ae", "f"], - ), - ( - [TNonLazy("a"), TLazy("lb", True), TLazy("lc", True), TApplyPending("ad"), TLazy("le", True), TNonLazy("f")], - {"reorder": "lazy_last"}, - ["a", "lb", "lc", "ad", "f", "le"], - ["a", "lb", "lc", "ad", "f", "le"], - ), - ( - [ - TNonLazy("a"), - TLazy("lb", True), - TLazy("lc", True), - TApplyPending("ad"), - TLazy("le", True), - TApplyPending("af"), - TApplyPending("ag"), - ], - {"reorder": "lazy_last"}, - ["a", "lb", "lc", "ad", "le", "af", "ag"], - ["a", "lb", "lc", "ad", "le", "af", "ag"], - ), - ( - [TLazy("la", True), TLazy("lb", True), TNonLazy("c"), TLazy("ld", True)], - {"reorder": "lazy_last"}, - ["c", "la", "lb", "ld"], - ["c", "la", "lb", "ld"], - ), - ( - [TLazy("la", True), TLazy("lb", False), TNonLazy("c"), TLazy("ld", True)], - {"reorder": "lazy_last"}, - ["lb", "c", "la", "ld"], - ["c", "la", "lb", "ld"], - ), -] - - -class TestTransformReordering(unittest.TestCase): - @parameterized.expand(TRANSFORM_REORDERING_TEST_CASES) - def test_transform_reordering_test_cases(self, transforms, options, lazy_enabled_expected, lazy_on_expected): - with self.subTest("enable lazy"): - c = ExecutionOptions()(transforms, lazy=None, options={"reorder": "lazy_last"}) - reordered = [transforms[i] for i in c["indices"]] - actual = [t.tag for t in reordered] - self.assertListEqual(actual, lazy_enabled_expected) - - with self.subTest("force lazy"): - c = ExecutionOptions()(transforms, lazy=True, options={"reorder": "lazy_last"}) - reordered = [transforms[i] for i in c["indices"]] - actual = [t.tag for t in reordered] - self.assertListEqual(actual, lazy_on_expected) - - if __name__ == "__main__": unittest.main() diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 6afb756748..5a3e9dd3b1 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -33,9 +33,7 @@ def _no_op(x): return x -def run_training_test( - root_dir, device="cuda:0", cachedataset=0, readers=(None, None), num_workers=4, lazy=True, options=None -): +def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, None), num_workers=4, lazy=True): print(f"test case: {locals()}") images = sorted(glob(os.path.join(root_dir, "img*.nii.gz"))) segs = sorted(glob(os.path.join(root_dir, "seg*.nii.gz"))) @@ -78,7 +76,6 @@ def run_training_test( mt.Lambdad(keys=["img"], func=_no_op), ], lazy=lazy, - options=options, overrides=lazy_kwargs, ) @@ -157,11 +154,7 @@ def run_training_test( saver(in_img) saver(in_seg) invertible, reasons = is_tensor_invertible(batch_data) - if options == {"reorder": "lazy_last_nosync"}: - assert invertible is False, f"the output of this pipeline with options {options} should not be invertible" - else: - assert invertible is True, f"the output of this pipeline with options {options} should be invertible" - inverted = [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error + inverted = [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error return ops @@ -201,30 +194,23 @@ def train_and_infer(self, idx=0): results_expected = run_training_test( self.data_dir, device=self.device, cachedataset=0, readers=_readers, num_workers=_w, lazy=False ) - for options in (None, {"reorder": "lazy_last_nosync"}): - results = run_training_test( - self.data_dir, - device=self.device, - cachedataset=idx, - readers=_readers, - num_workers=_w, - lazy=True, - options=options, - ) - self.assertFalse(np.allclose(results, [0])) - self.assertFalse(np.allclose(results_expected, [0])) - np.testing.assert_allclose(results, results_expected) - lazy_files = glob(os.path.join(self.data_dir, "output", "*_True_*.nii.gz")) - regular_files = glob(os.path.join(self.data_dir, "output", "*_False_*.nii.gz")) - diffs = [] - for a, b in zip(sorted(lazy_files), sorted(regular_files)): - img_lazy = mt.LoadImage(image_only=True)(a) - img_regular = mt.LoadImage(image_only=True)(b) - diff = np.size(img_lazy) - np.sum(np.isclose(img_lazy, img_regular, atol=1e-4)) - diff_rate = diff / np.size(img_lazy) - diffs.append(diff_rate) - np.testing.assert_allclose(diff_rate, 0.0, atol=0.03) - print("volume diff:", diffs) + results = run_training_test( + self.data_dir, device=self.device, cachedataset=idx, readers=_readers, num_workers=_w, lazy=True + ) + self.assertFalse(np.allclose(results, [0])) + self.assertFalse(np.allclose(results_expected, [0])) + np.testing.assert_allclose(results, results_expected) + lazy_files = glob(os.path.join(self.data_dir, "output", "*_True_*.nii.gz")) + regular_files = glob(os.path.join(self.data_dir, "output", "*_False_*.nii.gz")) + diffs = [] + for a, b in zip(sorted(lazy_files), sorted(regular_files)): + img_lazy = mt.LoadImage(image_only=True)(a) + img_regular = mt.LoadImage(image_only=True)(b) + diff = np.size(img_lazy) - np.sum(np.isclose(img_lazy, img_regular, atol=1e-4)) + diff_rate = diff / np.size(img_lazy) + diffs.append(diff_rate) + np.testing.assert_allclose(diff_rate, 0.0, atol=0.03) + print("volume diff:", diffs) def test_training(self): for i in range(4): From ba33784a6183e7ea524d7f63c04f00ea31d148fb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 07:45:57 +0000 Subject: [PATCH 119/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/compose.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index af56531d7d..7b9402464f 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -25,12 +25,10 @@ from monai.apps.utils import get_logger from monai.config import NdarrayOrTensor from monai.transforms.inverse import InvertibleTransform -from monai.transforms.lazy.array import ApplyPending -from monai.transforms.lazy.dictionary import ApplyPendingd # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) from monai.transforms.lazy.executors import apply_pending_transforms -from monai.transforms.traits import LazyTrait, ThreadUnsafe +from monai.transforms.traits import ThreadUnsafe from monai.transforms.transform import ( # noqa: F401 LazyTransform, MapTransform, @@ -363,7 +361,6 @@ def __len__(self): return len(self.flatten().transforms) def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): - lazy_ = self.lazy if lazy is None else lazy result = execute_compose( input_, From ab47030c74a493c832a971740296c45ea17eacc2 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 09:52:04 +0100 Subject: [PATCH 120/175] Fixing isort complaint that doesn't show up when running --autofix on windows Signed-off-by: Ben Murray --- monai/transforms/compose.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 7b9402464f..2914cac964 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -361,7 +361,6 @@ def __len__(self): return len(self.flatten().transforms) def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): - result = execute_compose( input_, transforms=self.transforms, From f5d332b9b50d680170b50b4654ec220584f75527 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 10:41:03 +0100 Subject: [PATCH 121/175] Homogenizing initialiser values for OneOf, RandomOrder, SomeOf wrt laziness --- monai/transforms/compose.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 2914cac964..e753deb32e 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -450,11 +450,11 @@ def __init__( map_items: bool = True, unpack_items: bool = False, log_stats: bool | str | None = False, - lazy: bool | None = None, + lazy: bool | None = False, overrides: dict | None = None, logger_name: bool | str | None = False, ) -> None: - super().__init__(transforms, map_items, unpack_items, lazy, overrides) + super().__init__(transforms, map_items, unpack_items, log_stats, lazy, overrides, logger_name) if len(self.transforms) == 0: weights = [] elif weights is None or isinstance(weights, float): @@ -586,15 +586,15 @@ def __init__( map_items: bool = True, unpack_items: bool = False, log_stats: bool | str | None = False, - lazy: bool | None = None, + lazy: bool | None = False, overrides: dict | None = None, logger_name: bool | str | None = False, ) -> None: - super().__init__(transforms, map_items, unpack_items, lazy, overrides) + super().__init__(transforms, map_items, unpack_items, log_stats, lazy, overrides, logger_name) self.log_stats = log_stats self.logger_name = logger_name - def __call__(self, input_, start=0, end=None, threading=False, lazy: str | bool | None = None): + def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): if start != 0: raise ValueError(f"RandomOrder requires 'start' parameter to be 0 (start set to {start})") if end is not None: @@ -690,12 +690,13 @@ def __init__( unpack_items: bool = False, log_stats: bool | str | None = False, *, + lazy: bool | None = False, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, logger_name: bool | str | None = False, ) -> None: - super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) + super().__init__(transforms, map_items, unpack_items, log_stats=log_stats, lazy=lazy, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) @@ -754,7 +755,7 @@ def _normalize_probabilities(self, weights): return ensure_tuple(list(weights)) - def __call__(self, data, start=0, end=None, threading=False, lazy: str | bool | None = None): + def __call__(self, data, start=0, end=None, threading=False, lazy: bool | None = None): if start != 0: raise ValueError(f"SomeOf requires 'start' parameter to be 0 (start set to {start})") if end is not None: From 5406a2ba32f33a5e7e17abf379c1649dd78360f1 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 10:47:51 +0100 Subject: [PATCH 122/175] DCO Remediation Commit for Ben Murray I, Ben Murray , hereby add my Signed-off-by to this commit: f5d332b9b50d680170b50b4654ec220584f75527 Signed-off-by: Ben Murray --- monai/transforms/compose.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index e753deb32e..77b5a286d5 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -246,6 +246,9 @@ class Compose(Randomizable, InvertibleTransform): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged lazy: whether to enable lazy evaluation for lazy transforms. This is an optional bool that can take the following values. If lazy=False, lazy execution is disabled and transforms will be carried out on a transform by transform basis. If lazy=True, all lazy transforms will @@ -427,6 +430,9 @@ class OneOf(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be carried out on a transform by transform basis. @@ -564,6 +570,9 @@ class RandomOrder(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be carried out on a transform by transform basis. @@ -675,6 +684,9 @@ class SomeOf(Compose): Defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. Defaults to `False`. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged num_transforms: a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. None sets it to `len(transforms)`. Default to `None`. From 0ce6fa9698082427bbf1418564acdd0c46ae883a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 12:47:25 +0100 Subject: [PATCH 123/175] Simplify types for log_stats and logger_name Signed-off-by: Ben Murray --- monai/transforms/compose.py | 20 ++++++++++---------- monai/transforms/lazy/executors.py | 10 +++++----- monai/transforms/transform.py | 16 +++++++++------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 2914cac964..cbb890f1c7 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -55,8 +55,8 @@ def execute_compose( lazy: bool | None = False, overrides: dict | None = None, threading: bool = False, - log_stats: bool | str | None = False, - logger_name: bool | str | None = False, + log_stats: bool | str = False, + logger_name: bool | str = False, ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -269,10 +269,10 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool | str | None = False, + log_stats: bool | str = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ) -> None: if transforms is None: transforms = [] @@ -449,10 +449,10 @@ def __init__( weights: Sequence[float] | float | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool | str | None = False, + log_stats: bool | str = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) if len(self.transforms) == 0: @@ -585,10 +585,10 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool | str | None = False, + log_stats: bool | str = False, lazy: bool | None = None, overrides: dict | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ) -> None: super().__init__(transforms, map_items, unpack_items, lazy, overrides) self.log_stats = log_stats @@ -688,12 +688,12 @@ def __init__( transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, - log_stats: bool | str | None = False, + log_stats: bool | str = False, *, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ) -> None: super().__init__(transforms, map_items, unpack_items, logger_name=logger_name) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 86b5ffde3f..a106bf8942 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -32,7 +32,7 @@ def _log_pending_info( *, lazy: bool | None = None, key: str | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ): if logger_name is False: return @@ -69,7 +69,7 @@ def _log_pending_info( ) -def _log_applied_info(data: Any, key=None, logger_name: bool | str | None = False): +def _log_applied_info(data: Any, key=None, logger_name: bool | str = False): if logger_name is False: return logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" @@ -80,10 +80,10 @@ def _log_applied_info(data: Any, key=None, logger_name: bool | str | None = Fals def apply_pending_transforms( - data: NdarrayOrTensor | Sequence[Any, NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], + data: NdarrayOrTensor | Sequence[Any | NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], keys: tuple | None, overrides: dict | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ): """ apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain @@ -141,7 +141,7 @@ def apply_pending_transforms( def apply_pending_transforms_in_order( - transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str | None = False + transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str = False ): """ This method causes "in order" processing of pending transforms to occur. diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index d780eb3269..f5e91a866d 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -51,7 +51,7 @@ def _apply_transform( unpack_parameters: bool = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ) -> ReturnType: """ Perform a transform 'transform' on 'data', according to the other parameters specified. @@ -107,10 +107,10 @@ def apply_transform( data: Any, map_items: bool = True, unpack_items: bool = False, - log_stats: bool | str | None = False, + log_stats: bool | str = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str | None = False, + logger_name: bool | str = False, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -149,10 +149,12 @@ def apply_transform( raise if log_stats is not False and not isinstance(transform, transforms.compose.Compose): # log the input data information of exact transform in the transform chain - log_stats_logger_name = log_stats if isinstance(log_stats, str) else None - datastats = transforms.utility.array.DataStats( - data_shape=False, value_range=False, name=log_stats_logger_name - ) + if isinstance(log_stats, str): + datastats = transforms.utility.array.DataStats( + data_shape=False, value_range=False, name=log_stats + ) + else: + datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False) logger = logging.getLogger(datastats._logger_name) logger.error(f"\n=== Transform input info -- {type(transform).__name__} ===") if isinstance(data, (list, tuple)): From 0cf616ade35cec76fe178e043e3d43f2c1aeefd2 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 12:56:23 +0100 Subject: [PATCH 124/175] Docstring fixes Signed-off-by: Ben Murray --- monai/transforms/compose.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 0e93062515..72c9fa08e9 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -86,8 +86,12 @@ def execute_compose( please see also :py:func:`monai.transforms.lazy.apply_pending` and ``Compose`` for more details. threading: whether executing is happening in a threaded environment. If set, copies are made of transforms that have the ``RandomizedTrait`` interface. - logger_name: The name of the logger that should be used during transform execution. If None, logging is - suppressed. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged + logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. Returns: A tensorlike, sequence of tensorlikes or dict of tensorlists containing the result of running @@ -247,7 +251,7 @@ class Compose(Randomizable, InvertibleTransform): unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + disables the logger for processing pipeline errors. Setting it to True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged lazy: whether to enable lazy evaluation for lazy transforms. This is an optional bool that can take the following values. If lazy=False, lazy execution is disabled and transforms will be @@ -263,8 +267,9 @@ class Compose(Randomizable, InvertibleTransform): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - logger_name: this optional parameter allows you to specify a logger by name. If this is not set - it defaults to 'Compose'. You can also suppress logging by setting this to None. + logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -445,8 +450,9 @@ class OneOf(Compose): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - logger_name: this optional parameter allows you to specify a logger by name. If this is not set - it defaults to 'OneOf'. You can also suppress logging by setting this to None. + logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -571,7 +577,7 @@ class RandomOrder(Compose): unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + disables the logger for processing pipeline errors. Setting it to True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If False, transforms will be @@ -585,8 +591,9 @@ class RandomOrder(Compose): currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. - logger_name: this optional parameter allows you to specify a logger by name. If this is not set - it defaults to 'Compose'. You can also suppress logging by setting this to None. + logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -685,14 +692,16 @@ class SomeOf(Compose): unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. Defaults to `False`. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + disables the logger for processing pipeline errors. Setting it to True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged num_transforms: a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. None sets it to `len(transforms)`. Default to `None`. replace: whether to sample with replacement. Defaults to `False`. weights: weights to use in for sampling transforms. Will be normalized to 1. Default: None (uniform). - logger_name: the name of the logger to use when logging output. + logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. """ def __init__( From 8ffae00dd9bce7fd37ad247db87408840759df74 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Mon, 22 May 2023 17:33:15 +0100 Subject: [PATCH 125/175] monai/transforms/transform.py Signed-off-by: Ben Murray --- monai/transforms/transform.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index f5e91a866d..be2b8a0ad8 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -150,9 +150,7 @@ def apply_transform( if log_stats is not False and not isinstance(transform, transforms.compose.Compose): # log the input data information of exact transform in the transform chain if isinstance(log_stats, str): - datastats = transforms.utility.array.DataStats( - data_shape=False, value_range=False, name=log_stats - ) + datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False, name=log_stats) else: datastats = transforms.utility.array.DataStats(data_shape=False, value_range=False) logger = logging.getLogger(datastats._logger_name) From 1c19c31545172337c31a4fc2d4948f8603369e54 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 23 May 2023 23:54:02 +0100 Subject: [PATCH 126/175] Resolving circular import issue with Compose and utils in a way that docs are happy with Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- monai/transforms/utils.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 72c9fa08e9..aa5260a60c 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -37,7 +37,6 @@ Transform, apply_transform, ) -from monai.transforms.utils import is_tensor_invertible from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed logger = get_logger(__name__) @@ -412,6 +411,7 @@ def inverse(self, data): @staticmethod def _raise_if_tensor_is_not_invertible(data: Any): + from monai.transforms.utils import is_tensor_invertible invertible, reasons = is_tensor_invertible(data) if invertible is False: diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 0720eadcd7..1ddcd02653 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -18,7 +18,7 @@ from contextlib import contextmanager from functools import lru_cache, wraps from inspect import getmembers, isclass -from typing import TYPE_CHECKING, Any +from typing import Any import numpy as np import torch @@ -29,7 +29,7 @@ from monai.networks.layers import GaussianFilter from monai.networks.utils import meshgrid_ij -# from monai.transforms.compose import Compose, OneOf +from monai.transforms.compose import Compose from monai.transforms.transform import MapTransform, Transform, apply_transform from monai.transforms.utils_pytorch_numpy_unification import ( any_np_pt, @@ -68,9 +68,6 @@ from monai.utils.enums import TransformBackends from monai.utils.type_conversion import convert_data_type, convert_to_cupy, convert_to_dst_type, convert_to_tensor -if TYPE_CHECKING: - from monai.transforms.compose import Compose - measure, has_measure = optional_import("skimage.measure", "0.14.2", min_version) morphology, has_morphology = optional_import("skimage.morphology") ndimage, _ = optional_import("scipy.ndimage") From 4670f08d114bd8427aa14c6322ecda194c50109e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 23 May 2023 23:55:48 +0100 Subject: [PATCH 127/175] Work towards lazy resampling docs Signed-off-by: Ben Murray --- .../lazy_resampling_homogeneous_matrices.svg | 1 + .../images/lazy_resampling_lazy_example_1.svg | 1 + .../images/lazy_resampling_trad_example_1.svg | 1 + docs/source/lazy_resampling.rst | 152 +++++++++++------- 4 files changed, 99 insertions(+), 56 deletions(-) create mode 100644 docs/images/lazy_resampling_homogeneous_matrices.svg create mode 100644 docs/images/lazy_resampling_lazy_example_1.svg create mode 100644 docs/images/lazy_resampling_trad_example_1.svg diff --git a/docs/images/lazy_resampling_homogeneous_matrices.svg b/docs/images/lazy_resampling_homogeneous_matrices.svg new file mode 100644 index 0000000000..ad74471877 --- /dev/null +++ b/docs/images/lazy_resampling_homogeneous_matrices.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/lazy_resampling_lazy_example_1.svg b/docs/images/lazy_resampling_lazy_example_1.svg new file mode 100644 index 0000000000..5d35e1a551 --- /dev/null +++ b/docs/images/lazy_resampling_lazy_example_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/lazy_resampling_trad_example_1.svg b/docs/images/lazy_resampling_trad_example_1.svg new file mode 100644 index 0000000000..b38621ab1b --- /dev/null +++ b/docs/images/lazy_resampling_trad_example_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index f2e9911e3f..f9b44b2c49 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -4,10 +4,10 @@ Lazy Resampling =============== .. toctree:: - : maxdepth: 2 + :maxdepth: 2 - mb_specification - config_syntax.md + mb_specification + config_syntax.md Introduction ^^^^^^^^^^^^ @@ -15,28 +15,37 @@ Introduction Lazy Resampling is a new feature for MONAI 1.2. This feature is still experimental at this time and it is possible that behaviour and APIs will change in upcoming releases. -Lazy resampling is a feature that can be used to improve preprocessing pipelines in the following ways: - * it can improve pipeline execution time - * it can improve pipeline memory usage - * it can improve image and segmentation quality by reducing incidental noise caused by resampling +Lazy resamping reworks the way that preprocessing is performed. It improves upon standard preprocessing pipelines and +can provide significant benefits over traditional preprocessing. It can improve: +* pipeline execution time +* pipeline memory usage in CPU or GPU +* image and segmentation quality by reducing incidental noise and artifacts caused by resampling + +The way it does this is by adopting the methods used in computer graphics pipelines, in which transformations to objects +in a scene are modified by composing together a sequence of "homogeneous matrices". + +Rather than each transform being executed in isolation, potentially requiring the data to be resampled to make a new +tensor, transforms whose operations can be described in terms of homogeneous transforms do not execute their transforms +immediately. Instead, they create a "pending operation", which is added to a list of operations that will be fused +together and carried out at the point that they are required. + How Lazy Resampling changes preprocessing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In order to understand how lazy resampling changes preprocessing, we'll first discuss standard processing pipeline -behaviour, and then compare it with the way lazy resampling works. +In order to understand the difference between traditional pipelines and lazy pipelines, it is best to look at an example +pipeline and the differences between their execution strategies: -Traditional resampling pipelines -++++++++++++++++++++++++++++++++ + +Traditional execution ++++++++++++++++++++++ With traditional resampling, found both in MONAI and many other preprocessing libraries, you typically define a sequence -of transforms and pass them to a ``Compose`` object, such as `monai.transforms.compose.Compose`_. +of transforms and pass them to a ``Compose`` object, such as :class:`monai.transforms.compose.Compose`. Example:: transforms = [ - LoadImaged(keys=["img", "seg"], ...), - EnsureChannelFirstd(keys=["img", "seg"], ...), Spacingd(keys=["img", "seg"], ...), Orientationd(keys=["img", "seg"], ...), RandSpatialCropd(keys=["img", "seg"], ...), @@ -45,60 +54,91 @@ Example:: RandZoomd(keys=["img", "seg"], ...), RandGaussianNoised(keys="img", ...), ] - compose = Compose(transforms) + pipeline = Compose(transforms) # elsewhere this will be called many times (such as in a Dataset instance) - outputs = compose(inputs) -:: - -The following will then happen when we call ``compose(inputs)``: - -1. ``LoadImaged`` is called with its inputs (a dictionary of strings containing file locations). This loads and - returns a dictionary of the corresponding data samples -2. ``EnsureChannelFirstd`` is called with the dictionary of data samples and adds a channel so that they have the - appropriate shape for the rest of the pipeline -3. ``Spacingd`` is called and reinterpolates the data samples -4. ``Orientationd`` permutes the data samples so that their spatial dimensions are reorganised -5. ``RandSpatialCropd`` crops a random patch of the data samples, throwing away the rest of the data in the process -6. ``RandRotate90d`` has a chance of performing a tensor-based rotation of the data samples -7. ``RandRotated`` has a chance of performing a full resample of the data samples -8. ``RandZoomd`` has a chance of performing a reinterpolation of the data samples -9. ``RandGaussianNoised`` has a chance of adding noise to ``img`` - -Overall, there are up to three occasions where the data is either interpolated or resampled through spatial transforms. -Furthermore, the crop that occurs means that the output data samples might contain pixels for which there is data but -that show padding values, because the data was thrown away by ``RandSpatialCrop``. + outputs = pipeline(inputs) + + +The following will then happen when we call ``pipeline(inputs)``: + +1. ``Spacingd`` is called and interpolates the data samples +2. ``Orientationd`` permutes the data samples so that their spatial dimensions are reorganised +3. ``RandSpatialCropd`` crops a random patch of the data samples, throwing away the rest of the data in the process +4. ``RandRotate90d`` has a chance of performing a tensor-based rotation of the data samples +5. ``RandRotated`` has a chance of performing a full resample of the data samples +6. ``RandZoomd`` has a chance of performing a interpolation of the data samples +7. ``RandGaussianNoised`` has a chance of adding noise to ``img`` + +.. figure:: ../images/lazy_resampling_trad_example_1.svg + + Figure showing traditional pipeline execution. Tensors (the boxes in the main body of the image) are passed through + the pipeline, and the state of their `applied_operations` property is shown at each step. Tensors with a thick red + border have undergone some kind of resample operation at that stage. + +Overall, there are up to three occasions where the data is either interpolated or resampled through spatial transforms +(``Spaciald``, ``RandRotated`` and ``RandZoomd``). Furthermore, the crop that occurs means that the output data +samples might contain pixels for which there is data but that show padding values, because the data was thrown away by +``RandSpatialCrop``. Each of these operations takes time and memory, but, as we can see in the example above, also creates resampling -artifacts and can even destroy data in the resulting data samples (see `lazy resampling best practices`_ for examples). +artifacts and can even destroy data in the resulting data samples. -Lazy resampling pipelines -^^^^^^^^^^^^^^^^^^^^^^^^^ +Lazy execution +++++++++++++++ Lazy resampling works very differently. When you execute the same pipeline with `lazy=True`, the following happens: -1. ``LoadImaged`` behaves identically -2. ``EnsureChannelFirstd`` behaves identically -3. ``Spacingd`` is executing lazily. It puts a description of the operation that it wants to perform onto a list of +1. ``Spacingd`` is executed lazily. It puts a description of the operation that it wants to perform onto a list of pending operations -4. ``Orientationd`` is executing lazily. It adds a description of its own operation to the pending operation list so +2. ``Orientationd`` is executed lazily. It adds a description of its own operation to the pending operation list so now there are 2 pending operations -5. ``RandSpatialCropd`` is executing lazily. It adds a description of its own operation to the pending operation list - so now there are 3 pending operations -6. ``RandRotate90d`` is executing lazily. It adds a description of its own operation to the pending operation list - so now there are 4 pending operations -7. ``RandRotated`` is executing lazily. It adds a description of its own operation to the pending operation list - so now there are 5 pending operations -8. ``RandZoomd`` is executing lazily. It adds a description of its own operation to the pending operation list - so now there are 6 pending operations - 1. ``[Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, RandZoomd]`` are all on the pending - operations list but have yet to be carried out on the data -9. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their - descriptions are mathematically composited together, to determine the operation that results from all of them - being carried out. This is then applied in a single resample operation. Once that is done, ``RandGaussianNoised`` - operates on the resulting data +3. ``RandSpatialCropd`` is executedexecuting lazily. It adds a description of its own operation to the pending + operation list so now there are 3 pending operations +4. ``RandRotate90d`` is executedexecuting lazily. It adds a description of its own operation to the pending operation + list so now there are 4 pending operations +5. ``RandRotated`` is executedexecuting lazily. It adds a description of its own operation to the pending operation + list so now there are 5 pending operations +6. ``RandZoomd`` is executedexecuting lazily. It adds a description of its own operation to the pending operation + list so now there are 6 pending operations 1. [Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, + RandZoomd] are all on the pending operations list but have yet to be carried out on the data +7. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their + descriptions are mathematically composited together, to determine the operation that results from all of them being + carried out. This is then applied in a single resample operation. Once that is done, RandGaussianNoised operates on + the resulting data + +.. figure:: ../images/lazy_resampling_lazy_example_1.svg + + Figure showing lazy pipeline execution. We show the state of the `pending_operations` and `applied_operations` + properties of the tensor as it is processed by the pipeline. Thick red borders indicate some kind of resampling + operation has taken place at that step. Lazy resampling performs far fewer of these operations. The single resampling operation has less noise induced by resampling, as it only occurs once in this pipeline rather than three times in the traditional pipeline. More importantly, although the crop describes an operation to keep only a subset of the data sample, the crop is not performed until after the spatial transforms are completed, which means that all of the data sample that is within bounds is preserved and is part of the resulting output. + + +Composing homogeneous matrices +++++++++++++++++++++++++++++++ + +.. image:: ../images/lazy_resampling_homogeneous_matrices.svg + + +Although a full treatment of homogeneous matrices is outside the scope of this document, a brief overview of them is +useful to understand the mechanics of lazy resampling. Homogeneous matrices are used in computer graphics to describe +operations in cartesian space in a unified (homogeneous) fashion. Rotation, scaling, translation, and skewing are +amongst the operations that can be performed. Homogeneous matrices have the interesting property that they can be +composited together, thus describing the result of a sequence of operations. Note that ordering is important; +`scale -> rotate -> translation` gives a very different result to `translation -> rotate -> scale`. + +The ability to composite homogeneous matrices together allows a sequence of operations to be carried out as a single +operation, which is the key mechanism by which lazy resampling functions. + + +API changes +^^^^^^^^^^^ + +A number of new arguments have been added to existing properties, which we'll go over in detail here. In particular, +we'll focus on :class:`monai.transforms.compose.Compose` and :class:`monai.transforms.traits.LazyTrait`/ +:class:`monai.transforms.transform.LazyTransform` and the way that they interact with each other. From 0838f91222fec4354d81d84299ac5413cf8b7c7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 22:56:31 +0000 Subject: [PATCH 128/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/images/lazy_resampling_homogeneous_matrices.svg | 2 +- docs/images/lazy_resampling_lazy_example_1.svg | 2 +- docs/images/lazy_resampling_trad_example_1.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/images/lazy_resampling_homogeneous_matrices.svg b/docs/images/lazy_resampling_homogeneous_matrices.svg index ad74471877..2a20b120da 100644 --- a/docs/images/lazy_resampling_homogeneous_matrices.svg +++ b/docs/images/lazy_resampling_homogeneous_matrices.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/docs/images/lazy_resampling_lazy_example_1.svg b/docs/images/lazy_resampling_lazy_example_1.svg index 5d35e1a551..6554635809 100644 --- a/docs/images/lazy_resampling_lazy_example_1.svg +++ b/docs/images/lazy_resampling_lazy_example_1.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/docs/images/lazy_resampling_trad_example_1.svg b/docs/images/lazy_resampling_trad_example_1.svg index b38621ab1b..5d29bb08f2 100644 --- a/docs/images/lazy_resampling_trad_example_1.svg +++ b/docs/images/lazy_resampling_trad_example_1.svg @@ -1 +1 @@ - \ No newline at end of file + From f3f0dd13ef1127de8c28927c524673e2070df58f Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 00:08:27 +0100 Subject: [PATCH 129/175] autofix Signed-off-by: Ben Murray --- monai/transforms/compose.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index aa5260a60c..3817b7ccd6 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -412,6 +412,7 @@ def inverse(self, data): @staticmethod def _raise_if_tensor_is_not_invertible(data: Any): from monai.transforms.utils import is_tensor_invertible + invertible, reasons = is_tensor_invertible(data) if invertible is False: From ed835048d155b7564496e8c20d8abfc1ae51a98b Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 08:38:32 +0100 Subject: [PATCH 130/175] autofix Signed-off-by: Ben Murray --- monai/transforms/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 1ddcd02653..178b251ace 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -28,7 +28,6 @@ from monai.config.type_definitions import NdarrayOrTensor, NdarrayTensor from monai.networks.layers import GaussianFilter from monai.networks.utils import meshgrid_ij - from monai.transforms.compose import Compose from monai.transforms.transform import MapTransform, Transform, apply_transform from monai.transforms.utils_pytorch_numpy_unification import ( From 4abab4d6c132ad72fdcb121dd34a65ab3ffe620f Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 08:58:22 +0100 Subject: [PATCH 131/175] docstring fixes for is_tensor_invertible Signed-off-by: Ben Murray --- monai/transforms/utils.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 178b251ace..c9de6f495b 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -2006,15 +2006,18 @@ def check_applied_operations(entry: list | dict, status_key: str, default_messag def is_tensor_invertible(data: torch.Tensor): """ Checks whether a given tensor is invertible. The rules are as follows: - 1. If the tensor is not a MetaTensor, it is not invertible - 2. If the tensor is a MetaTensor but it has `TraceStatusKeys.PENDING_DURING_APPLY` in the `TraceKeys.STATUS` of any + + #. If the tensor is not a MetaTensor, it is not invertible + #. If the tensor is a MetaTensor but it has `TraceStatusKeys.PENDING_DURING_APPLY` in the `TraceKeys.STATUS` of any of its `applied_operations` it is not invertible - 3. Otherwise, it is invertible + #. Otherwise, it is invertible This function also accepts: - * dictionaries of tensors - * lists or tuples of tensors - * list or tuples of dictionaries of tensors + + * dictionaries of tensors + * lists or tuples of tensors + * list or tuples of dictionaries of tensors + In any of the above scenarios, it iterates through the collections and executes itself recursively until it is operating on tensors. From 4e92eb0d883c00d91123b7821c1f103fdb4181b2 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 09:40:42 +0100 Subject: [PATCH 132/175] lazy on init should have a default of False Signed-off-by: Ben Murray --- monai/transforms/compose.py | 99 +++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 55 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 3817b7ccd6..093ff27f66 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -73,16 +73,15 @@ def execute_compose( start: the index of the first transform to be executed. If not set, this defaults to 0 end: the index after the last transform to be exectued. If set, the transform at index-1 is the last transform that is executed. If this is not set, it defaults to len(transforms) - lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be + lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy - is True. If lazy is False they are ignored. - currently supported args are: - {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, - please see also :py:func:`monai.transforms.lazy.apply_pending` and ``Compose`` for more details. + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. threading: whether executing is happening in a threaded environment. If set, copies are made of transforms that have the ``RandomizedTrait`` interface. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which @@ -196,52 +195,24 @@ class Compose(Randomizable, InvertibleTransform): them are called on the labels. Lazy resampling: + Lazy resampling is an experimental feature introduced in 1.2. Its purpose is to reduce the number of resample operations that must be carried out when executing a pipeline of transforms. This can provide significant performance improvements in - terms of pipeline executing speed and memory usage, but can also significantly + terms of pipeline executing speed and memory usage, and can also significantly reduce the loss of information that occurs when performing a number of spatial resamples in succession. - Lazy resampling can be thought of as acting in a similar fashion to the `Affine` & `RandAffine` - transforms, in that they allow several spatial transform operations can be specified and carried out with - a single resample step. Unlike these transforms, however, lazy resampling can operate on any set of - transforms specified in any ordering. The user is free to mix monai transforms with transforms from other - libraries; lazy resampling will determine the minimum number of resample steps required in order to - execute the pipeline. - - Lazy resampling works with monai `Dataset` classes that provide caching and persistence. However, if you - are implementing your own caching dataset implementation and wish to make use of lazy resampling, you - should ensure that you fully execute the part of the pipeline that generates the data to be cached - before caching it. This is quite simply done however, as shown by the following example. - - Lazy resampling can be enabled or disabled through the ``lazy`` parameter. This is specified as an - optional boolean parameter. + Lazy resampling can be enabled or disabled through the ``lazy`` parameter, either by + specifying it at initialisation time or overriding it at call time. * False (default): Don't perform any lazy resampling * None: Perform lazy resampling based on the 'lazy' properties of the transform instances. * True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties of the transform instances - If you only want some of the pipeline to be executed lazily, there are two ways to achieve this. - - The first way is to set lazy=True on your Compose instance and specify for each transform whether you - want it to be lazily executed or not. - - The second way is to set lazy=True on your Compose instance and add ``ApplyPending`` or `ApplyPendingd` - transforms after the final transform in a sequence that you want to execute lazily. This can be done at multiple - points in the pipeline. - - Example: - # run the part of the pipeline that needs to be cached - data = self.transform(data, end=self.post_cache_index) - - # --- - - # fetch the data from the cache and run the rest of the pipeline - data = get_data_from_my_cache(data) - data = self.transform(data, start=self.post_cache_index) - + Please see the :ref:`Lazy Resampling` topic page for more details of this feature and examples + of its use. Args: transforms: sequence of callables. @@ -252,20 +223,16 @@ class Compose(Randomizable, InvertibleTransform): log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged - lazy: whether to enable lazy evaluation for lazy transforms. This is an optional bool that can take - the following values. If lazy=False, lazy execution is disabled and transforms will be - carried out on a transform by transform basis. If lazy=True, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. If lazy is None, - Compose will perform lazy execution on lazy transforms that have their lazy flag set to True. - A `monai.transforms.ApplyPending[d]` transform in the pipeline will trigger the evaluation of - the pending operations and make the primary data up-to-date. + lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be + carried out on a transform by transform basis. If True, all lazy transforms will + be executed by accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will + perform lazy execution on lazy transforms that have their `lazy` property set to True. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy - is True. If lazy is False they are ignored. - currently supported args are: - {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, - please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. Setting this to False disables logging. Setting it to True enables logging to the default loggers. Setting a string overrides the logger name to which logging is performed. @@ -463,7 +430,7 @@ def __init__( map_items: bool = True, unpack_items: bool = False, log_stats: bool | str = False, - lazy: bool | None = None, + lazy: bool | None = False, overrides: dict | None = None, logger_name: bool | str = False, ) -> None: @@ -577,6 +544,15 @@ class RandomOrder(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. + lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be + carried out on a transform by transform basis. If True, all lazy transforms will + be executed by accumulating changes and resampling as few times as possible. + overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden + when executing a pipeline. These each parameter that is compatible with a given transform is then applied + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged @@ -603,7 +579,7 @@ def __init__( map_items: bool = True, unpack_items: bool = False, log_stats: bool | str = False, - lazy: bool | None = None, + lazy: bool | None = False, overrides: dict | None = None, logger_name: bool | str = False, ) -> None: @@ -695,6 +671,15 @@ class SomeOf(Compose): log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged + lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be + carried out on a transform by transform basis. If True, all lazy transforms will + be executed by accumulating changes and resampling as few times as possible. + overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden + when executing a pipeline. These each parameter that is compatible with a given transform is then applied + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. num_transforms: a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. None sets it to `len(transforms)`. Default to `None`. @@ -713,12 +698,16 @@ def __init__( log_stats: bool | str = False, *, lazy: bool | None = False, + overrides: dict | None = None, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, logger_name: bool | str = False, ) -> None: - super().__init__(transforms, map_items, unpack_items, log_stats=log_stats, lazy=lazy, logger_name=logger_name) + super().__init__( + transforms, map_items, unpack_items, log_stats=log_stats, lazy=lazy, overrides=overrides, + logger_name=logger_name + ) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) From 0cfd098e5d398bc9819d480753cb41a13772306e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 09:41:16 +0100 Subject: [PATCH 133/175] Reworked text for Compose and subclasses for lazy resamping; added in refs to the topic page Signed-off-by: Ben Murray --- docs/source/index.rst | 1 + docs/source/lazy_resampling.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/source/index.rst b/docs/source/index.rst index 9f8c3cb7ec..2af6c6f6f9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -69,6 +69,7 @@ Technical documentation is available at `docs.monai.io `_ :caption: Specifications bundle_intro + lazy_resampling Model Zoo --------- diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index f9b44b2c49..344cd565b6 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -1,3 +1,5 @@ +.. _lazy resampling: + :github_url: https://github.com/Project-MONAI/MONAI Lazy Resampling From fe56297b4f37b72b1d26339bbce18b27df2e9729 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 10:04:29 +0100 Subject: [PATCH 134/175] autofix Signed-off-by: Ben Murray --- monai/transforms/compose.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 093ff27f66..5c6ab1b26e 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -705,8 +705,13 @@ def __init__( logger_name: bool | str = False, ) -> None: super().__init__( - transforms, map_items, unpack_items, log_stats=log_stats, lazy=lazy, overrides=overrides, - logger_name=logger_name + transforms, + map_items, + unpack_items, + log_stats=log_stats, + lazy=lazy, + overrides=overrides, + logger_name=logger_name, ) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace From a58b91d4bb1f8535f3c835a34dafb151c136228c Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 15:08:47 +0100 Subject: [PATCH 135/175] Updating docstrings for things that interact with lazy resampling Signed-off-by: Ben Murray --- monai/transforms/compose.py | 88 +++++++++++--------------- monai/transforms/croppad/array.py | 77 ++++++++++++++++++++-- monai/transforms/croppad/dictionary.py | 71 +++++++++++++++++++-- monai/transforms/lazy/executors.py | 2 +- monai/transforms/spatial/array.py | 56 +++++++++++++++- monai/transforms/spatial/dictionary.py | 55 +++++++++++++++- monai/transforms/transform.py | 30 ++++----- 7 files changed, 298 insertions(+), 81 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 5c6ab1b26e..6ee84af19f 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -86,7 +86,7 @@ def execute_compose( of transforms that have the ``RandomizedTrait`` interface. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged + default logger name. Setting it to a string specifies the logger to which errors should be logged. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. Setting this to False disables logging. Setting it to True enables logging to the default loggers. Setting a string overrides the logger name to which logging is performed. @@ -211,8 +211,8 @@ class Compose(Randomizable, InvertibleTransform): * True: Always perform lazy resampling if possible. This will ignore the ``lazy`` properties of the transform instances - Please see the :ref:`Lazy Resampling` topic page for more details of this feature and examples - of its use. + Please see the :ref:`Lazy Resampling topic` for more details of this feature + and examples of its use. Args: transforms: sequence of callables. @@ -222,15 +222,15 @@ class Compose(Randomizable, InvertibleTransform): defaults to `False`. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged - lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be - carried out on a transform by transform basis. If True, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will + default logger name. Setting it to a string specifies the logger to which errors should be logged. + lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will + be carried out on a transform by transform basis. If True, all lazy transforms will be executed by + accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will perform lazy execution on lazy transforms that have their `lazy` property set to True. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when - :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. @@ -405,19 +405,17 @@ class OneOf(Compose): defaults to `False`. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged - lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. If False, transforms will be - carried out on a transform by transform basis. - A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of - the pending operations and make the primary data up-to-date. + default logger name. Setting it to a string specifies the logger to which errors should be logged. + lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will + be carried out on a transform by transform basis. If True, all lazy transforms will be executed by + accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will + perform lazy execution on lazy transforms that have their `lazy` property set to True. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy - is True. If lazy is False they are ignored. - currently supported args are: - {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, - please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. Setting this to False disables logging. Setting it to True enables logging to the default loggers. Setting a string overrides the logger name to which logging is performed. @@ -544,30 +542,19 @@ class RandomOrder(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be - carried out on a transform by transform basis. If True, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged. + lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will + be carried out on a transform by transform basis. If True, all lazy transforms will be executed by + accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will + perform lazy execution on lazy transforms that have their `lazy` property set to True. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when - :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. - log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged - lazy: whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. If False, transforms will be - carried out on a transform by transform basis. - A `monai.transforms.Identity[D]` transform in the pipeline will trigger the evaluation of - the pending operations and make the primary data up-to-date. - overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden - when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy - is True. If lazy is False they are ignored. - currently supported args are: - {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, - please see also :py:func:`monai.transforms.lazy.apply_transforms` for more details. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. Setting this to False disables logging. Setting it to True enables logging to the default loggers. Setting a string overrides the logger name to which logging is performed. @@ -670,21 +657,22 @@ class SomeOf(Compose): Defaults to `False`. log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged - lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be - carried out on a transform by transform basis. If True, all lazy transforms will - be executed by accumulating changes and resampling as few times as possible. - overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden - when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when - :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False - they are ignored. Currently supported args are: - {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. + default logger name. Setting it to a string specifies the logger to which errors should be logged. num_transforms: a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. None sets it to `len(transforms)`. Default to `None`. replace: whether to sample with replacement. Defaults to `False`. weights: weights to use in for sampling transforms. Will be normalized to 1. Default: None (uniform). + lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will + be carried out on a transform by transform basis. If True, all lazy transforms will be executed by + accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will + perform lazy execution on lazy transforms that have their `lazy` property set to True. + overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden + when executing a pipeline. These each parameter that is compatible with a given transform is then applied + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. Setting this to False disables logging. Setting it to True enables logging to the default loggers. Setting a string overrides the logger name to which logging is performed. @@ -697,11 +685,11 @@ def __init__( unpack_items: bool = False, log_stats: bool | str = False, *, - lazy: bool | None = False, - overrides: dict | None = None, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, + lazy: bool | None = False, + overrides: dict | None = None, logger_name: bool | str = False, ) -> None: super().__init__( diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index a0243cd237..f4cbe43015 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -88,6 +88,9 @@ class Pad(InvertibleTransform, LazyTransform): `torch.nn.functional.pad` is used unless the mode or kwargs are not available in torch, in which case `np.pad` will be used. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: to_pad: the amount to pad in each dimension (including the channel) [(low_H, high_H), (low_W, high_W), ...]. if None, must provide in the `__call__` at runtime. @@ -98,6 +101,7 @@ class Pad(InvertibleTransform, LazyTransform): See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html requires pytorch >= 1.10 for best compatibility. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -147,6 +151,7 @@ def __call__( # type: ignore[override] One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html + lazy: a flag to override the lazy behaviour for this call, if set. Defaults to None. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -181,6 +186,9 @@ class SpatialPad(Pad): """ Performs padding to the data, symmetric for all sides or all on one side for each dimension. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_size: the spatial size of output data after padding, if a dimension of the input data size is larger than the pad size, will not pad that dimension. @@ -195,6 +203,7 @@ class SpatialPad(Pad): One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -235,6 +244,9 @@ class BorderPad(Pad): """ Pad the input data by adding specified borders to every dimension. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_border: specified size for every spatial border. Any -ve values will be set to 0. It can be 3 shapes: @@ -252,6 +264,7 @@ class BorderPad(Pad): One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -288,6 +301,9 @@ def compute_pad_width(self, spatial_shape: Sequence[int]) -> tuple[tuple[int, in class DivisiblePad(Pad): """ Pad the input data, so that the spatial sizes are divisible by `k`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = SpatialPad.backend @@ -313,6 +329,7 @@ def __init__( https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html method: {``"symmetric"``, ``"end"``} Pad image symmetrically on every side or only pad at the end sides. Defaults to ``"symmetric"``. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -332,9 +349,11 @@ class Crop(InvertibleTransform, LazyTransform): """ Perform crop operations on the input image. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: - lazy: a flag to indicate whether this transform should execute lazily or not. - Defaults to False + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ backend = [TransformBackends.TORCH] @@ -436,6 +455,9 @@ class SpatialCrop(Crop): - a list of slices for each spatial dimension (allows for use of negative indexing and `None`) - a spatial center and size - the start and end coordinates of the ROI + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ def __init__( @@ -456,6 +478,8 @@ def __init__( roi_end: voxel coordinates for end of the crop ROI, if a coordinate is out of image, use the end coordinate of image. roi_slices: list of slices for each of the spatial dimensions. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. + """ super().__init__(lazy) self.slices = self.compute_slices( @@ -479,12 +503,16 @@ class CenterSpatialCrop(Crop): So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: roi_size: the spatial size of the crop region e.g. [224,224,128] if a dimension of ROI size is larger than image size, will not crop that dimension of the image. If its components have non-positive values, the corresponding size of input image will be used. for example: if the spatial size of input data is [40, 40, 40] and `roi_size=[32, 64, -1]`, the spatial size of output data will be [32, 40, 40]. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ def __init__(self, roi_size: Sequence[int] | int, lazy: bool = False) -> None: @@ -514,10 +542,13 @@ class CenterScaleCrop(Crop): """ Crop at the center of image with specified scale of ROI size. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: roi_scale: specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5] or a number for all dims. If its components have non-positive values, will use `1.0` instead, which means the input image size. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ def __init__(self, roi_scale: Sequence[float] | float, lazy: bool = False): @@ -542,6 +573,9 @@ class RandSpatialCrop(Randomizable, Crop): will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: roi_size: if `random_size` is True, it specifies the minimum crop region. if `random_size` is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] @@ -555,6 +589,7 @@ class RandSpatialCrop(Randomizable, Crop): random_center: crop at random position as center or the image center. random_size: crop with random size or specific size ROI. if True, the actual size is sampled from `randint(roi_size, max_roi_size + 1)`. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ @deprecated_arg_default("random_size", True, False, since="1.1", replaced="1.3") @@ -610,6 +645,9 @@ class RandScaleCrop(RandSpatialCrop): center or at the image center. And allows to set the minimum and maximum scale of image size to limit the randomly generated ROI. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: roi_scale: if `random_size` is True, it specifies the minimum crop size: `roi_scale * image spatial size`. if `random_size` is False, it specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5]. @@ -622,6 +660,7 @@ class RandScaleCrop(RandSpatialCrop): random_size: crop with random size or specified size ROI by `roi_scale * image spatial size`. if True, the actual size is sampled from `randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1)`. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ @deprecated_arg_default("random_size", True, False, since="1.1", replaced="1.3") @@ -673,6 +712,9 @@ class RandSpatialCropSamples(Randomizable, TraceableTransform, LazyTransform, Mu will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: roi_size: if `random_size` is True, it specifies the minimum crop region. if `random_size` is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] @@ -687,6 +729,7 @@ class RandSpatialCropSamples(Randomizable, TraceableTransform, LazyTransform, Mu random_center: crop at random position as center or the image center. random_size: crop with random size or specific size ROI. The actual size is sampled from `randint(roi_size, img_size)`. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. Raises: ValueError: When ``num_samples`` is nonpositive. @@ -772,6 +815,9 @@ def threshold_at_one(x): [3, 2], [2, 1]]] + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + """ def __init__( @@ -804,6 +850,7 @@ def __init__( One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. pad_kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -919,6 +966,9 @@ class RandWeightedCrop(Randomizable, TraceableTransform, LazyTransform, MultiSam """ Samples a list of `num_samples` image patches according to the provided `weight_map`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_size: the spatial size of the image patch e.g. [224, 224, 128]. If its components have non-positive values, the corresponding size of `img` will be used. @@ -926,6 +976,7 @@ class RandWeightedCrop(Randomizable, TraceableTransform, LazyTransform, MultiSam weight_map: weight map used to generate patch samples. The weights must be non-negative. Each element denotes a sampling weight of the spatial location. 0 indicates no sampling. It should be a single-channel array in shape, for example, `(1, spatial_dim_0, spatial_dim_1, ...)`. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ backend = SpatialCrop.backend @@ -966,6 +1017,7 @@ def __call__( Each element denotes a sampling weight of the spatial location. 0 indicates no sampling. It should be a single-channel array in shape, for example, `(1, spatial_dim_0, spatial_dim_1, ...)` randomize: whether to execute random operations, default to `True`. + lazy: a flag to override the lazy behaviour for this call, if set. Defaults to None. Returns: A list of image patches @@ -1016,6 +1068,9 @@ class RandCropByPosNegLabel(Randomizable, TraceableTransform, LazyTransform, Mul And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the valid crop ROI. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_size: the spatial size of the crop region e.g. [224, 224, 128]. if a dimension of ROI size is larger than image size, will not crop that dimension of the image. @@ -1045,6 +1100,7 @@ class RandCropByPosNegLabel(Randomizable, TraceableTransform, LazyTransform, Mul allow_smaller: if `False`, an exception will be raised if the image is smaller than the requested ROI in any dimension. If `True`, any smaller dimensions will be set to match the cropped size (i.e., no cropping in that dimension). + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. Raises: ValueError: When ``pos`` or ``neg`` are negative. @@ -1146,6 +1202,7 @@ def __call__( bg_indices: background indices to randomly select crop centers, need to provide `fg_indices` and `bg_indices` together. randomize: whether to execute the random operations, default to `True`. + lazy: a flag to override the lazy behaviour for this call, if set. Defaults to None. """ if image is None: @@ -1210,6 +1267,9 @@ class RandCropByLabelClasses(Randomizable, TraceableTransform, LazyTransform, Mu And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the valid crop ROI. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_size: the spatial size of the crop region e.g. [224, 224, 128]. if a dimension of ROI size is larger than image size, will not crop that dimension of the image. @@ -1235,7 +1295,7 @@ class RandCropByLabelClasses(Randomizable, TraceableTransform, LazyTransform, Mu warn: if `True` prints a warning if a class is not present in the label. max_samples_per_class: maximum length of indices to sample in each class to reduce memory consumption. Default is None, no subsampling. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ backend = SpatialCrop.backend @@ -1319,7 +1379,7 @@ def __call__( use ``image > image_threshold`` to select the centers only in valid region. if None, use `self.image`. indices: list of indices for every class in the image, used to randomly select crop centers. randomize: whether to execute the random operations, default to `True`. - + lazy: a flag to override the lazy behaviour for this call, if set. Defaults to None. """ if image is None: image = self.image @@ -1352,6 +1412,9 @@ class ResizeWithPadOrCrop(InvertibleTransform, LazyTransform): When the dimension is smaller than the target size, do symmetric padding along that dim. When the dimension is larger than the target size, do central cropping along that dim. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_size: the spatial size of output data after padding or crop. If has non-positive values, the corresponding size of input image will be used (no padding). @@ -1365,6 +1428,7 @@ class ResizeWithPadOrCrop(InvertibleTransform, LazyTransform): https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html pad_kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ @@ -1401,7 +1465,8 @@ def __call__( # type: ignore[override] One of the listed string values or a user supplied function. Defaults to ``"constant"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html - kwargs: other arguments for the `np.pad` or `torch.pad` function. + lazy: a flag to override the lazy behaviour for this call, if set. Defaults to None. + pad_kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. """ diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 0ea6dc3442..3a7bef414c 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -114,6 +114,8 @@ class Padd(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Pad`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Pad.backend @@ -139,7 +141,7 @@ def __init__( https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html It also can be a sequence of string, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ MapTransform.__init__(self, keys, allow_missing_keys) LazyTransform.__init__(self, lazy) @@ -181,6 +183,8 @@ class SpatialPadd(Padd): Dictionary-based wrapper of :py:class:`monai.transforms.SpatialPad`. Performs padding to the data, symmetric for all sides or all on one side for each dimension. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ def __init__( @@ -212,6 +216,7 @@ def __init__( https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html It also can be a sequence of string, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -225,6 +230,9 @@ class BorderPadd(Padd): """ Pad the input data by adding specified borders to every dimension. Dictionary-based wrapper of :py:class:`monai.transforms.BorderPad`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = BorderPad.backend @@ -261,6 +269,7 @@ def __init__( https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html It also can be a sequence of string, each element corresponds to a key in ``keys``. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -274,6 +283,9 @@ class DivisiblePadd(Padd): """ Pad the input data, so that the spatial sizes are divisible by `k`. Dictionary-based wrapper of :py:class:`monai.transforms.DivisiblePad`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = DivisiblePad.backend @@ -305,6 +317,7 @@ def __init__( method: {``"symmetric"``, ``"end"``} Pad image symmetrically on every side or only pad at the end sides. Defaults to ``"symmetric"``. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -320,12 +333,15 @@ class Cropd(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of abstract class :py:class:`monai.transforms.Crop`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` cropper: crop transform for the input image. allow_missing_keys: don't raise exception if key is missing. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ backend = Crop.backend @@ -359,12 +375,15 @@ class RandCropd(Cropd, Randomizable): """ Base class for random crop transform. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` cropper: random crop transform for the input image. allow_missing_keys: don't raise exception if key is missing. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ backend = Crop.backend @@ -414,6 +433,9 @@ class SpatialCropd(Cropd): - a list of slices for each spatial dimension (allows for use of -ve indexing and `None`) - a spatial center and size - the start and end coordinates of the ROI + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ def __init__( @@ -439,7 +461,7 @@ def __init__( use the end coordinate of image. roi_slices: list of slices for each of the spatial dimensions. allow_missing_keys: don't raise exception if key is missing. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ cropper = SpatialCrop(roi_center, roi_size, roi_start, roi_end, roi_slices, lazy=lazy) super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys, lazy=lazy) @@ -452,6 +474,9 @@ class CenterSpatialCropd(Cropd): So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: monai.transforms.MapTransform @@ -461,6 +486,7 @@ class CenterSpatialCropd(Cropd): for example: if the spatial size of input data is [40, 40, 40] and `roi_size=[32, 64, -1]`, the spatial size of output data will be [32, 40, 40]. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ def __init__( @@ -476,12 +502,16 @@ class CenterScaleCropd(Cropd): Note: as using the same scaled ROI to crop, all the input data specified by `keys` should have the same spatial shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: monai.transforms.MapTransform roi_scale: specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5] or a number for all dims. If its components have non-positive values, will use `1.0` instead, which means the input image size. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ def __init__( @@ -506,6 +536,9 @@ class RandSpatialCropd(RandCropd): will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: monai.transforms.MapTransform @@ -523,6 +556,7 @@ class RandSpatialCropd(RandCropd): if True, the actual size is sampled from: `randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1)`. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ @deprecated_arg_default("random_size", True, False, since="1.1", replaced="1.3") @@ -548,6 +582,9 @@ class RandScaleCropd(RandCropd): And allows to set the minimum and maximum scale of image size to limit the randomly generated ROI. Suppose all the expected fields specified by `keys` have same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: monai.transforms.MapTransform @@ -563,6 +600,7 @@ class RandScaleCropd(RandCropd): if True, the actual size is sampled from: `randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1)`. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ @deprecated_arg_default("random_size", True, False, since="1.1", replaced="1.3") @@ -593,6 +631,9 @@ class RandSpatialCropSamplesd(Randomizable, MapTransform, LazyTransform, MultiSa will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: monai.transforms.MapTransform @@ -610,6 +651,7 @@ class RandSpatialCropSamplesd(Randomizable, MapTransform, LazyTransform, MultiSa random_size: crop with random size or specific size ROI. The actual size is sampled from `randint(roi_size, img_size)`. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. Raises: ValueError: When ``num_samples`` is nonpositive. @@ -675,6 +717,9 @@ class CropForegroundd(Cropd): - Select label > 0 in the third channel of a One-Hot label field as the foreground to crop all `keys` fields. Users can define arbitrary function to select expected foreground from the whole source image or specified channels. And it can also add margin to every dim of the bounding box of foreground object. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ def __init__( @@ -717,6 +762,7 @@ def __init__( start_coord_key: key to record the start coordinate of spatial bounding box for foreground. end_coord_key: key to record the end coordinate of spatial bounding box for foreground. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. pad_kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. @@ -764,6 +810,9 @@ class RandWeightedCropd(Randomizable, MapTransform, LazyTransform, MultiSampleTr """ Samples a list of `num_samples` image patches according to the provided `weight_map`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` @@ -773,6 +822,7 @@ class RandWeightedCropd(Randomizable, MapTransform, LazyTransform, MultiSampleTr If its components have non-positive values, the corresponding size of `img` will be used. num_samples: number of samples (image patches) to take in the returned list. allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. See Also: :py:class:`monai.transforms.RandWeightedCrop` @@ -842,6 +892,9 @@ class RandCropByPosNegLabeld(Randomizable, MapTransform, LazyTransform, MultiSam And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the valid crop ROI. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` @@ -872,6 +925,7 @@ class RandCropByPosNegLabeld(Randomizable, MapTransform, LazyTransform, MultiSam the requested ROI in any dimension. If `True`, any smaller dimensions will be set to match the cropped size (i.e., no cropping in that dimension). allow_missing_keys: don't raise exception if key is missing. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. Raises: ValueError: When ``pos`` or ``neg`` are negative. @@ -1005,6 +1059,9 @@ class RandCropByLabelClassesd(Randomizable, MapTransform, LazyTransform, MultiSa And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the valid crop ROI. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` @@ -1033,7 +1090,7 @@ class RandCropByLabelClassesd(Randomizable, MapTransform, LazyTransform, MultiSa warn: if `True` prints a warning if a class is not present in the label. max_samples_per_class: maximum length of indices in each class to reduce memory consumption. Default is None, no subsampling. - + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. """ backend = RandCropByLabelClasses.backend @@ -1115,6 +1172,9 @@ class ResizeWithPadOrCropd(Padd): """ Dictionary-based wrapper of :py:class:`monai.transforms.ResizeWithPadOrCrop`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: monai.transforms.MapTransform @@ -1130,6 +1190,7 @@ class ResizeWithPadOrCropd(Padd): allow_missing_keys: don't raise exception if key is missing. method: {``"symmetric"``, ``"end"``} Pad image symmetrically on every side or only pad at the end sides. Defaults to ``"symmetric"``. + lazy: a flag to indicate whether this transform should execute lazily or not. Defaults to False. pad_kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index a106bf8942..353ed06d91 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -153,7 +153,7 @@ def apply_pending_transforms_in_order( This method is designed to be used only in the context of implementing lazy resampling functionality. In general you should not need to interact with or use this method directly, and its API may change without warning between - releases. + releases. See the :ref:`lazy_resampling for more information about lazy resampling. Args: transform: a transform that should be evaluated to determine whether pending transforms should be applied diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index eb7f273e4c..cde51724f8 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -124,6 +124,9 @@ class SpatialResample(InvertibleTransform, LazyTransform): Internally this transform computes the affine transform matrix from ``src_affine`` to ``dst_affine``, by ``xform = linalg.solve(src_affine, dst_affine)``, and call ``monai.transforms.Affine`` with ``xform``. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = [TransformBackends.TORCH, TransformBackends.NUMPY, TransformBackends.CUPY] @@ -249,8 +252,13 @@ def inverse(self, data: torch.Tensor) -> torch.Tensor: class ResampleToMatch(SpatialResample): - """Resample an image to match given metadata. The affine matrix will be aligned, - and the size of the output image will match.""" + """ + Resample an image to match given metadata. The affine matrix will be aligned, + and the size of the output image will match. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + """ def __call__( # type: ignore self, @@ -328,6 +336,9 @@ def __call__( # type: ignore class Spacing(InvertibleTransform, LazyTransform): """ Resample input image into the specified `pixdim`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = SpatialResample.backend @@ -540,6 +551,9 @@ def inverse(self, data: torch.Tensor) -> torch.Tensor: class Orientation(InvertibleTransform, LazyTransform): """ Change the input image's orientation into the specified based on `axcodes`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = [TransformBackends.NUMPY, TransformBackends.TORCH] @@ -658,6 +672,9 @@ class Flip(InvertibleTransform, LazyTransform): See `torch.flip` documentation for additional details: https://pytorch.org/docs/stable/generated/torch.flip.html + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_axis: spatial axes along which to flip over. Default is None. The default `axis=None` will flip over all of the axes of the input array. @@ -699,6 +716,9 @@ class Resize(InvertibleTransform, LazyTransform): Resize the input image to given spatial size (with scaling, not cropping/padding). Implemented using :py:class:`torch.nn.functional.interpolate`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: spatial_size: expected shape of spatial dimensions after resize operation. if some components of the `spatial_size` are non-positive values, the transform will use the @@ -856,6 +876,9 @@ class Rotate(InvertibleTransform, LazyTransform): """ Rotates an input image by given angle using :py:class:`monai.networks.layers.AffineTransform`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: angle: Rotation angle(s) in radians. should a float for 2D, three floats for 3D. keep_size: If it is True, the output shape is kept the same as the input. @@ -989,6 +1012,9 @@ class Zoom(InvertibleTransform, LazyTransform): Different from :py:class:`monai.transforms.resize`, this transform takes scaling factors as input, and provides an option of preserving the input spatial size. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: zoom: The zoom factor along the spatial axes. If a float, zoom is the same for each spatial axis. @@ -1120,6 +1146,8 @@ class Rotate90(InvertibleTransform, LazyTransform): See `torch.rot90` for additional details: https://pytorch.org/docs/stable/generated/torch.rot90.html#torch-rot90. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = [TransformBackends.TORCH] @@ -1171,6 +1199,9 @@ class RandRotate90(RandomizableTransform, InvertibleTransform, LazyTransform): """ With probability `prob`, input arrays are rotated by 90 degrees in the plane specified by `spatial_axes`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Rotate90.backend @@ -1236,6 +1267,9 @@ class RandRotate(RandomizableTransform, InvertibleTransform, LazyTransform): """ Randomly rotate the input arrays. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: range_x: Range of rotation angle in radians in the plane defined by the first and second axes. If single number, angle is uniformly sampled from (-range_x, range_x). @@ -1370,6 +1404,9 @@ class RandFlip(RandomizableTransform, InvertibleTransform, LazyTransform): See numpy.flip for additional details. https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: prob: Probability of flipping. spatial_axis: Spatial axes along which to flip over. Default is None. @@ -1420,6 +1457,9 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform, LazyTransform): See numpy.flip for additional details. https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: prob: Probability of flipping. lazy: a flag to indicate whether this transform should execute lazily or not. @@ -1479,6 +1519,9 @@ class RandZoom(RandomizableTransform, InvertibleTransform, LazyTransform): """ Randomly zooms input arrays with given probability within given zoom range. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: prob: Probability of zooming. min_zoom: Min zoom factor. Can be float or sequence same size as image. @@ -1624,6 +1667,9 @@ class AffineGrid(LazyTransform): """ Affine transforms on the coordinates. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: rotate_params: a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. Defaults to no rotation. @@ -1745,6 +1791,8 @@ class RandAffineGrid(Randomizable, LazyTransform): """ Generate randomised affine grid. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = AffineGrid.backend @@ -2078,6 +2126,8 @@ class Affine(InvertibleTransform, LazyTransform): Transform ``img`` given the affine parameters. A tutorial is available: https://github.com/Project-MONAI/tutorials/blob/0.6.0/modules/transforms_demo_2d.ipynb. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = list(set(AffineGrid.backend) & set(Resample.backend)) @@ -2273,6 +2323,8 @@ class RandAffine(RandomizableTransform, InvertibleTransform, LazyTransform): Random affine transform. A tutorial is available: https://github.com/Project-MONAI/tutorials/blob/0.6.0/modules/transforms_demo_2d.ipynb. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Affine.backend diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index b0fd53d3cd..4ba5849c46 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -154,6 +154,9 @@ class SpatialResampled(MapTransform, InvertibleTransform, LazyTransform): changes) in the dictionary so that ``src_affine`` always refers to the current status of affine. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + See also: :py:class:`monai.transforms.SpatialResample` """ @@ -198,7 +201,7 @@ def __init__( dst_keys: the key of the corresponding ``dst_affine`` in the metadata dictionary. allow_missing_keys: don't raise exception if key is missing. lazy: a flag to indicate whether this transform should execute lazily or not. - Defaults to False + Defaults to False. """ MapTransform.__init__(self, keys, allow_missing_keys) LazyTransform.__init__(self, lazy=lazy) @@ -252,7 +255,13 @@ def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch class ResampleToMatchd(MapTransform, InvertibleTransform, LazyTransform): - """Dictionary-based wrapper of :py:class:`monai.transforms.ResampleToMatch`.""" + """ + Dictionary-based wrapper of :py:class:`monai.transforms.ResampleToMatch`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + + """ backend = ResampleToMatch.backend @@ -356,6 +365,9 @@ class Spacingd(MapTransform, InvertibleTransform, LazyTransform): After resampling the input array, this transform will write the new affine to the `affine` field of metadata which is formed by ``key_{meta_key_postfix}``. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + see also: :py:class:`monai.transforms.Spacing` """ @@ -518,6 +530,9 @@ class Orientationd(MapTransform, InvertibleTransform, LazyTransform): This transform assumes the channel-first input format. In the case of using this transform for normalizing the orientations of images, it should be used before any anisotropic spatial transforms. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Orientation.backend @@ -590,6 +605,9 @@ def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch class Rotate90d(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Rotate90`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Rotate90.backend @@ -651,6 +669,9 @@ class RandRotate90d(RandomizableTransform, MapTransform, InvertibleTransform, La Dictionary-based version :py:class:`monai.transforms.RandRotate90`. With probability `prob`, input arrays are rotated by 90 degrees in the plane specified by `spatial_axes`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Rotate90.backend @@ -733,6 +754,9 @@ class Resized(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Resize`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: keys of the corresponding items to be transformed. See also: :py:class:`monai.transforms.compose.MapTransform` @@ -837,6 +861,9 @@ def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch class Affined(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Affine`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = Affine.backend @@ -966,6 +993,9 @@ def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch class RandAffined(RandomizableTransform, MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.RandAffine`. + + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. """ backend = RandAffine.backend @@ -1436,6 +1466,9 @@ class Flipd(MapTransform, InvertibleTransform, LazyTransform): See `numpy.flip` for additional details. https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. spatial_axis: Spatial axes along which to flip over. Default is None. @@ -1495,6 +1528,9 @@ class RandFlipd(RandomizableTransform, MapTransform, InvertibleTransform, LazyTr See `numpy.flip` for additional details. https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. prob: Probability of flipping. @@ -1571,6 +1607,9 @@ class RandAxisFlipd(RandomizableTransform, MapTransform, InvertibleTransform, La See `numpy.flip` for additional details. https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. prob: Probability of flipping. @@ -1645,6 +1684,9 @@ class Rotated(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Rotate`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. angle: Rotation angle(s) in radians. @@ -1734,6 +1776,9 @@ class RandRotated(RandomizableTransform, MapTransform, InvertibleTransform, Lazy Dictionary-based version :py:class:`monai.transforms.RandRotate` Randomly rotates the input arrays. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. range_x: Range of rotation angle in radians in the plane defined by the first and second axes. @@ -1856,6 +1901,9 @@ class Zoomd(MapTransform, InvertibleTransform, LazyTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Zoom`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. zoom: The zoom factor along the spatial axes. @@ -1950,6 +1998,9 @@ class RandZoomd(RandomizableTransform, MapTransform, InvertibleTransform, LazyTr """ Dict-based version :py:class:`monai.transforms.RandZoom`. + This transform is capable of lazy execution. See the :ref:`Lazy Resampling topic` + for more information. + Args: keys: Keys to pick data for transformation. prob: Probability of zooming. diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index be2b8a0ad8..024945afdc 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -76,18 +76,16 @@ def _apply_transform( lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. - lazy_strategy: this field controls how execution occurs when processing data lazily. Permitted - options are "in_order", "out_of_order". Please see `Compose`_ for more details of what these - options mean. In general, you should not need to change this from its default. + See the :ref:`lazy_resampling for more information about lazy resampling. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied - to that transform before it is executed. Note that overrides are currently only applied when lazy - is True. If lazy is False they are ignored. - currently supported args are: - {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}, - please see also :py:func:`monai.transforms.lazy.apply_pending` and ``Compose`` for more details. - logger_name: The name of the logger that should be used during transform execution. If None, logging is - suppressed. + to that transform before it is executed. Note that overrides are currently only applied when + :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + they are ignored. Currently supported args are: + {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. + logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. Returns: ReturnType: The return type of `transform`. @@ -125,12 +123,14 @@ def apply_transform( map_items: whether to apply transform to each item in `data`, if `data` is a list or tuple. Defaults to True. unpack_items: whether to unpack parameters using `*`. Defaults to False. - lazy: whether to execute in lazy mode or not. See ``Compose`` for more information about lazy resampling. - lazy_strategy: this field controls how execution occurs when processing data lazily. Permitted - options are "in_order", "out_of_order". Please see `Compose`_ for more details of what these - options mean. In general, you should not need to change this from its default. + log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which + disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the + default logger name. Setting it to a string specifies the logger to which errors should be logged. + lazy: whether to execute in lazy mode or not. See the :ref:`lazy_resampling for more + information about lazy resampling. overrides: optional overrides to apply to transform parameters. This parameter is ignored unless transforms - are being executed lazily. + are being executed lazily. See the :ref:`lazy_resampling for more details and + examples of its usage. Raises: Exception: When ``transform`` raises an exception. From 22f34a1263ae0541302eb5230a058823dfc5d87e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 24 May 2023 15:35:28 +0100 Subject: [PATCH 136/175] Fixing ref anchor; more work on the topic contents Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index 344cd565b6..707122ed43 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -1,4 +1,4 @@ -.. _lazy resampling: +.. _lazy_resampling: :github_url: https://github.com/Project-MONAI/MONAI @@ -142,5 +142,23 @@ API changes ^^^^^^^^^^^ A number of new arguments have been added to existing properties, which we'll go over in detail here. In particular, -we'll focus on :class:`monai.transforms.compose.Compose` and :class:`monai.transforms.traits.LazyTrait`/ -:class:`monai.transforms.transform.LazyTransform` and the way that they interact with each other. +we'll focus on :class:`monai.transforms.compose.Compose` and :class:`LazyTrait`/ +:class:`LazyTransform` and the way that they interact with each other. + +Compose ++++++++ + +:class:`monai.transforms.compose.Compose` gains a number of new arguments that can be used to control resampling +behaviour. Each of them is covered in its own section: + + +*``lazy``* + +``lazy`` controls whether execution is carried out in a lazy manner or not. It has three values that it can take: + +* `lazy=False` forces the pipeline to be executed in the standard way with every transform applied immediately +* `lazy=True` forces the pipeline to be executed lazily. Every transform that implements + :class:`monai.transforms.traits.LazyTrait` (or inherits + :class:`monai.transforms.transform.LazyTransform`) will be executed lazily +* `lazy=None` means that the pipeline can execute lazily, but only on transforms that have their own `lazy` property set to True. + From ba5873943759c60f28d75852eb37ce0df264c324 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 14:36:04 +0000 Subject: [PATCH 137/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/lazy_resampling.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index 707122ed43..839870ca63 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -161,4 +161,3 @@ behaviour. Each of them is covered in its own section: :class:`monai.transforms.traits.LazyTrait` (or inherits :class:`monai.transforms.transform.LazyTransform`) will be executed lazily * `lazy=None` means that the pipeline can execute lazily, but only on transforms that have their own `lazy` property set to True. - From b6bdf2275ad7164fdc009a54d935942c52cd1278 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 12:36:58 +0100 Subject: [PATCH 138/175] Typo in docstring Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 5324fa7058..1d887c7a40 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -42,7 +42,7 @@ def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, Currently, only 2d and 3d inputs are supported. This method is designed to be called by ``apply_pending_transforms`` and other methods / classes - that are part of the implementation of lazy resampling. In general, you should not need to tall + that are part of the implementation of lazy resampling. In general, you should not need to call this method unless you are directly developing custom lazy execution strategies. It works by calculating the overall effect of the accumulated pending transforms. When it runs From 43014524202802ed34f06a0fce0ca51e88f1b0e8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 12:38:59 +0100 Subject: [PATCH 139/175] Fixed typo in lazy/functional.py docstring Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 1d887c7a40..6e469e91af 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -48,7 +48,7 @@ def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, It works by calculating the overall effect of the accumulated pending transforms. When it runs out of pending transforms or when it finds incompatibilities between the accumulated pending transform and the next pending transform, it then applies the accumulated transform in a call to - '`resample``. + ``resample``. Pending transforms are incompatible with each other if one or more of the arguments in the pending transforms differ. These are parameters such as 'mode', 'padding_mode', 'dtype' and so forth. If From dd56b760b10077bc82633e0afcd59d759e1f191a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 13:53:43 +0100 Subject: [PATCH 140/175] Improving tests for OneOf, RandomOrder and SomeOf based on review Signed-off-by: Ben Murray --- tests/test_one_of.py | 8 ++++++-- tests/test_random_order.py | 8 ++++++-- tests/test_some_of.py | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/test_one_of.py b/tests/test_one_of.py index 2977b141ce..2909597507 100644 --- a/tests/test_one_of.py +++ b/tests/test_one_of.py @@ -254,12 +254,16 @@ def data_from_keys(keys): def test_execute_change_start_end(self, keys, pipeline): data = self.data_from_keys(keys) + c = OneOf(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, start=1) with self.assertRaises(ValueError): - c = OneOf(deepcopy(pipeline)) c(data, start=1) + c = OneOf(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, end=1) with self.assertRaises(ValueError): - c = OneOf(deepcopy(pipeline)) c(data, end=1) diff --git a/tests/test_random_order.py b/tests/test_random_order.py index 5eadedb58a..e5507fafca 100644 --- a/tests/test_random_order.py +++ b/tests/test_random_order.py @@ -127,12 +127,16 @@ def data_from_keys(keys): def test_execute_change_start_end(self, keys, pipeline): data = self.data_from_keys(keys) + c = RandomOrder(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, start=1) with self.assertRaises(ValueError): - c = RandomOrder(deepcopy(pipeline)) c(data, start=1) + c = RandomOrder(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, end=1) with self.assertRaises(ValueError): - c = RandomOrder(deepcopy(pipeline)) c(data, end=1) diff --git a/tests/test_some_of.py b/tests/test_some_of.py index cba2c8a464..8880c376b9 100644 --- a/tests/test_some_of.py +++ b/tests/test_some_of.py @@ -235,12 +235,16 @@ def data_from_keys(keys): def test_execute_change_start_end(self, keys, pipeline): data = self.data_from_keys(keys) + c = SomeOf(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, start=1) with self.assertRaises(ValueError): - c = SomeOf(deepcopy(pipeline)) c(data, start=1) + c = SomeOf(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, end=1) with self.assertRaises(ValueError): - c = SomeOf(deepcopy(pipeline)) c(data, end=1) From 9649731223187429fef197327b45c3cec19d76b4 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 13:57:34 +0100 Subject: [PATCH 141/175] removal of dead code Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- tests/test_integration_lazy_samples.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 5a3e9dd3b1..2af3accf33 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -42,9 +42,6 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, num_workers = 0 if torch.cuda.is_available() else num_workers # define transforms for image and segmentation - # lazy_kwargs = dict( - # mode=("bilinear", 0), device=device, padding_mode=("border", "nearest"), dtype=(torch.float32, torch.uint8) - # ) lazy_kwargs = { "img": {"mode": "bilinear", "device": device, "padding_mode": "border", "dtype": torch.float32}, "seg": {"mode": 0, "device": device, "padding_mode": "nearest", "dtype": torch.uint8}, From 5c1dbb27c2dad6ff51f2ed5b63dae04d024c37ad Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 13:58:07 +0100 Subject: [PATCH 142/175] simplification of tensor manipulation in test_compose Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- tests/test_compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 10c97f9e61..9a28a8d724 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -260,7 +260,7 @@ class TestComposeExecute(unittest.TestCase): @staticmethod def data_from_keys(keys): if keys is None: - data = torch.unsqueeze(torch.tensor(np.arange(12 * 16).reshape(12, 16)), dim=0) + data = torch.arange(12 * 16).reshape(1, 12, 16) else: data = {} for i_k, k in enumerate(keys): From d4d6b2ce0d49be93dd134f845e457118d27e7456 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 14:01:50 +0100 Subject: [PATCH 143/175] Refactoring compose tests to move initialiser out of assertRaises Signed-off-by: Ben Murray --- tests/test_compose.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 9a28a8d724..7d26613644 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -294,15 +294,19 @@ def test_compose_execute_equivalence(self, keys, pipeline): def test_compose_execute_bad_start_param(self, keys, pipeline): data = self.data_from_keys(keys) + c = mt.Compose(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, start=None) with self.assertRaises(ValueError): - c = mt.Compose(deepcopy(pipeline)) c(data, start=None) with self.assertRaises(ValueError): execute_compose(data, deepcopy(pipeline), start=None) + c = mt.Compose(deepcopy(pipeline)) + with self.assertRaises(ValueError): + c(data, start=-1) with self.assertRaises(ValueError): - c = mt.Compose(deepcopy(pipeline)) c(data, start=-1) with self.assertRaises(ValueError): From ec3236fbfdef336d7ab0eb576f47c1314b4d2282 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 14:28:16 +0100 Subject: [PATCH 144/175] Using Eric's logging refactor suggestion; fixing issue with integration lazy test not logging Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 56 ++++++++++++++++++-------- tests/test_compose.py | 38 ++++++++--------- tests/test_integration_lazy_samples.py | 1 + 3 files changed, 60 insertions(+), 35 deletions(-) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 353ed06d91..01d7fb22a5 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -34,39 +34,63 @@ def _log_pending_info( key: str | None = None, logger_name: bool | str = False, ): + # if logger_name is False: + # return + # logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" + # logger = get_logger(logger_name) + # + # if isinstance(transform, LazyTrait): + # if lazy is not None and lazy != transform.lazy: + # tlazy = f", transform.lazy: {transform.lazy} (overridden)" + # else: + # tlazy = f", transform.lazy: {transform.lazy}" + # else: + # tlazy = ", transform is not lazy" + # + # if isinstance(transform, MapTransform): + # transform_keys = transform.keys if key is None else (key,) + # for k in transform_keys: + # if k in data: + # pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 + # logger.info( + # f"{activity} - lazy mode: {lazy}, key: '{k}', " + # f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + # ) + # else: + # pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 + # if key is None: + # logger.info( + # f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + # ) + # else: + # logger.info( + # f"{activity} - lazy mode: {lazy}, key: '{key}', " + # f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" + # ) if logger_name is False: return logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" logger = get_logger(logger_name) + tcname = type(transform).__name__ if isinstance(transform, LazyTrait): + tlazy = f", transform.lazy: {transform.lazy}" if lazy is not None and lazy != transform.lazy: - tlazy = f", transform.lazy: {transform.lazy} (overridden)" - else: - tlazy = f", transform.lazy: {transform.lazy}" + tlazy += " (overridden)" else: tlazy = ", transform is not lazy" + msg = f"{activity} - lazy: {lazy}, {{key_msg}}pending: {{pcount}}, upcoming '{tcname}'{tlazy}" + if isinstance(transform, MapTransform): transform_keys = transform.keys if key is None else (key,) for k in transform_keys: if k in data: pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 - logger.info( - f"{activity} - lazy mode: {lazy}, key: '{k}', " - f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) + logger.info(msg.format(pcount=pcount, key_msg=f"key: '{k}', ")) else: pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 - if key is None: - logger.info( - f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) - else: - logger.info( - f"{activity} - lazy mode: {lazy}, key: '{key}', " - f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - ) + logger.info(msg.format(pcount=pcount, key_msg="" if key is None else f"key: '{key}', ")) def _log_applied_info(data: Any, key=None, logger_name: bool | str = False): diff --git a/tests/test_compose.py b/tests/test_compose.py index 7d26613644..e131524b40 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -429,19 +429,19 @@ def test_compose_with_logger_name(self, keys, pipeline): ), True, ( - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 0, " "upcoming 'Flipd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 0, " "upcoming 'Flipd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 1, " "upcoming 'Spacingd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 1, " "upcoming 'Spacingd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 2, " "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 2, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 2, " "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "INFO - Apply pending transforms - lazy mode: True, key: 'a', pending: 3, " + "INFO - Apply pending transforms - lazy: True, key: 'a', pending: 3, " "upcoming 'NormalizeIntensityd', transform is not lazy\n" "INFO - Pending transforms applied: key: 'a', applied_operations: 3\n" "INFO - Pending transforms applied: key: 'b', applied_operations: 3\n" @@ -457,15 +457,15 @@ def test_compose_with_logger_name(self, keys, pipeline): ), True, ( - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 0, " "upcoming 'Flipd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 0, " "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 1, " "upcoming 'Zoomd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 1, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 1, " "upcoming 'Zoomd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 2, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 2, " "upcoming 'Spacingd', transform.lazy: False (overridden)\n" "INFO - Pending transforms applied: key: 'a', applied_operations: 3\n" "INFO - Pending transforms applied: key: 'b', applied_operations: 2\n" @@ -542,21 +542,21 @@ def test_compose_with_logger_name(self, keys, pipeline): ), True, ( - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 0, " "upcoming 'Flipd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 0, " "upcoming 'Rotate90d', transform.lazy: False (overridden)\n" - "INFO - Apply pending transforms - lazy mode: True, key: 'a', pending: 1, " + "INFO - Apply pending transforms - lazy: True, key: 'a', pending: 1, " "upcoming 'ApplyPendingd', transform is not lazy\n" - "INFO - Apply pending transforms - lazy mode: True, key: 'b', pending: 1, " + "INFO - Apply pending transforms - lazy: True, key: 'b', pending: 1, " "upcoming 'ApplyPendingd', transform is not lazy\n" "INFO - Pending transforms applied: key: 'a', applied_operations: 1\n" "INFO - Pending transforms applied: key: 'b', applied_operations: 1\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 0, " "upcoming 'Zoomd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'b', pending: 0, " + "INFO - Accumulate pending transforms - lazy: True, key: 'b', pending: 0, " "upcoming 'Zoomd', transform.lazy: False (overridden)\n" - "INFO - Accumulate pending transforms - lazy mode: True, key: 'a', pending: 1, " + "INFO - Accumulate pending transforms - lazy: True, key: 'a', pending: 1, " "upcoming 'Spacingd', transform.lazy: False (overridden)\n" "INFO - Pending transforms applied: key: 'a', applied_operations: 3\n" "INFO - Pending transforms applied: key: 'b', applied_operations: 2\n" diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 2af3accf33..d3d58bda5d 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -74,6 +74,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, ], lazy=lazy, overrides=lazy_kwargs, + logger_name=num_workers > 0, ) # create a training data loader From e8402777b6f57ca50b3d3c1c0356f855c6447198 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 14:57:02 +0100 Subject: [PATCH 145/175] Refactored data_from_keys Signed-off-by: Ben Murray --- tests/test_compose.py | 50 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index e131524b40..d804d29941 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -28,6 +28,15 @@ from monai.utils import set_determinism +def data_from_keys(keys, h, w): + if keys is None: + data = torch.arange(h * w).reshape(1, h, w) + else: + data = {} + for i_k, k in enumerate(keys): + data[k] = torch.unsqueeze(torch.tensor(np.arange(h * w)).reshape(h, w) + i_k * h * w, dim=0) + return data + class _RandXform(Randomizable): def randomize(self): self.val = self.R.random_sample() @@ -257,19 +266,10 @@ def test_backwards_compatible_imports(self): class TestComposeExecute(unittest.TestCase): - @staticmethod - def data_from_keys(keys): - if keys is None: - data = torch.arange(12 * 16).reshape(1, 12, 16) - else: - data = {} - for i_k, k in enumerate(keys): - data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) - return data @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_equivalence(self, keys, pipeline): - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) expected = mt.Compose(deepcopy(pipeline))(data) @@ -292,7 +292,7 @@ def test_compose_execute_equivalence(self, keys, pipeline): @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_bad_start_param(self, keys, pipeline): - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) c = mt.Compose(deepcopy(pipeline)) with self.assertRaises(ValueError): @@ -314,7 +314,7 @@ def test_compose_execute_bad_start_param(self, keys, pipeline): @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_negative_range(self, keys, pipeline): - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) with self.assertRaises(ValueError): c = mt.Compose(deepcopy(pipeline)) @@ -325,7 +325,7 @@ def test_compose_execute_negative_range(self, keys, pipeline): @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_bad_end_param(self, keys, pipeline): - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) with self.assertRaises(ValueError): c = mt.Compose(deepcopy(pipeline)) @@ -336,7 +336,7 @@ def test_compose_execute_bad_end_param(self, keys, pipeline): @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_empty_range(self, keys, pipeline): - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) c = mt.Compose(deepcopy(pipeline)) for i in range(len(pipeline)): @@ -345,7 +345,7 @@ def test_compose_execute_empty_range(self, keys, pipeline): @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_with_logger_name(self, keys, pipeline): - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) c = mt.Compose(deepcopy(pipeline), logger_name="a_logger_name") c(data) @@ -566,15 +566,6 @@ def test_compose_with_logger_name(self, keys, pipeline): class TestComposeExecuteWithLogging(unittest.TestCase): - @staticmethod - def data_from_keys(keys): - if keys is None: - data = torch.arange(12 * 16).reshape(1, 12, 16) - else: - data = {} - for i_k, k in enumerate(keys): - data[k] = torch.unsqueeze(torch.tensor(np.arange(12 * 16)).reshape(12, 16) + i_k * 192, dim=0) - return data @parameterized.expand(TEST_COMPOSE_EXECUTE_LOGGING_TEST_CASES) def test_compose_with_logging(self, keys, pipeline, lazy, expected): @@ -588,7 +579,7 @@ def test_compose_with_logging(self, keys, pipeline, lazy, expected): logger.removeHandler(logger.handlers[-1]) logger.addHandler(handler) - data = self.data_from_keys(keys) + data = data_from_keys(keys, 12, 16) c = mt.Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") c(data) @@ -631,15 +622,6 @@ def _inner(data1, data2): class TestComposeExecuteWithFlags(unittest.TestCase): @parameterized.expand(TEST_COMPOSE_EXECUTE_FLAG_TEST_CASES) def test_compose_execute_equivalence_with_flags(self, flags, data, pipeline): - @staticmethod - def data_from_keys(keys): - if keys is None: - data = torch.unsqueeze(torch.tensor(np.arange(24 * 32).reshape(24, 32)), dim=0) - else: - data = {} - for i_k, k in enumerate(keys): - data[k] = torch.unsqueeze(torch.tensor(np.arange(24 * 32)).reshape(24, 32) + i_k * 768, dim=0) - return data expected = mt.Compose(pipeline, **flags)(data) From f7c43b93bb48cf1ca5b34ab6a085fa5ef1b18ee3 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 15:04:16 +0100 Subject: [PATCH 146/175] Removing dead logging code Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 33 ------------------------------ 1 file changed, 33 deletions(-) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 01d7fb22a5..6c7f306dd4 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -34,39 +34,6 @@ def _log_pending_info( key: str | None = None, logger_name: bool | str = False, ): - # if logger_name is False: - # return - # logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" - # logger = get_logger(logger_name) - # - # if isinstance(transform, LazyTrait): - # if lazy is not None and lazy != transform.lazy: - # tlazy = f", transform.lazy: {transform.lazy} (overridden)" - # else: - # tlazy = f", transform.lazy: {transform.lazy}" - # else: - # tlazy = ", transform is not lazy" - # - # if isinstance(transform, MapTransform): - # transform_keys = transform.keys if key is None else (key,) - # for k in transform_keys: - # if k in data: - # pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 - # logger.info( - # f"{activity} - lazy mode: {lazy}, key: '{k}', " - # f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - # ) - # else: - # pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 - # if key is None: - # logger.info( - # f"{activity} - lazy: {lazy}, " f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - # ) - # else: - # logger.info( - # f"{activity} - lazy mode: {lazy}, key: '{key}', " - # f"pending: {pcount}, upcoming '{transform.__class__.__name__}'{tlazy}" - # ) if logger_name is False: return logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" From 77bb4e2671711e572889a64fa34ab2f86206918a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 15:13:26 +0100 Subject: [PATCH 147/175] autofix Signed-off-by: Ben Murray --- tests/test_compose.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index d804d29941..c936f9384a 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -37,6 +37,7 @@ def data_from_keys(keys, h, w): data[k] = torch.unsqueeze(torch.tensor(np.arange(h * w)).reshape(h, w) + i_k * h * w, dim=0) return data + class _RandXform(Randomizable): def randomize(self): self.val = self.R.random_sample() @@ -266,7 +267,6 @@ def test_backwards_compatible_imports(self): class TestComposeExecute(unittest.TestCase): - @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) def test_compose_execute_equivalence(self, keys, pipeline): data = data_from_keys(keys, 12, 16) @@ -566,7 +566,6 @@ def test_compose_with_logger_name(self, keys, pipeline): class TestComposeExecuteWithLogging(unittest.TestCase): - @parameterized.expand(TEST_COMPOSE_EXECUTE_LOGGING_TEST_CASES) def test_compose_with_logging(self, keys, pipeline, lazy, expected): stream = StringIO() @@ -622,7 +621,6 @@ def _inner(data1, data2): class TestComposeExecuteWithFlags(unittest.TestCase): @parameterized.expand(TEST_COMPOSE_EXECUTE_FLAG_TEST_CASES) def test_compose_execute_equivalence_with_flags(self, flags, data, pipeline): - expected = mt.Compose(pipeline, **flags)(data) for cutoff in range(len(pipeline)): From ff962ef17f3477e4653476b2c339d5cf3059973f Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 17:54:03 +0100 Subject: [PATCH 148/175] Dead code removal, doc fixes Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 28 ++++++++++++++-------------- monai/transforms/inverse.py | 1 - monai/transforms/lazy/executors.py | 1 - monai/transforms/lazy/utils.py | 2 -- monai/transforms/transform.py | 1 - monai/transforms/utils.py | 2 +- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index 839870ca63..bb01c819ac 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -8,16 +8,15 @@ Lazy Resampling .. toctree:: :maxdepth: 2 - mb_specification config_syntax.md Introduction ^^^^^^^^^^^^ -Lazy Resampling is a new feature for MONAI 1.2. This feature is still experimental at this time and it is possible that -behaviour and APIs will change in upcoming releases. +Lazy Resampling is a new feature introduced in MONAI 1.2. This feature is still experimental at this time and it is +possible that behaviour and APIs will change in upcoming releases. -Lazy resamping reworks the way that preprocessing is performed. It improves upon standard preprocessing pipelines and +Lazy resampling reworks the way that preprocessing is performed. It improves upon standard preprocessing pipelines and can provide significant benefits over traditional preprocessing. It can improve: * pipeline execution time * pipeline memory usage in CPU or GPU @@ -79,7 +78,7 @@ The following will then happen when we call ``pipeline(inputs)``: border have undergone some kind of resample operation at that stage. Overall, there are up to three occasions where the data is either interpolated or resampled through spatial transforms -(``Spaciald``, ``RandRotated`` and ``RandZoomd``). Furthermore, the crop that occurs means that the output data +(``Spacingd``, ``RandRotated`` and ``RandZoomd``). Furthermore, the crop that occurs means that the output data samples might contain pixels for which there is data but that show padding values, because the data was thrown away by ``RandSpatialCrop``. @@ -91,20 +90,21 @@ Lazy execution Lazy resampling works very differently. When you execute the same pipeline with `lazy=True`, the following happens: -1. ``Spacingd`` is executed lazily. It puts a description of the operation that it wants to perform onto a list of +#. ``Spacingd`` is executed lazily. It puts a description of the operation that it wants to perform onto a list of pending operations -2. ``Orientationd`` is executed lazily. It adds a description of its own operation to the pending operation list so +#. ``Orientationd`` is executed lazily. It adds a description of its own operation to the pending operation list so now there are 2 pending operations -3. ``RandSpatialCropd`` is executedexecuting lazily. It adds a description of its own operation to the pending +#. ``RandSpatialCropd`` is executed lazily. It adds a description of its own operation to the pending operation list so now there are 3 pending operations -4. ``RandRotate90d`` is executedexecuting lazily. It adds a description of its own operation to the pending operation +#. ``RandRotate90d`` is executed lazily. It adds a description of its own operation to the pending operation list so now there are 4 pending operations -5. ``RandRotated`` is executedexecuting lazily. It adds a description of its own operation to the pending operation +#. ``RandRotated`` is executed lazily. It adds a description of its own operation to the pending operation list so now there are 5 pending operations -6. ``RandZoomd`` is executedexecuting lazily. It adds a description of its own operation to the pending operation - list so now there are 6 pending operations 1. [Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, - RandZoomd] are all on the pending operations list but have yet to be carried out on the data -7. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their +#. ``RandZoomd`` is executed lazily. It adds a description of its own operation to the pending operation + list so now there are 6 pending operations + #. [Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, RandZoomd] are all on the pending + operations list but have yet to be carried out on the data +#. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their descriptions are mathematically composited together, to determine the operation that results from all of them being carried out. This is then applied in a single resample operation. Once that is done, RandGaussianNoised operates on the resulting data diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index f010aa9de9..ade9034563 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -113,7 +113,6 @@ def push_transform(self, data, *args, **kwargs): """ lazy_eval = kwargs.get("lazy", False) transform_info = self.get_transform_info() - # lazy_eval = transform_info.get(TraceKeys.lazy, False) do_transform = transform_info.get(TraceKeys.DO_TRANSFORM, True) kwargs = kwargs or {} replace = kwargs.pop("replace", False) # whether to rewrite the most recently pushed transform info diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 6c7f306dd4..760ddd8da3 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -160,7 +160,6 @@ def apply_pending_transforms_in_order( an object of the same type as data if pending transforms were applied, or 'data' if they were not """ - apply_pending = False keys = None if isinstance(transform, LazyTrait): if transform.checks_data: diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index d9c8404cdb..359559e319 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -223,8 +223,6 @@ def resample(data: torch.Tensor, matrix: NdarrayOrTensor, kwargs: dict | None = img.affine = call_kwargs["dst_affine"] return img - # TODO: lazy evaluation - no need to separately set lazy to False - # resampler = monai.transforms.SpatialResample(lazy=False, **init_kwargs) resampler = monai.transforms.SpatialResample(**init_kwargs) resampler.lazy = False # resampler is a lazytransform with resampler.trace_transform(False): # don't track this transform in `img` diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 024945afdc..2737a518df 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -26,7 +26,6 @@ from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor -# from monai.transforms.lazy.executors import apply_pending_transforms_in_order, apply_pending_transforms_out_of_order from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first from monai.utils.enums import TransformBackends diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index c9de6f495b..7594a3fa58 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -1345,7 +1345,7 @@ def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransfo with allow_missing_keys_mode(t): _ = t(data) # OK! """ - from monai.transforms.compose import Compose + # from monai.transforms.compose import Compose # If given a sequence of transforms, Compose them to get a single list if issequenceiterable(transform): From dface199d7398b274618b07c8382b2c420106a7e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 18:09:45 +0100 Subject: [PATCH 149/175] Removing dead imports from lazy/functional.py Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 6e469e91af..de2dc87b7b 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -15,13 +15,9 @@ import torch -# from monai.apps.utils import get_logger from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd -# from monai.transforms.lazy.array import ApplyPending -# from monai.transforms.lazy.dictionary import ApplyPendingd -# from monai.transforms.traits import LazyTrait from monai.transforms.lazy.utils import ( affine_from_pending, combine_transforms, From fc8ba591198d92a06fc589be327c8eee66068267 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 18:17:31 +0100 Subject: [PATCH 150/175] lazy_resampling doc :ref: fix Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 6ee84af19f..8666c25fea 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -73,7 +73,7 @@ def execute_compose( start: the index of the first transform to be executed. If not set, this defaults to 0 end: the index after the last transform to be exectued. If set, the transform at index-1 is the last transform that is executed. If this is not set, it defaults to len(transforms) - lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be + lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden From e2a4d51f9c2ca2a268b9f5bc7c62d3e446701392 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 25 May 2023 18:19:38 +0100 Subject: [PATCH 151/175] neater tensor manipulation Co-authored-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Signed-off-by: Ben Murray --- tests/test_compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index c936f9384a..42667c043b 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -34,7 +34,7 @@ def data_from_keys(keys, h, w): else: data = {} for i_k, k in enumerate(keys): - data[k] = torch.unsqueeze(torch.tensor(np.arange(h * w)).reshape(h, w) + i_k * h * w, dim=0) + data[k] = torch.arange(h * w).reshape(1, h, w).mul_(i_k * h * w) return data From b2eeadd2f49b2c411da39c75e399e22f6cdbdc65 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 09:11:35 +0100 Subject: [PATCH 152/175] autofixes Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 1 + monai/transforms/lazy/functional.py | 1 - monai/transforms/transform.py | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index bb01c819ac..9aecde9e56 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -102,6 +102,7 @@ Lazy resampling works very differently. When you execute the same pipeline with list so now there are 5 pending operations #. ``RandZoomd`` is executed lazily. It adds a description of its own operation to the pending operation list so now there are 6 pending operations + #. [Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, RandZoomd] are all on the pending operations list but have yet to be carried out on the data #. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index de2dc87b7b..f401d72261 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -17,7 +17,6 @@ from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd - from monai.transforms.lazy.utils import ( affine_from_pending, combine_transforms, diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 2737a518df..1570749c2e 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -25,7 +25,6 @@ from monai import config, transforms from monai.config import KeysCollection from monai.data.meta_tensor import MetaTensor - from monai.transforms.traits import LazyTrait, RandomizableTrait, ThreadUnsafe from monai.utils import MAX_SEED, ensure_tuple, first from monai.utils.enums import TransformBackends From f5551f71d512bb84b6b743917b5a7094454debd5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 09:46:24 +0100 Subject: [PATCH 153/175] Additional documentation Signed-off-by: Ben Murray --- docs/index.html | 656 + docs/source/lazy_resampling.rst | 75 +- docs/transforms.html | 24586 ++++++++++++++++++++++++++++++ 3 files changed, 25313 insertions(+), 4 deletions(-) create mode 100644 docs/index.html create mode 100644 docs/transforms.html diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000000..bc1477a7d2 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,656 @@ + + + + + + + + + + + Project MONAI — MONAI 0 Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + +
+
+ +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+ + + + + +
+ +
+

Project MONAI#

+

Medical Open Network for AI

+

MONAI is a PyTorch-based, open-source framework +for deep learning in healthcare imaging, part of the PyTorch Ecosystem.

+

Its ambitions are:

+
    +
  • developing a community of academic, industrial and clinical researchers collaborating on a common foundation;

  • +
  • creating state-of-the-art, end-to-end training workflows for healthcare imaging;

  • +
  • providing researchers with an optimized and standardized way to create and evaluate deep learning models.

  • +
+
+

Features#

+
    +
  • flexible pre-processing for multi-dimensional medical imaging data;

  • +
  • compositional & portable APIs for ease of integration in existing workflows;

  • +
  • domain-specific implementations for networks, losses, evaluation metrics and more;

  • +
  • customizable design for varying user expertise;

  • +
  • multi-GPU data parallelism support.

  • +
+
+
+

Getting started#

+

MedNIST demo and MONAI for PyTorch Users are available on Colab.

+

Examples and notebook tutorials are located at Project-MONAI/tutorials.

+

Technical documentation is available at docs.monai.io.

+
+

Feature highlights

+ +
+
+

API Reference

+ +
+
+

Installation

+ +
+
+

Contributing

+ +
+
+

Specifications

+ +
+
+
+

Model Zoo#

+

The MONAI Model Zoo is a place for researchers and data scientists to share the latest and great models from the community. +Utilizing the MONAI Bundle format makes it easy to get started building workflows with MONAI.

+
+ +
+
+

Indices and tables#

+ +
+ + +
+ + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index bb01c819ac..db38d02f21 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -145,14 +145,16 @@ A number of new arguments have been added to existing properties, which we'll go we'll focus on :class:`monai.transforms.compose.Compose` and :class:`LazyTrait`/ :class:`LazyTransform` and the way that they interact with each other. + Compose +++++++ -:class:`monai.transforms.compose.Compose` gains a number of new arguments that can be used to control resampling -behaviour. Each of them is covered in its own section: +:class:`monai.transforms.compose.Compose` gains a number of new arguments that can be used to control +resampling behaviour. Each of them is covered in its own section: -*``lazy``* +lazy +"""" ``lazy`` controls whether execution is carried out in a lazy manner or not. It has three values that it can take: @@ -160,4 +162,69 @@ behaviour. Each of them is covered in its own section: * `lazy=True` forces the pipeline to be executed lazily. Every transform that implements :class:`monai.transforms.traits.LazyTrait` (or inherits :class:`monai.transforms.transform.LazyTransform`) will be executed lazily -* `lazy=None` means that the pipeline can execute lazily, but only on transforms that have their own `lazy` property set to True. +* `lazy=None` means that the pipeline can execute lazily, but only on transforms that have their own `lazy` property + set to True. + + +overrides +""""""""" + +``overrides`` allows the user to specify certain parameters that transforms can be overridden with when they are +executed lazily. This parameter is primarily provided to allow you to run a pipeline without having to modify fields +like ``mode`` and ``padding_mode``. +When executing dictionary-based transforms, you provide a dictionary containing overrides for each key, as follows. You +can omit keys that don't require overrides: + +.. code-block:: + + { + "image": {"mode": "bilinear"}, + "label": {"padding_mode": "zeros"} + } + + +logger_name +""""""""""" + +Logging of transform execution is provided if you wish to understand exactly how your pipelines execute. It can take a +``bool`` or ``str`` value, and is False by default, which disables logging. Otherwise, you can enable it by passing it +the name of a logger that you wish to use (note, you don't have to construct the logger beforehand). + + +LazyTrait / LazyTransform ++++++++++++++++++++++++++ + +Many transforms now implement either LazyTrait or LazyTransform. Doing so marks the transform for lazy execution. Lazy +transforms have the following in common: + + +``__init__`` has a ``lazy`` argument +"""""""""""""""""""""""""""""""""""" + +``lazy`` is a ``bool`` value that can be passed to the initialiser when a lazy transform is instantiated. This +indicates to the transform that it should execute lazily or not lazily. Note that this value can be overridden by +passing ``lazy`` to ``__init__``. ``lazy`` is ``False`` by default + + +``__call__`` has a ``lazy`` argument +"""""""""""""""""""""""""""""""""""" + +``lazy`` is an optional ``bool`` value that can be passed at call time to override the behaviour defined during +initialisation. It has a default value of ``None``. If it is not ``None``, then this value is used instead of +``self.lazy``. This allows the calling :class:`Compose` instance to override +default values rather than having to set it on every lazy transform (unless the user sets +:class:`Compose.lazy` to ``None``). + +lazy property +""""""""""""" + +The lazy property allows you to get or set the lazy status of a lazy transform after constructing it. + +checks_data property (get only) +""""""""""""""""""""""""""""""" + +The ``checks_data`` property indicates that a transform makes use of the data in one or more of the tensors that it is +passed during its execution. Such transforms require that the tensors must therefore be up to date, even if the +transform itself is executing lazily. This is required for transforms such as ``CropForeground[d]``, +``RandCropByPosNegLabel[d]``, and ``RandCropByLabelClasses[d]``. This property is implemented to return ``False`` on +``LazyTransform`` and must be overridden to return ``True`` by transforms that check data values when executing. diff --git a/docs/transforms.html b/docs/transforms.html new file mode 100644 index 0000000000..d254506868 --- /dev/null +++ b/docs/transforms.html @@ -0,0 +1,24586 @@ + + + + + + + + + + + Transforms — MONAI 0 Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + +
+
+ +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Transforms#

+
+

Generic Interfaces#

+
+

Transform#

+
+
+class monai.transforms.Transform[source]#
+

An abstract class of a Transform. +A transform is callable that processes data.

+

It could be stateful and may modify data in place, +the implementation should be aware of:

+
+
    +
  1. thread safety when mutating its own states. +When used from a multi-process context, transform’s instance variables are read-only. +thread-unsafe transforms should inherit monai.transforms.ThreadUnsafe.

  2. +
  3. data content unused by this transform may still be used in the +subsequent transforms in a composed transform.

  4. +
  5. storing too much information in data may cause some memory issue or IPC sync issue, +especially in the multi-processing environment of PyTorch DataLoader.

  6. +
+
+

See Also

+
+
+
+
+abstract __call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

MapTransform#

+
+
+class monai.transforms.MapTransform(keys, allow_missing_keys=False)[source]#
+

A subclass of monai.transforms.Transform with an assumption +that the data input of self.__call__ is a MutableMapping such as dict.

+

The keys parameter will be used to get and set the actual data +item to transform. That is, the callable of this transform should +follow the pattern:

+
+
def __call__(self, data):
+    for key in self.keys:
+        if key in data:
+            # update output data with some_transform_function(data[key]).
+        else:
+            # raise exception unless allow_missing_keys==True.
+    return data
+
+
+
+
+
Raises:
+
    +
  • ValueError – When keys is an empty iterable.

  • +
  • TypeError – When keys type is not in Union[Hashable, Iterable[Hashable]].

  • +
+
+
+
+
+abstract __call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+call_update(data)[source]#
+

This function is to be called after every self.__call__(data), +update data[key_transforms] and data[key_meta_dict] using the content from MetaTensor data[key], +for MetaTensor backward compatibility 0.9.0.

+
+ +
+
+first_key(data)[source]#
+

Get the first available key of self.keys in the input data dictionary. +If no available key, return an empty tuple ().

+
+
Parameters:
+

data (dict[Hashable, Any]) – data that the transform will be applied to.

+
+
+
+ +
+
+key_iterator(data, *extra_iterables)[source]#
+

Iterate across keys and optionally extra iterables. If key is missing, exception is raised if +allow_missing_keys==False (default). If allow_missing_keys==True, key is skipped.

+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Any]) – data that the transform will be applied to

  • +
  • extra_iterables (UnionType[Iterable, None]) – anything else to be iterated through

  • +
+
+
Return type:
+

Generator

+
+
+
+ +
+ +
+
+

RandomizableTrait#

+
+
+class monai.transforms.RandomizableTrait[source]#
+

An interface to indicate that the transform has the capability to perform +randomized transforms to the data that it is called upon. This interface +can be extended from by people adapting transforms to the MONAI framework as well as by +implementors of MONAI transforms.

+
+ +
+
+

LazyTrait#

+
+
+class monai.transforms.LazyTrait[source]#
+

An interface to indicate that the transform has the capability to execute using +MONAI’s lazy resampling feature. In order to do this, the implementing class needs +to be able to describe its operation as an affine matrix or grid with accompanying metadata. +This interface can be extended from by people adapting transforms to the MONAI framework as +well as by implementors of MONAI transforms.

+
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

MultiSampleTrait#

+
+
+class monai.transforms.MultiSampleTrait[source]#
+

An interface to indicate that the transform has the capability to return multiple samples +given an input, such as when performing random crops of a sample. This interface can be +extended from by people adapting transforms to the MONAI framework as well as by implementors +of MONAI transforms.

+
+ +
+
+

Randomizable#

+
+
+class monai.transforms.Randomizable[source]#
+

An interface for handling random state locally, currently based on a class +variable R, which is an instance of np.random.RandomState. This +provides the flexibility of component-specific determinism without +affecting the global states. It is recommended to use this API with +monai.data.DataLoader for deterministic behaviour of the +preprocessing pipelines. This API is not thread-safe. Additionally, +deepcopying instance of this class often causes insufficient randomness as +the random states will be duplicated.

+
+
+randomize(data)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Randomizable

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

LazyTransform#

+
+
+class monai.transforms.LazyTransform(lazy=False)[source]#
+

An implementation of functionality for lazy transforms that can be subclassed by array and +dictionary transforms to simplify implementation of new lazy transforms.

+
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandomizableTransform#

+
+
+class monai.transforms.RandomizableTransform(prob=1.0, do_transform=True)[source]#
+

An interface for handling random state locally, currently based on a class variable R, +which is an instance of np.random.RandomState. +This class introduces a randomized flag _do_transform, is mainly for randomized data augmentation transforms. +For example:

+
from monai.transforms import RandomizableTransform
+
+class RandShiftIntensity100(RandomizableTransform):
+    def randomize(self):
+        super().randomize(None)
+        self._offset = self.R.uniform(low=0, high=100)
+
+    def __call__(self, img):
+        self.randomize()
+        if not self._do_transform:
+            return img
+        return img + self._offset
+
+transform = RandShiftIntensity()
+transform.set_random_state(seed=0)
+print(transform(10))
+
+
+
+
+randomize(data)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

Compose#

+
+
+class monai.transforms.Compose(transforms=None, map_items=True, unpack_items=False, log_stats=False, lazy=False, overrides=None, logger_name=False)[source]#
+

Compose provides the ability to chain a series of callables together in +a sequential manner. Each transform in the sequence must take a single +argument and return a single value.

+

Compose can be used in two ways:

+
    +
  1. With a series of transforms that accept and return a single +ndarray / tensor / tensor-like parameter.

  2. +
  3. With a series of transforms that accept and return a dictionary that +contains one or more parameters. Such transforms must have pass-through +semantics that unused values in the dictionary must be copied to the return +dictionary. It is required that the dictionary is copied between input +and output of each transform.

  4. +
+

If some transform takes a data item dictionary as input, and returns a +sequence of data items in the transform chain, all following transforms +will be applied to each item of this list if map_items is True (the +default). If map_items is False, the returned sequence is passed whole +to the next callable in the chain.

+

For example:

+

A Compose([transformA, transformB, transformC], +map_items=True)(data_dict) could achieve the following patch-based +transformation on the data_dict input:

+
    +
  1. transformA normalizes the intensity of ‘img’ field in the data_dict.

  2. +
  3. transformB crops out image patches from the ‘img’ and ‘seg’ of +data_dict, and return a list of three patch samples:

    +
    {'img': 3x100x100 data, 'seg': 1x100x100 data, 'shape': (100, 100)}
    +                     applying transformB
    +                         ---------->
    +[{'img': 3x20x20 data, 'seg': 1x20x20 data, 'shape': (20, 20)},
    + {'img': 3x20x20 data, 'seg': 1x20x20 data, 'shape': (20, 20)},
    + {'img': 3x20x20 data, 'seg': 1x20x20 data, 'shape': (20, 20)},]
    +
    +
    +
  4. +
  5. transformC then randomly rotates or flips ‘img’ and ‘seg’ of +each dictionary item in the list returned by transformB.

  6. +
+

The composed transforms will be set the same global random seed if user called +set_determinism().

+

When using the pass-through dictionary operation, you can make use of +monai.transforms.adaptors.adaptor to wrap transforms that don’t conform +to the requirements. This approach allows you to use transforms from +otherwise incompatible libraries with minimal additional work.

+
+

Note

+

In many cases, Compose is not the best way to create pre-processing +pipelines. Pre-processing is often not a strictly sequential series of +operations, and much of the complexity arises when a not-sequential +set of functions must be called as if it were a sequence.

+

Example: images and labels +Images typically require some kind of normalization that labels do not. +Both are then typically augmented through the use of random rotations, +flips, and deformations. +Compose can be used with a series of transforms that take a dictionary +that contains ‘image’ and ‘label’ entries. This might require wrapping +torchvision transforms before passing them to compose. +Alternatively, one can create a class with a __call__ function that +calls your pre-processing functions taking into account that not all of +them are called on the labels.

+
+
+
Lazy resampling:

Lazy resampling is an experimental feature introduced in 1.2. Its purpose is +to reduce the number of resample operations that must be carried out when executing +a pipeline of transforms. This can provide significant performance improvements in +terms of pipeline executing speed and memory usage, but can also significantly +reduce the loss of information that occurs when performing a number of spatial +resamples in succession.

+

Lazy resampling can be thought of as acting in a similar fashion to the Affine & RandAffine +transforms, in that they allow several spatial transform operations can be specified and carried out with +a single resample step. Unlike these transforms, however, lazy resampling can operate on any set of +transforms specified in any ordering. The user is free to mix monai transforms with transforms from other +libraries; lazy resampling will determine the minimum number of resample steps required in order to +execute the pipeline.

+

Lazy resampling works with monai Dataset classes that provide caching and persistence. However, if you +are implementing your own caching dataset implementation and wish to make use of lazy resampling, you +should ensure that you fully execute the part of the pipeline that generates the data to be cached +before caching it. This is quite simply done however, as shown by the following example.

+

Lazy resampling can be enabled or disabled through the lazy parameter. This is specified as an +optional boolean parameter.

+
    +
  • False (default): Don’t perform any lazy resampling

  • +
  • None: Perform lazy resampling based on the ‘lazy’ properties of the transform instances.

  • +
  • True: Always perform lazy resampling if possible. This will ignore the lazy properties +of the transform instances

  • +
+

If you only want some of the pipeline to be executed lazily, there are two ways to achieve this.

+

The first way is to set lazy=True on your Compose instance and specify for each transform whether you +want it to be lazily executed or not.

+

The second way is to set lazy=True on your Compose instance and add ApplyPending or ApplyPendingd +transforms after the final transform in a sequence that you want to execute lazily. This can be done at multiple +points in the pipeline.

+
+
Example:

# run the part of the pipeline that needs to be cached +data = self.transform(data, end=self.post_cache_index)

+

# —

+

# fetch the data from the cache and run the rest of the pipeline +data = get_data_from_my_cache(data) +data = self.transform(data, start=self.post_cache_index)

+
+
+
+
+
+
Parameters:
+
    +
  • transforms (Union[Sequence[Callable], Callable, None]) – sequence of callables.

  • +
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. +defaults to True.

  • +
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. +defaults to False.

  • +
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which +disables the logger for processing pipeline errors. Setting it to True will enable logging to the +default logger name. Setting it to a string specifies the logger to which errors should be logged

  • +
  • lazy (UnionType[bool, None]) – whether to enable lazy evaluation for lazy transforms. This is an optional bool that can take +the following values. If lazy=False, lazy execution is disabled and transforms will be +carried out on a transform by transform basis. If lazy=True, all lazy transforms will +be executed by accumulating changes and resampling as few times as possible. If lazy is None, +Compose will perform lazy execution on lazy transforms that have their lazy flag set to True. +A monai.transforms.ApplyPending[d] transform in the pipeline will trigger the evaluation of +the pending operations and make the primary data up-to-date.

  • +
  • overrides (Optional[dict, None]) – this optional parameter allows you to specify a dictionary of parameters that should be overridden +when executing a pipeline. These each parameter that is compatible with a given transform is then applied +to that transform before it is executed. Note that overrides are currently only applied when lazy +is True. If lazy is False they are ignored. +currently supported args are: +{"mode", "padding_mode", "dtype", "align_corners", "resample_mode", device}, +please see also monai.transforms.lazy.apply_transforms() for more details.

  • +
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. +Setting this to False disables logging. Setting it to True enables logging to the default loggers. +Setting a string overrides the logger name to which logging is performed.

  • +
+
+
+
+
+__call__(input_, start=0, end=None, threading=False, lazy=None)[source]#
+

Call self as a function.

+
+ +
+
+flatten()[source]#
+

Return a Composition with a simple list of transforms, as opposed to any nested Compositions.

+

e.g., t1 = Compose([x, x, x, x, Compose([Compose([x, x]), x, x])]).flatten() +will result in the equivalent of t1 = Compose([x, x, x, x, x, x, x, x]).

+
+ +
+
+get_index_of_first(predicate)[source]#
+

get_index_of_first takes a predicate and returns the index of the first transform that +satisfies the predicate (ie. makes the predicate return True). If it is unable to find +a transform that satisfies the predicate, it returns None.

+

Example

+

c = Compose([Flip(…), Rotate90(…), Zoom(…), RandRotate(…), Resize(…)])

+

print(c.get_index_of_first(lambda t: isinstance(t, RandomTrait))) +>>> 3 +print(c.get_index_of_first(lambda t: isinstance(t, Compose))) +>>> None

+
+

Note

+

This is only performed on the transforms directly held by this instance. If this +instance has nested Compose transforms or other transforms that contain transforms, +it does not iterate into them.

+
+
+
Parameters:
+
    +
  • predicate – a callable that takes a single argument and returns a bool. When called

  • +
  • compose (it is passed a transform from the sequence of transforms contained by this) –

  • +
  • instance.

  • +
+
+
Returns:
+

The index of the first transform in the sequence for which predicate returns +True. None if no transform satisfies the predicate

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Compose

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

InvertibleTransform#

+
+
+class monai.transforms.InvertibleTransform[source]#
+

Classes for invertible transforms.

+

This class exists so that an invert method can be implemented. This allows, for +example, images to be cropped, rotated, padded, etc., during training and inference, +and after be returned to their original size before saving to file for comparison in +an external viewer.

+

When the inverse method is called:

+
+
    +
  • the inverse is called on each key individually, which allows for +different parameters being passed to each label (e.g., different +interpolation for image and label).

  • +
  • the inverse transforms are applied in a last-in-first-out order. As +the inverse is applied, its entry is removed from the list detailing +the applied transformations. That is to say that during the forward +pass, the list of applied transforms grows, and then during the +inverse it shrinks back down to an empty list.

  • +
+
+

We currently check that the id() of the transform is the same in the forward and +inverse directions. This is a useful check to ensure that the inverses are being +processed in the correct order.

+

Note to developers: When converting a transform to an invertible transform, you need to:

+
+
    +
  1. Inherit from this class.

  2. +
  3. In __call__, add a call to push_transform.

  4. +
  5. Any extra information that might be needed for the inverse can be included with the +dictionary extra_info. This dictionary should have the same keys regardless of +whether do_transform was True or False and can only contain objects that are +accepted in pytorch data loader’s collate function (e.g., None is not allowed).

  6. +
  7. Implement an inverse method. Make sure that after performing the inverse, +pop_transform is called.

  8. +
+
+
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Any

+
+
+
+ +
+
+inverse_update(data)[source]#
+

This function is to be called before every self.inverse(data), +update each MetaTensor data[key] using data[key_transforms] and data[key_meta_dict], +for MetaTensor backward compatibility 0.9.0.

+
+ +
+ +
+
+

TraceableTransform#

+
+
+class monai.transforms.TraceableTransform[source]#
+

Maintains a stack of applied transforms to data.

+
+
Data can be one of two types:
    +
  1. A MetaTensor (this is the preferred data type).

  2. +
  3. +
    A dictionary of data containing arrays/tensors and auxiliary metadata. In

    this case, a key must be supplied (this dictionary-based approach is deprecated).

    +
    +
    +
  4. +
+
+
+

If data is of type MetaTensor, then the applied transform will be added to data.applied_operations.

+
+
If data is a dictionary, then one of two things can happen:
    +
  1. If data[key] is a MetaTensor, the applied transform will be added to data[key].applied_operations.

  2. +
  3. +
    Else, the applied transform will be appended to an adjacent list using

    trace_key. If, for example, the key is image, then the transform +will be appended to image_transforms (this dictionary-based approach is deprecated).

    +
    +
    +
  4. +
+
+
Hopefully it is clear that there are three total possibilities:
    +
  1. data is MetaTensor

  2. +
  3. data is dictionary, data[key] is MetaTensor

  4. +
  5. data is dictionary, data[key] is not MetaTensor (this is a deprecated approach).

  6. +
+
+
+

The __call__ method of this transform class must be implemented so +that the transformation information is stored during the data transformation.

+

The information in the stack of applied transforms must be compatible with the +default collate, by only storing strings, numbers and arrays.

+

tracing could be enabled by self.set_tracing or setting +MONAI_TRACE_TRANSFORM when initializing the class.

+
+
+check_transforms_match(transform)[source]#
+

Check transforms are of same instance.

+
+
Return type:
+

None

+
+
+
+ +
+
+get_most_recent_transform(data, key=None, check=True, pop=False)[source]#
+

Get most recent transform for the stack.

+
+
Parameters:
+
    +
  • data – dictionary of data or MetaTensor.

  • +
  • key (Optional[Hashable, None]) – if data is a dictionary, data[key] will be modified.

  • +
  • check (bool) – if true, check that self is the same type as the most recently-applied transform.

  • +
  • pop (bool) – if true, remove the transform as it is returned.

  • +
+
+
Returns:
+

Dictionary of most recently applied transform

+
+
Raises:
+

- RuntimeError – data is neither MetaTensor nor dictionary

+
+
+
+ +
+
+get_transform_info()[source]#
+

Return a dictionary with the relevant information pertaining to an applied transform.

+
+
Return type:
+

dict

+
+
+
+ +
+
+pop_transform(data, key=None, check=True)[source]#
+

Return and pop the most recent transform.

+
+
Parameters:
+
    +
  • data – dictionary of data or MetaTensor

  • +
  • key (Optional[Hashable, None]) – if data is a dictionary, data[key] will be modified

  • +
  • check (bool) – if true, check that self is the same type as the most recently-applied transform.

  • +
+
+
Returns:
+

Dictionary of most recently applied transform

+
+
Raises:
+

- RuntimeError – data is neither MetaTensor nor dictionary

+
+
+
+ +
+
+push_transform(data, *args, **kwargs)[source]#
+

Push to a stack of applied transforms of data.

+
+
Parameters:
+
    +
  • data – dictionary of data or MetaTensor.

  • +
  • args – additional positional arguments to track_transform_meta.

  • +
  • kwargs – additional keyword arguments to track_transform_meta, +set replace=True (default False) to rewrite the last transform infor in +applied_operation/pending_operation based on self.get_transform_info().

  • +
+
+
+
+ +
+
+set_tracing(tracing)[source]#
+

Set whether to trace transforms.

+
+
Return type:
+

None

+
+
+
+ +
+
+static trace_key(key=None)[source]#
+

The key to store the stack of applied transforms.

+
+ +
+
+trace_transform(to_trace)[source]#
+

Temporarily set the tracing status of a transform with a context manager.

+
+ +
+
+classmethod track_transform_meta(data, key=None, sp_size=None, affine=None, extra_info=None, orig_size=None, transform_info=None, lazy=False)[source]#
+

Update a stack of applied/pending transforms metadata of data.

+
+
Parameters:
+
    +
  • data – dictionary of data or MetaTensor.

  • +
  • key (Optional[Hashable, None]) – if data is a dictionary, data[key] will be modified.

  • +
  • sp_size – the expected output spatial size when the transform is applied. +it can be tensor or numpy, but will be converted to a list of integers.

  • +
  • affine – the affine representation of the (spatial) transform in the image space. +When the transform is applied, meta_tensor.affine will be updated to meta_tensor.affine @ affine.

  • +
  • extra_info (Optional[dict, None]) – if desired, any extra information pertaining to the applied +transform can be stored in this dictionary. These are often needed for +computing the inverse transformation.

  • +
  • orig_size (Optional[tuple, None]) – sometimes during the inverse it is useful to know what the size +of the original image was, in which case it can be supplied here.

  • +
  • transform_info – info from self.get_transform_info().

  • +
  • lazy – whether to push the transform to pending_operations or applied_operations.

  • +
+
+
Returns:
+

For backward compatibility, if data is a dictionary, it returns the dictionary with +updated data[key]. Otherwise, this function returns a MetaObj with updated transform metadata.

+
+
+
+ +
+
+static transform_info_keys()[source]#
+

The keys to store necessary info of an applied transform.

+
+ +
+ +
+
+

BatchInverseTransform#

+
+
+class monai.transforms.BatchInverseTransform(transform, loader, collate_fn=<function no_collation>, num_workers=0, detach=True, pad_batch=True, fill_value=None)[source]#
+

Perform inverse on a batch of data. This is useful if you have inferred a batch of images and want to invert +them all.

+
+
+__init__(transform, loader, collate_fn=<function no_collation>, num_workers=0, detach=True, pad_batch=True, fill_value=None)[source]#
+
+
Parameters:
+
    +
  • transform (InvertibleTransform) – a callable data transform on input data.

  • +
  • loader (DataLoader) – data loader used to run transforms and generate the batch of data.

  • +
  • collate_fn (UnionType[Callable, None]) – how to collate data after inverse transformations. +default won’t do any collation, so the output will be a list of size batch size.

  • +
  • num_workers (UnionType[int, None]) – number of workers when run data loader for inverse transforms, +default to 0 as only run 1 iteration and multi-processing may be even slower. +if the transforms are really slow, set num_workers for multi-processing. +if set to None, use the num_workers of the transform data loader.

  • +
  • detach (bool) – whether to detach the tensors. Scalars tensors will be detached into number types +instead of torch tensors.

  • +
  • pad_batch (bool) – when the items in a batch indicate different batch size, +whether to pad all the sequences to the longest. +If False, the batch size will be the length of the shortest sequence.

  • +
  • fill_value – the value to fill the padded sequences when pad_batch=True.

  • +
+
+
+
+ +
+ +
+
+

Decollated#

+
+
+class monai.transforms.Decollated(keys=None, detach=True, pad_batch=True, fill_value=None, allow_missing_keys=False)[source]#
+

Decollate a batch of data. If input is a dictionary, it also supports to only decollate specified keys. +Note that unlike most MapTransforms, it will delete the other keys that are not specified. +if keys=None, it will decollate all the data in the input. +It replicates the scalar values to every item of the decollated list.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable, None]) – keys of the corresponding items to decollate, note that it will delete other keys not specified. +if None, will decollate all the keys. see also: monai.transforms.compose.MapTransform.

  • +
  • detach (bool) – whether to detach the tensors. Scalars tensors will be detached into number types +instead of torch tensors.

  • +
  • pad_batch (bool) – when the items in a batch indicate different batch size, +whether to pad all the sequences to the longest. +If False, the batch size will be the length of the shortest sequence.

  • +
  • fill_value – the value to fill the padded sequences when pad_batch=True.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+

OneOf#

+
+
+class monai.transforms.OneOf(transforms=None, weights=None, map_items=True, unpack_items=False, log_stats=False, lazy=None, overrides=None, logger_name=False)[source]#
+

OneOf provides the ability to randomly choose one transform out of a +list of callables with pre-defined probabilities for each.

+
+
Parameters:
+
    +
  • transforms (Union[Sequence[Callable], Callable, None]) – sequence of callables.

  • +
  • weights (Union[Sequence[float], float, None]) – probabilities corresponding to each callable in transforms. +Probabilities are normalized to sum to one.

  • +
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. +defaults to True.

  • +
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. +defaults to False.

  • +
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which +disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the +default logger name. Setting it to a string specifies the logger to which errors should be logged

  • +
  • lazy (Optional[bool, None]) – whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will +be executed by accumulating changes and resampling as few times as possible. If False, transforms will be +carried out on a transform by transform basis. +A monai.transforms.Identity[D] transform in the pipeline will trigger the evaluation of +the pending operations and make the primary data up-to-date.

  • +
  • overrides (Optional[dict, None]) – this optional parameter allows you to specify a dictionary of parameters that should be overridden +when executing a pipeline. These each parameter that is compatible with a given transform is then applied +to that transform before it is executed. Note that overrides are currently only applied when lazy +is True. If lazy is False they are ignored. +currently supported args are: +{"mode", "padding_mode", "dtype", "align_corners", "resample_mode", device}, +please see also monai.transforms.lazy.apply_transforms() for more details.

  • +
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. +Setting this to False disables logging. Setting it to True enables logging to the default loggers. +Setting a string overrides the logger name to which logging is performed.

  • +
+
+
+
+
+flatten()[source]#
+

Return a Composition with a simple list of transforms, as opposed to any nested Compositions.

+

e.g., t1 = Compose([x, x, x, x, Compose([Compose([x, x]), x, x])]).flatten() +will result in the equivalent of t1 = Compose([x, x, x, x, x, x, x, x]).

+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

RandomOrder#

+
+
+class monai.transforms.RandomOrder(transforms=None, map_items=True, unpack_items=False, log_stats=False, lazy=None, overrides=None, logger_name=False)[source]#
+

RandomOrder provides the ability to apply a list of transformations in random order.

+
+
Parameters:
+
    +
  • transforms (Union[Sequence[Callable], Callable, None]) – sequence of callables.

  • +
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. +defaults to True.

  • +
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. +defaults to False.

  • +
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which +disables the logger for processing pipeline errors. Setting it to True will enable logging to the +default logger name. Setting it to a string specifies the logger to which errors should be logged

  • +
  • lazy (Optional[bool, None]) – whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will +be executed by accumulating changes and resampling as few times as possible. If False, transforms will be +carried out on a transform by transform basis. +A monai.transforms.Identity[D] transform in the pipeline will trigger the evaluation of +the pending operations and make the primary data up-to-date.

  • +
  • overrides (Optional[dict, None]) – this optional parameter allows you to specify a dictionary of parameters that should be overridden +when executing a pipeline. These each parameter that is compatible with a given transform is then applied +to that transform before it is executed. Note that overrides are currently only applied when lazy +is True. If lazy is False they are ignored. +currently supported args are: +{"mode", "padding_mode", "dtype", "align_corners", "resample_mode", device}, +please see also monai.transforms.lazy.apply_transforms() for more details.

  • +
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. +Setting this to False disables logging. Setting it to True enables logging to the default loggers. +Setting a string overrides the logger name to which logging is performed.

  • +
+
+
+
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

SomeOf#

+
+
+class monai.transforms.SomeOf(transforms=None, map_items=True, unpack_items=False, log_stats=False, *, lazy=False, num_transforms=None, replace=False, weights=None, logger_name=False)[source]#
+

SomeOf samples a different sequence of transforms to apply each time it is called.

+

It can be configured to sample a fixed or varying number of transforms each time its called. Samples are drawn +uniformly, or from user supplied transform weights. When varying the number of transforms sampled per call, +the number of transforms to sample that call is sampled uniformly from a range supplied by the user.

+
+
Parameters:
+
    +
  • transforms (Union[Sequence[Callable], Callable, None]) – list of callables.

  • +
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. +Defaults to True.

  • +
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. +Defaults to False.

  • +
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which +disables the logger for processing pipeline errors. Setting it to True will enable logging to the +default logger name. Setting it to a string specifies the logger to which errors should be logged

  • +
  • num_transforms (Union[int, tuple[int, int], None]) – a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of +transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. +None sets it to len(transforms). Default to None.

  • +
  • replace (bool) – whether to sample with replacement. Defaults to False.

  • +
  • weights (Optional[list[int], None]) – weights to use in for sampling transforms. Will be normalized to 1. Default: None (uniform).

  • +
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. +Setting this to False disables logging. Setting it to True enables logging to the default loggers. +Setting a string overrides the logger name to which logging is performed.

  • +
+
+
+
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+
+

Functionals#

+
+

Crop and Pad (functional)#

+

A collection of “functional” transforms for spatial operations +Project-MONAI/MONAI

+
+
+monai.transforms.croppad.functional.crop_func(img, slices, lazy, transform_info)[source]#
+

Functional implementation of cropping a MetaTensor. This function operates eagerly or lazily according +to lazy (default False).

+
+
Parameters:
+
    +
  • img (Tensor) – data to be transformed, assuming img is channel-first and cropping doesn’t apply to the channel dim.

  • +
  • slices (tuple[slice, …]) – the crop slices computed based on specified center & size or start & end or slices.

  • +
  • lazy (bool) – a flag indicating whether the operation should be performed in a lazy fashion or not.

  • +
  • transform_info (dict) – a dictionary with the relevant information pertaining to an applied transform.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+monai.transforms.croppad.functional.crop_or_pad_nd(img, translation_mat, spatial_size, mode, **kwargs)[source]#
+

Crop or pad using the translation matrix and spatial size. The translation coefficients are rounded +to the nearest integers. For a more generic implementation, please see monai.transforms.SpatialResample.

+
+
Parameters:
+
    +
  • img (Tensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • +
  • translation_mat – the translation matrix to be applied to the image. A translation matrix generated by, +for example, monai.transforms.utils.create_translate(). The translation coefficients are rounded +to the nearest integers.

  • +
  • spatial_size (tuple[int, …]) – the spatial size of the output image.

  • +
  • mode (str) – the padding mode.

  • +
  • kwargs – other arguments for the np.pad or torch.pad function.

  • +
+
+
+
+ +
+
+monai.transforms.croppad.functional.pad_func(img, to_pad, transform_info, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
+

Functional implementation of padding a MetaTensor. This function operates eagerly or lazily according +to lazy (default False).

+

torch.nn.functional.pad is used unless the mode or kwargs are not available in torch, +in which case np.pad will be used.

+
+
Parameters:
+
    +
  • img (Tensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • +
  • to_pad (tuple[tuple[int, int]]) – the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), …]. +note that it including channel dimension.

  • +
  • transform_info (dict) – a dictionary with the relevant information pertaining to an applied transform.

  • +
  • mode (str) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • lazy (bool) – a flag indicating whether the operation should be performed in a lazy fashion or not.

  • +
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+monai.transforms.croppad.functional.pad_nd(img, to_pad, mode=PytorchPadMode.CONSTANT, **kwargs)[source]#
+

Pad img for a given an amount of padding in each dimension.

+

torch.nn.functional.pad is used unless the mode or kwargs are not available in torch, +in which case np.pad will be used.

+
+
Parameters:
+
    +
  • img (~NdarrayTensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • +
  • to_pad (list[tuple[int, int]]) – the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), …]. +default to self.to_pad.

  • +
  • mode (str) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+

Spatial (functional)#

+

A collection of “functional” transforms for spatial operations +Project-MONAI/MONAI

+
+
+monai.transforms.spatial.functional.affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, lazy, transform_info)[source]#
+

Functional implementation of affine. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
    +
  • img – data to be changed, assuming img is channel-first.

  • +
  • affine – the affine transformation to be applied, it can be a 3x3 or 4x4 matrix. This should be defined +for the voxel space spatial centers (float(size - 1)/2).

  • +
  • grid – used in non-lazy mode to pre-compute the grid to do the resampling.

  • +
  • resampler – the resampler function, see also: monai.transforms.Resample.

  • +
  • sp_size – output image spatial size.

  • +
  • mode – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode – {"zeros", "border", "reflection"} +Padding mode for outside grid values. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • do_resampling – whether to do the resampling, this is a flag for the use case of updating metadata but +skipping the actual (potentially heavy) resampling operation.

  • +
  • image_only – if True return only the image volume, otherwise return (image, affine).

  • +
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • +
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • +
+
+
+
+ +
+
+monai.transforms.spatial.functional.flip(img, sp_axes, lazy, transform_info)[source]#
+

Functional implementation of flip. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
    +
  • img – data to be changed, assuming img is channel-first.

  • +
  • sp_axes – spatial axes along which to flip over. +If None, will flip over all of the axes of the input array. +If axis is negative it counts from the last to the first axis. +If axis is a tuple of ints, flipping is performed on all of the axes +specified in the tuple.

  • +
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • +
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • +
+
+
+
+ +
+
+monai.transforms.spatial.functional.orientation(img, original_affine, spatial_ornt, lazy, transform_info)[source]#
+

Functional implementation of changing the input image’s orientation into the specified based on spatial_ornt. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
    +
  • img – data to be changed, assuming img is channel-first.

  • +
  • original_affine – original affine of the input image.

  • +
  • spatial_ornt – orientations of the spatial axes, +see also https://nipy.org/nibabel/reference/nibabel.orientations.html

  • +
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • +
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+monai.transforms.spatial.functional.resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, lazy, transform_info)[source]#
+

Functional implementation of resize. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
    +
  • img – data to be changed, assuming img is channel-first.

  • +
  • out_size – expected shape of spatial dimensions after resize operation.

  • +
  • mode – {"nearest", "nearest-exact", "linear", +"bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • align_corners – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’.

  • +
  • dtype – data type for resampling computation. If None, use the data type of input data.

  • +
  • input_ndim – number of spatial dimensions.

  • +
  • anti_aliasing – whether to apply a Gaussian filter to smooth the image prior +to downsampling. It is crucial to filter when downsampling +the image to avoid aliasing artifacts. See also skimage.transform.resize

  • +
  • anti_aliasing_sigma – {float, tuple of floats}, optional +Standard deviation for Gaussian filtering used when anti-aliasing.

  • +
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • +
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • +
+
+
+
+ +
+
+monai.transforms.spatial.functional.rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, lazy, transform_info)[source]#
+

Functional implementation of rotate. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
+
+
+
+ +
+
+monai.transforms.spatial.functional.rotate90(img, axes, k, lazy, transform_info)[source]#
+

Functional implementation of rotate90. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
    +
  • img – data to be changed, assuming img is channel-first.

  • +
  • axes – 2 int numbers, defines the plane to rotate with 2 spatial axes. +If axis is negative it counts from the last to the first axis.

  • +
  • k – number of times to rotate by 90 degrees.

  • +
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • +
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • +
+
+
+
+ +
+
+monai.transforms.spatial.functional.spatial_resample(img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, lazy, transform_info)[source]#
+

Functional implementation of resampling the input image to the specified dst_affine matrix and spatial_size. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+monai.transforms.spatial.functional.zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, lazy, transform_info)[source]#
+

Functional implementation of zoom. +This function operates eagerly or lazily according to +lazy (default False).

+
+
Parameters:
+
+
+
+
+ +
+
+
+

Vanilla Transforms#

+
+

Crop and Pad#

+
+

PadListDataCollate#

+
+
+class monai.transforms.PadListDataCollate(method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, **kwargs)[source]#
+

Same as MONAI’s list_data_collate, except any tensors are centrally padded to match the shape of the biggest +tensor in each dimension. This transform is useful if some of the applied transforms generate batch data of +different sizes.

+

This can be used on both list and dictionary data. +Note that in the case of the dictionary data, it may add the transform information to the list of invertible transforms +if input batch have different spatial shape, so need to call static method: inverse before inverting other transforms.

+

Note that normally, a user won’t explicitly use the __call__ method. Rather this would be passed to the DataLoader. +This means that __call__ handles data as it comes out of a DataLoader, containing batch dimension. However, the +inverse operates on dictionaries containing images of shape C,H,W,[D]. This asymmetry is necessary so that we can +pass the inverse through multiprocessing.

+
+
Parameters:
+
+
+
+
+
+__call__(batch)[source]#
+
+
Parameters:
+

batch (Any) – batch of data to pad-collate

+
+
+
+ +
+
+static inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, ndarray]

+
+
+
+ +
+ +
+
+

Pad#

+
+
+class monai.transforms.Pad(to_pad=None, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
+

Perform padding for a given an amount of padding in each dimension.

+

torch.nn.functional.pad is used unless the mode or kwargs are not available in torch, +in which case np.pad will be used.

+
+
Parameters:
+
    +
  • to_pad (Optional[tuple[tuple[int, int]], None]) – the amount to pad in each dimension (including the channel) [(low_H, high_H), (low_W, high_W), …]. +if None, must provide in the __call__ at runtime.

  • +
  • mode (str) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +requires pytorch >= 1.10 for best compatibility.

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+__call__(img, to_pad=None, mode=None, lazy=None, **kwargs)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • +
  • to_pad (Optional[tuple[tuple[int, int]], None]) – the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), …]. +default to self.to_pad.

  • +
  • mode (Optional[str, None]) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+compute_pad_width(spatial_shape)[source]#
+

dynamically compute the pad width according to the spatial shape. +the output is the amount of padding for all dimensions including the channel.

+
+
Parameters:
+

spatial_shape (Sequence[int]) – spatial shape of the original image.

+
+
Return type:
+

tuple[tuple[int, int]]

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

MetaTensor

+
+
+
+ +
+ +
+
+

SpatialPad#

+example of SpatialPad +
+
+class monai.transforms.SpatialPad(spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
+

Performs padding to the data, symmetric for all sides or all on one side for each dimension.

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int, tuple[UnionType[tuple[int, …], int], …]]) – the spatial size of output data after padding, if a dimension of the input +data size is larger than the pad size, will not pad that dimension. +If its components have non-positive values, the corresponding size of input image will be used +(no padding). for example: if the spatial size of input data is [30, 30, 30] and +spatial_size=[32, 25, -1], the spatial size of output data will be [32, 30, 30].

  • +
  • method (str) – {"symmetric", "end"} +Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • +
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+compute_pad_width(spatial_shape)[source]#
+

dynamically compute the pad width according to the spatial shape.

+
+
Parameters:
+

spatial_shape (Sequence[int]) – spatial shape of the original image.

+
+
Return type:
+

tuple[tuple[int, int]]

+
+
+
+ +
+ +
+
+

BorderPad#

+example of BorderPad +
+
+class monai.transforms.BorderPad(spatial_border, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
+

Pad the input data by adding specified borders to every dimension.

+
+
Parameters:
+
    +
  • spatial_border (UnionType[Sequence[int], int]) –

    specified size for every spatial border. Any -ve values will be set to 0. It can be 3 shapes:

    +
      +
    • single int number, pad all the borders with the same size.

    • +
    • length equals the length of image shape, pad every spatial dimension separately. +for example, image shape(CHW) is [1, 4, 4], spatial_border is [2, 1], +pad every border of H dim with 2, pad every border of W dim with 1, result shape is [1, 8, 6].

    • +
    • length equals 2 x (length of image shape), pad every border of every dimension separately. +for example, image shape(CHW) is [1, 4, 4], spatial_border is [1, 2, 3, 4], pad top of H dim with 1, +pad bottom of H dim with 2, pad left of W dim with 3, pad right of W dim with 4. +the result shape is [1, 7, 11].

    • +
    +

  • +
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+compute_pad_width(spatial_shape)[source]#
+

dynamically compute the pad width according to the spatial shape. +the output is the amount of padding for all dimensions including the channel.

+
+
Parameters:
+

spatial_shape (Sequence[int]) – spatial shape of the original image.

+
+
Return type:
+

tuple[tuple[int, int]]

+
+
+
+ +
+ +
+
+

DivisiblePad#

+example of DivisiblePad +
+
+class monai.transforms.DivisiblePad(k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, lazy=False, **kwargs)[source]#
+

Pad the input data, so that the spatial sizes are divisible by k.

+
+
+__init__(k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, lazy=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • k (UnionType[Sequence[int], int]) – the target k for each spatial dimension. +if k is negative or 0, the original size is preserved. +if k is an int, the same k be applied to all the input spatial dimensions.

  • +
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • method (str) – {"symmetric", "end"} +Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+

See also monai.transforms.SpatialPad

+
+ +
+
+compute_pad_width(spatial_shape)[source]#
+

dynamically compute the pad width according to the spatial shape. +the output is the amount of padding for all dimensions including the channel.

+
+
Parameters:
+

spatial_shape (Sequence[int]) – spatial shape of the original image.

+
+
Return type:
+

tuple[tuple[int, int]]

+
+
+
+ +
+ +
+
+

Crop#

+
+
+class monai.transforms.Crop(lazy=False)[source]#
+

Perform crop operations on the input image.

+
+
Parameters:
+

lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

+
+
+
+
+__call__(img, slices, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t apply to the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+static compute_slices(roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None)[source]#
+

Compute the crop slices based on specified center & size or start & end or slices.

+
+
Parameters:
+
    +
  • roi_center (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for center of the crop ROI.

  • +
  • roi_size (Union[Sequence[int], ndarray, Tensor, None]) – size of the crop ROI, if a dimension of ROI size is larger than image size, +will not crop that dimension of the image.

  • +
  • roi_start (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for start of the crop ROI.

  • +
  • roi_end (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for end of the crop ROI, if a coordinate is out of image, +use the end coordinate of image.

  • +
  • roi_slices (Optional[Sequence[slice], None]) – list of slices for each of the spatial dimensions.

  • +
+
+
Return type:
+

tuple[slice]

+
+
+
+ +
+
+inverse(img)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

MetaTensor

+
+
+
+ +
+ +
+
+

SpatialCrop#

+example of SpatialCrop +
+
+class monai.transforms.SpatialCrop(roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, lazy=False)[source]#
+

General purpose cropper to produce sub-volume region of interest (ROI). +If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. +So the cropped result may be smaller than the expected ROI, and the cropped results of several images may +not have exactly the same shape. +It can support to crop ND spatial (channel-first) data.

+
+
The cropped region can be parameterised in various ways:
    +
  • a list of slices for each spatial dimension (allows for use of negative indexing and None)

  • +
  • a spatial center and size

  • +
  • the start and end coordinates of the ROI

  • +
+
+
+
+
+__call__(img, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t apply to the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, lazy=False)[source]#
+
+
Parameters:
+
    +
  • roi_center (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for center of the crop ROI.

  • +
  • roi_size (Union[Sequence[int], ndarray, Tensor, None]) – size of the crop ROI, if a dimension of ROI size is larger than image size, +will not crop that dimension of the image.

  • +
  • roi_start (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for start of the crop ROI.

  • +
  • roi_end (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for end of the crop ROI, if a coordinate is out of image, +use the end coordinate of image.

  • +
  • roi_slices (Optional[Sequence[slice], None]) – list of slices for each of the spatial dimensions.

  • +
+
+
+
+ +
+ +
+
+

CenterSpatialCrop#

+example of CenterSpatialCrop +
+
+class monai.transforms.CenterSpatialCrop(roi_size, lazy=False)[source]#
+

Crop at the center of image with specified ROI size. +If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. +So the cropped result may be smaller than the expected ROI, and the cropped results of several images may +not have exactly the same shape.

+
+
Parameters:
+

roi_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224,224,128] +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

+
+
+
+
+__call__(img, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t apply to the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+compute_slices(spatial_size)[source]#
+

Compute the crop slices based on specified center & size or start & end or slices.

+
+
Parameters:
+
    +
  • roi_center – voxel coordinates for center of the crop ROI.

  • +
  • roi_size – size of the crop ROI, if a dimension of ROI size is larger than image size, +will not crop that dimension of the image.

  • +
  • roi_start – voxel coordinates for start of the crop ROI.

  • +
  • roi_end – voxel coordinates for end of the crop ROI, if a coordinate is out of image, +use the end coordinate of image.

  • +
  • roi_slices – list of slices for each of the spatial dimensions.

  • +
+
+
Return type:
+

tuple[slice]

+
+
+
+ +
+ +
+
+

RandSpatialCrop#

+example of RandSpatialCrop +
+
+class monai.transforms.RandSpatialCrop(roi_size, max_roi_size=None, random_center=True, random_size=True, lazy=False)[source]#
+

Crop image with random size or specific size ROI. It can crop at a random position as center +or at the image center. And allows to set the minimum and maximum size to limit the randomly generated ROI.

+

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results +of several images may not have exactly the same shape.

+
+
Parameters:
+
    +
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. +if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size +can specify the max crop region size. if None, defaults to the input image size. +if its components have non-positive values, the corresponding size of input image will be used.

  • +
  • random_center (bool) – crop at random position as center or the image center.

  • +
  • random_size (bool) – crop with random size or specific size ROI. +if True, the actual size is sampled from randint(roi_size, max_roi_size + 1).

  • +
+
+
+
+
+__call__(img, randomize=True, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t apply to the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+randomize(img_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandSpatialCropSamples#

+example of RandSpatialCropSamples +
+
+class monai.transforms.RandSpatialCropSamples(roi_size, num_samples, max_roi_size=None, random_center=True, random_size=True, lazy=False)[source]#
+

Crop image with random size or specific size ROI to generate a list of N samples. +It can crop at a random position as center or at the image center. And allows to set +the minimum size to limit the randomly generated ROI. +It will return a list of cropped images.

+

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped +results of several images may not have exactly the same shape.

+
+
Parameters:
+
    +
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. +if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • num_samples (int) – number of samples (crop regions) to take in the returned list.

  • +
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size +can specify the max crop region size. if None, defaults to the input image size. +if its components have non-positive values, the corresponding size of input image will be used.

  • +
  • random_center (bool) – crop at random position as center or the image center.

  • +
  • random_size (bool) – crop with random size or specific size ROI. +The actual size is sampled from randint(roi_size, img_size).

  • +
+
+
Raises:
+

ValueError – When num_samples is nonpositive.

+
+
+
+
+__call__(img, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +cropping doesn’t change the channel dim.

+
+
Return type:
+

list[Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandSpatialCropSamples

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

CropForeground#

+example of CropForeground +
+
+class monai.transforms.CropForeground(select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, return_coords=False, k_divisible=1, mode=PytorchPadMode.CONSTANT, lazy=False, **pad_kwargs)[source]#
+

Crop an image using a bounding box. The bounding box is generated by selecting foreground using select_fn +at channels channel_indices. margin is added in each spatial dimension of the bounding box. +The typical usage is to help training and evaluation if the valid part is small in the whole medical image. +Users can define arbitrary function to select expected foreground from the whole image or specified channels. +And it can also add margin to every dim of the bounding box of foreground object. +For example:

+
image = np.array(
+    [[[0, 0, 0, 0, 0],
+      [0, 1, 2, 1, 0],
+      [0, 1, 3, 2, 0],
+      [0, 1, 2, 1, 0],
+      [0, 0, 0, 0, 0]]])  # 1x5x5, single channel 5x5 image
+
+
+def threshold_at_one(x):
+    # threshold at 1
+    return x > 1
+
+
+cropper = CropForeground(select_fn=threshold_at_one, margin=0)
+print(cropper(image))
+[[[2, 1],
+  [3, 2],
+  [2, 1]]]
+
+
+
+
+__call__(img, mode=None, lazy=None, **pad_kwargs)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t change the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, return_coords=False, k_divisible=1, mode=PytorchPadMode.CONSTANT, lazy=False, **pad_kwargs)[source]#
+
+
Parameters:
+
    +
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • +
  • channel_indices (Union[Iterable[int], int, None]) – if defined, select foreground only on the specified channels +of image. if None, select foreground on the whole image.

  • +
  • margin (UnionType[Sequence[int], int]) – add margin value to spatial dims of the bounding box, if only 1 value provided, use it for all dims.

  • +
  • allow_smaller (bool) – when computing box size with margin, whether allow the image size to be smaller +than box size, default to True. if the margined size is larger than image size, will pad with +specified mode.

  • +
  • return_coords (bool) – whether return the coordinates of spatial bounding box for foreground.

  • +
  • k_divisible (UnionType[Sequence[int], int]) – make each spatial dimension to be divisible by k, default to 1. +if k_divisible is an int, the same k be applied to all the input spatial dimensions.

  • +
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+ +
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+compute_bounding_box(img)[source]#
+

Compute the start points and end points of bounding box to crop. +And adjust bounding box coords to be divisible by k.

+
+
Return type:
+

tuple[ndarray, ndarray]

+
+
+
+ +
+
+crop_pad(img, box_start, box_end, mode=None, lazy=False, **pad_kwargs)[source]#
+

Crop and pad based on the bounding box.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(img)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

MetaTensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandWeightedCrop#

+example of RandWeightedCrop +
+
+class monai.transforms.RandWeightedCrop(spatial_size, num_samples=1, weight_map=None, lazy=False)[source]#
+

Samples a list of num_samples image patches according to the provided weight_map.

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the image patch e.g. [224, 224, 128]. +If its components have non-positive values, the corresponding size of img will be used.

  • +
  • num_samples (int) – number of samples (image patches) to take in the returned list.

  • +
  • weight_map (Union[ndarray, Tensor, None]) – weight map used to generate patch samples. The weights must be non-negative. +Each element denotes a sampling weight of the spatial location. 0 indicates no sampling. +It should be a single-channel array in shape, for example, (1, spatial_dim_0, spatial_dim_1, …).

  • +
+
+
+
+
+__call__(img, weight_map=None, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – input image to sample patches from. assuming img is a channel-first array.

  • +
  • weight_map (Union[ndarray, Tensor, None]) – weight map used to generate patch samples. The weights must be non-negative. +Each element denotes a sampling weight of the spatial location. 0 indicates no sampling. +It should be a single-channel array in shape, for example, (1, spatial_dim_0, spatial_dim_1, …)

  • +
  • randomize (bool) – whether to execute random operations, default to True.

  • +
+
+
Return type:
+

list[Tensor]

+
+
Returns:
+

A list of image patches

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(weight_map)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandCropByPosNegLabel#

+example of RandCropByPosNegLabel +
+
+class monai.transforms.RandCropByPosNegLabel(spatial_size, label=None, pos=1.0, neg=1.0, num_samples=1, image=None, image_threshold=0.0, fg_indices=None, bg_indices=None, allow_smaller=False, lazy=False)[source]#
+

Crop random fixed sized regions with the center being a foreground or background voxel +based on the Pos Neg Ratio. +And will return a list of arrays for all the cropped images. +For example, crop two (3 x 3) arrays from (5 x 5) array with pos/neg=1:

+
[[[0, 0, 0, 0, 0],
+  [0, 1, 2, 1, 0],            [[0, 1, 2],     [[2, 1, 0],
+  [0, 1, 3, 0, 0],     -->     [0, 1, 3],      [3, 0, 0],
+  [0, 0, 0, 0, 0],             [0, 0, 0]]      [0, 0, 0]]
+  [0, 0, 0, 0, 0]]]
+
+
+

If a dimension of the expected spatial size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than expected size, and the cropped +results of several images may not have exactly same shape. +And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the +valid crop ROI.

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +if its components have non-positive values, the corresponding size of label will be used. +for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • label (Optional[Tensor, None]) – the label image that is used for finding foreground/background, if None, must set at +self.__call__. Non-zero indicates foreground, zero indicates background.

  • +
  • pos (float) – used with neg together to calculate the ratio pos / (pos + neg) for the probability +to pick a foreground voxel as a center rather than a background voxel.

  • +
  • neg (float) – used with pos together to calculate the ratio pos / (pos + neg) for the probability +to pick a foreground voxel as a center rather than a background voxel.

  • +
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • +
  • image (Optional[Tensor, None]) – optional image data to help select valid area, can be same as img or another image array. +if not None, use label == 0 & image > image_threshold to select the negative +sample (background) center. So the crop center will only come from the valid image areas.

  • +
  • image_threshold (float) – if enabled image, use image > image_threshold to determine +the valid image content areas.

  • +
  • fg_indices (Union[ndarray, Tensor, None]) – if provided pre-computed foreground indices of label, will ignore above image and +image_threshold, and randomly select crop centers based on them, need to provide fg_indices +and bg_indices together, expect to be 1 dim array of spatial indices after flattening. +a typical usage is to call FgBgToIndices transform first and cache the results.

  • +
  • bg_indices (Union[ndarray, Tensor, None]) – if provided pre-computed background indices of label, will ignore above image and +image_threshold, and randomly select crop centers based on them, need to provide fg_indices +and bg_indices together, expect to be 1 dim array of spatial indices after flattening. +a typical usage is to call FgBgToIndices transform first and cache the results.

  • +
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than +the requested ROI in any dimension. If True, any smaller dimensions will be set to +match the cropped size (i.e., no cropping in that dimension).

  • +
+
+
Raises:
+
    +
  • ValueError – When pos or neg are negative.

  • +
  • ValueError – When pos=0 and neg=0. Incompatible values.

  • +
+
+
+
+
+__call__(img, label=None, image=None, fg_indices=None, bg_indices=None, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – input data to crop samples from based on the pos/neg ratio of label and image. +Assumes img is a channel-first array.

  • +
  • label (Optional[Tensor, None]) – the label image that is used for finding foreground/background, if None, use self.label.

  • +
  • image (Optional[Tensor, None]) – optional image data to help select valid area, can be same as img or another image array. +use label == 0 & image > image_threshold to select the negative sample(background) center. +so the crop center will only exist on valid image area. if None, use self.image.

  • +
  • fg_indices (Union[ndarray, Tensor, None]) – foreground indices to randomly select crop centers, +need to provide fg_indices and bg_indices together.

  • +
  • bg_indices (Union[ndarray, Tensor, None]) – background indices to randomly select crop centers, +need to provide fg_indices and bg_indices together.

  • +
  • randomize (bool) – whether to execute the random operations, default to True.

  • +
+
+
Return type:
+

list[Tensor]

+
+
+
+ +
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(label=None, fg_indices=None, bg_indices=None, image=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandCropByLabelClasses#

+example of RandCropByLabelClasses +
+
+class monai.transforms.RandCropByLabelClasses(spatial_size, ratios=None, label=None, num_classes=None, num_samples=1, image=None, image_threshold=0.0, indices=None, allow_smaller=False, warn=True, max_samples_per_class=None, lazy=False)[source]#
+

Crop random fixed sized regions with the center being a class based on the specified ratios of every class. +The label data can be One-Hot format array or Argmax data. And will return a list of arrays for all the +cropped images. For example, crop two (3 x 3) arrays from (5 x 5) array with ratios=[1, 2, 3, 1]:

+
image = np.array([
+    [[0.0, 0.3, 0.4, 0.2, 0.0],
+    [0.0, 0.1, 0.2, 0.1, 0.4],
+    [0.0, 0.3, 0.5, 0.2, 0.0],
+    [0.1, 0.2, 0.1, 0.1, 0.0],
+    [0.0, 0.1, 0.2, 0.1, 0.0]]
+])
+label = np.array([
+    [[0, 0, 0, 0, 0],
+    [0, 1, 2, 1, 0],
+    [0, 1, 3, 0, 0],
+    [0, 0, 0, 0, 0],
+    [0, 0, 0, 0, 0]]
+])
+cropper = RandCropByLabelClasses(
+    spatial_size=[3, 3],
+    ratios=[1, 2, 3, 1],
+    num_classes=4,
+    num_samples=2,
+)
+label_samples = cropper(img=label, label=label, image=image)
+
+The 2 randomly cropped samples of `label` can be:
+[[0, 1, 2],     [[0, 0, 0],
+ [0, 1, 3],      [1, 2, 1],
+ [0, 0, 0]]      [1, 3, 0]]
+
+
+

If a dimension of the expected spatial size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than expected size, and the cropped +results of several images may not have exactly same shape. +And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the +valid crop ROI.

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +if its components have non-positive values, the corresponding size of label will be used. +for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • ratios (Optional[list[UnionType[float, int]], None]) – specified ratios of every class in the label to generate crop centers, including background class. +if None, every class will have the same ratio to generate crop centers.

  • +
  • label (Optional[Tensor, None]) – the label image that is used for finding every class, if None, must set at self.__call__.

  • +
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • +
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • +
  • image (Optional[Tensor, None]) – if image is not None, only return the indices of every class that are within the valid +region of the image (image > image_threshold).

  • +
  • image_threshold (float) – if enabled image, use image > image_threshold to +determine the valid image content area and select class indices only in this area.

  • +
  • indices (Optional[list[Union[ndarray, Tensor]], None]) – if provided pre-computed indices of every class, will ignore above image and +image_threshold, and randomly select crop centers based on them, expect to be 1 dim array +of spatial indices after flattening. a typical usage is to call ClassesToIndices transform first +and cache the results for better performance.

  • +
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than +the requested ROI in any dimension. If True, any smaller dimensions will remain +unchanged.

  • +
  • warn (bool) – if True prints a warning if a class is not present in the label.

  • +
  • max_samples_per_class (Optional[int, None]) – maximum length of indices to sample in each class to reduce memory consumption. +Default is None, no subsampling.

  • +
+
+
+
+
+__call__(img, label=None, image=None, indices=None, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – input data to crop samples from based on the ratios of every class, assumes img is a +channel-first array.

  • +
  • label (Optional[Tensor, None]) – the label image that is used for finding indices of every class, if None, use self.label.

  • +
  • image (Optional[Tensor, None]) – optional image data to help select valid area, can be same as img or another image array. +use image > image_threshold to select the centers only in valid region. if None, use self.image.

  • +
  • indices (Optional[list[Union[ndarray, Tensor]], None]) – list of indices for every class in the image, used to randomly select crop centers.

  • +
  • randomize (bool) – whether to execute the random operations, default to True.

  • +
+
+
Return type:
+

list[Tensor]

+
+
+
+ +
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(label=None, indices=None, image=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

ResizeWithPadOrCrop#

+example of ResizeWithPadOrCrop +
+
+class monai.transforms.ResizeWithPadOrCrop(spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, lazy=False, **pad_kwargs)[source]#
+

Resize an image to a target spatial size by either centrally cropping the image or +padding it evenly with a user-specified mode. +When the dimension is smaller than the target size, do symmetric padding along that dim. +When the dimension is larger than the target size, do central cropping along that dim.

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of output data after padding or crop. +If has non-positive values, the corresponding size of input image will be used (no padding).

  • +
  • method (str) – {"symmetric", "end"} +Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • +
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+__call__(img, mode=None, lazy=None, **pad_kwargs)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – data to pad or crop, assuming img is channel-first and +padding or cropping doesn’t apply to the channel dim.

  • +
  • mode (Optional[str, None]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(img)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

MetaTensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

BoundingRect#

+
+
+class monai.transforms.BoundingRect(select_fn=<function is_positive>)[source]#
+

Compute coordinates of axis-aligned bounding rectangles from input image img. +The output format of the coordinates is (shape is [channel, 2 * spatial dims]):

+
+
+
[[1st_spatial_dim_start, 1st_spatial_dim_end,

2nd_spatial_dim_start, 2nd_spatial_dim_end, +…, +Nth_spatial_dim_start, Nth_spatial_dim_end],

+

+

[1st_spatial_dim_start, 1st_spatial_dim_end, +2nd_spatial_dim_start, 2nd_spatial_dim_end, +…, +Nth_spatial_dim_start, Nth_spatial_dim_end]]

+
+
+
+

The bounding boxes edges are aligned with the input image edges. +This function returns [0, 0, …] if there’s no positive intensity.

+
+
Parameters:
+

select_fn (Callable) – function to select expected foreground, default is to select values > 0.

+
+
+
+
+__call__(img)[source]#
+

See also: monai.transforms.utils.generate_spatial_bounding_box.

+
+
Return type:
+

ndarray

+
+
+
+ +
+ +
+
+

RandScaleCrop#

+example of RandScaleCrop +
+
+class monai.transforms.RandScaleCrop(roi_scale, max_roi_scale=None, random_center=True, random_size=True, lazy=False)[source]#
+

Subclass of monai.transforms.RandSpatialCrop. Crop image with +random size or specific size ROI. It can crop at a random position as +center or at the image center. And allows to set the minimum and maximum +scale of image size to limit the randomly generated ROI.

+
+
Parameters:
+
    +
  • roi_scale (UnionType[Sequence[float], float]) – if random_size is True, it specifies the minimum crop size: roi_scale * image spatial size. +if random_size is False, it specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5]. +If its components have non-positive values, will use 1.0 instead, which means the input image size.

  • +
  • max_roi_scale (Union[Sequence[float], float, None]) – if random_size is True and roi_scale specifies the min crop region size, max_roi_scale +can specify the max crop region size: max_roi_scale * image spatial size. +if None, defaults to the input image size. if its components have non-positive values, +will use 1.0 instead, which means the input image size.

  • +
  • random_center (bool) – crop at random position as center or the image center.

  • +
  • random_size (bool) – crop with random size or specified size ROI by roi_scale * image spatial size. +if True, the actual size is sampled from +randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1).

  • +
+
+
+
+
+__call__(img, randomize=True, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t apply to the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+randomize(img_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

CenterScaleCrop#

+example of CenterScaleCrop +
+
+class monai.transforms.CenterScaleCrop(roi_scale, lazy=False)[source]#
+

Crop at the center of image with specified scale of ROI size.

+
+
Parameters:
+

roi_scale (UnionType[Sequence[float], float]) – specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5] or a number for all dims. +If its components have non-positive values, will use 1.0 instead, which means the input image size.

+
+
+
+
+__call__(img, lazy=None)[source]#
+

Apply the transform to img, assuming img is channel-first and +slicing doesn’t apply to the channel dim.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+
+

Intensity#

+
+

RandGaussianNoise#

+example of RandGaussianNoise +
+
+class monai.transforms.RandGaussianNoise(prob=0.1, mean=0.0, std=0.1, dtype=<class 'numpy.float32'>)[source]#
+

Add Gaussian noise to image.

+
+
Parameters:
+
    +
  • prob (float) – Probability to add Gaussian noise.

  • +
  • mean (float) – Mean or “centre” of the distribution.

  • +
  • std (float) – Standard deviation (spread) of distribution.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+
+__call__(img, mean=None, randomize=True)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(img, mean=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

ShiftIntensity#

+example of ShiftIntensity +
+
+class monai.transforms.ShiftIntensity(offset, safe=False)[source]#
+

Shift intensity uniformly for the entire image with specified offset.

+
+
Parameters:
+
    +
  • offset (float) – offset value to shift the intensity of image.

  • +
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. +E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • +
+
+
+
+
+__call__(img, offset=None)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

RandShiftIntensity#

+example of RandShiftIntensity +
+
+class monai.transforms.RandShiftIntensity(offsets, safe=False, prob=0.1)[source]#
+

Randomly shift intensity with randomly picked offset.

+
+
+__call__(img, factor=None, randomize=True)[source]#
+

Apply the transform to img.

+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – input image to shift intensity.

  • +
  • factor (Optional[float, None]) – a factor to multiply the random offset, then shift. +can be some image specific value at runtime, like: max(img), etc.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(offsets, safe=False, prob=0.1)[source]#
+
+
Parameters:
+
    +
  • offsets (UnionType[tuple[float, float], float]) – offset range to randomly shift. +if single number, offset value is picked from (-offsets, offsets).

  • +
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. +E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • +
  • prob (float) – probability of shift.

  • +
+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

StdShiftIntensity#

+example of StdShiftIntensity +
+
+class monai.transforms.StdShiftIntensity(factor, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+

Shift intensity for the image with a factor and the standard deviation of the image +by: v = v + factor * std(v). +This transform can focus on only non-zero values or the entire image, +and can also calculate the std on each channel separately.

+
+
Parameters:
+
    +
  • factor (float) – factor shift by v = v + factor * std(v).

  • +
  • nonzero (bool) – whether only count non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately. Please ensure +that the first dimension represents the channel of the image if True.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

RandStdShiftIntensity#

+example of RandStdShiftIntensity +
+
+class monai.transforms.RandStdShiftIntensity(factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+

Shift intensity for the image with a factor and the standard deviation of the image +by: v = v + factor * std(v) where the factor is randomly picked.

+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+
+
Parameters:
+
    +
  • factors (UnionType[tuple[float, float], float]) – if tuple, the randomly picked range is (min(factors), max(factors)). +If single number, the range is (-factors, factors).

  • +
  • prob (float) – probability of std shift.

  • +
  • nonzero (bool) – whether only count non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandBiasField#

+example of RandBiasField +
+
+class monai.transforms.RandBiasField(degree=3, coeff_range=(0.0, 0.1), dtype=<class 'numpy.float32'>, prob=0.1)[source]#
+

Random bias field augmentation for MR images. +The bias field is considered as a linear combination of smoothly varying basis (polynomial) +functions, as described in Automated Model-Based Tissue Classification of MR Images of the Brain. +This implementation adapted from NiftyNet. +Referred to Longitudinal segmentation of age-related white matter hyperintensities.

+
+
Parameters:
+
    +
  • degree (int) – degree of freedom of the polynomials. The value should be no less than 1. +Defaults to 3.

  • +
  • coeff_range (tuple[float, float]) – range of the random coefficients. Defaults to (0.0, 0.1).

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • prob (float) – probability to do random bias field.

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(img_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

ScaleIntensity#

+example of ScaleIntensity +
+
+class monai.transforms.ScaleIntensity(minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+

Scale the intensity of input image to the given value range (minv, maxv). +If minv and maxv not provided, use factor to scale image by v = v * (1 + factor).

+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Raises:
+

ValueError – When self.minv=None or self.maxv=None and self.factor=None. Incompatible values.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+
+
Parameters:
+
    +
  • minv (UnionType[float, None]) – minimum value of output data.

  • +
  • maxv (UnionType[float, None]) – maximum value of output data.

  • +
  • factor (Optional[float, None]) – factor scale by v = v * (1 + factor). In order to use +this parameter, please set both minv and maxv into None.

  • +
  • channel_wise (bool) – if True, scale on each channel separately. Please ensure +that the first dimension represents the channel of the image if True.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+ +
+ +
+
+

RandScaleIntensity#

+example of RandScaleIntensity +
+
+class monai.transforms.RandScaleIntensity(factors, prob=0.1, dtype=<class 'numpy.float32'>)[source]#
+

Randomly scale the intensity of input image by v = v * (1 + factor) where the factor +is randomly picked.

+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(factors, prob=0.1, dtype=<class 'numpy.float32'>)[source]#
+
+
Parameters:
+
    +
  • factors (UnionType[tuple[float, float], float]) – factor range to randomly scale by v = v * (1 + factor). +if single number, factor value is picked from (-factors, factors).

  • +
  • prob (float) – probability of scale.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

NormalizeIntensity#

+example of NormalizeIntensity +
+
+class monai.transforms.NormalizeIntensity(subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+

Normalize input based on the subtrahend and divisor: (img - subtrahend) / divisor. +Use calculated mean or std value of the input image if no subtrahend or divisor provided. +This transform can normalize only non-zero values or entire image, and can also calculate +mean and std on each channel separately. +When channel_wise is True, the first dimension of subtrahend and divisor should +be the number of image channels if they are not None.

+
+
Parameters:
+
    +
  • subtrahend (Union[Sequence, ndarray, Tensor, None]) – the amount to subtract by (usually the mean).

  • +
  • divisor (Union[Sequence, ndarray, Tensor, None]) – the amount to divide by (usually the standard deviation).

  • +
  • nonzero (bool) – whether only normalize non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately, otherwise, calculate on +the entire image directly. default to False.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img, assuming img is a channel-first array if self.channel_wise is True,

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

ThresholdIntensity#

+example of ThresholdIntensity +
+
+class monai.transforms.ThresholdIntensity(threshold, above=True, cval=0.0)[source]#
+

Filter the intensity values of whole image to below threshold or above threshold. +And fill the remaining parts of the image to the cval value.

+
+
Parameters:
+
    +
  • threshold (float) – the threshold to filter intensity values.

  • +
  • above (bool) – filter values above the threshold or below the threshold, default is True.

  • +
  • cval (float) – value to fill the remaining parts of the image, default is 0.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

ScaleIntensityRange#

+example of ScaleIntensityRange +
+
+class monai.transforms.ScaleIntensityRange(a_min, a_max, b_min=None, b_max=None, clip=False, dtype=<class 'numpy.float32'>)[source]#
+

Apply specific intensity scaling to the whole numpy array. +Scaling from [a_min, a_max] to [b_min, b_max] with clip option.

+

When b_min or b_max are None, scaled_array * (b_max - b_min) + b_min will be skipped. +If clip=True, when b_min/b_max is None, the clipping is not performed on the corresponding edge.

+
+
Parameters:
+
    +
  • a_min (float) – intensity original range min.

  • +
  • a_max (float) – intensity original range max.

  • +
  • b_min (Optional[float, None]) – intensity target range min.

  • +
  • b_max (Optional[float, None]) – intensity target range max.

  • +
  • clip (bool) – whether to perform clip after scaling.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

ScaleIntensityRangePercentiles#

+example of ScaleIntensityRangePercentiles +
+
+class monai.transforms.ScaleIntensityRangePercentiles(lower, upper, b_min, b_max, clip=False, relative=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
+

Apply range scaling to a numpy array based on the intensity distribution of the input.

+

By default this transform will scale from [lower_intensity_percentile, upper_intensity_percentile] to +[b_min, b_max], where {lower,upper}_intensity_percentile are the intensity values at the corresponding +percentiles of img.

+

The relative parameter can also be set to scale from [lower_intensity_percentile, upper_intensity_percentile] +to the lower and upper percentiles of the output range [b_min, b_max].

+

For example:

+
image = np.array(
+    [[[1, 2, 3, 4, 5],
+      [1, 2, 3, 4, 5],
+      [1, 2, 3, 4, 5],
+      [1, 2, 3, 4, 5],
+      [1, 2, 3, 4, 5],
+      [1, 2, 3, 4, 5]]])
+
+# Scale from lower and upper image intensity percentiles
+# to output range [b_min, b_max]
+scaler = ScaleIntensityRangePercentiles(10, 90, 0, 200, False, False)
+print(scaler(image))
+[[[0., 50., 100., 150., 200.],
+  [0., 50., 100., 150., 200.],
+  [0., 50., 100., 150., 200.],
+  [0., 50., 100., 150., 200.],
+  [0., 50., 100., 150., 200.],
+  [0., 50., 100., 150., 200.]]]
+
+# Scale from lower and upper image intensity percentiles
+# to lower and upper percentiles of the output range [b_min, b_max]
+rel_scaler = ScaleIntensityRangePercentiles(10, 90, 0, 200, False, True)
+print(rel_scaler(image))
+[[[20., 60., 100., 140., 180.],
+  [20., 60., 100., 140., 180.],
+  [20., 60., 100., 140., 180.],
+  [20., 60., 100., 140., 180.],
+  [20., 60., 100., 140., 180.],
+  [20., 60., 100., 140., 180.]]]
+
+
+ +
+
Parameters:
+
    +
  • lower (float) – lower intensity percentile.

  • +
  • upper (float) – upper intensity percentile.

  • +
  • b_min (UnionType[float, None]) – intensity target range min.

  • +
  • b_max (UnionType[float, None]) – intensity target range max.

  • +
  • clip (bool) – whether to perform clip after scaling.

  • +
  • relative (bool) – whether to scale to the corresponding percentiles of [b_min, b_max].

  • +
  • channel_wise (bool) – if True, compute intensity percentile and normalize every channel separately. +default to False.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

AdjustContrast#

+example of AdjustContrast +
+
+class monai.transforms.AdjustContrast(gamma)[source]#
+

Changes image intensity by gamma. Each pixel/voxel intensity is updated as:

+
x = ((x - min) / intensity_range) ^ gamma * intensity_range + min
+
+
+
+
Parameters:
+

gamma (float) – gamma value to adjust the contrast as function.

+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

RandAdjustContrast#

+example of RandAdjustContrast +
+
+class monai.transforms.RandAdjustContrast(prob=0.1, gamma=(0.5, 4.5))[source]#
+

Randomly changes image intensity by gamma. Each pixel/voxel intensity is updated as:

+
x = ((x - min) / intensity_range) ^ gamma * intensity_range + min
+
+
+
+
Parameters:
+
    +
  • prob (float) – Probability of adjustment.

  • +
  • gamma (UnionType[Sequence[float], float]) – Range of gamma values. +If single number, value is picked from (0.5, gamma), default is (0.5, 4.5).

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

MaskIntensity#

+example of MaskIntensity +
+
+class monai.transforms.MaskIntensity(mask_data=None, select_fn=<function is_positive>)[source]#
+

Mask the intensity values of input image with the specified mask data. +Mask data must have the same spatial size as the input image, and all +the intensity values of input image corresponding to the selected values +in the mask data will keep the original value, others will be set to 0.

+
+
Parameters:
+
    +
  • mask_data (Union[ndarray, Tensor, None]) – if mask_data is single channel, apply to every channel +of input image. if multiple channels, the number of channels must +match the input data. the intensity values of input image corresponding +to the selected values in the mask data will keep the original value, +others will be set to 0. if None, must specify the mask_data at runtime.

  • +
  • select_fn (Callable) – function to select valid values of the mask_data, default is +to select values > 0.

  • +
+
+
+
+
+__call__(img, mask_data=None)[source]#
+
+
Parameters:
+

mask_data (Union[ndarray, Tensor, None]) – if mask data is single channel, apply to every channel +of input image. if multiple channels, the channel number must +match input data. mask_data will be converted to bool values +by mask_data > 0 before applying transform to input image.

+
+
Raises:
+
    +
  • - ValueError – When both mask_data and self.mask_data are None.

  • +
  • - ValueError – When mask_data and img channels differ and mask_data is not single channel.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

SavitzkyGolaySmooth#

+example of SavitzkyGolaySmooth +
+
+class monai.transforms.SavitzkyGolaySmooth(window_length, order, axis=1, mode='zeros')[source]#
+

Smooth the input data along the given axis using a Savitzky-Golay filter.

+
+
Parameters:
+
    +
  • window_length (int) – Length of the filter window, must be a positive odd integer.

  • +
  • order (int) – Order of the polynomial to fit to each window, must be less than window_length.

  • +
  • axis (int) – Optional axis along which to apply the filter kernel. Default 1 (first spatial dimension).

  • +
  • mode (str) – Optional padding mode, passed to convolution class. 'zeros', 'reflect', 'replicate' +or 'circular'. Default: 'zeros'. See torch.nn.Conv1d() for more information.

  • +
+
+
+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – array containing input data. Must be real and in shape [channels, spatial1, spatial2, …].

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

array containing smoothed result.

+
+
+
+ +
+ +
+
+

MedianSmooth#

+example of MedianSmooth +
+
+class monai.transforms.MedianSmooth(radius=1)[source]#
+

Apply median filter to the input data based on specified radius parameter. +A default value radius=1 is provided for reference.

+

See also: monai.networks.layers.median_filter()

+
+
Parameters:
+

radius (UnionType[Sequence[int], int]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

+
+
+
+
+__call__(img)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+ +
+
+

GaussianSmooth#

+example of GaussianSmooth +
+
+class monai.transforms.GaussianSmooth(sigma=1.0, approx='erf')[source]#
+

Apply Gaussian smooth to the input data based on specified sigma parameter. +A default value sigma=1.0 is provided for reference.

+
+
Parameters:
+
    +
  • sigma (UnionType[Sequence[float], float]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
+
+
+
+
+__call__(img)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+ +
+
+

RandGaussianSmooth#

+example of RandGaussianSmooth +
+
+class monai.transforms.RandGaussianSmooth(sigma_x=(0.25, 1.5), sigma_y=(0.25, 1.5), sigma_z=(0.25, 1.5), prob=0.1, approx='erf')[source]#
+

Apply Gaussian smooth to the input data based on randomly selected sigma parameters.

+
+
Parameters:
+
    +
  • sigma_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension.

  • +
  • sigma_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension if have.

  • +
  • sigma_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension if have.

  • +
  • prob (float) – probability of Gaussian smooth.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

GaussianSharpen#

+example of GaussianSharpen +
+
+class monai.transforms.GaussianSharpen(sigma1=3.0, sigma2=1.0, alpha=30.0, approx='erf')[source]#
+

Sharpen images using the Gaussian Blur filter. +Referring to: http://scipy-lectures.org/advanced/image_processing/auto_examples/plot_sharpen.html. +The algorithm is shown as below

+
blurred_f = gaussian_filter(img, sigma1)
+filter_blurred_f = gaussian_filter(blurred_f, sigma2)
+img = blurred_f + alpha * (blurred_f - filter_blurred_f)
+
+
+

A set of default values sigma1=3.0, sigma2=1.0 and alpha=30.0 is provide for reference.

+
+
Parameters:
+
    +
  • sigma1 (UnionType[Sequence[float], float]) – sigma parameter for the first gaussian kernel. if a list of values, must match the count +of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. +if only 1 value provided, use it for all spatial dimensions.

  • +
  • sigma2 (UnionType[Sequence[float], float]) – sigma parameter for the second gaussian kernel. if a list of values, must match the count +of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. +if only 1 value provided, use it for all spatial dimensions.

  • +
  • alpha (float) – weight parameter to compute the final result.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
+
+
+
+
+__call__(img)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+ +
+
+

RandGaussianSharpen#

+example of RandGaussianSharpen +
+
+class monai.transforms.RandGaussianSharpen(sigma1_x=(0.5, 1.0), sigma1_y=(0.5, 1.0), sigma1_z=(0.5, 1.0), sigma2_x=0.5, sigma2_y=0.5, sigma2_z=0.5, alpha=(10.0, 30.0), approx='erf', prob=0.1)[source]#
+

Sharpen images using the Gaussian Blur filter based on randomly selected sigma1, sigma2 and alpha. +The algorithm is monai.transforms.GaussianSharpen.

+
+
Parameters:
+
    +
  • sigma1_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension of first gaussian kernel.

  • +
  • sigma1_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension(if have) of first gaussian kernel.

  • +
  • sigma1_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension(if have) of first gaussian kernel.

  • +
  • sigma2_x (UnionType[tuple[float, float], float]) – randomly select sigma value for the first spatial dimension of second gaussian kernel. +if only 1 value X provided, it must be smaller than sigma1_x and randomly select from [X, sigma1_x].

  • +
  • sigma2_y (UnionType[tuple[float, float], float]) – randomly select sigma value for the second spatial dimension(if have) of second gaussian kernel. +if only 1 value Y provided, it must be smaller than sigma1_y and randomly select from [Y, sigma1_y].

  • +
  • sigma2_z (UnionType[tuple[float, float], float]) – randomly select sigma value for the third spatial dimension(if have) of second gaussian kernel. +if only 1 value Z provided, it must be smaller than sigma1_z and randomly select from [Z, sigma1_z].

  • +
  • alpha (tuple[float, float]) – randomly select weight parameter to compute the final result.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
  • prob (float) – probability of Gaussian sharpen.

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandHistogramShift#

+example of RandHistogramShift +
+
+class monai.transforms.RandHistogramShift(num_control_points=10, prob=0.1)[source]#
+

Apply random nonlinear transform to the image’s intensity histogram.

+
+
Parameters:
+
    +
  • num_control_points (UnionType[tuple[int, int], int]) – number of control points governing the nonlinear intensity mapping. +a smaller number of control points allows for larger intensity shifts. if two values provided, number of +control points selecting from range (min_value, max_value).

  • +
  • prob (float) – probability of histogram shift.

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

DetectEnvelope#

+
+
+class monai.transforms.DetectEnvelope(axis=1, n=None)[source]#
+

Find the envelope of the input data along the requested axis using a Hilbert transform.

+
+
Parameters:
+
    +
  • axis (int) – Axis along which to detect the envelope. Default 1, i.e. the first spatial dimension.

  • +
  • n (Optional[int, None]) – FFT size. Default img.shape[axis]. Input will be zero-padded or truncated to this size along dimension

  • +
  • axis.

  • +
+
+
+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – numpy.ndarray containing input data. Must be real and in shape [channels, spatial1, spatial2, …].

+
+
Returns:
+

np.ndarray containing envelope of data in img along the specified axis.

+
+
+
+ +
+ +
+
+

GibbsNoise#

+example of GibbsNoise +
+
+class monai.transforms.GibbsNoise(alpha=0.1)[source]#
+

The transform applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts +are one of the common type of type artifacts appearing in MRI scans.

+

The transform is applied to all the channels in the data.

+

For general information on Gibbs artifacts, please refer to:

+

An Image-based Approach to Understanding the Physics of MR Artifacts.

+

The AAPM/RSNA Physics Tutorial for Residents

+
+
Parameters:
+

alpha (float) – Parametrizes the intensity of the Gibbs noise filter applied. Takes +values in the interval [0,1] with alpha = 0 acting as the identity mapping.

+
+
+
+
+__call__(img)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

RandGibbsNoise#

+example of RandGibbsNoise +
+
+class monai.transforms.RandGibbsNoise(prob=0.1, alpha=(0.0, 1.0))[source]#
+

Naturalistic image augmentation via Gibbs artifacts. The transform +randomly applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts +are one of the common type of type artifacts appearing in MRI scans.

+

The transform is applied to all the channels in the data.

+

For general information on Gibbs artifacts, please refer to: +https://pubs.rsna.org/doi/full/10.1148/rg.313105115 +https://pubs.rsna.org/doi/full/10.1148/radiographics.22.4.g02jl14949

+
+
Parameters:
+
    +
  • prob (float) – probability of applying the transform.

  • +
  • alpha (Sequence(float)) – Parametrizes the intensity of the Gibbs noise filter applied. Takes +values in the interval [0,1] with alpha = 0 acting as the identity mapping. +If a length-2 list is given as [a,b] then the value of alpha will be +sampled uniformly from the interval [a,b]. 0 <= a <= b <= 1.

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+
+randomize(data)[source]#
+
    +
  1. Set random variable to apply the transform.

  2. +
  3. Get alpha from uniform distribution.

  4. +
+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

KSpaceSpikeNoise#

+example of KSpaceSpikeNoise +
+
+class monai.transforms.KSpaceSpikeNoise(loc, k_intensity=None)[source]#
+

Apply localized spikes in k-space at the given locations and intensities. +Spike (Herringbone) artifact is a type of data acquisition artifact which +may occur during MRI scans.

+

For general information on spike artifacts, please refer to:

+

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

+

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s +perspective.

+
+
Parameters:
+
    +
  • loc (UnionType[tuple, Sequence[tuple]]) – spatial location for the spikes. For +images with 3D spatial dimensions, the user can provide (C, X, Y, Z) +to fix which channel C is affected, or (X, Y, Z) to place the same +spike in all channels. For 2D cases, the user can provide (C, X, Y) +or (X, Y).

  • +
  • k_intensity (Union[Sequence[float], float, None]) – value for the log-intensity of the +k-space version of the image. If one location is passed to loc or the +channel is not specified, then this argument should receive a float. If +loc is given a sequence of locations, then this argument should +receive a sequence of intensities. This value should be tested as it is +data-dependent. The default values are the 2.5 the mean of the +log-intensity for each channel.

  • +
+
+
+

Example

+

When working with 4D data, KSpaceSpikeNoise(loc = ((3,60,64,32), (64,60,32)), k_intensity = (13,14)) +will place a spike at [3, 60, 64, 32] with log-intensity = 13, and +one spike per channel located respectively at [: , 64, 60, 32] +with log-intensity = 14.

+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – image with dimensions (C, H, W) or (C, H, W, D)

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

RandKSpaceSpikeNoise#

+example of RandKSpaceSpikeNoise +
+
+class monai.transforms.RandKSpaceSpikeNoise(prob=0.1, intensity_range=None, channel_wise=True)[source]#
+

Naturalistic data augmentation via spike artifacts. The transform applies +localized spikes in k-space, and it is the random version of +monai.transforms.KSpaceSpikeNoise.

+

Spike (Herringbone) artifact is a type of data acquisition artifact which +may occur during MRI scans. For general information on spike artifacts, +please refer to:

+

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

+

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s +perspective.

+
+
Parameters:
+
    +
  • prob (float) – probability of applying the transform, either on all +channels at once, or channel-wise if channel_wise = True.

  • +
  • intensity_range (Optional[Sequence[UnionType[Sequence[float], float]], None]) – pass a tuple (a, b) to sample the log-intensity from the interval (a, b) +uniformly for all channels. Or pass sequence of intervals +((a0, b0), (a1, b1), …) to sample for each respective channel. +In the second case, the number of 2-tuples must match the number of channels. +Default ranges is (0.95x, 1.10x) where x is the mean +log-intensity for each channel.

  • +
  • channel_wise (bool) – treat each channel independently. True by +default.

  • +
+
+
+

Example

+

To apply k-space spikes randomly with probability 0.5, and +log-intensity sampled from the interval [11, 12] for each channel +independently, one uses +RandKSpaceSpikeNoise(prob=0.5, intensity_range=(11, 12), channel_wise=True)

+
+
+__call__(img, randomize=True)[source]#
+

Apply transform to img. Assumes data is in channel-first form.

+
+
Parameters:
+

img (Union[ndarray, Tensor]) – image with dimensions (C, H, W) or (C, H, W, D)

+
+
+
+ +
+
+randomize(img, intensity_range)[source]#
+

Helper method to sample both the location and intensity of the spikes. +When not working channel wise (channel_wise=False) it use the random +variable self._do_transform to decide whether to sample a location +and intensity.

+

When working channel wise, the method randomly samples a location and +intensity for each channel depending on self._do_transform.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandRicianNoise#

+example of RandRicianNoise +
+
+class monai.transforms.RandRicianNoise(prob=0.1, mean=0.0, std=1.0, channel_wise=False, relative=False, sample_std=True, dtype=<class 'numpy.float32'>)[source]#
+

Add Rician noise to image. +Rician noise in MRI is the result of performing a magnitude operation on complex +data with Gaussian noise of the same variance in both channels, as described in +Noise in Magnitude Magnetic Resonance Images. +This transform is adapted from DIPY. +See also: The rician distribution of noisy mri data.

+
+
Parameters:
+
    +
  • prob (float) – Probability to add Rician noise.

  • +
  • mean (UnionType[Sequence[float], float]) – Mean or “centre” of the Gaussian distributions sampled to make up +the Rician noise.

  • +
  • std (UnionType[Sequence[float], float]) – Standard deviation (spread) of the Gaussian distributions sampled +to make up the Rician noise.

  • +
  • channel_wise (bool) – If True, treats each channel of the image separately.

  • +
  • relative (bool) – If True, the spread of the sampled Gaussian distributions will +be std times the standard deviation of the image or channel’s intensity +histogram.

  • +
  • sample_std (bool) – If True, sample the spread of the Gaussian distributions +uniformly from 0 to std.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

RandCoarseTransform#

+
+
+class monai.transforms.RandCoarseTransform(holes, spatial_size, max_holes=None, max_spatial_size=None, prob=0.1)[source]#
+

Randomly select coarse regions in the image, then execute transform operations for the regions. +It’s the base class of all kinds of region transforms. +Refer to papers: https://arxiv.org/abs/1708.04552

+
+
Parameters:
+
    +
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to +randomly select the expected number of regions.

  • +
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg +as the minimum spatial size to randomly select size for every region. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • +
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. +if some components of the max_spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of applying the transform.

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(img_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandCoarseDropout#

+example of RandCoarseDropout +
+
+class monai.transforms.RandCoarseDropout(holes, spatial_size, dropout_holes=True, fill_value=None, max_holes=None, max_spatial_size=None, prob=0.1)[source]#
+

Randomly coarse dropout regions in the image, then fill in the rectangular regions with specified value. +Or keep the rectangular regions and fill in the other areas with specified value. +Refer to papers: https://arxiv.org/abs/1708.04552, https://arxiv.org/pdf/1604.07379 +And other implementation: https://albumentations.ai/docs/api_reference/augmentations/transforms/ +#albumentations.augmentations.transforms.CoarseDropout.

+
+
Parameters:
+
    +
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to +randomly select the expected number of regions.

  • +
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg +as the minimum spatial size to randomly select size for every region. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • dropout_holes (bool) – if True, dropout the regions of holes and fill value, if False, keep the holes and +dropout the outside and fill value. default to True.

  • +
  • fill_value (Union[tuple[float, float], float, None]) – target value to fill the dropout regions, if providing a number, will use it as constant +value to fill all the regions. if providing a tuple for the min and max, will randomly select +value for every pixel / voxel from the range [min, max). if None, will compute the min and max +value of input image then randomly select value to fill, default to None.

  • +
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • +
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. +if some components of the max_spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of applying the transform.

  • +
+
+
+
+ +
+
+

RandCoarseShuffle#

+example of RandCoarseShuffle +
+
+class monai.transforms.RandCoarseShuffle(holes, spatial_size, max_holes=None, max_spatial_size=None, prob=0.1)[source]#
+

Randomly select regions in the image, then shuffle the pixels within every region. +It shuffles every channel separately. +Refer to paper: +Kang, Guoliang, et al. “Patchshuffle regularization.” arXiv preprint arXiv:1707.07103 (2017). +https://arxiv.org/abs/1707.07103

+
+
Parameters:
+
    +
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to +randomly select the expected number of regions.

  • +
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg +as the minimum spatial size to randomly select size for every region. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • +
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. +if some components of the max_spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of applying the transform.

  • +
+
+
+
+ +
+
+

HistogramNormalize#

+example of HistogramNormalize +
+
+class monai.transforms.HistogramNormalize(num_bins=256, min=0, max=255, mask=None, dtype=<class 'numpy.float32'>)[source]#
+

Apply the histogram normalization to input image. +Refer to: facebookresearch/CovidPrognosis.

+
+
Parameters:
+
    +
  • num_bins (int) – number of the bins to use in histogram, default to 256. for more details: +https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.

  • +
  • min (int) – the min value to normalize input image, default to 0.

  • +
  • max (int) – the max value to normalize input image, default to 255.

  • +
  • mask (Union[ndarray, Tensor, None]) – if provided, must be ndarray of bools or 0s and 1s, and same shape as image. +only points at which mask==True are used for the equalization. +can also provide the mask along with img at runtime.

  • +
  • dtype (Union[dtype, type, str, None]) – data type of the output, if None, same as input image. default to float32.

  • +
+
+
+
+
+__call__(img, mask=None)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

ForegroundMask#

+example of ForegroundMask +
+
+class monai.transforms.ForegroundMask(threshold='otsu', hsv_threshold=None, invert=False)[source]#
+

Creates a binary mask that defines the foreground based on thresholds in RGB or HSV color space. +This transform receives an RGB (or grayscale) image where by default it is assumed that the foreground has +low values (dark) while the background has high values (white). Otherwise, set invert argument to True.

+
+
Parameters:
+
    +
  • threshold (UnionType[dict, Callable, str, float, int]) – an int or a float number that defines the threshold that values less than that are foreground. +It also can be a callable that receives each dimension of the image and calculate the threshold, +or a string that defines such callable from skimage.filter.threshold_…. For the list of available +threshold functions, please refer to https://scikit-image.org/docs/stable/api/skimage.filters.html +Moreover, a dictionary can be passed that defines such thresholds for each channel, like +{“R”: 100, “G”: “otsu”, “B”: skimage.filter.threshold_mean}

  • +
  • hsv_threshold (Union[dict, Callable, str, float, int, None]) – similar to threshold but HSV color space (“H”, “S”, and “V”). +Unlike RBG, in HSV, value greater than hsv_threshold are considered foreground.

  • +
  • invert (bool) – invert the intensity range of the input image, so that the dtype maximum is now the dtype minimum, +and vice-versa.

  • +
+
+
+
+
+__call__(image)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

ComputeHoVerMaps#

+
+
+class monai.transforms.ComputeHoVerMaps(dtype='float32')[source]#
+

Compute horizontal and vertical maps from an instance mask +It generates normalized horizontal and vertical distances to the center of mass of each region. +Input data with the size of [1xHxW[xD]], which channel dim will temporarily removed for calculating coordinates.

+
+
Parameters:
+

dtype (Union[dtype, type, str, None]) – the data type of output Tensor. Defaults to “float32”.

+
+
Returns:
+

A torch.Tensor with the size of [2xHxW[xD]], which is stack horizontal and vertical maps

+
+
+
+
+__call__(mask)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+
+

IO#

+
+

LoadImage#

+
+
+class monai.transforms.LoadImage(reader=None, image_only=False, dtype=<class 'numpy.float32'>, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', expanduser=True, *args, **kwargs)[source]#
+

Load image file or files from provided path based on reader. +If reader is not specified, this class automatically chooses readers +based on the supported suffixes and in the following order:

+
+
    +
  • User-specified reader at runtime when calling this loader.

  • +
  • User-specified reader in the constructor of LoadImage.

  • +
  • Readers from the last to the first in the registered list.

  • +
  • Current default readers: (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), +(npz, npy -> NumpyReader), (nrrd -> NrrdReader), (DICOM file -> ITKReader).

  • +
+
+

Please note that for png, jpg, bmp, and other 2D formats, readers by default swap axis 0 and 1 after +loading the array with reverse_indexing set to True because the spatial axes definition +for non-medical specific file formats is different from other common medical packages.

+
+

See also

+ +
+
+
+__call__(filename, reader=None)[source]#
+

Load image file and metadata from the given filename(s). +If reader is not specified, this class automatically chooses readers based on the +reversed order of registered readers self.readers.

+
+
Parameters:
+
    +
  • filename (Union[Sequence[Union[str, PathLike]], str, PathLike]) – path file or file-like object or a list of files. +will save the filename to meta_data with key filename_or_obj. +if provided a list of files, use the filename of first file to save, +and will stack them together as multi-channels data. +if provided directory path instead of file path, will treat it as +DICOM images series and read.

  • +
  • reader (Optional[ImageReader, None]) – runtime reader to load image file and metadata.

  • +
+
+
+
+ +
+
+__init__(reader=None, image_only=False, dtype=<class 'numpy.float32'>, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', expanduser=True, *args, **kwargs)[source]#
+
+
Parameters:
+
    +
  • reader – reader to load image file and metadata +- if reader is None, a default set of SUPPORTED_READERS will be used. +- if reader is a string, it’s treated as a class name or dotted path +(such as "monai.data.ITKReader"), the supported built-in reader classes are +"ITKReader", "NibabelReader", "NumpyReader", "PydicomReader". +a reader instance will be constructed with the *args and **kwargs parameters. +- if reader is a reader class/instance, it will be registered to this loader accordingly.

  • +
  • image_only (bool) – if True return only the image MetaTensor, otherwise return image and header dict.

  • +
  • dtype (Union[dtype, type, str, None]) – if not None convert the loaded image to this data type.

  • +
  • ensure_channel_first (bool) – if True and loaded both image array and metadata, automatically convert +the image array shape to channel first. default to False.

  • +
  • simple_keys (bool) – whether to remove redundant metadata keys, default to False for backward compatibility.

  • +
  • prune_meta_pattern (Optional[str, None]) – combined with prune_meta_sep, a regular expression used to match and prune keys +in the metadata (nested dictionary), default to None, no key deletion.

  • +
  • prune_meta_sep (str) – combined with prune_meta_pattern, used to match and prune keys +in the metadata (nested dictionary). default is “.”, see also monai.transforms.DeleteItemsd. +e.g. prune_meta_pattern=".*_code$", prune_meta_sep=" " removes meta keys that ends with "_code".

  • +
  • expanduser (bool) – if True cast filename to Path and call .expanduser on it, otherwise keep filename as is.

  • +
  • args – additional parameters for reader if providing a reader name.

  • +
  • kwargs – additional parameters for reader if providing a reader name.

  • +
+
+
+
+

Note

+
    +
  • The transform returns a MetaTensor, unless set_track_meta(False) has been used, in which case, a +torch.Tensor will be returned.

  • +
  • If reader is specified, the loader will attempt to use the specified readers and the default supported +readers. This might introduce overheads when handling the exceptions of trying the incompatible loaders. +In this case, it is therefore recommended setting the most appropriate reader as +the last item of the reader parameter.

  • +
+
+
+ +
+
+register(reader)[source]#
+

Register image reader to load image file and metadata.

+
+
Parameters:
+

reader (ImageReader) – reader instance to be registered with this loader.

+
+
+
+ +
+ +
+
+

SaveImage#

+
+
+class monai.transforms.SaveImage(output_dir='./', output_postfix='trans', output_ext='.nii.gz', output_dtype=<class 'numpy.float32'>, resample=True, mode='nearest', padding_mode=GridSamplePadMode.BORDER, scale=None, dtype=<class 'numpy.float64'>, squeeze_end_dims=True, data_root_dir='', separate_folder=True, print_log=True, output_format='', writer=None, channel_dim=0, output_name_formatter=None, folder_layout=None, savepath_in_metadict=False)[source]#
+

Save the image (in the form of torch tensor or numpy ndarray) and metadata dictionary into files.

+

The name of saved file will be {input_image_name}_{output_postfix}{output_ext}, +where the input_image_name is extracted from the provided metadata dictionary. +If no metadata provided, a running index starting from 0 will be used as the filename prefix.

+
+
Parameters:
+
    +
  • output_dir (Union[str, PathLike]) – output image directory.

  • +
  • instead (Handled by folder_layout) –

  • +
  • None. (if folder_layout is not) –

  • +
  • output_postfix (str) – a string appended to all output file names, default to trans.

  • +
  • instead

  • +
  • None.

  • +
  • output_ext (str) – output file extension name.

  • +
  • instead

  • +
  • None.

  • +
  • output_dtype (Union[dtype, type, str, None]) – data type (if not None) for saving data. Defaults to np.float32.

  • +
  • resample (bool) – whether to resample image (if needed) before saving the data array, +based on the "spatial_shape" (and "original_affine") from metadata.

  • +
  • mode (str) –

    This option is used when resample=True. Defaults to "nearest". +Depending on the writers, the possible options are

    + +

  • +
  • padding_mode (str) – This option is used when resample = True. Defaults to "border". +Possible options are {"zeros", "border", "reflection"} +See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample

  • +
  • scale (Optional[int, None]) – {255, 65535} postprocess data by clipping to [0, 1] and scaling +[0, 255] (uint8) or [0, 65535] (uint16). Default is None (no scaling).

  • +
  • dtype (Union[dtype, type, str, None]) – data type during resampling computation. Defaults to np.float64 for best precision. +if None, use the data type of input data. To set the output data type, use output_dtype.

  • +
  • squeeze_end_dims (bool) – if True, any trailing singleton dimensions will be removed (after the channel +has been moved to the end). So if input is (C,H,W,D), this will be altered to (H,W,D,C), and +then if C==1, it will be saved as (H,W,D). If D is also 1, it will be saved as (H,W). If False, +image will always be saved as (H,W,D,C).

  • +
  • data_root_dir (Union[str, PathLike]) –

    if not empty, it specifies the beginning parts of the input file’s +absolute path. It’s used to compute input_file_rel_path, the relative path to the file from +data_root_dir to preserve folder structure when saving in case there are files in different +folders with the same file names. For example, with the following inputs:

    +
      +
    • input_file_name: /foo/bar/test1/image.nii

    • +
    • output_postfix: seg

    • +
    • output_ext: .nii.gz

    • +
    • output_dir: /output

    • +
    • data_root_dir: /foo/bar

    • +
    +

    The output will be: /output/test1/image/image_seg.nii.gz

    +

    Handled by folder_layout instead, if folder_layout is not None.

    +

  • +
  • separate_folder (bool) – whether to save every file in a separate folder. For example: for the input filename +image.nii, postfix seg and folder_path output, if separate_folder=True, it will be +saved as: output/image/image_seg.nii, if False, saving as output/image_seg.nii. +Default to True. +Handled by folder_layout instead, if folder_layout is not None.

  • +
  • print_log (bool) – whether to print logs when saving. Default to True.

  • +
  • output_format (str) – an optional string of filename extension to specify the output image writer. +see also: monai.data.image_writer.SUPPORTED_WRITERS.

  • +
  • writer (Union[type[ImageWriter], str, None]) – a customised monai.data.ImageWriter subclass to save data arrays. +if None, use the default writer from monai.data.image_writer according to output_ext. +if it’s a string, it’s treated as a class name or dotted path (such as "monai.data.ITKWriter"); +the supported built-in writer classes are "NibabelWriter", "ITKWriter", "PILWriter".

  • +
  • channel_dim (UnionType[int, None]) – the index of the channel dimension. Default to 0. +None to indicate no channel dimension.

  • +
  • output_name_formatter (Optional[Callable[[dict, Transform], dict], None]) – a callable function (returning a kwargs dict) to format the output file name. +If using a custom monai.data.FolderLayoutBase class in folder_layout, consider providing +your own formatter. +see also: monai.data.folder_layout.default_name_formatter().

  • +
  • folder_layout (Optional[FolderLayoutBase, None]) – A customized monai.data.FolderLayoutBase subclass to define file naming schemes. +if None, uses the default FolderLayout.

  • +
  • savepath_in_metadict (bool) – if True, adds a key "saved_to" to the metadata, which contains the path +to where the input image has been saved.

  • +
+
+
+
+
+__call__(img, meta_data=None)[source]#
+
+
Parameters:
+
    +
  • img (UnionType[Tensor, ndarray]) – target data content that save into file. The image should be channel-first, shape: [C,H,W,[D]].

  • +
  • meta_data (Optional[dict, None]) – key-value pairs of metadata corresponding to the data.

  • +
+
+
+
+ +
+
+set_options(init_kwargs=None, data_kwargs=None, meta_kwargs=None, write_kwargs=None)[source]#
+

Set the options for the underlying writer by updating the self.*_kwargs dictionaries.

+

The arguments correspond to the following usage:

+
+
    +
  • writer = ImageWriter(**init_kwargs)

  • +
  • writer.set_data_array(array, **data_kwargs)

  • +
  • writer.set_metadata(meta_data, **meta_kwargs)

  • +
  • writer.write(filename, **write_kwargs)

  • +
+
+
+ +
+ +
+
+
+

NVIDIA Tool Extension (NVTX)#

+
+

RangePush#

+
+
+class monai.transforms.RangePush(msg)[source]#
+

Pushes a range onto a stack of nested range span. +Stores zero-based depth of the range that is started.

+
+
Parameters:
+

msg (str) – ASCII message to associate with range

+
+
+
+ +
+
+

RandRangePush#

+
+
+class monai.transforms.RandRangePush(msg)[source]#
+

Pushes a range onto a stack of nested range span (for randomizable transforms). +Stores zero-based depth of the range that is started.

+
+
Parameters:
+

msg (str) – ASCII message to associate with range

+
+
+
+ +
+
+

RangePop#

+
+
+class monai.transforms.RangePop[source]#
+

Pops a range off of a stack of nested range spans. +Stores zero-based depth of the range that is ended.

+
+ +
+
+

RandRangePop#

+
+
+class monai.transforms.RandRangePop[source]#
+

Pops a range off of a stack of nested range spans (for randomizable transforms). +Stores zero-based depth of the range that is ended.

+
+ +
+
+

Mark#

+
+
+class monai.transforms.Mark(msg)[source]#
+

Mark an instantaneous event that occurred at some point.

+
+
Parameters:
+

msg (str) – ASCII message to associate with the event.

+
+
+
+ +
+
+

RandMark#

+
+
+class monai.transforms.RandMark(msg)[source]#
+

Mark an instantaneous event that occurred at some point (for randomizable transforms).

+
+
Parameters:
+

msg (str) – ASCII message to associate with the event.

+
+
+
+ +
+
+
+

Post-processing#

+
+

Activations#

+
+
+class monai.transforms.Activations(sigmoid=False, softmax=False, other=None, **kwargs)[source]#
+

Activation operations, typically Sigmoid or Softmax.

+
+
Parameters:
+
    +
  • sigmoid (bool) – whether to execute sigmoid function on model output before transform. +Defaults to False.

  • +
  • softmax (bool) – whether to execute softmax function on model output before transform. +Defaults to False.

  • +
  • other (Optional[Callable, None]) – callable function to execute other activation layers, for example: +other = lambda x: torch.tanh(x). Defaults to None.

  • +
  • kwargs – additional parameters to torch.softmax (used when softmax=True). +Defaults to dim=0, unrecognized parameters will be ignored.

  • +
+
+
Raises:
+

TypeError – When other is not an Optional[Callable].

+
+
+
+
+__call__(img, sigmoid=None, softmax=None, other=None)[source]#
+
+
Parameters:
+
    +
  • sigmoid (Optional[bool, None]) – whether to execute sigmoid function on model output before transform. +Defaults to self.sigmoid.

  • +
  • softmax (Optional[bool, None]) – whether to execute softmax function on model output before transform. +Defaults to self.softmax.

  • +
  • other (Optional[Callable, None]) – callable function to execute other activation layers, for example: +other = torch.tanh. Defaults to self.other.

  • +
+
+
Raises:
+
    +
  • ValueError – When sigmoid=True and softmax=True. Incompatible values.

  • +
  • TypeError – When other is not an Optional[Callable].

  • +
  • ValueError – When self.other=None and other=None. Incompatible values.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

AsDiscrete#

+example of AsDiscrete +
+
+class monai.transforms.AsDiscrete(argmax=False, to_onehot=None, threshold=None, rounding=None, **kwargs)[source]#
+

Convert the input tensor/array into discrete values, possible operations are:

+
+
    +
  • argmax.

  • +
  • threshold input value to binary values.

  • +
  • convert input value to One-Hot format (set to_one_hot=N, N is the number of classes).

  • +
  • round the value to the closest integer.

  • +
+
+
+
Parameters:
+
    +
  • argmax (bool) – whether to execute argmax function on input data before transform. +Defaults to False.

  • +
  • to_onehot (Optional[int, None]) – if not None, convert input data into the one-hot format with specified number of classes. +Defaults to None.

  • +
  • threshold (Optional[float, None]) – if not None, threshold the float values to int number 0 or 1 with specified threshold. +Defaults to None.

  • +
  • rounding (Optional[str, None]) – if not None, round the data according to the specified option, +available options: [“torchrounding”].

  • +
  • kwargs – additional parameters to torch.argmax, monai.networks.one_hot. +currently dim, keepdim, dtype are supported, unrecognized parameters will be ignored. +These default to 0, True, torch.float respectively.

  • +
+
+
+

Example

+
>>> transform = AsDiscrete(argmax=True)
+>>> print(transform(np.array([[[0.0, 1.0]], [[2.0, 3.0]]])))
+# [[[1.0, 1.0]]]
+
+
+
>>> transform = AsDiscrete(threshold=0.6)
+>>> print(transform(np.array([[[0.0, 0.5], [0.8, 3.0]]])))
+# [[[0.0, 0.0], [1.0, 1.0]]]
+
+
+
>>> transform = AsDiscrete(argmax=True, to_onehot=2, threshold=0.5)
+>>> print(transform(np.array([[[0.0, 1.0]], [[2.0, 3.0]]])))
+# [[[0.0, 0.0]], [[1.0, 1.0]]]
+
+
+
+
+__call__(img, argmax=None, to_onehot=None, threshold=None, rounding=None)[source]#
+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – the input tensor data to convert, if no channel dimension when converting to One-Hot, +will automatically add it.

  • +
  • argmax (Optional[bool, None]) – whether to execute argmax function on input data before transform. +Defaults to self.argmax.

  • +
  • to_onehot (Optional[int, None]) – if not None, convert input data into the one-hot format with specified number of classes. +Defaults to self.to_onehot.

  • +
  • threshold (Optional[float, None]) – if not None, threshold the float values to int number 0 or 1 with specified threshold value. +Defaults to self.threshold.

  • +
  • rounding (Optional[str, None]) – if not None, round the data according to the specified option, +available options: [“torchrounding”].

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

KeepLargestConnectedComponent#

+example of KeepLargestConnectedComponent +
+
+class monai.transforms.KeepLargestConnectedComponent(applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1)[source]#
+

Keeps only the largest connected component in the image. +This transform can be used as a post-processing step to clean up over-segment areas in model output.

+
+
The input is assumed to be a channel-first PyTorch Tensor:

1) For not OneHot format data, the values correspond to expected labels, +0 will be treated as background and the over-segment pixels will be set to 0. +2) For OneHot format data, the values should be 0, 1 on each labels, +the over-segment pixels will be set to 0 in its channel.

+
+
+

For example: +Use with applied_labels=[1], is_onehot=False, connectivity=1:

+
[1, 0, 0]         [0, 0, 0]
+[0, 1, 1]    =>   [0, 1 ,1]
+[0, 1, 1]         [0, 1, 1]
+
+
+

Use with applied_labels=[1, 2], is_onehot=False, independent=False, connectivity=1:

+
[0, 0, 1, 0 ,0]           [0, 0, 1, 0 ,0]
+[0, 2, 1, 1 ,1]           [0, 2, 1, 1 ,1]
+[1, 2, 1, 0 ,0]    =>     [1, 2, 1, 0 ,0]
+[1, 2, 0, 1 ,0]           [1, 2, 0, 0 ,0]
+[2, 2, 0, 0 ,2]           [2, 2, 0, 0 ,0]
+
+
+

Use with applied_labels=[1, 2], is_onehot=False, independent=True, connectivity=1:

+
[0, 0, 1, 0 ,0]           [0, 0, 1, 0 ,0]
+[0, 2, 1, 1 ,1]           [0, 2, 1, 1 ,1]
+[1, 2, 1, 0 ,0]    =>     [0, 2, 1, 0 ,0]
+[1, 2, 0, 1 ,0]           [0, 2, 0, 0 ,0]
+[2, 2, 0, 0 ,2]           [2, 2, 0, 0 ,0]
+
+
+

Use with applied_labels=[1, 2], is_onehot=False, independent=False, connectivity=2:

+
[0, 0, 1, 0 ,0]           [0, 0, 1, 0 ,0]
+[0, 2, 1, 1 ,1]           [0, 2, 1, 1 ,1]
+[1, 2, 1, 0 ,0]    =>     [1, 2, 1, 0 ,0]
+[1, 2, 0, 1 ,0]           [1, 2, 0, 1 ,0]
+[2, 2, 0, 0 ,2]           [2, 2, 0, 0 ,2]
+
+
+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – shape must be (C, spatial_dim1[, spatial_dim2, …]).

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

An array with shape (C, spatial_dim1[, spatial_dim2, …]).

+
+
+
+ +
+
+__init__(applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1)[source]#
+
+
Parameters:
+
    +
  • applied_labels (Union[Sequence[int], int, None]) – Labels for applying the connected component analysis on. +If given, voxels whose value is in this list will be analyzed. +If None, all non-zero values will be analyzed.

  • +
  • is_onehot (Optional[bool, None]) – if True, treat the input data as OneHot format data, otherwise, not OneHot format data. +default to None, which treats multi-channel data as OneHot and single channel data as not OneHot.

  • +
  • independent (bool) – whether to treat applied_labels as a union of foreground labels. +If True, the connected component analysis will be performed on each foreground label independently +and return the intersection of the largest components. +If False, the analysis will be performed on the union of foreground labels. +default is True.

  • +
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. If None, a full +connectivity of input.ndim is used. for more details: +https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label.

  • +
  • num_components (int) – The number of largest components to preserve.

  • +
+
+
+
+ +
+ +
+
+

RemoveSmallObjects#

+example of RemoveSmallObjects +
+
+class monai.transforms.RemoveSmallObjects(min_size=64, connectivity=1, independent_channels=True)[source]#
+

Use skimage.morphology.remove_small_objects to remove small objects from images. +See: https://scikit-image.org/docs/dev/api/skimage.morphology.html#remove-small-objects.

+

Data should be one-hotted.

+
+
Parameters:
+
    +
  • min_size (int) – objects smaller than this size (in pixel) are removed.

  • +
  • connectivity (int) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. If None, a full +connectivity of input.ndim is used. For more details refer to linked scikit-image +documentation.

  • +
  • independent_channels (bool) – Whether or not to consider channels as independent. If true, then +conjoining islands from different labels will be removed if they are below the threshold. +If false, the overall size islands made from all non-background voxels will be used.

  • +
+
+
+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – shape must be (C, spatial_dim1[, spatial_dim2, …]). Data +should be one-hotted.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

An array with shape (C, spatial_dim1[, spatial_dim2, …]).

+
+
+
+ +
+ +
+
+

LabelFilter#

+example of LabelFilter +
+
+class monai.transforms.LabelFilter(applied_labels)[source]#
+

This transform filters out labels and can be used as a processing step to view only certain labels.

+

The list of applied labels defines which labels will be kept.

+
+

Note

+

All labels which do not match the applied_labels are set to the background label (0).

+
+

For example:

+

Use LabelFilter with applied_labels=[1, 5, 9]:

+
[1, 2, 3]         [1, 0, 0]
+[4, 5, 6]    =>   [0, 5 ,0]
+[7, 8, 9]         [0, 0, 9]
+
+
+
+
+__call__(img)[source]#
+

Filter the image on the applied_labels.

+
+
Parameters:
+

img (Union[ndarray, Tensor]) – Pytorch tensor or numpy array of any shape.

+
+
Raises:
+

NotImplementedError – The provided image was not a Pytorch Tensor or numpy array.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Pytorch tensor or numpy array of the same shape as the input.

+
+
+
+ +
+
+__init__(applied_labels)[source]#
+

Initialize the LabelFilter class with the labels to filter on.

+
+
Parameters:
+

applied_labels (UnionType[Iterable[int], int]) – Label(s) to filter on.

+
+
+
+ +
+ +
+
+

FillHoles#

+
+
+class monai.transforms.FillHoles(applied_labels=None, connectivity=None)[source]#
+

This transform fills holes in the image and can be used to remove artifacts inside segments.

+

An enclosed hole is defined as a background pixel/voxel which is only enclosed by a single class. +The definition of enclosed can be defined with the connectivity parameter:

+
1-connectivity     2-connectivity     diagonal connection close-up
+
+     [ ]           [ ]  [ ]  [ ]             [ ]
+      |               \  |  /                 |  <- hop 2
+[ ]--[x]--[ ]      [ ]--[x]--[ ]        [x]--[ ]
+      |               /  |  \             hop 1
+     [ ]           [ ]  [ ]  [ ]
+
+
+

It is possible to define for which labels the hole filling should be applied. +The input image is assumed to be a PyTorch Tensor or numpy array with shape [C, spatial_dim1[, spatial_dim2, …]]. +If C = 1, then the values correspond to expected labels. +If C > 1, then a one-hot-encoding is expected where the index of C matches the label indexing.

+
+

Note

+

The label 0 will be treated as background and the enclosed holes will be set to the neighboring class label.

+

The performance of this method heavily depends on the number of labels. +It is a bit faster if the list of applied_labels is provided. +Limiting the number of applied_labels results in a big decrease in processing time.

+
+

For example:

+
+

Use FillHoles with default parameters:

+
[1, 1, 1, 2, 2, 2, 3, 3]         [1, 1, 1, 2, 2, 2, 3, 3]
+[1, 0, 1, 2, 0, 0, 3, 0]    =>   [1, 1 ,1, 2, 0, 0, 3, 0]
+[1, 1, 1, 2, 2, 2, 3, 3]         [1, 1, 1, 2, 2, 2, 3, 3]
+
+
+

The hole in label 1 is fully enclosed and therefore filled with label 1. +The background label near label 2 and 3 is not fully enclosed and therefore not filled.

+
+
+
+__call__(img)[source]#
+

Fill the holes in the provided image.

+
+

Note

+

The value 0 is assumed as background label.

+
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – Pytorch Tensor or numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

+
+
Raises:
+

NotImplementedError – The provided image was not a Pytorch Tensor or numpy array.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Pytorch Tensor or numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

+
+
+
+ +
+
+__init__(applied_labels=None, connectivity=None)[source]#
+

Initialize the connectivity and limit the labels for which holes are filled.

+
+
Parameters:
+
    +
  • applied_labels (Union[Iterable[int], int, None]) – Labels for which to fill holes. Defaults to None, that is filling holes for all labels.

  • +
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. Defaults to a full connectivity of input.ndim.

  • +
+
+
+
+ +
+ +
+
+

LabelToContour#

+example of LabelToContour +
+
+class monai.transforms.LabelToContour(kernel_type='Laplace')[source]#
+

Return the contour of binary input images that only compose of 0 and 1, with Laplacian kernel +set as default for edge detection. Typical usage is to plot the edge of label or segmentation output.

+
+
Parameters:
+

kernel_type (str) – the method applied to do edge detection, default is “Laplace”.

+
+
Raises:
+

NotImplementedError – When kernel_type is not “Laplace”.

+
+
+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – torch tensor data to extract the contour, with shape: [channels, height, width[, depth]]

+
+
Raises:
+

ValueError – When image ndim is not one of [3, 4].

+
+
Returns:
+

    +
  1. it’s the binary classification result of whether a pixel is edge or not.

  2. +
  3. in order to keep the original shape of mask image, we use padding as default.

  4. +
  5. the edge detection is just approximate because it defects inherent to Laplace kernel, +ideally the edge should be thin enough, but now it has a thickness.

  6. +
+

+
+
Return type:
+

A torch tensor with the same shape as img, note

+
+
+
+ +
+ +
+
+

MeanEnsemble#

+
+
+class monai.transforms.MeanEnsemble(weights=None)[source]#
+

Execute mean ensemble on the input data. +The input data can be a list or tuple of PyTorch Tensor with shape: [C[, H, W, D]], +Or a single PyTorch Tensor with shape: [E, C[, H, W, D]], the E dimension represents +the output data from different models. +Typically, the input data is model output of segmentation task or classification task. +And it also can support to add weights for the input data.

+
+
Parameters:
+

weights (Union[Sequence[float], ndarray, Tensor, None]) – can be a list or tuple of numbers for input data with shape: [E, C, H, W[, D]]. +or a Numpy ndarray or a PyTorch Tensor data. +the weights will be added to input data from highest dimension, for example: +1. if the weights only has 1 dimension, it will be added to the E dimension of input data. +2. if the weights has 2 dimensions, it will be added to E and C dimensions. +it’s a typical practice to add weights for different classes: +to ensemble 3 segmentation model outputs, every output has 4 channels(classes), +so the input data shape can be: [3, 4, H, W, D]. +and add different weights for different classes, so the weights shape can be: [3, 4]. +for example: weights = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 1, 1, 1]].

+
+
+
+
+__call__(img)[source]#
+

Call self as a function.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

ProbNMS#

+
+
+class monai.transforms.ProbNMS(spatial_dims=2, sigma=0.0, prob_threshold=0.5, box_size=48)[source]#
+

Performs probability based non-maximum suppression (NMS) on the probabilities map via +iteratively selecting the coordinate with highest probability and then move it as well +as its surrounding values. The remove range is determined by the parameter box_size. +If multiple coordinates have the same highest probability, only one of them will be +selected.

+
+
Parameters:
+
    +
  • spatial_dims (int) – number of spatial dimensions of the input probabilities map. +Defaults to 2.

  • +
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – the standard deviation for gaussian filter. +It could be a single value, or spatial_dims number of values. Defaults to 0.0.

  • +
  • prob_threshold (float) – the probability threshold, the function will stop searching if +the highest probability is no larger than the threshold. The value should be +no less than 0.0. Defaults to 0.5.

  • +
  • box_size (UnionType[int, Sequence[int]]) – the box size (in pixel) to be removed around the pixel with the maximum probability. +It can be an integer that defines the size of a square or cube, +or a list containing different values for each dimensions. Defaults to 48.

  • +
+
+
Returns:
+

a list of selected lists, where inner lists contain probability and coordinates. +For example, for 3D input, the inner lists are in the form of [probability, x, y, z].

+
+
Raises:
+
    +
  • ValueError – When prob_threshold is less than 0.0.

  • +
  • ValueError – When box_size is a list or tuple, and its length is not equal to spatial_dims.

  • +
  • ValueError – When box_size has a less than 1 value.

  • +
+
+
+
+ +
+
+

SobelGradients#

+
+
+class monai.transforms.SobelGradients(kernel_size=3, spatial_axes=None, normalize_kernels=True, normalize_gradients=False, padding_mode='reflect', dtype=torch.float32)[source]#
+

Calculate Sobel gradients of a grayscale image with the shape of CxH[xWxDx…] or BxH[xWxDx…].

+
+
Parameters:
+
    +
  • kernel_size (int) – the size of the Sobel kernel. Defaults to 3.

  • +
  • spatial_axes (Union[Sequence[int], int, None]) – the axes that define the direction of the gradient to be calculated. It calculate the gradient +along each of the provide axis. By default it calculate the gradient for all spatial axes.

  • +
  • normalize_kernels (bool) – if normalize the Sobel kernel to provide proper gradients. Defaults to True.

  • +
  • normalize_gradients (bool) – if normalize the output gradient to 0 and 1. Defaults to False.

  • +
  • padding_mode (str) – the padding mode of the image when convolving with Sobel kernels. Defaults to “reflect”. +Acceptable values are 'zeros', 'reflect', 'replicate' or 'circular'. +See torch.nn.Conv1d() for more information.

  • +
  • dtype (dtype) – kernel data type (torch.dtype). Defaults to torch.float32.

  • +
+
+
+
+
+__call__(image)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

VoteEnsemble#

+
+
+class monai.transforms.VoteEnsemble(num_classes=None)[source]#
+

Execute vote ensemble on the input data. +The input data can be a list or tuple of PyTorch Tensor with shape: [C[, H, W, D]], +Or a single PyTorch Tensor with shape: [E[, C, H, W, D]], the E dimension represents +the output data from different models. +Typically, the input data is model output of segmentation task or classification task.

+
+

Note

+

This vote transform expects the input data is discrete values. It can be multiple channels +data in One-Hot format or single channel data. It will vote to select the most common data +between items. +The output data has the same shape as every item of the input data.

+
+
+
Parameters:
+

num_classes (Optional[int, None]) – if the input is single channel data instead of One-Hot, we can’t get class number +from channel, need to explicitly specify the number of classes to vote.

+
+
+
+
+__call__(img)[source]#
+

Call self as a function.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+
+

Signal#

+
+

SignalRandDrop#

+
+
+class monai.transforms.SignalRandDrop(boundaries=(0.0, 1.0))[source]#
+

Randomly drop a portion of a signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to be dropped

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(0.0, 1.0))[source]#
+
+
Parameters:
+
    +
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the signal drop,

  • +
  • default (lower and upper values need to be positive) – [0.0, 1.0]

  • +
+
+
+
+ +
+ +
+
+

SignalRandScale#

+
+
+class monai.transforms.SignalRandScale(boundaries=(-1.0, 1.0))[source]#
+

Apply a random rescaling on a signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to be scaled

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(-1.0, 1.0))[source]#
+
+
Parameters:
+

boundaries (Sequence[float]) – list defining lower and upper boundaries for the signal scaling, default : [-1.0, 1.0]

+
+
+
+ +
+ +
+
+

SignalRandShift#

+
+
+class monai.transforms.SignalRandShift(mode='wrap', filling=0.0, boundaries=(-1.0, 1.0))[source]#
+

Apply a random shift on a signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to be shifted

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(mode='wrap', filling=0.0, boundaries=(-1.0, 1.0))[source]#
+
+
Parameters:
+
+
+
+
+ +
+ +
+
+

SignalRandAddSine#

+
+
+class monai.transforms.SignalRandAddSine(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02))[source]#
+

Add a random sinusoidal signal to the input signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which sinusoidal signal will be added

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02))[source]#
+
+
Parameters:
+
    +
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the sinusoidal magnitude, +lower and upper values need to be positive ,default : [0.1, 0.3]

  • +
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for sinusoidal +signal generation ,default : [0.001, 0.02]

  • +
+
+
+
+ +
+ +
+
+

SignalRandAddSquarePulse#

+
+
+class monai.transforms.SignalRandAddSquarePulse(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02))[source]#
+

Add a random square pulse signal to the input signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which square pulse will be added

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02))[source]#
+
+
Parameters:
+
    +
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the square pulse magnitude, +lower and upper values need to be positive , default : [0.01, 0.2]

  • +
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for the square pulse +signal generation , default : [0.001, 0.02]

  • +
+
+
+
+ +
+ +
+
+

SignalRandAddGaussianNoise#

+
+
+class monai.transforms.SignalRandAddGaussianNoise(boundaries=(0.001, 0.02))[source]#
+

Add a random gaussian noise to the input signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which gaussian noise will be added

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(0.001, 0.02))[source]#
+
+
Parameters:
+

boundaries (Sequence[float]) – list defining lower and upper boundaries for the signal magnitude, +default : [0.001,0.02]

+
+
+
+ +
+ +
+
+

SignalRandAddSinePartial#

+
+
+class monai.transforms.SignalRandAddSinePartial(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
+

Add a random partial sinusoidal signal to the input signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+
    +
  • signal (Union[ndarray, Tensor]) – input 1 dimension signal to which a partial sinusoidal signal

  • +
  • added (will be) –

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
+
+
Parameters:
+
    +
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the sinusoidal magnitude, +lower and upper values need to be positive , default : [0.1, 0.3]

  • +
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for sinusoidal +signal generation , default : [0.001, 0.02]

  • +
  • fraction (Sequence[float]) – list defining lower and upper boundaries for partial signal generation +default : [0.01, 0.2]

  • +
+
+
+
+ +
+ +
+
+

SignalRandAddSquarePulsePartial#

+
+
+class monai.transforms.SignalRandAddSquarePulsePartial(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
+

Add a random partial square pulse to a signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which a partial square pulse will be added

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
+
+
Parameters:
+
    +
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the square pulse magnitude, +lower and upper values need to be positive , default : [0.01, 0.2]

  • +
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for square pulse +signal generation example : [0.001, 0.02]

  • +
  • fraction (Sequence[float]) – list defining lower and upper boundaries for partial square pulse generation +default: [0.01, 0.2]

  • +
+
+
+
+ +
+ +
+
+

SignalFillEmpty#

+
+
+class monai.transforms.SignalFillEmpty(replacement=0.0)[source]#
+

replace empty part of a signal (NaN)

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (Union[ndarray, Tensor]) – signal to be filled

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(replacement=0.0)[source]#
+
+
Parameters:
+

replacement (float) – value to replace nan items in signal

+
+
+
+ +
+ +
+
+

SignalRemoveFrequency#

+
+
+class monai.transforms.SignalRemoveFrequency(frequency=None, quality_factor=None, sampling_freq=None)[source]#
+

Remove a frequency from a signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (ndarray) – signal to be frequency removed

+
+
Return type:
+

Any

+
+
+
+ +
+
+__init__(frequency=None, quality_factor=None, sampling_freq=None)[source]#
+
+
Parameters:
+
+
+
+
+ +
+ +
+
+

SignalContinuousWavelet#

+
+
+class monai.transforms.SignalContinuousWavelet(type='mexh', length=125.0, frequency=500.0)[source]#
+

Generate continuous wavelet transform of a signal

+
+
+__call__(signal)[source]#
+
+
Parameters:
+

signal (ndarray) – signal for which to generate continuous wavelet transform

+
+
Return type:
+

Any

+
+
+
+ +
+
+__init__(type='mexh', length=125.0, frequency=500.0)[source]#
+
+
Parameters:
+
+
+
+
+ +
+ +
+
+
+

Spatial#

+
+

SpatialResample#

+
+
+class monai.transforms.SpatialResample(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, lazy=False)[source]#
+

Resample input image from the orientation/spacing defined by src_affine affine matrix into +the ones specified by dst_affine affine matrix.

+

Internally this transform computes the affine transform matrix from src_affine to dst_affine, +by xform = linalg.solve(src_affine, dst_affine), and call monai.transforms.Affine with xform.

+
+
+__call__(img, dst_affine=None, spatial_size=None, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – input image to be resampled. It currently supports channel-first arrays with +at most three spatial dimensions.

  • +
  • dst_affine (Optional[Tensor, None]) – destination affine matrix. Defaults to None, which means the same as img.affine. +the shape should be (r+1, r+1) where r is the spatial rank of img. +when dst_affine and spatial_size are None, the input will be returned without resampling, +but the data type will be float32.

  • +
  • spatial_size (Union[Sequence[int], Tensor, int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, +the transform will compute a spatial size automatically containing the previous field of view. +if spatial_size is -1 are the transform will use the corresponding input img size.

  • +
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to self.padding_mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • align_corners (Optional[bool, None]) – Geometrically, we consider the pixels of the input as squares rather than points. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +Defaults to None, effectively using the value of self.align_corners.

  • +
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to self.dtype or +np.float64 (for best precision). If None, use the data type of input data. +To be compatible with other modules, the output data type is always float32.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
+

The spatial rank is determined by the smallest among img.ndim -1, len(src_affine) - 1, and 3.

+

When both monai.config.USE_COMPILED and align_corners are set to True, +MONAI’s resampling implementation will be used. +Set dst_affine and spatial_size to None to turn off the resampling step.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, lazy=False)[source]#
+
+
Parameters:
+
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

ResampleToMatch#

+
+
+class monai.transforms.ResampleToMatch(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, lazy=False)[source]#
+

Resample an image to match given metadata. The affine matrix will be aligned, +and the size of the output image will match.

+
+
+__call__(img, img_dst, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
+
+
Parameters:
+
+
+
Raises:
+

ValueError – When the affine matrix of the source image is not invertible.

+
+
Return type:
+

Tensor

+
+
Returns:
+

Resampled input tensor or MetaTensor.

+
+
+
+ +
+ +
+
+

Spacing#

+example of Spacing +
+
+class monai.transforms.Spacing(pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, lazy=False)[source]#
+

Resample input image into the specified pixdim.

+
+
+__call__(data_array, affine=None, mode=None, padding_mode=None, align_corners=None, dtype=None, scale_extent=None, output_spatial_shape=None, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data_array (Tensor) – in shape (num_channels, H[, W, …]).

  • +
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "self.mode". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "self.padding_mode". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • align_corners (Optional[bool, None]) – Geometrically, we consider the pixels of the input as squares rather than points. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +Defaults to None, effectively using the value of self.align_corners.

  • +
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to self.dtype. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • scale_extent (Optional[bool, None]) – whether the scale is computed based on the spacing or the full extent of voxels, +The option is ignored if output spatial size is specified when calling this transform. +See also: monai.data.utils.compute_shape_offset(). When this is True, align_corners +should be True because compute_shape_offset already provides the corner alignment shift/scaling.

  • +
  • output_spatial_shape (Union[Sequence[int], ndarray, int, None]) – specify the shape of the output data_array. This is typically useful for +the inverse of Spacingd where sometimes we could not compute the exact shape due to the quantization +error with the affine.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Raises:
+
    +
  • ValueError – When data_array has no spatial dimensions.

  • +
  • ValueError – When pixdim is nonpositive.

  • +
+
+
Return type:
+

Tensor

+
+
Returns:
+

data tensor or MetaTensor (resampled into self.pixdim).

+
+
+
+ +
+
+__init__(pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, lazy=False)[source]#
+
+
Parameters:
+
    +
  • pixdim (Union[Sequence[float], float, ndarray]) – output voxel spacing. if providing a single number, will use it for the first dimension. +items of the pixdim sequence map to the spatial dimensions of input image, if length +of pixdim sequence is longer than image spatial dimensions, will ignore the longer part, +if shorter, will pad with the last value. For example, for 3D image if pixdim is [1.0, 2.0] it +will be padded to [1.0, 2.0, 2.0] +if the components of the pixdim are non-positive values, the transform will use the +corresponding components of the original pixdim, which is computed from the affine +matrix of input image.

  • +
  • diagonal (bool) –

    whether to resample the input to have a diagonal affine matrix. +If True, the input data is resampled to the following affine:

    +
    np.diag((pixdim_0, pixdim_1, ..., pixdim_n, 1))
    +
    +
    +

    This effectively resets the volume to the world coordinate system (RAS+ in nibabel). +The original orientation, rotation, shearing are not preserved.

    +

    If False, this transform preserves the axes orientation, orthogonal rotation and +translation components from the original affine. This option will not flip/swap axes +of the original data.

    +

  • +
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • align_corners (bool) – Geometrically, we consider the pixels of the input as squares rather than points. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • scale_extent (bool) – whether the scale is computed based on the spacing or the full extent of voxels, +default False. The option is ignored if output spatial size is specified when calling this transform. +See also: monai.data.utils.compute_shape_offset(). When this is True, align_corners +should be True because compute_shape_offset already provides the corner alignment shift/scaling.

  • +
  • recompute_affine (bool) – whether to recompute affine based on the output shape. The affine computed +analytically does not reflect the potential quantization errors in terms of the output shape. +Set this flag to True to recompute the output affine based on the actual pixdim. Default to False.

  • +
  • min_pixdim (Union[Sequence[float], float, ndarray, None]) – minimal input spacing to be resampled. If provided, input image with a larger spacing than this +value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the +value of pixdim. Default to None.

  • +
  • max_pixdim (Union[Sequence[float], float, ndarray, None]) – maximal input spacing to be resampled. If provided, input image with a smaller spacing than this +value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the +value of pixdim. Default to None.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Orientation#

+example of Orientation +
+
+class monai.transforms.Orientation(axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), lazy=False)[source]#
+

Change the input image’s orientation into the specified based on axcodes.

+
+
+__call__(data_array, lazy=None)[source]#
+

If input type is MetaTensor, original affine is extracted with data_array.affine. +If input type is torch.Tensor, original affine is assumed to be identity.

+
+
Parameters:
+
    +
  • data_array (Tensor) – in shape (num_channels, H[, W, …]).

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Raises:
+
    +
  • ValueError – When data_array has no spatial dimensions.

  • +
  • ValueError – When axcodes spatiality differs from data_array.

  • +
+
+
Return type:
+

Tensor

+
+
Returns:
+

+
data_array [reoriented in self.axcodes]. Output type will be MetaTensor

unless get_track_meta() == False, in which case it will be +torch.Tensor.

+
+
+

+
+
+
+ +
+
+__init__(axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), lazy=False)[source]#
+
+
Parameters:
+
    +
  • axcodes (Optional[str, None]) – N elements sequence for spatial ND input’s orientation. +e.g. axcodes=’RAS’ represents 3D orientation: +(Left, Right), (Posterior, Anterior), (Inferior, Superior). +default orientation labels options are: ‘L’ and ‘R’ for the first dimension, +‘P’ and ‘A’ for the second, ‘I’ and ‘S’ for the third.

  • +
  • as_closest_canonical (bool) – if True, load the image as closest to canonical axis format.

  • +
  • labels (Optional[Sequence[tuple[str, str]], None]) – optional, None or sequence of (2,) sequences +(2,) sequences are labels for (beginning, end) of output axis. +Defaults to (('L', 'R'), ('P', 'A'), ('I', 'S')).

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
Raises:
+

ValueError – When axcodes=None and as_closest_canonical=True. Incompatible values.

+
+
+

See Also: nibabel.orientations.ornt2axcodes.

+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

RandRotate#

+example of RandRotate +
+
+class monai.transforms.RandRotate(range_x=0.0, range_y=0.0, range_z=0.0, prob=0.1, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float32'>, lazy=False)[source]#
+

Randomly rotate the input arrays.

+
+
Parameters:
+
    +
  • range_x (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and second axes. +If single number, angle is uniformly sampled from (-range_x, range_x).

  • +
  • range_y (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and third axes. +If single number, angle is uniformly sampled from (-range_y, range_y). only work for 3D data.

  • +
  • range_z (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the second and third axes. +If single number, angle is uniformly sampled from (-range_z, range_z). only work for 3D data.

  • +
  • prob (float) – Probability of rotation.

  • +
  • keep_size (bool) – If it is False, the output shape is adapted so that the +input array is contained completely in the output. +If it is True, the output shape is the same as the input. Default is True.

  • +
  • mode (str) – {"bilinear", "nearest"} +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • align_corners (bool) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape 2D: (nchannels, H, W), or 3D: (nchannels, H, W, D).

  • +
  • mode (Optional[str, None]) – {"bilinear", "nearest"} +Interpolation mode to calculate output values. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to self.padding_mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • align_corners (Optional[bool, None]) – Defaults to self.align_corners. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandFlip#

+example of RandFlip +
+
+class monai.transforms.RandFlip(prob=0.1, spatial_axis=None, lazy=False)[source]#
+

Randomly flips the image along axes. Preserves shape. +See numpy.flip for additional details. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

+
+
Parameters:
+
    +
  • prob (float) – Probability of flipping.

  • +
  • spatial_axis (Union[Sequence[int], int, None]) – Spatial axes along which to flip over. Default is None.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(img, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]),

  • +
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandAxisFlip#

+example of RandAxisFlip +
+
+class monai.transforms.RandAxisFlip(prob=0.1, lazy=False)[source]#
+

Randomly select a spatial axis and flip along it. +See numpy.flip for additional details. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

+
+
Parameters:
+
    +
  • prob (float) – Probability of flipping.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(img, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ])

  • +
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(data)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

RandZoom#

+example of RandZoom +
+
+class monai.transforms.RandZoom(prob=0.1, min_zoom=0.9, max_zoom=1.1, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=torch.float32, keep_size=True, lazy=False, **kwargs)[source]#
+

Randomly zooms input arrays with given probability within given zoom range.

+
+
Parameters:
+
    +
  • prob (float) – Probability of zooming.

  • +
  • min_zoom (Union[Sequence[float], float]) – Min zoom factor. Can be float or sequence same size as image. +If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims +to keep the original spatial shape ratio. +If a sequence, min_zoom should contain one value for each spatial axis. +If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • +
  • max_zoom (Union[Sequence[float], float]) – Max zoom factor. Can be float or sequence same size as image. +If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims +to keep the original spatial shape ratio. +If a sequence, max_zoom should contain one value for each spatial axis. +If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • +
  • mode (str) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to "area". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • padding_mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +The mode to pad data after zooming. +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • align_corners (Optional[bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data.

  • +
  • keep_size (bool) – Should keep original size (pad if needed), default is True.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape 2D: (nchannels, H, W), or 3D: (nchannels, H, W, D).

  • +
  • mode (Optional[str, None]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", +"area"}, the interpolation mode. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • padding_mode (Optional[str, None]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +The mode to pad data after zooming. +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • align_corners (Optional[bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Defaults to self.align_corners. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. +If None, use the data type of input data.

  • +
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+randomize(img)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

Affine#

+example of Affine +
+
+class monai.transforms.Affine(rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, normalized=False, device=None, dtype=<class 'numpy.float32'>, align_corners=False, image_only=False, lazy=False)[source]#
+

Transform img given the affine parameters. +A tutorial is available: Project-MONAI/tutorials.

+
+
+__call__(img, spatial_size=None, mode=None, padding_mode=None, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – shape must be (num_channels, H, W[, D]),

  • +
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if img has two spatial dimensions, spatial_size should have 2 elements [h, w]. +if img has three spatial dimensions, spatial_size should have 3 elements [h, w, d].

  • +
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to self.padding_mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

UnionType[Tensor, tuple[Tensor, Union[ndarray, Tensor]]]

+
+
+
+ +
+
+__init__(rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, normalized=False, device=None, dtype=<class 'numpy.float32'>, align_corners=False, image_only=False, lazy=False)[source]#
+

The affine transformations are applied in rotate, shear, translate, scale order.

+
+
Parameters:
+
    +
  • rotate_params (Union[Sequence[float], float, None]) – a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. +Defaults to no rotation.

  • +
  • shear_params (Union[Sequence[float], float, None]) –

    shearing factors for affine matrix, take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +a tuple of 2 floats for 2D, a tuple of 6 floats for 3D. Defaults to no shearing.
    +
    +
    +

  • +
  • translate_params (Union[Sequence[float], float, None]) – a tuple of 2 floats for 2D, a tuple of 3 floats for 3D. Translation is in +pixel/voxel relative to the center of the input image. Defaults to no translation.

  • +
  • scale_params (Union[Sequence[float], float, None]) – scale factor for every spatial dims. a tuple of 2 floats for 2D, +a tuple of 3 floats for 3D. Defaults to 1.0.

  • +
  • affine (Union[ndarray, Tensor, None]) – If applied, ignore the params (rotate_params, etc.) and use the +supplied matrix. Should be square with each side = num of image spatial +dimensions + 1.

  • +
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • normalized (bool) – indicating whether the provided affine is defined to include a normalization +transform converting the coordinates from [-(size-1)/2, (size-1)/2] (defined in create_grid) to +[0, size - 1] or [-1, 1] in order to be compatible with the underlying resampling API. +If normalized=False, additional coordinate normalization will be applied before resampling. +See also: monai.networks.utils.normalize_transform().

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • align_corners (bool) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • image_only (bool) – if True return only the image volume, otherwise return (image, affine).

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Resample#

+
+
+class monai.transforms.Resample(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, norm_coords=True, device=None, align_corners=False, dtype=<class 'numpy.float64'>)[source]#
+
+
+__call__(img, grid=None, mode=None, padding_mode=None, dtype=None, align_corners=None)[source]#
+
+
Parameters:
+
+
+
+
+

See also

+

monai.config.USE_COMPILED

+
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, norm_coords=True, device=None, align_corners=False, dtype=<class 'numpy.float64'>)[source]#
+

computes output image using values from img, locations from grid using pytorch. +supports spatially 2D or 3D (num_channels, H, W[, D]).

+
+
Parameters:
+
+
+
+
+ +
+ +
+
+

RandAffine#

+example of RandAffine +
+
+class monai.transforms.RandAffine(prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, lazy=False)[source]#
+

Random affine transform. +A tutorial is available: Project-MONAI/tutorials.

+
+
+__call__(img, spatial_size=None, mode=None, padding_mode=None, randomize=True, grid=None, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – shape must be (num_channels, H, W[, D]),

  • +
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if img has two spatial dimensions, spatial_size should have 2 elements [h, w]. +if img has three spatial dimensions, spatial_size should have 3 elements [h, w, d].

  • +
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to self.padding_mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • +
  • grid – precomputed grid to be used (mainly to accelerate RandAffined).

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, lazy=False)[source]#
+
+
Parameters:
+
    +
  • prob (float) – probability of returning a randomized affine grid. +defaults to 0.1, with 10% chance returns a randomized grid.

  • +
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 2 floats for 2D, a tuple of 6 floats for 3D) for affine matrix, +take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select pixel/voxel to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to bilinear. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to reflection. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • cache_grid (bool) – whether to cache the identity sampling grid. +If the spatial size is not dynamically defined by input image, enabling this option could +accelerate the transform.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+

See also

+
    +
  • RandAffineGrid for the random affine parameters configurations.

  • +
  • Affine for the affine transformation parameters configurations.

  • +
+
+
+ +
+
+get_identity_grid(spatial_size, lazy)[source]#
+

Return a cached or new identity grid depends on the availability.

+
+
Parameters:
+

spatial_size (Sequence[int]) – non-dynamic spatial size

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandAffine

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandDeformGrid#

+
+
+class monai.transforms.RandDeformGrid(spacing, magnitude_range, device=None)[source]#
+

Generate random deformation grid.

+
+
+__call__(spatial_size)[source]#
+
+
Parameters:
+

spatial_size (Sequence[int]) – spatial size of the grid.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(spacing, magnitude_range, device=None)[source]#
+
+
Parameters:
+
    +
  • spacing (Union[Sequence[float], float]) – spacing of the grid in 2D or 3D. +e.g., spacing=(1, 1) indicates pixel-wise deformation in 2D, +spacing=(1, 1, 1) indicates voxel-wise deformation in 3D, +spacing=(2, 2) indicates deformation field defined on every other pixel in 2D.

  • +
  • magnitude_range (tuple[float, float]) – the random offsets will be generated from +uniform[magnitude[0], magnitude[1]).

  • +
  • device (Optional[device, None]) – device to store the output grid data.

  • +
+
+
+
+ +
+
+randomize(grid_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

AffineGrid#

+
+
+class monai.transforms.AffineGrid(rotate_params=None, shear_params=None, translate_params=None, scale_params=None, device=None, dtype=<class 'numpy.float32'>, align_corners=False, affine=None, lazy=False)[source]#
+

Affine transforms on the coordinates.

+
+
Parameters:
+
    +
  • rotate_params (Union[Sequence[float], float, None]) – a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. +Defaults to no rotation.

  • +
  • shear_params (Union[Sequence[float], float, None]) –

    shearing factors for affine matrix, take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +a tuple of 2 floats for 2D, a tuple of 6 floats for 3D. Defaults to no shearing.
    +
    +
    +

  • +
  • translate_params (Union[Sequence[float], float, None]) – a tuple of 2 floats for 2D, a tuple of 3 floats for 3D. Translation is in +pixel/voxel relative to the center of the input image. Defaults to no translation.

  • +
  • scale_params (Union[Sequence[float], float, None]) – scale factor for every spatial dims. a tuple of 2 floats for 2D, +a tuple of 3 floats for 3D. Defaults to 1.0.

  • +
  • dtype (Union[dtype, type, str, None]) – data type for the grid computation. Defaults to float32. +If None, use the data type of input data (if grid is provided).

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated, if a new grid is generated.

  • +
  • align_corners (bool) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • affine (Union[ndarray, Tensor, None]) – If applied, ignore the params (rotate_params, etc.) and use the +supplied matrix. Should be square with each side = num of image spatial +dimensions + 1.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(spatial_size=None, grid=None, lazy=None)[source]#
+

The grid can be initialized with a spatial_size parameter, or provided directly as grid. +Therefore, either spatial_size or grid must be provided. +When initialising from spatial_size, the backend “torch” will be used.

+
+
Parameters:
+
    +
  • spatial_size (Optional[Sequence[int], None]) – output grid size.

  • +
  • grid (Optional[Tensor, None]) – grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Raises:
+

ValueError – When grid=None and spatial_size=None. Incompatible values.

+
+
Return type:
+

tuple[UnionType[Tensor, None], Tensor]

+
+
+
+ +
+ +
+
+

RandAffineGrid#

+
+
+class monai.transforms.RandAffineGrid(rotate_range=None, shear_range=None, translate_range=None, scale_range=None, device=None, dtype=<class 'numpy.float32'>, lazy=False)[source]#
+

Generate randomised affine grid.

+
+
+__call__(spatial_size=None, grid=None, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • spatial_size (Optional[Sequence[int], None]) – output grid size.

  • +
  • grid (Union[ndarray, Tensor, None]) – grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D.

  • +
  • randomize (bool) – boolean as to whether the grid parameters governing the grid should be randomized.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
Returns:
+

a 2D (3xHxW) or 3D (4xHxWxD) grid.

+
+
+
+ +
+
+__init__(rotate_range=None, shear_range=None, translate_range=None, scale_range=None, device=None, dtype=<class 'numpy.float32'>, lazy=False)[source]#
+
+
Parameters:
+
    +
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 2 floats for 2D, a tuple of 6 floats for 3D) for affine matrix, +take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select voxels to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • device (Optional[device, None]) – device to store the output grid data.

  • +
  • dtype (Union[dtype, type, str, None]) – data type for the grid computation. Defaults to np.float32. +If None, use the data type of input data (if grid is provided).

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+ +
+ +
+
+get_transformation_matrix()[source]#
+

Get the most recently applied transformation matrix

+
+
Return type:
+

UnionType[Tensor, None]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

GridDistortion#

+example of GridDistortion +
+
+class monai.transforms.GridDistortion(num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
+
+
+__call__(img, distort_steps=None, mode=None, padding_mode=None)[source]#
+
+
Parameters:
+
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
+

Grid distortion transform. Refer to: +albumentations-team/albumentations

+
+
Parameters:
+
+
+
+
+ +
+ +
+
+

RandGridDistortion#

+example of RandGridDistortion +
+
+class monai.transforms.RandGridDistortion(num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
+
+
+__call__(img, mode=None, padding_mode=None, randomize=True)[source]#
+
+
Parameters:
+
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
+

Random grid distortion transform. Refer to: +albumentations-team/albumentations

+
+
Parameters:
+
+
+
+
+ +
+
+randomize(spatial_shape)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

Rand2DElastic#

+example of Rand2DElastic +
+
+class monai.transforms.Rand2DElastic(spacing, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
+

Random elastic deformation and affine in 2D. +A tutorial is available: Project-MONAI/tutorials.

+
+
+__call__(img, spatial_size=None, mode=None, padding_mode=None, randomize=True)[source]#
+
+
Parameters:
+
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(spacing, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
+
+
Parameters:
+
    +
  • spacing (UnionType[tuple[float, float], float]) – distance in between the control points.

  • +
  • magnitude_range (tuple[float, float]) – the random offsets will be generated from uniform[magnitude[0], magnitude[1]).

  • +
  • prob (float) – probability of returning a randomized elastic transform. +defaults to 0.1, with 10% chance returns a randomized elastic transform, +otherwise returns a spatial_size centered area extracted from the input image.

  • +
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 2 floats for 2D) for affine matrix, take a 2D affine as example:

    +
    [
    +    [1.0, params[0], 0.0],
    +    [params[1], 1.0, 0.0],
    +    [0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select pixel to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • spatial_size (Union[int, tuple[int, int], None]) – specifying output image spatial size [h, w]. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
+
+
+
+

See also

+
    +
  • RandAffineGrid for the random affine parameters configurations.

  • +
  • Affine for the affine transformation parameters configurations.

  • +
+
+
+ +
+
+randomize(spatial_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Rand2DElastic

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

Rand3DElastic#

+example of Rand3DElastic +
+
+class monai.transforms.Rand3DElastic(sigma_range, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
+

Random elastic deformation and affine in 3D. +A tutorial is available: Project-MONAI/tutorials.

+
+
+__call__(img, spatial_size=None, mode=None, padding_mode=None, randomize=True)[source]#
+
+
Parameters:
+
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(sigma_range, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
+
+
Parameters:
+
    +
  • sigma_range (tuple[float, float]) – a Gaussian kernel with standard deviation sampled from +uniform[sigma_range[0], sigma_range[1]) will be used to smooth the random offset grid.

  • +
  • magnitude_range (tuple[float, float]) – the random offsets on the grid will be generated from +uniform[magnitude[0], magnitude[1]).

  • +
  • prob (float) – probability of returning a randomized elastic transform. +defaults to 0.1, with 10% chance returns a randomized elastic transform, +otherwise returns a spatial_size centered area extracted from the input image.

  • +
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 6 floats for 3D) for affine matrix, take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select voxel to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • spatial_size (Union[tuple[int, int, int], int, None]) – specifying output image spatial size [h, w, d]. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, 32, -1) will be adapted +to (32, 32, 64) if the third spatial dimension size of img is 64.

  • +
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
+
+
+
+

See also

+
    +
  • RandAffineGrid for the random affine parameters configurations.

  • +
  • Affine for the affine transformation parameters configurations.

  • +
+
+
+ +
+
+randomize(grid_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Rand3DElastic

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

Rotate90#

+example of Rotate90 +
+
+class monai.transforms.Rotate90(k=1, spatial_axes=(0, 1), lazy=False)[source]#
+

Rotate an array by 90 degrees in the plane specified by axes. +See torch.rot90 for additional details: +https://pytorch.org/docs/stable/generated/torch.rot90.html#torch-rot90.

+
+
+__call__(img, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]),

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(k=1, spatial_axes=(0, 1), lazy=False)[source]#
+
+
Parameters:
+
    +
  • k (int) – number of times to rotate by 90 degrees.

  • +
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. +Default: (0, 1), this is the first two axis in spatial dimensions. +If axis is negative it counts from the last to the first axis.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

RandRotate90#

+example of RandRotate90 +
+
+class monai.transforms.RandRotate90(prob=0.1, max_k=3, spatial_axes=(0, 1), lazy=False)[source]#
+

With probability prob, input arrays are rotated by 90 degrees +in the plane specified by spatial_axes.

+
+
+__call__(img, randomize=True, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]),

  • +
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+__init__(prob=0.1, max_k=3, spatial_axes=(0, 1), lazy=False)[source]#
+
+
Parameters:
+
    +
  • prob (float) – probability of rotating. +(Default 0.1, with 10% probability it returns a rotated array)

  • +
  • max_k (int) – number of rotations will be sampled from np.random.randint(max_k) + 1, (Default 3).

  • +
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. +Default: (0, 1), this is the first two axis in spatial dimensions.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

Flip#

+example of Flip +
+
+class monai.transforms.Flip(spatial_axis=None, lazy=False)[source]#
+

Reverses the order of elements along the given spatial axis. Preserves shape. +See torch.flip documentation for additional details: +https://pytorch.org/docs/stable/generated/torch.flip.html

+
+
Parameters:
+
    +
  • spatial_axis (Union[Sequence[int], int, None]) – spatial axes along which to flip over. Default is None. +The default axis=None will flip over all of the axes of the input array. +If axis is negative it counts from the last to the first axis. +If axis is a tuple of ints, flipping is performed on all of the axes +specified in the tuple.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(img, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ])

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

Resize#

+example of Resize +
+
+class monai.transforms.Resize(spatial_size, size_mode='all', mode=InterpolateMode.AREA, align_corners=None, anti_aliasing=False, anti_aliasing_sigma=None, dtype=torch.float32, lazy=False)[source]#
+

Resize the input image to given spatial size (with scaling, not cropping/padding). +Implemented using torch.nn.functional.interpolate.

+
+
Parameters:
+
    +
  • spatial_size (Union[Sequence[int], int]) – expected shape of spatial dimensions after resize operation. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • size_mode (str) – should be “all” or “longest”, if “all”, will use spatial_size for all the spatial dims, +if “longest”, rescale the image so that only the longest side is equal to specified spatial_size, +which must be an int number in this case, keeping the aspect ratio of the initial image, refer to: +https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ +#albumentations.augmentations.geometric.resize.LongestMaxSize.

  • +
  • mode (str) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to "area". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • align_corners (Optional[bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • anti_aliasing (bool) – bool +Whether to apply a Gaussian filter to smooth the image prior +to downsampling. It is crucial to filter when downsampling +the image to avoid aliasing artifacts. See also skimage.transform.resize

  • +
  • anti_aliasing_sigma (Union[Sequence[float], float, None]) – {float, tuple of floats}, optional +Standard deviation for Gaussian filtering used when anti-aliasing. +By default, this value is chosen as (s - 1) / 2 where s is the +downsampling factor, where s > 1. For the up-size case, s < 1, no +anti-aliasing is performed prior to rescaling.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(img, mode=None, align_corners=None, anti_aliasing=None, anti_aliasing_sigma=None, dtype=None, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]).

  • +
  • mode (Optional[str, None]) – {"nearest", "nearest-exact", "linear", +"bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • align_corners (Optional[bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Defaults to self.align_corners. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • anti_aliasing (Optional[bool, None]) – bool, optional +Whether to apply a Gaussian filter to smooth the image prior +to downsampling. It is crucial to filter when downsampling +the image to avoid aliasing artifacts. See also skimage.transform.resize

  • +
  • anti_aliasing_sigma (Union[Sequence[float], float, None]) – {float, tuple of floats}, optional +Standard deviation for Gaussian filtering used when anti-aliasing. +By default, this value is chosen as (s - 1) / 2 where s is the +downsampling factor, where s > 1. For the up-size case, s < 1, no +anti-aliasing is performed prior to rescaling.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. +If None, use the data type of input data.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Raises:
+

ValueError – When self.spatial_size length is less than img spatial dimensions.

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

Rotate#

+example of Rotate +
+
+class monai.transforms.Rotate(angle, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=torch.float32, lazy=False)[source]#
+

Rotates an input image by given angle using monai.networks.layers.AffineTransform.

+
+
Parameters:
+
    +
  • angle (Union[Sequence[float], float]) – Rotation angle(s) in radians. should a float for 2D, three floats for 3D.

  • +
  • keep_size (bool) – If it is True, the output shape is kept the same as the input. +If it is False, the output shape is adapted so that the +input array is contained completely in the output. Default is True.

  • +
  • mode (str) – {"bilinear", "nearest"} +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • align_corners (bool) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
+
+
Parameters:
+
+
+
Raises:
+

ValueError – When img spatially is not one of [2D, 3D].

+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

Zoom#

+example of Zoom +
+
+class monai.transforms.Zoom(zoom, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=torch.float32, keep_size=True, lazy=False, **kwargs)[source]#
+

Zooms an ND image using torch.nn.functional.interpolate. +For details, please see https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html.

+

Different from monai.transforms.resize, this transform takes scaling factors +as input, and provides an option of preserving the input spatial size.

+
+
Parameters:
+
    +
  • zoom (Union[Sequence[float], float]) – The zoom factor along the spatial axes. +If a float, zoom is the same for each spatial axis. +If a sequence, zoom should contain one value for each spatial axis.

  • +
  • mode (str) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to "area". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • padding_mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "edge". +The mode to pad data after zooming. +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • align_corners (Optional[bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data.

  • +
  • keep_size (bool) – Should keep original size (padding/slicing if needed), default is True.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
+
+
Parameters:
+
    +
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]).

  • +
  • mode (Optional[str, None]) – {"nearest", "nearest-exact", "linear", +"bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to self.mode. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • padding_mode (Optional[str, None]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "edge". +The mode to pad data after zooming. +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • align_corners (Optional[bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Defaults to self.align_corners. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. +If None, use the data type of input data.

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

GridPatch#

+
+
+class monai.transforms.GridPatch(patch_size, offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, **pad_kwargs)[source]#
+

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps. +It can sort the patches and return all or a subset of them.

+
+
Parameters:
+
    +
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • +
  • offset (Optional[Sequence[int], None]) – offset of starting position in the array, default is 0 for each dimension.

  • +
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. +If the requested number of patches is greater than the number of available patches, +padding will be applied to provide exactly num_patches patches unless threshold is set. +When threshold is set, this value is treated as the maximum number of patches. +Defaults to None, which does not limit number of the patches.

  • +
  • overlap (Union[Sequence[float], float]) – the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). +If only one float number is given, it will be applied to all dimensions. Defaults to 0.0.

  • +
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), +lowest values (“min”), or in their default order (None). Default to None.

  • +
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. +Defaults to no filtering.

  • +
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. +Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. +Defaults to None, which means no padding will be applied. +See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +requires pytorch >= 1.10 for best compatibility.

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Returns:
+

+
the extracted patches as a single tensor (with patch dimension as the first dimension),

with following metadata:

+
    +
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • +
  • PatchKeys.COUNT: total number of patches in the image,

  • +
  • ”spatial_shape”: spatial size of the extracted patch, and

  • +
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • +
+
+
+

+
+
Return type:
+

MetaTensor

+
+
+
+
+__call__(array)[source]#
+

Extract the patches (sweeping the entire image in a row-major sliding-window manner with possible overlaps).

+
+
Parameters:
+

array (Union[ndarray, Tensor]) – a input image as numpy.ndarray or torch.Tensor

+
+
Returns:
+

+
the extracted patches as a single tensor (with patch dimension as the first dimension),

with defined PatchKeys.LOCATION and PatchKeys.COUNT metadata.

+
+
+

+
+
Return type:
+

MetaTensor

+
+
+
+ +
+
+filter_count(image_np, locations)[source]#
+

Sort the patches based on the sum of their intensity, and just keep self.num_patches of them.

+
+
Parameters:
+
    +
  • image_np (Union[ndarray, Tensor]) – a numpy.ndarray or torch.Tensor representing a stack of patches.

  • +
  • locations (ndarray) – a numpy.ndarray representing the stack of location of each patch.

  • +
+
+
Return type:
+

tuple[Union[ndarray, Tensor], ndarray]

+
+
+
+ +
+
+filter_threshold(image_np, locations)[source]#
+

Filter the patches and their locations according to a threshold.

+
+
Parameters:
+
    +
  • image_np (Union[ndarray, Tensor]) – a numpy.ndarray or torch.Tensor representing a stack of patches.

  • +
  • locations (ndarray) – a numpy.ndarray representing the stack of location of each patch.

  • +
+
+
Returns:
+

tuple of filtered patches and locations.

+
+
Return type:
+

tuple[NdarrayOrTensor, numpy.ndarray]

+
+
+
+ +
+ +
+
+

RandGridPatch#

+
+
+class monai.transforms.RandGridPatch(patch_size, min_offset=None, max_offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, **pad_kwargs)[source]#
+

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps, +and with random offset for the minimal corner of the image, (0,0) for 2D and (0,0,0) for 3D. +It can sort the patches and return all or a subset of them.

+
+
Parameters:
+
    +
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • +
  • min_offset (Union[Sequence[int], int, None]) – the minimum range of offset to be selected randomly. Defaults to 0.

  • +
  • max_offset (Union[Sequence[int], int, None]) – the maximum range of offset to be selected randomly. +Defaults to image size modulo patch size.

  • +
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. +If the requested number of patches is greater than the number of available patches, +padding will be applied to provide exactly num_patches patches unless threshold is set. +When threshold is set, this value is treated as the maximum number of patches. +Defaults to None, which does not limit number of the patches.

  • +
  • overlap (Union[Sequence[float], float]) – the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). +If only one float number is given, it will be applied to all dimensions. Defaults to 0.0.

  • +
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), +lowest values (“min”), or in their default order (None). Default to None.

  • +
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. +Defaults to no filtering.

  • +
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. +Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. +Defaults to None, which means no padding will be applied. +See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +requires pytorch >= 1.10 for best compatibility.

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Returns:
+

+
the extracted patches as a single tensor (with patch dimension as the first dimension),

with following metadata:

+
    +
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • +
  • PatchKeys.COUNT: total number of patches in the image,

  • +
  • ”spatial_shape”: spatial size of the extracted patch, and

  • +
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • +
+
+
+

+
+
Return type:
+

MetaTensor

+
+
+
+
+__call__(array, randomize=True)[source]#
+

Extract the patches (sweeping the entire image in a row-major sliding-window manner with possible overlaps).

+
+
Parameters:
+

array (Union[ndarray, Tensor]) – a input image as numpy.ndarray or torch.Tensor

+
+
Returns:
+

+
the extracted patches as a single tensor (with patch dimension as the first dimension),

with defined PatchKeys.LOCATION and PatchKeys.COUNT metadata.

+
+
+

+
+
Return type:
+

MetaTensor

+
+
+
+ +
+
+randomize(array)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+ +
+ +
+
+

GridSplit#

+
+
+class monai.transforms.GridSplit(grid=(2, 2), size=None)[source]#
+

Split the image into patches based on the provided grid in 2D.

+
+
Parameters:
+
    +
  • grid (tuple[int, int]) – a tuple define the shape of the grid upon which the image is split. Defaults to (2, 2)

  • +
  • size (Union[int, tuple[int, int], None]) – a tuple or an integer that defines the output patch sizes. +If it’s an integer, the value will be repeated for each dimension. +The default is None, where the patch size will be inferred from the grid shape.

  • +
+
+
+

Example

+

Given an image (torch.Tensor or numpy.ndarray) with size of (3, 10, 10) and a grid of (2, 2), +it will return a Tensor or array with the size of (4, 3, 5, 5). +Here, if the size is provided, the returned shape will be (4, 3, size, size)

+

Note: This transform currently support only image with two spatial dimensions.

+
+
+__call__(image, size=None)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

list[Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+
+

Smooth Field#

+
+

RandSmoothFieldAdjustContrast#

+example of RandSmoothFieldAdjustContrast +
+
+class monai.transforms.RandSmoothFieldAdjustContrast(spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.5, 4.5), device=None)[source]#
+

Randomly adjust the contrast of input images by calculating a randomized smooth field for each invocation.

+

This uses SmoothField internally to define the adjustment over the image. If pad is greater than 0 the +edges of the input volume of that width will be mostly unchanged. Contrast is changed by raising input +values by the power of the smooth field so the range of values given by gamma should be chosen with this +in mind. For example, a minimum value of 0 in gamma will produce white areas so this should be avoided. +After the contrast is adjusted the values of the result are rescaled to the range of the original input.

+
+
Parameters:
+
    +
  • spatial_size (Sequence[int]) – size of input array’s spatial dimensions

  • +
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • +
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 1

  • +
  • mode (str) – interpolation mode to use when upsampling

  • +
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • +
  • prob (float) – probability transform is applied

  • +
  • gamma (UnionType[Sequence[float], float]) – (min, max) range for exponential field

  • +
  • device (Optional[device, None]) – Pytorch device to define field on

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img, if randomize randomizing the smooth field otherwise reusing the previous.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandSmoothFieldAdjustContrast

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandSmoothFieldAdjustIntensity#

+example of RandSmoothFieldAdjustIntensity +
+
+class monai.transforms.RandSmoothFieldAdjustIntensity(spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.1, 1.0), device=None)[source]#
+

Randomly adjust the intensity of input images by calculating a randomized smooth field for each invocation.

+

This uses SmoothField internally to define the adjustment over the image. If pad is greater than 0 the +edges of the input volume of that width will be mostly unchanged. Intensity is changed by multiplying the +inputs by the smooth field, so the values of gamma should be chosen with this in mind. The default values +of (0.1, 1.0) are sensible in that values will not be zeroed out by the field nor multiplied greater than +the original value range.

+
+
Parameters:
+
    +
  • spatial_size (Sequence[int]) – size of input array

  • +
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • +
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 1

  • +
  • mode (str) – interpolation mode to use when upsampling

  • +
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • +
  • prob (float) – probability transform is applied

  • +
  • gamma (UnionType[Sequence[float], float]) – (min, max) range of intensity multipliers

  • +
  • device (Optional[device, None]) – Pytorch device to define field on

  • +
+
+
+
+
+__call__(img, randomize=True)[source]#
+

Apply the transform to img, if randomize randomizing the smooth field otherwise reusing the previous.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandSmoothFieldAdjustIntensity

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandSmoothDeform#

+example of RandSmoothDeform +
+
+class monai.transforms.RandSmoothDeform(spatial_size, rand_size, pad=0, field_mode=InterpolateMode.AREA, align_corners=None, prob=0.1, def_range=1.0, grid_dtype=torch.float32, grid_mode=GridSampleMode.NEAREST, grid_padding_mode=GridSamplePadMode.BORDER, grid_align_corners=False, device=None)[source]#
+

Deform an image using a random smooth field and Pytorch’s grid_sample.

+

The amount of deformation is given by def_range in fractions of the size of the image. The size of each dimension +of the input image is always defined as 2 regardless of actual image voxel dimensions, that is the coordinates in +every dimension range from -1 to 1. A value of 0.1 means pixels/voxels can be moved by up to 5% of the image’s size.

+
+
Parameters:
+
    +
  • spatial_size (Sequence[int]) – input array size to which deformation grid is interpolated

  • +
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • +
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • +
  • field_mode (str) – interpolation mode to use when upsampling the deformation field

  • +
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • +
  • prob (float) – probability transform is applied

  • +
  • def_range (UnionType[Sequence[float], float]) – value of the deformation range in image size fractions, single min/max value or min/max pair

  • +
  • grid_dtype – type for the deformation grid calculated from the field

  • +
  • grid_mode (str) – interpolation mode used for sampling input using deformation grid

  • +
  • grid_padding_mode (str) – padding mode used for sampling input using deformation grid

  • +
  • grid_align_corners (UnionType[bool, None]) – if True align the corners when sampling the deformation grid

  • +
  • device (Optional[device, None]) – Pytorch device to define field on

  • +
+
+
+
+
+__call__(img, randomize=True, device=None)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Randomizable

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+
+

MRI Transforms#

+
+

Kspace under-sampling#

+
+
+class monai.apps.reconstruction.transforms.array.KspaceMask(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
+

A basic class for under-sampling mask setup. It provides common +features for under-sampling mask generators. +For example, RandomMaskFunc and EquispacedMaskFunc (two mask +transform objects defined right after this module) +both inherit MaskFunc to properly setup properties like the +acceleration factor.

+
+
+abstract __call__(kspace)[source]#
+

This is an extra instance to allow for defining new mask generators. +For creating other mask transforms, define a new class and simply +override __call__. See an example of this in +monai.apps.reconstruction.transforms.array.RandomKspacemask.

+
+
Parameters:
+

kspace (Union[ndarray, Tensor]) – The input k-space data. The shape is (…,num_coils,H,W,2) +for complex 2D inputs and (…,num_coils,H,W,D) for real 3D +data.

+
+
Return type:
+

Sequence[Tensor]

+
+
+
+ +
+
+__init__(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
+
+
Parameters:
+
    +
  • center_fractions (Sequence[float]) – Fraction of low-frequency columns to be retained. +If multiple values are provided, then one of these numbers +is chosen uniformly each time.

  • +
  • accelerations (Sequence[float]) – Amount of under-sampling. This should have the +same length as center_fractions. If multiple values are +provided, then one of these is chosen uniformly each time.

  • +
  • spatial_dims (int) – Number of spatial dims (e.g., it’s 2 for a 2D data; +it’s also 2 for pseudo-3D datasets like the fastMRI dataset). +The last spatial dim is selected for sampling. For the fastMRI +dataset, k-space has the form (…,num_slices,num_coils,H,W) +and sampling is done along W. For a general 3D data with the +shape (…,num_coils,H,W,D), sampling is done along D.

  • +
  • is_complex (bool) – if True, then the last dimension will be reserved for +real/imaginary parts.

  • +
+
+
+
+ +
+
+randomize_choose_acceleration()[source]#
+

If multiple values are provided for center_fractions and +accelerations, this function selects one value uniformly +for each training/test sample.

+
+
Return type:
+

Sequence[float]

+
+
Returns:
+

+
A tuple containing

(1) center_fraction: chosen fraction of center kspace +lines to exclude from under-sampling +(2) acceleration: chosen acceleration factor

+
+
+

+
+
+
+ +
+ +
+
+class monai.apps.reconstruction.transforms.array.RandomKspaceMask(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
+

This k-space mask transform under-samples the k-space according to a +random sampling pattern. Precisely, it uniformly selects a subset of +columns from the input k-space data. If the k-space data has N columns, +the mask picks out:

+

1. N_low_freqs = (N * center_fraction) columns in the center +corresponding to low-frequencies

+

2. The other columns are selected uniformly at random with a probability +equal to: +prob = (N / acceleration - N_low_freqs) / (N - N_low_freqs). +This ensures that the expected number of columns selected is equal to +(N / acceleration)

+

It is possible to use multiple center_fractions and accelerations, +in which case one possible (center_fraction, acceleration) is chosen +uniformly at random each time the transform is called.

+

Example

+

If accelerations = [4, 8] and center_fractions = [0.08, 0.04], +then there is a 50% probability that 4-fold acceleration with 8% +center fraction is selected and a 50% probability that 8-fold +acceleration with 4% center fraction is selected.

+
+
Modified and adopted from:

facebookresearch/fastMRI

+
+
+
+
+__call__(kspace)[source]#
+
+
Parameters:
+

kspace (Union[ndarray, Tensor]) – The input k-space data. The shape is (…,num_coils,H,W,2) +for complex 2D inputs and (…,num_coils,H,W,D) for real 3D +data. The last spatial dim is selected for sampling. For the +fastMRI dataset, k-space has the form +(…,num_slices,num_coils,H,W) and sampling is done along W. +For a general 3D data with the shape (…,num_coils,H,W,D), +sampling is done along D.

+
+
Return type:
+

Sequence[Tensor]

+
+
Returns:
+

+
A tuple containing
    +
  1. the under-sampled kspace

  2. +
  3. absolute value of the inverse fourier of the under-sampled kspace

  4. +
+
+
+

+
+
+
+ +
+ +
+
+class monai.apps.reconstruction.transforms.array.EquispacedKspaceMask(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
+

This k-space mask transform under-samples the k-space according to an +equi-distant sampling pattern. Precisely, it selects an equi-distant +subset of columns from the input k-space data. If the k-space data has N +columns, the mask picks out:

+

1. N_low_freqs = (N * center_fraction) columns in the center corresponding +to low-frequencies

+

2. The other columns are selected with equal spacing at a proportion that +reaches the desired acceleration rate taking into consideration the number +of low frequencies. This ensures that the expected number of columns +selected is equal to (N / acceleration)

+

It is possible to use multiple center_fractions and accelerations, in +which case one possible (center_fraction, acceleration) is chosen +uniformly at random each time the EquispacedMaskFunc object is called.

+

Example

+

If accelerations = [4, 8] and center_fractions = [0.08, 0.04], +then there is a 50% probability that 4-fold acceleration with 8% +center fraction is selected and a 50% probability that 8-fold +acceleration with 4% center fraction is selected.

+
+
Modified and adopted from:

facebookresearch/fastMRI

+
+
+
+
+__call__(kspace)[source]#
+
+
Parameters:
+

kspace (Union[ndarray, Tensor]) – The input k-space data. The shape is (…,num_coils,H,W,2) +for complex 2D inputs and (…,num_coils,H,W,D) for real 3D +data. The last spatial dim is selected for sampling. For the +fastMRI multi-coil dataset, k-space has the form +(…,num_slices,num_coils,H,W) and sampling is done along W. +For a general 3D data with the shape (…,num_coils,H,W,D), +sampling is done along D.

+
+
Return type:
+

Sequence[Tensor]

+
+
Returns:
+

+
A tuple containing
    +
  1. the under-sampled kspace

  2. +
  3. absolute value of the inverse fourier of the under-sampled kspace

  4. +
+
+
+

+
+
+
+ +
+ +
+
+
+

Lazy#

+
+

ApplyPending#

+
+
+class monai.transforms.ApplyPending[source]#
+

ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure +resampling happens before the next transform. It doesn’t do anything itself, but its presence +causes the pipeline to be executed as ApplyPending doesn’t implement `LazyTrait.

+

See Compose for a detailed explanation of the lazy resampling feature.

+
+
+__call__(data)[source]#
+

Call self as a function.

+
+ +
+ +
+
+
+

Utility#

+
+

Identity#

+
+
+class monai.transforms.Identity[source]#
+

Do nothing to the data. +As the output value is same as input, it can be used as a testing tool to verify the transform chain, +Compose or transform adaptor, etc.

+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

AsChannelFirst#

+
+
+class monai.transforms.AsChannelFirst(channel_dim=-1)[source]#
+

Change the channel dimension of the image to the first dimension.

+

Most of the image transformations in monai.transforms +assume the input image is in the channel-first format, which has the shape +(num_channels, spatial_dim_1[, spatial_dim_2, …]).

+

This transform could be used to convert, for example, a channel-last image array in shape +(spatial_dim_1[, spatial_dim_2, …], num_channels) into the channel-first format, +so that the multidimensional image array can be correctly interpreted by the other transforms.

+
+
Parameters:
+

channel_dim (int) – which dimension of input image is the channel, default is the last dimension.

+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

AsChannelLast#

+
+
+class monai.transforms.AsChannelLast(channel_dim=0)[source]#
+

Change the channel dimension of the image to the last dimension.

+

Some of other 3rd party transforms assume the input image is in the channel-last format with shape +(spatial_dim_1[, spatial_dim_2, …], num_channels).

+

This transform could be used to convert, for example, a channel-first image array in shape +(num_channels, spatial_dim_1[, spatial_dim_2, …]) into the channel-last format, +so that MONAI transforms can construct a chain with other 3rd party transforms together.

+
+
Parameters:
+

channel_dim (int) – which dimension of input image is the channel, default is the first dimension.

+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

AddChannel#

+
+
+class monai.transforms.AddChannel[source]#
+

Adds a 1-length channel dimension to the input image.

+

Most of the image transformations in monai.transforms +assumes the input image is in the channel-first format, which has the shape +(num_channels, spatial_dim_1[, spatial_dim_2, …]).

+

This transform could be used, for example, to convert a (spatial_dim_1[, spatial_dim_2, …]) +spatial image into the channel-first format so that the +multidimensional image array can be correctly interpreted by the other +transforms.

+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

EnsureChannelFirst#

+
+
+class monai.transforms.EnsureChannelFirst(strict_check=True, channel_dim=None)[source]#
+

Adjust or add the channel dimension of input data to ensure channel_first shape.

+

This extracts the original_channel_dim info from provided meta_data dictionary or MetaTensor input. This value +should state which dimension is the channel dimension so that it can be moved forward, or contain “no_channel” to +state no dimension is the channel and so a 1-size first dimension is to be added.

+
+
Parameters:
+
    +
  • strict_check (bool) – whether to raise an error when the meta information is insufficient.

  • +
  • channel_dim (Union[str, int, None]) – This argument can be used to specify the original channel dimension (integer) of the input array. +It overrides the original_channel_dim from provided MetaTensor input. +If the input array doesn’t have a channel dim, this value should be 'no_channel'. +If this is set to None, this class relies on img or meta_dict to provide the channel dimension.

  • +
+
+
+
+
+__call__(img, meta_dict=None)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Tensor

+
+
+
+ +
+ +
+
+

RepeatChannel#

+
+
+class monai.transforms.RepeatChannel(repeats)[source]#
+

Repeat channel data to construct expected input shape for models. +The repeats count includes the origin data, for example: +RepeatChannel(repeats=2)([[1, 2], [3, 4]]) generates: [[1, 2], [1, 2], [3, 4], [3, 4]]

+
+
Parameters:
+

repeats (int) – the number of repetitions for each element.

+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img, assuming img is a “channel-first” array.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

SplitDim#

+
+
+class monai.transforms.SplitDim(dim=-1, keepdim=True, update_meta=True)[source]#
+

Given an image of size X along a certain dimension, return a list of length X containing +images. Useful for converting 3D images into a stack of 2D images, splitting multichannel inputs into +single channels, for example.

+

Note: torch.split/np.split is used, so the outputs are views of the input (shallow copy).

+
+
Parameters:
+
    +
  • dim (int) – dimension on which to split

  • +
  • keepdim (bool) – if True, output will have singleton in the split dimension. If False, this +dimension will be squeezed.

  • +
  • update_meta – whether to update the MetaObj in each split result.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

list[Tensor]

+
+
+
+ +
+ +
+
+

SplitChannel#

+
+
+class monai.transforms.SplitChannel(channel_dim=0)[source]#
+

Split Numpy array or PyTorch Tensor data according to the channel dim. +It can help applying different following transforms to different channels.

+

Note: torch.split/np.split is used, so the outputs are views of the input (shallow copy).

+
+
Parameters:
+

channel_dim (int) – which dimension of input image is the channel, default to 0.

+
+
+
+ +
+
+

CastToType#

+
+
+class monai.transforms.CastToType(dtype=<class 'numpy.float32'>)[source]#
+

Cast the Numpy data to specified numpy data type, or cast the PyTorch Tensor to +specified PyTorch data type.

+
+
+__call__(img, dtype=None)[source]#
+

Apply the transform to img, assuming img is a numpy array or PyTorch Tensor.

+
+
Parameters:
+

dtype (Union[dtype, type, str, None, dtype]) – convert image to this data type, default is self.dtype.

+
+
Raises:
+

TypeError – When img type is not in Union[numpy.ndarray, torch.Tensor].

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(dtype=<class 'numpy.float32'>)[source]#
+
+
Parameters:
+

dtype – convert image to this data type, default is np.float32.

+
+
+
+ +
+ +
+
+

ToTensor#

+
+
+class monai.transforms.ToTensor(dtype=None, device=None, wrap_sequence=True, track_meta=None)[source]#
+

Converts the input image to a tensor without applying any other transformations. +Input data can be PyTorch Tensor, numpy array, list, dictionary, int, float, bool, str, etc. +Will convert Tensor, Numpy array, float, int, bool to Tensor, strings and objects keep the original. +For dictionary, list or tuple, convert every item to a Tensor if applicable and wrap_sequence=False.

+
+
Parameters:
+
    +
  • dtype (Optional[dtype, None]) – target data type to when converting to Tensor.

  • +
  • device (Optional[device, None]) – target device to put the converted Tensor data.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • +
  • track_meta (Optional[bool, None]) – whether to convert to MetaTensor or regular tensor, default to None, +use the return value of get_track_meta.

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img and make it contiguous.

+
+ +
+ +
+
+

ToNumpy#

+
+
+class monai.transforms.ToNumpy(dtype=None, wrap_sequence=True)[source]#
+

Converts the input data to numpy array, can support list or tuple of numbers and PyTorch Tensor.

+
+
Parameters:
+
    +
  • dtype (Union[dtype, type, str, None]) – target data type when converting to numpy array.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • +
+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img and make it contiguous.

+
+ +
+ +
+
+

ToCupy#

+
+
+class monai.transforms.ToCupy(dtype=None, wrap_sequence=True)[source]#
+

Converts the input data to CuPy array, can support list or tuple of numbers, NumPy and PyTorch Tensor.

+
+
Parameters:
+
    +
  • dtype (Optional[dtype, None]) – data type specifier. It is inferred from the input by default. +if not None, must be an argument of numpy.dtype, for more details: +https://docs.cupy.dev/en/stable/reference/generated/cupy.array.html.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • +
+
+
+
+
+__call__(data)[source]#
+

Create a CuPy array from data and make it contiguous

+
+ +
+ +
+
+

Transpose#

+
+
+class monai.transforms.Transpose(indices)[source]#
+

Transposes the input image based on the given indices dimension ordering.

+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

SqueezeDim#

+
+
+class monai.transforms.SqueezeDim(dim=0, update_meta=True)[source]#
+

Squeeze a unitary dimension.

+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – numpy arrays with required dimension dim removed

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(dim=0, update_meta=True)[source]#
+
+
Parameters:
+
    +
  • dim (UnionType[int, None]) – dimension to be squeezed. Default = 0 +“None” works when the input is numpy array.

  • +
  • update_meta – whether to update the meta info if the input is a metatensor. Default is True.

  • +
+
+
Raises:
+

TypeError – When dim is not an Optional[int].

+
+
+
+ +
+ +
+
+

DataStats#

+
+
+class monai.transforms.DataStats(prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats')[source]#
+

Utility transform to show the statistics of data for debug or analysis. +It can be inserted into any place of a transform chain and check results of previous transforms. +It support both numpy.ndarray and torch.tensor as input data, +so it can be used in pre-processing and post-processing.

+

It gets logger from logging.getLogger(name), we can setup a logger outside first with the same name. +If the log level of logging.RootLogger is higher than INFO, will add a separate StreamHandler +log handler with INFO level and record to stdout.

+
+
+__call__(img, prefix=None, data_type=None, data_shape=None, value_range=None, data_value=None, additional_info=None)[source]#
+

Apply the transform to img, optionally take arguments similar to the class constructor.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats')[source]#
+
+
Parameters:
+
    +
  • prefix (str) – will be printed in format: “{prefix} statistics”.

  • +
  • data_type (bool) – whether to show the type of input data.

  • +
  • data_shape (bool) – whether to show the shape of input data.

  • +
  • value_range (bool) – whether to show the value range of input data.

  • +
  • data_value (bool) – whether to show the raw value of input data. +a typical example is to print some properties of Nifti image: affine, pixdim, etc.

  • +
  • additional_info (Optional[Callable, None]) – user can define callable function to extract additional info from input data.

  • +
  • name (str) – identifier of logging.logger to use, defaulting to “DataStats”.

  • +
+
+
Raises:
+

TypeError – When additional_info is not an Optional[Callable].

+
+
+
+ +
+ +
+
+

SimulateDelay#

+
+
+class monai.transforms.SimulateDelay(delay_time=0.0)[source]#
+

This is a pass through transform to be used for testing purposes. It allows +adding fake behaviors that are useful for testing purposes to simulate +how large datasets behave without needing to test on large data sets.

+

For example, simulating slow NFS data transfers, or slow network transfers +in testing by adding explicit timing delays. Testing of small test data +can lead to incomplete understanding of real world issues, and may lead +to sub-optimal design choices.

+
+
+__call__(img, delay_time=None)[source]#
+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – data remain unchanged throughout this transform.

  • +
  • delay_time (Optional[float, None]) – The minimum amount of time, in fractions of seconds, +to accomplish this delay task.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+__init__(delay_time=0.0)[source]#
+
+
Parameters:
+

delay_time (float) – The minimum amount of time, in fractions of seconds, +to accomplish this delay task.

+
+
+
+ +
+ +
+
+

Lambda#

+
+
+class monai.transforms.Lambda(func=None, inv_func=<function no_collation>, track_meta=True)[source]#
+

Apply a user-defined lambda as a transform.

+

For example:

+
image = np.ones((10, 2, 2))
+lambd = Lambda(func=lambda x: x[:4, :, :])
+print(lambd(image).shape)
+(4, 2, 2)
+
+
+
+
Parameters:
+
    +
  • func (Optional[Callable, None]) – Lambda/function to be applied.

  • +
  • inv_func (Callable) – Lambda/function of inverse operation, default to lambda x: x.

  • +
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) +as opposed to MONAI’s enhanced objects. By default, this is True.

  • +
+
+
Raises:
+

TypeError – When func is not an Optional[Callable].

+
+
+
+
+__call__(img, func=None)[source]#
+

Apply self.func to img.

+
+
Parameters:
+

func (Optional[Callable, None]) – Lambda/function to be applied. Defaults to self.func.

+
+
Raises:
+

TypeError – When func is not an Optional[Callable].

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

RandLambda#

+
+
+class monai.transforms.RandLambda(func=None, prob=1.0, inv_func=<function no_collation>, track_meta=True)[source]#
+

Randomizable version monai.transforms.Lambda, the input func may contain random logic, +or randomly execute the function based on prob.

+
+
Parameters:
+
    +
  • func (Optional[Callable, None]) – Lambda/function to be applied.

  • +
  • prob (float) – probability of executing the random function, default to 1.0, with 100% probability to execute.

  • +
  • inv_func (Callable) – Lambda/function of inverse operation, default to lambda x: x.

  • +
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) +as opposed to MONAI’s enhanced objects. By default, this is True.

  • +
+
+
+

For more details, please check monai.transforms.Lambda.

+
+
+__call__(img, func=None)[source]#
+

Apply self.func to img.

+
+
Parameters:
+

func (Optional[Callable, None]) – Lambda/function to be applied. Defaults to self.func.

+
+
Raises:
+

TypeError – When func is not an Optional[Callable].

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

RemoveRepeatedChannel#

+
+
+class monai.transforms.RemoveRepeatedChannel(repeats)[source]#
+

RemoveRepeatedChannel data to undo RepeatChannel +The repeats count specifies the deletion of the origin data, for example: +RemoveRepeatedChannel(repeats=2)([[1, 2], [1, 2], [3, 4], [3, 4]]) generates: [[1, 2], [3, 4]]

+
+
Parameters:
+

repeats (int) – the number of repetitions to be deleted for each element.

+
+
+
+
+__call__(img)[source]#
+

Apply the transform to img, assuming img is a “channel-first” array.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

LabelToMask#

+
+
+class monai.transforms.LabelToMask(select_labels, merge_channels=False)[source]#
+

Convert labels to mask for other tasks. A typical usage is to convert segmentation labels +to mask data to pre-process images and then feed the images into classification network. +It can support single channel labels or One-Hot labels with specified select_labels. +For example, users can select label value = [2, 3] to construct mask data, or select the +second and the third channels of labels to construct mask data. +The output mask data can be a multiple channels binary data or a single channel binary +data that merges all the channels.

+
+
Parameters:
+
    +
  • select_labels (UnionType[Sequence[int], int]) – labels to generate mask from. for 1 channel label, the select_labels +is the expected label values, like: [1, 2, 3]. for One-Hot format label, the +select_labels is the expected channel indices.

  • +
  • merge_channels (bool) – whether to use np.any() to merge the result on channel dim. if yes, +will return a single channel mask with binary data.

  • +
+
+
+
+
+__call__(img, select_labels=None, merge_channels=False)[source]#
+
+
Parameters:
+
    +
  • select_labels (Union[Sequence[int], int, None]) – labels to generate mask from. for 1 channel label, the select_labels +is the expected label values, like: [1, 2, 3]. for One-Hot format label, the +select_labels is the expected channel indices.

  • +
  • merge_channels (bool) – whether to use np.any() to merge the result on channel dim. if yes, +will return a single channel mask with binary data.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

FgBgToIndices#

+
+
+class monai.transforms.FgBgToIndices(image_threshold=0.0, output_shape=None)[source]#
+

Compute foreground and background of the input label data, return the indices. +If no output_shape specified, output data will be 1 dim indices after flattening. +This transform can help pre-compute foreground and background regions for other transforms. +A typical usage is to randomly select foreground and background to crop. +The main logic is based on monai.transforms.utils.map_binary_to_indices.

+
+
Parameters:
+
    +
  • image_threshold (float) – if enabled image at runtime, use image > image_threshold to +determine the valid image content area and select background only in this area.

  • +
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • +
+
+
+
+
+__call__(label, image=None, output_shape=None)[source]#
+
+
Parameters:
+
    +
  • label (Union[ndarray, Tensor]) – input data to compute foreground and background indices.

  • +
  • image (Union[ndarray, Tensor, None]) – if image is not None, use label = 0 & image > image_threshold +to define background. so the output items will not map to all the voxels in the label.

  • +
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if None, use self.output_shape instead.

  • +
+
+
Return type:
+

tuple[Union[ndarray, Tensor], Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+

ClassesToIndices#

+
+
+class monai.transforms.ClassesToIndices(num_classes=None, image_threshold=0.0, output_shape=None, max_samples_per_class=None)[source]#
+
+
+__call__(label, image=None, output_shape=None)[source]#
+
+
Parameters:
+
    +
  • label (Union[ndarray, Tensor]) – input data to compute the indices of every class.

  • +
  • image (Union[ndarray, Tensor, None]) – if image is not None, use image > image_threshold to define valid region, and only select +the indices within the valid region.

  • +
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if None, use self.output_shape instead.

  • +
+
+
Return type:
+

list[Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(num_classes=None, image_threshold=0.0, output_shape=None, max_samples_per_class=None)[source]#
+

Compute indices of every class of the input label data, return a list of indices. +If no output_shape specified, output data will be 1 dim indices after flattening. +This transform can help pre-compute indices of the class regions for other transforms. +A typical usage is to randomly select indices of classes to crop. +The main logic is based on monai.transforms.utils.map_classes_to_indices.

+
+
Parameters:
+
    +
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • +
  • image_threshold (float) – if enabled image at runtime, use image > image_threshold to +determine the valid image content area and select only the indices of classes in this area.

  • +
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • +
  • max_samples_per_class (Optional[int, None]) – maximum length of indices to sample in each class to reduce memory consumption. +Default is None, no subsampling.

  • +
+
+
+
+ +
+ +
+
+

ConvertToMultiChannelBasedOnBratsClasses#

+
+
+class monai.transforms.ConvertToMultiChannelBasedOnBratsClasses[source]#
+

Convert labels to multi channels based on brats18 classes: +label 1 is the necrotic and non-enhancing tumor core +label 2 is the peritumoral edema +label 4 is the GD-enhancing tumor +The possible classes are TC (Tumor core), WT (Whole tumor) +and ET (Enhancing tumor).

+
+
+__call__(img)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

AddExtremePointsChannel#

+
+
+class monai.transforms.AddExtremePointsChannel(background=0, pert=0.0)[source]#
+

Add extreme points of label to the image as a new channel. This transform generates extreme +point from label and applies a gaussian filter. The pixel values in points image are rescaled +to range [rescale_min, rescale_max] and added as a new channel to input image. The algorithm is +described in Roth et al., Going to Extremes: Weakly Supervised Medical Image Segmentation +https://arxiv.org/abs/2009.11988.

+

This transform only supports single channel labels (1, spatial_dim1, [spatial_dim2, …]). The +background index is ignored when calculating extreme points.

+
+
Parameters:
+
    +
  • background (int) – Class index of background label, defaults to 0.

  • +
  • pert (float) – Random perturbation amount to add to the points, defaults to 0.0.

  • +
+
+
Raises:
+
    +
  • ValueError – When no label image provided.

  • +
  • ValueError – When label image is not single channel.

  • +
+
+
+
+
+__call__(img, label=None, sigma=3.0, rescale_min=-1.0, rescale_max=1.0)[source]#
+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – the image that we want to add new channel to.

  • +
  • label (Union[ndarray, Tensor, None]) – label image to get extreme points from. Shape must be +(1, spatial_dim1, [, spatial_dim2, …]). Doesn’t support one-hot labels.

  • +
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

  • +
  • rescale_min (float) – minimum value of output data.

  • +
  • rescale_max (float) – maximum value of output data.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+randomize(label)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

TorchVision#

+
+
+class monai.transforms.TorchVision(name, *args, **kwargs)[source]#
+

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.

+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – PyTorch Tensor data for the TorchVision transform.

+
+
+
+ +
+
+__init__(name, *args, **kwargs)[source]#
+
+
Parameters:
+
    +
  • name (str) – The transform name in TorchVision package.

  • +
  • args – parameters for the TorchVision transform.

  • +
  • kwargs – parameters for the TorchVision transform.

  • +
+
+
+
+ +
+ +
+
+

MapLabelValue#

+
+
+class monai.transforms.MapLabelValue(orig_labels, target_labels, dtype=<class 'numpy.float32'>)[source]#
+

Utility to map label values to another set of values. +For example, map [3, 2, 1] to [0, 1, 2], [1, 2, 3] -> [0.5, 1.5, 2.5], [“label3”, “label2”, “label1”] -> [0, 1, 2], +[3.5, 2.5, 1.5] -> [“label0”, “label1”, “label2”], etc. +The label data must be numpy array or array-like data and the output data will be numpy array.

+
+
+__call__(img)[source]#
+

Call self as a function.

+
+ +
+
+__init__(orig_labels, target_labels, dtype=<class 'numpy.float32'>)[source]#
+
+
Parameters:
+
    +
  • orig_labels (Sequence) – original labels that map to others.

  • +
  • target_labels (Sequence) – expected label values, 1: 1 map to the orig_labels.

  • +
  • dtype (Union[dtype, type, str, None]) – convert the output data to dtype, default to float32.

  • +
+
+
+
+ +
+ +
+
+

EnsureType#

+
+
+class monai.transforms.EnsureType(data_type='tensor', dtype=None, device=None, wrap_sequence=True, track_meta=None)[source]#
+

Ensure the input data to be a PyTorch Tensor or numpy array, support: numpy array, PyTorch Tensor, +float, int, bool, string and object keep the original. +If passing a dictionary, list or tuple, still return dictionary, list or tuple will recursively convert +every item to the expected data type if wrap_sequence=False.

+
+
Parameters:
+
    +
  • data_type (str) – target data type to convert, should be “tensor” or “numpy”.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – target data content type to convert, for example: np.float32, torch.float, etc.

  • +
  • device (Optional[device, None]) – for Tensor data type, specify the target device.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • +
  • track_meta (Optional[bool, None]) – if True convert to MetaTensor, otherwise to Pytorch Tensor, +if None behave according to return value of py:func:monai.data.meta_obj.get_track_meta.

  • +
+
+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Union[ndarray, Tensor]) – input data can be PyTorch Tensor, numpy array, list, dictionary, int, float, bool, str, etc. +will ensure Tensor, Numpy array, float, int, bool as Tensors or numpy arrays, strings and +objects keep the original. for dictionary, list or tuple, ensure every item as expected type +if applicable and wrap_sequence=False.

+
+
+
+ +
+ +
+
+

IntensityStats#

+
+
+class monai.transforms.IntensityStats(ops, key_prefix, channel_wise=False)[source]#
+

Compute statistics for the intensity values of input image and store into the metadata dictionary. +For example: if ops=[lambda x: np.mean(x), “max”] and key_prefix=”orig”, may generate below stats: +{“orig_custom_0”: 1.5, “orig_max”: 3.0}.

+
+
Parameters:
+
    +
  • ops (Sequence[Union[str, Callable]]) – expected operations to compute statistics for the intensity. +if a string, will map to the predefined operations, supported: [“mean”, “median”, “max”, “min”, “std”] +mapping to np.nanmean, np.nanmedian, np.nanmax, np.nanmin, np.nanstd. +if a callable function, will execute the function on input image.

  • +
  • key_prefix (str) – the prefix to combine with ops name to generate the key to store the results in the +metadata dictionary. if some ops are callable functions, will use “{key_prefix}_custom_{index}” +as the key, where index counts from 0.

  • +
  • channel_wise (bool) – whether to compute statistics for every channel of input image separately. +if True, return a list of values for every operation, default to False.

  • +
+
+
+
+
+__call__(img, meta_data=None, mask=None)[source]#
+

Compute statistics for the intensity of input image.

+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – input image to compute intensity stats.

  • +
  • meta_data (Optional[dict, None]) – metadata dictionary to store the statistics data, if None, will create an empty dictionary.

  • +
  • mask (Optional[ndarray, None]) – if not None, mask the image to extract only the interested area to compute statistics. +mask must have the same shape as input img.

  • +
+
+
Return type:
+

tuple[Union[ndarray, Tensor], dict]

+
+
+
+ +
+ +
+
+

ToDevice#

+
+
+class monai.transforms.ToDevice(device, **kwargs)[source]#
+

Move PyTorch Tensor to the specified device. +It can help cache data into GPU and execute following logic on GPU directly.

+
+

Note

+

If moving data to GPU device in the multi-processing workers of DataLoader, may got below CUDA error: +“RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, +you must use the ‘spawn’ start method.” +So usually suggest to set num_workers=0 in the DataLoader or ThreadDataLoader.

+
+
+
+__call__(img)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+
+__init__(device, **kwargs)[source]#
+
+
Parameters:
+
+
+
+
+ +
+ +
+
+

CuCIM#

+
+
+class monai.transforms.CuCIM(name, *args, **kwargs)[source]#
+

Wrap a non-randomized cuCIM transform, defined based on the transform name and args. +For randomized transforms use monai.transforms.RandCuCIM.

+
+
Parameters:
+
    +
  • name (str) – 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.

+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data – a CuPy array (cupy.ndarray) for the cuCIM transform

+
+
Returns:
+

cupy.ndarray

+
+
+
+ +
+ +
+
+

RandCuCIM#

+
+
+class monai.transforms.RandCuCIM(name, *args, **kwargs)[source]#
+

Wrap a randomized cuCIM transform, defined based on the transform name and args +For deterministic non-randomized transforms use monai.transforms.CuCIM.

+
+
Parameters:
+
    +
  • name (str) – 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.

  • +
  • If the random factor of the underlying cuCIM transform is not derived from self.R, +the results may not be deterministic. See Also: monai.transforms.Randomizable.

  • +
+
+
+ +
+
+

AddCoordinateChannels#

+
+
+class monai.transforms.AddCoordinateChannels(spatial_dims)[source]#
+

Appends additional channels encoding coordinates of the input. Useful when e.g. training using patch-based sampling, +to allow feeding of the patch’s location into the network.

+

This can be seen as a input-only version of CoordConv:

+

Liu, R. et al. An Intriguing Failing of Convolutional Neural Networks and the CoordConv Solution, NeurIPS 2018.

+
+
Parameters:
+

spatial_dims (Sequence[int]) – the spatial dimensions that are to have their coordinates encoded in a channel and +appended to the input image. E.g., (0, 1, 2) represents H, W, D dims and append three channels +to the input image, encoding the coordinates of the input’s three spatial dimensions.

+
+
+
+

Deprecated since version 0.8.0: spatial_channels is deprecated, use spatial_dims instead.

+
+
+
+__call__(img)[source]#
+
+
Parameters:
+

img (Union[ndarray, Tensor]) – data to be transformed, assuming img is channel first.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+

ImageFilter#

+
+
+class monai.transforms.ImageFilter(filter, filter_size=None, **kwargs)[source]#
+

Applies a convolution filter to the input image.

+
+
Parameters:
+
    +
  • filter (Union[str, ndarray, Tensor, Module]) – A string specifying the filter, a custom filter as torch.Tenor or np.ndarray or a nn.Module. +Available options for string are: mean, laplace, elliptical, sobel, sharpen, median, gauss +See below for short explanations on every filter.

  • +
  • filter_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic filter. +Computational complexity scales to the power of 2 (2D filter) or 3 (3D filter), which +should be considered when choosing filter size.

  • +
  • kwargs – Additional arguments passed to filter function, required by sobel and gauss. +See below for details.

  • +
+
+
Raises:
+
    +
  • ValueError – When filter_size is not an uneven integer

  • +
  • ValueError – When filter is an array and ndim is not in [1,2,3]

  • +
  • ValueError – When filter is an array and any dimension has an even shape

  • +
  • NotImplementedError – When filter is a string and not in self.supported_filters

  • +
  • KeyError – When necessary kwargs are not passed to a filter that requires additional arguments.

  • +
+
+
+

Mean Filtering: filter='mean'

+

Mean filtering can smooth edges and remove aliasing artifacts in an segmentation image. +See also py:func:monai.networks.layers.simplelayers.MeanFilter +Example 2D filter (5 x 5):

+
[[1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1]]
+
+
+

If smoothing labels with this filter, ensure they are in one-hot format.

+

Outline Detection: filter='laplace'

+

Laplacian filtering for outline detection in images. Can be used to transform labels to contours. +See also py:func:monai.networks.layers.simplelayers.LaplaceFilter

+

Example 2D filter (5x5):

+
[[-1., -1., -1., -1., -1.],
+ [-1., -1., -1., -1., -1.],
+ [-1., -1., 24., -1., -1.],
+ [-1., -1., -1., -1., -1.],
+ [-1., -1., -1., -1., -1.]]
+
+
+

Dilation: filter='elliptical'

+

An elliptical filter can be used to dilate labels or label-contours. +Example 2D filter (5x5):

+
[[0., 0., 1., 0., 0.],
+ [1., 1., 1., 1., 1.],
+ [1., 1., 1., 1., 1.],
+ [1., 1., 1., 1., 1.],
+ [0., 0., 1., 0., 0.]]
+
+
+

Edge Detection: filter='sobel'

+

This filter allows for additional arguments passed as kwargs during initialization. +See also py:func:monai.transforms.post.SobelGradients

+

kwargs

+
    +
  • spatial_axes: the axes that define the direction of the gradient to be calculated. +It calculates the gradient along each of the provide axis. +By default it calculate the gradient for all spatial axes.

  • +
  • normalize_kernels: if normalize the Sobel kernel to provide proper gradients. Defaults to True.

  • +
  • normalize_gradients: if normalize the output gradient to 0 and 1. Defaults to False.

  • +
  • padding_mode: the padding mode of the image when convolving with Sobel kernels. Defaults to "reflect". +Acceptable values are 'zeros', 'reflect', 'replicate' or 'circular'. +See torch.nn.Conv1d() for more information.

  • +
  • dtype: kernel data type (torch.dtype). Defaults to torch.float32.

  • +
+

Sharpening: filter='sharpen'

+

Sharpen an image with a 2D or 3D filter. +Example 2D filter (5x5):

+
[[ 0.,  0., -1.,  0.,  0.],
+ [-1., -1., -1., -1., -1.],
+ [-1., -1., 17., -1., -1.],
+ [-1., -1., -1., -1., -1.],
+ [ 0.,  0., -1.,  0.,  0.]]
+
+
+

Gaussian Smooth: filter='gauss'

+

Blur/smooth an image with 2D or 3D gaussian filter. +This filter requires additional arguments passed as kwargs during initialization. +See also py:func:monai.networks.layers.simplelayers.GaussianFilter

+

kwargs

+
    +
  • sigma: std. could be a single value, or spatial_dims number of values.

  • +
  • truncated: spreads how many stds.

  • +
  • approx: discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”.

  • +
+

Median Filter: filter='median'

+

Blur an image with 2D or 3D median filter to remove noise. +Useful in image preprocessing to improve results of later processing. +See also py:func:monai.networks.layers.simplelayers.MedianFilter

+

Savitzky Golay Filter: filter = 'savitzky_golay'

+

Convolve a Tensor along a particular axis with a Savitzky-Golay kernel. +This filter requires additional arguments passed as kwargs during initialization. +See also py:func:monai.networks.layers.simplelayers.SavitzkyGolayFilter

+

kwargs

+
    +
  • order: Order of the polynomial to fit to each window, must be less than window_length.

  • +
  • axis: (optional): Axis along which to apply the filter kernel. Default 2 (first spatial dimension).

  • +
  • mode: (string, optional): padding mode passed to convolution class. 'zeros', 'reflect', 'replicate' or +'circular'. Default: 'zeros'. See torch.nn.Conv1d() for more information.

  • +
+
+
+__call__(img, meta_dict=None)[source]#
+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – torch tensor data to apply filter to with shape: [channels, height, width[, depth]]

  • +
  • meta_dict (Optional[dict, None]) – An optional dictionary with metadata

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

A MetaTensor with the same shape as img and identical metadata

+
+
+
+ +
+ +
+
+

RandImageFilter#

+
+
+class monai.transforms.RandImageFilter(filter, filter_size=None, prob=0.1, **kwargs)[source]#
+

Randomly apply a convolutional filter to the input data.

+
+
Parameters:
+
    +
  • filter (Union[str, ndarray, Tensor]) – A string specifying the filter or a custom filter as torch.Tenor or np.ndarray. +Available options are: mean, laplace, elliptical, gaussian` +See below for short explanations on every filter.

  • +
  • filter_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic filter. +Computational complexity scales to the power of 2 (2D filter) or 3 (3D filter), which +should be considered when choosing filter size.

  • +
  • prob (float) – Probability the transform is applied to the data

  • +
+
+
+
+
+__call__(img, meta_dict=None)[source]#
+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – torch tensor data to apply filter to with shape: [channels, height, width[, depth]]

  • +
  • meta_dict (Optional[Mapping, None]) – An optional dictionary with metadata

  • +
  • kwargs – optional arguments required by specific filters. E.g. sigma`if filter is `gauss. +see py:func:monai.transforms.utility.array.ImageFilter for more details

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

A MetaTensor with the same shape as img and identical metadata

+
+
+
+ +
+ +
+
+
+
+

Dictionary Transforms#

+
+

Crop and Pad (Dict)#

+
+

Padd#

+
+
+class monai.transforms.Padd(keys, padder, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Pad.

+
+
+__call__(data, lazy=None)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, padder, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • padder (Pad) – pad transform for the input image.

  • +
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, MetaTensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

SpatialPadd#

+example of SpatialPadd +
+
+class monai.transforms.SpatialPadd(keys, spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.SpatialPad. +Performs padding to the data, symmetric for all sides or all on one side for each dimension.

+
+
+__init__(keys, spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of output data after padding, if a dimension of the input +data size is larger than the pad size, will not pad that dimension. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [30, 30, 30] and spatial_size=[32, 25, -1], +the spatial size of output data will be [32, 30, 30].

  • +
  • method (str) – {"symmetric", "end"} +Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • +
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+ +
+ +
+
+

BorderPadd#

+example of BorderPadd +
+
+class monai.transforms.BorderPadd(keys, spatial_border, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+

Pad the input data by adding specified borders to every dimension. +Dictionary-based wrapper of monai.transforms.BorderPad.

+
+
+__init__(keys, spatial_border, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • spatial_border (UnionType[Sequence[int], int]) –

    specified size for every spatial border. it can be 3 shapes:

    +
      +
    • single int number, pad all the borders with the same size.

    • +
    • length equals the length of image shape, pad every spatial dimension separately. +for example, image shape(CHW) is [1, 4, 4], spatial_border is [2, 1], +pad every border of H dim with 2, pad every border of W dim with 1, result shape is [1, 8, 6].

    • +
    • length equals 2 x (length of image shape), pad every border of every dimension separately. +for example, image shape(CHW) is [1, 4, 4], spatial_border is [1, 2, 3, 4], pad top of H dim with 1, +pad bottom of H dim with 2, pad left of W dim with 3, pad right of W dim with 4. +the result shape is [1, 7, 11].

    • +
    +

  • +
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+ +
+ +
+
+

DivisiblePadd#

+example of DivisiblePadd +
+
+class monai.transforms.DivisiblePadd(keys, k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+

Pad the input data, so that the spatial sizes are divisible by k. +Dictionary-based wrapper of monai.transforms.DivisiblePad.

+
+
+__init__(keys, k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • k (UnionType[Sequence[int], int]) – the target k for each spatial dimension. +if k is negative or 0, the original size is preserved. +if k is an int, the same k be applied to all the input spatial dimensions.

  • +
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • method (str) – {"symmetric", "end"} +Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+

See also monai.transforms.SpatialPad

+
+ +
+ +
+
+

Cropd#

+
+
+class monai.transforms.Cropd(keys, cropper, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of abstract class monai.transforms.Crop.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • cropper (Crop) – crop transform for the input image.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, MetaTensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandCropd#

+
+
+class monai.transforms.RandCropd(keys, cropper, allow_missing_keys=False, lazy=False)[source]#
+

Base class for random crop transform.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • cropper (Crop) – random crop transform for the input image.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+randomize(img_size)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandCropd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

SpatialCropd#

+example of SpatialCropd +
+
+class monai.transforms.SpatialCropd(keys, roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SpatialCrop. +General purpose cropper to produce sub-volume region of interest (ROI). +If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. +So the cropped result may be smaller than the expected ROI, and the cropped results of several images may +not have exactly the same shape. +It can support to crop ND spatial (channel-first) data.

+
+
The cropped region can be parameterised in various ways:
    +
  • a list of slices for each spatial dimension (allows for use of -ve indexing and None)

  • +
  • a spatial center and size

  • +
  • the start and end coordinates of the ROI

  • +
+
+
+
+
+__init__(keys, roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • roi_center (Optional[Sequence[int], None]) – voxel coordinates for center of the crop ROI.

  • +
  • roi_size (Optional[Sequence[int], None]) – size of the crop ROI, if a dimension of ROI size is larger than image size, +will not crop that dimension of the image.

  • +
  • roi_start (Optional[Sequence[int], None]) – voxel coordinates for start of the crop ROI.

  • +
  • roi_end (Optional[Sequence[int], None]) – voxel coordinates for end of the crop ROI, if a coordinate is out of image, +use the end coordinate of image.

  • +
  • roi_slices (Optional[Sequence[slice], None]) – list of slices for each of the spatial dimensions.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

CenterSpatialCropd#

+example of CenterSpatialCropd +
+
+class monai.transforms.CenterSpatialCropd(keys, roi_size, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.CenterSpatialCrop. +If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. +So the cropped result may be smaller than the expected ROI, and the cropped results of several images may +not have exactly the same shape.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • roi_size (UnionType[Sequence[int], int]) – the size of the crop region e.g. [224,224,128] +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+

RandSpatialCropd#

+example of RandSpatialCropd +
+
+class monai.transforms.RandSpatialCropd(keys, roi_size, max_roi_size=None, random_center=True, random_size=True, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandSpatialCrop. +Crop image with random size or specific size ROI. It can crop at a random position as +center or at the image center. And allows to set the minimum and maximum size to limit the randomly +generated ROI. Suppose all the expected fields specified by keys have same shape.

+

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped +results of several images may not have exactly the same shape.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. +if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size +can specify the max crop region size. if None, defaults to the input image size. +if its components have non-positive values, the corresponding size of input image will be used.

  • +
  • random_center (bool) – crop at random position as center or the image center.

  • +
  • random_size (bool) – crop with random size or specific size ROI. +if True, the actual size is sampled from: +randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+

RandSpatialCropSamplesd#

+example of RandSpatialCropSamplesd +
+
+class monai.transforms.RandSpatialCropSamplesd(keys, roi_size, num_samples, max_roi_size=None, random_center=True, random_size=True, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandSpatialCropSamples. +Crop image with random size or specific size ROI to generate a list of N samples. +It can crop at a random position as center or at the image center. And allows to set +the minimum size to limit the randomly generated ROI. Suppose all the expected fields +specified by keys have same shape, and add patch_index to the corresponding metadata. +It will return a list of dictionaries for all the cropped images.

+

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped +results of several images may not have exactly the same shape.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. +if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +If its components have non-positive values, the corresponding size of input image will be used. +for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • num_samples (int) – number of samples (crop regions) to take in the returned list.

  • +
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size +can specify the max crop region size. if None, defaults to the input image size. +if its components have non-positive values, the corresponding size of input image will be used.

  • +
  • random_center (bool) – crop at random position as center or the image center.

  • +
  • random_size (bool) – crop with random size or specific size ROI. +The actual size is sampled from randint(roi_size, img_size).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
Raises:
+

ValueError – When num_samples is nonpositive.

+
+
+
+
+__call__(data, lazy=None)[source]#
+

Call self as a function.

+
+
Return type:
+

list[dict[Hashable, Tensor]]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

CropForegroundd#

+example of CropForegroundd +
+
+class monai.transforms.CropForegroundd(keys, source_key, select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, k_divisible=1, mode=PytorchPadMode.CONSTANT, start_coord_key='foreground_start_coord', end_coord_key='foreground_end_coord', allow_missing_keys=False, lazy=False, **pad_kwargs)[source]#
+

Dictionary-based version monai.transforms.CropForeground. +Crop only the foreground object of the expected images. +The typical usage is to help training and evaluation if the valid part is small in the whole medical image. +The valid part can be determined by any field in the data with source_key, for example: +- Select values > 0 in image field as the foreground and crop on all fields specified by keys. +- Select label = 3 in label field as the foreground to crop on all fields specified by keys. +- Select label > 0 in the third channel of a One-Hot label field as the foreground to crop all keys fields. +Users can define arbitrary function to select expected foreground from the whole source image or specified +channels. And it can also add margin to every dim of the bounding box of foreground object.

+
+
+__call__(data, lazy=None)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, source_key, select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, k_divisible=1, mode=PytorchPadMode.CONSTANT, start_coord_key='foreground_start_coord', end_coord_key='foreground_end_coord', allow_missing_keys=False, lazy=False, **pad_kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • source_key (str) – data source to generate the bounding box of foreground, can be image or label, etc.

  • +
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • +
  • channel_indices (Union[Iterable[int], int, None]) – if defined, select foreground only on the specified channels +of image. if None, select foreground on the whole image.

  • +
  • margin (UnionType[Sequence[int], int]) – add margin value to spatial dims of the bounding box, if only 1 value provided, use it for all dims.

  • +
  • allow_smaller (bool) – when computing box size with margin, whether allow the image size to be smaller +than box size, default to True. if the margined size is larger than image size, will pad with +specified mode.

  • +
  • k_divisible (UnionType[Sequence[int], int]) – make each spatial dimension to be divisible by k, default to 1. +if k_divisible is an int, the same k be applied to all the input spatial dimensions.

  • +
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +it also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • start_coord_key (str) – key to record the start coordinate of spatial bounding box for foreground.

  • +
  • end_coord_key (str) – key to record the end coordinate of spatial bounding box for foreground.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+ +
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandWeightedCropd#

+example of RandWeightedCropd +
+
+class monai.transforms.RandWeightedCropd(keys, w_key, spatial_size, num_samples=1, allow_missing_keys=False, lazy=False)[source]#
+

Samples a list of num_samples image patches according to the provided weight_map.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • w_key (str) – key for the weight map. The corresponding value will be used as the sampling weights, +it should be a single-channel array in size, for example, (1, spatial_dim_0, spatial_dim_1, …)

  • +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the image patch e.g. [224, 224, 128]. +If its components have non-positive values, the corresponding size of img will be used.

  • +
  • num_samples (int) – number of samples (image patches) to take in the returned list.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+ +
+
+__call__(data, lazy=None)[source]#
+

Call self as a function.

+
+
Return type:
+

list[dict[Hashable, Tensor]]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(weight_map)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandWeightedCropd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandCropByPosNegLabeld#

+example of RandCropByPosNegLabeld +
+
+class monai.transforms.RandCropByPosNegLabeld(keys, label_key, spatial_size, pos=1.0, neg=1.0, num_samples=1, image_key=None, image_threshold=0.0, fg_indices_key=None, bg_indices_key=None, allow_smaller=False, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandCropByPosNegLabel. +Crop random fixed sized regions with the center being a foreground or background voxel +based on the Pos Neg Ratio. +Suppose all the expected fields specified by keys have same shape, +and add patch_index to the corresponding metadata. +And will return a list of dictionaries for all the cropped images.

+

If a dimension of the expected spatial size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than the expected size, +and the cropped results of several images may not have exactly the same shape. +And if the crop ROI is partly out of the image, will automatically adjust the crop center +to ensure the valid crop ROI.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • label_key (str) – name of key for label image, this will be used for finding foreground/background.

  • +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +if its components have non-positive values, the corresponding size of data[label_key] will be used. +for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • pos (float) – used with neg together to calculate the ratio pos / (pos + neg) for the probability +to pick a foreground voxel as a center rather than a background voxel.

  • +
  • neg (float) – used with pos together to calculate the ratio pos / (pos + neg) for the probability +to pick a foreground voxel as a center rather than a background voxel.

  • +
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • +
  • image_key (Optional[str, None]) – if image_key is not None, use label == 0 & image > image_threshold to select +the negative sample(background) center. so the crop center will only exist on valid image area.

  • +
  • image_threshold (float) – if enabled image_key, use image > image_threshold to determine +the valid image content area.

  • +
  • fg_indices_key (Optional[str, None]) – if provided pre-computed foreground indices of label, will ignore above image_key and +image_threshold, and randomly select crop centers based on them, need to provide fg_indices_key +and bg_indices_key together, expect to be 1 dim array of spatial indices after flattening. +a typical usage is to call FgBgToIndicesd transform first and cache the results.

  • +
  • bg_indices_key (Optional[str, None]) – if provided pre-computed background indices of label, will ignore above image_key and +image_threshold, and randomly select crop centers based on them, need to provide fg_indices_key +and bg_indices_key together, expect to be 1 dim array of spatial indices after flattening. +a typical usage is to call FgBgToIndicesd transform first and cache the results.

  • +
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than +the requested ROI in any dimension. If True, any smaller dimensions will be set to +match the cropped size (i.e., no cropping in that dimension).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
Raises:
+
    +
  • ValueError – When pos or neg are negative.

  • +
  • ValueError – When pos=0 and neg=0. Incompatible values.

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+

Call self as a function.

+
+
Return type:
+

list[dict[Hashable, Tensor]]

+
+
+
+ +
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(label=None, fg_indices=None, bg_indices=None, image=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandCropByPosNegLabeld

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandCropByLabelClassesd#

+example of RandCropByLabelClassesd +
+
+class monai.transforms.RandCropByLabelClassesd(keys, label_key, spatial_size, ratios=None, num_classes=None, num_samples=1, image_key=None, image_threshold=0.0, indices_key=None, allow_smaller=False, allow_missing_keys=False, warn=True, max_samples_per_class=None, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandCropByLabelClasses. +Crop random fixed sized regions with the center being a class based on the specified ratios of every class. +The label data can be One-Hot format array or Argmax data. And will return a list of arrays for all the +cropped images. For example, crop two (3 x 3) arrays from (5 x 5) array with ratios=[1, 2, 3, 1]:

+
cropper = RandCropByLabelClassesd(
+    keys=["image", "label"],
+    label_key="label",
+    spatial_size=[3, 3],
+    ratios=[1, 2, 3, 1],
+    num_classes=4,
+    num_samples=2,
+)
+data = {
+    "image": np.array([
+        [[0.0, 0.3, 0.4, 0.2, 0.0],
+        [0.0, 0.1, 0.2, 0.1, 0.4],
+        [0.0, 0.3, 0.5, 0.2, 0.0],
+        [0.1, 0.2, 0.1, 0.1, 0.0],
+        [0.0, 0.1, 0.2, 0.1, 0.0]]
+    ]),
+    "label": np.array([
+        [[0, 0, 0, 0, 0],
+        [0, 1, 2, 1, 0],
+        [0, 1, 3, 0, 0],
+        [0, 0, 0, 0, 0],
+        [0, 0, 0, 0, 0]]
+    ]),
+}
+result = cropper(data)
+
+The 2 randomly cropped samples of `label` can be:
+[[0, 1, 2],     [[0, 0, 0],
+ [0, 1, 3],      [1, 2, 1],
+ [0, 0, 0]]      [1, 3, 0]]
+
+
+

If a dimension of the expected spatial size is larger than the input image size, +will not crop that dimension. So the cropped result may be smaller than expected size, and the cropped +results of several images may not have exactly same shape. +And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the +valid crop ROI.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • label_key (str) – name of key for label image, this will be used for finding indices of every class.

  • +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. +if a dimension of ROI size is larger than image size, will not crop that dimension of the image. +if its components have non-positive values, the corresponding size of label will be used. +for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], +the spatial size of output data will be [32, 40, 40].

  • +
  • ratios (Optional[list[UnionType[float, int]], None]) – specified ratios of every class in the label to generate crop centers, including background class. +if None, every class will have the same ratio to generate crop centers.

  • +
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • +
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • +
  • image_key (Optional[str, None]) – if image_key is not None, only return the indices of every class that are within the valid +region of the image (image > image_threshold).

  • +
  • image_threshold (float) – if enabled image_key, use image > image_threshold to +determine the valid image content area and select class indices only in this area.

  • +
  • indices_key (Optional[str, None]) – if provided pre-computed indices of every class, will ignore above image and +image_threshold, and randomly select crop centers based on them, expect to be 1 dim array +of spatial indices after flattening. a typical usage is to call ClassesToIndices transform first +and cache the results for better performance.

  • +
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than +the requested ROI in any dimension. If True, any smaller dimensions will remain +unchanged.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • warn (bool) – if True prints a warning if a class is not present in the label.

  • +
  • max_samples_per_class (Optional[int, None]) – maximum length of indices in each class to reduce memory consumption. +Default is None, no subsampling.

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+

Call self as a function.

+
+
Return type:
+

list[dict[Hashable, Tensor]]

+
+
+
+ +
+
+property checks_data#
+

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its +operation. A transform that checks data requires that all of the pending operations on its input +transforms are up to date before it is executed, but it can still execute lazily by adding pending +operations to the input tensors. +:returns: True if the transform checks data and False if it does not

+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+randomize(label, indices=None, image=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandCropByLabelClassesd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

ResizeWithPadOrCropd#

+example of ResizeWithPadOrCropd +
+
+class monai.transforms.ResizeWithPadOrCropd(keys, spatial_size, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, method=Method.SYMMETRIC, lazy=False, **pad_kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.ResizeWithPadOrCrop.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of output data after padding or crop. +If has non-positive values, the corresponding size of input image will be used (no padding).

  • +
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "constant". +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • method (str) – {"symmetric", "end"} +Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+ +
+
+

BoundingRectd#

+
+
+class monai.transforms.BoundingRectd(keys, bbox_key_postfix='bbox', select_fn=<function is_positive>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.BoundingRect.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • bbox_key_postfix (str) – the output bounding box coordinates will be +written to the value of {key}_{bbox_key_postfix}.

  • +
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

See also: monai.transforms.utils.generate_spatial_bounding_box.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+

RandScaleCropd#

+example of RandScaleCropd +
+
+class monai.transforms.RandScaleCropd(keys, roi_scale, max_roi_scale=None, random_center=True, random_size=True, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandScaleCrop. +Crop image with random size or specific size ROI. +It can crop at a random position as center or at the image center. +And allows to set the minimum and maximum scale of image size to limit the randomly generated ROI. +Suppose all the expected fields specified by keys have same shape.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • roi_scale (UnionType[Sequence[float], float]) – if random_size is True, it specifies the minimum crop size: roi_scale * image spatial size. +if random_size is False, it specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5]. +If its components have non-positive values, will use 1.0 instead, which means the input image size.

  • +
  • max_roi_scale (Union[Sequence[float], float, None]) – if random_size is True and roi_scale specifies the min crop region size, max_roi_scale +can specify the max crop region size: max_roi_scale * image spatial size. +if None, defaults to the input image size. if its components have non-positive values, +will use 1.0 instead, which means the input image size.

  • +
  • random_center (bool) – crop at random position as center or the image center.

  • +
  • random_size (bool) – crop with random size or specified size ROI by roi_scale * image spatial size. +if True, the actual size is sampled from: +randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+

CenterScaleCropd#

+example of CenterScaleCropd +
+
+class monai.transforms.CenterScaleCropd(keys, roi_scale, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.CenterScaleCrop. +Note: as using the same scaled ROI to crop, all the input data specified by keys should have +the same spatial shape.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • roi_scale (UnionType[Sequence[float], float]) – specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5] or a number for all dims. +If its components have non-positive values, will use 1.0 instead, which means the input image size.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+
+

Intensity (Dict)#

+
+

RandGaussianNoised#

+example of RandGaussianNoised +
+
+class monai.transforms.RandGaussianNoised(keys, prob=0.1, mean=0.0, std=0.1, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandGaussianNoise. +Add Gaussian noise to image. This transform assumes all the expected fields have same shape, if want to add +different noise for every field, please use this transform separately.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • prob (float) – Probability to add Gaussian noise.

  • +
  • mean (float) – Mean or “centre” of the distribution.

  • +
  • std (float) – Standard deviation (spread) of distribution.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandGaussianNoised

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

ShiftIntensityd#

+example of ShiftIntensityd +
+
+class monai.transforms.ShiftIntensityd(keys, offset, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ShiftIntensity.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, offset, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • offset (float) – offset value to shift the intensity of image.

  • +
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. +E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • +
  • factor_key (Optional[str, None]) – if not None, use it as the key to extract a value from the corresponding +metadata dictionary of key at runtime, and multiply the offset to shift intensity. +Usually, IntensityStatsd transform can pre-compute statistics of intensity values +and store in the metadata. +it also can be a sequence of strings, map to keys.

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. +used to extract the factor value is factor_key is not None. +for example, for data with key image, the metadata by default is in image_meta_dict. +the metadata is a dictionary object which contains: filename, original_shape, etc. +it can be a sequence of string, map to the keys. +if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • +
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to fetch the metadata according +to the key data, default is meta_dict, the metadata is a dictionary object. +used to extract the factor value is factor_key is not None.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

RandShiftIntensityd#

+example of RandShiftIntensityd +
+
+class monai.transforms.RandShiftIntensityd(keys, offsets, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandShiftIntensity.

+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(keys, offsets, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', prob=0.1, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • offsets (UnionType[tuple[float, float], float]) – offset range to randomly shift. +if single number, offset value is picked from (-offsets, offsets).

  • +
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. +E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • +
  • factor_key (Optional[str, None]) – if not None, use it as the key to extract a value from the corresponding +metadata dictionary of key at runtime, and multiply the random offset to shift intensity. +Usually, IntensityStatsd transform can pre-compute statistics of intensity values +and store in the metadata. +it also can be a sequence of strings, map to keys.

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. +used to extract the factor value is factor_key is not None. +for example, for data with key image, the metadata by default is in image_meta_dict. +the metadata is a dictionary object which contains: filename, original_shape, etc. +it can be a sequence of string, map to the keys. +if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • +
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to fetch the metadata according +to the key data, default is meta_dict, the metadata is a dictionary object. +used to extract the factor value is factor_key is not None.

  • +
  • prob (float) – probability of shift. +(Default 0.1, with 10% probability it returns an array shifted intensity.)

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandShiftIntensityd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

StdShiftIntensityd#

+example of StdShiftIntensityd +
+
+class monai.transforms.StdShiftIntensityd(keys, factor, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.StdShiftIntensity.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, factor, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • factor (float) – factor shift by v = v + factor * std(v).

  • +
  • nonzero (bool) – whether only count non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately. Please ensure +that the first dimension represents the channel of the image if True.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

RandStdShiftIntensityd#

+example of RandStdShiftIntensityd +
+
+class monai.transforms.RandStdShiftIntensityd(keys, factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandStdShiftIntensity.

+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(keys, factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • factors (UnionType[tuple[float, float], float]) – if tuple, the randomly picked range is (min(factors), max(factors)). +If single number, the range is (-factors, factors).

  • +
  • prob (float) – probability of std shift.

  • +
  • nonzero (bool) – whether only count non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandStdShiftIntensityd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandBiasFieldd#

+example of RandBiasFieldd +
+
+class monai.transforms.RandBiasFieldd(keys, degree=3, coeff_range=(0.0, 0.1), dtype=<class 'numpy.float32'>, prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandBiasField.

+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(keys, degree=3, coeff_range=(0.0, 0.1), dtype=<class 'numpy.float32'>, prob=0.1, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • degree (int) – degree of freedom of the polynomials. The value should be no less than 1. +Defaults to 3.

  • +
  • coeff_range (tuple[float, float]) – range of the random coefficients. Defaults to (0.0, 0.1).

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • prob (float) – probability to do random bias field.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandBiasFieldd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

ScaleIntensityd#

+example of ScaleIntensityd +
+
+class monai.transforms.ScaleIntensityd(keys, minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ScaleIntensity. +Scale the intensity of input image to the given value range (minv, maxv). +If minv and maxv not provided, use factor to scale image by v = v * (1 + factor).

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • minv (UnionType[float, None]) – minimum value of output data.

  • +
  • maxv (UnionType[float, None]) – maximum value of output data.

  • +
  • factor (Optional[float, None]) – factor scale by v = v * (1 + factor). In order to use +this parameter, please set both minv and maxv into None.

  • +
  • channel_wise (bool) – if True, scale on each channel separately. Please ensure +that the first dimension represents the channel of the image if True.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

RandScaleIntensityd#

+example of RandScaleIntensityd +
+
+class monai.transforms.RandScaleIntensityd(keys, factors, prob=0.1, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandScaleIntensity.

+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(keys, factors, prob=0.1, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • factors (UnionType[tuple[float, float], float]) – factor range to randomly scale by v = v * (1 + factor). +if single number, factor value is picked from (-factors, factors).

  • +
  • prob (float) – probability of scale. +(Default 0.1, with 10% probability it returns a scaled array.)

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandScaleIntensityd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

NormalizeIntensityd#

+example of NormalizeIntensityd +
+
+class monai.transforms.NormalizeIntensityd(keys, subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.NormalizeIntensity. +This transform can normalize only non-zero values or entire image, and can also calculate +mean and std on each channel separately.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • subtrahend (Union[ndarray, Tensor, None]) – the amount to subtract by (usually the mean)

  • +
  • divisor (Union[ndarray, Tensor, None]) – the amount to divide by (usually the standard deviation)

  • +
  • nonzero (bool) – whether only normalize non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately, otherwise, calculate on +the entire image directly. default to False.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ThresholdIntensityd#

+example of ThresholdIntensityd +
+
+class monai.transforms.ThresholdIntensityd(keys, threshold, above=True, cval=0.0, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ThresholdIntensity.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • threshold (float) – the threshold to filter intensity values.

  • +
  • above (bool) – filter values above the threshold or below the threshold, default is True.

  • +
  • cval (float) – value to fill the remaining parts of the image, default is 0.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ScaleIntensityRanged#

+example of ScaleIntensityRanged +
+
+class monai.transforms.ScaleIntensityRanged(keys, a_min, a_max, b_min=None, b_max=None, clip=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ScaleIntensityRange.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • a_min (float) – intensity original range min.

  • +
  • a_max (float) – intensity original range max.

  • +
  • b_min (Optional[float, None]) – intensity target range min.

  • +
  • b_max (Optional[float, None]) – intensity target range max.

  • +
  • clip (bool) – whether to perform clip after scaling.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

GibbsNoised#

+example of GibbsNoised +
+
+class monai.transforms.GibbsNoised(keys, alpha=0.5, allow_missing_keys=False)[source]#
+

Dictionary-based version of GibbsNoise.

+

The transform applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts +are one of the common type of type artifacts appearing in MRI scans.

+

For general information on Gibbs artifacts, please refer to: +https://pubs.rsna.org/doi/full/10.1148/rg.313105115 +https://pubs.rsna.org/doi/full/10.1148/radiographics.22.4.g02jl14949

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – ‘image’, ‘label’, or [‘image’, ‘label’] depending on which data +you need to transform.

  • +
  • alpha (float) – Parametrizes the intensity of the Gibbs noise filter applied. Takes +values in the interval [0,1] with alpha = 0 acting as the identity mapping.

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

RandGibbsNoised#

+example of RandGibbsNoised +
+
+class monai.transforms.RandGibbsNoised(keys, prob=0.1, alpha=(0.0, 1.0), allow_missing_keys=False)[source]#
+

Dictionary-based version of RandGibbsNoise.

+

Naturalistic image augmentation via Gibbs artifacts. The transform +randomly applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts +are one of the common type of type artifacts appearing in MRI scans.

+

The transform is applied to all the channels in the data.

+

For general information on Gibbs artifacts, please refer to: +https://pubs.rsna.org/doi/full/10.1148/rg.313105115 +https://pubs.rsna.org/doi/full/10.1148/radiographics.22.4.g02jl14949

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – ‘image’, ‘label’, or [‘image’, ‘label’] depending on which data +you need to transform.

  • +
  • prob (float) – probability of applying the transform.

  • +
  • alpha (float, List[float]) – Parametrizes the intensity of the Gibbs noise filter applied. Takes +values in the interval [0,1] with alpha = 0 acting as the identity mapping. +If a length-2 list is given as [a,b] then the value of alpha will be sampled +uniformly from the interval [a,b].

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandGibbsNoised

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

KSpaceSpikeNoised#

+example of KSpaceSpikeNoised +
+
+class monai.transforms.KSpaceSpikeNoised(keys, loc, k_intensity=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.KSpaceSpikeNoise.

+

Applies localized spikes in k-space at the given locations and intensities. +Spike (Herringbone) artifact is a type of data acquisition artifact which +may occur during MRI scans.

+

For general information on spike artifacts, please refer to:

+

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

+

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s +perspective.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – “image”, “label”, or [“image”, “label”] depending +on which data you need to transform.

  • +
  • loc (Union[tuple, Sequence[tuple]]) – spatial location for the spikes. For +images with 3D spatial dimensions, the user can provide (C, X, Y, Z) +to fix which channel C is affected, or (X, Y, Z) to place the same +spike in all channels. For 2D cases, the user can provide (C, X, Y) +or (X, Y).

  • +
  • k_intensity (Union[Sequence[float], float, None]) – value for the log-intensity of the +k-space version of the image. If one location is passed to loc or the +channel is not specified, then this argument should receive a float. If +loc is given a sequence of locations, then this argument should +receive a sequence of intensities. This value should be tested as it is +data-dependent. The default values are the 2.5 the mean of the +log-intensity for each channel.

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+

Example

+

When working with 4D data, +KSpaceSpikeNoised("image", loc = ((3,60,64,32), (64,60,32)), k_intensity = (13,14)) +will place a spike at [3, 60, 64, 32] with log-intensity = 13, and +one spike per channel located respectively at [: , 64, 60, 32] +with log-intensity = 14.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – Expects image/label to have dimensions (C, H, W) or +(C, H, W, D), where C is the channel.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+

RandKSpaceSpikeNoised#

+example of RandKSpaceSpikeNoised +
+
+class monai.transforms.RandKSpaceSpikeNoised(keys, prob=0.1, intensity_range=None, channel_wise=True, allow_missing_keys=False)[source]#
+

Dictionary-based version of monai.transforms.RandKSpaceSpikeNoise.

+

Naturalistic data augmentation via spike artifacts. The transform applies +localized spikes in k-space.

+

For general information on spike artifacts, please refer to:

+

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

+

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s +perspective.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – “image”, “label”, or [“image”, “label”] depending +on which data you need to transform.

  • +
  • prob (float) – probability to add spike artifact to each item in the +dictionary provided it is realized that the noise will be applied +to the dictionary.

  • +
  • intensity_range (Optional[Sequence[Union[Sequence[float], float]], None]) – pass a tuple (a, b) to sample the log-intensity from the interval (a, b) +uniformly for all channels. Or pass sequence of intervals +((a0, b0), (a1, b1), …) to sample for each respective channel. +In the second case, the number of 2-tuples must match the number of channels. +Default ranges is (0.95x, 1.10x) where x is the mean +log-intensity for each channel.

  • +
  • channel_wise (bool) – treat each channel independently. True by default.

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+

Example

+

To apply k-space spikes randomly on the image only, with probability +0.5, and log-intensity sampled from the interval [13, 15] for each +channel independently, one uses +RandKSpaceSpikeNoised("image", prob=0.5, intensity_ranges=(13, 15), channel_wise=True).

+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandKSpaceSpikeNoised

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandRicianNoised#

+example of RandRicianNoised +
+
+class monai.transforms.RandRicianNoised(keys, prob=0.1, mean=0.0, std=1.0, channel_wise=False, relative=False, sample_std=True, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandRicianNoise. +Add Rician noise to image. This transform assumes all the expected fields have same shape, if want to add +different noise for every field, please use this transform separately.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • prob (float) – Probability to add Rician noise to the dictionary.

  • +
  • mean (Union[Sequence[float], float]) – Mean or “centre” of the Gaussian distributions sampled to make up +the Rician noise.

  • +
  • std (Union[Sequence[float], float]) – Standard deviation (spread) of the Gaussian distributions sampled +to make up the Rician noise.

  • +
  • channel_wise (bool) – If True, treats each channel of the image separately.

  • +
  • relative (bool) – If True, the spread of the sampled Gaussian distributions will +be std times the standard deviation of the image or channel’s intensity +histogram.

  • +
  • sample_std (bool) – If True, sample the spread of the Gaussian distributions +uniformly from 0 to std.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – Don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandRicianNoised

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

ScaleIntensityRangePercentilesd#

+example of ScaleIntensityRangePercentilesd +
+
+class monai.transforms.ScaleIntensityRangePercentilesd(keys, lower, upper, b_min, b_max, clip=False, relative=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ScaleIntensityRangePercentiles.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • lower (float) – lower percentile.

  • +
  • upper (float) – upper percentile.

  • +
  • b_min (UnionType[float, None]) – intensity target range min.

  • +
  • b_max (UnionType[float, None]) – intensity target range max.

  • +
  • clip (bool) – whether to perform clip after scaling.

  • +
  • relative (bool) – whether to scale to the corresponding percentiles of [b_min, b_max]

  • +
  • channel_wise (bool) – if True, compute intensity percentile and normalize every channel separately. +default to False.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

AdjustContrastd#

+example of AdjustContrastd +
+
+class monai.transforms.AdjustContrastd(keys, gamma, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.AdjustContrast. +Changes image intensity by gamma. Each pixel/voxel intensity is updated as:

+
+

x = ((x - min) / intensity_range) ^ gamma * intensity_range + min

+
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • gamma (float) – gamma value to adjust the contrast as function.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

RandAdjustContrastd#

+example of RandAdjustContrastd +
+
+class monai.transforms.RandAdjustContrastd(keys, prob=0.1, gamma=(0.5, 4.5), allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandAdjustContrast. +Randomly changes image intensity by gamma. Each pixel/voxel intensity is updated as:

+
+

x = ((x - min) / intensity_range) ^ gamma * intensity_range + min

+
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • prob (float) – Probability of adjustment.

  • +
  • gamma (UnionType[tuple[float, float], float]) – Range of gamma values. +If single number, value is picked from (0.5, gamma), default is (0.5, 4.5).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandAdjustContrastd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

MaskIntensityd#

+example of MaskIntensityd +
+
+class monai.transforms.MaskIntensityd(keys, mask_data=None, mask_key=None, select_fn=<function is_positive>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.MaskIntensity.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • mask_data (Union[ndarray, Tensor, None]) – if mask data is single channel, apply to every channel +of input image. if multiple channels, the channel number must +match input data. the intensity values of input image corresponding +to the selected values in the mask data will keep the original value, +others will be set to 0. if None, will extract the mask data from +input data based on mask_key.

  • +
  • mask_key (Optional[str, None]) – the key to extract mask data from input dictionary, only works +when mask_data is None.

  • +
  • select_fn (Callable) – function to select valid values of the mask_data, default is +to select values > 0.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

SavitzkyGolaySmoothd#

+example of SavitzkyGolaySmoothd +
+
+class monai.transforms.SavitzkyGolaySmoothd(keys, window_length, order, axis=1, mode='zeros', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SavitzkyGolaySmooth.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • window_length (int) – length of the filter window, must be a positive odd integer.

  • +
  • order (int) – order of the polynomial to fit to each window, must be less than window_length.

  • +
  • axis (int) – optional axis along which to apply the filter kernel. Default 1 (first spatial dimension).

  • +
  • mode (str) – optional padding mode, passed to convolution class. 'zeros', 'reflect', 'replicate' +or 'circular'. default: 'zeros'. See torch.nn.Conv1d() for more information.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

MedianSmoothd#

+example of MedianSmoothd +
+
+class monai.transforms.MedianSmoothd(keys, radius, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.MedianSmooth.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • radius (Union[Sequence[int], int]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

GaussianSmoothd#

+example of GaussianSmoothd +
+
+class monai.transforms.GaussianSmoothd(keys, sigma, approx='erf', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.GaussianSmooth.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • sigma (Union[Sequence[float], float]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

RandGaussianSmoothd#

+example of RandGaussianSmoothd +
+
+class monai.transforms.RandGaussianSmoothd(keys, sigma_x=(0.25, 1.5), sigma_y=(0.25, 1.5), sigma_z=(0.25, 1.5), approx='erf', prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.GaussianSmooth.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • sigma_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension.

  • +
  • sigma_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension if have.

  • +
  • sigma_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension if have.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
  • prob (float) – probability of Gaussian smooth.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandGaussianSmoothd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

GaussianSharpend#

+example of GaussianSharpend +
+
+class monai.transforms.GaussianSharpend(keys, sigma1=3.0, sigma2=1.0, alpha=30.0, approx='erf', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.GaussianSharpen.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • sigma1 (Union[Sequence[float], float]) – sigma parameter for the first gaussian kernel. if a list of values, must match the count +of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. +if only 1 value provided, use it for all spatial dimensions.

  • +
  • sigma2 (Union[Sequence[float], float]) – sigma parameter for the second gaussian kernel. if a list of values, must match the count +of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. +if only 1 value provided, use it for all spatial dimensions.

  • +
  • alpha (float) – weight parameter to compute the final result.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

RandGaussianSharpend#

+example of RandGaussianSharpend +
+
+class monai.transforms.RandGaussianSharpend(keys, sigma1_x=(0.5, 1.0), sigma1_y=(0.5, 1.0), sigma1_z=(0.5, 1.0), sigma2_x=0.5, sigma2_y=0.5, sigma2_z=0.5, alpha=(10.0, 30.0), approx='erf', prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.GaussianSharpen.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • sigma1_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension of first gaussian kernel.

  • +
  • sigma1_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension(if have) of first gaussian kernel.

  • +
  • sigma1_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension(if have) of first gaussian kernel.

  • +
  • sigma2_x (UnionType[tuple[float, float], float]) – randomly select sigma value for the first spatial dimension of second gaussian kernel. +if only 1 value X provided, it must be smaller than sigma1_x and randomly select from [X, sigma1_x].

  • +
  • sigma2_y (UnionType[tuple[float, float], float]) – randomly select sigma value for the second spatial dimension(if have) of second gaussian kernel. +if only 1 value Y provided, it must be smaller than sigma1_y and randomly select from [Y, sigma1_y].

  • +
  • sigma2_z (UnionType[tuple[float, float], float]) – randomly select sigma value for the third spatial dimension(if have) of second gaussian kernel. +if only 1 value Z provided, it must be smaller than sigma1_z and randomly select from [Z, sigma1_z].

  • +
  • alpha (tuple[float, float]) – randomly select weight parameter to compute the final result.

  • +
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. +see also monai.networks.layers.GaussianFilter().

  • +
  • prob (float) – probability of Gaussian sharpen.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandGaussianSharpend

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandHistogramShiftd#

+example of RandHistogramShiftd +
+
+class monai.transforms.RandHistogramShiftd(keys, num_control_points=10, prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based version monai.transforms.RandHistogramShift. +Apply random nonlinear transform the image’s intensity histogram.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • num_control_points (UnionType[tuple[int, int], int]) – number of control points governing the nonlinear intensity mapping. +a smaller number of control points allows for larger intensity shifts. if two values provided, number of +control points selecting from range (min_value, max_value).

  • +
  • prob (float) – probability of histogram shift.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandHistogramShiftd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandCoarseDropoutd#

+example of RandCoarseDropoutd +
+
+class monai.transforms.RandCoarseDropoutd(keys, holes, spatial_size, dropout_holes=True, fill_value=None, max_holes=None, max_spatial_size=None, prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RandCoarseDropout. +Expect all the data specified by keys have same spatial shape and will randomly dropout the same regions +for every key, if want to dropout differently for every key, please use this transform separately.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to +randomly select the expected number of regions.

  • +
  • spatial_size (Union[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg +as the minimum spatial size to randomly select size for every region. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • dropout_holes (bool) – if True, dropout the regions of holes and fill value, if False, keep the holes and +dropout the outside and fill value. default to True.

  • +
  • fill_value (Union[tuple[float, float], float, None]) – target value to fill the dropout regions, if providing a number, will use it as constant +value to fill all the regions. if providing a tuple for the min and max, will randomly select +value for every pixel / voxel from the range [min, max). if None, will compute the min and max +value of input image then randomly select value to fill, default to None.

  • +
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • +
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. +if some components of the max_spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of applying the transform.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandCoarseDropoutd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandCoarseShuffled#

+example of RandCoarseShuffled +
+
+class monai.transforms.RandCoarseShuffled(keys, holes, spatial_size, max_holes=None, max_spatial_size=None, prob=0.1, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RandCoarseShuffle. +Expect all the data specified by keys have same spatial shape and will randomly dropout the same regions +for every key, if want to shuffle different regions for every key, please use this transform separately.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to +randomly select the expected number of regions.

  • +
  • spatial_size (Union[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg +as the minimum spatial size to randomly select size for every region. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • +
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. +if some components of the max_spatial_size are non-positive values, the transform will use the +corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of applying the transform.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandCoarseShuffled

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

HistogramNormalized#

+example of HistogramNormalized +
+
+class monai.transforms.HistogramNormalized(keys, num_bins=256, min=0, max=255, mask=None, mask_key=None, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.HistogramNormalize.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • num_bins (int) – number of the bins to use in histogram, default to 256. for more details: +https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.

  • +
  • min (int) – the min value to normalize input image, default to 255.

  • +
  • max (int) – the max value to normalize input image, default to 255.

  • +
  • mask (Union[ndarray, Tensor, None]) – if provided, must be ndarray of bools or 0s and 1s, and same shape as image. +only points at which mask==True are used for the equalization. +can also provide the mask by mask_key at runtime.

  • +
  • mask_key (Optional[str, None]) – if mask is None, will try to get the mask with mask_key.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ForegroundMaskd#

+example of ForegroundMaskd +
+
+class monai.transforms.ForegroundMaskd(keys, threshold='otsu', hsv_threshold=None, invert=False, new_key_prefix=None, allow_missing_keys=False)[source]#
+

Creates a binary mask that defines the foreground based on thresholds in RGB or HSV color space. +This transform receives an RGB (or grayscale) image where by default it is assumed that the foreground has +low values (dark) while the background is white.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • threshold (Union[dict, Callable, str, float]) – an int or a float number that defines the threshold that values less than that are foreground. +It also can be a callable that receives each dimension of the image and calculate the threshold, +or a string that defines such callable from skimage.filter.threshold_…. For the list of available +threshold functions, please refer to https://scikit-image.org/docs/stable/api/skimage.filters.html +Moreover, a dictionary can be passed that defines such thresholds for each channel, like +{“R”: 100, “G”: “otsu”, “B”: skimage.filter.threshold_mean}

  • +
  • hsv_threshold (Union[dict, Callable, str, float, int, None]) – similar to threshold but HSV color space (“H”, “S”, and “V”). +Unlike RBG, in HSV, value greater than hsv_threshold are considered foreground.

  • +
  • invert (bool) – invert the intensity range of the input image, so that the dtype maximum is now the dtype minimum, +and vice-versa.

  • +
  • new_key_prefix (Optional[str, None]) – this prefix be prepended to the key to create a new key for the output and keep the value of +key intact. By default not prefix is set and the corresponding array to the key will be replaced.

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ComputeHoVerMapsd#

+
+
+class monai.transforms.ComputeHoVerMapsd(keys, dtype='float32', new_key_prefix='hover_', allow_missing_keys=False)[source]#
+

Compute horizontal and vertical maps from an instance mask +It generates normalized horizontal and vertical distances to the center of mass of each region.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • dtype (Union[dtype, type, str, None]) – the type of output Tensor. Defaults to “float32”.

  • +
  • new_key_prefix (str) – this prefix be prepended to the key to create a new key for the output and keep the value of +key intact. Defaults to ‘“_hover”, so if the input key is “mask” the output will be “hover_mask”.

  • +
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+
+

IO (Dict)#

+
+

LoadImaged#

+
+
+class monai.transforms.LoadImaged(keys, reader=None, dtype=<class 'numpy.float32'>, meta_keys=None, meta_key_postfix='meta_dict', overwriting=False, image_only=False, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', allow_missing_keys=False, expanduser=True, *args, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.LoadImage, +It can load both image data and metadata. When loading a list of files in one key, +the arrays will be stacked and a new dimension will be added as the first dimension +In this case, the metadata of the first image will be used to represent the stacked result. +The affine transform of all the stacked images should be same. +The output metadata field will be created as meta_keys or key_{meta_key_postfix}.

+

If reader is not specified, this class automatically chooses readers +based on the supported suffixes and in the following order:

+
+
    +
  • User-specified reader at runtime when calling this loader.

  • +
  • User-specified reader in the constructor of LoadImage.

  • +
  • Readers from the last to the first in the registered list.

  • +
  • Current default readers: (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), +(npz, npy -> NumpyReader), (dcm, DICOM series and others -> ITKReader).

  • +
+
+

Please note that for png, jpg, bmp, and other 2D formats, readers by default swap axis 0 and 1 after +loading the array with reverse_indexing set to True because the spatial axes definition +for non-medical specific file formats is different from other common medical packages.

+
+

Note

+
    +
  • If reader is specified, the loader will attempt to use the specified readers and the default supported +readers. This might introduce overheads when handling the exceptions of trying the incompatible loaders. +In this case, it is therefore recommended setting the most appropriate reader as +the last item of the reader parameter.

  • +
+
+
+

See also

+ +
+
+
+__call__(data, reader=None)[source]#
+
+
Raises:
+

KeyError – When not self.overwriting and key already exists in data.

+
+
+
+ +
+
+__init__(keys, reader=None, dtype=<class 'numpy.float32'>, meta_keys=None, meta_key_postfix='meta_dict', overwriting=False, image_only=False, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', allow_missing_keys=False, expanduser=True, *args, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • reader (Union[type[ImageReader], str, None]) – reader to load image file and metadata +- if reader is None, a default set of SUPPORTED_READERS will be used. +- if reader is a string, it’s treated as a class name or dotted path +(such as "monai.data.ITKReader"), the supported built-in reader classes are +"ITKReader", "NibabelReader", "NumpyReader". +a reader instance will be constructed with the *args and **kwargs parameters. +- if reader is a reader class/instance, it will be registered to this loader accordingly.

  • +
  • dtype (Union[dtype, type, str, None]) – if not None, convert the loaded image data to this data type.

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key to store the corresponding metadata dictionary. +the metadata is a dictionary object which contains: filename, original_shape, etc. +it can be a sequence of string, map to the keys. +if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • +
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to store the metadata of the nifti image, +default is meta_dict. The metadata is a dictionary object. +For example, load nifti file for image, store the metadata into image_meta_dict.

  • +
  • overwriting (bool) – whether allow overwriting existing metadata of same key. +default is False, which will raise exception if encountering existing key.

  • +
  • image_only (bool) – if True return dictionary containing just only the image volumes, otherwise return +dictionary containing image data array and header dict per input key.

  • +
  • ensure_channel_first (bool) – if True and loaded both image array and metadata, automatically convert +the image array shape to channel first. default to False.

  • +
  • simple_keys (bool) – whether to remove redundant metadata keys, default to False for backward compatibility.

  • +
  • prune_meta_pattern (Optional[str, None]) – combined with prune_meta_sep, a regular expression used to match and prune keys +in the metadata (nested dictionary), default to None, no key deletion.

  • +
  • prune_meta_sep (str) – combined with prune_meta_pattern, used to match and prune keys +in the metadata (nested dictionary). default is “.”, see also monai.transforms.DeleteItemsd. +e.g. prune_meta_pattern=".*_code$", prune_meta_sep=" " removes meta keys that ends with "_code".

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • expanduser (bool) – if True cast filename to Path and call .expanduser on it, otherwise keep filename as is.

  • +
  • args – additional parameters for reader if providing a reader name.

  • +
  • kwargs – additional parameters for reader if providing a reader name.

  • +
+
+
+
+ +
+
+register(reader)[source]#
+

Register a virtual subclass of an ABC.

+

Returns the subclass, to allow usage as a class decorator.

+
+ +
+ +
+
+

SaveImaged#

+
+
+class monai.transforms.SaveImaged(keys, meta_keys=None, meta_key_postfix='meta_dict', output_dir='./', output_postfix='trans', output_ext='.nii.gz', resample=True, mode='nearest', padding_mode=GridSamplePadMode.BORDER, scale=None, dtype=<class 'numpy.float64'>, output_dtype=<class 'numpy.float32'>, allow_missing_keys=False, squeeze_end_dims=True, data_root_dir='', separate_folder=True, print_log=True, output_format='', writer=None, output_name_formatter=None, folder_layout=None, savepath_in_metadict=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SaveImage.

+
+

Note

+

Image should be channel-first shape: [C,H,W,[D]]. +If the data is a patch of an image, the patch index will be appended to the filename.

+
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. +For example, for data with key image, the metadata by default is in image_meta_dict. +The metadata is a dictionary contains values such as filename, original_shape. +This argument can be a sequence of strings, mapped to the keys. +If None, will try to construct meta_keys by key_{meta_key_postfix}.

  • +
  • meta_key_postfix (str) – if meta_keys is None, use key_{meta_key_postfix} to retrieve the metadict.

  • +
  • output_dir (UnionType[Path, str]) – output image directory. +Handled by folder_layout instead, if folder_layout is not None.

  • +
  • output_postfix (str) – a string appended to all output file names, default to trans. +Handled by folder_layout instead, if folder_layout is not None.

  • +
  • output_ext (str) – output file extension name, available extensions: .nii.gz, .nii, .png. +Handled by folder_layout instead, if folder_layout not None.

  • +
  • resample (bool) – whether to resample image (if needed) before saving the data array, +based on the spatial_shape (and original_affine) from metadata.

  • +
  • mode (str) –

    This option is used when resample=True. Defaults to "nearest". +Depending on the writers, the possible options are:

    + +

  • +
  • padding_mode (str) – This option is used when resample = True. Defaults to "border". +Possible options are {"zeros", "border", "reflection"} +See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample

  • +
  • scale (Optional[int, None]) – {255, 65535} postprocess data by clipping to [0, 1] and scaling +[0, 255] (uint8) or [0, 65535] (uint16). Default is None (no scaling).

  • +
  • dtype (Union[dtype, type, str, None]) – data type during resampling computation. Defaults to np.float64 for best precision. +if None, use the data type of input data. To set the output data type, use output_dtype.

  • +
  • output_dtype (Union[dtype, type, str, None]) – data type for saving data. Defaults to np.float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • squeeze_end_dims (bool) – if True, any trailing singleton dimensions will be removed (after the channel +has been moved to the end). So if input is (C,H,W,D), this will be altered to (H,W,D,C), and +then if C==1, it will be saved as (H,W,D). If D is also 1, it will be saved as (H,W). If false, +image will always be saved as (H,W,D,C).

  • +
  • data_root_dir (str) –

    if not empty, it specifies the beginning parts of the input file’s +absolute path. It’s used to compute input_file_rel_path, the relative path to the file from +data_root_dir to preserve folder structure when saving in case there are files in different +folders with the same file names. For example, with the following inputs:

    +
      +
    • input_file_name: /foo/bar/test1/image.nii

    • +
    • output_postfix: seg

    • +
    • output_ext: .nii.gz

    • +
    • output_dir: /output

    • +
    • data_root_dir: /foo/bar

    • +
    +

    The output will be: /output/test1/image/image_seg.nii.gz

    +

    Handled by folder_layout instead, if folder_layout is not None.

    +

  • +
  • separate_folder (bool) – whether to save every file in a separate folder. For example: for the input filename +image.nii, postfix seg and folder_path output, if separate_folder=True, it will be saved as: +output/image/image_seg.nii, if False, saving as output/image_seg.nii. Default to True. +Handled by folder_layout instead, if folder_layout is not None.

  • +
  • print_log (bool) – whether to print logs when saving. Default to True.

  • +
  • output_format (str) – an optional string to specify the output image writer. +see also: monai.data.image_writer.SUPPORTED_WRITERS.

  • +
  • writer (Union[type[ImageWriter], str, None]) – a customised monai.data.ImageWriter subclass to save data arrays. +if None, use the default writer from monai.data.image_writer according to output_ext. +if it’s a string, it’s treated as a class name or dotted path; +the supported built-in writer classes are "NibabelWriter", "ITKWriter", "PILWriter".

  • +
  • output_name_formatter (Optional[Callable[[dict, Transform], dict], None]) – a callable function (returning a kwargs dict) to format the output file name. +see also: monai.data.folder_layout.default_name_formatter(). +If using a custom folder_layout, consider providing your own formatter.

  • +
  • folder_layout (Optional[FolderLayoutBase, None]) – A customized monai.data.FolderLayoutBase subclass to define file naming schemes. +if None, uses the default FolderLayout.

  • +
  • savepath_in_metadict (bool) – if True, adds a key saved_to to the metadata, which contains the path +to where the input image has been saved.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+
+

Post-processing (Dict)#

+
+

Activationsd#

+
+
+class monai.transforms.Activationsd(keys, sigmoid=False, softmax=False, other=None, allow_missing_keys=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.AddActivations. +Add activation layers to the input data specified by keys.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, sigmoid=False, softmax=False, other=None, allow_missing_keys=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output and label. +See also: monai.transforms.compose.MapTransform

  • +
  • sigmoid (UnionType[Sequence[bool], bool]) – whether to execute sigmoid function on model output before transform. +it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • softmax (UnionType[Sequence[bool], bool]) – whether to execute softmax function on model output before transform. +it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • other (Union[Sequence[Callable], Callable, None]) – callable function to execute other activation layers, +for example: other = torch.tanh. it also can be a sequence of Callable, each +element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • kwargs – additional parameters to torch.softmax (used when softmax=True). +Defaults to dim=0, unrecognized parameters will be ignored.

  • +
+
+
+
+ +
+ +
+
+

AsDiscreted#

+example of AsDiscreted +
+
+class monai.transforms.AsDiscreted(keys, argmax=False, to_onehot=None, threshold=None, rounding=None, allow_missing_keys=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.AsDiscrete.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, argmax=False, to_onehot=None, threshold=None, rounding=None, allow_missing_keys=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output and label. +See also: monai.transforms.compose.MapTransform

  • +
  • argmax (UnionType[Sequence[bool], bool]) – whether to execute argmax function on input data before transform. +it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • to_onehot (Union[Sequence[UnionType[int, None]], int, None]) – if not None, convert input data into the one-hot format with specified number of classes. +defaults to None. it also can be a sequence, each element corresponds to a key in keys.

  • +
  • threshold (Union[Sequence[UnionType[float, None]], float, None]) – if not None, threshold the float values to int number 0 or 1 with specified threshold value. +defaults to None. it also can be a sequence, each element corresponds to a key in keys.

  • +
  • rounding (Union[Sequence[UnionType[str, None]], str, None]) – if not None, round the data according to the specified option, +available options: [“torchrounding”]. it also can be a sequence of str or None, +each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • kwargs – additional parameters to AsDiscrete. +dim, keepdim, dtype are supported, unrecognized parameters will be ignored. +These default to 0, True, torch.float respectively.

  • +
+
+
+
+ +
+ +
+
+

KeepLargestConnectedComponentd#

+example of KeepLargestConnectedComponentd +
+
+class monai.transforms.KeepLargestConnectedComponentd(keys, applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.KeepLargestConnectedComponent.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • applied_labels (Union[Sequence[int], int, None]) – Labels for applying the connected component analysis on. +If given, voxels whose value is in this list will be analyzed. +If None, all non-zero values will be analyzed.

  • +
  • is_onehot (Optional[bool, None]) – if True, treat the input data as OneHot format data, otherwise, not OneHot format data. +default to None, which treats multi-channel data as OneHot and single channel data as not OneHot.

  • +
  • independent (bool) – whether to treat applied_labels as a union of foreground labels. +If True, the connected component analysis will be performed on each foreground label independently +and return the intersection of the largest components. +If False, the analysis will be performed on the union of foreground labels. +default is True.

  • +
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. If None, a full +connectivity of input.ndim is used. for more details: +https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label.

  • +
  • num_components (int) – The number of largest components to preserve.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

RemoveSmallObjectsd#

+example of RemoveSmallObjectsd +
+
+class monai.transforms.RemoveSmallObjectsd(keys, min_size=64, connectivity=1, independent_channels=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RemoveSmallObjectsd.

+
+
Parameters:
+
    +
  • min_size (int) – objects smaller than this size are removed.

  • +
  • connectivity (int) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. If None, a full +connectivity of input.ndim is used. For more details refer to linked scikit-image +documentation.

  • +
  • independent_channels (bool) – Whether or not to consider channels as independent. If true, then +conjoining islands from different labels will be removed if they are below the threshold. +If false, the overall size islands made from all non-background voxels will be used.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

LabelFilterd#

+example of LabelFilterd +
+
+class monai.transforms.LabelFilterd(keys, applied_labels, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.LabelFilter.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, applied_labels, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • applied_labels (UnionType[Sequence[int], int]) – Label(s) to filter on.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

FillHolesd#

+
+
+class monai.transforms.FillHolesd(keys, applied_labels=None, connectivity=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.FillHoles.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, applied_labels=None, connectivity=None, allow_missing_keys=False)[source]#
+

Initialize the connectivity and limit the labels for which holes are filled.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • applied_labels (Optional[Union[Iterable[int], int]], optional) – Labels for which to fill holes. Defaults to None, +that is filling holes for all labels.

  • +
  • connectivity (int, optional) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. Defaults to a full +connectivity of input.ndim.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

LabelToContourd#

+example of LabelToContourd +
+
+class monai.transforms.LabelToContourd(keys, kernel_type='Laplace', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.LabelToContour.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, kernel_type='Laplace', allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • kernel_type (str) – the method applied to do edge detection, default is “Laplace”.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

Ensembled#

+
+
+class monai.transforms.Ensembled(keys, ensemble, output_key=None, allow_missing_keys=False)[source]#
+

Base class of dictionary-based ensemble transforms.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, ensemble, output_key=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be stack and execute ensemble. +if only 1 key provided, suppose it’s a PyTorch Tensor with data stacked on dimension E.

  • +
  • output_key (Optional[str, None]) – the key to store ensemble result in the dictionary.

  • +
  • ensemble (Callable[Union[Sequence[Union[ndarray, Tensor]], ndarray, Tensor], Union[ndarray, Tensor]]) – callable method to execute ensemble on specified data. +if only 1 key provided in keys, output_key can be None and use keys as default.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
Raises:
+
    +
  • TypeError – When ensemble is not callable.

  • +
  • ValueError – When len(keys) > 1 and output_key=None. Incompatible values.

  • +
+
+
+
+ +
+ +
+
+

MeanEnsembled#

+
+
+class monai.transforms.MeanEnsembled(keys, output_key=None, weights=None)[source]#
+

Dictionary-based wrapper of monai.transforms.MeanEnsemble.

+
+
+__init__(keys, output_key=None, weights=None)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be stack and execute ensemble. +if only 1 key provided, suppose it’s a PyTorch Tensor with data stacked on dimension E.

  • +
  • output_key (Optional[str, None]) – the key to store ensemble result in the dictionary. +if only 1 key provided in keys, output_key can be None and use keys as default.

  • +
  • weights (Union[Sequence[float], ndarray, Tensor, None]) – can be a list or tuple of numbers for input data with shape: [E, C, H, W[, D]]. +or a Numpy ndarray or a PyTorch Tensor data. +the weights will be added to input data from highest dimension, for example: +1. if the weights only has 1 dimension, it will be added to the E dimension of input data. +2. if the weights has 2 dimensions, it will be added to E and C dimensions. +it’s a typical practice to add weights for different classes: +to ensemble 3 segmentation model outputs, every output has 4 channels(classes), +so the input data shape can be: [3, 4, H, W, D]. +and add different weights for different classes, so the weights shape can be: [3, 4]. +for example: weights = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 1, 1, 1]].

  • +
+
+
+
+ +
+ +
+
+

VoteEnsembled#

+
+
+class monai.transforms.VoteEnsembled(keys, output_key=None, num_classes=None)[source]#
+

Dictionary-based wrapper of monai.transforms.VoteEnsemble.

+
+
+__init__(keys, output_key=None, num_classes=None)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be stack and execute ensemble. +if only 1 key provided, suppose it’s a PyTorch Tensor with data stacked on dimension E.

  • +
  • output_key (Optional[str, None]) – the key to store ensemble result in the dictionary. +if only 1 key provided in keys, output_key can be None and use keys as default.

  • +
  • num_classes (Optional[int, None]) – if the input is single channel data instead of One-Hot, we can’t get class number +from channel, need to explicitly specify the number of classes to vote.

  • +
+
+
+
+ +
+ +
+
+

Invertd#

+
+
+class monai.transforms.Invertd(keys, transform, orig_keys=None, meta_keys=None, orig_meta_keys=None, meta_key_postfix='meta_dict', nearest_interp=True, to_tensor=True, device=None, post_func=None, allow_missing_keys=False)[source]#
+

Utility transform to invert the previously applied transforms.

+

Taking the transform previously applied on orig_keys, this Invertd will apply the inverse of it +to the data stored at keys.

+

Invertd’s output will also include a copy of the metadata +dictionary (originally from orig_meta_keys or the metadata of orig_keys), +with the relevant fields inverted and stored at meta_keys.

+

A typical usage is to apply the inverse of the preprocessing (transform=preprocessings) on +input orig_keys=image to the model predictions keys=pred.

+

A detailed usage example is available in the tutorial: +Project-MONAI/tutorials

+
+

Note

+
    +
  • The output of the inverted data and metadata will be stored at keys and meta_keys respectively.

  • +
  • To correctly invert the transforms, the information of the previously applied transforms should be +available at {orig_keys}_transforms, and the original metadata at orig_meta_keys. +(meta_key_postfix is an optional string to conveniently construct “meta_keys” and/or “orig_meta_keys”.) +see also: monai.transforms.TraceableTransform.

  • +
  • The transform will not change the content in orig_keys and orig_meta_key. +These keys are only used to represent the data status of key before inverting.

  • +
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Any]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, transform, orig_keys=None, meta_keys=None, orig_meta_keys=None, meta_key_postfix='meta_dict', nearest_interp=True, to_tensor=True, device=None, post_func=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – the key of expected data in the dict, the inverse of transforms will be applied on it in-place. +It also can be a list of keys, will apply the inverse transform respectively.

  • +
  • transform (InvertibleTransform) – the transform applied to orig_key, its inverse will be applied on key.

  • +
  • orig_keys (Union[Collection[Hashable], Hashable, None]) – the key of the original input data in the dict. These keys default to self.keys if not set. +the transform trace information of transforms should be stored at {orig_keys}_transforms. +It can also be a list of keys, each matches the keys.

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – The key to output the inverted metadata dictionary. +The metadata is a dictionary optionally containing: filename, original_shape. +It can be a sequence of strings, maps to keys. +If None, will try to create a metadata dict with the default key: {key}_{meta_key_postfix}.

  • +
  • orig_meta_keys (Union[Collection[Hashable], Hashable, None]) – the key of the metadata of original input data. +The metadata is a dictionary optionally containing: filename, original_shape. +It can be a sequence of strings, maps to the keys. +If None, will try to create a metadata dict with the default key: {orig_key}_{meta_key_postfix}. +This metadata dict will also be included in the inverted dict, stored in meta_keys.

  • +
  • meta_key_postfix (str) – if orig_meta_keys is None, use {orig_key}_{meta_key_postfix} to fetch the +metadata from dict, if meta_keys is None, use {key}_{meta_key_postfix}. Default: "meta_dict".

  • +
  • nearest_interp (UnionType[bool, Sequence[bool]]) – whether to use nearest interpolation mode when inverting the spatial transforms, +default to True. If False, use the same interpolation mode as the original transform. +It also can be a list of bool, each matches to the keys data.

  • +
  • to_tensor (UnionType[bool, Sequence[bool]]) – whether to convert the inverted data into PyTorch Tensor first, default to True. +It also can be a list of bool, each matches to the keys data.

  • +
  • device (Union[str, device, Sequence[UnionType[str, device]], None]) – if converted to Tensor, move the inverted results to target device before post_func, +default to None, it also can be a list of string or torch.device, each matches to the keys data.

  • +
  • post_func (Union[Sequence[Callable], Callable, None]) – post processing for the inverted data, should be a callable function. +It also can be a list of callable, each matches to the keys data.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

SaveClassificationd#

+
+
+class monai.transforms.SaveClassificationd(keys, meta_keys=None, meta_key_postfix='meta_dict', saver=None, output_dir='./', filename='predictions.csv', delimiter=',', overwrite=True, flush=True, allow_missing_keys=False)[source]#
+

Save the classification results and metadata into CSV file or other storage.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, meta_keys=None, meta_key_postfix='meta_dict', saver=None, output_dir='./', filename='predictions.csv', delimiter=',', overwrite=True, flush=True, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output, this transform only supports 1 key. +See also: monai.transforms.compose.MapTransform

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. +for example, for data with key image, the metadata by default is in image_meta_dict. +the metadata is a dictionary object which contains: filename, original_shape, etc. +it can be a sequence of string, map to the keys. +if None, will try to construct meta_keys by key_{meta_key_postfix}. +will extract the filename of input image to save classification results.

  • +
  • meta_key_postfix (str) – key_{postfix} was used to store the metadata in LoadImaged. +so need the key to extract the metadata of input image, like filename, etc. default is meta_dict. +for example, for data with key image, the metadata by default is in image_meta_dict. +the metadata is a dictionary object which contains: filename, original_shape, etc. +this arg only works when meta_keys=None. if no corresponding metadata, set to None.

  • +
  • saver (Optional[CSVSaver, None]) – the saver instance to save classification results, if None, create a CSVSaver internally. +the saver must provide save(data, meta_data) and finalize() APIs.

  • +
  • output_dir (Union[str, PathLike]) – if saver=None, specify the directory to save the CSV file.

  • +
  • filename (str) – if saver=None, specify the name of the saved CSV file.

  • +
  • delimiter (str) – the delimiter character in the saved file, default to “,” as the default output type is csv. +to be consistent with: https://docs.python.org/3/library/csv.html#csv.Dialect.delimiter.

  • +
  • overwrite (bool) – if saver=None, indicate whether to overwriting existing CSV file content, if True, +will clear the file before saving. otherwise, will append new content to the CSV file.

  • +
  • flush (bool) – if saver=None, indicate whether to write the cache data to CSV file immediately +in this transform and clear the cache. default to True. +If False, may need user to call saver.finalize() manually or use ClassificationSaver handler.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+get_saver()[source]#
+

If want to write content into file, may need to call finalize of saver when epoch completed. +Or users can also get the cache content from saver instead of writing into file.

+
+ +
+ +
+
+

ProbNMSd#

+
+
+class monai.transforms.ProbNMSd(keys, spatial_dims=2, sigma=0.0, prob_threshold=0.5, box_size=48, allow_missing_keys=False)[source]#
+

Performs probability based non-maximum suppression (NMS) on the probabilities map via +iteratively selecting the coordinate with highest probability and then move it as well +as its surrounding values. The remove range is determined by the parameter box_size. +If multiple coordinates have the same highest probability, only one of them will be +selected.

+
+
Parameters:
+
    +
  • spatial_dims (int) – number of spatial dimensions of the input probabilities map. +Defaults to 2.

  • +
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – the standard deviation for gaussian filter. +It could be a single value, or spatial_dims number of values. Defaults to 0.0.

  • +
  • prob_threshold (float) – the probability threshold, the function will stop searching if +the highest probability is no larger than the threshold. The value should be +no less than 0.0. Defaults to 0.5.

  • +
  • box_size (UnionType[int, Sequence[int]]) – the box size (in pixel) to be removed around the pixel with the maximum probability. +It can be an integer that defines the size of a square or cube, +or a list containing different values for each dimensions. Defaults to 48.

  • +
+
+
Returns:
+

a list of selected lists, where inner lists contain probability and coordinates. +For example, for 3D input, the inner lists are in the form of [probability, x, y, z].

+
+
Raises:
+
    +
  • ValueError – When prob_threshold is less than 0.0.

  • +
  • ValueError – When box_size is a list or tuple, and its length is not equal to spatial_dims.

  • +
  • ValueError – When box_size has a less than 1 value.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

SobelGradientsd#

+
+
+class monai.transforms.SobelGradientsd(keys, kernel_size=3, spatial_axes=None, normalize_kernels=True, normalize_gradients=False, padding_mode='reflect', dtype=torch.float32, new_key_prefix=None, allow_missing_keys=False)[source]#
+

Calculate Sobel horizontal and vertical gradients of a grayscale image.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output.

  • +
  • kernel_size (int) – the size of the Sobel kernel. Defaults to 3.

  • +
  • spatial_axes (Union[Sequence[int], int, None]) – the axes that define the direction of the gradient to be calculated. It calculate the gradient +along each of the provide axis. By default it calculate the gradient for all spatial axes.

  • +
  • normalize_kernels (bool) – if normalize the Sobel kernel to provide proper gradients. Defaults to True.

  • +
  • normalize_gradients (bool) – if normalize the output gradient to 0 and 1. Defaults to False.

  • +
  • padding_mode (str) – the padding mode of the image when convolving with Sobel kernels. Defaults to “reflect”. +Acceptable values are 'zeros', 'reflect', 'replicate' or 'circular'. +See torch.nn.Conv1d() for more information.

  • +
  • dtype (dtype) – kernel data type (torch.dtype). Defaults to torch.float32.

  • +
  • new_key_prefix (Optional[str, None]) – this prefix be prepended to the key to create a new key for the output and keep the value of +key intact. By default not prefix is set and the corresponding array to the key will be replaced.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+
+

Spatial (Dict)#

+
+

SpatialResampled#

+
+
+class monai.transforms.SpatialResampled(keys, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, dst_keys='dst_affine', allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SpatialResample.

+

This transform assumes the data dictionary has a key for the input +data’s metadata and contains src_affine and dst_affine required by +SpatialResample. The key is formed by key_{meta_key_postfix}. The +transform will swap src_affine and dst_affine affine (with potential data type +changes) in the dictionary so that src_affine always refers to the current +status of affine.

+ +
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, dst_keys='dst_affine', allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • align_corners (UnionType[Sequence[bool], bool]) – Geometrically, we consider the pixels of the input as squares rather than points. +See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample +It also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None]], dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32. +It also can be a sequence of dtypes, each element corresponds to a key in keys.

  • +
  • dst_keys (Union[Collection[Hashable], Hashable, None]) – the key of the corresponding dst_affine in the metadata dictionary.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

ResampleToMatchd#

+
+
+class monai.transforms.ResampleToMatchd(keys, key_dst, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ResampleToMatch.

+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, key_dst, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • key_dst (str) – key of image to resample to match.

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • align_corners (UnionType[Sequence[bool], bool]) – Geometrically, we consider the pixels of the input as squares rather than points. +See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample +It also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None]], dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32. +It also can be a sequence of dtypes, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Spacingd#

+example of Spacingd +
+
+class monai.transforms.Spacingd(keys, pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, ensure_same_shape=True, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Spacing.

+

This transform assumes the data dictionary has a key for the input +data’s metadata and contains affine field. The key is formed by key_{meta_key_postfix}.

+

After resampling the input array, this transform will write the new affine +to the affine field of metadata which is formed by key_{meta_key_postfix}.

+ +
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, ensure_same_shape=True, allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • pixdim (UnionType[Sequence[float], float]) – output voxel spacing. if providing a single number, will use it for the first dimension. +items of the pixdim sequence map to the spatial dimensions of input image, if length +of pixdim sequence is longer than image spatial dimensions, will ignore the longer part, +if shorter, will pad with 1.0. +if the components of the pixdim are non-positive values, the transform will use the +corresponding components of the original pixdim, which is computed from the affine +matrix of input image.

  • +
  • diagonal (bool) –

    whether to resample the input to have a diagonal affine matrix. +If True, the input data is resampled to the following affine:

    +
    np.diag((pixdim_0, pixdim_1, pixdim_2, 1))
    +
    +
    +

    This effectively resets the volume to the world coordinate system (RAS+ in nibabel). +The original orientation, rotation, shearing are not preserved.

    +

    If False, the axes orientation, orthogonal rotation and +translations components from the original affine will be +preserved in the target affine. This option will not flip/swap +axes against the original ones.

    +

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • align_corners (UnionType[Sequence[bool], bool]) – Geometrically, we consider the pixels of the input as squares rather than points. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +It also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None]], dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32. +It also can be a sequence of dtypes, each element corresponds to a key in keys.

  • +
  • scale_extent (bool) – whether the scale is computed based on the spacing or the full extent of voxels, +default False. The option is ignored if output spatial size is specified when calling this transform. +See also: monai.data.utils.compute_shape_offset(). When this is True, align_corners +should be True because compute_shape_offset already provides the corner alignment shift/scaling.

  • +
  • recompute_affine (bool) – whether to recompute affine based on the output shape. The affine computed +analytically does not reflect the potential quantization errors in terms of the output shape. +Set this flag to True to recompute the output affine based on the actual pixdim. Default to False.

  • +
  • min_pixdim (Union[Sequence[float], float, None]) – minimal input spacing to be resampled. If provided, input image with a larger spacing than this +value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the +value of pixdim. Default to None.

  • +
  • max_pixdim (Union[Sequence[float], float, None]) – maximal input spacing to be resampled. If provided, input image with a smaller spacing than this +value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the +value of pixdim. Default to None.

  • +
  • ensure_same_shape (bool) – when the inputs have the same spatial shape, and almost the same pixdim, +whether to ensure exactly the same output spatial shape. Default to True.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Orientationd#

+example of Orientationd +
+
+class monai.transforms.Orientationd(keys, axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Orientation.

+

This transform assumes the channel-first input format. +In the case of using this transform for normalizing the orientations of images, +it should be used before any anisotropic spatial transforms.

+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • axcodes (Optional[str, None]) – N elements sequence for spatial ND input’s orientation. +e.g. axcodes=’RAS’ represents 3D orientation: +(Left, Right), (Posterior, Anterior), (Inferior, Superior). +default orientation labels options are: ‘L’ and ‘R’ for the first dimension, +‘P’ and ‘A’ for the second, ‘I’ and ‘S’ for the third.

  • +
  • as_closest_canonical (bool) – if True, load the image as closest to canonical axis format.

  • +
  • labels (UnionType[Sequence[tuple[str, str]], None]) – optional, None or sequence of (2,) sequences +(2,) sequences are labels for (beginning, end) of output axis. +Defaults to (('L', 'R'), ('P', 'A'), ('I', 'S')).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+

See also

+

nibabel.orientations.ornt2axcodes.

+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Flipd#

+example of Flipd +
+
+class monai.transforms.Flipd(keys, spatial_axis=None, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Flip.

+

See numpy.flip for additional details. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • spatial_axis (Union[Sequence[int], int, None]) – Spatial axes along which to flip over. Default is None.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandFlipd#

+example of RandFlipd +
+
+class monai.transforms.RandFlipd(keys, prob=0.1, spatial_axis=None, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandFlip.

+

See numpy.flip for additional details. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • prob (float) – Probability of flipping.

  • +
  • spatial_axis (Union[Sequence[int], int, None]) – Spatial axes along which to flip over. Default is None.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandFlipd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandAxisFlipd#

+example of RandAxisFlipd +
+
+class monai.transforms.RandAxisFlipd(keys, prob=0.1, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandAxisFlip.

+

See numpy.flip for additional details. +https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • prob (float) – Probability of flipping.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandAxisFlipd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

Rotated#

+example of Rotated +
+
+class monai.transforms.Rotated(keys, angle, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Rotate.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • angle (UnionType[Sequence[float], float]) – Rotation angle(s) in radians.

  • +
  • keep_size (bool) – If it is False, the output shape is adapted so that the +input array is contained completely in the output. +If it is True, the output shape is the same as the input. Default is True.

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • align_corners (UnionType[Sequence[bool], bool]) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +It also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32. +It also can be a sequence of dtype or None, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandRotated#

+example of RandRotated +
+
+class monai.transforms.RandRotated(keys, range_x=0.0, range_y=0.0, range_z=0.0, prob=0.1, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandRotate +Randomly rotates the input arrays.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • range_x (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and second axes. +If single number, angle is uniformly sampled from (-range_x, range_x).

  • +
  • range_y (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and third axes. +If single number, angle is uniformly sampled from (-range_y, range_y). only work for 3D data.

  • +
  • range_z (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the second and third axes. +If single number, angle is uniformly sampled from (-range_z, range_z). only work for 3D data.

  • +
  • prob (float) – Probability of rotation.

  • +
  • keep_size (bool) – If it is False, the output shape is adapted so that the +input array is contained completely in the output. +If it is True, the output shape is the same as the input. Default is True.

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • align_corners (UnionType[Sequence[bool], bool]) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float64 for best precision. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32. +It also can be a sequence of dtype or None, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandRotated

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

Zoomd#

+example of Zoomd +
+
+class monai.transforms.Zoomd(keys, zoom, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=<class 'numpy.float32'>, keep_size=True, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.Zoom.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • zoom (UnionType[Sequence[float], float]) – The zoom factor along the spatial axes. +If a float, zoom is the same for each spatial axis. +If a sequence, zoom should contain one value for each spatial axis.

  • +
  • mode (Union[Sequence[str], str]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to "area". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "edge". +The mode to pad data after zooming. +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • align_corners (Union[Sequence[UnionType[bool, None]], bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of bool or None, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data.

  • +
  • keep_size (bool) – Should keep original size (pad if needed), default is True.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
  • kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandZoomd#

+example of RandZoomd +
+
+class monai.transforms.RandZoomd(keys, prob=0.1, min_zoom=0.9, max_zoom=1.1, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=<class 'numpy.float32'>, keep_size=True, allow_missing_keys=False, lazy=False, **kwargs)[source]#
+

Dict-based version monai.transforms.RandZoom.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • +
  • prob (float) – Probability of zooming.

  • +
  • min_zoom (UnionType[Sequence[float], float]) – Min zoom factor. Can be float or sequence same size as image. +If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims +to keep the original spatial shape ratio. +If a sequence, min_zoom should contain one value for each spatial axis. +If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • +
  • max_zoom (UnionType[Sequence[float], float]) – Max zoom factor. Can be float or sequence same size as image. +If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims +to keep the original spatial shape ratio. +If a sequence, max_zoom should contain one value for each spatial axis. +If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • +
  • mode (Union[Sequence[str], str]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to "area". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. Defaults to "edge". +The mode to pad data after zooming. +See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • +
  • align_corners (Union[Sequence[UnionType[bool, None]], bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of bool or None, each element corresponds to a key in keys.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data.

  • +
  • keep_size (bool) – Should keep original size (pad if needed), default is True.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
  • kwargs – other args for np.pad API, note that np.pad treats channel dimension as the first dimension. +more details: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandZoomd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

GridPatchd#

+
+
+class monai.transforms.GridPatchd(keys, patch_size, offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, allow_missing_keys=False, **pad_kwargs)[source]#
+

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps. +It can sort the patches and return all or a subset of them.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • +
  • offset (Optional[Sequence[int], None]) – starting position in the array, default is 0 for each dimension. +np.random.randint(0, patch_size, 2) creates random start between 0 and patch_size for a 2D image.

  • +
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. +If the requested number of patches is greater than the number of available patches, +padding will be applied to provide exactly num_patches patches unless threshold is set. +When threshold is set, this value is treated as the maximum number of patches. +Defaults to None, which does not limit number of the patches.

  • +
  • overlap (float) – amount of overlap between patches in each dimension. Default to 0.0.

  • +
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), +lowest values (“min”), or in their default order (None). Default to None.

  • +
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. +Defaults to no filtering.

  • +
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. +Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. +Defaults to None, which means no padding will be applied. +See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +requires pytorch >= 1.10 for best compatibility.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Returns:
+

+
dictionary, contains the all the original key/value with the values for keys

replaced by the patches, a MetaTensor with following metadata:

+
    +
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • +
  • PatchKeys.COUNT: total number of patches in the image,

  • +
  • ”spatial_shape”: spatial size of the extracted patch, and

  • +
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • +
+
+
+

+
+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+ +
+
+

RandGridPatchd#

+
+
+class monai.transforms.RandGridPatchd(keys, patch_size, min_offset=None, max_offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, allow_missing_keys=False, **pad_kwargs)[source]#
+

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps, +and with random offset for the minimal corner of the image, (0,0) for 2D and (0,0,0) for 3D. +It can sort the patches and return all or a subset of them.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • +
  • min_offset (Union[Sequence[int], int, None]) – the minimum range of starting position to be selected randomly. Defaults to 0.

  • +
  • max_offset (Union[Sequence[int], int, None]) – the maximum range of starting position to be selected randomly. +Defaults to image size modulo patch size.

  • +
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. +If the requested number of patches is greater than the number of available patches, +padding will be applied to provide exactly num_patches patches unless threshold is set. +When threshold is set, this value is treated as the maximum number of patches. +Defaults to None, which does not limit number of the patches.

  • +
  • overlap (float) – the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). +If only one float number is given, it will be applied to all dimensions. Defaults to 0.0.

  • +
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), +lowest values (“min”), or in their default order (None). Default to None.

  • +
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. +Defaults to no filtering.

  • +
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. +Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", +"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} +(PyTorch) {"constant", "reflect", "replicate", "circular"}. +One of the listed string values or a user supplied function. +Defaults to None, which means no padding will be applied. +See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html +https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html +requires pytorch >= 1.10 for best compatibility.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • pad_kwargs – other arguments for the np.pad or torch.pad function. +note that np.pad treats channel dimension as the first dimension.

  • +
+
+
Returns:
+

+
dictionary, contains the all the original key/value with the values for keys

replaced by the patches, a MetaTensor with following metadata:

+
    +
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • +
  • PatchKeys.COUNT: total number of patches in the image,

  • +
  • ”spatial_shape”: spatial size of the extracted patch, and

  • +
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • +
+
+
+

+
+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandGridPatchd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

GridSplitd#

+
+
+class monai.transforms.GridSplitd(keys, grid=(2, 2), size=None, allow_missing_keys=False)[source]#
+

Split the image into patches based on the provided grid in 2D.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • grid (tuple[int, int]) – a tuple define the shape of the grid upon which the image is split. Defaults to (2, 2)

  • +
  • size (Union[int, tuple[int, int], dict[Hashable, UnionType[int, tuple[int, int], None]], None]) – a tuple or an integer that defines the output patch sizes, +or a dictionary that define it separately for each key, like {“image”: 3, “mask”, (2, 2)}. +If it’s an integer, the value will be repeated for each dimension. +The default is None, where the patch size will be inferred from the grid shape.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+

Note: This transform currently support only image with two spatial dimensions.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

list[dict[Hashable, Union[ndarray, Tensor]]]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+ +
+
+

RandRotate90d#

+example of RandRotate90d +
+
+class monai.transforms.RandRotate90d(keys, prob=0.1, max_k=3, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based version monai.transforms.RandRotate90. +With probability prob, input arrays are rotated by 90 degrees +in the plane specified by spatial_axes.

+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

Mapping[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, prob=0.1, max_k=3, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • prob (float) – probability of rotating. +(Default 0.1, with 10% probability it returns a rotated array.)

  • +
  • max_k (int) – number of rotations will be sampled from np.random.randint(max_k) + 1. +(Default 3)

  • +
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. +Default: (0, 1), this is the first two axis in spatial dimensions.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

Rotate90d#

+example of Rotate90d +
+
+class monai.transforms.Rotate90d(keys, k=1, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Rotate90.

+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, k=1, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • k (int) – number of times to rotate by 90 degrees.

  • +
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. +Default: (0, 1), this is the first two axis in spatial dimensions.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Resized#

+example of Resized +
+
+class monai.transforms.Resized(keys, spatial_size, size_mode='all', mode=InterpolateMode.AREA, align_corners=None, anti_aliasing=False, anti_aliasing_sigma=None, dtype=<class 'numpy.float32'>, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Resize.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • spatial_size (UnionType[Sequence[int], int]) – expected shape of spatial dimensions after resize operation. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • size_mode (str) – should be “all” or “longest”, if “all”, will use spatial_size for all the spatial dims, +if “longest”, rescale the image so that only the longest side is equal to specified spatial_size, +which must be an int number in this case, keeping the aspect ratio of the initial image, refer to: +https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ +#albumentations.augmentations.geometric.resize.LongestMaxSize.

  • +
  • mode (Union[Sequence[str], str]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} +The interpolation mode. Defaults to "area". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • align_corners (Union[Sequence[UnionType[bool, None]], bool, None]) – This only has an effect when mode is +‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html +It also can be a sequence of bool or None, each element corresponds to a key in keys.

  • +
  • anti_aliasing (UnionType[Sequence[bool], bool]) – bool +Whether to apply a Gaussian filter to smooth the image prior +to downsampling. It is crucial to filter when downsampling +the image to avoid aliasing artifacts. See also skimage.transform.resize

  • +
  • anti_aliasing_sigma (Union[Sequence[UnionType[Sequence[float], float, None]], Sequence[float], float, None]) – {float, tuple of floats}, optional +Standard deviation for Gaussian filtering used when anti-aliasing. +By default, this value is chosen as (s - 1) / 2 where s is the +downsampling factor, where s > 1. For the up-size case, s < 1, no +anti-aliasing is performed prior to rescaling.

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

Affined#

+example of Affined +
+
+class monai.transforms.Affined(keys, rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, dtype=<class 'numpy.float32'>, align_corners=False, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Affine.

+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, dtype=<class 'numpy.float32'>, align_corners=False, allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • rotate_params (Union[Sequence[float], float, None]) – a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. +Defaults to no rotation.

  • +
  • shear_params (Union[Sequence[float], float, None]) –

    shearing factors for affine matrix, take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +a tuple of 2 floats for 2D, a tuple of 6 floats for 3D. Defaults to no shearing.
    +
    +
    +

  • +
  • translate_params (Union[Sequence[float], float, None]) – a tuple of 2 floats for 2D, a tuple of 3 floats for 3D. Translation is in +pixel/voxel relative to the center of the input image. Defaults to no translation.

  • +
  • scale_params (Union[Sequence[float], float, None]) – scale factor for every spatial dims. a tuple of 2 floats for 2D, +a tuple of 3 floats for 3D. Defaults to 1.0.

  • +
  • affine (Union[ndarray, Tensor, None]) – if applied, ignore the params (rotate_params, etc.) and use the +supplied matrix. Should be square with each side = num of image spatial +dimensions + 1.

  • +
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. +If None, use the data type of input data. To be compatible with other modules, +the output data type is always float32.

  • +
  • align_corners (bool) – Defaults to False. +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+

See also

+
    +
  • monai.transforms.compose.MapTransform

  • +
  • RandAffineGrid for the random affine parameters configurations.

  • +
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+ +
+
+

RandAffined#

+example of RandAffined +
+
+class monai.transforms.RandAffined(keys, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, allow_missing_keys=False, lazy=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RandAffine.

+
+
+__call__(data, lazy=None)[source]#
+
+
Parameters:
+
    +
  • data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

  • +
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not +during this call. Setting this to False or True overrides the lazy flag set +during initialization for this call. Defaults to None.

  • +
+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, allow_missing_keys=False, lazy=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of returning a randomized affine grid. +defaults to 0.1, with 10% chance returns a randomized grid.

  • +
  • rotate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 2 floats for 2D, a tuple of 6 floats for 3D) for affine matrix, +take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select pixel/voxel to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • cache_grid (bool) – whether to cache the identity sampling grid. +If the spatial size is not dynamically defined by input image, enabling this option could +accelerate the transform.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. +Defaults to False

  • +
+
+
+
+

See also

+
    +
  • monai.transforms.compose.MapTransform

  • +
  • RandAffineGrid for the random affine parameters configurations.

  • +
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+property lazy#
+

Get whether lazy evaluation is enabled for this transform instance. +:returns: True if the transform is operating in a lazy fashion, False if not.

+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandAffined

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

Rand2DElasticd#

+example of Rand2DElasticd +
+
+class monai.transforms.Rand2DElasticd(keys, spacing, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Rand2DElastic.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, spacing, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • spacing (UnionType[tuple[float, float], float]) – distance in between the control points.

  • +
  • magnitude_range (tuple[float, float]) – 2 int numbers, the random offsets will be generated from +uniform[magnitude[0], magnitude[1]).

  • +
  • spatial_size (Union[int, tuple[int, int], None]) – specifying output image spatial size [h, w]. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, -1) will be adapted +to (32, 64) if the second spatial dimension size of img is 64.

  • +
  • prob (float) – probability of returning a randomized affine grid. +defaults to 0.1, with 10% chance returns a randomized grid, +otherwise returns a spatial_size centered area extracted from the input image.

  • +
  • rotate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 2 floats for 2D) for affine matrix, take a 2D affine as example:

    +
    [
    +    [1.0, params[0], 0.0],
    +    [params[1], 1.0, 0.0],
    +    [0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select pixel to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+

See also

+
    +
  • RandAffineGrid for the random affine parameters configurations.

  • +
  • Affine for the affine transformation parameters configurations.

  • +
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Rand2DElasticd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

Rand3DElasticd#

+example of Rand3DElasticd +
+
+class monai.transforms.Rand3DElasticd(keys, sigma_range, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Rand3DElastic.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, sigma_range, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • sigma_range (tuple[float, float]) – a Gaussian kernel with standard deviation sampled from +uniform[sigma_range[0], sigma_range[1]) will be used to smooth the random offset grid.

  • +
  • magnitude_range (tuple[float, float]) – the random offsets on the grid will be generated from +uniform[magnitude[0], magnitude[1]).

  • +
  • spatial_size (Union[tuple[int, int, int], int, None]) – specifying output image spatial size [h, w, d]. +if spatial_size and self.spatial_size are not defined, or smaller than 1, +the transform will use the spatial size of img. +if some components of the spatial_size are non-positive values, the transform will use the +corresponding components of img size. For example, spatial_size=(32, 32, -1) will be adapted +to (32, 32, 64) if the third spatial dimension size of img is 64.

  • +
  • prob (float) – probability of returning a randomized affine grid. +defaults to 0.1, with 10% chance returns a randomized grid, +otherwise returns a spatial_size centered area extracted from the input image.

  • +
  • rotate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then +uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter +for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. +This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be +in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] +for dim0 and nothing for the remaining dimensions.

  • +
  • shear_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select +shearing factors(a tuple of 6 floats for 3D) for affine matrix, take a 3D affine as example:

    +
    [
    +    [1.0, params[0], params[1], 0.0],
    +    [params[2], 1.0, params[3], 0.0],
    +    [params[4], params[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • translate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly +select voxel to translate for every spatial dims.

  • +
  • scale_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select +the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. +This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • +
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "reflection". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+

See also

+
    +
  • RandAffineGrid for the random affine parameters configurations.

  • +
  • Affine for the affine transformation parameters configurations.

  • +
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

Rand3DElasticd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

GridDistortiond#

+example of GridDistortiond +
+
+class monai.transforms.GridDistortiond(keys, num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.GridDistortion.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • num_cells (UnionType[tuple[int], int]) – number of grid cells on each dimension.

  • +
  • distort_steps (list[tuple]) – This argument is a list of tuples, where each tuple contains the distort steps of the +corresponding dimensions (in the order of H, W[, D]). The length of each tuple equals to num_cells + 1. +Each value in the tuple represents the distort step of the related cell.

  • +
  • mode (str) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

RandGridDistortiond#

+example of RandGridDistortiond +
+
+class monai.transforms.RandGridDistortiond(keys, num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RandGridDistortion.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified +in this dictionary must be tensor like arrays that are channel first and have at most +three spatial dimensions

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

a dictionary containing the transformed data, as well as any other data present in the dictionary

+
+
+
+ +
+
+__init__(keys, num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • +
  • num_cells (UnionType[tuple[int], int]) – number of grid cells on each dimension.

  • +
  • prob (float) – probability of returning a randomized grid distortion transform. Defaults to 0.1.

  • +
  • distort_limit (UnionType[tuple[float, float], float]) – range to randomly distort. +If single number, distort_limit is picked from (-distort_limit, distort_limit). +Defaults to (-0.03, 0.03).

  • +
  • mode (str) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). +Interpolation mode to calculate output values. Defaults to "bilinear". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used +and the value represents the order of the spline interpolation. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • padding_mode (str) – {"zeros", "border", "reflection"} +Padding mode for outside grid values. Defaults to "border". +See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html +When mode is an integer, using numpy/cupy backends, this argument accepts +{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. +See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html +It also can be a sequence, each element corresponds to a key in keys.

  • +
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandGridDistortiond

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+
+

Smooth Field (Dict)#

+
+

RandSmoothFieldAdjustContrastd#

+example of RandSmoothFieldAdjustContrastd +
+
+class monai.transforms.RandSmoothFieldAdjustContrastd(keys, spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.5, 4.5), device=None)[source]#
+

Dictionary version of RandSmoothFieldAdjustContrast.

+

The field is randomized once per invocation by default so the same field is applied to every selected key. The +mode parameter specifying interpolation mode for the field can be a single value or a sequence of values with +one for each key in keys.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – key names to apply the augment to

  • +
  • spatial_size (Sequence[int]) – size of input arrays, all arrays stated in keys must have same dimensions

  • +
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • +
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • +
  • mode (Union[Sequence[str], str]) – interpolation mode to use when upsampling

  • +
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • +
  • prob (float) – probability transform is applied

  • +
  • gamma (UnionType[Sequence[float], float]) – (min, max) range for exponential field

  • +
  • device (Optional[device, None]) – Pytorch device to define field on

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Mapping[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandSmoothFieldAdjustContrastd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandSmoothFieldAdjustIntensityd#

+example of RandSmoothFieldAdjustIntensityd +
+
+class monai.transforms.RandSmoothFieldAdjustIntensityd(keys, spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.1, 1.0), device=None)[source]#
+

Dictionary version of RandSmoothFieldAdjustIntensity.

+

The field is randomized once per invocation by default so the same field is applied to every selected key. The +mode parameter specifying interpolation mode for the field can be a single value or a sequence of values with +one for each key in keys.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – key names to apply the augment to

  • +
  • spatial_size (Sequence[int]) – size of input arrays, all arrays stated in keys must have same dimensions

  • +
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • +
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • +
  • mode (Union[Sequence[str], str]) – interpolation mode to use when upsampling

  • +
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • +
  • prob (float) – probability transform is applied

  • +
  • gamma (UnionType[Sequence[float], float]) – (min, max) range of intensity multipliers

  • +
  • device (Optional[device, None]) – Pytorch device to define field on

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Mapping[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandSmoothFieldAdjustIntensityd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+

RandSmoothDeformd#

+example of RandSmoothDeformd +
+
+class monai.transforms.RandSmoothDeformd(keys, spatial_size, rand_size, pad=0, field_mode=InterpolateMode.AREA, align_corners=None, prob=0.1, def_range=1.0, grid_dtype=torch.float32, grid_mode=GridSampleMode.NEAREST, grid_padding_mode=GridSamplePadMode.BORDER, grid_align_corners=False, device=None)[source]#
+

Dictionary version of RandSmoothDeform.

+

The field is randomized once per invocation by default so the same field is applied to every selected key. The +field_mode parameter specifying interpolation mode for the field can be a single value or a sequence of values +with one for each key in keys. Similarly the grid_mode parameter can be one value or one per key.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – key names to apply the augment to

  • +
  • spatial_size (Sequence[int]) – input array size to which deformation grid is interpolated

  • +
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • +
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • +
  • field_mode (Union[Sequence[str], str]) – interpolation mode to use when upsampling the deformation field

  • +
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • +
  • prob (float) – probability transform is applied

  • +
  • def_range (UnionType[Sequence[float], float]) – value of the deformation range in image size fractions

  • +
  • grid_dtype – type for the deformation grid calculated from the field

  • +
  • grid_mode (Union[Sequence[str], str]) – interpolation mode used for sampling input using deformation grid

  • +
  • grid_padding_mode (str) – padding mode used for sampling input using deformation grid

  • +
  • grid_align_corners (UnionType[bool, None]) – if True align the corners when sampling the deformation grid

  • +
  • device (Optional[device, None]) – Pytorch device to define field on

  • +
+
+
+
+
+__call__(data)[source]#
+

data is an element which often comes from an iteration over an +iterable, such as torch.utils.data.Dataset. This method should +return an updated version of data. +To simplify the input validations, most of the transforms assume that

+
    +
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • +
  • the data shape can be:

    +
      +
    1. string data without shape, LoadImage transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+

This method can optionally take additional arguments to help execute transformation operation.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

Mapping[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+randomize(data=None)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Return type:
+

None

+
+
+
+ +
+
+set_random_state(seed=None, state=None)[source]#
+

Set the random state locally, to control the randomness, the derived +classes should use self.R instead of np.random to introduce random +factors.

+
+
Parameters:
+
    +
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • +
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • +
+
+
Raises:
+

TypeError – When state is not an Optional[np.random.RandomState].

+
+
Return type:
+

RandSmoothDeformd

+
+
Returns:
+

a Randomizable instance.

+
+
+
+ +
+ +
+
+
+

MRI transforms (Dict)#

+
+

Kspace under-sampling (Dict)#

+
+
+class monai.apps.reconstruction.transforms.dictionary.RandomKspaceMaskd(keys, center_fractions, accelerations, spatial_dims=2, is_complex=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.apps.reconstruction.transforms.array.RandomKspacemask. +Other mask transforms can inherit from this class, for example: +monai.apps.reconstruction.transforms.dictionary.EquispacedKspaceMaskd.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • center_fractions (Sequence[float]) – Fraction of low-frequency columns to be retained. +If multiple values are provided, then one of these numbers is +chosen uniformly each time.

  • +
  • accelerations (Sequence[float]) – Amount of under-sampling. This should have the +same length as center_fractions. If multiple values are provided, +then one of these is chosen uniformly each time.

  • +
  • spatial_dims (int) – Number of spatial dims (e.g., it’s 2 for a 2D data; it’s +also 2 for pseudo-3D datasets like the fastMRI dataset). +The last spatial dim is selected for sampling. For the fastMRI +dataset, k-space has the form (…,num_slices,num_coils,H,W) +and sampling is done along W. For a general 3D data with the +shape (…,num_coils,H,W,D), sampling is done along D.

  • +
  • is_complex (bool) – if True, then the last dimension will be reserved +for real/imaginary parts.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from the +loaded dataset

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

the new data dictionary

+
+
+
+ +
+ +
+
+class monai.apps.reconstruction.transforms.dictionary.EquispacedKspaceMaskd(keys, center_fractions, accelerations, spatial_dims=2, is_complex=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of +monai.apps.reconstruction.transforms.array.EquispacedKspaceMask.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • center_fractions (Sequence[float]) – Fraction of low-frequency columns to be retained. +If multiple values are provided, then one of these numbers is +chosen uniformly each time.

  • +
  • accelerations (Sequence[float]) – Amount of under-sampling. This should have the same +length as center_fractions. If multiple values are provided, +then one of these is chosen uniformly each time.

  • +
  • spatial_dims (int) – Number of spatial dims (e.g., it’s 2 for a 2D data; +it’s also 2 for pseudo-3D datasets like the fastMRI dataset). +The last spatial dim is selected for sampling. For the fastMRI +dataset, k-space has the form (…,num_slices,num_coils,H,W) +and sampling is done along W. For a general 3D data with the shape +(…,num_coils,H,W,D), sampling is done along D.

  • +
  • is_complex (bool) – if True, then the last dimension will be reserved +for real/imaginary parts.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from the +loaded dataset

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

the new data dictionary

+
+
+
+ +
+ +
+
+

ExtractDataKeyFromMetaKeyd#

+
+
+class monai.apps.reconstruction.transforms.dictionary.ExtractDataKeyFromMetaKeyd(keys, meta_key, allow_missing_keys=False)[source]#
+

Moves keys from meta to data. It is useful when a dataset of paired samples +is loaded and certain keys should be moved from meta to data.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys to be transferred from meta to data

  • +
  • meta_key (str) – the meta key where all the meta-data is stored

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing

  • +
+
+
+

Example

+

When the fastMRI dataset is loaded, “kspace” is stored in the data dictionary, +but the ground-truth image with the key “reconstruction_rss” is stored in the meta data. +In this case, ExtractDataKeyFromMetaKeyd moves “reconstruction_rss” to data.

+
+
+__call__(data)[source]#
+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from the +loaded dataset

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

the new data dictionary

+
+
+
+ +
+ +
+
+

ReferenceBasedSpatialCropd#

+
+
+class monai.apps.reconstruction.transforms.dictionary.ReferenceBasedSpatialCropd(keys, ref_key, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SpatialCrop. +This is similar to monai.transforms.SpatialCropd which is a +general purpose cropper to produce sub-volume region of interest (ROI). +Their difference is that this transform does cropping according to a reference image.

+

If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • ref_key (str) – key of the item to be used to crop items of “keys”

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+

Example

+

In an image reconstruction task, let keys=[“image”] and ref_key=[“target”]. +Also, let data be the data dictionary. Then, ReferenceBasedSpatialCropd +center-crops data[“image”] based on the spatial size of data[“target”] by +calling monai.transforms.SpatialCrop.

+
+
+__call__(data, lazy=None)[source]#
+

This transform can support to crop ND spatial (channel-first) data. +It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D +data point where C is the number of slices)

+
+
Parameters:
+

data (Mapping[Hashable, Tensor]) – is a dictionary containing (key,value) pairs from +the loaded dataset

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

the new data dictionary

+
+
+
+ +
+ +
+
+

ReferenceBasedNormalizeIntensityd#

+
+
+class monai.apps.reconstruction.transforms.dictionary.ReferenceBasedNormalizeIntensityd(keys, ref_key, subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of +monai.transforms.NormalizeIntensity. +This is similar to monai.transforms.NormalizeIntensityd +and can normalize non-zero values or the entire image. The difference +is that this transform does normalization according to a reference image.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • ref_key (str) – key of the item to be used to normalize items of “keys”

  • +
  • subtrahend (Union[ndarray, Tensor, None]) – the amount to subtract by (usually the mean)

  • +
  • divisor (Union[ndarray, Tensor, None]) – the amount to divide by (usually the standard deviation)

  • +
  • nonzero (bool) – whether only normalize non-zero values.

  • +
  • channel_wise (bool) – if True, calculate on each channel separately, +otherwise, calculate on the entire image directly. default +to False.

  • +
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults +to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+

Example

+

In an image reconstruction task, let keys=[“image”, “target”] and ref_key=[“image”]. +Also, let data be the data dictionary. Then, ReferenceBasedNormalizeIntensityd +normalizes data[“target”] and data[“image”] based on the mean-std of data[“image”] by +calling monai.transforms.NormalizeIntensity.

+
+
+__call__(data)[source]#
+

This transform can support to normalize ND spatial (channel-first) data. +It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D +data point where C is the number of slices)

+
+
Parameters:
+

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from +the loaded dataset

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

the new data dictionary

+
+
+
+ +
+ +
+
+
+

Lazy (Dict)#

+
+

ApplyPendingd#

+
+
+class monai.transforms.ApplyPendingd(keys)[source]#
+

ApplyPendingd can be inserted into a pipeline that is being executed lazily in order +to ensure resampling happens before the next transform. It doesn’t do anything itself, +but its presence causes the pipeline to be executed as it doesn’t implement LazyTrait

+

See Compose for a detailed explanation of the lazy resampling feature.

+
+
Parameters:
+

keys (Union[Collection[Hashable], Hashable]) – the keys for tensors that should have their pending transforms executed

+
+
+
+
+__call__(data)[source]#
+

Call self as a function.

+
+ +
+ +
+
+
+

Utility (Dict)#

+
+

Identityd#

+
+
+class monai.transforms.Identityd(keys, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Identity.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

AsChannelFirstd#

+
+
+class monai.transforms.AsChannelFirstd(keys, channel_dim=-1, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.AsChannelFirst.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, channel_dim=-1, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • channel_dim (int) – which dimension of input image is the channel, default is the last dimension.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

AsChannelLastd#

+
+
+class monai.transforms.AsChannelLastd(keys, channel_dim=0, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.AsChannelLast.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, channel_dim=0, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • channel_dim (int) – which dimension of input image is the channel, default is the first dimension.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

AddChanneld#

+
+
+class monai.transforms.AddChanneld(keys, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.AddChannel.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

EnsureChannelFirstd#

+
+
+class monai.transforms.EnsureChannelFirstd(keys, meta_keys=None, meta_key_postfix='meta_dict', strict_check=True, allow_missing_keys=False, channel_dim=None)[source]#
+

Dictionary-based wrapper of monai.transforms.EnsureChannelFirst.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, meta_keys=None, meta_key_postfix='meta_dict', strict_check=True, allow_missing_keys=False, channel_dim=None)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • strict_check (bool) – whether to raise an error when the meta information is insufficient.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • channel_dim – This argument can be used to specify the original channel dimension (integer) of the input array. +It overrides the original_channel_dim from provided MetaTensor input. +If the input array doesn’t have a channel dim, this value should be 'no_channel'. +If this is set to None, this class relies on img or meta_dict to provide the channel dimension.

  • +
+
+
+
+ +
+ +
+
+

RepeatChanneld#

+
+
+class monai.transforms.RepeatChanneld(keys, repeats, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RepeatChannel.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, repeats, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • repeats (int) – the number of repetitions for each element.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

SplitDimd#

+
+
+class monai.transforms.SplitDimd(keys, output_postfixes=None, dim=0, keepdim=True, update_meta=True, list_output=False, allow_missing_keys=False)[source]#
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

UnionType[dict[Hashable, Tensor], list[dict[Hashable, Tensor]]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, output_postfixes=None, dim=0, keepdim=True, update_meta=True, list_output=False, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • output_postfixes (Optional[Sequence[str], None]) – the postfixes to construct keys to store split data. +for example: if the key of input data is pred and split 2 classes, the output +data keys will be: pred_(output_postfixes[0]), pred_(output_postfixes[1]) +if None, using the index number: pred_0, pred_1, … pred_N.

  • +
  • dim (int) – which dimension of input image is the channel, default to 0.

  • +
  • keepdim (bool) – if True, output will have singleton in the split dimension. If False, this +dimension will be squeezed.

  • +
  • update_meta (bool) – if True, copy [key]_meta_dict for each output and update affine to +reflect the cropped image

  • +
  • list_output (bool) – it True, the output will be a list of dictionaries with the same keys as original.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

SplitChanneld#

+
+
+class monai.transforms.SplitChanneld(keys, output_postfixes=None, channel_dim=0, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SplitChannel. +All the input specified by keys should be split into same count of data.

+
+ +
+
+

CastToTyped#

+
+
+class monai.transforms.CastToTyped(keys, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.CastToType.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – convert image to this data type, default is np.float32. +it also can be a sequence of dtypes or torch.dtype, +each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

ToTensord#

+
+
+class monai.transforms.ToTensord(keys, dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ToTensor.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • dtype (Optional[dtype, None]) – target data content type to convert, for example: torch.float, etc.

  • +
  • device (Optional[device, None]) – specify the target device to put the Tensor data.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • +
  • track_meta (Optional[bool, None]) – if True convert to MetaTensor, otherwise to Pytorch Tensor, +if None behave according to return value of py:func:monai.data.meta_obj.get_track_meta.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+

ToNumpyd#

+
+
+class monai.transforms.ToNumpyd(keys, dtype=None, wrap_sequence=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ToNumpy.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Any]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, dtype=None, wrap_sequence=True, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • dtype (Union[dtype, type, str, None]) – target data type when converting to numpy array.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

ToPIL#

+
+
+class monai.transforms.ToPIL[source]#
+

Converts the input image (in the form of NumPy array or PyTorch Tensor) to PIL image

+
+
+__call__(img)[source]#
+

Apply the transform to img.

+
+ +
+ +
+
+

ToCupyd#

+
+
+class monai.transforms.ToCupyd(keys, dtype=None, wrap_sequence=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ToCupy.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • dtype (Optional[dtype, None]) – data type specifier. It is inferred from the input by default. +if not None, must be an argument of numpy.dtype, for more details: +https://docs.cupy.dev/en/stable/reference/generated/cupy.array.html.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ToPILd#

+
+
+class monai.transforms.ToPILd(keys, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ToNumpy.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Any]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

DeleteItemsd#

+
+
+class monai.transforms.DeleteItemsd(keys, sep='.', use_re=False)[source]#
+

Delete specified items from data dictionary to release memory. +It will remove the key-values and copy the others to construct a new dictionary.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, sep='.', use_re=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to delete, can be “A{sep}B{sep}C” +to delete key C in nested dictionary, C can be regular expression. +See also: monai.transforms.compose.MapTransform

  • +
  • sep (str) – the separator tag to define nested dictionary keys, default to “.”.

  • +
  • use_re (Union[Sequence[bool], bool]) – whether the specified key is a regular expression, it also can be +a list of bool values, mapping them to keys.

  • +
+
+
+
+ +
+ +
+
+

SelectItemsd#

+
+
+class monai.transforms.SelectItemsd(keys, allow_missing_keys=False)[source]#
+

Select only specified items from data dictionary to release memory. +It will copy the selected key-values and construct a new dictionary.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

FlattenSubKeysd#

+
+
+class monai.transforms.FlattenSubKeysd(keys, sub_keys=None, delete_keys=True, prefix=None)[source]#
+

If an item is dictionary, it flatten the item by moving the sub-items (defined by sub-keys) to the top level. +{“pred”: {“a”: …, “b”, … }} –> {“a”: …, “b”, … }

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be flatten

  • +
  • sub_keys (Union[Collection[Hashable], Hashable, None]) – the sub-keys of items to be flatten. If not provided all the sub-keys are flattened.

  • +
  • delete_keys (bool) – whether to delete the key of the items that their sub-keys are flattened. Default to True.

  • +
  • prefix (Optional[str, None]) – optional prefix to be added to the sub-keys when moving to the top level. +By default no prefix will be added.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

Transposed#

+
+
+class monai.transforms.Transposed(keys, indices, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Transpose.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Any]

+
+
+
+ +
+ +
+
+

SqueezeDimd#

+
+
+class monai.transforms.SqueezeDimd(keys, dim=0, update_meta=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SqueezeDim.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, dim=0, update_meta=True, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • dim (int) – dimension to be squeezed. Default: 0 (the first dimension)

  • +
  • update_meta (bool) – whether to update the meta info if the input is a metatensor. Default is True.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

DataStatsd#

+
+
+class monai.transforms.DataStatsd(keys, prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.DataStats.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats', allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • prefix (Union[Sequence[str], str]) – will be printed in format: “{prefix} statistics”. +it also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • data_type (Union[Sequence[bool], bool]) – whether to show the type of input data. +it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • data_shape (Union[Sequence[bool], bool]) – whether to show the shape of input data. +it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • value_range (Union[Sequence[bool], bool]) – whether to show the value range of input data. +it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • data_value (Union[Sequence[bool], bool]) – whether to show the raw value of input data. +it also can be a sequence of bool, each element corresponds to a key in keys. +a typical example is to print some properties of Nifti image: affine, pixdim, etc.

  • +
  • additional_info (Union[Sequence[Callable], Callable, None]) – user can define callable function to extract +additional info from input data. it also can be a sequence of string, each element +corresponds to a key in keys.

  • +
  • name (str) – identifier of logging.logger to use, defaulting to “DataStats”.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

SimulateDelayd#

+
+
+class monai.transforms.SimulateDelayd(keys, delay_time=0.0, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.SimulateDelay.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, delay_time=0.0, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • delay_time (Union[Sequence[float], float]) – The minimum amount of time, in fractions of seconds, to accomplish this identity task. +It also can be a sequence of string, each element corresponds to a key in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

CopyItemsd#

+
+
+class monai.transforms.CopyItemsd(keys, times=1, names=None, allow_missing_keys=False)[source]#
+

Copy specified items from data dictionary and save with different key names. +It can copy several items together and copy several times.

+
+
+__call__(data)[source]#
+
+
Raises:
+

KeyError – When a key in self.names already exists in data.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(keys, times=1, names=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • times (int) – expected copy times, for example, if keys is “img”, times is 3, +it will add 3 copies of “img” data to the dictionary, default to 1.

  • +
  • names (Union[Collection[Hashable], Hashable, None]) – the names corresponding to the newly copied data, +the length should match len(keys) x times. for example, if keys is [“img”, “seg”] +and times is 2, names can be: [“img_1”, “seg_1”, “img_2”, “seg_2”]. +if None, use “{key}_{index}” as key for copy times N, index from 0 to N-1.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
Raises:
+
    +
  • ValueError – When times is nonpositive.

  • +
  • ValueError – When len(names) is not len(keys) * times. Incompatible values.

  • +
+
+
+
+ +
+ +
+
+

ConcatItemsd#

+
+
+class monai.transforms.ConcatItemsd(keys, name, dim=0, allow_missing_keys=False)[source]#
+

Concatenate specified items from data dictionary together on the first dim to construct a big array. +Expect all the items are numpy array or PyTorch Tensor or MetaTensor. +Return the first input’s meta information when items are MetaTensor.

+
+
+__call__(data)[source]#
+
+
Raises:
+
    +
  • TypeError – When items in data differ in type.

  • +
  • TypeError – When the item type is not in Union[numpy.ndarray, torch.Tensor, MetaTensor].

  • +
+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+__init__(keys, name, dim=0, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be concatenated together. +See also: monai.transforms.compose.MapTransform

  • +
  • name (str) – the name corresponding to the key to store the concatenated data.

  • +
  • dim (int) – on which dimension to concatenate the items, default is 0.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

Lambdad#

+
+
+class monai.transforms.Lambdad(keys, func, inv_func=<function no_collation>, track_meta=True, overwrite=True, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.Lambda.

+

For example:

+
input_data={'image': np.zeros((10, 2, 2)), 'label': np.ones((10, 2, 2))}
+lambd = Lambdad(keys='label', func=lambda x: x[:4, :, :])
+print(lambd(input_data)['label'].shape)
+(4, 2, 2)
+
+
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • func (Union[Sequence[Callable], Callable]) – Lambda/function to be applied. It also can be a sequence of Callable, +each element corresponds to a key in keys.

  • +
  • inv_func (Union[Sequence[Callable], Callable]) – Lambda/function of inverse operation if want to invert transforms, default to lambda x: x. +It also can be a sequence of Callable, each element corresponds to a key in keys.

  • +
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) +as opposed to MONAI’s enhanced objects. By default, this is True.

  • +
  • overwrite (Union[Sequence[bool], bool, Sequence[str], str]) – whether to overwrite the original data in the input dictionary with lambda function output. it +can be bool or str, when setting to str, it will create a new key for the output and keep the value of +key intact. default to True. it also can be a sequence of bool or str, each element corresponds to a key +in keys.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
Note: The inverse operation doesn’t allow to define extra_info or access other information, such as the

image’s original size. If need these complicated information, please write a new InvertibleTransform directly.

+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
+
+ +
+ +
+
+

RandLambdad#

+
+
+class monai.transforms.RandLambdad(keys, func, inv_func=<function no_collation>, track_meta=True, overwrite=True, prob=1.0, allow_missing_keys=False)[source]#
+

Randomizable version monai.transforms.Lambdad, the input func may contain random logic, +or randomly execute the function based on prob. so CacheDataset will not execute it and cache the results.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • func (Union[Sequence[Callable], Callable]) – Lambda/function to be applied. It also can be a sequence of Callable, +each element corresponds to a key in keys.

  • +
  • inv_func (Union[Sequence[Callable], Callable]) – Lambda/function of inverse operation if want to invert transforms, default to lambda x: x. +It also can be a sequence of Callable, each element corresponds to a key in keys.

  • +
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) +as opposed to MONAI’s enhanced objects. By default, this is True.

  • +
  • overwrite (Union[Sequence[bool], bool]) – whether to overwrite the original data in the input dictionary with lambda function output. +default to True. it also can be a sequence of bool, each element corresponds to a key in keys.

  • +
  • prob (float) – probability of executing the random function, default to 1.0, with 100% probability to execute. +note that all the data specified by keys will share the same random probability to execute or not.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+

For more details, please check monai.transforms.Lambdad.

+
+
Note: The inverse operation doesn’t allow to define extra_info or access other information, such as the

image’s original size. If need these complicated information, please write a new InvertibleTransform directly.

+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
+
+ +
+ +
+
+

RemoveRepeatedChanneld#

+
+
+class monai.transforms.RemoveRepeatedChanneld(keys, repeats, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.RemoveRepeatedChannel.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, repeats, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • repeats (int) – the number of repetitions for each element.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

LabelToMaskd#

+
+
+class monai.transforms.LabelToMaskd(keys, select_labels, merge_channels=False, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.LabelToMask.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • select_labels (Union[Sequence[int], int]) – labels to generate mask from. for 1 channel label, the select_labels +is the expected label values, like: [1, 2, 3]. for One-Hot format label, the +select_labels is the expected channel indices.

  • +
  • merge_channels (bool) – whether to use np.any() to merge the result on channel dim. +if yes, will return a single channel mask with binary data.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

FgBgToIndicesd#

+
+
+class monai.transforms.FgBgToIndicesd(keys, fg_postfix='_fg_indices', bg_postfix='_bg_indices', image_key=None, image_threshold=0.0, output_shape=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.FgBgToIndices.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • fg_postfix (str) – postfix to save the computed foreground indices in dict. +for example, if computed on label and postfix = “_fg_indices”, the key will be label_fg_indices.

  • +
  • bg_postfix (str) – postfix to save the computed background indices in dict. +for example, if computed on label and postfix = “_bg_indices”, the key will be label_bg_indices.

  • +
  • image_key (Optional[str, None]) – if image_key is not None, use label == 0 & image > image_threshold to determine +the negative sample(background). so the output items will not map to all the voxels in the label.

  • +
  • image_threshold (float) – if enabled image_key, use image > image_threshold to determine +the valid image content area and select background only in this area.

  • +
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ClassesToIndicesd#

+
+
+class monai.transforms.ClassesToIndicesd(keys, indices_postfix='_cls_indices', num_classes=None, image_key=None, image_threshold=0.0, output_shape=None, max_samples_per_class=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ClassesToIndices.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • indices_postfix (str) – postfix to save the computed indices of all classes in dict. +for example, if computed on label and postfix = “_cls_indices”, the key will be label_cls_indices.

  • +
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • +
  • image_key (Optional[str, None]) – if image_key is not None, use image > image_threshold to define valid region, and only select +the indices within the valid region.

  • +
  • image_threshold (float) – if enabled image_key, use image > image_threshold to determine the valid image content +area and select only the indices of classes in this area.

  • +
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • +
  • max_samples_per_class (Optional[int, None]) – maximum length of indices to sample in each class to reduce memory consumption. +Default is None, no subsampling.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ConvertToMultiChannelBasedOnBratsClassesd#

+
+
+class monai.transforms.ConvertToMultiChannelBasedOnBratsClassesd(keys, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.ConvertToMultiChannelBasedOnBratsClasses. +Convert labels to multi channels based on brats18 classes: +label 1 is the necrotic and non-enhancing tumor core +label 2 is the peritumoral edema +label 4 is the GD-enhancing tumor +The possible classes are TC (Tumor core), WT (Whole tumor) +and ET (Enhancing tumor).

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

AddExtremePointsChanneld#

+
+
+class monai.transforms.AddExtremePointsChanneld(keys, label_key, background=0, pert=0.0, sigma=3.0, rescale_min=-1.0, rescale_max=1.0, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.AddExtremePointsChannel.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • label_key (str) – key to label source to get the extreme points.

  • +
  • background (int) – Class index of background label, defaults to 0.

  • +
  • pert (float) – Random perturbation amount to add to the points, defaults to 0.0.

  • +
  • sigma (Union[Sequence[float], float, Sequence[Tensor], Tensor]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

  • +
  • rescale_min (float) – minimum value of output data.

  • +
  • rescale_max (float) – maximum value of output data.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

Call self as a function.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+
+randomize(label)[source]#
+

Within this method, self.R should be used, instead of np.random, to introduce random factors.

+

all self.R calls happen here so that we have a better chance to +identify errors of sync the random state.

+

This method can generate the random factors based on properties of the input data.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+

TorchVisiond#

+
+
+class monai.transforms.TorchVisiond(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.TorchVision for non-randomized transforms. +For randomized transforms of TorchVision use monai.transforms.RandTorchVisiond.

+
+

Note

+

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.

+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • name (str) – The transform name in TorchVision package.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • args – parameters for the TorchVision transform.

  • +
  • kwargs – parameters for the TorchVision transform.

  • +
+
+
+
+ +
+ +
+
+

RandTorchVisiond#

+
+
+class monai.transforms.RandTorchVisiond(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.TorchVision for randomized transforms. +For deterministic non-randomized transforms of TorchVision use monai.transforms.TorchVisiond.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • name (str) – The transform name in TorchVision package.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • args – parameters for the TorchVision transform.

  • +
  • kwargs – parameters for the TorchVision transform.

  • +
+
+
+
+

Note

+
    +
  • 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 should call ToTensord transform first to convert Numpy to Tensor.

  • +
  • This class inherits the Randomizable purely to prevent any dataset caching to skip the transform +computation. If the random factor of the underlying torchvision transform is not derived from self.R, +the results may not be deterministic. See Also: monai.transforms.Randomizable.

  • +
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

MapLabelValued#

+
+
+class monai.transforms.MapLabelValued(keys, orig_labels, target_labels, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.MapLabelValue.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, orig_labels, target_labels, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • orig_labels (Sequence) – original labels that map to others.

  • +
  • target_labels (Sequence) – expected label values, 1: 1 map to the orig_labels.

  • +
  • dtype (Union[dtype, type, str, None]) – convert the output data to dtype, default to float32.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

EnsureTyped#

+
+
+class monai.transforms.EnsureTyped(keys, data_type='tensor', dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.EnsureType.

+

Ensure the input data to be a PyTorch Tensor or numpy array, support: numpy array, PyTorch Tensor, +float, int, bool, string and object keep the original. +If passing a dictionary, list or tuple, still return dictionary, list or tuple and recursively convert +every item to the expected data type if wrap_sequence=False.

+

Note: Currently, we only convert tensor data to numpy array or scalar number in the inverse operation.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, data_type='tensor', dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • data_type (str) – target data type to convert, should be “tensor” or “numpy”.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – target data content type to convert, for example: np.float32, torch.float, etc.

  • +
  • device (Optional[device, None]) – for Tensor data type, specify the target device.

  • +
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. +E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • +
  • track_meta (Optional[bool, None]) – whether to convert to MetaTensor when data_type is “tensor”. +If False, the output data type will be torch.Tensor. Default to the return value of get_track_meta.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+ +
+
+

IntensityStatsd#

+
+
+class monai.transforms.IntensityStatsd(keys, ops, key_prefix, mask_keys=None, channel_wise=False, meta_keys=None, meta_key_postfix='meta_dict', allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.IntensityStats. +Compute statistics for the intensity values of input image and store into the metadata dictionary. +For example: if ops=[lambda x: np.mean(x), “max”] and key_prefix=”orig”, may generate below stats: +{“orig_custom_0”: 1.5, “orig_max”: 3.0}.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • ops (Sequence[UnionType[str, Callable]]) – expected operations to compute statistics for the intensity. +if a string, will map to the predefined operations, supported: [“mean”, “median”, “max”, “min”, “std”] +mapping to np.nanmean, np.nanmedian, np.nanmax, np.nanmin, np.nanstd. +if a callable function, will execute the function on input image.

  • +
  • key_prefix (str) – the prefix to combine with ops name to generate the key to store the results in the +metadata dictionary. if some ops are callable functions, will use “{key_prefix}_custom_{index}” +as the key, where index counts from 0.

  • +
  • mask_keys (Union[Collection[Hashable], Hashable, None]) – if not None, specify the mask array for the image to extract only the interested area to compute +statistics, mask must have the same shape as the image. +it should be a sequence of strings or None, map to the keys.

  • +
  • channel_wise (bool) – whether to compute statistics for every channel of input image separately. +if True, return a list of values for every operation, default to False.

  • +
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. +used to store the computed statistics to the meta dict. +for example, for data with key image, the metadata by default is in image_meta_dict. +the metadata is a dictionary object which contains: filename, original_shape, etc. +it can be a sequence of string, map to the keys. +if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • +
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to fetch the metadata according +to the key data, default is meta_dict, the metadata is a dictionary object. +used to store the computed statistics to the meta dict.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ToDeviced#

+
+
+class monai.transforms.ToDeviced(keys, device, allow_missing_keys=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.ToDevice.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Tensor]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, device, allow_missing_keys=False, **kwargs)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • device (UnionType[device, str]) – target device to move the Tensor, for example: “cuda:1”.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
  • kwargs – other args for the PyTorch Tensor.to() API, for more details: +https://pytorch.org/docs/stable/generated/torch.Tensor.to.html.

  • +
+
+
+
+ +
+ +
+
+

CuCIMd#

+
+
+class monai.transforms.CuCIMd(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.CuCIM for non-randomized transforms. +For randomized transforms of CuCIM use monai.transforms.RandCuCIMd.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • name (str) – The transform name in CuCIM package.

  • +
  • allow_missing_keys (bool) – 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.

+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data – Dict[Hashable, cupy.ndarray]

+
+
Returns:
+

Dict[Hashable, cupy.ndarray]

+
+
+
+ +
+ +
+
+

RandCuCIMd#

+
+
+class monai.transforms.RandCuCIMd(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.CuCIM for randomized transforms. +For deterministic non-randomized transforms of CuCIM use monai.transforms.CuCIMd.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • name (str) – The transform name in CuCIM package.

  • +
  • allow_missing_keys (bool) – 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 should call ToCuPy transform first 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: monai.transforms.Randomizable.

  • +
+
+
+
+__call__(data)[source]#
+
+
Parameters:
+

data – Dict[Hashable, cupy.ndarray]

+
+
Returns:
+

Dict[Hashable, cupy.ndarray]

+
+
+
+ +
+ +
+
+

AddCoordinateChannelsd#

+
+
+class monai.transforms.AddCoordinateChannelsd(keys, spatial_dims, allow_missing_keys=False)[source]#
+

Dictionary-based wrapper of monai.transforms.AddCoordinateChannels.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • spatial_dims (Sequence[int]) – the spatial dimensions that are to have their coordinates encoded in a channel and +appended to the input image. E.g., (0, 1, 2) represents H, W, D dims and append three channels +to the input image, encoding the coordinates of the input’s three spatial dimensions.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+

Deprecated since version 0.8.0: spatial_channels is deprecated, use spatial_dims instead.

+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

ImageFilterd#

+
+
+class monai.transforms.ImageFilterd(keys, kernel, kernel_size=None, allow_missing_keys=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.ImageFilter.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • kernel (Union[str, ndarray, Tensor]) – A string specifying the kernel or a custom kernel as torch.Tenor or np.ndarray. +Available options are: mean, laplacian, elliptical, sobel_{w,h,d}`

  • +
  • kernel_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic kernel. +Computational complexity increases exponentially with kernel_size, which +should be considered when choosing the kernel size.

  • +
  • allow_missing_keys (bool) – Don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+

RandImageFilterd#

+
+
+class monai.transforms.RandImageFilterd(keys, kernel, kernel_size=None, prob=0.1, allow_missing_keys=False, **kwargs)[source]#
+

Dictionary-based wrapper of monai.transforms.RandomFilterKernel.

+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.MapTransform

  • +
  • kernel (Union[str, ndarray, Tensor]) – A string specifying the kernel or a custom kernel as torch.Tenor or np.ndarray. +Available options are: mean, laplacian, elliptical, sobel_{w,h,d}`

  • +
  • kernel_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic kernel. +Computational complexity increases exponentially with kernel_size, which +should be considered when choosing the kernel size.

  • +
  • prob (float) – Probability the transform is applied to the data

  • +
  • allow_missing_keys (bool) – Don’t raise exception if key is missing.

  • +
+
+
+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+ +
+
+
+

MetaTensor#

+
+

ToMetaTensord#

+
+
+class monai.transforms.ToMetaTensord(keys, allow_missing_keys=False)[source]#
+

Dictionary-based transform to convert a dictionary to MetaTensor.

+

If input is {“a”: torch.Tensor, “a_meta_dict”: dict, “b”: …}, then output will +have the form {“a”: MetaTensor, “b”: MetaTensor}.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+

FromMetaTensord#

+
+
+class monai.transforms.FromMetaTensord(keys, data_type='tensor', allow_missing_keys=False)[source]#
+

Dictionary-based transform to convert MetaTensor to a dictionary.

+

If input is {“a”: MetaTensor, “b”: MetaTensor}, then output will +have the form {“a”: torch.Tensor, “a_meta_dict”: dict, “a_transforms”: list, “b”: …}.

+
+
+__call__(data)[source]#
+

data often comes from an iteration over an iterable, +such as torch.utils.data.Dataset.

+

To simplify the input validations, this method assumes:

+
    +
  • data is a Python dictionary,

  • +
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element +of self.keys, the data shape can be:

    +
      +
    1. string data without shape, LoadImaged transform expects file paths,

    2. +
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), +except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and +AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. +
    +
  • +
  • the channel dimension is often not omitted even if number of channels is one.

  • +
+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
Returns:
+

An updated dictionary version of data by applying the transform.

+
+
+
+ +
+
+__init__(keys, data_type='tensor', allow_missing_keys=False)[source]#
+
+
Parameters:
+
    +
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. +See also: monai.transforms.compose.MapTransform

  • +
  • data_type (UnionType[Sequence[str], str]) – target data type to convert, should be “tensor” or “numpy”.

  • +
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • +
+
+
+
+ +
+
+inverse(data)[source]#
+

Inverse of __call__.

+
+
Raises:
+

NotImplementedError – When the subclass does not override this method.

+
+
Return type:
+

dict[Hashable, Union[ndarray, Tensor]]

+
+
+
+ +
+ +
+
+
+
+

Transform Adaptors#

+
+

How to use the adaptor function#

+

The key to using ‘adaptor’ lies in understanding the function that want to +adapt. The ‘inputs’ and ‘outputs’ parameters take either strings, lists/tuples +of strings or a dictionary mapping strings, depending on call signature of the +function being called.

+

The adaptor function is written to minimise the cognitive load on the caller. +There should be a minimal number of cases where the caller has to set anything +on the input parameter, and for functions that return a single value, it is +only necessary to name the dictionary keyword to which that value is assigned.

+
+

Use of outputs#

+

outputs can take either a string, a list/tuple of string or a dict of string +to string, depending on what the transform being adapted returns:

+
+
    +
  • If the transform returns a single argument, then outputs can be supplied a +string that indicates what key to assign the return value to in the +dictionary

  • +
  • If the transform returns a list/tuple of values, then outputs can be supplied +a list/tuple of the same length. The strings in outputs map the return value +at the corresponding position to a key in the dictionary

  • +
  • If the transform returns a dictionary of values, then outputs must be supplied +a dictionary that maps keys in the function’s return dictionary to the +dictionary being passed between functions

  • +
+
+

Note, the caller is free to use a more complex way of specifying the outputs +parameter than is required. The following are synonymous and will be treated +identically:

+
# single argument
+adaptor(MyTransform(), 'image')
+adaptor(MyTransform(), ['image'])
+adaptor(MyTransform(), {'image': 'image'})
+
+# multiple arguments
+adaptor(MyTransform(), ['image', 'label'])
+adaptor(MyTransform(), {'image': 'image', 'label': 'label'})
+
+
+
+
+

Use of inputs#

+

inputs can usually be omitted when using adaptor. It is only required when a +the function’s parameter names do not match the names in the dictionary that is +used to chain transform calls.

+
class MyTransform1:
+    def __call__(self, image):
+        # do stuff to image
+        return image + 1
+
+
+class MyTransform2:
+    def __call__(self, img_dict):
+        # do stuff to image
+        img_dict["image"] += 1
+        return img_dict
+
+
+xform = Compose([adaptor(MyTransform1(), "image"), MyTransform2()])
+d = {"image": 1}
+print(xform(d))
+
+>>> {'image': 3}
+
+
+
class MyTransform3:
+    def __call__(self, img_dict):
+        # do stuff to image
+        img_dict["image"] -= 1
+        img_dict["segment"] = img_dict["image"]
+        return img_dict
+
+
+class MyTransform4:
+    def __call__(self, img, seg):
+        # do stuff to image
+        img -= 1
+        seg -= 1
+        return img, seg
+
+
+xform = Compose([MyTransform3(), adaptor(MyTransform4(), ["img", "seg"], {"image": "img", "segment": "seg"})])
+d = {"image": 1}
+print(xform(d))
+
+>>> {'image': 0, 'segment': 0, 'img': -1, 'seg': -1}
+
+
+

Inputs:

+
    +
  • dictionary in: None | Name maps

  • +
  • params in (match): None | Name list | Name maps

  • +
  • params in (mismatch): Name maps

  • +
  • params & **kwargs (match) : None | Name maps

  • +
  • params & **kwargs (mismatch) : Name maps

  • +
+

Outputs:

+
    +
  • dictionary out: None | Name maps

  • +
  • list/tuple out: list/tuple

  • +
  • variable out: string

  • +
+
+
+
+

FunctionSignature#

+
+
+class monai.transforms.adaptors.FunctionSignature(function)[source]#
+
+ +
+
+

adaptor#

+
+
+monai.transforms.adaptors.adaptor(function, outputs, inputs=None)[source]#
+
+ +
+
+

apply_alias#

+
+
+monai.transforms.adaptors.apply_alias(fn, name_map)[source]#
+
+ +
+
+

to_kwargs#

+
+
+monai.transforms.adaptors.to_kwargs(fn)[source]#
+
+ +
+
+
+

Utilities#

+
+
+class monai.transforms.utils.Fourier[source]#
+

Helper class storing Fourier mappings

+
+
+static inv_shift_fourier(k, spatial_dims, n_dims=None)[source]#
+

Applies inverse shift and fourier transform. Only the spatial +dimensions are transformed.

+
+
Parameters:
+
    +
  • k (Union[ndarray, Tensor]) – K-space data.

  • +
  • spatial_dims (int) – Number of spatial dimensions.

  • +
+
+
Returns:
+

Tensor in image space.

+
+
Return type:
+

x

+
+
+
+ +
+
+static shift_fourier(x, spatial_dims)[source]#
+

Applies fourier transform and shifts the zero-frequency component to the +center of the spectrum. Only the spatial dimensions get transformed.

+
+
Parameters:
+
    +
  • x (Union[ndarray, Tensor]) – Image to transform.

  • +
  • spatial_dims (int) – Number of spatial dimensions.

  • +
+
+
+
+
Returns

k: K-space data.

+
+
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+ +
+
+monai.transforms.utils.allow_missing_keys_mode(transform)[source]#
+

Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states.

+
+
Parameters:
+

transform (UnionType[MapTransform, Compose, tuple[MapTransform], tuple[Compose]]) – either MapTransform or a Compose

+
+
+

Example:

+
data = {"image": np.arange(16, dtype=float).reshape(1, 4, 4)}
+t = SpatialPadd(["image", "label"], 10, allow_missing_keys=False)
+_ = t(data)  # would raise exception
+with allow_missing_keys_mode(t):
+    _ = t(data)  # OK!
+
+
+
+ +
+
+monai.transforms.utils.attach_hook(func, hook, mode='pre')[source]#
+

Adds hook before or after a func call. If mode is “pre”, the wrapper will call hook then func. +If the mode is “post”, the wrapper will call func then hook.

+
+ +
+
+monai.transforms.utils.check_boundaries(boundaries)[source]#
+

Check boundaries for Signal transforms

+
+
Return type:
+

None

+
+
+
+ +
+
+monai.transforms.utils.check_non_lazy_pending_ops(input_array, name=None, raise_error=False)[source]#
+

Check whether the input array has pending operations, raise an error or warn when it has.

+
+
Parameters:
+
    +
  • input_array (Union[ndarray, Tensor]) – input array to be checked.

  • +
  • name (Optional[str, None]) – an optional name to be included in the error message.

  • +
  • raise_error (bool) – whether to raise an error, default to False, a warning message will be issued instead.

  • +
+
+
Return type:
+

None

+
+
+
+ +
+
+monai.transforms.utils.compute_divisible_spatial_size(spatial_shape, k)[source]#
+

Compute the target spatial size which should be divisible by k.

+
+
Parameters:
+
    +
  • spatial_shape (Sequence[int]) – original spatial shape.

  • +
  • k (UnionType[Sequence[int], int]) – the target k for each spatial dimension. +if k is negative or 0, the original size is preserved. +if k is an int, the same k be applied to all the input spatial dimensions.

  • +
+
+
+
+ +
+
+monai.transforms.utils.convert_applied_interp_mode(trans_info, mode='nearest', align_corners=None)[source]#
+

Recursively change the interpolation mode in the applied operation stacks, default to “nearest”.

+

See also: monai.transform.inverse.InvertibleTransform

+
+
Parameters:
+
    +
  • trans_info – applied operation stack, tracking the previously applied invertible transform.

  • +
  • mode (str) – target interpolation mode to convert, default to “nearest” as it’s usually used to save the mode output.

  • +
  • align_corners (Optional[bool, None]) – target align corner value in PyTorch interpolation API, need to align with the mode.

  • +
+
+
+
+ +
+
+monai.transforms.utils.convert_pad_mode(dst, mode)[source]#
+

Utility to convert padding mode between numpy array and PyTorch Tensor.

+
+
Parameters:
+
    +
  • dst (Union[ndarray, Tensor]) – target data to convert padding mode for, should be numpy array or PyTorch Tensor.

  • +
  • mode (UnionType[str, None]) – current padding mode.

  • +
+
+
+
+ +
+
+monai.transforms.utils.convert_to_contiguous(data, **kwargs)[source]#
+

Check and ensure the numpy array or PyTorch Tensor in data to be contiguous in memory.

+
+
Parameters:
+
+
+
Return type:
+

Union[ndarray, Tensor, Mapping, Sequence[Any]]

+
+
+
+ +
+
+monai.transforms.utils.copypaste_arrays(src_shape, dest_shape, srccenter, destcenter, dims)[source]#
+

Calculate the slices to copy a sliced area of array in src_shape into array in dest_shape.

+

The area has dimensions dims (use 0 or None to copy everything in that dimension), +the source area is centered at srccenter index in src and copied into area centered at destcenter in dest. +The dimensions of the copied area will be clipped to fit within the +source and destination arrays so a smaller area may be copied than expected. Return value is the tuples of slice +objects indexing the copied area in src, and those indexing the copy area in dest.

+

Example

+
src_shape = (6,6)
+src = np.random.randint(0,10,src_shape)
+dest = np.zeros_like(src)
+srcslices, destslices = copypaste_arrays(src_shape, dest.shape, (3, 2),(2, 1),(3, 4))
+dest[destslices] = src[srcslices]
+print(src)
+print(dest)
+
+>>> [[9 5 6 6 9 6]
+     [4 3 5 6 1 2]
+     [0 7 3 2 4 1]
+     [3 0 0 1 5 1]
+     [9 4 7 1 8 2]
+     [6 6 5 8 6 7]]
+    [[0 0 0 0 0 0]
+     [7 3 2 4 0 0]
+     [0 0 1 5 0 0]
+     [4 7 1 8 0 0]
+     [0 0 0 0 0 0]
+     [0 0 0 0 0 0]]
+
+
+
+
Return type:
+

tuple[tuple[slice, …], tuple[slice, …]]

+
+
+
+ +
+
+monai.transforms.utils.create_control_grid(spatial_shape, spacing, homogeneous=True, dtype=<class 'float'>, device=None, backend=TransformBackends.NUMPY)[source]#
+

control grid with two additional point in each direction

+
+ +
+
+monai.transforms.utils.create_grid(spatial_size, spacing=None, homogeneous=True, dtype=<class 'float'>, device=None, backend=TransformBackends.NUMPY)[source]#
+

compute a spatial_size mesh.

+
+
    +
  • when homogeneous=True, the output shape is (N+1, dim_size_1, dim_size_2, …, dim_size_N)

  • +
  • when homogeneous=False, the output shape is (N, dim_size_1, dim_size_2, …, dim_size_N)

  • +
+
+
+
Parameters:
+
    +
  • spatial_size (Sequence[int]) – spatial size of the grid.

  • +
  • spacing (Optional[Sequence[float], None]) – same len as spatial_size, defaults to 1.0 (dense grid).

  • +
  • homogeneous (bool) – whether to make homogeneous coordinates.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – output grid data type, defaults to float.

  • +
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • +
  • backend – APIs to use, numpy or torch.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils.create_rotate(spatial_dims, radians, device=None, backend=TransformBackends.NUMPY)[source]#
+

create a 2D or 3D rotation matrix

+
+
Parameters:
+
    +
  • spatial_dims (int) – {2, 3} spatial rank

  • +
  • radians (UnionType[Sequence[float], float]) – rotation radians +when spatial_dims == 3, the radians sequence corresponds to +rotation in the 1st, 2nd, and 3rd dim respectively.

  • +
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • +
  • backend (str) – APIs to use, numpy or torch.

  • +
+
+
Raises:
+
    +
  • ValueError – When radians is empty.

  • +
  • ValueError – When spatial_dims is not one of [2, 3].

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils.create_scale(spatial_dims, scaling_factor, device=None, backend=TransformBackends.NUMPY)[source]#
+

create a scaling matrix

+
+
Parameters:
+
    +
  • spatial_dims (int) – spatial rank

  • +
  • scaling_factor (UnionType[Sequence[float], float]) – scaling factors for every spatial dim, defaults to 1.

  • +
  • device (Union[device, str, None]) – device to compute and store the output (when the backend is “torch”).

  • +
  • backend – APIs to use, numpy or torch.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils.create_shear(spatial_dims, coefs, device=None, backend=TransformBackends.NUMPY)[source]#
+

create a shearing matrix

+
+
Parameters:
+
    +
  • spatial_dims (int) – spatial rank

  • +
  • coefs (UnionType[Sequence[float], float]) –

    shearing factors, a tuple of 2 floats for 2D, a tuple of 6 floats for 3D), +take a 3D affine as example:

    +
    [
    +    [1.0, coefs[0], coefs[1], 0.0],
    +    [coefs[2], 1.0, coefs[3], 0.0],
    +    [coefs[4], coefs[5], 1.0, 0.0],
    +    [0.0, 0.0, 0.0, 1.0],
    +]
    +
    +
    +

  • +
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • +
  • backend – APIs to use, numpy or torch.

  • +
+
+
Raises:
+

NotImplementedError – When spatial_dims is not one of [2, 3].

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils.create_translate(spatial_dims, shift, device=None, backend=TransformBackends.NUMPY)[source]#
+

create a translation matrix

+
+
Parameters:
+
    +
  • spatial_dims (int) – spatial rank

  • +
  • shift (UnionType[Sequence[float], float]) – translate pixel/voxel for every spatial dim, defaults to 0.

  • +
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • +
  • backend – APIs to use, numpy or torch.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils.equalize_hist(img, mask=None, num_bins=256, min=0, max=255)[source]#
+

Utility to equalize input image based on the histogram. +If skimage installed, will leverage skimage.exposure.histogram, otherwise, use +np.histogram instead.

+
+
Parameters:
+
    +
  • img (ndarray) – input image to equalize.

  • +
  • mask (Optional[ndarray, None]) – if provided, must be ndarray of bools or 0s and 1s, and same shape as image. +only points at which mask==True are used for the equalization.

  • +
  • num_bins (int) – number of the bins to use in histogram, default to 256. for more details: +https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.

  • +
  • min (int) – the min value to normalize input image, default to 0.

  • +
  • max (int) – the max value to normalize input image, default to 255.

  • +
+
+
Return type:
+

ndarray

+
+
+
+ +
+
+monai.transforms.utils.extreme_points_to_image(points, label, sigma=0.0, rescale_min=-1.0, rescale_max=1.0)[source]#
+

Please refer to monai.transforms.AddExtremePointsChannel for the usage.

+

Applies a gaussian filter to the extreme points image. Then the pixel values in points image are rescaled +to range [rescale_min, rescale_max].

+
+
Parameters:
+
    +
  • points (list[tuple[int, …]]) – Extreme points of the object/organ.

  • +
  • label (Union[ndarray, Tensor]) – label image to get extreme points from. Shape must be +(1, spatial_dim1, [, spatial_dim2, …]). Doesn’t support one-hot labels.

  • +
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – if a list of values, must match the count of spatial dimensions of input data, +and apply every value in the list to 1 spatial dimension. if only 1 value provided, +use it for all spatial dimensions.

  • +
  • rescale_min (float) – minimum value of output data.

  • +
  • rescale_max (float) – maximum value of output data.

  • +
+
+
Return type:
+

Tensor

+
+
+
+ +
+
+monai.transforms.utils.fill_holes(img_arr, applied_labels=None, connectivity=None)[source]#
+

Fill the holes in the provided image.

+

The label 0 will be treated as background and the enclosed holes will be set to the neighboring class label. +What is considered to be an enclosed hole is defined by the connectivity. +Holes on the edge are always considered to be open (not enclosed).

+
+

Note

+

The performance of this method heavily depends on the number of labels. +It is a bit faster if the list of applied_labels is provided. +Limiting the number of applied_labels results in a big decrease in processing time.

+

If the image is one-hot-encoded, then the applied_labels need to match the channel index.

+
+
+
Parameters:
+
    +
  • img_arr (ndarray) – numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

  • +
  • applied_labels (Optional[Iterable[int], None]) – Labels for which to fill holes. Defaults to None, +that is filling holes for all labels.

  • +
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to +consider a pixel/voxel as a neighbor. Accepted values are ranging from 1 to input.ndim. +Defaults to a full connectivity of input.ndim.

  • +
+
+
Return type:
+

ndarray

+
+
Returns:
+

numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

+
+
+
+ +
+
+monai.transforms.utils.generate_label_classes_crop_centers(spatial_size, num_samples, label_spatial_shape, indices, ratios=None, rand_state=None, allow_smaller=False, warn=True)[source]#
+

Generate valid sample locations based on the specified ratios of label classes. +Valid: samples sitting entirely within image, expected input shape: [C, H, W, D] or [C, H, W]

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the ROIs to be sampled.

  • +
  • num_samples (int) – total sample centers to be generated.

  • +
  • label_spatial_shape (Sequence[int]) – spatial shape of the original label data to unravel selected centers.

  • +
  • indices (Sequence[Union[ndarray, Tensor]]) – sequence of pre-computed foreground indices of every class in 1 dimension.

  • +
  • ratios (Optional[list[UnionType[float, int]], None]) – ratios of every class in the label to generate crop centers, including background class. +if None, every class will have the same ratio to generate crop centers.

  • +
  • rand_state (Optional[RandomState, None]) – numpy randomState object to align with other modules.

  • +
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than +the requested ROI in any dimension. If True, any smaller dimensions will be set to +match the cropped size (i.e., no cropping in that dimension).

  • +
  • warn (bool) – if True prints a warning if a class is not present in the label.

  • +
+
+
Return type:
+

tuple[tuple]

+
+
+
+ +
+
+monai.transforms.utils.generate_pos_neg_label_crop_centers(spatial_size, num_samples, pos_ratio, label_spatial_shape, fg_indices, bg_indices, rand_state=None, allow_smaller=False)[source]#
+

Generate valid sample locations based on the label with option for specifying foreground ratio +Valid: samples sitting entirely within image, expected input shape: [C, H, W, D] or [C, H, W]

+
+
Parameters:
+
    +
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the ROIs to be sampled.

  • +
  • num_samples (int) – total sample centers to be generated.

  • +
  • pos_ratio (float) – ratio of total locations generated that have center being foreground.

  • +
  • label_spatial_shape (Sequence[int]) – spatial shape of the original label data to unravel selected centers.

  • +
  • fg_indices (Union[ndarray, Tensor]) – pre-computed foreground indices in 1 dimension.

  • +
  • bg_indices (Union[ndarray, Tensor]) – pre-computed background indices in 1 dimension.

  • +
  • rand_state (Optional[RandomState, None]) – numpy randomState object to align with other modules.

  • +
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than +the requested ROI in any dimension. If True, any smaller dimensions will be set to +match the cropped size (i.e., no cropping in that dimension).

  • +
+
+
Raises:
+
    +
  • ValueError – When the proposed roi is larger than the image.

  • +
  • ValueError – When the foreground and background indices lengths are 0.

  • +
+
+
Return type:
+

tuple[tuple]

+
+
+
+ +
+
+monai.transforms.utils.generate_spatial_bounding_box(img, select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True)[source]#
+

Generate the spatial bounding box of foreground in the image with start-end positions (inclusive). +Users can define arbitrary function to select expected foreground from the whole image or specified channels. +And it can also add margin to every dim of the bounding box. +The output format of the coordinates is:

+
+

[1st_spatial_dim_start, 2nd_spatial_dim_start, …, Nth_spatial_dim_start], +[1st_spatial_dim_end, 2nd_spatial_dim_end, …, Nth_spatial_dim_end]

+
+

If allow_smaller, the bounding boxes edges are aligned with the input image edges. +This function returns [0, 0, …], [0, 0, …] if there’s no positive intensity.

+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – a “channel-first” image of shape (C, spatial_dim1[, spatial_dim2, …]) to generate bounding box from.

  • +
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • +
  • channel_indices (Union[Iterable[int], int, None]) – if defined, select foreground only on the specified channels +of image. if None, select foreground on the whole image.

  • +
  • margin (UnionType[Sequence[int], int]) – add margin value to spatial dims of the bounding box, if only 1 value provided, use it for all dims.

  • +
  • allow_smaller (bool) – when computing box size with margin, whether allow the image size to be smaller +than box size, default to True.

  • +
+
+
Return type:
+

tuple[list[int], list[int]]

+
+
+
+ +
+
+monai.transforms.utils.get_extreme_points(img, rand_state=None, background=0, pert=0.0)[source]#
+

Generate extreme points from an image. These are used to generate initial segmentation +for annotation models. An optional perturbation can be passed to simulate user clicks.

+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – Image to generate extreme points from. Expected Shape is (spatial_dim1, [, spatial_dim2, ...]).

  • +
  • rand_state (Optional[RandomState, None]) – np.random.RandomState object used to select random indices.

  • +
  • background (int) – Value to be consider as background, defaults to 0.

  • +
  • pert (float) – Random perturbation amount to add to the points, defaults to 0.0.

  • +
+
+
Return type:
+

list[tuple[int, …]]

+
+
Returns:
+

A list of extreme points, its length is equal to 2 * spatial dimension of input image. +The output format of the coordinates is:

+

[1st_spatial_dim_min, 1st_spatial_dim_max, 2nd_spatial_dim_min, …, Nth_spatial_dim_max]

+

+
+
Raises:
+

ValueError – When the input image does not have any foreground pixel.

+
+
+
+ +
+
+monai.transforms.utils.get_largest_connected_component_mask(img, connectivity=None, num_components=1)[source]#
+

Gets the largest connected component mask of an image.

+
+
Parameters:
+
    +
  • img (~NdarrayTensor) – Image to get largest connected component from. Shape is (spatial_dim1 [, spatial_dim2, …])

  • +
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. If None, a full +connectivity of input.ndim is used. for more details: +https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label.

  • +
  • num_components (int) – The number of largest components to preserve.

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils.get_number_image_type_conversions(transform, test_data, key=None)[source]#
+

Get the number of times that the data need to be converted (e.g., numpy to torch). +Conversions between different devices are also counted (e.g., CPU to GPU).

+
+
Parameters:
+
    +
  • transform (Compose) – composed transforms to be tested

  • +
  • test_data (Any) – data to be used to count the number of conversions

  • +
  • key (Optional[Hashable, None]) – if using dictionary transforms, this key will be used to check the number of conversions.

  • +
+
+
Return type:
+

int

+
+
+
+ +
+
+monai.transforms.utils.get_transform_backends()[source]#
+

Get the backends of all MONAI transforms.

+
+
Returns:
+

Dictionary, where each key is a transform, and its +corresponding values are a boolean list, stating +whether that transform supports (1) torch.Tensor, +and (2) np.ndarray as input without needing to +convert.

+
+
+
+ +
+
+monai.transforms.utils.get_unique_labels(img, is_onehot, discard=None)[source]#
+

Get list of non-background labels in an image.

+
+
Parameters:
+
    +
  • img (Union[ndarray, Tensor]) – Image to be processed. Shape should be [C, W, H, [D]] with C=1 if not onehot else num_classes.

  • +
  • is_onehot (bool) – Boolean as to whether input image is one-hotted. If one-hotted, only return channels with

  • +
  • discard (Union[Iterable[int], int, None]) – Can be used to remove labels (e.g., background). Can be any value, sequence of values, or +None (nothing is discarded).

  • +
+
+
Return type:
+

set[int]

+
+
Returns:
+

Set of labels

+
+
+
+ +
+
+monai.transforms.utils.img_bounds(img)[source]#
+

Returns the minimum and maximum indices of non-zero lines in axis 0 of img, followed by that for axis 1.

+
+ +
+
+monai.transforms.utils.in_bounds(x, y, margin, maxx, maxy)[source]#
+

Returns True if (x,y) is within the rectangle (margin, margin, maxx-margin, maxy-margin).

+
+
Return type:
+

bool

+
+
+
+ +
+
+monai.transforms.utils.is_empty(img)[source]#
+

Returns True if img is empty, that is its maximum value is not greater than its minimum.

+
+
Return type:
+

bool

+
+
+
+ +
+
+monai.transforms.utils.is_positive(img)[source]#
+

Returns a boolean version of img where the positive values are converted into True, the other values are False.

+
+ +
+
+monai.transforms.utils.is_tensor_invertible(data)[source]#
+

Checks whether a given tensor is invertible. The rules are as follows:

+
    +
  1. If the tensor is not a MetaTensor, it is not invertible

  2. +
  3. If the tensor is a MetaTensor but it has TraceStatusKeys.PENDING_DURING_APPLY in the TraceKeys.STATUS of any +of its applied_operations it is not invertible

  4. +
  5. Otherwise, it is invertible

  6. +
+

This function also accepts:

+
    +
  • dictionaries of tensors

  • +
  • lists or tuples of tensors

  • +
  • list or tuples of dictionaries of tensors

  • +
+

In any of the above scenarios, it iterates through the collections and executes itself recursively until it is +operating on tensors.

+
+
Parameters:
+

data (Tensor) – a torch.Tensor or MetaTensor or collections of torch.Tensor or MetaTensor, as described above

+
+
Returns:
+

A tuple. The first entry is False or True. The second entry is the status messages that can be used for the +user to help debug their pipelines.

+
+
+
+ +
+
+monai.transforms.utils.map_binary_to_indices(label, image=None, image_threshold=0.0)[source]#
+

Compute the foreground and background of input label data, return the indices after fattening. +For example: +label = np.array([[[0, 1, 1], [1, 0, 1], [1, 1, 0]]]) +foreground indices = np.array([1, 2, 3, 5, 6, 7]) and background indices = np.array([0, 4, 8])

+
+
Parameters:
+
    +
  • label (Union[ndarray, Tensor]) – use the label data to get the foreground/background information.

  • +
  • image (Union[ndarray, Tensor, None]) – if image is not None, use label = 0 & image > image_threshold +to define background. so the output items will not map to all the voxels in the label.

  • +
  • image_threshold (float) – if enabled image, use image > image_threshold to +determine the valid image content area and select background only in this area.

  • +
+
+
Return type:
+

tuple[Union[ndarray, Tensor], Union[ndarray, Tensor]]

+
+
+
+ +
+
+monai.transforms.utils.map_classes_to_indices(label, num_classes=None, image=None, image_threshold=0.0, max_samples_per_class=None)[source]#
+

Filter out indices of every class of the input label data, return the indices after fattening. +It can handle both One-Hot format label and Argmax format label, must provide num_classes for +Argmax label.

+

For example: +label = np.array([[[0, 1, 2], [2, 0, 1], [1, 2, 0]]]) and num_classes=3, will return a list +which contains the indices of the 3 classes: +[np.array([0, 4, 8]), np.array([1, 5, 6]), np.array([2, 3, 7])]

+
+
Parameters:
+
    +
  • label (Union[ndarray, Tensor]) – use the label data to get the indices of every class.

  • +
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • +
  • image (Union[ndarray, Tensor, None]) – if image is not None, only return the indices of every class that are within the valid +region of the image (image > image_threshold).

  • +
  • image_threshold (float) – if enabled image, use image > image_threshold to +determine the valid image content area and select class indices only in this area.

  • +
  • max_samples_per_class (Optional[int, None]) – maximum length of indices in each class to reduce memory consumption. +Default is None, no subsampling.

  • +
+
+
Return type:
+

list[Union[ndarray, Tensor]]

+
+
+
+ +
+
+monai.transforms.utils.map_spatial_axes(img_ndim, spatial_axes=None, channel_first=True)[source]#
+

Utility to map the spatial axes to real axes in channel first/last shape. +For example: +If channel_first is True, and img has 3 spatial dims, map spatial axes to real axes as below: +None -> [1, 2, 3] +[0, 1] -> [1, 2] +[0, -1] -> [1, -1] +If channel_first is False, and img has 3 spatial dims, map spatial axes to real axes as below: +None -> [0, 1, 2] +[0, 1] -> [0, 1] +[0, -1] -> [0, -2]

+
+
Parameters:
+
    +
  • img_ndim (int) – dimension number of the target image.

  • +
  • spatial_axes (Union[Sequence[int], int, None]) – spatial axes to be converted, default is None. +The default None will convert to all the spatial axes of the image. +If axis is negative it counts from the last to the first axis. +If axis is a tuple of ints.

  • +
  • channel_first (bool) – the image data is channel first or channel last, default to channel first.

  • +
+
+
Return type:
+

list[int]

+
+
+
+ +
+
+monai.transforms.utils.print_transform_backends()[source]#
+

Prints a list of backends of all MONAI transforms.

+
+ +
+
+monai.transforms.utils.rand_choice(prob=0.5)[source]#
+

Returns True if a randomly chosen number is less than or equal to prob, by default this is a 50/50 chance.

+
+
Return type:
+

bool

+
+
+
+ +
+
+monai.transforms.utils.remove_small_objects(img, min_size=64, connectivity=1, independent_channels=True)[source]#
+

Use skimage.morphology.remove_small_objects to remove small objects from images. +See: https://scikit-image.org/docs/dev/api/skimage.morphology.html#remove-small-objects.

+

Data should be one-hotted.

+
+
Parameters:
+
    +
  • img (~NdarrayTensor) – image to process. Expected shape: C, H,W,[D]. Expected to only have singleton channel dimension, +i.e., not be one-hotted. Converted to type int.

  • +
  • min_size (int) – objects smaller than this size are removed.

  • +
  • connectivity (int) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. +Accepted values are ranging from 1 to input.ndim. If None, a full +connectivity of input.ndim is used. For more details refer to linked scikit-image +documentation.

  • +
  • independent_channels (bool) – Whether to consider each channel independently.

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils.rescale_array(arr, minv=0.0, maxv=1.0, dtype=<class 'numpy.float32'>)[source]#
+

Rescale the values of numpy array arr to be from minv to maxv. +If either minv or maxv is None, it returns (a - min_a) / (max_a - min_a).

+
+
Parameters:
+
    +
  • arr (Union[ndarray, Tensor]) – input array to rescale.

  • +
  • minv (UnionType[float, None]) – minimum value of target rescaled array.

  • +
  • maxv (UnionType[float, None]) – maxmum value of target rescaled array.

  • +
  • dtype (Union[dtype, type, str, None, dtype]) – if not None, convert input array to dtype before computation.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils.rescale_array_int_max(arr, dtype=<class 'numpy.uint16'>)[source]#
+

Rescale the array arr to be between the minimum and maximum values of the type dtype.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+monai.transforms.utils.rescale_instance_array(arr, minv=0.0, maxv=1.0, dtype=<class 'numpy.float32'>)[source]#
+

Rescale each array slice along the first dimension of arr independently.

+
+
Return type:
+

ndarray

+
+
+
+ +
+
+monai.transforms.utils.reset_ops_id(data)[source]#
+

find MetaTensors in list or dict data and (in-place) set TraceKeys.ID to Tracekeys.NONE.

+
+ +
+
+monai.transforms.utils.resize_center(img, *resize_dims, fill_value=0.0, inplace=True)[source]#
+

Resize img by cropping or expanding the image from the center. The resize_dims values are the output dimensions +(or None to use original dimension of img). If a dimension is smaller than that of img then the result will be +cropped and if larger padded with zeros, in both cases this is done relative to the center of img. The result is +a new image with the specified dimensions and values from img copied into its center.

+
+ +
+
+monai.transforms.utils.resolves_modes(interp_mode='constant', padding_mode='zeros', backend=TransformBackends.TORCH, **kwargs)[source]#
+

Automatically adjust the resampling interpolation mode and padding mode, +so that they are compatible with the corresponding API of the backend. +Depending on the availability of the backends, when there’s no exact +equivalent, a similar mode is returned.

+
+
Parameters:
+
    +
  • interp_mode (UnionType[str, None]) – interpolation mode.

  • +
  • padding_mode – padding mode.

  • +
  • backend – optional backend of TransformBackends. If None, the backend will be decided from interp_mode.

  • +
  • kwargs – additional keyword arguments. currently support torch_interpolate_spatial_nd, to provide +additional information to determine linear, bilinear and trilinear; +use_compiled to use MONAI’s precompiled backend (pytorch c++ extensions), default to False.

  • +
+
+
+
+ +
+
+monai.transforms.utils.scale_affine(spatial_size, new_spatial_size, centered=True)[source]#
+

Compute the scaling matrix according to the new spatial size

+
+
Parameters:
+
    +
  • spatial_size – original spatial size.

  • +
  • new_spatial_size – new spatial size.

  • +
  • centered (bool) – whether the scaling is with respect to the image center (True, default) or corner (False).

  • +
+
+
Returns:
+

the scaling matrix.

+
+
+
+ +
+
+monai.transforms.utils.sync_meta_info(key, data_dict, t=True)[source]#
+

Given the key, sync up between metatensor data_dict[key] and meta_dict data_dict[key_transforms/meta_dict]. +t=True: the one with more applied_operations in metatensor vs meta_dict is the output, False: less is the output.

+
+ +
+
+monai.transforms.utils.weighted_patch_samples(spatial_size, w, n_samples=1, r_state=None)[source]#
+

Computes n_samples of random patch sampling locations, given the sampling weight map w and patch spatial_size.

+
+
Parameters:
+
    +
  • spatial_size (UnionType[int, Sequence[int]]) – length of each spatial dimension of the patch.

  • +
  • w (Union[ndarray, Tensor]) – weight map, the weights must be non-negative. each element denotes a sampling weight of the spatial location. +0 indicates no sampling. +The weight map shape is assumed (spatial_dim_0, spatial_dim_1, ..., spatial_dim_n).

  • +
  • n_samples (int) – number of patch samples

  • +
  • r_state (Optional[RandomState, None]) – a random state container

  • +
+
+
Return type:
+

list

+
+
Returns:
+

a list of n_samples N-D integers representing the spatial sampling location of patches.

+
+
+
+ +
+
+monai.transforms.utils.zero_margins(img, margin)[source]#
+

Returns True if the values within margin indices of the edges of img in dimensions 1 and 2 are 0.

+
+
Return type:
+

bool

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.allclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)[source]#
+

np.allclose with equivalent implementation for torch.

+
+
Return type:
+

bool

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.any_np_pt(x, axis)[source]#
+

np.any with equivalent implementation for torch.

+

For pytorch, convert to boolean for compatibility with older versions.

+
+
Parameters:
+
    +
  • x (Union[ndarray, Tensor]) – input array/tensor.

  • +
  • axis (UnionType[int, Sequence[int]]) – axis to perform any over.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Return a contiguous flattened array/tensor.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.argsort(a, axis=-1)[source]#
+

np.argsort with equivalent implementation for torch.

+
+
Parameters:
+
    +
  • a (~NdarrayTensor) – the array/tensor to sort.

  • +
  • axis (UnionType[int, None]) – axis along which to sort.

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
Returns:
+

Array/Tensor of indices that sort a along the specified axis.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.argwhere(a)[source]#
+

np.argwhere with equivalent implementation for torch.

+
+
Parameters:
+

a (~NdarrayTensor) – input data.

+
+
Return type:
+

~NdarrayTensor

+
+
Returns:
+

Indices of elements that are non-zero. Indices are grouped by element. +This array will have shape (N, a.ndim) where N is the number of non-zero items.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.ascontiguousarray(x, **kwargs)[source]#
+

np.ascontiguousarray with equivalent implementation for torch (contiguous).

+
+
Parameters:
+
+
+
Return type:
+

Union[ndarray, Tensor, ~T]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.clip(a, a_min, a_max)[source]#
+

np.clip with equivalent implementation for torch.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.concatenate(to_cat, axis=0, out=None)[source]#
+

np.concatenate with equivalent implementation for torch (torch.cat).

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.cumsum(a, axis=None, **kwargs)[source]#
+

np.cumsum with equivalent implementation for torch.

+
+
Parameters:
+
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.floor_divide(a, b)[source]#
+

np.floor_divide with equivalent implementation for torch.

+

As of pt1.8, use torch.div(…, rounding_mode=”floor”), and +before that, use torch.floor_divide.

+
+
Parameters:
+
    +
  • a (Union[ndarray, Tensor]) – first array/tensor

  • +
  • b – scalar to divide by

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Element-wise floor division between two arrays/tensors.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.in1d(x, y)[source]#
+

np.in1d with equivalent implementation for torch.

+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.isfinite(x)[source]#
+

np.isfinite with equivalent implementation for torch.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.isnan(x)[source]#
+

np.isnan with equivalent implementation for torch.

+
+
Parameters:
+

x (Union[ndarray, Tensor]) – array/tensor.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.max(x, dim=None, **kwargs)[source]#
+

torch.max with equivalent implementation for numpy

+
+
Parameters:
+

x (~NdarrayTensor) – array/tensor.

+
+
Return type:
+

~NdarrayTensor

+
+
Returns:
+

the maximum of x.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.maximum(a, b)[source]#
+

np.maximum with equivalent implementation for torch.

+
+
Parameters:
+
    +
  • a (Union[ndarray, Tensor]) – first array/tensor.

  • +
  • b (Union[ndarray, Tensor]) – second array/tensor.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Element-wise maximum between two arrays/tensors.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.mean(x, dim=None, **kwargs)[source]#
+

torch.mean with equivalent implementation for numpy

+
+
Parameters:
+

x (~NdarrayTensor) – array/tensor.

+
+
Return type:
+

~NdarrayTensor

+
+
Returns:
+

the mean of x

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.median(x, dim=None, **kwargs)[source]#
+

torch.median with equivalent implementation for numpy

+
+
Parameters:
+

x (~NdarrayTensor) – array/tensor.

+
+
+
+
Returns

the median of x.

+
+
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.min(x, dim=None, **kwargs)[source]#
+

torch.min with equivalent implementation for numpy

+
+
Parameters:
+

x (~NdarrayTensor) – array/tensor.

+
+
Return type:
+

~NdarrayTensor

+
+
Returns:
+

the minimum of x.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.mode(x, dim=-1, to_long=True)[source]#
+

torch.mode with equivalent implementation for numpy.

+
+
Parameters:
+
    +
  • x (~NdarrayTensor) – array/tensor.

  • +
  • dim (int) – dimension along which to perform mode (referred to as axis by numpy).

  • +
  • to_long (bool) – convert input to long before performing mode.

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.moveaxis(x, src, dst)[source]#
+

moveaxis for pytorch and numpy

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.nonzero(x)[source]#
+

np.nonzero with equivalent implementation for torch.

+
+
Parameters:
+

x (Union[ndarray, Tensor]) – array/tensor.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Index unravelled for given shape

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.percentile(x, q, dim=None, keepdim=False, **kwargs)[source]#
+

np.percentile with equivalent implementation for torch.

+

Pytorch uses quantile. For more details please refer to: +https://pytorch.org/docs/stable/generated/torch.quantile.html. +https://numpy.org/doc/stable/reference/generated/numpy.percentile.html.

+
+
Parameters:
+
    +
  • x (Union[ndarray, Tensor]) – input data.

  • +
  • q – percentile to compute (should in range 0 <= q <= 100).

  • +
  • dim (Optional[int, None]) – the dim along which the percentiles are computed. default is to compute the percentile +along a flattened version of the array.

  • +
  • keepdim (bool) – whether the output data has dim retained or not.

  • +
  • kwargs – if x is numpy array, additional args for np.percentile, more details: +https://numpy.org/doc/stable/reference/generated/numpy.percentile.html.

  • +
+
+
Return type:
+

Union[ndarray, Tensor, float, int]

+
+
Returns:
+

Resulting value (scalar)

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.ravel(x)[source]#
+

np.ravel with equivalent implementation for torch.

+
+
Parameters:
+

x (Union[ndarray, Tensor]) – array/tensor to ravel.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Return a contiguous flattened array/tensor.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.repeat(a, repeats, axis=None, **kwargs)[source]#
+

np.repeat with equivalent implementation for torch (repeat_interleave).

+
+
Parameters:
+
    +
  • a (Union[ndarray, Tensor]) – input data to repeat.

  • +
  • repeats (int) – number of repetitions for each element, repeats is broadcast to fit the shape of the given axis.

  • +
  • axis (Optional[int, None]) – axis along which to repeat values.

  • +
  • kwargs – if a is PyTorch Tensor, additional args for torch.repeat_interleave, more details: +https://pytorch.org/docs/stable/generated/torch.repeat_interleave.html.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.searchsorted(a, v, right=False, sorter=None, **kwargs)[source]#
+

np.searchsorted with equivalent implementation for torch.

+
+
Parameters:
+
    +
  • a (~NdarrayTensor) – numpy array or tensor, containing monotonically increasing sequence on the innermost dimension.

  • +
  • v (Union[ndarray, Tensor]) – containing the search values.

  • +
  • right – if False, return the first suitable location that is found, if True, return the last such index.

  • +
  • sorter – if a is numpy array, optional array of integer indices that sort array a into ascending order.

  • +
  • kwargs – if a is PyTorch Tensor, additional args for torch.searchsorted, more details: +https://pytorch.org/docs/stable/generated/torch.searchsorted.html.

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.stack(x, dim)[source]#
+

np.stack with equivalent implementation for torch.

+
+
Parameters:
+
    +
  • x (Sequence[~NdarrayTensor]) – array/tensor.

  • +
  • dim (int) – dimension along which to perform the stack (referred to as axis by numpy).

  • +
+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.std(x, dim=None, unbiased=False)[source]#
+

torch.std with equivalent implementation for numpy

+
+
Parameters:
+

x (~NdarrayTensor) – array/tensor.

+
+
Return type:
+

~NdarrayTensor

+
+
Returns:
+

the standard deviation of x.

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.unique(x, **kwargs)[source]#
+

torch.unique with equivalent implementation for numpy.

+
+
Parameters:
+

x (~NdarrayTensor) – array/tensor.

+
+
Return type:
+

~NdarrayTensor

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.unravel_index(idx, shape)[source]#
+

np.unravel_index with equivalent implementation for torch.

+
+
Parameters:
+
    +
  • idx – index to unravel.

  • +
  • shape – shape of array/tensor.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Index unravelled for given shape

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.unravel_indices(idx, shape)[source]#
+

Computing unravel coordinates from indices.

+
+
Parameters:
+
    +
  • idx – a sequence of indices to unravel.

  • +
  • shape – shape of array/tensor.

  • +
+
+
Return type:
+

Union[ndarray, Tensor]

+
+
Returns:
+

Stacked indices unravelled for given shape

+
+
+
+ +
+
+monai.transforms.utils_pytorch_numpy_unification.where(condition, x=None, y=None)[source]#
+

Note that torch.where may convert y.dtype to x.dtype.

+
+
Return type:
+

Union[ndarray, Tensor]

+
+
+
+ +
+
+ + +
+ + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file From 3ba57cecf55b758e31a3e906e292b6663a3adb51 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 08:49:40 +0000 Subject: [PATCH 154/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/index.html | 312 ++++++++++++++++++++--------------------- docs/transforms.html | 324 +++++++++++++++++++++---------------------- 2 files changed, 318 insertions(+), 318 deletions(-) diff --git a/docs/index.html b/docs/index.html index bc1477a7d2..58ffbd2f61 100644 --- a/docs/index.html +++ b/docs/index.html @@ -9,20 +9,20 @@ Project MONAI — MONAI 0 Documentation - - - + + + - + - + @@ -30,7 +30,7 @@ - + @@ -46,26 +46,26 @@ - - + + - - + + - + - + - +
@@ -86,41 +86,41 @@ Ctrl+K
- + - +
- +
- - + + - + - - + + - +
- +
- - + +
- +
- - - - + + + +
- - - + + +
- + - \ No newline at end of file + diff --git a/docs/transforms.html b/docs/transforms.html index d254506868..d94f834734 100644 --- a/docs/transforms.html +++ b/docs/transforms.html @@ -9,20 +9,20 @@ Transforms — MONAI 0 Documentation - - - + + + - + - + @@ -30,7 +30,7 @@ - + @@ -47,26 +47,26 @@ - - + + - - + + - + - + - +
@@ -87,41 +87,41 @@ Ctrl+K
- + - +
- +
- - + + - + - - + + - +
- +
- - + +
- +
- +
- +
- +
- - + +
- - - - + + + +
- - - + + +
- + - \ No newline at end of file + From 8525e6d7a4c0d5dc7ba04253d03b0276c955b8cc Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 09:53:40 +0100 Subject: [PATCH 155/175] Fixing doc typos Signed-off-by: Ben Murray --- monai/transforms/compose.py | 4 ++-- monai/transforms/lazy/executors.py | 4 ++-- monai/transforms/transform.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 8666c25fea..d62633fb3a 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -71,7 +71,7 @@ def execute_compose( unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. start: the index of the first transform to be executed. If not set, this defaults to 0 - end: the index after the last transform to be exectued. If set, the transform at index-1 + end: the index after the last transform to be executed. If set, the transform at index-1 is the last transform that is executed. If this is not set, it defaults to len(transforms) lazy: whether to enable :ref:`lazy evaluation` for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will @@ -79,7 +79,7 @@ def execute_compose( overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when - :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + :ref:`lazy evaluation` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. threading: whether executing is happening in a threaded environment. If set, copies are made diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 760ddd8da3..26d384f619 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -138,13 +138,13 @@ def apply_pending_transforms_in_order( This method causes "in order" processing of pending transforms to occur. "in order" processing of pending transforms ensures that all pending transforms have been applied to the tensor before a non-lazy transform (or lazy transform that is executing non-lazily) is carried out. - It ensures that no operations will be added to a metatensors's apply_operations while there are outstanding + It ensures that no operations will be added to a metatensor's apply_operations while there are outstanding pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this is expected to change in future releases. This method is designed to be used only in the context of implementing lazy resampling functionality. In general you should not need to interact with or use this method directly, and its API may change without warning between - releases. See the :ref:`lazy_resampling for more information about lazy resampling. + releases. See the :ref:`Lazy Resampling topic for more information about lazy resampling. Args: transform: a transform that should be evaluated to determine whether pending transforms should be applied diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 1570749c2e..89a3676b0a 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -74,11 +74,11 @@ def _apply_transform( lazy: whether to enable lazy evaluation for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. - See the :ref:`lazy_resampling for more information about lazy resampling. + See the :ref:`Lazy Resampling topic for more information about lazy resampling. overrides: this optional parameter allows you to specify a dictionary of parameters that should be overridden when executing a pipeline. These each parameter that is compatible with a given transform is then applied to that transform before it is executed. Note that overrides are currently only applied when - :ref:`lazy resampling` is enabled for the pipeline or a given transform. If lazy is False + :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. @@ -124,10 +124,10 @@ def apply_transform( log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the default logger name. Setting it to a string specifies the logger to which errors should be logged. - lazy: whether to execute in lazy mode or not. See the :ref:`lazy_resampling for more + lazy: whether to execute in lazy mode or not. See the :ref:`Lazy Resampling topic for more information about lazy resampling. overrides: optional overrides to apply to transform parameters. This parameter is ignored unless transforms - are being executed lazily. See the :ref:`lazy_resampling for more details and + are being executed lazily. See the :ref:`Lazy Resampling topic for more details and examples of its usage. Raises: From ee6751f066fa30ff3a57914f0cd895b0cf2b042a Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 09:54:27 +0100 Subject: [PATCH 156/175] Removing accidentally added html files Signed-off-by: Ben Murray --- docs/index.html | 656 -- docs/transforms.html | 24586 ----------------------------------------- 2 files changed, 25242 deletions(-) delete mode 100644 docs/index.html delete mode 100644 docs/transforms.html diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 58ffbd2f61..0000000000 --- a/docs/index.html +++ /dev/null @@ -1,656 +0,0 @@ - - - - - - - - - - - Project MONAI — MONAI 0 Documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - Ctrl+K -
-
- - - -
-
- -
- - - - - - - - - - - - - -
- -
- - -
-
- -
- - - - - -
- -
-

Project MONAI#

-

Medical Open Network for AI

-

MONAI is a PyTorch-based, open-source framework -for deep learning in healthcare imaging, part of the PyTorch Ecosystem.

-

Its ambitions are:

-
    -
  • developing a community of academic, industrial and clinical researchers collaborating on a common foundation;

  • -
  • creating state-of-the-art, end-to-end training workflows for healthcare imaging;

  • -
  • providing researchers with an optimized and standardized way to create and evaluate deep learning models.

  • -
-
-

Features#

-
    -
  • flexible pre-processing for multi-dimensional medical imaging data;

  • -
  • compositional & portable APIs for ease of integration in existing workflows;

  • -
  • domain-specific implementations for networks, losses, evaluation metrics and more;

  • -
  • customizable design for varying user expertise;

  • -
  • multi-GPU data parallelism support.

  • -
-
-
-

Getting started#

-

MedNIST demo and MONAI for PyTorch Users are available on Colab.

-

Examples and notebook tutorials are located at Project-MONAI/tutorials.

-

Technical documentation is available at docs.monai.io.

-
-

Feature highlights

- -
-
-

API Reference

- -
-
-

Installation

- -
-
-

Contributing

- -
-
-

Specifications

- -
-
-
-

Model Zoo#

-

The MONAI Model Zoo is a place for researchers and data scientists to share the latest and great models from the community. -Utilizing the MONAI Bundle format makes it easy to get started building workflows with MONAI.

-
- -
-
-

Indices and tables#

- -
- - -
- - - - - -
- - - -
- - -
-
- -
- -
-
-
- - - - - -
- - -
- - diff --git a/docs/transforms.html b/docs/transforms.html deleted file mode 100644 index d94f834734..0000000000 --- a/docs/transforms.html +++ /dev/null @@ -1,24586 +0,0 @@ - - - - - - - - - - - Transforms — MONAI 0 Documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -
-

Transforms#

-
-

Generic Interfaces#

-
-

Transform#

-
-
-class monai.transforms.Transform[source]#
-

An abstract class of a Transform. -A transform is callable that processes data.

-

It could be stateful and may modify data in place, -the implementation should be aware of:

-
-
    -
  1. thread safety when mutating its own states. -When used from a multi-process context, transform’s instance variables are read-only. -thread-unsafe transforms should inherit monai.transforms.ThreadUnsafe.

  2. -
  3. data content unused by this transform may still be used in the -subsequent transforms in a composed transform.

  4. -
  5. storing too much information in data may cause some memory issue or IPC sync issue, -especially in the multi-processing environment of PyTorch DataLoader.

  6. -
-
-

See Also

-
-
-
-
-abstract __call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

MapTransform#

-
-
-class monai.transforms.MapTransform(keys, allow_missing_keys=False)[source]#
-

A subclass of monai.transforms.Transform with an assumption -that the data input of self.__call__ is a MutableMapping such as dict.

-

The keys parameter will be used to get and set the actual data -item to transform. That is, the callable of this transform should -follow the pattern:

-
-
def __call__(self, data):
-    for key in self.keys:
-        if key in data:
-            # update output data with some_transform_function(data[key]).
-        else:
-            # raise exception unless allow_missing_keys==True.
-    return data
-
-
-
-
-
Raises:
-
    -
  • ValueError – When keys is an empty iterable.

  • -
  • TypeError – When keys type is not in Union[Hashable, Iterable[Hashable]].

  • -
-
-
-
-
-abstract __call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-call_update(data)[source]#
-

This function is to be called after every self.__call__(data), -update data[key_transforms] and data[key_meta_dict] using the content from MetaTensor data[key], -for MetaTensor backward compatibility 0.9.0.

-
- -
-
-first_key(data)[source]#
-

Get the first available key of self.keys in the input data dictionary. -If no available key, return an empty tuple ().

-
-
Parameters:
-

data (dict[Hashable, Any]) – data that the transform will be applied to.

-
-
-
- -
-
-key_iterator(data, *extra_iterables)[source]#
-

Iterate across keys and optionally extra iterables. If key is missing, exception is raised if -allow_missing_keys==False (default). If allow_missing_keys==True, key is skipped.

-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Any]) – data that the transform will be applied to

  • -
  • extra_iterables (UnionType[Iterable, None]) – anything else to be iterated through

  • -
-
-
Return type:
-

Generator

-
-
-
- -
- -
-
-

RandomizableTrait#

-
-
-class monai.transforms.RandomizableTrait[source]#
-

An interface to indicate that the transform has the capability to perform -randomized transforms to the data that it is called upon. This interface -can be extended from by people adapting transforms to the MONAI framework as well as by -implementors of MONAI transforms.

-
- -
-
-

LazyTrait#

-
-
-class monai.transforms.LazyTrait[source]#
-

An interface to indicate that the transform has the capability to execute using -MONAI’s lazy resampling feature. In order to do this, the implementing class needs -to be able to describe its operation as an affine matrix or grid with accompanying metadata. -This interface can be extended from by people adapting transforms to the MONAI framework as -well as by implementors of MONAI transforms.

-
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

MultiSampleTrait#

-
-
-class monai.transforms.MultiSampleTrait[source]#
-

An interface to indicate that the transform has the capability to return multiple samples -given an input, such as when performing random crops of a sample. This interface can be -extended from by people adapting transforms to the MONAI framework as well as by implementors -of MONAI transforms.

-
- -
-
-

Randomizable#

-
-
-class monai.transforms.Randomizable[source]#
-

An interface for handling random state locally, currently based on a class -variable R, which is an instance of np.random.RandomState. This -provides the flexibility of component-specific determinism without -affecting the global states. It is recommended to use this API with -monai.data.DataLoader for deterministic behaviour of the -preprocessing pipelines. This API is not thread-safe. Additionally, -deepcopying instance of this class often causes insufficient randomness as -the random states will be duplicated.

-
-
-randomize(data)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Randomizable

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

LazyTransform#

-
-
-class monai.transforms.LazyTransform(lazy=False)[source]#
-

An implementation of functionality for lazy transforms that can be subclassed by array and -dictionary transforms to simplify implementation of new lazy transforms.

-
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandomizableTransform#

-
-
-class monai.transforms.RandomizableTransform(prob=1.0, do_transform=True)[source]#
-

An interface for handling random state locally, currently based on a class variable R, -which is an instance of np.random.RandomState. -This class introduces a randomized flag _do_transform, is mainly for randomized data augmentation transforms. -For example:

-
from monai.transforms import RandomizableTransform
-
-class RandShiftIntensity100(RandomizableTransform):
-    def randomize(self):
-        super().randomize(None)
-        self._offset = self.R.uniform(low=0, high=100)
-
-    def __call__(self, img):
-        self.randomize()
-        if not self._do_transform:
-            return img
-        return img + self._offset
-
-transform = RandShiftIntensity()
-transform.set_random_state(seed=0)
-print(transform(10))
-
-
-
-
-randomize(data)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

Compose#

-
-
-class monai.transforms.Compose(transforms=None, map_items=True, unpack_items=False, log_stats=False, lazy=False, overrides=None, logger_name=False)[source]#
-

Compose provides the ability to chain a series of callables together in -a sequential manner. Each transform in the sequence must take a single -argument and return a single value.

-

Compose can be used in two ways:

-
    -
  1. With a series of transforms that accept and return a single -ndarray / tensor / tensor-like parameter.

  2. -
  3. With a series of transforms that accept and return a dictionary that -contains one or more parameters. Such transforms must have pass-through -semantics that unused values in the dictionary must be copied to the return -dictionary. It is required that the dictionary is copied between input -and output of each transform.

  4. -
-

If some transform takes a data item dictionary as input, and returns a -sequence of data items in the transform chain, all following transforms -will be applied to each item of this list if map_items is True (the -default). If map_items is False, the returned sequence is passed whole -to the next callable in the chain.

-

For example:

-

A Compose([transformA, transformB, transformC], -map_items=True)(data_dict) could achieve the following patch-based -transformation on the data_dict input:

-
    -
  1. transformA normalizes the intensity of ‘img’ field in the data_dict.

  2. -
  3. transformB crops out image patches from the ‘img’ and ‘seg’ of -data_dict, and return a list of three patch samples:

    -
    {'img': 3x100x100 data, 'seg': 1x100x100 data, 'shape': (100, 100)}
    -                     applying transformB
    -                         ---------->
    -[{'img': 3x20x20 data, 'seg': 1x20x20 data, 'shape': (20, 20)},
    - {'img': 3x20x20 data, 'seg': 1x20x20 data, 'shape': (20, 20)},
    - {'img': 3x20x20 data, 'seg': 1x20x20 data, 'shape': (20, 20)},]
    -
    -
    -
  4. -
  5. transformC then randomly rotates or flips ‘img’ and ‘seg’ of -each dictionary item in the list returned by transformB.

  6. -
-

The composed transforms will be set the same global random seed if user called -set_determinism().

-

When using the pass-through dictionary operation, you can make use of -monai.transforms.adaptors.adaptor to wrap transforms that don’t conform -to the requirements. This approach allows you to use transforms from -otherwise incompatible libraries with minimal additional work.

-
-

Note

-

In many cases, Compose is not the best way to create pre-processing -pipelines. Pre-processing is often not a strictly sequential series of -operations, and much of the complexity arises when a not-sequential -set of functions must be called as if it were a sequence.

-

Example: images and labels -Images typically require some kind of normalization that labels do not. -Both are then typically augmented through the use of random rotations, -flips, and deformations. -Compose can be used with a series of transforms that take a dictionary -that contains ‘image’ and ‘label’ entries. This might require wrapping -torchvision transforms before passing them to compose. -Alternatively, one can create a class with a __call__ function that -calls your pre-processing functions taking into account that not all of -them are called on the labels.

-
-
-
Lazy resampling:

Lazy resampling is an experimental feature introduced in 1.2. Its purpose is -to reduce the number of resample operations that must be carried out when executing -a pipeline of transforms. This can provide significant performance improvements in -terms of pipeline executing speed and memory usage, but can also significantly -reduce the loss of information that occurs when performing a number of spatial -resamples in succession.

-

Lazy resampling can be thought of as acting in a similar fashion to the Affine & RandAffine -transforms, in that they allow several spatial transform operations can be specified and carried out with -a single resample step. Unlike these transforms, however, lazy resampling can operate on any set of -transforms specified in any ordering. The user is free to mix monai transforms with transforms from other -libraries; lazy resampling will determine the minimum number of resample steps required in order to -execute the pipeline.

-

Lazy resampling works with monai Dataset classes that provide caching and persistence. However, if you -are implementing your own caching dataset implementation and wish to make use of lazy resampling, you -should ensure that you fully execute the part of the pipeline that generates the data to be cached -before caching it. This is quite simply done however, as shown by the following example.

-

Lazy resampling can be enabled or disabled through the lazy parameter. This is specified as an -optional boolean parameter.

-
    -
  • False (default): Don’t perform any lazy resampling

  • -
  • None: Perform lazy resampling based on the ‘lazy’ properties of the transform instances.

  • -
  • True: Always perform lazy resampling if possible. This will ignore the lazy properties -of the transform instances

  • -
-

If you only want some of the pipeline to be executed lazily, there are two ways to achieve this.

-

The first way is to set lazy=True on your Compose instance and specify for each transform whether you -want it to be lazily executed or not.

-

The second way is to set lazy=True on your Compose instance and add ApplyPending or ApplyPendingd -transforms after the final transform in a sequence that you want to execute lazily. This can be done at multiple -points in the pipeline.

-
-
Example:

# run the part of the pipeline that needs to be cached -data = self.transform(data, end=self.post_cache_index)

-

# —

-

# fetch the data from the cache and run the rest of the pipeline -data = get_data_from_my_cache(data) -data = self.transform(data, start=self.post_cache_index)

-
-
-
-
-
-
Parameters:
-
    -
  • transforms (Union[Sequence[Callable], Callable, None]) – sequence of callables.

  • -
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. -defaults to True.

  • -
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. -defaults to False.

  • -
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which -disables the logger for processing pipeline errors. Setting it to True will enable logging to the -default logger name. Setting it to a string specifies the logger to which errors should be logged

  • -
  • lazy (UnionType[bool, None]) – whether to enable lazy evaluation for lazy transforms. This is an optional bool that can take -the following values. If lazy=False, lazy execution is disabled and transforms will be -carried out on a transform by transform basis. If lazy=True, all lazy transforms will -be executed by accumulating changes and resampling as few times as possible. If lazy is None, -Compose will perform lazy execution on lazy transforms that have their lazy flag set to True. -A monai.transforms.ApplyPending[d] transform in the pipeline will trigger the evaluation of -the pending operations and make the primary data up-to-date.

  • -
  • overrides (Optional[dict, None]) – this optional parameter allows you to specify a dictionary of parameters that should be overridden -when executing a pipeline. These each parameter that is compatible with a given transform is then applied -to that transform before it is executed. Note that overrides are currently only applied when lazy -is True. If lazy is False they are ignored. -currently supported args are: -{"mode", "padding_mode", "dtype", "align_corners", "resample_mode", device}, -please see also monai.transforms.lazy.apply_transforms() for more details.

  • -
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. -Setting this to False disables logging. Setting it to True enables logging to the default loggers. -Setting a string overrides the logger name to which logging is performed.

  • -
-
-
-
-
-__call__(input_, start=0, end=None, threading=False, lazy=None)[source]#
-

Call self as a function.

-
- -
-
-flatten()[source]#
-

Return a Composition with a simple list of transforms, as opposed to any nested Compositions.

-

e.g., t1 = Compose([x, x, x, x, Compose([Compose([x, x]), x, x])]).flatten() -will result in the equivalent of t1 = Compose([x, x, x, x, x, x, x, x]).

-
- -
-
-get_index_of_first(predicate)[source]#
-

get_index_of_first takes a predicate and returns the index of the first transform that -satisfies the predicate (ie. makes the predicate return True). If it is unable to find -a transform that satisfies the predicate, it returns None.

-

Example

-

c = Compose([Flip(…), Rotate90(…), Zoom(…), RandRotate(…), Resize(…)])

-

print(c.get_index_of_first(lambda t: isinstance(t, RandomTrait))) ->>> 3 -print(c.get_index_of_first(lambda t: isinstance(t, Compose))) ->>> None

-
-

Note

-

This is only performed on the transforms directly held by this instance. If this -instance has nested Compose transforms or other transforms that contain transforms, -it does not iterate into them.

-
-
-
Parameters:
-
    -
  • predicate – a callable that takes a single argument and returns a bool. When called

  • -
  • compose (it is passed a transform from the sequence of transforms contained by this) –

  • -
  • instance.

  • -
-
-
Returns:
-

The index of the first transform in the sequence for which predicate returns -True. None if no transform satisfies the predicate

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Compose

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

InvertibleTransform#

-
-
-class monai.transforms.InvertibleTransform[source]#
-

Classes for invertible transforms.

-

This class exists so that an invert method can be implemented. This allows, for -example, images to be cropped, rotated, padded, etc., during training and inference, -and after be returned to their original size before saving to file for comparison in -an external viewer.

-

When the inverse method is called:

-
-
    -
  • the inverse is called on each key individually, which allows for -different parameters being passed to each label (e.g., different -interpolation for image and label).

  • -
  • the inverse transforms are applied in a last-in-first-out order. As -the inverse is applied, its entry is removed from the list detailing -the applied transformations. That is to say that during the forward -pass, the list of applied transforms grows, and then during the -inverse it shrinks back down to an empty list.

  • -
-
-

We currently check that the id() of the transform is the same in the forward and -inverse directions. This is a useful check to ensure that the inverses are being -processed in the correct order.

-

Note to developers: When converting a transform to an invertible transform, you need to:

-
-
    -
  1. Inherit from this class.

  2. -
  3. In __call__, add a call to push_transform.

  4. -
  5. Any extra information that might be needed for the inverse can be included with the -dictionary extra_info. This dictionary should have the same keys regardless of -whether do_transform was True or False and can only contain objects that are -accepted in pytorch data loader’s collate function (e.g., None is not allowed).

  6. -
  7. Implement an inverse method. Make sure that after performing the inverse, -pop_transform is called.

  8. -
-
-
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Any

-
-
-
- -
-
-inverse_update(data)[source]#
-

This function is to be called before every self.inverse(data), -update each MetaTensor data[key] using data[key_transforms] and data[key_meta_dict], -for MetaTensor backward compatibility 0.9.0.

-
- -
- -
-
-

TraceableTransform#

-
-
-class monai.transforms.TraceableTransform[source]#
-

Maintains a stack of applied transforms to data.

-
-
Data can be one of two types:
    -
  1. A MetaTensor (this is the preferred data type).

  2. -
  3. -
    A dictionary of data containing arrays/tensors and auxiliary metadata. In

    this case, a key must be supplied (this dictionary-based approach is deprecated).

    -
    -
    -
  4. -
-
-
-

If data is of type MetaTensor, then the applied transform will be added to data.applied_operations.

-
-
If data is a dictionary, then one of two things can happen:
    -
  1. If data[key] is a MetaTensor, the applied transform will be added to data[key].applied_operations.

  2. -
  3. -
    Else, the applied transform will be appended to an adjacent list using

    trace_key. If, for example, the key is image, then the transform -will be appended to image_transforms (this dictionary-based approach is deprecated).

    -
    -
    -
  4. -
-
-
Hopefully it is clear that there are three total possibilities:
    -
  1. data is MetaTensor

  2. -
  3. data is dictionary, data[key] is MetaTensor

  4. -
  5. data is dictionary, data[key] is not MetaTensor (this is a deprecated approach).

  6. -
-
-
-

The __call__ method of this transform class must be implemented so -that the transformation information is stored during the data transformation.

-

The information in the stack of applied transforms must be compatible with the -default collate, by only storing strings, numbers and arrays.

-

tracing could be enabled by self.set_tracing or setting -MONAI_TRACE_TRANSFORM when initializing the class.

-
-
-check_transforms_match(transform)[source]#
-

Check transforms are of same instance.

-
-
Return type:
-

None

-
-
-
- -
-
-get_most_recent_transform(data, key=None, check=True, pop=False)[source]#
-

Get most recent transform for the stack.

-
-
Parameters:
-
    -
  • data – dictionary of data or MetaTensor.

  • -
  • key (Optional[Hashable, None]) – if data is a dictionary, data[key] will be modified.

  • -
  • check (bool) – if true, check that self is the same type as the most recently-applied transform.

  • -
  • pop (bool) – if true, remove the transform as it is returned.

  • -
-
-
Returns:
-

Dictionary of most recently applied transform

-
-
Raises:
-

- RuntimeError – data is neither MetaTensor nor dictionary

-
-
-
- -
-
-get_transform_info()[source]#
-

Return a dictionary with the relevant information pertaining to an applied transform.

-
-
Return type:
-

dict

-
-
-
- -
-
-pop_transform(data, key=None, check=True)[source]#
-

Return and pop the most recent transform.

-
-
Parameters:
-
    -
  • data – dictionary of data or MetaTensor

  • -
  • key (Optional[Hashable, None]) – if data is a dictionary, data[key] will be modified

  • -
  • check (bool) – if true, check that self is the same type as the most recently-applied transform.

  • -
-
-
Returns:
-

Dictionary of most recently applied transform

-
-
Raises:
-

- RuntimeError – data is neither MetaTensor nor dictionary

-
-
-
- -
-
-push_transform(data, *args, **kwargs)[source]#
-

Push to a stack of applied transforms of data.

-
-
Parameters:
-
    -
  • data – dictionary of data or MetaTensor.

  • -
  • args – additional positional arguments to track_transform_meta.

  • -
  • kwargs – additional keyword arguments to track_transform_meta, -set replace=True (default False) to rewrite the last transform infor in -applied_operation/pending_operation based on self.get_transform_info().

  • -
-
-
-
- -
-
-set_tracing(tracing)[source]#
-

Set whether to trace transforms.

-
-
Return type:
-

None

-
-
-
- -
-
-static trace_key(key=None)[source]#
-

The key to store the stack of applied transforms.

-
- -
-
-trace_transform(to_trace)[source]#
-

Temporarily set the tracing status of a transform with a context manager.

-
- -
-
-classmethod track_transform_meta(data, key=None, sp_size=None, affine=None, extra_info=None, orig_size=None, transform_info=None, lazy=False)[source]#
-

Update a stack of applied/pending transforms metadata of data.

-
-
Parameters:
-
    -
  • data – dictionary of data or MetaTensor.

  • -
  • key (Optional[Hashable, None]) – if data is a dictionary, data[key] will be modified.

  • -
  • sp_size – the expected output spatial size when the transform is applied. -it can be tensor or numpy, but will be converted to a list of integers.

  • -
  • affine – the affine representation of the (spatial) transform in the image space. -When the transform is applied, meta_tensor.affine will be updated to meta_tensor.affine @ affine.

  • -
  • extra_info (Optional[dict, None]) – if desired, any extra information pertaining to the applied -transform can be stored in this dictionary. These are often needed for -computing the inverse transformation.

  • -
  • orig_size (Optional[tuple, None]) – sometimes during the inverse it is useful to know what the size -of the original image was, in which case it can be supplied here.

  • -
  • transform_info – info from self.get_transform_info().

  • -
  • lazy – whether to push the transform to pending_operations or applied_operations.

  • -
-
-
Returns:
-

For backward compatibility, if data is a dictionary, it returns the dictionary with -updated data[key]. Otherwise, this function returns a MetaObj with updated transform metadata.

-
-
-
- -
-
-static transform_info_keys()[source]#
-

The keys to store necessary info of an applied transform.

-
- -
- -
-
-

BatchInverseTransform#

-
-
-class monai.transforms.BatchInverseTransform(transform, loader, collate_fn=<function no_collation>, num_workers=0, detach=True, pad_batch=True, fill_value=None)[source]#
-

Perform inverse on a batch of data. This is useful if you have inferred a batch of images and want to invert -them all.

-
-
-__init__(transform, loader, collate_fn=<function no_collation>, num_workers=0, detach=True, pad_batch=True, fill_value=None)[source]#
-
-
Parameters:
-
    -
  • transform (InvertibleTransform) – a callable data transform on input data.

  • -
  • loader (DataLoader) – data loader used to run transforms and generate the batch of data.

  • -
  • collate_fn (UnionType[Callable, None]) – how to collate data after inverse transformations. -default won’t do any collation, so the output will be a list of size batch size.

  • -
  • num_workers (UnionType[int, None]) – number of workers when run data loader for inverse transforms, -default to 0 as only run 1 iteration and multi-processing may be even slower. -if the transforms are really slow, set num_workers for multi-processing. -if set to None, use the num_workers of the transform data loader.

  • -
  • detach (bool) – whether to detach the tensors. Scalars tensors will be detached into number types -instead of torch tensors.

  • -
  • pad_batch (bool) – when the items in a batch indicate different batch size, -whether to pad all the sequences to the longest. -If False, the batch size will be the length of the shortest sequence.

  • -
  • fill_value – the value to fill the padded sequences when pad_batch=True.

  • -
-
-
-
- -
- -
-
-

Decollated#

-
-
-class monai.transforms.Decollated(keys=None, detach=True, pad_batch=True, fill_value=None, allow_missing_keys=False)[source]#
-

Decollate a batch of data. If input is a dictionary, it also supports to only decollate specified keys. -Note that unlike most MapTransforms, it will delete the other keys that are not specified. -if keys=None, it will decollate all the data in the input. -It replicates the scalar values to every item of the decollated list.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable, None]) – keys of the corresponding items to decollate, note that it will delete other keys not specified. -if None, will decollate all the keys. see also: monai.transforms.compose.MapTransform.

  • -
  • detach (bool) – whether to detach the tensors. Scalars tensors will be detached into number types -instead of torch tensors.

  • -
  • pad_batch (bool) – when the items in a batch indicate different batch size, -whether to pad all the sequences to the longest. -If False, the batch size will be the length of the shortest sequence.

  • -
  • fill_value – the value to fill the padded sequences when pad_batch=True.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-

OneOf#

-
-
-class monai.transforms.OneOf(transforms=None, weights=None, map_items=True, unpack_items=False, log_stats=False, lazy=None, overrides=None, logger_name=False)[source]#
-

OneOf provides the ability to randomly choose one transform out of a -list of callables with pre-defined probabilities for each.

-
-
Parameters:
-
    -
  • transforms (Union[Sequence[Callable], Callable, None]) – sequence of callables.

  • -
  • weights (Union[Sequence[float], float, None]) – probabilities corresponding to each callable in transforms. -Probabilities are normalized to sum to one.

  • -
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. -defaults to True.

  • -
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. -defaults to False.

  • -
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which -disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the -default logger name. Setting it to a string specifies the logger to which errors should be logged

  • -
  • lazy (Optional[bool, None]) – whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will -be executed by accumulating changes and resampling as few times as possible. If False, transforms will be -carried out on a transform by transform basis. -A monai.transforms.Identity[D] transform in the pipeline will trigger the evaluation of -the pending operations and make the primary data up-to-date.

  • -
  • overrides (Optional[dict, None]) – this optional parameter allows you to specify a dictionary of parameters that should be overridden -when executing a pipeline. These each parameter that is compatible with a given transform is then applied -to that transform before it is executed. Note that overrides are currently only applied when lazy -is True. If lazy is False they are ignored. -currently supported args are: -{"mode", "padding_mode", "dtype", "align_corners", "resample_mode", device}, -please see also monai.transforms.lazy.apply_transforms() for more details.

  • -
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. -Setting this to False disables logging. Setting it to True enables logging to the default loggers. -Setting a string overrides the logger name to which logging is performed.

  • -
-
-
-
-
-flatten()[source]#
-

Return a Composition with a simple list of transforms, as opposed to any nested Compositions.

-

e.g., t1 = Compose([x, x, x, x, Compose([Compose([x, x]), x, x])]).flatten() -will result in the equivalent of t1 = Compose([x, x, x, x, x, x, x, x]).

-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

RandomOrder#

-
-
-class monai.transforms.RandomOrder(transforms=None, map_items=True, unpack_items=False, log_stats=False, lazy=None, overrides=None, logger_name=False)[source]#
-

RandomOrder provides the ability to apply a list of transformations in random order.

-
-
Parameters:
-
    -
  • transforms (Union[Sequence[Callable], Callable, None]) – sequence of callables.

  • -
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. -defaults to True.

  • -
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. -defaults to False.

  • -
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which -disables the logger for processing pipeline errors. Setting it to True will enable logging to the -default logger name. Setting it to a string specifies the logger to which errors should be logged

  • -
  • lazy (Optional[bool, None]) – whether to enable lazy evaluation for lazy transforms. If True, all lazy transforms will -be executed by accumulating changes and resampling as few times as possible. If False, transforms will be -carried out on a transform by transform basis. -A monai.transforms.Identity[D] transform in the pipeline will trigger the evaluation of -the pending operations and make the primary data up-to-date.

  • -
  • overrides (Optional[dict, None]) – this optional parameter allows you to specify a dictionary of parameters that should be overridden -when executing a pipeline. These each parameter that is compatible with a given transform is then applied -to that transform before it is executed. Note that overrides are currently only applied when lazy -is True. If lazy is False they are ignored. -currently supported args are: -{"mode", "padding_mode", "dtype", "align_corners", "resample_mode", device}, -please see also monai.transforms.lazy.apply_transforms() for more details.

  • -
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. -Setting this to False disables logging. Setting it to True enables logging to the default loggers. -Setting a string overrides the logger name to which logging is performed.

  • -
-
-
-
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

SomeOf#

-
-
-class monai.transforms.SomeOf(transforms=None, map_items=True, unpack_items=False, log_stats=False, *, lazy=False, num_transforms=None, replace=False, weights=None, logger_name=False)[source]#
-

SomeOf samples a different sequence of transforms to apply each time it is called.

-

It can be configured to sample a fixed or varying number of transforms each time its called. Samples are drawn -uniformly, or from user supplied transform weights. When varying the number of transforms sampled per call, -the number of transforms to sample that call is sampled uniformly from a range supplied by the user.

-
-
Parameters:
-
    -
  • transforms (Union[Sequence[Callable], Callable, None]) – list of callables.

  • -
  • map_items (bool) – whether to apply transform to each item in the input data if data is a list or tuple. -Defaults to True.

  • -
  • unpack_items (bool) – whether to unpack input data with * as parameters for the callable function of transform. -Defaults to False.

  • -
  • log_stats (UnionType[bool, str]) – log errors when they occur in the processing pipeline. By default, this is set to False, which -disables the logger for processing pipeline errors. Setting it to True will enable logging to the -default logger name. Setting it to a string specifies the logger to which errors should be logged

  • -
  • num_transforms (Union[int, tuple[int, int], None]) – a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of -transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. -None sets it to len(transforms). Default to None.

  • -
  • replace (bool) – whether to sample with replacement. Defaults to False.

  • -
  • weights (Optional[list[int], None]) – weights to use in for sampling transforms. Will be normalized to 1. Default: None (uniform).

  • -
  • logger_name (UnionType[bool, str]) – this optional parameter allows you to specify a logger by name for logging of pipeline execution. -Setting this to False disables logging. Setting it to True enables logging to the default loggers. -Setting a string overrides the logger name to which logging is performed.

  • -
-
-
-
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-
-

Functionals#

-
-

Crop and Pad (functional)#

-

A collection of “functional” transforms for spatial operations -Project-MONAI/MONAI

-
-
-monai.transforms.croppad.functional.crop_func(img, slices, lazy, transform_info)[source]#
-

Functional implementation of cropping a MetaTensor. This function operates eagerly or lazily according -to lazy (default False).

-
-
Parameters:
-
    -
  • img (Tensor) – data to be transformed, assuming img is channel-first and cropping doesn’t apply to the channel dim.

  • -
  • slices (tuple[slice, …]) – the crop slices computed based on specified center & size or start & end or slices.

  • -
  • lazy (bool) – a flag indicating whether the operation should be performed in a lazy fashion or not.

  • -
  • transform_info (dict) – a dictionary with the relevant information pertaining to an applied transform.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-monai.transforms.croppad.functional.crop_or_pad_nd(img, translation_mat, spatial_size, mode, **kwargs)[source]#
-

Crop or pad using the translation matrix and spatial size. The translation coefficients are rounded -to the nearest integers. For a more generic implementation, please see monai.transforms.SpatialResample.

-
-
Parameters:
-
    -
  • img (Tensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • -
  • translation_mat – the translation matrix to be applied to the image. A translation matrix generated by, -for example, monai.transforms.utils.create_translate(). The translation coefficients are rounded -to the nearest integers.

  • -
  • spatial_size (tuple[int, …]) – the spatial size of the output image.

  • -
  • mode (str) – the padding mode.

  • -
  • kwargs – other arguments for the np.pad or torch.pad function.

  • -
-
-
-
- -
-
-monai.transforms.croppad.functional.pad_func(img, to_pad, transform_info, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
-

Functional implementation of padding a MetaTensor. This function operates eagerly or lazily according -to lazy (default False).

-

torch.nn.functional.pad is used unless the mode or kwargs are not available in torch, -in which case np.pad will be used.

-
-
Parameters:
-
    -
  • img (Tensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • -
  • to_pad (tuple[tuple[int, int]]) – the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), …]. -note that it including channel dimension.

  • -
  • transform_info (dict) – a dictionary with the relevant information pertaining to an applied transform.

  • -
  • mode (str) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • lazy (bool) – a flag indicating whether the operation should be performed in a lazy fashion or not.

  • -
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-monai.transforms.croppad.functional.pad_nd(img, to_pad, mode=PytorchPadMode.CONSTANT, **kwargs)[source]#
-

Pad img for a given an amount of padding in each dimension.

-

torch.nn.functional.pad is used unless the mode or kwargs are not available in torch, -in which case np.pad will be used.

-
-
Parameters:
-
    -
  • img (~NdarrayTensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • -
  • to_pad (list[tuple[int, int]]) – the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), …]. -default to self.to_pad.

  • -
  • mode (str) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-

Spatial (functional)#

-

A collection of “functional” transforms for spatial operations -Project-MONAI/MONAI

-
-
-monai.transforms.spatial.functional.affine_func(img, affine, grid, resampler, sp_size, mode, padding_mode, do_resampling, image_only, lazy, transform_info)[source]#
-

Functional implementation of affine. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
    -
  • img – data to be changed, assuming img is channel-first.

  • -
  • affine – the affine transformation to be applied, it can be a 3x3 or 4x4 matrix. This should be defined -for the voxel space spatial centers (float(size - 1)/2).

  • -
  • grid – used in non-lazy mode to pre-compute the grid to do the resampling.

  • -
  • resampler – the resampler function, see also: monai.transforms.Resample.

  • -
  • sp_size – output image spatial size.

  • -
  • mode – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode – {"zeros", "border", "reflection"} -Padding mode for outside grid values. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • do_resampling – whether to do the resampling, this is a flag for the use case of updating metadata but -skipping the actual (potentially heavy) resampling operation.

  • -
  • image_only – if True return only the image volume, otherwise return (image, affine).

  • -
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • -
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • -
-
-
-
- -
-
-monai.transforms.spatial.functional.flip(img, sp_axes, lazy, transform_info)[source]#
-

Functional implementation of flip. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
    -
  • img – data to be changed, assuming img is channel-first.

  • -
  • sp_axes – spatial axes along which to flip over. -If None, will flip over all of the axes of the input array. -If axis is negative it counts from the last to the first axis. -If axis is a tuple of ints, flipping is performed on all of the axes -specified in the tuple.

  • -
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • -
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • -
-
-
-
- -
-
-monai.transforms.spatial.functional.orientation(img, original_affine, spatial_ornt, lazy, transform_info)[source]#
-

Functional implementation of changing the input image’s orientation into the specified based on spatial_ornt. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
    -
  • img – data to be changed, assuming img is channel-first.

  • -
  • original_affine – original affine of the input image.

  • -
  • spatial_ornt – orientations of the spatial axes, -see also https://nipy.org/nibabel/reference/nibabel.orientations.html

  • -
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • -
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-monai.transforms.spatial.functional.resize(img, out_size, mode, align_corners, dtype, input_ndim, anti_aliasing, anti_aliasing_sigma, lazy, transform_info)[source]#
-

Functional implementation of resize. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
    -
  • img – data to be changed, assuming img is channel-first.

  • -
  • out_size – expected shape of spatial dimensions after resize operation.

  • -
  • mode – {"nearest", "nearest-exact", "linear", -"bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • align_corners – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’.

  • -
  • dtype – data type for resampling computation. If None, use the data type of input data.

  • -
  • input_ndim – number of spatial dimensions.

  • -
  • anti_aliasing – whether to apply a Gaussian filter to smooth the image prior -to downsampling. It is crucial to filter when downsampling -the image to avoid aliasing artifacts. See also skimage.transform.resize

  • -
  • anti_aliasing_sigma – {float, tuple of floats}, optional -Standard deviation for Gaussian filtering used when anti-aliasing.

  • -
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • -
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • -
-
-
-
- -
-
-monai.transforms.spatial.functional.rotate(img, angle, output_shape, mode, padding_mode, align_corners, dtype, lazy, transform_info)[source]#
-

Functional implementation of rotate. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
-
-
-
- -
-
-monai.transforms.spatial.functional.rotate90(img, axes, k, lazy, transform_info)[source]#
-

Functional implementation of rotate90. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
    -
  • img – data to be changed, assuming img is channel-first.

  • -
  • axes – 2 int numbers, defines the plane to rotate with 2 spatial axes. -If axis is negative it counts from the last to the first axis.

  • -
  • k – number of times to rotate by 90 degrees.

  • -
  • lazy – a flag that indicates whether the operation should be performed lazily or not

  • -
  • transform_info – a dictionary with the relevant information pertaining to an applied transform.

  • -
-
-
-
- -
-
-monai.transforms.spatial.functional.spatial_resample(img, dst_affine, spatial_size, mode, padding_mode, align_corners, dtype_pt, lazy, transform_info)[source]#
-

Functional implementation of resampling the input image to the specified dst_affine matrix and spatial_size. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
-
-
Return type:
-

Tensor

-
-
-
- -
-
-monai.transforms.spatial.functional.zoom(img, scale_factor, keep_size, mode, padding_mode, align_corners, dtype, lazy, transform_info)[source]#
-

Functional implementation of zoom. -This function operates eagerly or lazily according to -lazy (default False).

-
-
Parameters:
-
-
-
-
- -
-
-
-

Vanilla Transforms#

-
-

Crop and Pad#

-
-

PadListDataCollate#

-
-
-class monai.transforms.PadListDataCollate(method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, **kwargs)[source]#
-

Same as MONAI’s list_data_collate, except any tensors are centrally padded to match the shape of the biggest -tensor in each dimension. This transform is useful if some of the applied transforms generate batch data of -different sizes.

-

This can be used on both list and dictionary data. -Note that in the case of the dictionary data, it may add the transform information to the list of invertible transforms -if input batch have different spatial shape, so need to call static method: inverse before inverting other transforms.

-

Note that normally, a user won’t explicitly use the __call__ method. Rather this would be passed to the DataLoader. -This means that __call__ handles data as it comes out of a DataLoader, containing batch dimension. However, the -inverse operates on dictionaries containing images of shape C,H,W,[D]. This asymmetry is necessary so that we can -pass the inverse through multiprocessing.

-
-
Parameters:
-
-
-
-
-
-__call__(batch)[source]#
-
-
Parameters:
-

batch (Any) – batch of data to pad-collate

-
-
-
- -
-
-static inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, ndarray]

-
-
-
- -
- -
-
-

Pad#

-
-
-class monai.transforms.Pad(to_pad=None, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
-

Perform padding for a given an amount of padding in each dimension.

-

torch.nn.functional.pad is used unless the mode or kwargs are not available in torch, -in which case np.pad will be used.

-
-
Parameters:
-
    -
  • to_pad (Optional[tuple[tuple[int, int]], None]) – the amount to pad in each dimension (including the channel) [(low_H, high_H), (low_W, high_W), …]. -if None, must provide in the __call__ at runtime.

  • -
  • mode (str) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -requires pytorch >= 1.10 for best compatibility.

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-__call__(img, to_pad=None, mode=None, lazy=None, **kwargs)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – data to be transformed, assuming img is channel-first and padding doesn’t apply to the channel dim.

  • -
  • to_pad (Optional[tuple[tuple[int, int]], None]) – the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), …]. -default to self.to_pad.

  • -
  • mode (Optional[str, None]) – available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-compute_pad_width(spatial_shape)[source]#
-

dynamically compute the pad width according to the spatial shape. -the output is the amount of padding for all dimensions including the channel.

-
-
Parameters:
-

spatial_shape (Sequence[int]) – spatial shape of the original image.

-
-
Return type:
-

tuple[tuple[int, int]]

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

MetaTensor

-
-
-
- -
- -
-
-

SpatialPad#

-example of SpatialPad -
-
-class monai.transforms.SpatialPad(spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
-

Performs padding to the data, symmetric for all sides or all on one side for each dimension.

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int, tuple[UnionType[tuple[int, …], int], …]]) – the spatial size of output data after padding, if a dimension of the input -data size is larger than the pad size, will not pad that dimension. -If its components have non-positive values, the corresponding size of input image will be used -(no padding). for example: if the spatial size of input data is [30, 30, 30] and -spatial_size=[32, 25, -1], the spatial size of output data will be [32, 30, 30].

  • -
  • method (str) – {"symmetric", "end"} -Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • -
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-compute_pad_width(spatial_shape)[source]#
-

dynamically compute the pad width according to the spatial shape.

-
-
Parameters:
-

spatial_shape (Sequence[int]) – spatial shape of the original image.

-
-
Return type:
-

tuple[tuple[int, int]]

-
-
-
- -
- -
-
-

BorderPad#

-example of BorderPad -
-
-class monai.transforms.BorderPad(spatial_border, mode=PytorchPadMode.CONSTANT, lazy=False, **kwargs)[source]#
-

Pad the input data by adding specified borders to every dimension.

-
-
Parameters:
-
    -
  • spatial_border (UnionType[Sequence[int], int]) –

    specified size for every spatial border. Any -ve values will be set to 0. It can be 3 shapes:

    -
      -
    • single int number, pad all the borders with the same size.

    • -
    • length equals the length of image shape, pad every spatial dimension separately. -for example, image shape(CHW) is [1, 4, 4], spatial_border is [2, 1], -pad every border of H dim with 2, pad every border of W dim with 1, result shape is [1, 8, 6].

    • -
    • length equals 2 x (length of image shape), pad every border of every dimension separately. -for example, image shape(CHW) is [1, 4, 4], spatial_border is [1, 2, 3, 4], pad top of H dim with 1, -pad bottom of H dim with 2, pad left of W dim with 3, pad right of W dim with 4. -the result shape is [1, 7, 11].

    • -
    -

  • -
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-compute_pad_width(spatial_shape)[source]#
-

dynamically compute the pad width according to the spatial shape. -the output is the amount of padding for all dimensions including the channel.

-
-
Parameters:
-

spatial_shape (Sequence[int]) – spatial shape of the original image.

-
-
Return type:
-

tuple[tuple[int, int]]

-
-
-
- -
- -
-
-

DivisiblePad#

-example of DivisiblePad -
-
-class monai.transforms.DivisiblePad(k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, lazy=False, **kwargs)[source]#
-

Pad the input data, so that the spatial sizes are divisible by k.

-
-
-__init__(k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, lazy=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • k (UnionType[Sequence[int], int]) – the target k for each spatial dimension. -if k is negative or 0, the original size is preserved. -if k is an int, the same k be applied to all the input spatial dimensions.

  • -
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • method (str) – {"symmetric", "end"} -Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-

See also monai.transforms.SpatialPad

-
- -
-
-compute_pad_width(spatial_shape)[source]#
-

dynamically compute the pad width according to the spatial shape. -the output is the amount of padding for all dimensions including the channel.

-
-
Parameters:
-

spatial_shape (Sequence[int]) – spatial shape of the original image.

-
-
Return type:
-

tuple[tuple[int, int]]

-
-
-
- -
- -
-
-

Crop#

-
-
-class monai.transforms.Crop(lazy=False)[source]#
-

Perform crop operations on the input image.

-
-
Parameters:
-

lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

-
-
-
-
-__call__(img, slices, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t apply to the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-static compute_slices(roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None)[source]#
-

Compute the crop slices based on specified center & size or start & end or slices.

-
-
Parameters:
-
    -
  • roi_center (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for center of the crop ROI.

  • -
  • roi_size (Union[Sequence[int], ndarray, Tensor, None]) – size of the crop ROI, if a dimension of ROI size is larger than image size, -will not crop that dimension of the image.

  • -
  • roi_start (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for start of the crop ROI.

  • -
  • roi_end (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for end of the crop ROI, if a coordinate is out of image, -use the end coordinate of image.

  • -
  • roi_slices (Optional[Sequence[slice], None]) – list of slices for each of the spatial dimensions.

  • -
-
-
Return type:
-

tuple[slice]

-
-
-
- -
-
-inverse(img)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

MetaTensor

-
-
-
- -
- -
-
-

SpatialCrop#

-example of SpatialCrop -
-
-class monai.transforms.SpatialCrop(roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, lazy=False)[source]#
-

General purpose cropper to produce sub-volume region of interest (ROI). -If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. -So the cropped result may be smaller than the expected ROI, and the cropped results of several images may -not have exactly the same shape. -It can support to crop ND spatial (channel-first) data.

-
-
The cropped region can be parameterised in various ways:
    -
  • a list of slices for each spatial dimension (allows for use of negative indexing and None)

  • -
  • a spatial center and size

  • -
  • the start and end coordinates of the ROI

  • -
-
-
-
-
-__call__(img, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t apply to the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, lazy=False)[source]#
-
-
Parameters:
-
    -
  • roi_center (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for center of the crop ROI.

  • -
  • roi_size (Union[Sequence[int], ndarray, Tensor, None]) – size of the crop ROI, if a dimension of ROI size is larger than image size, -will not crop that dimension of the image.

  • -
  • roi_start (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for start of the crop ROI.

  • -
  • roi_end (Union[Sequence[int], ndarray, Tensor, None]) – voxel coordinates for end of the crop ROI, if a coordinate is out of image, -use the end coordinate of image.

  • -
  • roi_slices (Optional[Sequence[slice], None]) – list of slices for each of the spatial dimensions.

  • -
-
-
-
- -
- -
-
-

CenterSpatialCrop#

-example of CenterSpatialCrop -
-
-class monai.transforms.CenterSpatialCrop(roi_size, lazy=False)[source]#
-

Crop at the center of image with specified ROI size. -If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. -So the cropped result may be smaller than the expected ROI, and the cropped results of several images may -not have exactly the same shape.

-
-
Parameters:
-

roi_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224,224,128] -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

-
-
-
-
-__call__(img, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t apply to the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-compute_slices(spatial_size)[source]#
-

Compute the crop slices based on specified center & size or start & end or slices.

-
-
Parameters:
-
    -
  • roi_center – voxel coordinates for center of the crop ROI.

  • -
  • roi_size – size of the crop ROI, if a dimension of ROI size is larger than image size, -will not crop that dimension of the image.

  • -
  • roi_start – voxel coordinates for start of the crop ROI.

  • -
  • roi_end – voxel coordinates for end of the crop ROI, if a coordinate is out of image, -use the end coordinate of image.

  • -
  • roi_slices – list of slices for each of the spatial dimensions.

  • -
-
-
Return type:
-

tuple[slice]

-
-
-
- -
- -
-
-

RandSpatialCrop#

-example of RandSpatialCrop -
-
-class monai.transforms.RandSpatialCrop(roi_size, max_roi_size=None, random_center=True, random_size=True, lazy=False)[source]#
-

Crop image with random size or specific size ROI. It can crop at a random position as center -or at the image center. And allows to set the minimum and maximum size to limit the randomly generated ROI.

-

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results -of several images may not have exactly the same shape.

-
-
Parameters:
-
    -
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. -if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size -can specify the max crop region size. if None, defaults to the input image size. -if its components have non-positive values, the corresponding size of input image will be used.

  • -
  • random_center (bool) – crop at random position as center or the image center.

  • -
  • random_size (bool) – crop with random size or specific size ROI. -if True, the actual size is sampled from randint(roi_size, max_roi_size + 1).

  • -
-
-
-
-
-__call__(img, randomize=True, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t apply to the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-randomize(img_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandSpatialCropSamples#

-example of RandSpatialCropSamples -
-
-class monai.transforms.RandSpatialCropSamples(roi_size, num_samples, max_roi_size=None, random_center=True, random_size=True, lazy=False)[source]#
-

Crop image with random size or specific size ROI to generate a list of N samples. -It can crop at a random position as center or at the image center. And allows to set -the minimum size to limit the randomly generated ROI. -It will return a list of cropped images.

-

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped -results of several images may not have exactly the same shape.

-
-
Parameters:
-
    -
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. -if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • num_samples (int) – number of samples (crop regions) to take in the returned list.

  • -
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size -can specify the max crop region size. if None, defaults to the input image size. -if its components have non-positive values, the corresponding size of input image will be used.

  • -
  • random_center (bool) – crop at random position as center or the image center.

  • -
  • random_size (bool) – crop with random size or specific size ROI. -The actual size is sampled from randint(roi_size, img_size).

  • -
-
-
Raises:
-

ValueError – When num_samples is nonpositive.

-
-
-
-
-__call__(img, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -cropping doesn’t change the channel dim.

-
-
Return type:
-

list[Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandSpatialCropSamples

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

CropForeground#

-example of CropForeground -
-
-class monai.transforms.CropForeground(select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, return_coords=False, k_divisible=1, mode=PytorchPadMode.CONSTANT, lazy=False, **pad_kwargs)[source]#
-

Crop an image using a bounding box. The bounding box is generated by selecting foreground using select_fn -at channels channel_indices. margin is added in each spatial dimension of the bounding box. -The typical usage is to help training and evaluation if the valid part is small in the whole medical image. -Users can define arbitrary function to select expected foreground from the whole image or specified channels. -And it can also add margin to every dim of the bounding box of foreground object. -For example:

-
image = np.array(
-    [[[0, 0, 0, 0, 0],
-      [0, 1, 2, 1, 0],
-      [0, 1, 3, 2, 0],
-      [0, 1, 2, 1, 0],
-      [0, 0, 0, 0, 0]]])  # 1x5x5, single channel 5x5 image
-
-
-def threshold_at_one(x):
-    # threshold at 1
-    return x > 1
-
-
-cropper = CropForeground(select_fn=threshold_at_one, margin=0)
-print(cropper(image))
-[[[2, 1],
-  [3, 2],
-  [2, 1]]]
-
-
-
-
-__call__(img, mode=None, lazy=None, **pad_kwargs)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t change the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, return_coords=False, k_divisible=1, mode=PytorchPadMode.CONSTANT, lazy=False, **pad_kwargs)[source]#
-
-
Parameters:
-
    -
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • -
  • channel_indices (Union[Iterable[int], int, None]) – if defined, select foreground only on the specified channels -of image. if None, select foreground on the whole image.

  • -
  • margin (UnionType[Sequence[int], int]) – add margin value to spatial dims of the bounding box, if only 1 value provided, use it for all dims.

  • -
  • allow_smaller (bool) – when computing box size with margin, whether allow the image size to be smaller -than box size, default to True. if the margined size is larger than image size, will pad with -specified mode.

  • -
  • return_coords (bool) – whether return the coordinates of spatial bounding box for foreground.

  • -
  • k_divisible (UnionType[Sequence[int], int]) – make each spatial dimension to be divisible by k, default to 1. -if k_divisible is an int, the same k be applied to all the input spatial dimensions.

  • -
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
- -
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-compute_bounding_box(img)[source]#
-

Compute the start points and end points of bounding box to crop. -And adjust bounding box coords to be divisible by k.

-
-
Return type:
-

tuple[ndarray, ndarray]

-
-
-
- -
-
-crop_pad(img, box_start, box_end, mode=None, lazy=False, **pad_kwargs)[source]#
-

Crop and pad based on the bounding box.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(img)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

MetaTensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandWeightedCrop#

-example of RandWeightedCrop -
-
-class monai.transforms.RandWeightedCrop(spatial_size, num_samples=1, weight_map=None, lazy=False)[source]#
-

Samples a list of num_samples image patches according to the provided weight_map.

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the image patch e.g. [224, 224, 128]. -If its components have non-positive values, the corresponding size of img will be used.

  • -
  • num_samples (int) – number of samples (image patches) to take in the returned list.

  • -
  • weight_map (Union[ndarray, Tensor, None]) – weight map used to generate patch samples. The weights must be non-negative. -Each element denotes a sampling weight of the spatial location. 0 indicates no sampling. -It should be a single-channel array in shape, for example, (1, spatial_dim_0, spatial_dim_1, …).

  • -
-
-
-
-
-__call__(img, weight_map=None, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – input image to sample patches from. assuming img is a channel-first array.

  • -
  • weight_map (Union[ndarray, Tensor, None]) – weight map used to generate patch samples. The weights must be non-negative. -Each element denotes a sampling weight of the spatial location. 0 indicates no sampling. -It should be a single-channel array in shape, for example, (1, spatial_dim_0, spatial_dim_1, …)

  • -
  • randomize (bool) – whether to execute random operations, default to True.

  • -
-
-
Return type:
-

list[Tensor]

-
-
Returns:
-

A list of image patches

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(weight_map)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandCropByPosNegLabel#

-example of RandCropByPosNegLabel -
-
-class monai.transforms.RandCropByPosNegLabel(spatial_size, label=None, pos=1.0, neg=1.0, num_samples=1, image=None, image_threshold=0.0, fg_indices=None, bg_indices=None, allow_smaller=False, lazy=False)[source]#
-

Crop random fixed sized regions with the center being a foreground or background voxel -based on the Pos Neg Ratio. -And will return a list of arrays for all the cropped images. -For example, crop two (3 x 3) arrays from (5 x 5) array with pos/neg=1:

-
[[[0, 0, 0, 0, 0],
-  [0, 1, 2, 1, 0],            [[0, 1, 2],     [[2, 1, 0],
-  [0, 1, 3, 0, 0],     -->     [0, 1, 3],      [3, 0, 0],
-  [0, 0, 0, 0, 0],             [0, 0, 0]]      [0, 0, 0]]
-  [0, 0, 0, 0, 0]]]
-
-
-

If a dimension of the expected spatial size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than expected size, and the cropped -results of several images may not have exactly same shape. -And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the -valid crop ROI.

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -if its components have non-positive values, the corresponding size of label will be used. -for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • label (Optional[Tensor, None]) – the label image that is used for finding foreground/background, if None, must set at -self.__call__. Non-zero indicates foreground, zero indicates background.

  • -
  • pos (float) – used with neg together to calculate the ratio pos / (pos + neg) for the probability -to pick a foreground voxel as a center rather than a background voxel.

  • -
  • neg (float) – used with pos together to calculate the ratio pos / (pos + neg) for the probability -to pick a foreground voxel as a center rather than a background voxel.

  • -
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • -
  • image (Optional[Tensor, None]) – optional image data to help select valid area, can be same as img or another image array. -if not None, use label == 0 & image > image_threshold to select the negative -sample (background) center. So the crop center will only come from the valid image areas.

  • -
  • image_threshold (float) – if enabled image, use image > image_threshold to determine -the valid image content areas.

  • -
  • fg_indices (Union[ndarray, Tensor, None]) – if provided pre-computed foreground indices of label, will ignore above image and -image_threshold, and randomly select crop centers based on them, need to provide fg_indices -and bg_indices together, expect to be 1 dim array of spatial indices after flattening. -a typical usage is to call FgBgToIndices transform first and cache the results.

  • -
  • bg_indices (Union[ndarray, Tensor, None]) – if provided pre-computed background indices of label, will ignore above image and -image_threshold, and randomly select crop centers based on them, need to provide fg_indices -and bg_indices together, expect to be 1 dim array of spatial indices after flattening. -a typical usage is to call FgBgToIndices transform first and cache the results.

  • -
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than -the requested ROI in any dimension. If True, any smaller dimensions will be set to -match the cropped size (i.e., no cropping in that dimension).

  • -
-
-
Raises:
-
    -
  • ValueError – When pos or neg are negative.

  • -
  • ValueError – When pos=0 and neg=0. Incompatible values.

  • -
-
-
-
-
-__call__(img, label=None, image=None, fg_indices=None, bg_indices=None, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – input data to crop samples from based on the pos/neg ratio of label and image. -Assumes img is a channel-first array.

  • -
  • label (Optional[Tensor, None]) – the label image that is used for finding foreground/background, if None, use self.label.

  • -
  • image (Optional[Tensor, None]) – optional image data to help select valid area, can be same as img or another image array. -use label == 0 & image > image_threshold to select the negative sample(background) center. -so the crop center will only exist on valid image area. if None, use self.image.

  • -
  • fg_indices (Union[ndarray, Tensor, None]) – foreground indices to randomly select crop centers, -need to provide fg_indices and bg_indices together.

  • -
  • bg_indices (Union[ndarray, Tensor, None]) – background indices to randomly select crop centers, -need to provide fg_indices and bg_indices together.

  • -
  • randomize (bool) – whether to execute the random operations, default to True.

  • -
-
-
Return type:
-

list[Tensor]

-
-
-
- -
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(label=None, fg_indices=None, bg_indices=None, image=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandCropByLabelClasses#

-example of RandCropByLabelClasses -
-
-class monai.transforms.RandCropByLabelClasses(spatial_size, ratios=None, label=None, num_classes=None, num_samples=1, image=None, image_threshold=0.0, indices=None, allow_smaller=False, warn=True, max_samples_per_class=None, lazy=False)[source]#
-

Crop random fixed sized regions with the center being a class based on the specified ratios of every class. -The label data can be One-Hot format array or Argmax data. And will return a list of arrays for all the -cropped images. For example, crop two (3 x 3) arrays from (5 x 5) array with ratios=[1, 2, 3, 1]:

-
image = np.array([
-    [[0.0, 0.3, 0.4, 0.2, 0.0],
-    [0.0, 0.1, 0.2, 0.1, 0.4],
-    [0.0, 0.3, 0.5, 0.2, 0.0],
-    [0.1, 0.2, 0.1, 0.1, 0.0],
-    [0.0, 0.1, 0.2, 0.1, 0.0]]
-])
-label = np.array([
-    [[0, 0, 0, 0, 0],
-    [0, 1, 2, 1, 0],
-    [0, 1, 3, 0, 0],
-    [0, 0, 0, 0, 0],
-    [0, 0, 0, 0, 0]]
-])
-cropper = RandCropByLabelClasses(
-    spatial_size=[3, 3],
-    ratios=[1, 2, 3, 1],
-    num_classes=4,
-    num_samples=2,
-)
-label_samples = cropper(img=label, label=label, image=image)
-
-The 2 randomly cropped samples of `label` can be:
-[[0, 1, 2],     [[0, 0, 0],
- [0, 1, 3],      [1, 2, 1],
- [0, 0, 0]]      [1, 3, 0]]
-
-
-

If a dimension of the expected spatial size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than expected size, and the cropped -results of several images may not have exactly same shape. -And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the -valid crop ROI.

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -if its components have non-positive values, the corresponding size of label will be used. -for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • ratios (Optional[list[UnionType[float, int]], None]) – specified ratios of every class in the label to generate crop centers, including background class. -if None, every class will have the same ratio to generate crop centers.

  • -
  • label (Optional[Tensor, None]) – the label image that is used for finding every class, if None, must set at self.__call__.

  • -
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • -
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • -
  • image (Optional[Tensor, None]) – if image is not None, only return the indices of every class that are within the valid -region of the image (image > image_threshold).

  • -
  • image_threshold (float) – if enabled image, use image > image_threshold to -determine the valid image content area and select class indices only in this area.

  • -
  • indices (Optional[list[Union[ndarray, Tensor]], None]) – if provided pre-computed indices of every class, will ignore above image and -image_threshold, and randomly select crop centers based on them, expect to be 1 dim array -of spatial indices after flattening. a typical usage is to call ClassesToIndices transform first -and cache the results for better performance.

  • -
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than -the requested ROI in any dimension. If True, any smaller dimensions will remain -unchanged.

  • -
  • warn (bool) – if True prints a warning if a class is not present in the label.

  • -
  • max_samples_per_class (Optional[int, None]) – maximum length of indices to sample in each class to reduce memory consumption. -Default is None, no subsampling.

  • -
-
-
-
-
-__call__(img, label=None, image=None, indices=None, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – input data to crop samples from based on the ratios of every class, assumes img is a -channel-first array.

  • -
  • label (Optional[Tensor, None]) – the label image that is used for finding indices of every class, if None, use self.label.

  • -
  • image (Optional[Tensor, None]) – optional image data to help select valid area, can be same as img or another image array. -use image > image_threshold to select the centers only in valid region. if None, use self.image.

  • -
  • indices (Optional[list[Union[ndarray, Tensor]], None]) – list of indices for every class in the image, used to randomly select crop centers.

  • -
  • randomize (bool) – whether to execute the random operations, default to True.

  • -
-
-
Return type:
-

list[Tensor]

-
-
-
- -
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(label=None, indices=None, image=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

ResizeWithPadOrCrop#

-example of ResizeWithPadOrCrop -
-
-class monai.transforms.ResizeWithPadOrCrop(spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, lazy=False, **pad_kwargs)[source]#
-

Resize an image to a target spatial size by either centrally cropping the image or -padding it evenly with a user-specified mode. -When the dimension is smaller than the target size, do symmetric padding along that dim. -When the dimension is larger than the target size, do central cropping along that dim.

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of output data after padding or crop. -If has non-positive values, the corresponding size of input image will be used (no padding).

  • -
  • method (str) – {"symmetric", "end"} -Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • -
  • mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-__call__(img, mode=None, lazy=None, **pad_kwargs)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – data to pad or crop, assuming img is channel-first and -padding or cropping doesn’t apply to the channel dim.

  • -
  • mode (Optional[str, None]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(img)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

MetaTensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

BoundingRect#

-
-
-class monai.transforms.BoundingRect(select_fn=<function is_positive>)[source]#
-

Compute coordinates of axis-aligned bounding rectangles from input image img. -The output format of the coordinates is (shape is [channel, 2 * spatial dims]):

-
-
-
[[1st_spatial_dim_start, 1st_spatial_dim_end,

2nd_spatial_dim_start, 2nd_spatial_dim_end, -…, -Nth_spatial_dim_start, Nth_spatial_dim_end],

-

-

[1st_spatial_dim_start, 1st_spatial_dim_end, -2nd_spatial_dim_start, 2nd_spatial_dim_end, -…, -Nth_spatial_dim_start, Nth_spatial_dim_end]]

-
-
-
-

The bounding boxes edges are aligned with the input image edges. -This function returns [0, 0, …] if there’s no positive intensity.

-
-
Parameters:
-

select_fn (Callable) – function to select expected foreground, default is to select values > 0.

-
-
-
-
-__call__(img)[source]#
-

See also: monai.transforms.utils.generate_spatial_bounding_box.

-
-
Return type:
-

ndarray

-
-
-
- -
- -
-
-

RandScaleCrop#

-example of RandScaleCrop -
-
-class monai.transforms.RandScaleCrop(roi_scale, max_roi_scale=None, random_center=True, random_size=True, lazy=False)[source]#
-

Subclass of monai.transforms.RandSpatialCrop. Crop image with -random size or specific size ROI. It can crop at a random position as -center or at the image center. And allows to set the minimum and maximum -scale of image size to limit the randomly generated ROI.

-
-
Parameters:
-
    -
  • roi_scale (UnionType[Sequence[float], float]) – if random_size is True, it specifies the minimum crop size: roi_scale * image spatial size. -if random_size is False, it specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5]. -If its components have non-positive values, will use 1.0 instead, which means the input image size.

  • -
  • max_roi_scale (Union[Sequence[float], float, None]) – if random_size is True and roi_scale specifies the min crop region size, max_roi_scale -can specify the max crop region size: max_roi_scale * image spatial size. -if None, defaults to the input image size. if its components have non-positive values, -will use 1.0 instead, which means the input image size.

  • -
  • random_center (bool) – crop at random position as center or the image center.

  • -
  • random_size (bool) – crop with random size or specified size ROI by roi_scale * image spatial size. -if True, the actual size is sampled from -randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1).

  • -
-
-
-
-
-__call__(img, randomize=True, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t apply to the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-randomize(img_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

CenterScaleCrop#

-example of CenterScaleCrop -
-
-class monai.transforms.CenterScaleCrop(roi_scale, lazy=False)[source]#
-

Crop at the center of image with specified scale of ROI size.

-
-
Parameters:
-

roi_scale (UnionType[Sequence[float], float]) – specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5] or a number for all dims. -If its components have non-positive values, will use 1.0 instead, which means the input image size.

-
-
-
-
-__call__(img, lazy=None)[source]#
-

Apply the transform to img, assuming img is channel-first and -slicing doesn’t apply to the channel dim.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-
-

Intensity#

-
-

RandGaussianNoise#

-example of RandGaussianNoise -
-
-class monai.transforms.RandGaussianNoise(prob=0.1, mean=0.0, std=0.1, dtype=<class 'numpy.float32'>)[source]#
-

Add Gaussian noise to image.

-
-
Parameters:
-
    -
  • prob (float) – Probability to add Gaussian noise.

  • -
  • mean (float) – Mean or “centre” of the distribution.

  • -
  • std (float) – Standard deviation (spread) of distribution.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
-
-__call__(img, mean=None, randomize=True)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(img, mean=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

ShiftIntensity#

-example of ShiftIntensity -
-
-class monai.transforms.ShiftIntensity(offset, safe=False)[source]#
-

Shift intensity uniformly for the entire image with specified offset.

-
-
Parameters:
-
    -
  • offset (float) – offset value to shift the intensity of image.

  • -
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. -E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • -
-
-
-
-
-__call__(img, offset=None)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

RandShiftIntensity#

-example of RandShiftIntensity -
-
-class monai.transforms.RandShiftIntensity(offsets, safe=False, prob=0.1)[source]#
-

Randomly shift intensity with randomly picked offset.

-
-
-__call__(img, factor=None, randomize=True)[source]#
-

Apply the transform to img.

-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – input image to shift intensity.

  • -
  • factor (Optional[float, None]) – a factor to multiply the random offset, then shift. -can be some image specific value at runtime, like: max(img), etc.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(offsets, safe=False, prob=0.1)[source]#
-
-
Parameters:
-
    -
  • offsets (UnionType[tuple[float, float], float]) – offset range to randomly shift. -if single number, offset value is picked from (-offsets, offsets).

  • -
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. -E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • -
  • prob (float) – probability of shift.

  • -
-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

StdShiftIntensity#

-example of StdShiftIntensity -
-
-class monai.transforms.StdShiftIntensity(factor, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-

Shift intensity for the image with a factor and the standard deviation of the image -by: v = v + factor * std(v). -This transform can focus on only non-zero values or the entire image, -and can also calculate the std on each channel separately.

-
-
Parameters:
-
    -
  • factor (float) – factor shift by v = v + factor * std(v).

  • -
  • nonzero (bool) – whether only count non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately. Please ensure -that the first dimension represents the channel of the image if True.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

RandStdShiftIntensity#

-example of RandStdShiftIntensity -
-
-class monai.transforms.RandStdShiftIntensity(factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-

Shift intensity for the image with a factor and the standard deviation of the image -by: v = v + factor * std(v) where the factor is randomly picked.

-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-
-
Parameters:
-
    -
  • factors (UnionType[tuple[float, float], float]) – if tuple, the randomly picked range is (min(factors), max(factors)). -If single number, the range is (-factors, factors).

  • -
  • prob (float) – probability of std shift.

  • -
  • nonzero (bool) – whether only count non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandBiasField#

-example of RandBiasField -
-
-class monai.transforms.RandBiasField(degree=3, coeff_range=(0.0, 0.1), dtype=<class 'numpy.float32'>, prob=0.1)[source]#
-

Random bias field augmentation for MR images. -The bias field is considered as a linear combination of smoothly varying basis (polynomial) -functions, as described in Automated Model-Based Tissue Classification of MR Images of the Brain. -This implementation adapted from NiftyNet. -Referred to Longitudinal segmentation of age-related white matter hyperintensities.

-
-
Parameters:
-
    -
  • degree (int) – degree of freedom of the polynomials. The value should be no less than 1. -Defaults to 3.

  • -
  • coeff_range (tuple[float, float]) – range of the random coefficients. Defaults to (0.0, 0.1).

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • prob (float) – probability to do random bias field.

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(img_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

ScaleIntensity#

-example of ScaleIntensity -
-
-class monai.transforms.ScaleIntensity(minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-

Scale the intensity of input image to the given value range (minv, maxv). -If minv and maxv not provided, use factor to scale image by v = v * (1 + factor).

-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Raises:
-

ValueError – When self.minv=None or self.maxv=None and self.factor=None. Incompatible values.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-
-
Parameters:
-
    -
  • minv (UnionType[float, None]) – minimum value of output data.

  • -
  • maxv (UnionType[float, None]) – maximum value of output data.

  • -
  • factor (Optional[float, None]) – factor scale by v = v * (1 + factor). In order to use -this parameter, please set both minv and maxv into None.

  • -
  • channel_wise (bool) – if True, scale on each channel separately. Please ensure -that the first dimension represents the channel of the image if True.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
- -
- -
-
-

RandScaleIntensity#

-example of RandScaleIntensity -
-
-class monai.transforms.RandScaleIntensity(factors, prob=0.1, dtype=<class 'numpy.float32'>)[source]#
-

Randomly scale the intensity of input image by v = v * (1 + factor) where the factor -is randomly picked.

-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(factors, prob=0.1, dtype=<class 'numpy.float32'>)[source]#
-
-
Parameters:
-
    -
  • factors (UnionType[tuple[float, float], float]) – factor range to randomly scale by v = v * (1 + factor). -if single number, factor value is picked from (-factors, factors).

  • -
  • prob (float) – probability of scale.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

NormalizeIntensity#

-example of NormalizeIntensity -
-
-class monai.transforms.NormalizeIntensity(subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-

Normalize input based on the subtrahend and divisor: (img - subtrahend) / divisor. -Use calculated mean or std value of the input image if no subtrahend or divisor provided. -This transform can normalize only non-zero values or entire image, and can also calculate -mean and std on each channel separately. -When channel_wise is True, the first dimension of subtrahend and divisor should -be the number of image channels if they are not None.

-
-
Parameters:
-
    -
  • subtrahend (Union[Sequence, ndarray, Tensor, None]) – the amount to subtract by (usually the mean).

  • -
  • divisor (Union[Sequence, ndarray, Tensor, None]) – the amount to divide by (usually the standard deviation).

  • -
  • nonzero (bool) – whether only normalize non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately, otherwise, calculate on -the entire image directly. default to False.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img, assuming img is a channel-first array if self.channel_wise is True,

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

ThresholdIntensity#

-example of ThresholdIntensity -
-
-class monai.transforms.ThresholdIntensity(threshold, above=True, cval=0.0)[source]#
-

Filter the intensity values of whole image to below threshold or above threshold. -And fill the remaining parts of the image to the cval value.

-
-
Parameters:
-
    -
  • threshold (float) – the threshold to filter intensity values.

  • -
  • above (bool) – filter values above the threshold or below the threshold, default is True.

  • -
  • cval (float) – value to fill the remaining parts of the image, default is 0.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

ScaleIntensityRange#

-example of ScaleIntensityRange -
-
-class monai.transforms.ScaleIntensityRange(a_min, a_max, b_min=None, b_max=None, clip=False, dtype=<class 'numpy.float32'>)[source]#
-

Apply specific intensity scaling to the whole numpy array. -Scaling from [a_min, a_max] to [b_min, b_max] with clip option.

-

When b_min or b_max are None, scaled_array * (b_max - b_min) + b_min will be skipped. -If clip=True, when b_min/b_max is None, the clipping is not performed on the corresponding edge.

-
-
Parameters:
-
    -
  • a_min (float) – intensity original range min.

  • -
  • a_max (float) – intensity original range max.

  • -
  • b_min (Optional[float, None]) – intensity target range min.

  • -
  • b_max (Optional[float, None]) – intensity target range max.

  • -
  • clip (bool) – whether to perform clip after scaling.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

ScaleIntensityRangePercentiles#

-example of ScaleIntensityRangePercentiles -
-
-class monai.transforms.ScaleIntensityRangePercentiles(lower, upper, b_min, b_max, clip=False, relative=False, channel_wise=False, dtype=<class 'numpy.float32'>)[source]#
-

Apply range scaling to a numpy array based on the intensity distribution of the input.

-

By default this transform will scale from [lower_intensity_percentile, upper_intensity_percentile] to -[b_min, b_max], where {lower,upper}_intensity_percentile are the intensity values at the corresponding -percentiles of img.

-

The relative parameter can also be set to scale from [lower_intensity_percentile, upper_intensity_percentile] -to the lower and upper percentiles of the output range [b_min, b_max].

-

For example:

-
image = np.array(
-    [[[1, 2, 3, 4, 5],
-      [1, 2, 3, 4, 5],
-      [1, 2, 3, 4, 5],
-      [1, 2, 3, 4, 5],
-      [1, 2, 3, 4, 5],
-      [1, 2, 3, 4, 5]]])
-
-# Scale from lower and upper image intensity percentiles
-# to output range [b_min, b_max]
-scaler = ScaleIntensityRangePercentiles(10, 90, 0, 200, False, False)
-print(scaler(image))
-[[[0., 50., 100., 150., 200.],
-  [0., 50., 100., 150., 200.],
-  [0., 50., 100., 150., 200.],
-  [0., 50., 100., 150., 200.],
-  [0., 50., 100., 150., 200.],
-  [0., 50., 100., 150., 200.]]]
-
-# Scale from lower and upper image intensity percentiles
-# to lower and upper percentiles of the output range [b_min, b_max]
-rel_scaler = ScaleIntensityRangePercentiles(10, 90, 0, 200, False, True)
-print(rel_scaler(image))
-[[[20., 60., 100., 140., 180.],
-  [20., 60., 100., 140., 180.],
-  [20., 60., 100., 140., 180.],
-  [20., 60., 100., 140., 180.],
-  [20., 60., 100., 140., 180.],
-  [20., 60., 100., 140., 180.]]]
-
-
- -
-
Parameters:
-
    -
  • lower (float) – lower intensity percentile.

  • -
  • upper (float) – upper intensity percentile.

  • -
  • b_min (UnionType[float, None]) – intensity target range min.

  • -
  • b_max (UnionType[float, None]) – intensity target range max.

  • -
  • clip (bool) – whether to perform clip after scaling.

  • -
  • relative (bool) – whether to scale to the corresponding percentiles of [b_min, b_max].

  • -
  • channel_wise (bool) – if True, compute intensity percentile and normalize every channel separately. -default to False.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

AdjustContrast#

-example of AdjustContrast -
-
-class monai.transforms.AdjustContrast(gamma)[source]#
-

Changes image intensity by gamma. Each pixel/voxel intensity is updated as:

-
x = ((x - min) / intensity_range) ^ gamma * intensity_range + min
-
-
-
-
Parameters:
-

gamma (float) – gamma value to adjust the contrast as function.

-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

RandAdjustContrast#

-example of RandAdjustContrast -
-
-class monai.transforms.RandAdjustContrast(prob=0.1, gamma=(0.5, 4.5))[source]#
-

Randomly changes image intensity by gamma. Each pixel/voxel intensity is updated as:

-
x = ((x - min) / intensity_range) ^ gamma * intensity_range + min
-
-
-
-
Parameters:
-
    -
  • prob (float) – Probability of adjustment.

  • -
  • gamma (UnionType[Sequence[float], float]) – Range of gamma values. -If single number, value is picked from (0.5, gamma), default is (0.5, 4.5).

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

MaskIntensity#

-example of MaskIntensity -
-
-class monai.transforms.MaskIntensity(mask_data=None, select_fn=<function is_positive>)[source]#
-

Mask the intensity values of input image with the specified mask data. -Mask data must have the same spatial size as the input image, and all -the intensity values of input image corresponding to the selected values -in the mask data will keep the original value, others will be set to 0.

-
-
Parameters:
-
    -
  • mask_data (Union[ndarray, Tensor, None]) – if mask_data is single channel, apply to every channel -of input image. if multiple channels, the number of channels must -match the input data. the intensity values of input image corresponding -to the selected values in the mask data will keep the original value, -others will be set to 0. if None, must specify the mask_data at runtime.

  • -
  • select_fn (Callable) – function to select valid values of the mask_data, default is -to select values > 0.

  • -
-
-
-
-
-__call__(img, mask_data=None)[source]#
-
-
Parameters:
-

mask_data (Union[ndarray, Tensor, None]) – if mask data is single channel, apply to every channel -of input image. if multiple channels, the channel number must -match input data. mask_data will be converted to bool values -by mask_data > 0 before applying transform to input image.

-
-
Raises:
-
    -
  • - ValueError – When both mask_data and self.mask_data are None.

  • -
  • - ValueError – When mask_data and img channels differ and mask_data is not single channel.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

SavitzkyGolaySmooth#

-example of SavitzkyGolaySmooth -
-
-class monai.transforms.SavitzkyGolaySmooth(window_length, order, axis=1, mode='zeros')[source]#
-

Smooth the input data along the given axis using a Savitzky-Golay filter.

-
-
Parameters:
-
    -
  • window_length (int) – Length of the filter window, must be a positive odd integer.

  • -
  • order (int) – Order of the polynomial to fit to each window, must be less than window_length.

  • -
  • axis (int) – Optional axis along which to apply the filter kernel. Default 1 (first spatial dimension).

  • -
  • mode (str) – Optional padding mode, passed to convolution class. 'zeros', 'reflect', 'replicate' -or 'circular'. Default: 'zeros'. See torch.nn.Conv1d() for more information.

  • -
-
-
-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – array containing input data. Must be real and in shape [channels, spatial1, spatial2, …].

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

array containing smoothed result.

-
-
-
- -
- -
-
-

MedianSmooth#

-example of MedianSmooth -
-
-class monai.transforms.MedianSmooth(radius=1)[source]#
-

Apply median filter to the input data based on specified radius parameter. -A default value radius=1 is provided for reference.

-

See also: monai.networks.layers.median_filter()

-
-
Parameters:
-

radius (UnionType[Sequence[int], int]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

-
-
-
-
-__call__(img)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
- -
-
-

GaussianSmooth#

-example of GaussianSmooth -
-
-class monai.transforms.GaussianSmooth(sigma=1.0, approx='erf')[source]#
-

Apply Gaussian smooth to the input data based on specified sigma parameter. -A default value sigma=1.0 is provided for reference.

-
-
Parameters:
-
    -
  • sigma (UnionType[Sequence[float], float]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
-
-
-
-
-__call__(img)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
- -
-
-

RandGaussianSmooth#

-example of RandGaussianSmooth -
-
-class monai.transforms.RandGaussianSmooth(sigma_x=(0.25, 1.5), sigma_y=(0.25, 1.5), sigma_z=(0.25, 1.5), prob=0.1, approx='erf')[source]#
-

Apply Gaussian smooth to the input data based on randomly selected sigma parameters.

-
-
Parameters:
-
    -
  • sigma_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension.

  • -
  • sigma_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension if have.

  • -
  • sigma_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension if have.

  • -
  • prob (float) – probability of Gaussian smooth.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

GaussianSharpen#

-example of GaussianSharpen -
-
-class monai.transforms.GaussianSharpen(sigma1=3.0, sigma2=1.0, alpha=30.0, approx='erf')[source]#
-

Sharpen images using the Gaussian Blur filter. -Referring to: http://scipy-lectures.org/advanced/image_processing/auto_examples/plot_sharpen.html. -The algorithm is shown as below

-
blurred_f = gaussian_filter(img, sigma1)
-filter_blurred_f = gaussian_filter(blurred_f, sigma2)
-img = blurred_f + alpha * (blurred_f - filter_blurred_f)
-
-
-

A set of default values sigma1=3.0, sigma2=1.0 and alpha=30.0 is provide for reference.

-
-
Parameters:
-
    -
  • sigma1 (UnionType[Sequence[float], float]) – sigma parameter for the first gaussian kernel. if a list of values, must match the count -of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. -if only 1 value provided, use it for all spatial dimensions.

  • -
  • sigma2 (UnionType[Sequence[float], float]) – sigma parameter for the second gaussian kernel. if a list of values, must match the count -of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. -if only 1 value provided, use it for all spatial dimensions.

  • -
  • alpha (float) – weight parameter to compute the final result.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
-
-
-
-
-__call__(img)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
- -
-
-

RandGaussianSharpen#

-example of RandGaussianSharpen -
-
-class monai.transforms.RandGaussianSharpen(sigma1_x=(0.5, 1.0), sigma1_y=(0.5, 1.0), sigma1_z=(0.5, 1.0), sigma2_x=0.5, sigma2_y=0.5, sigma2_z=0.5, alpha=(10.0, 30.0), approx='erf', prob=0.1)[source]#
-

Sharpen images using the Gaussian Blur filter based on randomly selected sigma1, sigma2 and alpha. -The algorithm is monai.transforms.GaussianSharpen.

-
-
Parameters:
-
    -
  • sigma1_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension of first gaussian kernel.

  • -
  • sigma1_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension(if have) of first gaussian kernel.

  • -
  • sigma1_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension(if have) of first gaussian kernel.

  • -
  • sigma2_x (UnionType[tuple[float, float], float]) – randomly select sigma value for the first spatial dimension of second gaussian kernel. -if only 1 value X provided, it must be smaller than sigma1_x and randomly select from [X, sigma1_x].

  • -
  • sigma2_y (UnionType[tuple[float, float], float]) – randomly select sigma value for the second spatial dimension(if have) of second gaussian kernel. -if only 1 value Y provided, it must be smaller than sigma1_y and randomly select from [Y, sigma1_y].

  • -
  • sigma2_z (UnionType[tuple[float, float], float]) – randomly select sigma value for the third spatial dimension(if have) of second gaussian kernel. -if only 1 value Z provided, it must be smaller than sigma1_z and randomly select from [Z, sigma1_z].

  • -
  • alpha (tuple[float, float]) – randomly select weight parameter to compute the final result.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
  • prob (float) – probability of Gaussian sharpen.

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandHistogramShift#

-example of RandHistogramShift -
-
-class monai.transforms.RandHistogramShift(num_control_points=10, prob=0.1)[source]#
-

Apply random nonlinear transform to the image’s intensity histogram.

-
-
Parameters:
-
    -
  • num_control_points (UnionType[tuple[int, int], int]) – number of control points governing the nonlinear intensity mapping. -a smaller number of control points allows for larger intensity shifts. if two values provided, number of -control points selecting from range (min_value, max_value).

  • -
  • prob (float) – probability of histogram shift.

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

DetectEnvelope#

-
-
-class monai.transforms.DetectEnvelope(axis=1, n=None)[source]#
-

Find the envelope of the input data along the requested axis using a Hilbert transform.

-
-
Parameters:
-
    -
  • axis (int) – Axis along which to detect the envelope. Default 1, i.e. the first spatial dimension.

  • -
  • n (Optional[int, None]) – FFT size. Default img.shape[axis]. Input will be zero-padded or truncated to this size along dimension

  • -
  • axis.

  • -
-
-
-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – numpy.ndarray containing input data. Must be real and in shape [channels, spatial1, spatial2, …].

-
-
Returns:
-

np.ndarray containing envelope of data in img along the specified axis.

-
-
-
- -
- -
-
-

GibbsNoise#

-example of GibbsNoise -
-
-class monai.transforms.GibbsNoise(alpha=0.1)[source]#
-

The transform applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts -are one of the common type of type artifacts appearing in MRI scans.

-

The transform is applied to all the channels in the data.

-

For general information on Gibbs artifacts, please refer to:

-

An Image-based Approach to Understanding the Physics of MR Artifacts.

-

The AAPM/RSNA Physics Tutorial for Residents

-
-
Parameters:
-

alpha (float) – Parametrizes the intensity of the Gibbs noise filter applied. Takes -values in the interval [0,1] with alpha = 0 acting as the identity mapping.

-
-
-
-
-__call__(img)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

RandGibbsNoise#

-example of RandGibbsNoise -
-
-class monai.transforms.RandGibbsNoise(prob=0.1, alpha=(0.0, 1.0))[source]#
-

Naturalistic image augmentation via Gibbs artifacts. The transform -randomly applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts -are one of the common type of type artifacts appearing in MRI scans.

-

The transform is applied to all the channels in the data.

-

For general information on Gibbs artifacts, please refer to: -https://pubs.rsna.org/doi/full/10.1148/rg.313105115 -https://pubs.rsna.org/doi/full/10.1148/radiographics.22.4.g02jl14949

-
-
Parameters:
-
    -
  • prob (float) – probability of applying the transform.

  • -
  • alpha (Sequence(float)) – Parametrizes the intensity of the Gibbs noise filter applied. Takes -values in the interval [0,1] with alpha = 0 acting as the identity mapping. -If a length-2 list is given as [a,b] then the value of alpha will be -sampled uniformly from the interval [a,b]. 0 <= a <= b <= 1.

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
-
-randomize(data)[source]#
-
    -
  1. Set random variable to apply the transform.

  2. -
  3. Get alpha from uniform distribution.

  4. -
-
-
Return type:
-

None

-
-
-
- -
- -
-
-

KSpaceSpikeNoise#

-example of KSpaceSpikeNoise -
-
-class monai.transforms.KSpaceSpikeNoise(loc, k_intensity=None)[source]#
-

Apply localized spikes in k-space at the given locations and intensities. -Spike (Herringbone) artifact is a type of data acquisition artifact which -may occur during MRI scans.

-

For general information on spike artifacts, please refer to:

-

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

-

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s -perspective.

-
-
Parameters:
-
    -
  • loc (UnionType[tuple, Sequence[tuple]]) – spatial location for the spikes. For -images with 3D spatial dimensions, the user can provide (C, X, Y, Z) -to fix which channel C is affected, or (X, Y, Z) to place the same -spike in all channels. For 2D cases, the user can provide (C, X, Y) -or (X, Y).

  • -
  • k_intensity (Union[Sequence[float], float, None]) – value for the log-intensity of the -k-space version of the image. If one location is passed to loc or the -channel is not specified, then this argument should receive a float. If -loc is given a sequence of locations, then this argument should -receive a sequence of intensities. This value should be tested as it is -data-dependent. The default values are the 2.5 the mean of the -log-intensity for each channel.

  • -
-
-
-

Example

-

When working with 4D data, KSpaceSpikeNoise(loc = ((3,60,64,32), (64,60,32)), k_intensity = (13,14)) -will place a spike at [3, 60, 64, 32] with log-intensity = 13, and -one spike per channel located respectively at [: , 64, 60, 32] -with log-intensity = 14.

-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – image with dimensions (C, H, W) or (C, H, W, D)

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

RandKSpaceSpikeNoise#

-example of RandKSpaceSpikeNoise -
-
-class monai.transforms.RandKSpaceSpikeNoise(prob=0.1, intensity_range=None, channel_wise=True)[source]#
-

Naturalistic data augmentation via spike artifacts. The transform applies -localized spikes in k-space, and it is the random version of -monai.transforms.KSpaceSpikeNoise.

-

Spike (Herringbone) artifact is a type of data acquisition artifact which -may occur during MRI scans. For general information on spike artifacts, -please refer to:

-

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

-

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s -perspective.

-
-
Parameters:
-
    -
  • prob (float) – probability of applying the transform, either on all -channels at once, or channel-wise if channel_wise = True.

  • -
  • intensity_range (Optional[Sequence[UnionType[Sequence[float], float]], None]) – pass a tuple (a, b) to sample the log-intensity from the interval (a, b) -uniformly for all channels. Or pass sequence of intervals -((a0, b0), (a1, b1), …) to sample for each respective channel. -In the second case, the number of 2-tuples must match the number of channels. -Default ranges is (0.95x, 1.10x) where x is the mean -log-intensity for each channel.

  • -
  • channel_wise (bool) – treat each channel independently. True by -default.

  • -
-
-
-

Example

-

To apply k-space spikes randomly with probability 0.5, and -log-intensity sampled from the interval [11, 12] for each channel -independently, one uses -RandKSpaceSpikeNoise(prob=0.5, intensity_range=(11, 12), channel_wise=True)

-
-
-__call__(img, randomize=True)[source]#
-

Apply transform to img. Assumes data is in channel-first form.

-
-
Parameters:
-

img (Union[ndarray, Tensor]) – image with dimensions (C, H, W) or (C, H, W, D)

-
-
-
- -
-
-randomize(img, intensity_range)[source]#
-

Helper method to sample both the location and intensity of the spikes. -When not working channel wise (channel_wise=False) it use the random -variable self._do_transform to decide whether to sample a location -and intensity.

-

When working channel wise, the method randomly samples a location and -intensity for each channel depending on self._do_transform.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandRicianNoise#

-example of RandRicianNoise -
-
-class monai.transforms.RandRicianNoise(prob=0.1, mean=0.0, std=1.0, channel_wise=False, relative=False, sample_std=True, dtype=<class 'numpy.float32'>)[source]#
-

Add Rician noise to image. -Rician noise in MRI is the result of performing a magnitude operation on complex -data with Gaussian noise of the same variance in both channels, as described in -Noise in Magnitude Magnetic Resonance Images. -This transform is adapted from DIPY. -See also: The rician distribution of noisy mri data.

-
-
Parameters:
-
    -
  • prob (float) – Probability to add Rician noise.

  • -
  • mean (UnionType[Sequence[float], float]) – Mean or “centre” of the Gaussian distributions sampled to make up -the Rician noise.

  • -
  • std (UnionType[Sequence[float], float]) – Standard deviation (spread) of the Gaussian distributions sampled -to make up the Rician noise.

  • -
  • channel_wise (bool) – If True, treats each channel of the image separately.

  • -
  • relative (bool) – If True, the spread of the sampled Gaussian distributions will -be std times the standard deviation of the image or channel’s intensity -histogram.

  • -
  • sample_std (bool) – If True, sample the spread of the Gaussian distributions -uniformly from 0 to std.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

RandCoarseTransform#

-
-
-class monai.transforms.RandCoarseTransform(holes, spatial_size, max_holes=None, max_spatial_size=None, prob=0.1)[source]#
-

Randomly select coarse regions in the image, then execute transform operations for the regions. -It’s the base class of all kinds of region transforms. -Refer to papers: https://arxiv.org/abs/1708.04552

-
-
Parameters:
-
    -
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to -randomly select the expected number of regions.

  • -
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg -as the minimum spatial size to randomly select size for every region. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • -
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. -if some components of the max_spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of applying the transform.

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(img_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandCoarseDropout#

-example of RandCoarseDropout -
-
-class monai.transforms.RandCoarseDropout(holes, spatial_size, dropout_holes=True, fill_value=None, max_holes=None, max_spatial_size=None, prob=0.1)[source]#
-

Randomly coarse dropout regions in the image, then fill in the rectangular regions with specified value. -Or keep the rectangular regions and fill in the other areas with specified value. -Refer to papers: https://arxiv.org/abs/1708.04552, https://arxiv.org/pdf/1604.07379 -And other implementation: https://albumentations.ai/docs/api_reference/augmentations/transforms/ -#albumentations.augmentations.transforms.CoarseDropout.

-
-
Parameters:
-
    -
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to -randomly select the expected number of regions.

  • -
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg -as the minimum spatial size to randomly select size for every region. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • dropout_holes (bool) – if True, dropout the regions of holes and fill value, if False, keep the holes and -dropout the outside and fill value. default to True.

  • -
  • fill_value (Union[tuple[float, float], float, None]) – target value to fill the dropout regions, if providing a number, will use it as constant -value to fill all the regions. if providing a tuple for the min and max, will randomly select -value for every pixel / voxel from the range [min, max). if None, will compute the min and max -value of input image then randomly select value to fill, default to None.

  • -
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • -
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. -if some components of the max_spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of applying the transform.

  • -
-
-
-
- -
-
-

RandCoarseShuffle#

-example of RandCoarseShuffle -
-
-class monai.transforms.RandCoarseShuffle(holes, spatial_size, max_holes=None, max_spatial_size=None, prob=0.1)[source]#
-

Randomly select regions in the image, then shuffle the pixels within every region. -It shuffles every channel separately. -Refer to paper: -Kang, Guoliang, et al. “Patchshuffle regularization.” arXiv preprint arXiv:1707.07103 (2017). -https://arxiv.org/abs/1707.07103

-
-
Parameters:
-
    -
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to -randomly select the expected number of regions.

  • -
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg -as the minimum spatial size to randomly select size for every region. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • -
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. -if some components of the max_spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of applying the transform.

  • -
-
-
-
- -
-
-

HistogramNormalize#

-example of HistogramNormalize -
-
-class monai.transforms.HistogramNormalize(num_bins=256, min=0, max=255, mask=None, dtype=<class 'numpy.float32'>)[source]#
-

Apply the histogram normalization to input image. -Refer to: facebookresearch/CovidPrognosis.

-
-
Parameters:
-
    -
  • num_bins (int) – number of the bins to use in histogram, default to 256. for more details: -https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.

  • -
  • min (int) – the min value to normalize input image, default to 0.

  • -
  • max (int) – the max value to normalize input image, default to 255.

  • -
  • mask (Union[ndarray, Tensor, None]) – if provided, must be ndarray of bools or 0s and 1s, and same shape as image. -only points at which mask==True are used for the equalization. -can also provide the mask along with img at runtime.

  • -
  • dtype (Union[dtype, type, str, None]) – data type of the output, if None, same as input image. default to float32.

  • -
-
-
-
-
-__call__(img, mask=None)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

ForegroundMask#

-example of ForegroundMask -
-
-class monai.transforms.ForegroundMask(threshold='otsu', hsv_threshold=None, invert=False)[source]#
-

Creates a binary mask that defines the foreground based on thresholds in RGB or HSV color space. -This transform receives an RGB (or grayscale) image where by default it is assumed that the foreground has -low values (dark) while the background has high values (white). Otherwise, set invert argument to True.

-
-
Parameters:
-
    -
  • threshold (UnionType[dict, Callable, str, float, int]) – an int or a float number that defines the threshold that values less than that are foreground. -It also can be a callable that receives each dimension of the image and calculate the threshold, -or a string that defines such callable from skimage.filter.threshold_…. For the list of available -threshold functions, please refer to https://scikit-image.org/docs/stable/api/skimage.filters.html -Moreover, a dictionary can be passed that defines such thresholds for each channel, like -{“R”: 100, “G”: “otsu”, “B”: skimage.filter.threshold_mean}

  • -
  • hsv_threshold (Union[dict, Callable, str, float, int, None]) – similar to threshold but HSV color space (“H”, “S”, and “V”). -Unlike RBG, in HSV, value greater than hsv_threshold are considered foreground.

  • -
  • invert (bool) – invert the intensity range of the input image, so that the dtype maximum is now the dtype minimum, -and vice-versa.

  • -
-
-
-
-
-__call__(image)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

ComputeHoVerMaps#

-
-
-class monai.transforms.ComputeHoVerMaps(dtype='float32')[source]#
-

Compute horizontal and vertical maps from an instance mask -It generates normalized horizontal and vertical distances to the center of mass of each region. -Input data with the size of [1xHxW[xD]], which channel dim will temporarily removed for calculating coordinates.

-
-
Parameters:
-

dtype (Union[dtype, type, str, None]) – the data type of output Tensor. Defaults to “float32”.

-
-
Returns:
-

A torch.Tensor with the size of [2xHxW[xD]], which is stack horizontal and vertical maps

-
-
-
-
-__call__(mask)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-
-

IO#

-
-

LoadImage#

-
-
-class monai.transforms.LoadImage(reader=None, image_only=False, dtype=<class 'numpy.float32'>, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', expanduser=True, *args, **kwargs)[source]#
-

Load image file or files from provided path based on reader. -If reader is not specified, this class automatically chooses readers -based on the supported suffixes and in the following order:

-
-
    -
  • User-specified reader at runtime when calling this loader.

  • -
  • User-specified reader in the constructor of LoadImage.

  • -
  • Readers from the last to the first in the registered list.

  • -
  • Current default readers: (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), -(npz, npy -> NumpyReader), (nrrd -> NrrdReader), (DICOM file -> ITKReader).

  • -
-
-

Please note that for png, jpg, bmp, and other 2D formats, readers by default swap axis 0 and 1 after -loading the array with reverse_indexing set to True because the spatial axes definition -for non-medical specific file formats is different from other common medical packages.

-
-

See also

- -
-
-
-__call__(filename, reader=None)[source]#
-

Load image file and metadata from the given filename(s). -If reader is not specified, this class automatically chooses readers based on the -reversed order of registered readers self.readers.

-
-
Parameters:
-
    -
  • filename (Union[Sequence[Union[str, PathLike]], str, PathLike]) – path file or file-like object or a list of files. -will save the filename to meta_data with key filename_or_obj. -if provided a list of files, use the filename of first file to save, -and will stack them together as multi-channels data. -if provided directory path instead of file path, will treat it as -DICOM images series and read.

  • -
  • reader (Optional[ImageReader, None]) – runtime reader to load image file and metadata.

  • -
-
-
-
- -
-
-__init__(reader=None, image_only=False, dtype=<class 'numpy.float32'>, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', expanduser=True, *args, **kwargs)[source]#
-
-
Parameters:
-
    -
  • reader – reader to load image file and metadata -- if reader is None, a default set of SUPPORTED_READERS will be used. -- if reader is a string, it’s treated as a class name or dotted path -(such as "monai.data.ITKReader"), the supported built-in reader classes are -"ITKReader", "NibabelReader", "NumpyReader", "PydicomReader". -a reader instance will be constructed with the *args and **kwargs parameters. -- if reader is a reader class/instance, it will be registered to this loader accordingly.

  • -
  • image_only (bool) – if True return only the image MetaTensor, otherwise return image and header dict.

  • -
  • dtype (Union[dtype, type, str, None]) – if not None convert the loaded image to this data type.

  • -
  • ensure_channel_first (bool) – if True and loaded both image array and metadata, automatically convert -the image array shape to channel first. default to False.

  • -
  • simple_keys (bool) – whether to remove redundant metadata keys, default to False for backward compatibility.

  • -
  • prune_meta_pattern (Optional[str, None]) – combined with prune_meta_sep, a regular expression used to match and prune keys -in the metadata (nested dictionary), default to None, no key deletion.

  • -
  • prune_meta_sep (str) – combined with prune_meta_pattern, used to match and prune keys -in the metadata (nested dictionary). default is “.”, see also monai.transforms.DeleteItemsd. -e.g. prune_meta_pattern=".*_code$", prune_meta_sep=" " removes meta keys that ends with "_code".

  • -
  • expanduser (bool) – if True cast filename to Path and call .expanduser on it, otherwise keep filename as is.

  • -
  • args – additional parameters for reader if providing a reader name.

  • -
  • kwargs – additional parameters for reader if providing a reader name.

  • -
-
-
-
-

Note

-
    -
  • The transform returns a MetaTensor, unless set_track_meta(False) has been used, in which case, a -torch.Tensor will be returned.

  • -
  • If reader is specified, the loader will attempt to use the specified readers and the default supported -readers. This might introduce overheads when handling the exceptions of trying the incompatible loaders. -In this case, it is therefore recommended setting the most appropriate reader as -the last item of the reader parameter.

  • -
-
-
- -
-
-register(reader)[source]#
-

Register image reader to load image file and metadata.

-
-
Parameters:
-

reader (ImageReader) – reader instance to be registered with this loader.

-
-
-
- -
- -
-
-

SaveImage#

-
-
-class monai.transforms.SaveImage(output_dir='./', output_postfix='trans', output_ext='.nii.gz', output_dtype=<class 'numpy.float32'>, resample=True, mode='nearest', padding_mode=GridSamplePadMode.BORDER, scale=None, dtype=<class 'numpy.float64'>, squeeze_end_dims=True, data_root_dir='', separate_folder=True, print_log=True, output_format='', writer=None, channel_dim=0, output_name_formatter=None, folder_layout=None, savepath_in_metadict=False)[source]#
-

Save the image (in the form of torch tensor or numpy ndarray) and metadata dictionary into files.

-

The name of saved file will be {input_image_name}_{output_postfix}{output_ext}, -where the input_image_name is extracted from the provided metadata dictionary. -If no metadata provided, a running index starting from 0 will be used as the filename prefix.

-
-
Parameters:
-
    -
  • output_dir (Union[str, PathLike]) – output image directory.

  • -
  • instead (Handled by folder_layout) –

  • -
  • None. (if folder_layout is not) –

  • -
  • output_postfix (str) – a string appended to all output file names, default to trans.

  • -
  • instead

  • -
  • None.

  • -
  • output_ext (str) – output file extension name.

  • -
  • instead

  • -
  • None.

  • -
  • output_dtype (Union[dtype, type, str, None]) – data type (if not None) for saving data. Defaults to np.float32.

  • -
  • resample (bool) – whether to resample image (if needed) before saving the data array, -based on the "spatial_shape" (and "original_affine") from metadata.

  • -
  • mode (str) –

    This option is used when resample=True. Defaults to "nearest". -Depending on the writers, the possible options are

    - -

  • -
  • padding_mode (str) – This option is used when resample = True. Defaults to "border". -Possible options are {"zeros", "border", "reflection"} -See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample

  • -
  • scale (Optional[int, None]) – {255, 65535} postprocess data by clipping to [0, 1] and scaling -[0, 255] (uint8) or [0, 65535] (uint16). Default is None (no scaling).

  • -
  • dtype (Union[dtype, type, str, None]) – data type during resampling computation. Defaults to np.float64 for best precision. -if None, use the data type of input data. To set the output data type, use output_dtype.

  • -
  • squeeze_end_dims (bool) – if True, any trailing singleton dimensions will be removed (after the channel -has been moved to the end). So if input is (C,H,W,D), this will be altered to (H,W,D,C), and -then if C==1, it will be saved as (H,W,D). If D is also 1, it will be saved as (H,W). If False, -image will always be saved as (H,W,D,C).

  • -
  • data_root_dir (Union[str, PathLike]) –

    if not empty, it specifies the beginning parts of the input file’s -absolute path. It’s used to compute input_file_rel_path, the relative path to the file from -data_root_dir to preserve folder structure when saving in case there are files in different -folders with the same file names. For example, with the following inputs:

    -
      -
    • input_file_name: /foo/bar/test1/image.nii

    • -
    • output_postfix: seg

    • -
    • output_ext: .nii.gz

    • -
    • output_dir: /output

    • -
    • data_root_dir: /foo/bar

    • -
    -

    The output will be: /output/test1/image/image_seg.nii.gz

    -

    Handled by folder_layout instead, if folder_layout is not None.

    -

  • -
  • separate_folder (bool) – whether to save every file in a separate folder. For example: for the input filename -image.nii, postfix seg and folder_path output, if separate_folder=True, it will be -saved as: output/image/image_seg.nii, if False, saving as output/image_seg.nii. -Default to True. -Handled by folder_layout instead, if folder_layout is not None.

  • -
  • print_log (bool) – whether to print logs when saving. Default to True.

  • -
  • output_format (str) – an optional string of filename extension to specify the output image writer. -see also: monai.data.image_writer.SUPPORTED_WRITERS.

  • -
  • writer (Union[type[ImageWriter], str, None]) – a customised monai.data.ImageWriter subclass to save data arrays. -if None, use the default writer from monai.data.image_writer according to output_ext. -if it’s a string, it’s treated as a class name or dotted path (such as "monai.data.ITKWriter"); -the supported built-in writer classes are "NibabelWriter", "ITKWriter", "PILWriter".

  • -
  • channel_dim (UnionType[int, None]) – the index of the channel dimension. Default to 0. -None to indicate no channel dimension.

  • -
  • output_name_formatter (Optional[Callable[[dict, Transform], dict], None]) – a callable function (returning a kwargs dict) to format the output file name. -If using a custom monai.data.FolderLayoutBase class in folder_layout, consider providing -your own formatter. -see also: monai.data.folder_layout.default_name_formatter().

  • -
  • folder_layout (Optional[FolderLayoutBase, None]) – A customized monai.data.FolderLayoutBase subclass to define file naming schemes. -if None, uses the default FolderLayout.

  • -
  • savepath_in_metadict (bool) – if True, adds a key "saved_to" to the metadata, which contains the path -to where the input image has been saved.

  • -
-
-
-
-
-__call__(img, meta_data=None)[source]#
-
-
Parameters:
-
    -
  • img (UnionType[Tensor, ndarray]) – target data content that save into file. The image should be channel-first, shape: [C,H,W,[D]].

  • -
  • meta_data (Optional[dict, None]) – key-value pairs of metadata corresponding to the data.

  • -
-
-
-
- -
-
-set_options(init_kwargs=None, data_kwargs=None, meta_kwargs=None, write_kwargs=None)[source]#
-

Set the options for the underlying writer by updating the self.*_kwargs dictionaries.

-

The arguments correspond to the following usage:

-
-
    -
  • writer = ImageWriter(**init_kwargs)

  • -
  • writer.set_data_array(array, **data_kwargs)

  • -
  • writer.set_metadata(meta_data, **meta_kwargs)

  • -
  • writer.write(filename, **write_kwargs)

  • -
-
-
- -
- -
-
-
-

NVIDIA Tool Extension (NVTX)#

-
-

RangePush#

-
-
-class monai.transforms.RangePush(msg)[source]#
-

Pushes a range onto a stack of nested range span. -Stores zero-based depth of the range that is started.

-
-
Parameters:
-

msg (str) – ASCII message to associate with range

-
-
-
- -
-
-

RandRangePush#

-
-
-class monai.transforms.RandRangePush(msg)[source]#
-

Pushes a range onto a stack of nested range span (for randomizable transforms). -Stores zero-based depth of the range that is started.

-
-
Parameters:
-

msg (str) – ASCII message to associate with range

-
-
-
- -
-
-

RangePop#

-
-
-class monai.transforms.RangePop[source]#
-

Pops a range off of a stack of nested range spans. -Stores zero-based depth of the range that is ended.

-
- -
-
-

RandRangePop#

-
-
-class monai.transforms.RandRangePop[source]#
-

Pops a range off of a stack of nested range spans (for randomizable transforms). -Stores zero-based depth of the range that is ended.

-
- -
-
-

Mark#

-
-
-class monai.transforms.Mark(msg)[source]#
-

Mark an instantaneous event that occurred at some point.

-
-
Parameters:
-

msg (str) – ASCII message to associate with the event.

-
-
-
- -
-
-

RandMark#

-
-
-class monai.transforms.RandMark(msg)[source]#
-

Mark an instantaneous event that occurred at some point (for randomizable transforms).

-
-
Parameters:
-

msg (str) – ASCII message to associate with the event.

-
-
-
- -
-
-
-

Post-processing#

-
-

Activations#

-
-
-class monai.transforms.Activations(sigmoid=False, softmax=False, other=None, **kwargs)[source]#
-

Activation operations, typically Sigmoid or Softmax.

-
-
Parameters:
-
    -
  • sigmoid (bool) – whether to execute sigmoid function on model output before transform. -Defaults to False.

  • -
  • softmax (bool) – whether to execute softmax function on model output before transform. -Defaults to False.

  • -
  • other (Optional[Callable, None]) – callable function to execute other activation layers, for example: -other = lambda x: torch.tanh(x). Defaults to None.

  • -
  • kwargs – additional parameters to torch.softmax (used when softmax=True). -Defaults to dim=0, unrecognized parameters will be ignored.

  • -
-
-
Raises:
-

TypeError – When other is not an Optional[Callable].

-
-
-
-
-__call__(img, sigmoid=None, softmax=None, other=None)[source]#
-
-
Parameters:
-
    -
  • sigmoid (Optional[bool, None]) – whether to execute sigmoid function on model output before transform. -Defaults to self.sigmoid.

  • -
  • softmax (Optional[bool, None]) – whether to execute softmax function on model output before transform. -Defaults to self.softmax.

  • -
  • other (Optional[Callable, None]) – callable function to execute other activation layers, for example: -other = torch.tanh. Defaults to self.other.

  • -
-
-
Raises:
-
    -
  • ValueError – When sigmoid=True and softmax=True. Incompatible values.

  • -
  • TypeError – When other is not an Optional[Callable].

  • -
  • ValueError – When self.other=None and other=None. Incompatible values.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

AsDiscrete#

-example of AsDiscrete -
-
-class monai.transforms.AsDiscrete(argmax=False, to_onehot=None, threshold=None, rounding=None, **kwargs)[source]#
-

Convert the input tensor/array into discrete values, possible operations are:

-
-
    -
  • argmax.

  • -
  • threshold input value to binary values.

  • -
  • convert input value to One-Hot format (set to_one_hot=N, N is the number of classes).

  • -
  • round the value to the closest integer.

  • -
-
-
-
Parameters:
-
    -
  • argmax (bool) – whether to execute argmax function on input data before transform. -Defaults to False.

  • -
  • to_onehot (Optional[int, None]) – if not None, convert input data into the one-hot format with specified number of classes. -Defaults to None.

  • -
  • threshold (Optional[float, None]) – if not None, threshold the float values to int number 0 or 1 with specified threshold. -Defaults to None.

  • -
  • rounding (Optional[str, None]) – if not None, round the data according to the specified option, -available options: [“torchrounding”].

  • -
  • kwargs – additional parameters to torch.argmax, monai.networks.one_hot. -currently dim, keepdim, dtype are supported, unrecognized parameters will be ignored. -These default to 0, True, torch.float respectively.

  • -
-
-
-

Example

-
>>> transform = AsDiscrete(argmax=True)
->>> print(transform(np.array([[[0.0, 1.0]], [[2.0, 3.0]]])))
-# [[[1.0, 1.0]]]
-
-
-
>>> transform = AsDiscrete(threshold=0.6)
->>> print(transform(np.array([[[0.0, 0.5], [0.8, 3.0]]])))
-# [[[0.0, 0.0], [1.0, 1.0]]]
-
-
-
>>> transform = AsDiscrete(argmax=True, to_onehot=2, threshold=0.5)
->>> print(transform(np.array([[[0.0, 1.0]], [[2.0, 3.0]]])))
-# [[[0.0, 0.0]], [[1.0, 1.0]]]
-
-
-
-
-__call__(img, argmax=None, to_onehot=None, threshold=None, rounding=None)[source]#
-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – the input tensor data to convert, if no channel dimension when converting to One-Hot, -will automatically add it.

  • -
  • argmax (Optional[bool, None]) – whether to execute argmax function on input data before transform. -Defaults to self.argmax.

  • -
  • to_onehot (Optional[int, None]) – if not None, convert input data into the one-hot format with specified number of classes. -Defaults to self.to_onehot.

  • -
  • threshold (Optional[float, None]) – if not None, threshold the float values to int number 0 or 1 with specified threshold value. -Defaults to self.threshold.

  • -
  • rounding (Optional[str, None]) – if not None, round the data according to the specified option, -available options: [“torchrounding”].

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

KeepLargestConnectedComponent#

-example of KeepLargestConnectedComponent -
-
-class monai.transforms.KeepLargestConnectedComponent(applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1)[source]#
-

Keeps only the largest connected component in the image. -This transform can be used as a post-processing step to clean up over-segment areas in model output.

-
-
The input is assumed to be a channel-first PyTorch Tensor:

1) For not OneHot format data, the values correspond to expected labels, -0 will be treated as background and the over-segment pixels will be set to 0. -2) For OneHot format data, the values should be 0, 1 on each labels, -the over-segment pixels will be set to 0 in its channel.

-
-
-

For example: -Use with applied_labels=[1], is_onehot=False, connectivity=1:

-
[1, 0, 0]         [0, 0, 0]
-[0, 1, 1]    =>   [0, 1 ,1]
-[0, 1, 1]         [0, 1, 1]
-
-
-

Use with applied_labels=[1, 2], is_onehot=False, independent=False, connectivity=1:

-
[0, 0, 1, 0 ,0]           [0, 0, 1, 0 ,0]
-[0, 2, 1, 1 ,1]           [0, 2, 1, 1 ,1]
-[1, 2, 1, 0 ,0]    =>     [1, 2, 1, 0 ,0]
-[1, 2, 0, 1 ,0]           [1, 2, 0, 0 ,0]
-[2, 2, 0, 0 ,2]           [2, 2, 0, 0 ,0]
-
-
-

Use with applied_labels=[1, 2], is_onehot=False, independent=True, connectivity=1:

-
[0, 0, 1, 0 ,0]           [0, 0, 1, 0 ,0]
-[0, 2, 1, 1 ,1]           [0, 2, 1, 1 ,1]
-[1, 2, 1, 0 ,0]    =>     [0, 2, 1, 0 ,0]
-[1, 2, 0, 1 ,0]           [0, 2, 0, 0 ,0]
-[2, 2, 0, 0 ,2]           [2, 2, 0, 0 ,0]
-
-
-

Use with applied_labels=[1, 2], is_onehot=False, independent=False, connectivity=2:

-
[0, 0, 1, 0 ,0]           [0, 0, 1, 0 ,0]
-[0, 2, 1, 1 ,1]           [0, 2, 1, 1 ,1]
-[1, 2, 1, 0 ,0]    =>     [1, 2, 1, 0 ,0]
-[1, 2, 0, 1 ,0]           [1, 2, 0, 1 ,0]
-[2, 2, 0, 0 ,2]           [2, 2, 0, 0 ,2]
-
-
-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – shape must be (C, spatial_dim1[, spatial_dim2, …]).

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

An array with shape (C, spatial_dim1[, spatial_dim2, …]).

-
-
-
- -
-
-__init__(applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1)[source]#
-
-
Parameters:
-
    -
  • applied_labels (Union[Sequence[int], int, None]) – Labels for applying the connected component analysis on. -If given, voxels whose value is in this list will be analyzed. -If None, all non-zero values will be analyzed.

  • -
  • is_onehot (Optional[bool, None]) – if True, treat the input data as OneHot format data, otherwise, not OneHot format data. -default to None, which treats multi-channel data as OneHot and single channel data as not OneHot.

  • -
  • independent (bool) – whether to treat applied_labels as a union of foreground labels. -If True, the connected component analysis will be performed on each foreground label independently -and return the intersection of the largest components. -If False, the analysis will be performed on the union of foreground labels. -default is True.

  • -
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. If None, a full -connectivity of input.ndim is used. for more details: -https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label.

  • -
  • num_components (int) – The number of largest components to preserve.

  • -
-
-
-
- -
- -
-
-

RemoveSmallObjects#

-example of RemoveSmallObjects -
-
-class monai.transforms.RemoveSmallObjects(min_size=64, connectivity=1, independent_channels=True)[source]#
-

Use skimage.morphology.remove_small_objects to remove small objects from images. -See: https://scikit-image.org/docs/dev/api/skimage.morphology.html#remove-small-objects.

-

Data should be one-hotted.

-
-
Parameters:
-
    -
  • min_size (int) – objects smaller than this size (in pixel) are removed.

  • -
  • connectivity (int) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. If None, a full -connectivity of input.ndim is used. For more details refer to linked scikit-image -documentation.

  • -
  • independent_channels (bool) – Whether or not to consider channels as independent. If true, then -conjoining islands from different labels will be removed if they are below the threshold. -If false, the overall size islands made from all non-background voxels will be used.

  • -
-
-
-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – shape must be (C, spatial_dim1[, spatial_dim2, …]). Data -should be one-hotted.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

An array with shape (C, spatial_dim1[, spatial_dim2, …]).

-
-
-
- -
- -
-
-

LabelFilter#

-example of LabelFilter -
-
-class monai.transforms.LabelFilter(applied_labels)[source]#
-

This transform filters out labels and can be used as a processing step to view only certain labels.

-

The list of applied labels defines which labels will be kept.

-
-

Note

-

All labels which do not match the applied_labels are set to the background label (0).

-
-

For example:

-

Use LabelFilter with applied_labels=[1, 5, 9]:

-
[1, 2, 3]         [1, 0, 0]
-[4, 5, 6]    =>   [0, 5 ,0]
-[7, 8, 9]         [0, 0, 9]
-
-
-
-
-__call__(img)[source]#
-

Filter the image on the applied_labels.

-
-
Parameters:
-

img (Union[ndarray, Tensor]) – Pytorch tensor or numpy array of any shape.

-
-
Raises:
-

NotImplementedError – The provided image was not a Pytorch Tensor or numpy array.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Pytorch tensor or numpy array of the same shape as the input.

-
-
-
- -
-
-__init__(applied_labels)[source]#
-

Initialize the LabelFilter class with the labels to filter on.

-
-
Parameters:
-

applied_labels (UnionType[Iterable[int], int]) – Label(s) to filter on.

-
-
-
- -
- -
-
-

FillHoles#

-
-
-class monai.transforms.FillHoles(applied_labels=None, connectivity=None)[source]#
-

This transform fills holes in the image and can be used to remove artifacts inside segments.

-

An enclosed hole is defined as a background pixel/voxel which is only enclosed by a single class. -The definition of enclosed can be defined with the connectivity parameter:

-
1-connectivity     2-connectivity     diagonal connection close-up
-
-     [ ]           [ ]  [ ]  [ ]             [ ]
-      |               \  |  /                 |  <- hop 2
-[ ]--[x]--[ ]      [ ]--[x]--[ ]        [x]--[ ]
-      |               /  |  \             hop 1
-     [ ]           [ ]  [ ]  [ ]
-
-
-

It is possible to define for which labels the hole filling should be applied. -The input image is assumed to be a PyTorch Tensor or numpy array with shape [C, spatial_dim1[, spatial_dim2, …]]. -If C = 1, then the values correspond to expected labels. -If C > 1, then a one-hot-encoding is expected where the index of C matches the label indexing.

-
-

Note

-

The label 0 will be treated as background and the enclosed holes will be set to the neighboring class label.

-

The performance of this method heavily depends on the number of labels. -It is a bit faster if the list of applied_labels is provided. -Limiting the number of applied_labels results in a big decrease in processing time.

-
-

For example:

-
-

Use FillHoles with default parameters:

-
[1, 1, 1, 2, 2, 2, 3, 3]         [1, 1, 1, 2, 2, 2, 3, 3]
-[1, 0, 1, 2, 0, 0, 3, 0]    =>   [1, 1 ,1, 2, 0, 0, 3, 0]
-[1, 1, 1, 2, 2, 2, 3, 3]         [1, 1, 1, 2, 2, 2, 3, 3]
-
-
-

The hole in label 1 is fully enclosed and therefore filled with label 1. -The background label near label 2 and 3 is not fully enclosed and therefore not filled.

-
-
-
-__call__(img)[source]#
-

Fill the holes in the provided image.

-
-

Note

-

The value 0 is assumed as background label.

-
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – Pytorch Tensor or numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

-
-
Raises:
-

NotImplementedError – The provided image was not a Pytorch Tensor or numpy array.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Pytorch Tensor or numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

-
-
-
- -
-
-__init__(applied_labels=None, connectivity=None)[source]#
-

Initialize the connectivity and limit the labels for which holes are filled.

-
-
Parameters:
-
    -
  • applied_labels (Union[Iterable[int], int, None]) – Labels for which to fill holes. Defaults to None, that is filling holes for all labels.

  • -
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. Defaults to a full connectivity of input.ndim.

  • -
-
-
-
- -
- -
-
-

LabelToContour#

-example of LabelToContour -
-
-class monai.transforms.LabelToContour(kernel_type='Laplace')[source]#
-

Return the contour of binary input images that only compose of 0 and 1, with Laplacian kernel -set as default for edge detection. Typical usage is to plot the edge of label or segmentation output.

-
-
Parameters:
-

kernel_type (str) – the method applied to do edge detection, default is “Laplace”.

-
-
Raises:
-

NotImplementedError – When kernel_type is not “Laplace”.

-
-
-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – torch tensor data to extract the contour, with shape: [channels, height, width[, depth]]

-
-
Raises:
-

ValueError – When image ndim is not one of [3, 4].

-
-
Returns:
-

    -
  1. it’s the binary classification result of whether a pixel is edge or not.

  2. -
  3. in order to keep the original shape of mask image, we use padding as default.

  4. -
  5. the edge detection is just approximate because it defects inherent to Laplace kernel, -ideally the edge should be thin enough, but now it has a thickness.

  6. -
-

-
-
Return type:
-

A torch tensor with the same shape as img, note

-
-
-
- -
- -
-
-

MeanEnsemble#

-
-
-class monai.transforms.MeanEnsemble(weights=None)[source]#
-

Execute mean ensemble on the input data. -The input data can be a list or tuple of PyTorch Tensor with shape: [C[, H, W, D]], -Or a single PyTorch Tensor with shape: [E, C[, H, W, D]], the E dimension represents -the output data from different models. -Typically, the input data is model output of segmentation task or classification task. -And it also can support to add weights for the input data.

-
-
Parameters:
-

weights (Union[Sequence[float], ndarray, Tensor, None]) – can be a list or tuple of numbers for input data with shape: [E, C, H, W[, D]]. -or a Numpy ndarray or a PyTorch Tensor data. -the weights will be added to input data from highest dimension, for example: -1. if the weights only has 1 dimension, it will be added to the E dimension of input data. -2. if the weights has 2 dimensions, it will be added to E and C dimensions. -it’s a typical practice to add weights for different classes: -to ensemble 3 segmentation model outputs, every output has 4 channels(classes), -so the input data shape can be: [3, 4, H, W, D]. -and add different weights for different classes, so the weights shape can be: [3, 4]. -for example: weights = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 1, 1, 1]].

-
-
-
-
-__call__(img)[source]#
-

Call self as a function.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

ProbNMS#

-
-
-class monai.transforms.ProbNMS(spatial_dims=2, sigma=0.0, prob_threshold=0.5, box_size=48)[source]#
-

Performs probability based non-maximum suppression (NMS) on the probabilities map via -iteratively selecting the coordinate with highest probability and then move it as well -as its surrounding values. The remove range is determined by the parameter box_size. -If multiple coordinates have the same highest probability, only one of them will be -selected.

-
-
Parameters:
-
    -
  • spatial_dims (int) – number of spatial dimensions of the input probabilities map. -Defaults to 2.

  • -
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – the standard deviation for gaussian filter. -It could be a single value, or spatial_dims number of values. Defaults to 0.0.

  • -
  • prob_threshold (float) – the probability threshold, the function will stop searching if -the highest probability is no larger than the threshold. The value should be -no less than 0.0. Defaults to 0.5.

  • -
  • box_size (UnionType[int, Sequence[int]]) – the box size (in pixel) to be removed around the pixel with the maximum probability. -It can be an integer that defines the size of a square or cube, -or a list containing different values for each dimensions. Defaults to 48.

  • -
-
-
Returns:
-

a list of selected lists, where inner lists contain probability and coordinates. -For example, for 3D input, the inner lists are in the form of [probability, x, y, z].

-
-
Raises:
-
    -
  • ValueError – When prob_threshold is less than 0.0.

  • -
  • ValueError – When box_size is a list or tuple, and its length is not equal to spatial_dims.

  • -
  • ValueError – When box_size has a less than 1 value.

  • -
-
-
-
- -
-
-

SobelGradients#

-
-
-class monai.transforms.SobelGradients(kernel_size=3, spatial_axes=None, normalize_kernels=True, normalize_gradients=False, padding_mode='reflect', dtype=torch.float32)[source]#
-

Calculate Sobel gradients of a grayscale image with the shape of CxH[xWxDx…] or BxH[xWxDx…].

-
-
Parameters:
-
    -
  • kernel_size (int) – the size of the Sobel kernel. Defaults to 3.

  • -
  • spatial_axes (Union[Sequence[int], int, None]) – the axes that define the direction of the gradient to be calculated. It calculate the gradient -along each of the provide axis. By default it calculate the gradient for all spatial axes.

  • -
  • normalize_kernels (bool) – if normalize the Sobel kernel to provide proper gradients. Defaults to True.

  • -
  • normalize_gradients (bool) – if normalize the output gradient to 0 and 1. Defaults to False.

  • -
  • padding_mode (str) – the padding mode of the image when convolving with Sobel kernels. Defaults to “reflect”. -Acceptable values are 'zeros', 'reflect', 'replicate' or 'circular'. -See torch.nn.Conv1d() for more information.

  • -
  • dtype (dtype) – kernel data type (torch.dtype). Defaults to torch.float32.

  • -
-
-
-
-
-__call__(image)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

VoteEnsemble#

-
-
-class monai.transforms.VoteEnsemble(num_classes=None)[source]#
-

Execute vote ensemble on the input data. -The input data can be a list or tuple of PyTorch Tensor with shape: [C[, H, W, D]], -Or a single PyTorch Tensor with shape: [E[, C, H, W, D]], the E dimension represents -the output data from different models. -Typically, the input data is model output of segmentation task or classification task.

-
-

Note

-

This vote transform expects the input data is discrete values. It can be multiple channels -data in One-Hot format or single channel data. It will vote to select the most common data -between items. -The output data has the same shape as every item of the input data.

-
-
-
Parameters:
-

num_classes (Optional[int, None]) – if the input is single channel data instead of One-Hot, we can’t get class number -from channel, need to explicitly specify the number of classes to vote.

-
-
-
-
-__call__(img)[source]#
-

Call self as a function.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-
-

Signal#

-
-

SignalRandDrop#

-
-
-class monai.transforms.SignalRandDrop(boundaries=(0.0, 1.0))[source]#
-

Randomly drop a portion of a signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to be dropped

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(0.0, 1.0))[source]#
-
-
Parameters:
-
    -
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the signal drop,

  • -
  • default (lower and upper values need to be positive) – [0.0, 1.0]

  • -
-
-
-
- -
- -
-
-

SignalRandScale#

-
-
-class monai.transforms.SignalRandScale(boundaries=(-1.0, 1.0))[source]#
-

Apply a random rescaling on a signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to be scaled

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(-1.0, 1.0))[source]#
-
-
Parameters:
-

boundaries (Sequence[float]) – list defining lower and upper boundaries for the signal scaling, default : [-1.0, 1.0]

-
-
-
- -
- -
-
-

SignalRandShift#

-
-
-class monai.transforms.SignalRandShift(mode='wrap', filling=0.0, boundaries=(-1.0, 1.0))[source]#
-

Apply a random shift on a signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to be shifted

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(mode='wrap', filling=0.0, boundaries=(-1.0, 1.0))[source]#
-
-
Parameters:
-
-
-
-
- -
- -
-
-

SignalRandAddSine#

-
-
-class monai.transforms.SignalRandAddSine(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02))[source]#
-

Add a random sinusoidal signal to the input signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which sinusoidal signal will be added

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02))[source]#
-
-
Parameters:
-
    -
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the sinusoidal magnitude, -lower and upper values need to be positive ,default : [0.1, 0.3]

  • -
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for sinusoidal -signal generation ,default : [0.001, 0.02]

  • -
-
-
-
- -
- -
-
-

SignalRandAddSquarePulse#

-
-
-class monai.transforms.SignalRandAddSquarePulse(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02))[source]#
-

Add a random square pulse signal to the input signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which square pulse will be added

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02))[source]#
-
-
Parameters:
-
    -
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the square pulse magnitude, -lower and upper values need to be positive , default : [0.01, 0.2]

  • -
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for the square pulse -signal generation , default : [0.001, 0.02]

  • -
-
-
-
- -
- -
-
-

SignalRandAddGaussianNoise#

-
-
-class monai.transforms.SignalRandAddGaussianNoise(boundaries=(0.001, 0.02))[source]#
-

Add a random gaussian noise to the input signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which gaussian noise will be added

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(0.001, 0.02))[source]#
-
-
Parameters:
-

boundaries (Sequence[float]) – list defining lower and upper boundaries for the signal magnitude, -default : [0.001,0.02]

-
-
-
- -
- -
-
-

SignalRandAddSinePartial#

-
-
-class monai.transforms.SignalRandAddSinePartial(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
-

Add a random partial sinusoidal signal to the input signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-
    -
  • signal (Union[ndarray, Tensor]) – input 1 dimension signal to which a partial sinusoidal signal

  • -
  • added (will be) –

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(0.1, 0.3), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
-
-
Parameters:
-
    -
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the sinusoidal magnitude, -lower and upper values need to be positive , default : [0.1, 0.3]

  • -
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for sinusoidal -signal generation , default : [0.001, 0.02]

  • -
  • fraction (Sequence[float]) – list defining lower and upper boundaries for partial signal generation -default : [0.01, 0.2]

  • -
-
-
-
- -
- -
-
-

SignalRandAddSquarePulsePartial#

-
-
-class monai.transforms.SignalRandAddSquarePulsePartial(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
-

Add a random partial square pulse to a signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – input 1 dimension signal to which a partial square pulse will be added

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(boundaries=(0.01, 0.2), frequencies=(0.001, 0.02), fraction=(0.01, 0.2))[source]#
-
-
Parameters:
-
    -
  • boundaries (Sequence[float]) – list defining lower and upper boundaries for the square pulse magnitude, -lower and upper values need to be positive , default : [0.01, 0.2]

  • -
  • frequencies (Sequence[float]) – list defining lower and upper frequencies for square pulse -signal generation example : [0.001, 0.02]

  • -
  • fraction (Sequence[float]) – list defining lower and upper boundaries for partial square pulse generation -default: [0.01, 0.2]

  • -
-
-
-
- -
- -
-
-

SignalFillEmpty#

-
-
-class monai.transforms.SignalFillEmpty(replacement=0.0)[source]#
-

replace empty part of a signal (NaN)

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (Union[ndarray, Tensor]) – signal to be filled

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(replacement=0.0)[source]#
-
-
Parameters:
-

replacement (float) – value to replace nan items in signal

-
-
-
- -
- -
-
-

SignalRemoveFrequency#

-
-
-class monai.transforms.SignalRemoveFrequency(frequency=None, quality_factor=None, sampling_freq=None)[source]#
-

Remove a frequency from a signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (ndarray) – signal to be frequency removed

-
-
Return type:
-

Any

-
-
-
- -
-
-__init__(frequency=None, quality_factor=None, sampling_freq=None)[source]#
-
-
Parameters:
-
-
-
-
- -
- -
-
-

SignalContinuousWavelet#

-
-
-class monai.transforms.SignalContinuousWavelet(type='mexh', length=125.0, frequency=500.0)[source]#
-

Generate continuous wavelet transform of a signal

-
-
-__call__(signal)[source]#
-
-
Parameters:
-

signal (ndarray) – signal for which to generate continuous wavelet transform

-
-
Return type:
-

Any

-
-
-
- -
-
-__init__(type='mexh', length=125.0, frequency=500.0)[source]#
-
-
Parameters:
-
-
-
-
- -
- -
-
-
-

Spatial#

-
-

SpatialResample#

-
-
-class monai.transforms.SpatialResample(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, lazy=False)[source]#
-

Resample input image from the orientation/spacing defined by src_affine affine matrix into -the ones specified by dst_affine affine matrix.

-

Internally this transform computes the affine transform matrix from src_affine to dst_affine, -by xform = linalg.solve(src_affine, dst_affine), and call monai.transforms.Affine with xform.

-
-
-__call__(img, dst_affine=None, spatial_size=None, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – input image to be resampled. It currently supports channel-first arrays with -at most three spatial dimensions.

  • -
  • dst_affine (Optional[Tensor, None]) – destination affine matrix. Defaults to None, which means the same as img.affine. -the shape should be (r+1, r+1) where r is the spatial rank of img. -when dst_affine and spatial_size are None, the input will be returned without resampling, -but the data type will be float32.

  • -
  • spatial_size (Union[Sequence[int], Tensor, int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, -the transform will compute a spatial size automatically containing the previous field of view. -if spatial_size is -1 are the transform will use the corresponding input img size.

  • -
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to self.padding_mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • align_corners (Optional[bool, None]) – Geometrically, we consider the pixels of the input as squares rather than points. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -Defaults to None, effectively using the value of self.align_corners.

  • -
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to self.dtype or -np.float64 (for best precision). If None, use the data type of input data. -To be compatible with other modules, the output data type is always float32.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
-

The spatial rank is determined by the smallest among img.ndim -1, len(src_affine) - 1, and 3.

-

When both monai.config.USE_COMPILED and align_corners are set to True, -MONAI’s resampling implementation will be used. -Set dst_affine and spatial_size to None to turn off the resampling step.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, lazy=False)[source]#
-
-
Parameters:
-
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

ResampleToMatch#

-
-
-class monai.transforms.ResampleToMatch(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, lazy=False)[source]#
-

Resample an image to match given metadata. The affine matrix will be aligned, -and the size of the output image will match.

-
-
-__call__(img, img_dst, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
-
-
Parameters:
-
-
-
Raises:
-

ValueError – When the affine matrix of the source image is not invertible.

-
-
Return type:
-

Tensor

-
-
Returns:
-

Resampled input tensor or MetaTensor.

-
-
-
- -
- -
-
-

Spacing#

-example of Spacing -
-
-class monai.transforms.Spacing(pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, lazy=False)[source]#
-

Resample input image into the specified pixdim.

-
-
-__call__(data_array, affine=None, mode=None, padding_mode=None, align_corners=None, dtype=None, scale_extent=None, output_spatial_shape=None, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data_array (Tensor) – in shape (num_channels, H[, W, …]).

  • -
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "self.mode". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "self.padding_mode". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • align_corners (Optional[bool, None]) – Geometrically, we consider the pixels of the input as squares rather than points. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -Defaults to None, effectively using the value of self.align_corners.

  • -
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to self.dtype. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • scale_extent (Optional[bool, None]) – whether the scale is computed based on the spacing or the full extent of voxels, -The option is ignored if output spatial size is specified when calling this transform. -See also: monai.data.utils.compute_shape_offset(). When this is True, align_corners -should be True because compute_shape_offset already provides the corner alignment shift/scaling.

  • -
  • output_spatial_shape (Union[Sequence[int], ndarray, int, None]) – specify the shape of the output data_array. This is typically useful for -the inverse of Spacingd where sometimes we could not compute the exact shape due to the quantization -error with the affine.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Raises:
-
    -
  • ValueError – When data_array has no spatial dimensions.

  • -
  • ValueError – When pixdim is nonpositive.

  • -
-
-
Return type:
-

Tensor

-
-
Returns:
-

data tensor or MetaTensor (resampled into self.pixdim).

-
-
-
- -
-
-__init__(pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, lazy=False)[source]#
-
-
Parameters:
-
    -
  • pixdim (Union[Sequence[float], float, ndarray]) – output voxel spacing. if providing a single number, will use it for the first dimension. -items of the pixdim sequence map to the spatial dimensions of input image, if length -of pixdim sequence is longer than image spatial dimensions, will ignore the longer part, -if shorter, will pad with the last value. For example, for 3D image if pixdim is [1.0, 2.0] it -will be padded to [1.0, 2.0, 2.0] -if the components of the pixdim are non-positive values, the transform will use the -corresponding components of the original pixdim, which is computed from the affine -matrix of input image.

  • -
  • diagonal (bool) –

    whether to resample the input to have a diagonal affine matrix. -If True, the input data is resampled to the following affine:

    -
    np.diag((pixdim_0, pixdim_1, ..., pixdim_n, 1))
    -
    -
    -

    This effectively resets the volume to the world coordinate system (RAS+ in nibabel). -The original orientation, rotation, shearing are not preserved.

    -

    If False, this transform preserves the axes orientation, orthogonal rotation and -translation components from the original affine. This option will not flip/swap axes -of the original data.

    -

  • -
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • align_corners (bool) – Geometrically, we consider the pixels of the input as squares rather than points. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • scale_extent (bool) – whether the scale is computed based on the spacing or the full extent of voxels, -default False. The option is ignored if output spatial size is specified when calling this transform. -See also: monai.data.utils.compute_shape_offset(). When this is True, align_corners -should be True because compute_shape_offset already provides the corner alignment shift/scaling.

  • -
  • recompute_affine (bool) – whether to recompute affine based on the output shape. The affine computed -analytically does not reflect the potential quantization errors in terms of the output shape. -Set this flag to True to recompute the output affine based on the actual pixdim. Default to False.

  • -
  • min_pixdim (Union[Sequence[float], float, ndarray, None]) – minimal input spacing to be resampled. If provided, input image with a larger spacing than this -value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the -value of pixdim. Default to None.

  • -
  • max_pixdim (Union[Sequence[float], float, ndarray, None]) – maximal input spacing to be resampled. If provided, input image with a smaller spacing than this -value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the -value of pixdim. Default to None.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Orientation#

-example of Orientation -
-
-class monai.transforms.Orientation(axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), lazy=False)[source]#
-

Change the input image’s orientation into the specified based on axcodes.

-
-
-__call__(data_array, lazy=None)[source]#
-

If input type is MetaTensor, original affine is extracted with data_array.affine. -If input type is torch.Tensor, original affine is assumed to be identity.

-
-
Parameters:
-
    -
  • data_array (Tensor) – in shape (num_channels, H[, W, …]).

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Raises:
-
    -
  • ValueError – When data_array has no spatial dimensions.

  • -
  • ValueError – When axcodes spatiality differs from data_array.

  • -
-
-
Return type:
-

Tensor

-
-
Returns:
-

-
data_array [reoriented in self.axcodes]. Output type will be MetaTensor

unless get_track_meta() == False, in which case it will be -torch.Tensor.

-
-
-

-
-
-
- -
-
-__init__(axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), lazy=False)[source]#
-
-
Parameters:
-
    -
  • axcodes (Optional[str, None]) – N elements sequence for spatial ND input’s orientation. -e.g. axcodes=’RAS’ represents 3D orientation: -(Left, Right), (Posterior, Anterior), (Inferior, Superior). -default orientation labels options are: ‘L’ and ‘R’ for the first dimension, -‘P’ and ‘A’ for the second, ‘I’ and ‘S’ for the third.

  • -
  • as_closest_canonical (bool) – if True, load the image as closest to canonical axis format.

  • -
  • labels (Optional[Sequence[tuple[str, str]], None]) – optional, None or sequence of (2,) sequences -(2,) sequences are labels for (beginning, end) of output axis. -Defaults to (('L', 'R'), ('P', 'A'), ('I', 'S')).

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
Raises:
-

ValueError – When axcodes=None and as_closest_canonical=True. Incompatible values.

-
-
-

See Also: nibabel.orientations.ornt2axcodes.

-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

RandRotate#

-example of RandRotate -
-
-class monai.transforms.RandRotate(range_x=0.0, range_y=0.0, range_z=0.0, prob=0.1, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float32'>, lazy=False)[source]#
-

Randomly rotate the input arrays.

-
-
Parameters:
-
    -
  • range_x (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and second axes. -If single number, angle is uniformly sampled from (-range_x, range_x).

  • -
  • range_y (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and third axes. -If single number, angle is uniformly sampled from (-range_y, range_y). only work for 3D data.

  • -
  • range_z (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the second and third axes. -If single number, angle is uniformly sampled from (-range_z, range_z). only work for 3D data.

  • -
  • prob (float) – Probability of rotation.

  • -
  • keep_size (bool) – If it is False, the output shape is adapted so that the -input array is contained completely in the output. -If it is True, the output shape is the same as the input. Default is True.

  • -
  • mode (str) – {"bilinear", "nearest"} -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • align_corners (bool) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape 2D: (nchannels, H, W), or 3D: (nchannels, H, W, D).

  • -
  • mode (Optional[str, None]) – {"bilinear", "nearest"} -Interpolation mode to calculate output values. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to self.padding_mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • align_corners (Optional[bool, None]) – Defaults to self.align_corners. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandFlip#

-example of RandFlip -
-
-class monai.transforms.RandFlip(prob=0.1, spatial_axis=None, lazy=False)[source]#
-

Randomly flips the image along axes. Preserves shape. -See numpy.flip for additional details. -https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

-
-
Parameters:
-
    -
  • prob (float) – Probability of flipping.

  • -
  • spatial_axis (Union[Sequence[int], int, None]) – Spatial axes along which to flip over. Default is None.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(img, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]),

  • -
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandAxisFlip#

-example of RandAxisFlip -
-
-class monai.transforms.RandAxisFlip(prob=0.1, lazy=False)[source]#
-

Randomly select a spatial axis and flip along it. -See numpy.flip for additional details. -https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

-
-
Parameters:
-
    -
  • prob (float) – Probability of flipping.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(img, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ])

  • -
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(data)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

RandZoom#

-example of RandZoom -
-
-class monai.transforms.RandZoom(prob=0.1, min_zoom=0.9, max_zoom=1.1, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=torch.float32, keep_size=True, lazy=False, **kwargs)[source]#
-

Randomly zooms input arrays with given probability within given zoom range.

-
-
Parameters:
-
    -
  • prob (float) – Probability of zooming.

  • -
  • min_zoom (Union[Sequence[float], float]) – Min zoom factor. Can be float or sequence same size as image. -If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims -to keep the original spatial shape ratio. -If a sequence, min_zoom should contain one value for each spatial axis. -If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • -
  • max_zoom (Union[Sequence[float], float]) – Max zoom factor. Can be float or sequence same size as image. -If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims -to keep the original spatial shape ratio. -If a sequence, max_zoom should contain one value for each spatial axis. -If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • -
  • mode (str) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to "area". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • padding_mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -The mode to pad data after zooming. -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • align_corners (Optional[bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data.

  • -
  • keep_size (bool) – Should keep original size (pad if needed), default is True.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape 2D: (nchannels, H, W), or 3D: (nchannels, H, W, D).

  • -
  • mode (Optional[str, None]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", -"area"}, the interpolation mode. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • padding_mode (Optional[str, None]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -The mode to pad data after zooming. -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • align_corners (Optional[bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Defaults to self.align_corners. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. -If None, use the data type of input data.

  • -
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-randomize(img)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

Affine#

-example of Affine -
-
-class monai.transforms.Affine(rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, normalized=False, device=None, dtype=<class 'numpy.float32'>, align_corners=False, image_only=False, lazy=False)[source]#
-

Transform img given the affine parameters. -A tutorial is available: Project-MONAI/tutorials.

-
-
-__call__(img, spatial_size=None, mode=None, padding_mode=None, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – shape must be (num_channels, H, W[, D]),

  • -
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if img has two spatial dimensions, spatial_size should have 2 elements [h, w]. -if img has three spatial dimensions, spatial_size should have 3 elements [h, w, d].

  • -
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to self.padding_mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

UnionType[Tensor, tuple[Tensor, Union[ndarray, Tensor]]]

-
-
-
- -
-
-__init__(rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, normalized=False, device=None, dtype=<class 'numpy.float32'>, align_corners=False, image_only=False, lazy=False)[source]#
-

The affine transformations are applied in rotate, shear, translate, scale order.

-
-
Parameters:
-
    -
  • rotate_params (Union[Sequence[float], float, None]) – a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. -Defaults to no rotation.

  • -
  • shear_params (Union[Sequence[float], float, None]) –

    shearing factors for affine matrix, take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -a tuple of 2 floats for 2D, a tuple of 6 floats for 3D. Defaults to no shearing.
    -
    -
    -

  • -
  • translate_params (Union[Sequence[float], float, None]) – a tuple of 2 floats for 2D, a tuple of 3 floats for 3D. Translation is in -pixel/voxel relative to the center of the input image. Defaults to no translation.

  • -
  • scale_params (Union[Sequence[float], float, None]) – scale factor for every spatial dims. a tuple of 2 floats for 2D, -a tuple of 3 floats for 3D. Defaults to 1.0.

  • -
  • affine (Union[ndarray, Tensor, None]) – If applied, ignore the params (rotate_params, etc.) and use the -supplied matrix. Should be square with each side = num of image spatial -dimensions + 1.

  • -
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • normalized (bool) – indicating whether the provided affine is defined to include a normalization -transform converting the coordinates from [-(size-1)/2, (size-1)/2] (defined in create_grid) to -[0, size - 1] or [-1, 1] in order to be compatible with the underlying resampling API. -If normalized=False, additional coordinate normalization will be applied before resampling. -See also: monai.networks.utils.normalize_transform().

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • dtype (Union[dtype, type, str, None]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • align_corners (bool) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • image_only (bool) – if True return only the image volume, otherwise return (image, affine).

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Resample#

-
-
-class monai.transforms.Resample(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, norm_coords=True, device=None, align_corners=False, dtype=<class 'numpy.float64'>)[source]#
-
-
-__call__(img, grid=None, mode=None, padding_mode=None, dtype=None, align_corners=None)[source]#
-
-
Parameters:
-
-
-
-
-

See also

-

monai.config.USE_COMPILED

-
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, norm_coords=True, device=None, align_corners=False, dtype=<class 'numpy.float64'>)[source]#
-

computes output image using values from img, locations from grid using pytorch. -supports spatially 2D or 3D (num_channels, H, W[, D]).

-
-
Parameters:
-
-
-
-
- -
- -
-
-

RandAffine#

-example of RandAffine -
-
-class monai.transforms.RandAffine(prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, lazy=False)[source]#
-

Random affine transform. -A tutorial is available: Project-MONAI/tutorials.

-
-
-__call__(img, spatial_size=None, mode=None, padding_mode=None, randomize=True, grid=None, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – shape must be (num_channels, H, W[, D]),

  • -
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if img has two spatial dimensions, spatial_size should have 2 elements [h, w]. -if img has three spatial dimensions, spatial_size should have 3 elements [h, w, d].

  • -
  • mode (Union[str, int, None]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (Optional[str, None]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to self.padding_mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • -
  • grid – precomputed grid to be used (mainly to accelerate RandAffined).

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, lazy=False)[source]#
-
-
Parameters:
-
    -
  • prob (float) – probability of returning a randomized affine grid. -defaults to 0.1, with 10% chance returns a randomized grid.

  • -
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 2 floats for 2D, a tuple of 6 floats for 3D) for affine matrix, -take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select pixel/voxel to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to bilinear. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to reflection. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • cache_grid (bool) – whether to cache the identity sampling grid. -If the spatial size is not dynamically defined by input image, enabling this option could -accelerate the transform.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-

See also

-
    -
  • RandAffineGrid for the random affine parameters configurations.

  • -
  • Affine for the affine transformation parameters configurations.

  • -
-
-
- -
-
-get_identity_grid(spatial_size, lazy)[source]#
-

Return a cached or new identity grid depends on the availability.

-
-
Parameters:
-

spatial_size (Sequence[int]) – non-dynamic spatial size

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandAffine

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandDeformGrid#

-
-
-class monai.transforms.RandDeformGrid(spacing, magnitude_range, device=None)[source]#
-

Generate random deformation grid.

-
-
-__call__(spatial_size)[source]#
-
-
Parameters:
-

spatial_size (Sequence[int]) – spatial size of the grid.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(spacing, magnitude_range, device=None)[source]#
-
-
Parameters:
-
    -
  • spacing (Union[Sequence[float], float]) – spacing of the grid in 2D or 3D. -e.g., spacing=(1, 1) indicates pixel-wise deformation in 2D, -spacing=(1, 1, 1) indicates voxel-wise deformation in 3D, -spacing=(2, 2) indicates deformation field defined on every other pixel in 2D.

  • -
  • magnitude_range (tuple[float, float]) – the random offsets will be generated from -uniform[magnitude[0], magnitude[1]).

  • -
  • device (Optional[device, None]) – device to store the output grid data.

  • -
-
-
-
- -
-
-randomize(grid_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

AffineGrid#

-
-
-class monai.transforms.AffineGrid(rotate_params=None, shear_params=None, translate_params=None, scale_params=None, device=None, dtype=<class 'numpy.float32'>, align_corners=False, affine=None, lazy=False)[source]#
-

Affine transforms on the coordinates.

-
-
Parameters:
-
    -
  • rotate_params (Union[Sequence[float], float, None]) – a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. -Defaults to no rotation.

  • -
  • shear_params (Union[Sequence[float], float, None]) –

    shearing factors for affine matrix, take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -a tuple of 2 floats for 2D, a tuple of 6 floats for 3D. Defaults to no shearing.
    -
    -
    -

  • -
  • translate_params (Union[Sequence[float], float, None]) – a tuple of 2 floats for 2D, a tuple of 3 floats for 3D. Translation is in -pixel/voxel relative to the center of the input image. Defaults to no translation.

  • -
  • scale_params (Union[Sequence[float], float, None]) – scale factor for every spatial dims. a tuple of 2 floats for 2D, -a tuple of 3 floats for 3D. Defaults to 1.0.

  • -
  • dtype (Union[dtype, type, str, None]) – data type for the grid computation. Defaults to float32. -If None, use the data type of input data (if grid is provided).

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated, if a new grid is generated.

  • -
  • align_corners (bool) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • affine (Union[ndarray, Tensor, None]) – If applied, ignore the params (rotate_params, etc.) and use the -supplied matrix. Should be square with each side = num of image spatial -dimensions + 1.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(spatial_size=None, grid=None, lazy=None)[source]#
-

The grid can be initialized with a spatial_size parameter, or provided directly as grid. -Therefore, either spatial_size or grid must be provided. -When initialising from spatial_size, the backend “torch” will be used.

-
-
Parameters:
-
    -
  • spatial_size (Optional[Sequence[int], None]) – output grid size.

  • -
  • grid (Optional[Tensor, None]) – grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Raises:
-

ValueError – When grid=None and spatial_size=None. Incompatible values.

-
-
Return type:
-

tuple[UnionType[Tensor, None], Tensor]

-
-
-
- -
- -
-
-

RandAffineGrid#

-
-
-class monai.transforms.RandAffineGrid(rotate_range=None, shear_range=None, translate_range=None, scale_range=None, device=None, dtype=<class 'numpy.float32'>, lazy=False)[source]#
-

Generate randomised affine grid.

-
-
-__call__(spatial_size=None, grid=None, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • spatial_size (Optional[Sequence[int], None]) – output grid size.

  • -
  • grid (Union[ndarray, Tensor, None]) – grid to be transformed. Shape must be (3, H, W) for 2D or (4, H, W, D) for 3D.

  • -
  • randomize (bool) – boolean as to whether the grid parameters governing the grid should be randomized.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
Returns:
-

a 2D (3xHxW) or 3D (4xHxWxD) grid.

-
-
-
- -
-
-__init__(rotate_range=None, shear_range=None, translate_range=None, scale_range=None, device=None, dtype=<class 'numpy.float32'>, lazy=False)[source]#
-
-
Parameters:
-
    -
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 2 floats for 2D, a tuple of 6 floats for 3D) for affine matrix, -take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select voxels to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • device (Optional[device, None]) – device to store the output grid data.

  • -
  • dtype (Union[dtype, type, str, None]) – data type for the grid computation. Defaults to np.float32. -If None, use the data type of input data (if grid is provided).

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
- -
- -
-
-get_transformation_matrix()[source]#
-

Get the most recently applied transformation matrix

-
-
Return type:
-

UnionType[Tensor, None]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

GridDistortion#

-example of GridDistortion -
-
-class monai.transforms.GridDistortion(num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
-
-
-__call__(img, distort_steps=None, mode=None, padding_mode=None)[source]#
-
-
Parameters:
-
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
-

Grid distortion transform. Refer to: -albumentations-team/albumentations

-
-
Parameters:
-
-
-
-
- -
- -
-
-

RandGridDistortion#

-example of RandGridDistortion -
-
-class monai.transforms.RandGridDistortion(num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
-
-
-__call__(img, mode=None, padding_mode=None, randomize=True)[source]#
-
-
Parameters:
-
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None)[source]#
-

Random grid distortion transform. Refer to: -albumentations-team/albumentations

-
-
Parameters:
-
-
-
-
- -
-
-randomize(spatial_shape)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

Rand2DElastic#

-example of Rand2DElastic -
-
-class monai.transforms.Rand2DElastic(spacing, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
-

Random elastic deformation and affine in 2D. -A tutorial is available: Project-MONAI/tutorials.

-
-
-__call__(img, spatial_size=None, mode=None, padding_mode=None, randomize=True)[source]#
-
-
Parameters:
-
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(spacing, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
-
-
Parameters:
-
    -
  • spacing (UnionType[tuple[float, float], float]) – distance in between the control points.

  • -
  • magnitude_range (tuple[float, float]) – the random offsets will be generated from uniform[magnitude[0], magnitude[1]).

  • -
  • prob (float) – probability of returning a randomized elastic transform. -defaults to 0.1, with 10% chance returns a randomized elastic transform, -otherwise returns a spatial_size centered area extracted from the input image.

  • -
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 2 floats for 2D) for affine matrix, take a 2D affine as example:

    -
    [
    -    [1.0, params[0], 0.0],
    -    [params[1], 1.0, 0.0],
    -    [0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select pixel to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • spatial_size (Union[int, tuple[int, int], None]) – specifying output image spatial size [h, w]. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
-
-
-
-

See also

-
    -
  • RandAffineGrid for the random affine parameters configurations.

  • -
  • Affine for the affine transformation parameters configurations.

  • -
-
-
- -
-
-randomize(spatial_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Rand2DElastic

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

Rand3DElastic#

-example of Rand3DElastic -
-
-class monai.transforms.Rand3DElastic(sigma_range, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
-

Random elastic deformation and affine in 3D. -A tutorial is available: Project-MONAI/tutorials.

-
-
-__call__(img, spatial_size=None, mode=None, padding_mode=None, randomize=True)[source]#
-
-
Parameters:
-
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(sigma_range, magnitude_range, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None)[source]#
-
-
Parameters:
-
    -
  • sigma_range (tuple[float, float]) – a Gaussian kernel with standard deviation sampled from -uniform[sigma_range[0], sigma_range[1]) will be used to smooth the random offset grid.

  • -
  • magnitude_range (tuple[float, float]) – the random offsets on the grid will be generated from -uniform[magnitude[0], magnitude[1]).

  • -
  • prob (float) – probability of returning a randomized elastic transform. -defaults to 0.1, with 10% chance returns a randomized elastic transform, -otherwise returns a spatial_size centered area extracted from the input image.

  • -
  • rotate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 6 floats for 3D) for affine matrix, take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select voxel to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[Union[Tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • spatial_size (Union[tuple[int, int, int], int, None]) – specifying output image spatial size [h, w, d]. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, 32, -1) will be adapted -to (32, 32, 64) if the third spatial dimension size of img is 64.

  • -
  • mode (UnionType[str, int]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
-
-
-
-

See also

-
    -
  • RandAffineGrid for the random affine parameters configurations.

  • -
  • Affine for the affine transformation parameters configurations.

  • -
-
-
- -
-
-randomize(grid_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Rand3DElastic

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

Rotate90#

-example of Rotate90 -
-
-class monai.transforms.Rotate90(k=1, spatial_axes=(0, 1), lazy=False)[source]#
-

Rotate an array by 90 degrees in the plane specified by axes. -See torch.rot90 for additional details: -https://pytorch.org/docs/stable/generated/torch.rot90.html#torch-rot90.

-
-
-__call__(img, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]),

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(k=1, spatial_axes=(0, 1), lazy=False)[source]#
-
-
Parameters:
-
    -
  • k (int) – number of times to rotate by 90 degrees.

  • -
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. -Default: (0, 1), this is the first two axis in spatial dimensions. -If axis is negative it counts from the last to the first axis.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

RandRotate90#

-example of RandRotate90 -
-
-class monai.transforms.RandRotate90(prob=0.1, max_k=3, spatial_axes=(0, 1), lazy=False)[source]#
-

With probability prob, input arrays are rotated by 90 degrees -in the plane specified by spatial_axes.

-
-
-__call__(img, randomize=True, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]),

  • -
  • randomize (bool) – whether to execute randomize() function first, default to True.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-__init__(prob=0.1, max_k=3, spatial_axes=(0, 1), lazy=False)[source]#
-
-
Parameters:
-
    -
  • prob (float) – probability of rotating. -(Default 0.1, with 10% probability it returns a rotated array)

  • -
  • max_k (int) – number of rotations will be sampled from np.random.randint(max_k) + 1, (Default 3).

  • -
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. -Default: (0, 1), this is the first two axis in spatial dimensions.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

Flip#

-example of Flip -
-
-class monai.transforms.Flip(spatial_axis=None, lazy=False)[source]#
-

Reverses the order of elements along the given spatial axis. Preserves shape. -See torch.flip documentation for additional details: -https://pytorch.org/docs/stable/generated/torch.flip.html

-
-
Parameters:
-
    -
  • spatial_axis (Union[Sequence[int], int, None]) – spatial axes along which to flip over. Default is None. -The default axis=None will flip over all of the axes of the input array. -If axis is negative it counts from the last to the first axis. -If axis is a tuple of ints, flipping is performed on all of the axes -specified in the tuple.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(img, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ])

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

Resize#

-example of Resize -
-
-class monai.transforms.Resize(spatial_size, size_mode='all', mode=InterpolateMode.AREA, align_corners=None, anti_aliasing=False, anti_aliasing_sigma=None, dtype=torch.float32, lazy=False)[source]#
-

Resize the input image to given spatial size (with scaling, not cropping/padding). -Implemented using torch.nn.functional.interpolate.

-
-
Parameters:
-
    -
  • spatial_size (Union[Sequence[int], int]) – expected shape of spatial dimensions after resize operation. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • size_mode (str) – should be “all” or “longest”, if “all”, will use spatial_size for all the spatial dims, -if “longest”, rescale the image so that only the longest side is equal to specified spatial_size, -which must be an int number in this case, keeping the aspect ratio of the initial image, refer to: -https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ -#albumentations.augmentations.geometric.resize.LongestMaxSize.

  • -
  • mode (str) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to "area". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • align_corners (Optional[bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • anti_aliasing (bool) – bool -Whether to apply a Gaussian filter to smooth the image prior -to downsampling. It is crucial to filter when downsampling -the image to avoid aliasing artifacts. See also skimage.transform.resize

  • -
  • anti_aliasing_sigma (Union[Sequence[float], float, None]) – {float, tuple of floats}, optional -Standard deviation for Gaussian filtering used when anti-aliasing. -By default, this value is chosen as (s - 1) / 2 where s is the -downsampling factor, where s > 1. For the up-size case, s < 1, no -anti-aliasing is performed prior to rescaling.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(img, mode=None, align_corners=None, anti_aliasing=None, anti_aliasing_sigma=None, dtype=None, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]).

  • -
  • mode (Optional[str, None]) – {"nearest", "nearest-exact", "linear", -"bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • align_corners (Optional[bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Defaults to self.align_corners. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • anti_aliasing (Optional[bool, None]) – bool, optional -Whether to apply a Gaussian filter to smooth the image prior -to downsampling. It is crucial to filter when downsampling -the image to avoid aliasing artifacts. See also skimage.transform.resize

  • -
  • anti_aliasing_sigma (Union[Sequence[float], float, None]) – {float, tuple of floats}, optional -Standard deviation for Gaussian filtering used when anti-aliasing. -By default, this value is chosen as (s - 1) / 2 where s is the -downsampling factor, where s > 1. For the up-size case, s < 1, no -anti-aliasing is performed prior to rescaling.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. -If None, use the data type of input data.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Raises:
-

ValueError – When self.spatial_size length is less than img spatial dimensions.

-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

Rotate#

-example of Rotate -
-
-class monai.transforms.Rotate(angle, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=torch.float32, lazy=False)[source]#
-

Rotates an input image by given angle using monai.networks.layers.AffineTransform.

-
-
Parameters:
-
    -
  • angle (Union[Sequence[float], float]) – Rotation angle(s) in radians. should a float for 2D, three floats for 3D.

  • -
  • keep_size (bool) – If it is True, the output shape is kept the same as the input. -If it is False, the output shape is adapted so that the -input array is contained completely in the output. Default is True.

  • -
  • mode (str) – {"bilinear", "nearest"} -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • align_corners (bool) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
-
-
Parameters:
-
-
-
Raises:
-

ValueError – When img spatially is not one of [2D, 3D].

-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

Zoom#

-example of Zoom -
-
-class monai.transforms.Zoom(zoom, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=torch.float32, keep_size=True, lazy=False, **kwargs)[source]#
-

Zooms an ND image using torch.nn.functional.interpolate. -For details, please see https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html.

-

Different from monai.transforms.resize, this transform takes scaling factors -as input, and provides an option of preserving the input spatial size.

-
-
Parameters:
-
    -
  • zoom (Union[Sequence[float], float]) – The zoom factor along the spatial axes. -If a float, zoom is the same for each spatial axis. -If a sequence, zoom should contain one value for each spatial axis.

  • -
  • mode (str) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to "area". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • padding_mode (str) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "edge". -The mode to pad data after zooming. -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • align_corners (Optional[bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data.

  • -
  • keep_size (bool) – Should keep original size (padding/slicing if needed), default is True.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-__call__(img, mode=None, padding_mode=None, align_corners=None, dtype=None, lazy=None)[source]#
-
-
Parameters:
-
    -
  • img (Tensor) – channel first array, must have shape: (num_channels, H[, W, …, ]).

  • -
  • mode (Optional[str, None]) – {"nearest", "nearest-exact", "linear", -"bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to self.mode. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • padding_mode (Optional[str, None]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "edge". -The mode to pad data after zooming. -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • align_corners (Optional[bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Defaults to self.align_corners. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to self.dtype. -If None, use the data type of input data.

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

GridPatch#

-
-
-class monai.transforms.GridPatch(patch_size, offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, **pad_kwargs)[source]#
-

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps. -It can sort the patches and return all or a subset of them.

-
-
Parameters:
-
    -
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • -
  • offset (Optional[Sequence[int], None]) – offset of starting position in the array, default is 0 for each dimension.

  • -
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. -If the requested number of patches is greater than the number of available patches, -padding will be applied to provide exactly num_patches patches unless threshold is set. -When threshold is set, this value is treated as the maximum number of patches. -Defaults to None, which does not limit number of the patches.

  • -
  • overlap (Union[Sequence[float], float]) – the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). -If only one float number is given, it will be applied to all dimensions. Defaults to 0.0.

  • -
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), -lowest values (“min”), or in their default order (None). Default to None.

  • -
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. -Defaults to no filtering.

  • -
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. -Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. -Defaults to None, which means no padding will be applied. -See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -requires pytorch >= 1.10 for best compatibility.

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Returns:
-

-
the extracted patches as a single tensor (with patch dimension as the first dimension),

with following metadata:

-
    -
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • -
  • PatchKeys.COUNT: total number of patches in the image,

  • -
  • ”spatial_shape”: spatial size of the extracted patch, and

  • -
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • -
-
-
-

-
-
Return type:
-

MetaTensor

-
-
-
-
-__call__(array)[source]#
-

Extract the patches (sweeping the entire image in a row-major sliding-window manner with possible overlaps).

-
-
Parameters:
-

array (Union[ndarray, Tensor]) – a input image as numpy.ndarray or torch.Tensor

-
-
Returns:
-

-
the extracted patches as a single tensor (with patch dimension as the first dimension),

with defined PatchKeys.LOCATION and PatchKeys.COUNT metadata.

-
-
-

-
-
Return type:
-

MetaTensor

-
-
-
- -
-
-filter_count(image_np, locations)[source]#
-

Sort the patches based on the sum of their intensity, and just keep self.num_patches of them.

-
-
Parameters:
-
    -
  • image_np (Union[ndarray, Tensor]) – a numpy.ndarray or torch.Tensor representing a stack of patches.

  • -
  • locations (ndarray) – a numpy.ndarray representing the stack of location of each patch.

  • -
-
-
Return type:
-

tuple[Union[ndarray, Tensor], ndarray]

-
-
-
- -
-
-filter_threshold(image_np, locations)[source]#
-

Filter the patches and their locations according to a threshold.

-
-
Parameters:
-
    -
  • image_np (Union[ndarray, Tensor]) – a numpy.ndarray or torch.Tensor representing a stack of patches.

  • -
  • locations (ndarray) – a numpy.ndarray representing the stack of location of each patch.

  • -
-
-
Returns:
-

tuple of filtered patches and locations.

-
-
Return type:
-

tuple[NdarrayOrTensor, numpy.ndarray]

-
-
-
- -
- -
-
-

RandGridPatch#

-
-
-class monai.transforms.RandGridPatch(patch_size, min_offset=None, max_offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, **pad_kwargs)[source]#
-

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps, -and with random offset for the minimal corner of the image, (0,0) for 2D and (0,0,0) for 3D. -It can sort the patches and return all or a subset of them.

-
-
Parameters:
-
    -
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • -
  • min_offset (Union[Sequence[int], int, None]) – the minimum range of offset to be selected randomly. Defaults to 0.

  • -
  • max_offset (Union[Sequence[int], int, None]) – the maximum range of offset to be selected randomly. -Defaults to image size modulo patch size.

  • -
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. -If the requested number of patches is greater than the number of available patches, -padding will be applied to provide exactly num_patches patches unless threshold is set. -When threshold is set, this value is treated as the maximum number of patches. -Defaults to None, which does not limit number of the patches.

  • -
  • overlap (Union[Sequence[float], float]) – the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). -If only one float number is given, it will be applied to all dimensions. Defaults to 0.0.

  • -
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), -lowest values (“min”), or in their default order (None). Default to None.

  • -
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. -Defaults to no filtering.

  • -
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. -Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. -Defaults to None, which means no padding will be applied. -See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -requires pytorch >= 1.10 for best compatibility.

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Returns:
-

-
the extracted patches as a single tensor (with patch dimension as the first dimension),

with following metadata:

-
    -
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • -
  • PatchKeys.COUNT: total number of patches in the image,

  • -
  • ”spatial_shape”: spatial size of the extracted patch, and

  • -
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • -
-
-
-

-
-
Return type:
-

MetaTensor

-
-
-
-
-__call__(array, randomize=True)[source]#
-

Extract the patches (sweeping the entire image in a row-major sliding-window manner with possible overlaps).

-
-
Parameters:
-

array (Union[ndarray, Tensor]) – a input image as numpy.ndarray or torch.Tensor

-
-
Returns:
-

-
the extracted patches as a single tensor (with patch dimension as the first dimension),

with defined PatchKeys.LOCATION and PatchKeys.COUNT metadata.

-
-
-

-
-
Return type:
-

MetaTensor

-
-
-
- -
-
-randomize(array)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
- -
- -
-
-

GridSplit#

-
-
-class monai.transforms.GridSplit(grid=(2, 2), size=None)[source]#
-

Split the image into patches based on the provided grid in 2D.

-
-
Parameters:
-
    -
  • grid (tuple[int, int]) – a tuple define the shape of the grid upon which the image is split. Defaults to (2, 2)

  • -
  • size (Union[int, tuple[int, int], None]) – a tuple or an integer that defines the output patch sizes. -If it’s an integer, the value will be repeated for each dimension. -The default is None, where the patch size will be inferred from the grid shape.

  • -
-
-
-

Example

-

Given an image (torch.Tensor or numpy.ndarray) with size of (3, 10, 10) and a grid of (2, 2), -it will return a Tensor or array with the size of (4, 3, 5, 5). -Here, if the size is provided, the returned shape will be (4, 3, size, size)

-

Note: This transform currently support only image with two spatial dimensions.

-
-
-__call__(image, size=None)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

list[Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-
-

Smooth Field#

-
-

RandSmoothFieldAdjustContrast#

-example of RandSmoothFieldAdjustContrast -
-
-class monai.transforms.RandSmoothFieldAdjustContrast(spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.5, 4.5), device=None)[source]#
-

Randomly adjust the contrast of input images by calculating a randomized smooth field for each invocation.

-

This uses SmoothField internally to define the adjustment over the image. If pad is greater than 0 the -edges of the input volume of that width will be mostly unchanged. Contrast is changed by raising input -values by the power of the smooth field so the range of values given by gamma should be chosen with this -in mind. For example, a minimum value of 0 in gamma will produce white areas so this should be avoided. -After the contrast is adjusted the values of the result are rescaled to the range of the original input.

-
-
Parameters:
-
    -
  • spatial_size (Sequence[int]) – size of input array’s spatial dimensions

  • -
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • -
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 1

  • -
  • mode (str) – interpolation mode to use when upsampling

  • -
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • -
  • prob (float) – probability transform is applied

  • -
  • gamma (UnionType[Sequence[float], float]) – (min, max) range for exponential field

  • -
  • device (Optional[device, None]) – Pytorch device to define field on

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img, if randomize randomizing the smooth field otherwise reusing the previous.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandSmoothFieldAdjustContrast

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandSmoothFieldAdjustIntensity#

-example of RandSmoothFieldAdjustIntensity -
-
-class monai.transforms.RandSmoothFieldAdjustIntensity(spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.1, 1.0), device=None)[source]#
-

Randomly adjust the intensity of input images by calculating a randomized smooth field for each invocation.

-

This uses SmoothField internally to define the adjustment over the image. If pad is greater than 0 the -edges of the input volume of that width will be mostly unchanged. Intensity is changed by multiplying the -inputs by the smooth field, so the values of gamma should be chosen with this in mind. The default values -of (0.1, 1.0) are sensible in that values will not be zeroed out by the field nor multiplied greater than -the original value range.

-
-
Parameters:
-
    -
  • spatial_size (Sequence[int]) – size of input array

  • -
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • -
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 1

  • -
  • mode (str) – interpolation mode to use when upsampling

  • -
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • -
  • prob (float) – probability transform is applied

  • -
  • gamma (UnionType[Sequence[float], float]) – (min, max) range of intensity multipliers

  • -
  • device (Optional[device, None]) – Pytorch device to define field on

  • -
-
-
-
-
-__call__(img, randomize=True)[source]#
-

Apply the transform to img, if randomize randomizing the smooth field otherwise reusing the previous.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandSmoothFieldAdjustIntensity

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandSmoothDeform#

-example of RandSmoothDeform -
-
-class monai.transforms.RandSmoothDeform(spatial_size, rand_size, pad=0, field_mode=InterpolateMode.AREA, align_corners=None, prob=0.1, def_range=1.0, grid_dtype=torch.float32, grid_mode=GridSampleMode.NEAREST, grid_padding_mode=GridSamplePadMode.BORDER, grid_align_corners=False, device=None)[source]#
-

Deform an image using a random smooth field and Pytorch’s grid_sample.

-

The amount of deformation is given by def_range in fractions of the size of the image. The size of each dimension -of the input image is always defined as 2 regardless of actual image voxel dimensions, that is the coordinates in -every dimension range from -1 to 1. A value of 0.1 means pixels/voxels can be moved by up to 5% of the image’s size.

-
-
Parameters:
-
    -
  • spatial_size (Sequence[int]) – input array size to which deformation grid is interpolated

  • -
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • -
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • -
  • field_mode (str) – interpolation mode to use when upsampling the deformation field

  • -
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • -
  • prob (float) – probability transform is applied

  • -
  • def_range (UnionType[Sequence[float], float]) – value of the deformation range in image size fractions, single min/max value or min/max pair

  • -
  • grid_dtype – type for the deformation grid calculated from the field

  • -
  • grid_mode (str) – interpolation mode used for sampling input using deformation grid

  • -
  • grid_padding_mode (str) – padding mode used for sampling input using deformation grid

  • -
  • grid_align_corners (UnionType[bool, None]) – if True align the corners when sampling the deformation grid

  • -
  • device (Optional[device, None]) – Pytorch device to define field on

  • -
-
-
-
-
-__call__(img, randomize=True, device=None)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Randomizable

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-
-

MRI Transforms#

-
-

Kspace under-sampling#

-
-
-class monai.apps.reconstruction.transforms.array.KspaceMask(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
-

A basic class for under-sampling mask setup. It provides common -features for under-sampling mask generators. -For example, RandomMaskFunc and EquispacedMaskFunc (two mask -transform objects defined right after this module) -both inherit MaskFunc to properly setup properties like the -acceleration factor.

-
-
-abstract __call__(kspace)[source]#
-

This is an extra instance to allow for defining new mask generators. -For creating other mask transforms, define a new class and simply -override __call__. See an example of this in -monai.apps.reconstruction.transforms.array.RandomKspacemask.

-
-
Parameters:
-

kspace (Union[ndarray, Tensor]) – The input k-space data. The shape is (…,num_coils,H,W,2) -for complex 2D inputs and (…,num_coils,H,W,D) for real 3D -data.

-
-
Return type:
-

Sequence[Tensor]

-
-
-
- -
-
-__init__(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
-
-
Parameters:
-
    -
  • center_fractions (Sequence[float]) – Fraction of low-frequency columns to be retained. -If multiple values are provided, then one of these numbers -is chosen uniformly each time.

  • -
  • accelerations (Sequence[float]) – Amount of under-sampling. This should have the -same length as center_fractions. If multiple values are -provided, then one of these is chosen uniformly each time.

  • -
  • spatial_dims (int) – Number of spatial dims (e.g., it’s 2 for a 2D data; -it’s also 2 for pseudo-3D datasets like the fastMRI dataset). -The last spatial dim is selected for sampling. For the fastMRI -dataset, k-space has the form (…,num_slices,num_coils,H,W) -and sampling is done along W. For a general 3D data with the -shape (…,num_coils,H,W,D), sampling is done along D.

  • -
  • is_complex (bool) – if True, then the last dimension will be reserved for -real/imaginary parts.

  • -
-
-
-
- -
-
-randomize_choose_acceleration()[source]#
-

If multiple values are provided for center_fractions and -accelerations, this function selects one value uniformly -for each training/test sample.

-
-
Return type:
-

Sequence[float]

-
-
Returns:
-

-
A tuple containing

(1) center_fraction: chosen fraction of center kspace -lines to exclude from under-sampling -(2) acceleration: chosen acceleration factor

-
-
-

-
-
-
- -
- -
-
-class monai.apps.reconstruction.transforms.array.RandomKspaceMask(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
-

This k-space mask transform under-samples the k-space according to a -random sampling pattern. Precisely, it uniformly selects a subset of -columns from the input k-space data. If the k-space data has N columns, -the mask picks out:

-

1. N_low_freqs = (N * center_fraction) columns in the center -corresponding to low-frequencies

-

2. The other columns are selected uniformly at random with a probability -equal to: -prob = (N / acceleration - N_low_freqs) / (N - N_low_freqs). -This ensures that the expected number of columns selected is equal to -(N / acceleration)

-

It is possible to use multiple center_fractions and accelerations, -in which case one possible (center_fraction, acceleration) is chosen -uniformly at random each time the transform is called.

-

Example

-

If accelerations = [4, 8] and center_fractions = [0.08, 0.04], -then there is a 50% probability that 4-fold acceleration with 8% -center fraction is selected and a 50% probability that 8-fold -acceleration with 4% center fraction is selected.

-
-
Modified and adopted from:

facebookresearch/fastMRI

-
-
-
-
-__call__(kspace)[source]#
-
-
Parameters:
-

kspace (Union[ndarray, Tensor]) – The input k-space data. The shape is (…,num_coils,H,W,2) -for complex 2D inputs and (…,num_coils,H,W,D) for real 3D -data. The last spatial dim is selected for sampling. For the -fastMRI dataset, k-space has the form -(…,num_slices,num_coils,H,W) and sampling is done along W. -For a general 3D data with the shape (…,num_coils,H,W,D), -sampling is done along D.

-
-
Return type:
-

Sequence[Tensor]

-
-
Returns:
-

-
A tuple containing
    -
  1. the under-sampled kspace

  2. -
  3. absolute value of the inverse fourier of the under-sampled kspace

  4. -
-
-
-

-
-
-
- -
- -
-
-class monai.apps.reconstruction.transforms.array.EquispacedKspaceMask(center_fractions, accelerations, spatial_dims=2, is_complex=True)[source]#
-

This k-space mask transform under-samples the k-space according to an -equi-distant sampling pattern. Precisely, it selects an equi-distant -subset of columns from the input k-space data. If the k-space data has N -columns, the mask picks out:

-

1. N_low_freqs = (N * center_fraction) columns in the center corresponding -to low-frequencies

-

2. The other columns are selected with equal spacing at a proportion that -reaches the desired acceleration rate taking into consideration the number -of low frequencies. This ensures that the expected number of columns -selected is equal to (N / acceleration)

-

It is possible to use multiple center_fractions and accelerations, in -which case one possible (center_fraction, acceleration) is chosen -uniformly at random each time the EquispacedMaskFunc object is called.

-

Example

-

If accelerations = [4, 8] and center_fractions = [0.08, 0.04], -then there is a 50% probability that 4-fold acceleration with 8% -center fraction is selected and a 50% probability that 8-fold -acceleration with 4% center fraction is selected.

-
-
Modified and adopted from:

facebookresearch/fastMRI

-
-
-
-
-__call__(kspace)[source]#
-
-
Parameters:
-

kspace (Union[ndarray, Tensor]) – The input k-space data. The shape is (…,num_coils,H,W,2) -for complex 2D inputs and (…,num_coils,H,W,D) for real 3D -data. The last spatial dim is selected for sampling. For the -fastMRI multi-coil dataset, k-space has the form -(…,num_slices,num_coils,H,W) and sampling is done along W. -For a general 3D data with the shape (…,num_coils,H,W,D), -sampling is done along D.

-
-
Return type:
-

Sequence[Tensor]

-
-
Returns:
-

-
A tuple containing
    -
  1. the under-sampled kspace

  2. -
  3. absolute value of the inverse fourier of the under-sampled kspace

  4. -
-
-
-

-
-
-
- -
- -
-
-
-

Lazy#

-
-

ApplyPending#

-
-
-class monai.transforms.ApplyPending[source]#
-

ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure -resampling happens before the next transform. It doesn’t do anything itself, but its presence -causes the pipeline to be executed as ApplyPending doesn’t implement `LazyTrait.

-

See Compose for a detailed explanation of the lazy resampling feature.

-
-
-__call__(data)[source]#
-

Call self as a function.

-
- -
- -
-
-
-

Utility#

-
-

Identity#

-
-
-class monai.transforms.Identity[source]#
-

Do nothing to the data. -As the output value is same as input, it can be used as a testing tool to verify the transform chain, -Compose or transform adaptor, etc.

-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

AsChannelFirst#

-
-
-class monai.transforms.AsChannelFirst(channel_dim=-1)[source]#
-

Change the channel dimension of the image to the first dimension.

-

Most of the image transformations in monai.transforms -assume the input image is in the channel-first format, which has the shape -(num_channels, spatial_dim_1[, spatial_dim_2, …]).

-

This transform could be used to convert, for example, a channel-last image array in shape -(spatial_dim_1[, spatial_dim_2, …], num_channels) into the channel-first format, -so that the multidimensional image array can be correctly interpreted by the other transforms.

-
-
Parameters:
-

channel_dim (int) – which dimension of input image is the channel, default is the last dimension.

-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

AsChannelLast#

-
-
-class monai.transforms.AsChannelLast(channel_dim=0)[source]#
-

Change the channel dimension of the image to the last dimension.

-

Some of other 3rd party transforms assume the input image is in the channel-last format with shape -(spatial_dim_1[, spatial_dim_2, …], num_channels).

-

This transform could be used to convert, for example, a channel-first image array in shape -(num_channels, spatial_dim_1[, spatial_dim_2, …]) into the channel-last format, -so that MONAI transforms can construct a chain with other 3rd party transforms together.

-
-
Parameters:
-

channel_dim (int) – which dimension of input image is the channel, default is the first dimension.

-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

AddChannel#

-
-
-class monai.transforms.AddChannel[source]#
-

Adds a 1-length channel dimension to the input image.

-

Most of the image transformations in monai.transforms -assumes the input image is in the channel-first format, which has the shape -(num_channels, spatial_dim_1[, spatial_dim_2, …]).

-

This transform could be used, for example, to convert a (spatial_dim_1[, spatial_dim_2, …]) -spatial image into the channel-first format so that the -multidimensional image array can be correctly interpreted by the other -transforms.

-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

EnsureChannelFirst#

-
-
-class monai.transforms.EnsureChannelFirst(strict_check=True, channel_dim=None)[source]#
-

Adjust or add the channel dimension of input data to ensure channel_first shape.

-

This extracts the original_channel_dim info from provided meta_data dictionary or MetaTensor input. This value -should state which dimension is the channel dimension so that it can be moved forward, or contain “no_channel” to -state no dimension is the channel and so a 1-size first dimension is to be added.

-
-
Parameters:
-
    -
  • strict_check (bool) – whether to raise an error when the meta information is insufficient.

  • -
  • channel_dim (Union[str, int, None]) – This argument can be used to specify the original channel dimension (integer) of the input array. -It overrides the original_channel_dim from provided MetaTensor input. -If the input array doesn’t have a channel dim, this value should be 'no_channel'. -If this is set to None, this class relies on img or meta_dict to provide the channel dimension.

  • -
-
-
-
-
-__call__(img, meta_dict=None)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Tensor

-
-
-
- -
- -
-
-

RepeatChannel#

-
-
-class monai.transforms.RepeatChannel(repeats)[source]#
-

Repeat channel data to construct expected input shape for models. -The repeats count includes the origin data, for example: -RepeatChannel(repeats=2)([[1, 2], [3, 4]]) generates: [[1, 2], [1, 2], [3, 4], [3, 4]]

-
-
Parameters:
-

repeats (int) – the number of repetitions for each element.

-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img, assuming img is a “channel-first” array.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

SplitDim#

-
-
-class monai.transforms.SplitDim(dim=-1, keepdim=True, update_meta=True)[source]#
-

Given an image of size X along a certain dimension, return a list of length X containing -images. Useful for converting 3D images into a stack of 2D images, splitting multichannel inputs into -single channels, for example.

-

Note: torch.split/np.split is used, so the outputs are views of the input (shallow copy).

-
-
Parameters:
-
    -
  • dim (int) – dimension on which to split

  • -
  • keepdim (bool) – if True, output will have singleton in the split dimension. If False, this -dimension will be squeezed.

  • -
  • update_meta – whether to update the MetaObj in each split result.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

list[Tensor]

-
-
-
- -
- -
-
-

SplitChannel#

-
-
-class monai.transforms.SplitChannel(channel_dim=0)[source]#
-

Split Numpy array or PyTorch Tensor data according to the channel dim. -It can help applying different following transforms to different channels.

-

Note: torch.split/np.split is used, so the outputs are views of the input (shallow copy).

-
-
Parameters:
-

channel_dim (int) – which dimension of input image is the channel, default to 0.

-
-
-
- -
-
-

CastToType#

-
-
-class monai.transforms.CastToType(dtype=<class 'numpy.float32'>)[source]#
-

Cast the Numpy data to specified numpy data type, or cast the PyTorch Tensor to -specified PyTorch data type.

-
-
-__call__(img, dtype=None)[source]#
-

Apply the transform to img, assuming img is a numpy array or PyTorch Tensor.

-
-
Parameters:
-

dtype (Union[dtype, type, str, None, dtype]) – convert image to this data type, default is self.dtype.

-
-
Raises:
-

TypeError – When img type is not in Union[numpy.ndarray, torch.Tensor].

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(dtype=<class 'numpy.float32'>)[source]#
-
-
Parameters:
-

dtype – convert image to this data type, default is np.float32.

-
-
-
- -
- -
-
-

ToTensor#

-
-
-class monai.transforms.ToTensor(dtype=None, device=None, wrap_sequence=True, track_meta=None)[source]#
-

Converts the input image to a tensor without applying any other transformations. -Input data can be PyTorch Tensor, numpy array, list, dictionary, int, float, bool, str, etc. -Will convert Tensor, Numpy array, float, int, bool to Tensor, strings and objects keep the original. -For dictionary, list or tuple, convert every item to a Tensor if applicable and wrap_sequence=False.

-
-
Parameters:
-
    -
  • dtype (Optional[dtype, None]) – target data type to when converting to Tensor.

  • -
  • device (Optional[device, None]) – target device to put the converted Tensor data.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • -
  • track_meta (Optional[bool, None]) – whether to convert to MetaTensor or regular tensor, default to None, -use the return value of get_track_meta.

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img and make it contiguous.

-
- -
- -
-
-

ToNumpy#

-
-
-class monai.transforms.ToNumpy(dtype=None, wrap_sequence=True)[source]#
-

Converts the input data to numpy array, can support list or tuple of numbers and PyTorch Tensor.

-
-
Parameters:
-
    -
  • dtype (Union[dtype, type, str, None]) – target data type when converting to numpy array.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • -
-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img and make it contiguous.

-
- -
- -
-
-

ToCupy#

-
-
-class monai.transforms.ToCupy(dtype=None, wrap_sequence=True)[source]#
-

Converts the input data to CuPy array, can support list or tuple of numbers, NumPy and PyTorch Tensor.

-
-
Parameters:
-
    -
  • dtype (Optional[dtype, None]) – data type specifier. It is inferred from the input by default. -if not None, must be an argument of numpy.dtype, for more details: -https://docs.cupy.dev/en/stable/reference/generated/cupy.array.html.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • -
-
-
-
-
-__call__(data)[source]#
-

Create a CuPy array from data and make it contiguous

-
- -
- -
-
-

Transpose#

-
-
-class monai.transforms.Transpose(indices)[source]#
-

Transposes the input image based on the given indices dimension ordering.

-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

SqueezeDim#

-
-
-class monai.transforms.SqueezeDim(dim=0, update_meta=True)[source]#
-

Squeeze a unitary dimension.

-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – numpy arrays with required dimension dim removed

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(dim=0, update_meta=True)[source]#
-
-
Parameters:
-
    -
  • dim (UnionType[int, None]) – dimension to be squeezed. Default = 0 -“None” works when the input is numpy array.

  • -
  • update_meta – whether to update the meta info if the input is a metatensor. Default is True.

  • -
-
-
Raises:
-

TypeError – When dim is not an Optional[int].

-
-
-
- -
- -
-
-

DataStats#

-
-
-class monai.transforms.DataStats(prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats')[source]#
-

Utility transform to show the statistics of data for debug or analysis. -It can be inserted into any place of a transform chain and check results of previous transforms. -It support both numpy.ndarray and torch.tensor as input data, -so it can be used in pre-processing and post-processing.

-

It gets logger from logging.getLogger(name), we can setup a logger outside first with the same name. -If the log level of logging.RootLogger is higher than INFO, will add a separate StreamHandler -log handler with INFO level and record to stdout.

-
-
-__call__(img, prefix=None, data_type=None, data_shape=None, value_range=None, data_value=None, additional_info=None)[source]#
-

Apply the transform to img, optionally take arguments similar to the class constructor.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats')[source]#
-
-
Parameters:
-
    -
  • prefix (str) – will be printed in format: “{prefix} statistics”.

  • -
  • data_type (bool) – whether to show the type of input data.

  • -
  • data_shape (bool) – whether to show the shape of input data.

  • -
  • value_range (bool) – whether to show the value range of input data.

  • -
  • data_value (bool) – whether to show the raw value of input data. -a typical example is to print some properties of Nifti image: affine, pixdim, etc.

  • -
  • additional_info (Optional[Callable, None]) – user can define callable function to extract additional info from input data.

  • -
  • name (str) – identifier of logging.logger to use, defaulting to “DataStats”.

  • -
-
-
Raises:
-

TypeError – When additional_info is not an Optional[Callable].

-
-
-
- -
- -
-
-

SimulateDelay#

-
-
-class monai.transforms.SimulateDelay(delay_time=0.0)[source]#
-

This is a pass through transform to be used for testing purposes. It allows -adding fake behaviors that are useful for testing purposes to simulate -how large datasets behave without needing to test on large data sets.

-

For example, simulating slow NFS data transfers, or slow network transfers -in testing by adding explicit timing delays. Testing of small test data -can lead to incomplete understanding of real world issues, and may lead -to sub-optimal design choices.

-
-
-__call__(img, delay_time=None)[source]#
-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – data remain unchanged throughout this transform.

  • -
  • delay_time (Optional[float, None]) – The minimum amount of time, in fractions of seconds, -to accomplish this delay task.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-__init__(delay_time=0.0)[source]#
-
-
Parameters:
-

delay_time (float) – The minimum amount of time, in fractions of seconds, -to accomplish this delay task.

-
-
-
- -
- -
-
-

Lambda#

-
-
-class monai.transforms.Lambda(func=None, inv_func=<function no_collation>, track_meta=True)[source]#
-

Apply a user-defined lambda as a transform.

-

For example:

-
image = np.ones((10, 2, 2))
-lambd = Lambda(func=lambda x: x[:4, :, :])
-print(lambd(image).shape)
-(4, 2, 2)
-
-
-
-
Parameters:
-
    -
  • func (Optional[Callable, None]) – Lambda/function to be applied.

  • -
  • inv_func (Callable) – Lambda/function of inverse operation, default to lambda x: x.

  • -
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) -as opposed to MONAI’s enhanced objects. By default, this is True.

  • -
-
-
Raises:
-

TypeError – When func is not an Optional[Callable].

-
-
-
-
-__call__(img, func=None)[source]#
-

Apply self.func to img.

-
-
Parameters:
-

func (Optional[Callable, None]) – Lambda/function to be applied. Defaults to self.func.

-
-
Raises:
-

TypeError – When func is not an Optional[Callable].

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

RandLambda#

-
-
-class monai.transforms.RandLambda(func=None, prob=1.0, inv_func=<function no_collation>, track_meta=True)[source]#
-

Randomizable version monai.transforms.Lambda, the input func may contain random logic, -or randomly execute the function based on prob.

-
-
Parameters:
-
    -
  • func (Optional[Callable, None]) – Lambda/function to be applied.

  • -
  • prob (float) – probability of executing the random function, default to 1.0, with 100% probability to execute.

  • -
  • inv_func (Callable) – Lambda/function of inverse operation, default to lambda x: x.

  • -
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) -as opposed to MONAI’s enhanced objects. By default, this is True.

  • -
-
-
-

For more details, please check monai.transforms.Lambda.

-
-
-__call__(img, func=None)[source]#
-

Apply self.func to img.

-
-
Parameters:
-

func (Optional[Callable, None]) – Lambda/function to be applied. Defaults to self.func.

-
-
Raises:
-

TypeError – When func is not an Optional[Callable].

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

RemoveRepeatedChannel#

-
-
-class monai.transforms.RemoveRepeatedChannel(repeats)[source]#
-

RemoveRepeatedChannel data to undo RepeatChannel -The repeats count specifies the deletion of the origin data, for example: -RemoveRepeatedChannel(repeats=2)([[1, 2], [1, 2], [3, 4], [3, 4]]) generates: [[1, 2], [3, 4]]

-
-
Parameters:
-

repeats (int) – the number of repetitions to be deleted for each element.

-
-
-
-
-__call__(img)[source]#
-

Apply the transform to img, assuming img is a “channel-first” array.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

LabelToMask#

-
-
-class monai.transforms.LabelToMask(select_labels, merge_channels=False)[source]#
-

Convert labels to mask for other tasks. A typical usage is to convert segmentation labels -to mask data to pre-process images and then feed the images into classification network. -It can support single channel labels or One-Hot labels with specified select_labels. -For example, users can select label value = [2, 3] to construct mask data, or select the -second and the third channels of labels to construct mask data. -The output mask data can be a multiple channels binary data or a single channel binary -data that merges all the channels.

-
-
Parameters:
-
    -
  • select_labels (UnionType[Sequence[int], int]) – labels to generate mask from. for 1 channel label, the select_labels -is the expected label values, like: [1, 2, 3]. for One-Hot format label, the -select_labels is the expected channel indices.

  • -
  • merge_channels (bool) – whether to use np.any() to merge the result on channel dim. if yes, -will return a single channel mask with binary data.

  • -
-
-
-
-
-__call__(img, select_labels=None, merge_channels=False)[source]#
-
-
Parameters:
-
    -
  • select_labels (Union[Sequence[int], int, None]) – labels to generate mask from. for 1 channel label, the select_labels -is the expected label values, like: [1, 2, 3]. for One-Hot format label, the -select_labels is the expected channel indices.

  • -
  • merge_channels (bool) – whether to use np.any() to merge the result on channel dim. if yes, -will return a single channel mask with binary data.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

FgBgToIndices#

-
-
-class monai.transforms.FgBgToIndices(image_threshold=0.0, output_shape=None)[source]#
-

Compute foreground and background of the input label data, return the indices. -If no output_shape specified, output data will be 1 dim indices after flattening. -This transform can help pre-compute foreground and background regions for other transforms. -A typical usage is to randomly select foreground and background to crop. -The main logic is based on monai.transforms.utils.map_binary_to_indices.

-
-
Parameters:
-
    -
  • image_threshold (float) – if enabled image at runtime, use image > image_threshold to -determine the valid image content area and select background only in this area.

  • -
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • -
-
-
-
-
-__call__(label, image=None, output_shape=None)[source]#
-
-
Parameters:
-
    -
  • label (Union[ndarray, Tensor]) – input data to compute foreground and background indices.

  • -
  • image (Union[ndarray, Tensor, None]) – if image is not None, use label = 0 & image > image_threshold -to define background. so the output items will not map to all the voxels in the label.

  • -
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if None, use self.output_shape instead.

  • -
-
-
Return type:
-

tuple[Union[ndarray, Tensor], Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-

ClassesToIndices#

-
-
-class monai.transforms.ClassesToIndices(num_classes=None, image_threshold=0.0, output_shape=None, max_samples_per_class=None)[source]#
-
-
-__call__(label, image=None, output_shape=None)[source]#
-
-
Parameters:
-
    -
  • label (Union[ndarray, Tensor]) – input data to compute the indices of every class.

  • -
  • image (Union[ndarray, Tensor, None]) – if image is not None, use image > image_threshold to define valid region, and only select -the indices within the valid region.

  • -
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if None, use self.output_shape instead.

  • -
-
-
Return type:
-

list[Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(num_classes=None, image_threshold=0.0, output_shape=None, max_samples_per_class=None)[source]#
-

Compute indices of every class of the input label data, return a list of indices. -If no output_shape specified, output data will be 1 dim indices after flattening. -This transform can help pre-compute indices of the class regions for other transforms. -A typical usage is to randomly select indices of classes to crop. -The main logic is based on monai.transforms.utils.map_classes_to_indices.

-
-
Parameters:
-
    -
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • -
  • image_threshold (float) – if enabled image at runtime, use image > image_threshold to -determine the valid image content area and select only the indices of classes in this area.

  • -
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • -
  • max_samples_per_class (Optional[int, None]) – maximum length of indices to sample in each class to reduce memory consumption. -Default is None, no subsampling.

  • -
-
-
-
- -
- -
-
-

ConvertToMultiChannelBasedOnBratsClasses#

-
-
-class monai.transforms.ConvertToMultiChannelBasedOnBratsClasses[source]#
-

Convert labels to multi channels based on brats18 classes: -label 1 is the necrotic and non-enhancing tumor core -label 2 is the peritumoral edema -label 4 is the GD-enhancing tumor -The possible classes are TC (Tumor core), WT (Whole tumor) -and ET (Enhancing tumor).

-
-
-__call__(img)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

AddExtremePointsChannel#

-
-
-class monai.transforms.AddExtremePointsChannel(background=0, pert=0.0)[source]#
-

Add extreme points of label to the image as a new channel. This transform generates extreme -point from label and applies a gaussian filter. The pixel values in points image are rescaled -to range [rescale_min, rescale_max] and added as a new channel to input image. The algorithm is -described in Roth et al., Going to Extremes: Weakly Supervised Medical Image Segmentation -https://arxiv.org/abs/2009.11988.

-

This transform only supports single channel labels (1, spatial_dim1, [spatial_dim2, …]). The -background index is ignored when calculating extreme points.

-
-
Parameters:
-
    -
  • background (int) – Class index of background label, defaults to 0.

  • -
  • pert (float) – Random perturbation amount to add to the points, defaults to 0.0.

  • -
-
-
Raises:
-
    -
  • ValueError – When no label image provided.

  • -
  • ValueError – When label image is not single channel.

  • -
-
-
-
-
-__call__(img, label=None, sigma=3.0, rescale_min=-1.0, rescale_max=1.0)[source]#
-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – the image that we want to add new channel to.

  • -
  • label (Union[ndarray, Tensor, None]) – label image to get extreme points from. Shape must be -(1, spatial_dim1, [, spatial_dim2, …]). Doesn’t support one-hot labels.

  • -
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

  • -
  • rescale_min (float) – minimum value of output data.

  • -
  • rescale_max (float) – maximum value of output data.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-randomize(label)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

TorchVision#

-
-
-class monai.transforms.TorchVision(name, *args, **kwargs)[source]#
-

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.

-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – PyTorch Tensor data for the TorchVision transform.

-
-
-
- -
-
-__init__(name, *args, **kwargs)[source]#
-
-
Parameters:
-
    -
  • name (str) – The transform name in TorchVision package.

  • -
  • args – parameters for the TorchVision transform.

  • -
  • kwargs – parameters for the TorchVision transform.

  • -
-
-
-
- -
- -
-
-

MapLabelValue#

-
-
-class monai.transforms.MapLabelValue(orig_labels, target_labels, dtype=<class 'numpy.float32'>)[source]#
-

Utility to map label values to another set of values. -For example, map [3, 2, 1] to [0, 1, 2], [1, 2, 3] -> [0.5, 1.5, 2.5], [“label3”, “label2”, “label1”] -> [0, 1, 2], -[3.5, 2.5, 1.5] -> [“label0”, “label1”, “label2”], etc. -The label data must be numpy array or array-like data and the output data will be numpy array.

-
-
-__call__(img)[source]#
-

Call self as a function.

-
- -
-
-__init__(orig_labels, target_labels, dtype=<class 'numpy.float32'>)[source]#
-
-
Parameters:
-
    -
  • orig_labels (Sequence) – original labels that map to others.

  • -
  • target_labels (Sequence) – expected label values, 1: 1 map to the orig_labels.

  • -
  • dtype (Union[dtype, type, str, None]) – convert the output data to dtype, default to float32.

  • -
-
-
-
- -
- -
-
-

EnsureType#

-
-
-class monai.transforms.EnsureType(data_type='tensor', dtype=None, device=None, wrap_sequence=True, track_meta=None)[source]#
-

Ensure the input data to be a PyTorch Tensor or numpy array, support: numpy array, PyTorch Tensor, -float, int, bool, string and object keep the original. -If passing a dictionary, list or tuple, still return dictionary, list or tuple will recursively convert -every item to the expected data type if wrap_sequence=False.

-
-
Parameters:
-
    -
  • data_type (str) – target data type to convert, should be “tensor” or “numpy”.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – target data content type to convert, for example: np.float32, torch.float, etc.

  • -
  • device (Optional[device, None]) – for Tensor data type, specify the target device.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • -
  • track_meta (Optional[bool, None]) – if True convert to MetaTensor, otherwise to Pytorch Tensor, -if None behave according to return value of py:func:monai.data.meta_obj.get_track_meta.

  • -
-
-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Union[ndarray, Tensor]) – input data can be PyTorch Tensor, numpy array, list, dictionary, int, float, bool, str, etc. -will ensure Tensor, Numpy array, float, int, bool as Tensors or numpy arrays, strings and -objects keep the original. for dictionary, list or tuple, ensure every item as expected type -if applicable and wrap_sequence=False.

-
-
-
- -
- -
-
-

IntensityStats#

-
-
-class monai.transforms.IntensityStats(ops, key_prefix, channel_wise=False)[source]#
-

Compute statistics for the intensity values of input image and store into the metadata dictionary. -For example: if ops=[lambda x: np.mean(x), “max”] and key_prefix=”orig”, may generate below stats: -{“orig_custom_0”: 1.5, “orig_max”: 3.0}.

-
-
Parameters:
-
    -
  • ops (Sequence[Union[str, Callable]]) – expected operations to compute statistics for the intensity. -if a string, will map to the predefined operations, supported: [“mean”, “median”, “max”, “min”, “std”] -mapping to np.nanmean, np.nanmedian, np.nanmax, np.nanmin, np.nanstd. -if a callable function, will execute the function on input image.

  • -
  • key_prefix (str) – the prefix to combine with ops name to generate the key to store the results in the -metadata dictionary. if some ops are callable functions, will use “{key_prefix}_custom_{index}” -as the key, where index counts from 0.

  • -
  • channel_wise (bool) – whether to compute statistics for every channel of input image separately. -if True, return a list of values for every operation, default to False.

  • -
-
-
-
-
-__call__(img, meta_data=None, mask=None)[source]#
-

Compute statistics for the intensity of input image.

-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – input image to compute intensity stats.

  • -
  • meta_data (Optional[dict, None]) – metadata dictionary to store the statistics data, if None, will create an empty dictionary.

  • -
  • mask (Optional[ndarray, None]) – if not None, mask the image to extract only the interested area to compute statistics. -mask must have the same shape as input img.

  • -
-
-
Return type:
-

tuple[Union[ndarray, Tensor], dict]

-
-
-
- -
- -
-
-

ToDevice#

-
-
-class monai.transforms.ToDevice(device, **kwargs)[source]#
-

Move PyTorch Tensor to the specified device. -It can help cache data into GPU and execute following logic on GPU directly.

-
-

Note

-

If moving data to GPU device in the multi-processing workers of DataLoader, may got below CUDA error: -“RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, -you must use the ‘spawn’ start method.” -So usually suggest to set num_workers=0 in the DataLoader or ThreadDataLoader.

-
-
-
-__call__(img)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
-
-__init__(device, **kwargs)[source]#
-
-
Parameters:
-
-
-
-
- -
- -
-
-

CuCIM#

-
-
-class monai.transforms.CuCIM(name, *args, **kwargs)[source]#
-

Wrap a non-randomized cuCIM transform, defined based on the transform name and args. -For randomized transforms use monai.transforms.RandCuCIM.

-
-
Parameters:
-
    -
  • name (str) – 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.

-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data – a CuPy array (cupy.ndarray) for the cuCIM transform

-
-
Returns:
-

cupy.ndarray

-
-
-
- -
- -
-
-

RandCuCIM#

-
-
-class monai.transforms.RandCuCIM(name, *args, **kwargs)[source]#
-

Wrap a randomized cuCIM transform, defined based on the transform name and args -For deterministic non-randomized transforms use monai.transforms.CuCIM.

-
-
Parameters:
-
    -
  • name (str) – 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.

  • -
  • If the random factor of the underlying cuCIM transform is not derived from self.R, -the results may not be deterministic. See Also: monai.transforms.Randomizable.

  • -
-
-
- -
-
-

AddCoordinateChannels#

-
-
-class monai.transforms.AddCoordinateChannels(spatial_dims)[source]#
-

Appends additional channels encoding coordinates of the input. Useful when e.g. training using patch-based sampling, -to allow feeding of the patch’s location into the network.

-

This can be seen as a input-only version of CoordConv:

-

Liu, R. et al. An Intriguing Failing of Convolutional Neural Networks and the CoordConv Solution, NeurIPS 2018.

-
-
Parameters:
-

spatial_dims (Sequence[int]) – the spatial dimensions that are to have their coordinates encoded in a channel and -appended to the input image. E.g., (0, 1, 2) represents H, W, D dims and append three channels -to the input image, encoding the coordinates of the input’s three spatial dimensions.

-
-
-
-

Deprecated since version 0.8.0: spatial_channels is deprecated, use spatial_dims instead.

-
-
-
-__call__(img)[source]#
-
-
Parameters:
-

img (Union[ndarray, Tensor]) – data to be transformed, assuming img is channel first.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-

ImageFilter#

-
-
-class monai.transforms.ImageFilter(filter, filter_size=None, **kwargs)[source]#
-

Applies a convolution filter to the input image.

-
-
Parameters:
-
    -
  • filter (Union[str, ndarray, Tensor, Module]) – A string specifying the filter, a custom filter as torch.Tenor or np.ndarray or a nn.Module. -Available options for string are: mean, laplace, elliptical, sobel, sharpen, median, gauss -See below for short explanations on every filter.

  • -
  • filter_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic filter. -Computational complexity scales to the power of 2 (2D filter) or 3 (3D filter), which -should be considered when choosing filter size.

  • -
  • kwargs – Additional arguments passed to filter function, required by sobel and gauss. -See below for details.

  • -
-
-
Raises:
-
    -
  • ValueError – When filter_size is not an uneven integer

  • -
  • ValueError – When filter is an array and ndim is not in [1,2,3]

  • -
  • ValueError – When filter is an array and any dimension has an even shape

  • -
  • NotImplementedError – When filter is a string and not in self.supported_filters

  • -
  • KeyError – When necessary kwargs are not passed to a filter that requires additional arguments.

  • -
-
-
-

Mean Filtering: filter='mean'

-

Mean filtering can smooth edges and remove aliasing artifacts in an segmentation image. -See also py:func:monai.networks.layers.simplelayers.MeanFilter -Example 2D filter (5 x 5):

-
[[1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1]]
-
-
-

If smoothing labels with this filter, ensure they are in one-hot format.

-

Outline Detection: filter='laplace'

-

Laplacian filtering for outline detection in images. Can be used to transform labels to contours. -See also py:func:monai.networks.layers.simplelayers.LaplaceFilter

-

Example 2D filter (5x5):

-
[[-1., -1., -1., -1., -1.],
- [-1., -1., -1., -1., -1.],
- [-1., -1., 24., -1., -1.],
- [-1., -1., -1., -1., -1.],
- [-1., -1., -1., -1., -1.]]
-
-
-

Dilation: filter='elliptical'

-

An elliptical filter can be used to dilate labels or label-contours. -Example 2D filter (5x5):

-
[[0., 0., 1., 0., 0.],
- [1., 1., 1., 1., 1.],
- [1., 1., 1., 1., 1.],
- [1., 1., 1., 1., 1.],
- [0., 0., 1., 0., 0.]]
-
-
-

Edge Detection: filter='sobel'

-

This filter allows for additional arguments passed as kwargs during initialization. -See also py:func:monai.transforms.post.SobelGradients

-

kwargs

-
    -
  • spatial_axes: the axes that define the direction of the gradient to be calculated. -It calculates the gradient along each of the provide axis. -By default it calculate the gradient for all spatial axes.

  • -
  • normalize_kernels: if normalize the Sobel kernel to provide proper gradients. Defaults to True.

  • -
  • normalize_gradients: if normalize the output gradient to 0 and 1. Defaults to False.

  • -
  • padding_mode: the padding mode of the image when convolving with Sobel kernels. Defaults to "reflect". -Acceptable values are 'zeros', 'reflect', 'replicate' or 'circular'. -See torch.nn.Conv1d() for more information.

  • -
  • dtype: kernel data type (torch.dtype). Defaults to torch.float32.

  • -
-

Sharpening: filter='sharpen'

-

Sharpen an image with a 2D or 3D filter. -Example 2D filter (5x5):

-
[[ 0.,  0., -1.,  0.,  0.],
- [-1., -1., -1., -1., -1.],
- [-1., -1., 17., -1., -1.],
- [-1., -1., -1., -1., -1.],
- [ 0.,  0., -1.,  0.,  0.]]
-
-
-

Gaussian Smooth: filter='gauss'

-

Blur/smooth an image with 2D or 3D gaussian filter. -This filter requires additional arguments passed as kwargs during initialization. -See also py:func:monai.networks.layers.simplelayers.GaussianFilter

-

kwargs

-
    -
  • sigma: std. could be a single value, or spatial_dims number of values.

  • -
  • truncated: spreads how many stds.

  • -
  • approx: discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”.

  • -
-

Median Filter: filter='median'

-

Blur an image with 2D or 3D median filter to remove noise. -Useful in image preprocessing to improve results of later processing. -See also py:func:monai.networks.layers.simplelayers.MedianFilter

-

Savitzky Golay Filter: filter = 'savitzky_golay'

-

Convolve a Tensor along a particular axis with a Savitzky-Golay kernel. -This filter requires additional arguments passed as kwargs during initialization. -See also py:func:monai.networks.layers.simplelayers.SavitzkyGolayFilter

-

kwargs

-
    -
  • order: Order of the polynomial to fit to each window, must be less than window_length.

  • -
  • axis: (optional): Axis along which to apply the filter kernel. Default 2 (first spatial dimension).

  • -
  • mode: (string, optional): padding mode passed to convolution class. 'zeros', 'reflect', 'replicate' or -'circular'. Default: 'zeros'. See torch.nn.Conv1d() for more information.

  • -
-
-
-__call__(img, meta_dict=None)[source]#
-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – torch tensor data to apply filter to with shape: [channels, height, width[, depth]]

  • -
  • meta_dict (Optional[dict, None]) – An optional dictionary with metadata

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

A MetaTensor with the same shape as img and identical metadata

-
-
-
- -
- -
-
-

RandImageFilter#

-
-
-class monai.transforms.RandImageFilter(filter, filter_size=None, prob=0.1, **kwargs)[source]#
-

Randomly apply a convolutional filter to the input data.

-
-
Parameters:
-
    -
  • filter (Union[str, ndarray, Tensor]) – A string specifying the filter or a custom filter as torch.Tenor or np.ndarray. -Available options are: mean, laplace, elliptical, gaussian` -See below for short explanations on every filter.

  • -
  • filter_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic filter. -Computational complexity scales to the power of 2 (2D filter) or 3 (3D filter), which -should be considered when choosing filter size.

  • -
  • prob (float) – Probability the transform is applied to the data

  • -
-
-
-
-
-__call__(img, meta_dict=None)[source]#
-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – torch tensor data to apply filter to with shape: [channels, height, width[, depth]]

  • -
  • meta_dict (Optional[Mapping, None]) – An optional dictionary with metadata

  • -
  • kwargs – optional arguments required by specific filters. E.g. sigma`if filter is `gauss. -see py:func:monai.transforms.utility.array.ImageFilter for more details

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

A MetaTensor with the same shape as img and identical metadata

-
-
-
- -
- -
-
-
-
-

Dictionary Transforms#

-
-

Crop and Pad (Dict)#

-
-

Padd#

-
-
-class monai.transforms.Padd(keys, padder, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Pad.

-
-
-__call__(data, lazy=None)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, padder, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • padder (Pad) – pad transform for the input image.

  • -
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, MetaTensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

SpatialPadd#

-example of SpatialPadd -
-
-class monai.transforms.SpatialPadd(keys, spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.SpatialPad. -Performs padding to the data, symmetric for all sides or all on one side for each dimension.

-
-
-__init__(keys, spatial_size, method=Method.SYMMETRIC, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of output data after padding, if a dimension of the input -data size is larger than the pad size, will not pad that dimension. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [30, 30, 30] and spatial_size=[32, 25, -1], -the spatial size of output data will be [32, 30, 30].

  • -
  • method (str) – {"symmetric", "end"} -Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • -
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
- -
- -
-
-

BorderPadd#

-example of BorderPadd -
-
-class monai.transforms.BorderPadd(keys, spatial_border, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-

Pad the input data by adding specified borders to every dimension. -Dictionary-based wrapper of monai.transforms.BorderPad.

-
-
-__init__(keys, spatial_border, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • spatial_border (UnionType[Sequence[int], int]) –

    specified size for every spatial border. it can be 3 shapes:

    -
      -
    • single int number, pad all the borders with the same size.

    • -
    • length equals the length of image shape, pad every spatial dimension separately. -for example, image shape(CHW) is [1, 4, 4], spatial_border is [2, 1], -pad every border of H dim with 2, pad every border of W dim with 1, result shape is [1, 8, 6].

    • -
    • length equals 2 x (length of image shape), pad every border of every dimension separately. -for example, image shape(CHW) is [1, 4, 4], spatial_border is [1, 2, 3, 4], pad top of H dim with 1, -pad bottom of H dim with 2, pad left of W dim with 3, pad right of W dim with 4. -the result shape is [1, 7, 11].

    • -
    -

  • -
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
- -
- -
-
-

DivisiblePadd#

-example of DivisiblePadd -
-
-class monai.transforms.DivisiblePadd(keys, k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-

Pad the input data, so that the spatial sizes are divisible by k. -Dictionary-based wrapper of monai.transforms.DivisiblePad.

-
-
-__init__(keys, k, mode=PytorchPadMode.CONSTANT, method=Method.SYMMETRIC, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • k (UnionType[Sequence[int], int]) – the target k for each spatial dimension. -if k is negative or 0, the original size is preserved. -if k is an int, the same k be applied to all the input spatial dimensions.

  • -
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • method (str) – {"symmetric", "end"} -Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-

See also monai.transforms.SpatialPad

-
- -
- -
-
-

Cropd#

-
-
-class monai.transforms.Cropd(keys, cropper, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of abstract class monai.transforms.Crop.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • cropper (Crop) – crop transform for the input image.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, MetaTensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandCropd#

-
-
-class monai.transforms.RandCropd(keys, cropper, allow_missing_keys=False, lazy=False)[source]#
-

Base class for random crop transform.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • cropper (Crop) – random crop transform for the input image.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-randomize(img_size)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandCropd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

SpatialCropd#

-example of SpatialCropd -
-
-class monai.transforms.SpatialCropd(keys, roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SpatialCrop. -General purpose cropper to produce sub-volume region of interest (ROI). -If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. -So the cropped result may be smaller than the expected ROI, and the cropped results of several images may -not have exactly the same shape. -It can support to crop ND spatial (channel-first) data.

-
-
The cropped region can be parameterised in various ways:
    -
  • a list of slices for each spatial dimension (allows for use of -ve indexing and None)

  • -
  • a spatial center and size

  • -
  • the start and end coordinates of the ROI

  • -
-
-
-
-
-__init__(keys, roi_center=None, roi_size=None, roi_start=None, roi_end=None, roi_slices=None, allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • roi_center (Optional[Sequence[int], None]) – voxel coordinates for center of the crop ROI.

  • -
  • roi_size (Optional[Sequence[int], None]) – size of the crop ROI, if a dimension of ROI size is larger than image size, -will not crop that dimension of the image.

  • -
  • roi_start (Optional[Sequence[int], None]) – voxel coordinates for start of the crop ROI.

  • -
  • roi_end (Optional[Sequence[int], None]) – voxel coordinates for end of the crop ROI, if a coordinate is out of image, -use the end coordinate of image.

  • -
  • roi_slices (Optional[Sequence[slice], None]) – list of slices for each of the spatial dimensions.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

CenterSpatialCropd#

-example of CenterSpatialCropd -
-
-class monai.transforms.CenterSpatialCropd(keys, roi_size, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.CenterSpatialCrop. -If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension. -So the cropped result may be smaller than the expected ROI, and the cropped results of several images may -not have exactly the same shape.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • roi_size (UnionType[Sequence[int], int]) – the size of the crop region e.g. [224,224,128] -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-

RandSpatialCropd#

-example of RandSpatialCropd -
-
-class monai.transforms.RandSpatialCropd(keys, roi_size, max_roi_size=None, random_center=True, random_size=True, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandSpatialCrop. -Crop image with random size or specific size ROI. It can crop at a random position as -center or at the image center. And allows to set the minimum and maximum size to limit the randomly -generated ROI. Suppose all the expected fields specified by keys have same shape.

-

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped -results of several images may not have exactly the same shape.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. -if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size -can specify the max crop region size. if None, defaults to the input image size. -if its components have non-positive values, the corresponding size of input image will be used.

  • -
  • random_center (bool) – crop at random position as center or the image center.

  • -
  • random_size (bool) – crop with random size or specific size ROI. -if True, the actual size is sampled from: -randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-

RandSpatialCropSamplesd#

-example of RandSpatialCropSamplesd -
-
-class monai.transforms.RandSpatialCropSamplesd(keys, roi_size, num_samples, max_roi_size=None, random_center=True, random_size=True, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandSpatialCropSamples. -Crop image with random size or specific size ROI to generate a list of N samples. -It can crop at a random position as center or at the image center. And allows to set -the minimum size to limit the randomly generated ROI. Suppose all the expected fields -specified by keys have same shape, and add patch_index to the corresponding metadata. -It will return a list of dictionaries for all the cropped images.

-

Note: even random_size=False, if a dimension of the expected ROI size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped -results of several images may not have exactly the same shape.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • roi_size (UnionType[Sequence[int], int]) – if random_size is True, it specifies the minimum crop region. -if random_size is False, it specifies the expected ROI size to crop. e.g. [224, 224, 128] -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -If its components have non-positive values, the corresponding size of input image will be used. -for example: if the spatial size of input data is [40, 40, 40] and roi_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • num_samples (int) – number of samples (crop regions) to take in the returned list.

  • -
  • max_roi_size (Union[Sequence[int], int, None]) – if random_size is True and roi_size specifies the min crop region size, max_roi_size -can specify the max crop region size. if None, defaults to the input image size. -if its components have non-positive values, the corresponding size of input image will be used.

  • -
  • random_center (bool) – crop at random position as center or the image center.

  • -
  • random_size (bool) – crop with random size or specific size ROI. -The actual size is sampled from randint(roi_size, img_size).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
Raises:
-

ValueError – When num_samples is nonpositive.

-
-
-
-
-__call__(data, lazy=None)[source]#
-

Call self as a function.

-
-
Return type:
-

list[dict[Hashable, Tensor]]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

CropForegroundd#

-example of CropForegroundd -
-
-class monai.transforms.CropForegroundd(keys, source_key, select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, k_divisible=1, mode=PytorchPadMode.CONSTANT, start_coord_key='foreground_start_coord', end_coord_key='foreground_end_coord', allow_missing_keys=False, lazy=False, **pad_kwargs)[source]#
-

Dictionary-based version monai.transforms.CropForeground. -Crop only the foreground object of the expected images. -The typical usage is to help training and evaluation if the valid part is small in the whole medical image. -The valid part can be determined by any field in the data with source_key, for example: -- Select values > 0 in image field as the foreground and crop on all fields specified by keys. -- Select label = 3 in label field as the foreground to crop on all fields specified by keys. -- Select label > 0 in the third channel of a One-Hot label field as the foreground to crop all keys fields. -Users can define arbitrary function to select expected foreground from the whole source image or specified -channels. And it can also add margin to every dim of the bounding box of foreground object.

-
-
-__call__(data, lazy=None)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, source_key, select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True, k_divisible=1, mode=PytorchPadMode.CONSTANT, start_coord_key='foreground_start_coord', end_coord_key='foreground_end_coord', allow_missing_keys=False, lazy=False, **pad_kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • source_key (str) – data source to generate the bounding box of foreground, can be image or label, etc.

  • -
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • -
  • channel_indices (Union[Iterable[int], int, None]) – if defined, select foreground only on the specified channels -of image. if None, select foreground on the whole image.

  • -
  • margin (UnionType[Sequence[int], int]) – add margin value to spatial dims of the bounding box, if only 1 value provided, use it for all dims.

  • -
  • allow_smaller (bool) – when computing box size with margin, whether allow the image size to be smaller -than box size, default to True. if the margined size is larger than image size, will pad with -specified mode.

  • -
  • k_divisible (UnionType[Sequence[int], int]) – make each spatial dimension to be divisible by k, default to 1. -if k_divisible is an int, the same k be applied to all the input spatial dimensions.

  • -
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -it also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • start_coord_key (str) – key to record the start coordinate of spatial bounding box for foreground.

  • -
  • end_coord_key (str) – key to record the end coordinate of spatial bounding box for foreground.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
- -
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandWeightedCropd#

-example of RandWeightedCropd -
-
-class monai.transforms.RandWeightedCropd(keys, w_key, spatial_size, num_samples=1, allow_missing_keys=False, lazy=False)[source]#
-

Samples a list of num_samples image patches according to the provided weight_map.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • w_key (str) – key for the weight map. The corresponding value will be used as the sampling weights, -it should be a single-channel array in size, for example, (1, spatial_dim_0, spatial_dim_1, …)

  • -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the image patch e.g. [224, 224, 128]. -If its components have non-positive values, the corresponding size of img will be used.

  • -
  • num_samples (int) – number of samples (image patches) to take in the returned list.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
- -
-
-__call__(data, lazy=None)[source]#
-

Call self as a function.

-
-
Return type:
-

list[dict[Hashable, Tensor]]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(weight_map)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandWeightedCropd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandCropByPosNegLabeld#

-example of RandCropByPosNegLabeld -
-
-class monai.transforms.RandCropByPosNegLabeld(keys, label_key, spatial_size, pos=1.0, neg=1.0, num_samples=1, image_key=None, image_threshold=0.0, fg_indices_key=None, bg_indices_key=None, allow_smaller=False, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandCropByPosNegLabel. -Crop random fixed sized regions with the center being a foreground or background voxel -based on the Pos Neg Ratio. -Suppose all the expected fields specified by keys have same shape, -and add patch_index to the corresponding metadata. -And will return a list of dictionaries for all the cropped images.

-

If a dimension of the expected spatial size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than the expected size, -and the cropped results of several images may not have exactly the same shape. -And if the crop ROI is partly out of the image, will automatically adjust the crop center -to ensure the valid crop ROI.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • label_key (str) – name of key for label image, this will be used for finding foreground/background.

  • -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -if its components have non-positive values, the corresponding size of data[label_key] will be used. -for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • pos (float) – used with neg together to calculate the ratio pos / (pos + neg) for the probability -to pick a foreground voxel as a center rather than a background voxel.

  • -
  • neg (float) – used with pos together to calculate the ratio pos / (pos + neg) for the probability -to pick a foreground voxel as a center rather than a background voxel.

  • -
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • -
  • image_key (Optional[str, None]) – if image_key is not None, use label == 0 & image > image_threshold to select -the negative sample(background) center. so the crop center will only exist on valid image area.

  • -
  • image_threshold (float) – if enabled image_key, use image > image_threshold to determine -the valid image content area.

  • -
  • fg_indices_key (Optional[str, None]) – if provided pre-computed foreground indices of label, will ignore above image_key and -image_threshold, and randomly select crop centers based on them, need to provide fg_indices_key -and bg_indices_key together, expect to be 1 dim array of spatial indices after flattening. -a typical usage is to call FgBgToIndicesd transform first and cache the results.

  • -
  • bg_indices_key (Optional[str, None]) – if provided pre-computed background indices of label, will ignore above image_key and -image_threshold, and randomly select crop centers based on them, need to provide fg_indices_key -and bg_indices_key together, expect to be 1 dim array of spatial indices after flattening. -a typical usage is to call FgBgToIndicesd transform first and cache the results.

  • -
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than -the requested ROI in any dimension. If True, any smaller dimensions will be set to -match the cropped size (i.e., no cropping in that dimension).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
Raises:
-
    -
  • ValueError – When pos or neg are negative.

  • -
  • ValueError – When pos=0 and neg=0. Incompatible values.

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-

Call self as a function.

-
-
Return type:
-

list[dict[Hashable, Tensor]]

-
-
-
- -
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(label=None, fg_indices=None, bg_indices=None, image=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandCropByPosNegLabeld

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandCropByLabelClassesd#

-example of RandCropByLabelClassesd -
-
-class monai.transforms.RandCropByLabelClassesd(keys, label_key, spatial_size, ratios=None, num_classes=None, num_samples=1, image_key=None, image_threshold=0.0, indices_key=None, allow_smaller=False, allow_missing_keys=False, warn=True, max_samples_per_class=None, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandCropByLabelClasses. -Crop random fixed sized regions with the center being a class based on the specified ratios of every class. -The label data can be One-Hot format array or Argmax data. And will return a list of arrays for all the -cropped images. For example, crop two (3 x 3) arrays from (5 x 5) array with ratios=[1, 2, 3, 1]:

-
cropper = RandCropByLabelClassesd(
-    keys=["image", "label"],
-    label_key="label",
-    spatial_size=[3, 3],
-    ratios=[1, 2, 3, 1],
-    num_classes=4,
-    num_samples=2,
-)
-data = {
-    "image": np.array([
-        [[0.0, 0.3, 0.4, 0.2, 0.0],
-        [0.0, 0.1, 0.2, 0.1, 0.4],
-        [0.0, 0.3, 0.5, 0.2, 0.0],
-        [0.1, 0.2, 0.1, 0.1, 0.0],
-        [0.0, 0.1, 0.2, 0.1, 0.0]]
-    ]),
-    "label": np.array([
-        [[0, 0, 0, 0, 0],
-        [0, 1, 2, 1, 0],
-        [0, 1, 3, 0, 0],
-        [0, 0, 0, 0, 0],
-        [0, 0, 0, 0, 0]]
-    ]),
-}
-result = cropper(data)
-
-The 2 randomly cropped samples of `label` can be:
-[[0, 1, 2],     [[0, 0, 0],
- [0, 1, 3],      [1, 2, 1],
- [0, 0, 0]]      [1, 3, 0]]
-
-
-

If a dimension of the expected spatial size is larger than the input image size, -will not crop that dimension. So the cropped result may be smaller than expected size, and the cropped -results of several images may not have exactly same shape. -And if the crop ROI is partly out of the image, will automatically adjust the crop center to ensure the -valid crop ROI.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • label_key (str) – name of key for label image, this will be used for finding indices of every class.

  • -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of the crop region e.g. [224, 224, 128]. -if a dimension of ROI size is larger than image size, will not crop that dimension of the image. -if its components have non-positive values, the corresponding size of label will be used. -for example: if the spatial size of input data is [40, 40, 40] and spatial_size=[32, 64, -1], -the spatial size of output data will be [32, 40, 40].

  • -
  • ratios (Optional[list[UnionType[float, int]], None]) – specified ratios of every class in the label to generate crop centers, including background class. -if None, every class will have the same ratio to generate crop centers.

  • -
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • -
  • num_samples (int) – number of samples (crop regions) to take in each list.

  • -
  • image_key (Optional[str, None]) – if image_key is not None, only return the indices of every class that are within the valid -region of the image (image > image_threshold).

  • -
  • image_threshold (float) – if enabled image_key, use image > image_threshold to -determine the valid image content area and select class indices only in this area.

  • -
  • indices_key (Optional[str, None]) – if provided pre-computed indices of every class, will ignore above image and -image_threshold, and randomly select crop centers based on them, expect to be 1 dim array -of spatial indices after flattening. a typical usage is to call ClassesToIndices transform first -and cache the results for better performance.

  • -
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than -the requested ROI in any dimension. If True, any smaller dimensions will remain -unchanged.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • warn (bool) – if True prints a warning if a class is not present in the label.

  • -
  • max_samples_per_class (Optional[int, None]) – maximum length of indices in each class to reduce memory consumption. -Default is None, no subsampling.

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-

Call self as a function.

-
-
Return type:
-

list[dict[Hashable, Tensor]]

-
-
-
- -
-
-property checks_data#
-

Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its -operation. A transform that checks data requires that all of the pending operations on its input -transforms are up to date before it is executed, but it can still execute lazily by adding pending -operations to the input tensors. -:returns: True if the transform checks data and False if it does not

-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-randomize(label, indices=None, image=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandCropByLabelClassesd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

ResizeWithPadOrCropd#

-example of ResizeWithPadOrCropd -
-
-class monai.transforms.ResizeWithPadOrCropd(keys, spatial_size, mode=PytorchPadMode.CONSTANT, allow_missing_keys=False, method=Method.SYMMETRIC, lazy=False, **pad_kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.ResizeWithPadOrCrop.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • spatial_size (UnionType[Sequence[int], int]) – the spatial size of output data after padding or crop. -If has non-positive values, the corresponding size of input image will be used (no padding).

  • -
  • mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "constant". -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • method (str) – {"symmetric", "end"} -Pad image symmetrically on every side or only pad at the end sides. Defaults to "symmetric".

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
- -
-
-

BoundingRectd#

-
-
-class monai.transforms.BoundingRectd(keys, bbox_key_postfix='bbox', select_fn=<function is_positive>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.BoundingRect.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • bbox_key_postfix (str) – the output bounding box coordinates will be -written to the value of {key}_{bbox_key_postfix}.

  • -
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

See also: monai.transforms.utils.generate_spatial_bounding_box.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-

RandScaleCropd#

-example of RandScaleCropd -
-
-class monai.transforms.RandScaleCropd(keys, roi_scale, max_roi_scale=None, random_center=True, random_size=True, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandScaleCrop. -Crop image with random size or specific size ROI. -It can crop at a random position as center or at the image center. -And allows to set the minimum and maximum scale of image size to limit the randomly generated ROI. -Suppose all the expected fields specified by keys have same shape.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • roi_scale (UnionType[Sequence[float], float]) – if random_size is True, it specifies the minimum crop size: roi_scale * image spatial size. -if random_size is False, it specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5]. -If its components have non-positive values, will use 1.0 instead, which means the input image size.

  • -
  • max_roi_scale (Union[Sequence[float], float, None]) – if random_size is True and roi_scale specifies the min crop region size, max_roi_scale -can specify the max crop region size: max_roi_scale * image spatial size. -if None, defaults to the input image size. if its components have non-positive values, -will use 1.0 instead, which means the input image size.

  • -
  • random_center (bool) – crop at random position as center or the image center.

  • -
  • random_size (bool) – crop with random size or specified size ROI by roi_scale * image spatial size. -if True, the actual size is sampled from: -randint(roi_scale * image spatial size, max_roi_scale * image spatial size + 1).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-

CenterScaleCropd#

-example of CenterScaleCropd -
-
-class monai.transforms.CenterScaleCropd(keys, roi_scale, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.CenterScaleCrop. -Note: as using the same scaled ROI to crop, all the input data specified by keys should have -the same spatial shape.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • roi_scale (UnionType[Sequence[float], float]) – specifies the expected scale of image size to crop. e.g. [0.3, 0.4, 0.5] or a number for all dims. -If its components have non-positive values, will use 1.0 instead, which means the input image size.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-
-

Intensity (Dict)#

-
-

RandGaussianNoised#

-example of RandGaussianNoised -
-
-class monai.transforms.RandGaussianNoised(keys, prob=0.1, mean=0.0, std=0.1, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandGaussianNoise. -Add Gaussian noise to image. This transform assumes all the expected fields have same shape, if want to add -different noise for every field, please use this transform separately.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • prob (float) – Probability to add Gaussian noise.

  • -
  • mean (float) – Mean or “centre” of the distribution.

  • -
  • std (float) – Standard deviation (spread) of distribution.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandGaussianNoised

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

ShiftIntensityd#

-example of ShiftIntensityd -
-
-class monai.transforms.ShiftIntensityd(keys, offset, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ShiftIntensity.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, offset, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • offset (float) – offset value to shift the intensity of image.

  • -
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. -E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • -
  • factor_key (Optional[str, None]) – if not None, use it as the key to extract a value from the corresponding -metadata dictionary of key at runtime, and multiply the offset to shift intensity. -Usually, IntensityStatsd transform can pre-compute statistics of intensity values -and store in the metadata. -it also can be a sequence of strings, map to keys.

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. -used to extract the factor value is factor_key is not None. -for example, for data with key image, the metadata by default is in image_meta_dict. -the metadata is a dictionary object which contains: filename, original_shape, etc. -it can be a sequence of string, map to the keys. -if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • -
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to fetch the metadata according -to the key data, default is meta_dict, the metadata is a dictionary object. -used to extract the factor value is factor_key is not None.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

RandShiftIntensityd#

-example of RandShiftIntensityd -
-
-class monai.transforms.RandShiftIntensityd(keys, offsets, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandShiftIntensity.

-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(keys, offsets, safe=False, factor_key=None, meta_keys=None, meta_key_postfix='meta_dict', prob=0.1, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • offsets (UnionType[tuple[float, float], float]) – offset range to randomly shift. -if single number, offset value is picked from (-offsets, offsets).

  • -
  • safe (bool) – if True, then do safe dtype convert when intensity overflow. default to False. -E.g., [256, -12] -> [array(0), array(244)]. If True, then [256, -12] -> [array(255), array(0)].

  • -
  • factor_key (Optional[str, None]) – if not None, use it as the key to extract a value from the corresponding -metadata dictionary of key at runtime, and multiply the random offset to shift intensity. -Usually, IntensityStatsd transform can pre-compute statistics of intensity values -and store in the metadata. -it also can be a sequence of strings, map to keys.

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. -used to extract the factor value is factor_key is not None. -for example, for data with key image, the metadata by default is in image_meta_dict. -the metadata is a dictionary object which contains: filename, original_shape, etc. -it can be a sequence of string, map to the keys. -if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • -
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to fetch the metadata according -to the key data, default is meta_dict, the metadata is a dictionary object. -used to extract the factor value is factor_key is not None.

  • -
  • prob (float) – probability of shift. -(Default 0.1, with 10% probability it returns an array shifted intensity.)

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandShiftIntensityd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

StdShiftIntensityd#

-example of StdShiftIntensityd -
-
-class monai.transforms.StdShiftIntensityd(keys, factor, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.StdShiftIntensity.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, factor, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • factor (float) – factor shift by v = v + factor * std(v).

  • -
  • nonzero (bool) – whether only count non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately. Please ensure -that the first dimension represents the channel of the image if True.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

RandStdShiftIntensityd#

-example of RandStdShiftIntensityd -
-
-class monai.transforms.RandStdShiftIntensityd(keys, factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandStdShiftIntensity.

-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(keys, factors, prob=0.1, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • factors (UnionType[tuple[float, float], float]) – if tuple, the randomly picked range is (min(factors), max(factors)). -If single number, the range is (-factors, factors).

  • -
  • prob (float) – probability of std shift.

  • -
  • nonzero (bool) – whether only count non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandStdShiftIntensityd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandBiasFieldd#

-example of RandBiasFieldd -
-
-class monai.transforms.RandBiasFieldd(keys, degree=3, coeff_range=(0.0, 0.1), dtype=<class 'numpy.float32'>, prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandBiasField.

-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(keys, degree=3, coeff_range=(0.0, 0.1), dtype=<class 'numpy.float32'>, prob=0.1, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • degree (int) – degree of freedom of the polynomials. The value should be no less than 1. -Defaults to 3.

  • -
  • coeff_range (tuple[float, float]) – range of the random coefficients. Defaults to (0.0, 0.1).

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • prob (float) – probability to do random bias field.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandBiasFieldd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

ScaleIntensityd#

-example of ScaleIntensityd -
-
-class monai.transforms.ScaleIntensityd(keys, minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ScaleIntensity. -Scale the intensity of input image to the given value range (minv, maxv). -If minv and maxv not provided, use factor to scale image by v = v * (1 + factor).

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, minv=0.0, maxv=1.0, factor=None, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • minv (UnionType[float, None]) – minimum value of output data.

  • -
  • maxv (UnionType[float, None]) – maximum value of output data.

  • -
  • factor (Optional[float, None]) – factor scale by v = v * (1 + factor). In order to use -this parameter, please set both minv and maxv into None.

  • -
  • channel_wise (bool) – if True, scale on each channel separately. Please ensure -that the first dimension represents the channel of the image if True.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

RandScaleIntensityd#

-example of RandScaleIntensityd -
-
-class monai.transforms.RandScaleIntensityd(keys, factors, prob=0.1, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandScaleIntensity.

-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(keys, factors, prob=0.1, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • factors (UnionType[tuple[float, float], float]) – factor range to randomly scale by v = v * (1 + factor). -if single number, factor value is picked from (-factors, factors).

  • -
  • prob (float) – probability of scale. -(Default 0.1, with 10% probability it returns a scaled array.)

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandScaleIntensityd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

NormalizeIntensityd#

-example of NormalizeIntensityd -
-
-class monai.transforms.NormalizeIntensityd(keys, subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.NormalizeIntensity. -This transform can normalize only non-zero values or entire image, and can also calculate -mean and std on each channel separately.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • subtrahend (Union[ndarray, Tensor, None]) – the amount to subtract by (usually the mean)

  • -
  • divisor (Union[ndarray, Tensor, None]) – the amount to divide by (usually the standard deviation)

  • -
  • nonzero (bool) – whether only normalize non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately, otherwise, calculate on -the entire image directly. default to False.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ThresholdIntensityd#

-example of ThresholdIntensityd -
-
-class monai.transforms.ThresholdIntensityd(keys, threshold, above=True, cval=0.0, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ThresholdIntensity.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • threshold (float) – the threshold to filter intensity values.

  • -
  • above (bool) – filter values above the threshold or below the threshold, default is True.

  • -
  • cval (float) – value to fill the remaining parts of the image, default is 0.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ScaleIntensityRanged#

-example of ScaleIntensityRanged -
-
-class monai.transforms.ScaleIntensityRanged(keys, a_min, a_max, b_min=None, b_max=None, clip=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ScaleIntensityRange.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • a_min (float) – intensity original range min.

  • -
  • a_max (float) – intensity original range max.

  • -
  • b_min (Optional[float, None]) – intensity target range min.

  • -
  • b_max (Optional[float, None]) – intensity target range max.

  • -
  • clip (bool) – whether to perform clip after scaling.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

GibbsNoised#

-example of GibbsNoised -
-
-class monai.transforms.GibbsNoised(keys, alpha=0.5, allow_missing_keys=False)[source]#
-

Dictionary-based version of GibbsNoise.

-

The transform applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts -are one of the common type of type artifacts appearing in MRI scans.

-

For general information on Gibbs artifacts, please refer to: -https://pubs.rsna.org/doi/full/10.1148/rg.313105115 -https://pubs.rsna.org/doi/full/10.1148/radiographics.22.4.g02jl14949

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – ‘image’, ‘label’, or [‘image’, ‘label’] depending on which data -you need to transform.

  • -
  • alpha (float) – Parametrizes the intensity of the Gibbs noise filter applied. Takes -values in the interval [0,1] with alpha = 0 acting as the identity mapping.

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

RandGibbsNoised#

-example of RandGibbsNoised -
-
-class monai.transforms.RandGibbsNoised(keys, prob=0.1, alpha=(0.0, 1.0), allow_missing_keys=False)[source]#
-

Dictionary-based version of RandGibbsNoise.

-

Naturalistic image augmentation via Gibbs artifacts. The transform -randomly applies Gibbs noise to 2D/3D MRI images. Gibbs artifacts -are one of the common type of type artifacts appearing in MRI scans.

-

The transform is applied to all the channels in the data.

-

For general information on Gibbs artifacts, please refer to: -https://pubs.rsna.org/doi/full/10.1148/rg.313105115 -https://pubs.rsna.org/doi/full/10.1148/radiographics.22.4.g02jl14949

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – ‘image’, ‘label’, or [‘image’, ‘label’] depending on which data -you need to transform.

  • -
  • prob (float) – probability of applying the transform.

  • -
  • alpha (float, List[float]) – Parametrizes the intensity of the Gibbs noise filter applied. Takes -values in the interval [0,1] with alpha = 0 acting as the identity mapping. -If a length-2 list is given as [a,b] then the value of alpha will be sampled -uniformly from the interval [a,b].

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandGibbsNoised

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

KSpaceSpikeNoised#

-example of KSpaceSpikeNoised -
-
-class monai.transforms.KSpaceSpikeNoised(keys, loc, k_intensity=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.KSpaceSpikeNoise.

-

Applies localized spikes in k-space at the given locations and intensities. -Spike (Herringbone) artifact is a type of data acquisition artifact which -may occur during MRI scans.

-

For general information on spike artifacts, please refer to:

-

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

-

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s -perspective.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – “image”, “label”, or [“image”, “label”] depending -on which data you need to transform.

  • -
  • loc (Union[tuple, Sequence[tuple]]) – spatial location for the spikes. For -images with 3D spatial dimensions, the user can provide (C, X, Y, Z) -to fix which channel C is affected, or (X, Y, Z) to place the same -spike in all channels. For 2D cases, the user can provide (C, X, Y) -or (X, Y).

  • -
  • k_intensity (Union[Sequence[float], float, None]) – value for the log-intensity of the -k-space version of the image. If one location is passed to loc or the -channel is not specified, then this argument should receive a float. If -loc is given a sequence of locations, then this argument should -receive a sequence of intensities. This value should be tested as it is -data-dependent. The default values are the 2.5 the mean of the -log-intensity for each channel.

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-

Example

-

When working with 4D data, -KSpaceSpikeNoised("image", loc = ((3,60,64,32), (64,60,32)), k_intensity = (13,14)) -will place a spike at [3, 60, 64, 32] with log-intensity = 13, and -one spike per channel located respectively at [: , 64, 60, 32] -with log-intensity = 14.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – Expects image/label to have dimensions (C, H, W) or -(C, H, W, D), where C is the channel.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-

RandKSpaceSpikeNoised#

-example of RandKSpaceSpikeNoised -
-
-class monai.transforms.RandKSpaceSpikeNoised(keys, prob=0.1, intensity_range=None, channel_wise=True, allow_missing_keys=False)[source]#
-

Dictionary-based version of monai.transforms.RandKSpaceSpikeNoise.

-

Naturalistic data augmentation via spike artifacts. The transform applies -localized spikes in k-space.

-

For general information on spike artifacts, please refer to:

-

AAPM/RSNA physics tutorial for residents: fundamental physics of MR imaging.

-

Body MRI artifacts in clinical practice: A physicist’s and radiologist’s -perspective.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – “image”, “label”, or [“image”, “label”] depending -on which data you need to transform.

  • -
  • prob (float) – probability to add spike artifact to each item in the -dictionary provided it is realized that the noise will be applied -to the dictionary.

  • -
  • intensity_range (Optional[Sequence[Union[Sequence[float], float]], None]) – pass a tuple (a, b) to sample the log-intensity from the interval (a, b) -uniformly for all channels. Or pass sequence of intervals -((a0, b0), (a1, b1), …) to sample for each respective channel. -In the second case, the number of 2-tuples must match the number of channels. -Default ranges is (0.95x, 1.10x) where x is the mean -log-intensity for each channel.

  • -
  • channel_wise (bool) – treat each channel independently. True by default.

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-

Example

-

To apply k-space spikes randomly on the image only, with probability -0.5, and log-intensity sampled from the interval [13, 15] for each -channel independently, one uses -RandKSpaceSpikeNoised("image", prob=0.5, intensity_ranges=(13, 15), channel_wise=True).

-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandKSpaceSpikeNoised

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandRicianNoised#

-example of RandRicianNoised -
-
-class monai.transforms.RandRicianNoised(keys, prob=0.1, mean=0.0, std=1.0, channel_wise=False, relative=False, sample_std=True, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandRicianNoise. -Add Rician noise to image. This transform assumes all the expected fields have same shape, if want to add -different noise for every field, please use this transform separately.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • prob (float) – Probability to add Rician noise to the dictionary.

  • -
  • mean (Union[Sequence[float], float]) – Mean or “centre” of the Gaussian distributions sampled to make up -the Rician noise.

  • -
  • std (Union[Sequence[float], float]) – Standard deviation (spread) of the Gaussian distributions sampled -to make up the Rician noise.

  • -
  • channel_wise (bool) – If True, treats each channel of the image separately.

  • -
  • relative (bool) – If True, the spread of the sampled Gaussian distributions will -be std times the standard deviation of the image or channel’s intensity -histogram.

  • -
  • sample_std (bool) – If True, sample the spread of the Gaussian distributions -uniformly from 0 to std.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – Don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandRicianNoised

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

ScaleIntensityRangePercentilesd#

-example of ScaleIntensityRangePercentilesd -
-
-class monai.transforms.ScaleIntensityRangePercentilesd(keys, lower, upper, b_min, b_max, clip=False, relative=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ScaleIntensityRangePercentiles.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • lower (float) – lower percentile.

  • -
  • upper (float) – upper percentile.

  • -
  • b_min (UnionType[float, None]) – intensity target range min.

  • -
  • b_max (UnionType[float, None]) – intensity target range max.

  • -
  • clip (bool) – whether to perform clip after scaling.

  • -
  • relative (bool) – whether to scale to the corresponding percentiles of [b_min, b_max]

  • -
  • channel_wise (bool) – if True, compute intensity percentile and normalize every channel separately. -default to False.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

AdjustContrastd#

-example of AdjustContrastd -
-
-class monai.transforms.AdjustContrastd(keys, gamma, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.AdjustContrast. -Changes image intensity by gamma. Each pixel/voxel intensity is updated as:

-
-

x = ((x - min) / intensity_range) ^ gamma * intensity_range + min

-
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • gamma (float) – gamma value to adjust the contrast as function.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

RandAdjustContrastd#

-example of RandAdjustContrastd -
-
-class monai.transforms.RandAdjustContrastd(keys, prob=0.1, gamma=(0.5, 4.5), allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandAdjustContrast. -Randomly changes image intensity by gamma. Each pixel/voxel intensity is updated as:

-
-

x = ((x - min) / intensity_range) ^ gamma * intensity_range + min

-
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • prob (float) – Probability of adjustment.

  • -
  • gamma (UnionType[tuple[float, float], float]) – Range of gamma values. -If single number, value is picked from (0.5, gamma), default is (0.5, 4.5).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandAdjustContrastd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

MaskIntensityd#

-example of MaskIntensityd -
-
-class monai.transforms.MaskIntensityd(keys, mask_data=None, mask_key=None, select_fn=<function is_positive>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.MaskIntensity.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • mask_data (Union[ndarray, Tensor, None]) – if mask data is single channel, apply to every channel -of input image. if multiple channels, the channel number must -match input data. the intensity values of input image corresponding -to the selected values in the mask data will keep the original value, -others will be set to 0. if None, will extract the mask data from -input data based on mask_key.

  • -
  • mask_key (Optional[str, None]) – the key to extract mask data from input dictionary, only works -when mask_data is None.

  • -
  • select_fn (Callable) – function to select valid values of the mask_data, default is -to select values > 0.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

SavitzkyGolaySmoothd#

-example of SavitzkyGolaySmoothd -
-
-class monai.transforms.SavitzkyGolaySmoothd(keys, window_length, order, axis=1, mode='zeros', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SavitzkyGolaySmooth.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • window_length (int) – length of the filter window, must be a positive odd integer.

  • -
  • order (int) – order of the polynomial to fit to each window, must be less than window_length.

  • -
  • axis (int) – optional axis along which to apply the filter kernel. Default 1 (first spatial dimension).

  • -
  • mode (str) – optional padding mode, passed to convolution class. 'zeros', 'reflect', 'replicate' -or 'circular'. default: 'zeros'. See torch.nn.Conv1d() for more information.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

MedianSmoothd#

-example of MedianSmoothd -
-
-class monai.transforms.MedianSmoothd(keys, radius, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.MedianSmooth.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • radius (Union[Sequence[int], int]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

GaussianSmoothd#

-example of GaussianSmoothd -
-
-class monai.transforms.GaussianSmoothd(keys, sigma, approx='erf', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.GaussianSmooth.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • sigma (Union[Sequence[float], float]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

RandGaussianSmoothd#

-example of RandGaussianSmoothd -
-
-class monai.transforms.RandGaussianSmoothd(keys, sigma_x=(0.25, 1.5), sigma_y=(0.25, 1.5), sigma_z=(0.25, 1.5), approx='erf', prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.GaussianSmooth.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • sigma_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension.

  • -
  • sigma_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension if have.

  • -
  • sigma_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension if have.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
  • prob (float) – probability of Gaussian smooth.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandGaussianSmoothd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

GaussianSharpend#

-example of GaussianSharpend -
-
-class monai.transforms.GaussianSharpend(keys, sigma1=3.0, sigma2=1.0, alpha=30.0, approx='erf', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.GaussianSharpen.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • sigma1 (Union[Sequence[float], float]) – sigma parameter for the first gaussian kernel. if a list of values, must match the count -of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. -if only 1 value provided, use it for all spatial dimensions.

  • -
  • sigma2 (Union[Sequence[float], float]) – sigma parameter for the second gaussian kernel. if a list of values, must match the count -of spatial dimensions of input data, and apply every value in the list to 1 spatial dimension. -if only 1 value provided, use it for all spatial dimensions.

  • -
  • alpha (float) – weight parameter to compute the final result.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

RandGaussianSharpend#

-example of RandGaussianSharpend -
-
-class monai.transforms.RandGaussianSharpend(keys, sigma1_x=(0.5, 1.0), sigma1_y=(0.5, 1.0), sigma1_z=(0.5, 1.0), sigma2_x=0.5, sigma2_y=0.5, sigma2_z=0.5, alpha=(10.0, 30.0), approx='erf', prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.GaussianSharpen.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • sigma1_x (tuple[float, float]) – randomly select sigma value for the first spatial dimension of first gaussian kernel.

  • -
  • sigma1_y (tuple[float, float]) – randomly select sigma value for the second spatial dimension(if have) of first gaussian kernel.

  • -
  • sigma1_z (tuple[float, float]) – randomly select sigma value for the third spatial dimension(if have) of first gaussian kernel.

  • -
  • sigma2_x (UnionType[tuple[float, float], float]) – randomly select sigma value for the first spatial dimension of second gaussian kernel. -if only 1 value X provided, it must be smaller than sigma1_x and randomly select from [X, sigma1_x].

  • -
  • sigma2_y (UnionType[tuple[float, float], float]) – randomly select sigma value for the second spatial dimension(if have) of second gaussian kernel. -if only 1 value Y provided, it must be smaller than sigma1_y and randomly select from [Y, sigma1_y].

  • -
  • sigma2_z (UnionType[tuple[float, float], float]) – randomly select sigma value for the third spatial dimension(if have) of second gaussian kernel. -if only 1 value Z provided, it must be smaller than sigma1_z and randomly select from [Z, sigma1_z].

  • -
  • alpha (tuple[float, float]) – randomly select weight parameter to compute the final result.

  • -
  • approx (str) – discrete Gaussian kernel type, available options are “erf”, “sampled”, and “scalespace”. -see also monai.networks.layers.GaussianFilter().

  • -
  • prob (float) – probability of Gaussian sharpen.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandGaussianSharpend

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandHistogramShiftd#

-example of RandHistogramShiftd -
-
-class monai.transforms.RandHistogramShiftd(keys, num_control_points=10, prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based version monai.transforms.RandHistogramShift. -Apply random nonlinear transform the image’s intensity histogram.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • num_control_points (UnionType[tuple[int, int], int]) – number of control points governing the nonlinear intensity mapping. -a smaller number of control points allows for larger intensity shifts. if two values provided, number of -control points selecting from range (min_value, max_value).

  • -
  • prob (float) – probability of histogram shift.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandHistogramShiftd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandCoarseDropoutd#

-example of RandCoarseDropoutd -
-
-class monai.transforms.RandCoarseDropoutd(keys, holes, spatial_size, dropout_holes=True, fill_value=None, max_holes=None, max_spatial_size=None, prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RandCoarseDropout. -Expect all the data specified by keys have same spatial shape and will randomly dropout the same regions -for every key, if want to dropout differently for every key, please use this transform separately.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to -randomly select the expected number of regions.

  • -
  • spatial_size (Union[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg -as the minimum spatial size to randomly select size for every region. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • dropout_holes (bool) – if True, dropout the regions of holes and fill value, if False, keep the holes and -dropout the outside and fill value. default to True.

  • -
  • fill_value (Union[tuple[float, float], float, None]) – target value to fill the dropout regions, if providing a number, will use it as constant -value to fill all the regions. if providing a tuple for the min and max, will randomly select -value for every pixel / voxel from the range [min, max). if None, will compute the min and max -value of input image then randomly select value to fill, default to None.

  • -
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • -
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. -if some components of the max_spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of applying the transform.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandCoarseDropoutd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandCoarseShuffled#

-example of RandCoarseShuffled -
-
-class monai.transforms.RandCoarseShuffled(keys, holes, spatial_size, max_holes=None, max_spatial_size=None, prob=0.1, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RandCoarseShuffle. -Expect all the data specified by keys have same spatial shape and will randomly dropout the same regions -for every key, if want to shuffle different regions for every key, please use this transform separately.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • holes (int) – number of regions to dropout, if max_holes is not None, use this arg as the minimum number to -randomly select the expected number of regions.

  • -
  • spatial_size (Union[Sequence[int], int]) – spatial size of the regions to dropout, if max_spatial_size is not None, use this arg -as the minimum spatial size to randomly select size for every region. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • max_holes (Optional[int, None]) – if not None, define the maximum number to randomly select the expected number of regions.

  • -
  • max_spatial_size (Union[Sequence[int], int, None]) – if not None, define the maximum spatial size to randomly select size for every region. -if some components of the max_spatial_size are non-positive values, the transform will use the -corresponding components of input img size. For example, max_spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of applying the transform.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandCoarseShuffled

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

HistogramNormalized#

-example of HistogramNormalized -
-
-class monai.transforms.HistogramNormalized(keys, num_bins=256, min=0, max=255, mask=None, mask_key=None, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.HistogramNormalize.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • num_bins (int) – number of the bins to use in histogram, default to 256. for more details: -https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.

  • -
  • min (int) – the min value to normalize input image, default to 255.

  • -
  • max (int) – the max value to normalize input image, default to 255.

  • -
  • mask (Union[ndarray, Tensor, None]) – if provided, must be ndarray of bools or 0s and 1s, and same shape as image. -only points at which mask==True are used for the equalization. -can also provide the mask by mask_key at runtime.

  • -
  • mask_key (Optional[str, None]) – if mask is None, will try to get the mask with mask_key.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults to float32.

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ForegroundMaskd#

-example of ForegroundMaskd -
-
-class monai.transforms.ForegroundMaskd(keys, threshold='otsu', hsv_threshold=None, invert=False, new_key_prefix=None, allow_missing_keys=False)[source]#
-

Creates a binary mask that defines the foreground based on thresholds in RGB or HSV color space. -This transform receives an RGB (or grayscale) image where by default it is assumed that the foreground has -low values (dark) while the background is white.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • threshold (Union[dict, Callable, str, float]) – an int or a float number that defines the threshold that values less than that are foreground. -It also can be a callable that receives each dimension of the image and calculate the threshold, -or a string that defines such callable from skimage.filter.threshold_…. For the list of available -threshold functions, please refer to https://scikit-image.org/docs/stable/api/skimage.filters.html -Moreover, a dictionary can be passed that defines such thresholds for each channel, like -{“R”: 100, “G”: “otsu”, “B”: skimage.filter.threshold_mean}

  • -
  • hsv_threshold (Union[dict, Callable, str, float, int, None]) – similar to threshold but HSV color space (“H”, “S”, and “V”). -Unlike RBG, in HSV, value greater than hsv_threshold are considered foreground.

  • -
  • invert (bool) – invert the intensity range of the input image, so that the dtype maximum is now the dtype minimum, -and vice-versa.

  • -
  • new_key_prefix (Optional[str, None]) – this prefix be prepended to the key to create a new key for the output and keep the value of -key intact. By default not prefix is set and the corresponding array to the key will be replaced.

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ComputeHoVerMapsd#

-
-
-class monai.transforms.ComputeHoVerMapsd(keys, dtype='float32', new_key_prefix='hover_', allow_missing_keys=False)[source]#
-

Compute horizontal and vertical maps from an instance mask -It generates normalized horizontal and vertical distances to the center of mass of each region.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • dtype (Union[dtype, type, str, None]) – the type of output Tensor. Defaults to “float32”.

  • -
  • new_key_prefix (str) – this prefix be prepended to the key to create a new key for the output and keep the value of -key intact. Defaults to ‘“_hover”, so if the input key is “mask” the output will be “hover_mask”.

  • -
  • allow_missing_keys (bool) – do not raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-
-

IO (Dict)#

-
-

LoadImaged#

-
-
-class monai.transforms.LoadImaged(keys, reader=None, dtype=<class 'numpy.float32'>, meta_keys=None, meta_key_postfix='meta_dict', overwriting=False, image_only=False, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', allow_missing_keys=False, expanduser=True, *args, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.LoadImage, -It can load both image data and metadata. When loading a list of files in one key, -the arrays will be stacked and a new dimension will be added as the first dimension -In this case, the metadata of the first image will be used to represent the stacked result. -The affine transform of all the stacked images should be same. -The output metadata field will be created as meta_keys or key_{meta_key_postfix}.

-

If reader is not specified, this class automatically chooses readers -based on the supported suffixes and in the following order:

-
-
    -
  • User-specified reader at runtime when calling this loader.

  • -
  • User-specified reader in the constructor of LoadImage.

  • -
  • Readers from the last to the first in the registered list.

  • -
  • Current default readers: (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), -(npz, npy -> NumpyReader), (dcm, DICOM series and others -> ITKReader).

  • -
-
-

Please note that for png, jpg, bmp, and other 2D formats, readers by default swap axis 0 and 1 after -loading the array with reverse_indexing set to True because the spatial axes definition -for non-medical specific file formats is different from other common medical packages.

-
-

Note

-
    -
  • If reader is specified, the loader will attempt to use the specified readers and the default supported -readers. This might introduce overheads when handling the exceptions of trying the incompatible loaders. -In this case, it is therefore recommended setting the most appropriate reader as -the last item of the reader parameter.

  • -
-
-
-

See also

- -
-
-
-__call__(data, reader=None)[source]#
-
-
Raises:
-

KeyError – When not self.overwriting and key already exists in data.

-
-
-
- -
-
-__init__(keys, reader=None, dtype=<class 'numpy.float32'>, meta_keys=None, meta_key_postfix='meta_dict', overwriting=False, image_only=False, ensure_channel_first=False, simple_keys=False, prune_meta_pattern=None, prune_meta_sep='.', allow_missing_keys=False, expanduser=True, *args, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • reader (Union[type[ImageReader], str, None]) – reader to load image file and metadata -- if reader is None, a default set of SUPPORTED_READERS will be used. -- if reader is a string, it’s treated as a class name or dotted path -(such as "monai.data.ITKReader"), the supported built-in reader classes are -"ITKReader", "NibabelReader", "NumpyReader". -a reader instance will be constructed with the *args and **kwargs parameters. -- if reader is a reader class/instance, it will be registered to this loader accordingly.

  • -
  • dtype (Union[dtype, type, str, None]) – if not None, convert the loaded image data to this data type.

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key to store the corresponding metadata dictionary. -the metadata is a dictionary object which contains: filename, original_shape, etc. -it can be a sequence of string, map to the keys. -if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • -
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to store the metadata of the nifti image, -default is meta_dict. The metadata is a dictionary object. -For example, load nifti file for image, store the metadata into image_meta_dict.

  • -
  • overwriting (bool) – whether allow overwriting existing metadata of same key. -default is False, which will raise exception if encountering existing key.

  • -
  • image_only (bool) – if True return dictionary containing just only the image volumes, otherwise return -dictionary containing image data array and header dict per input key.

  • -
  • ensure_channel_first (bool) – if True and loaded both image array and metadata, automatically convert -the image array shape to channel first. default to False.

  • -
  • simple_keys (bool) – whether to remove redundant metadata keys, default to False for backward compatibility.

  • -
  • prune_meta_pattern (Optional[str, None]) – combined with prune_meta_sep, a regular expression used to match and prune keys -in the metadata (nested dictionary), default to None, no key deletion.

  • -
  • prune_meta_sep (str) – combined with prune_meta_pattern, used to match and prune keys -in the metadata (nested dictionary). default is “.”, see also monai.transforms.DeleteItemsd. -e.g. prune_meta_pattern=".*_code$", prune_meta_sep=" " removes meta keys that ends with "_code".

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • expanduser (bool) – if True cast filename to Path and call .expanduser on it, otherwise keep filename as is.

  • -
  • args – additional parameters for reader if providing a reader name.

  • -
  • kwargs – additional parameters for reader if providing a reader name.

  • -
-
-
-
- -
-
-register(reader)[source]#
-

Register a virtual subclass of an ABC.

-

Returns the subclass, to allow usage as a class decorator.

-
- -
- -
-
-

SaveImaged#

-
-
-class monai.transforms.SaveImaged(keys, meta_keys=None, meta_key_postfix='meta_dict', output_dir='./', output_postfix='trans', output_ext='.nii.gz', resample=True, mode='nearest', padding_mode=GridSamplePadMode.BORDER, scale=None, dtype=<class 'numpy.float64'>, output_dtype=<class 'numpy.float32'>, allow_missing_keys=False, squeeze_end_dims=True, data_root_dir='', separate_folder=True, print_log=True, output_format='', writer=None, output_name_formatter=None, folder_layout=None, savepath_in_metadict=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SaveImage.

-
-

Note

-

Image should be channel-first shape: [C,H,W,[D]]. -If the data is a patch of an image, the patch index will be appended to the filename.

-
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. -For example, for data with key image, the metadata by default is in image_meta_dict. -The metadata is a dictionary contains values such as filename, original_shape. -This argument can be a sequence of strings, mapped to the keys. -If None, will try to construct meta_keys by key_{meta_key_postfix}.

  • -
  • meta_key_postfix (str) – if meta_keys is None, use key_{meta_key_postfix} to retrieve the metadict.

  • -
  • output_dir (UnionType[Path, str]) – output image directory. -Handled by folder_layout instead, if folder_layout is not None.

  • -
  • output_postfix (str) – a string appended to all output file names, default to trans. -Handled by folder_layout instead, if folder_layout is not None.

  • -
  • output_ext (str) – output file extension name, available extensions: .nii.gz, .nii, .png. -Handled by folder_layout instead, if folder_layout not None.

  • -
  • resample (bool) – whether to resample image (if needed) before saving the data array, -based on the spatial_shape (and original_affine) from metadata.

  • -
  • mode (str) –

    This option is used when resample=True. Defaults to "nearest". -Depending on the writers, the possible options are:

    - -

  • -
  • padding_mode (str) – This option is used when resample = True. Defaults to "border". -Possible options are {"zeros", "border", "reflection"} -See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample

  • -
  • scale (Optional[int, None]) – {255, 65535} postprocess data by clipping to [0, 1] and scaling -[0, 255] (uint8) or [0, 65535] (uint16). Default is None (no scaling).

  • -
  • dtype (Union[dtype, type, str, None]) – data type during resampling computation. Defaults to np.float64 for best precision. -if None, use the data type of input data. To set the output data type, use output_dtype.

  • -
  • output_dtype (Union[dtype, type, str, None]) – data type for saving data. Defaults to np.float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • squeeze_end_dims (bool) – if True, any trailing singleton dimensions will be removed (after the channel -has been moved to the end). So if input is (C,H,W,D), this will be altered to (H,W,D,C), and -then if C==1, it will be saved as (H,W,D). If D is also 1, it will be saved as (H,W). If false, -image will always be saved as (H,W,D,C).

  • -
  • data_root_dir (str) –

    if not empty, it specifies the beginning parts of the input file’s -absolute path. It’s used to compute input_file_rel_path, the relative path to the file from -data_root_dir to preserve folder structure when saving in case there are files in different -folders with the same file names. For example, with the following inputs:

    -
      -
    • input_file_name: /foo/bar/test1/image.nii

    • -
    • output_postfix: seg

    • -
    • output_ext: .nii.gz

    • -
    • output_dir: /output

    • -
    • data_root_dir: /foo/bar

    • -
    -

    The output will be: /output/test1/image/image_seg.nii.gz

    -

    Handled by folder_layout instead, if folder_layout is not None.

    -

  • -
  • separate_folder (bool) – whether to save every file in a separate folder. For example: for the input filename -image.nii, postfix seg and folder_path output, if separate_folder=True, it will be saved as: -output/image/image_seg.nii, if False, saving as output/image_seg.nii. Default to True. -Handled by folder_layout instead, if folder_layout is not None.

  • -
  • print_log (bool) – whether to print logs when saving. Default to True.

  • -
  • output_format (str) – an optional string to specify the output image writer. -see also: monai.data.image_writer.SUPPORTED_WRITERS.

  • -
  • writer (Union[type[ImageWriter], str, None]) – a customised monai.data.ImageWriter subclass to save data arrays. -if None, use the default writer from monai.data.image_writer according to output_ext. -if it’s a string, it’s treated as a class name or dotted path; -the supported built-in writer classes are "NibabelWriter", "ITKWriter", "PILWriter".

  • -
  • output_name_formatter (Optional[Callable[[dict, Transform], dict], None]) – a callable function (returning a kwargs dict) to format the output file name. -see also: monai.data.folder_layout.default_name_formatter(). -If using a custom folder_layout, consider providing your own formatter.

  • -
  • folder_layout (Optional[FolderLayoutBase, None]) – A customized monai.data.FolderLayoutBase subclass to define file naming schemes. -if None, uses the default FolderLayout.

  • -
  • savepath_in_metadict (bool) – if True, adds a key saved_to to the metadata, which contains the path -to where the input image has been saved.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-
-

Post-processing (Dict)#

-
-

Activationsd#

-
-
-class monai.transforms.Activationsd(keys, sigmoid=False, softmax=False, other=None, allow_missing_keys=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.AddActivations. -Add activation layers to the input data specified by keys.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, sigmoid=False, softmax=False, other=None, allow_missing_keys=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output and label. -See also: monai.transforms.compose.MapTransform

  • -
  • sigmoid (UnionType[Sequence[bool], bool]) – whether to execute sigmoid function on model output before transform. -it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • softmax (UnionType[Sequence[bool], bool]) – whether to execute softmax function on model output before transform. -it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • other (Union[Sequence[Callable], Callable, None]) – callable function to execute other activation layers, -for example: other = torch.tanh. it also can be a sequence of Callable, each -element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • kwargs – additional parameters to torch.softmax (used when softmax=True). -Defaults to dim=0, unrecognized parameters will be ignored.

  • -
-
-
-
- -
- -
-
-

AsDiscreted#

-example of AsDiscreted -
-
-class monai.transforms.AsDiscreted(keys, argmax=False, to_onehot=None, threshold=None, rounding=None, allow_missing_keys=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.AsDiscrete.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, argmax=False, to_onehot=None, threshold=None, rounding=None, allow_missing_keys=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output and label. -See also: monai.transforms.compose.MapTransform

  • -
  • argmax (UnionType[Sequence[bool], bool]) – whether to execute argmax function on input data before transform. -it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • to_onehot (Union[Sequence[UnionType[int, None]], int, None]) – if not None, convert input data into the one-hot format with specified number of classes. -defaults to None. it also can be a sequence, each element corresponds to a key in keys.

  • -
  • threshold (Union[Sequence[UnionType[float, None]], float, None]) – if not None, threshold the float values to int number 0 or 1 with specified threshold value. -defaults to None. it also can be a sequence, each element corresponds to a key in keys.

  • -
  • rounding (Union[Sequence[UnionType[str, None]], str, None]) – if not None, round the data according to the specified option, -available options: [“torchrounding”]. it also can be a sequence of str or None, -each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • kwargs – additional parameters to AsDiscrete. -dim, keepdim, dtype are supported, unrecognized parameters will be ignored. -These default to 0, True, torch.float respectively.

  • -
-
-
-
- -
- -
-
-

KeepLargestConnectedComponentd#

-example of KeepLargestConnectedComponentd -
-
-class monai.transforms.KeepLargestConnectedComponentd(keys, applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.KeepLargestConnectedComponent.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, applied_labels=None, is_onehot=None, independent=True, connectivity=None, num_components=1, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • applied_labels (Union[Sequence[int], int, None]) – Labels for applying the connected component analysis on. -If given, voxels whose value is in this list will be analyzed. -If None, all non-zero values will be analyzed.

  • -
  • is_onehot (Optional[bool, None]) – if True, treat the input data as OneHot format data, otherwise, not OneHot format data. -default to None, which treats multi-channel data as OneHot and single channel data as not OneHot.

  • -
  • independent (bool) – whether to treat applied_labels as a union of foreground labels. -If True, the connected component analysis will be performed on each foreground label independently -and return the intersection of the largest components. -If False, the analysis will be performed on the union of foreground labels. -default is True.

  • -
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. If None, a full -connectivity of input.ndim is used. for more details: -https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label.

  • -
  • num_components (int) – The number of largest components to preserve.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

RemoveSmallObjectsd#

-example of RemoveSmallObjectsd -
-
-class monai.transforms.RemoveSmallObjectsd(keys, min_size=64, connectivity=1, independent_channels=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RemoveSmallObjectsd.

-
-
Parameters:
-
    -
  • min_size (int) – objects smaller than this size are removed.

  • -
  • connectivity (int) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. If None, a full -connectivity of input.ndim is used. For more details refer to linked scikit-image -documentation.

  • -
  • independent_channels (bool) – Whether or not to consider channels as independent. If true, then -conjoining islands from different labels will be removed if they are below the threshold. -If false, the overall size islands made from all non-background voxels will be used.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

LabelFilterd#

-example of LabelFilterd -
-
-class monai.transforms.LabelFilterd(keys, applied_labels, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.LabelFilter.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, applied_labels, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • applied_labels (UnionType[Sequence[int], int]) – Label(s) to filter on.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

FillHolesd#

-
-
-class monai.transforms.FillHolesd(keys, applied_labels=None, connectivity=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.FillHoles.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, applied_labels=None, connectivity=None, allow_missing_keys=False)[source]#
-

Initialize the connectivity and limit the labels for which holes are filled.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • applied_labels (Optional[Union[Iterable[int], int]], optional) – Labels for which to fill holes. Defaults to None, -that is filling holes for all labels.

  • -
  • connectivity (int, optional) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. Defaults to a full -connectivity of input.ndim.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

LabelToContourd#

-example of LabelToContourd -
-
-class monai.transforms.LabelToContourd(keys, kernel_type='Laplace', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.LabelToContour.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, kernel_type='Laplace', allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • kernel_type (str) – the method applied to do edge detection, default is “Laplace”.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

Ensembled#

-
-
-class monai.transforms.Ensembled(keys, ensemble, output_key=None, allow_missing_keys=False)[source]#
-

Base class of dictionary-based ensemble transforms.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, ensemble, output_key=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be stack and execute ensemble. -if only 1 key provided, suppose it’s a PyTorch Tensor with data stacked on dimension E.

  • -
  • output_key (Optional[str, None]) – the key to store ensemble result in the dictionary.

  • -
  • ensemble (Callable[Union[Sequence[Union[ndarray, Tensor]], ndarray, Tensor], Union[ndarray, Tensor]]) – callable method to execute ensemble on specified data. -if only 1 key provided in keys, output_key can be None and use keys as default.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
Raises:
-
    -
  • TypeError – When ensemble is not callable.

  • -
  • ValueError – When len(keys) > 1 and output_key=None. Incompatible values.

  • -
-
-
-
- -
- -
-
-

MeanEnsembled#

-
-
-class monai.transforms.MeanEnsembled(keys, output_key=None, weights=None)[source]#
-

Dictionary-based wrapper of monai.transforms.MeanEnsemble.

-
-
-__init__(keys, output_key=None, weights=None)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be stack and execute ensemble. -if only 1 key provided, suppose it’s a PyTorch Tensor with data stacked on dimension E.

  • -
  • output_key (Optional[str, None]) – the key to store ensemble result in the dictionary. -if only 1 key provided in keys, output_key can be None and use keys as default.

  • -
  • weights (Union[Sequence[float], ndarray, Tensor, None]) – can be a list or tuple of numbers for input data with shape: [E, C, H, W[, D]]. -or a Numpy ndarray or a PyTorch Tensor data. -the weights will be added to input data from highest dimension, for example: -1. if the weights only has 1 dimension, it will be added to the E dimension of input data. -2. if the weights has 2 dimensions, it will be added to E and C dimensions. -it’s a typical practice to add weights for different classes: -to ensemble 3 segmentation model outputs, every output has 4 channels(classes), -so the input data shape can be: [3, 4, H, W, D]. -and add different weights for different classes, so the weights shape can be: [3, 4]. -for example: weights = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 1, 1, 1]].

  • -
-
-
-
- -
- -
-
-

VoteEnsembled#

-
-
-class monai.transforms.VoteEnsembled(keys, output_key=None, num_classes=None)[source]#
-

Dictionary-based wrapper of monai.transforms.VoteEnsemble.

-
-
-__init__(keys, output_key=None, num_classes=None)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be stack and execute ensemble. -if only 1 key provided, suppose it’s a PyTorch Tensor with data stacked on dimension E.

  • -
  • output_key (Optional[str, None]) – the key to store ensemble result in the dictionary. -if only 1 key provided in keys, output_key can be None and use keys as default.

  • -
  • num_classes (Optional[int, None]) – if the input is single channel data instead of One-Hot, we can’t get class number -from channel, need to explicitly specify the number of classes to vote.

  • -
-
-
-
- -
- -
-
-

Invertd#

-
-
-class monai.transforms.Invertd(keys, transform, orig_keys=None, meta_keys=None, orig_meta_keys=None, meta_key_postfix='meta_dict', nearest_interp=True, to_tensor=True, device=None, post_func=None, allow_missing_keys=False)[source]#
-

Utility transform to invert the previously applied transforms.

-

Taking the transform previously applied on orig_keys, this Invertd will apply the inverse of it -to the data stored at keys.

-

Invertd’s output will also include a copy of the metadata -dictionary (originally from orig_meta_keys or the metadata of orig_keys), -with the relevant fields inverted and stored at meta_keys.

-

A typical usage is to apply the inverse of the preprocessing (transform=preprocessings) on -input orig_keys=image to the model predictions keys=pred.

-

A detailed usage example is available in the tutorial: -Project-MONAI/tutorials

-
-

Note

-
    -
  • The output of the inverted data and metadata will be stored at keys and meta_keys respectively.

  • -
  • To correctly invert the transforms, the information of the previously applied transforms should be -available at {orig_keys}_transforms, and the original metadata at orig_meta_keys. -(meta_key_postfix is an optional string to conveniently construct “meta_keys” and/or “orig_meta_keys”.) -see also: monai.transforms.TraceableTransform.

  • -
  • The transform will not change the content in orig_keys and orig_meta_key. -These keys are only used to represent the data status of key before inverting.

  • -
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Any]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, transform, orig_keys=None, meta_keys=None, orig_meta_keys=None, meta_key_postfix='meta_dict', nearest_interp=True, to_tensor=True, device=None, post_func=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – the key of expected data in the dict, the inverse of transforms will be applied on it in-place. -It also can be a list of keys, will apply the inverse transform respectively.

  • -
  • transform (InvertibleTransform) – the transform applied to orig_key, its inverse will be applied on key.

  • -
  • orig_keys (Union[Collection[Hashable], Hashable, None]) – the key of the original input data in the dict. These keys default to self.keys if not set. -the transform trace information of transforms should be stored at {orig_keys}_transforms. -It can also be a list of keys, each matches the keys.

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – The key to output the inverted metadata dictionary. -The metadata is a dictionary optionally containing: filename, original_shape. -It can be a sequence of strings, maps to keys. -If None, will try to create a metadata dict with the default key: {key}_{meta_key_postfix}.

  • -
  • orig_meta_keys (Union[Collection[Hashable], Hashable, None]) – the key of the metadata of original input data. -The metadata is a dictionary optionally containing: filename, original_shape. -It can be a sequence of strings, maps to the keys. -If None, will try to create a metadata dict with the default key: {orig_key}_{meta_key_postfix}. -This metadata dict will also be included in the inverted dict, stored in meta_keys.

  • -
  • meta_key_postfix (str) – if orig_meta_keys is None, use {orig_key}_{meta_key_postfix} to fetch the -metadata from dict, if meta_keys is None, use {key}_{meta_key_postfix}. Default: "meta_dict".

  • -
  • nearest_interp (UnionType[bool, Sequence[bool]]) – whether to use nearest interpolation mode when inverting the spatial transforms, -default to True. If False, use the same interpolation mode as the original transform. -It also can be a list of bool, each matches to the keys data.

  • -
  • to_tensor (UnionType[bool, Sequence[bool]]) – whether to convert the inverted data into PyTorch Tensor first, default to True. -It also can be a list of bool, each matches to the keys data.

  • -
  • device (Union[str, device, Sequence[UnionType[str, device]], None]) – if converted to Tensor, move the inverted results to target device before post_func, -default to None, it also can be a list of string or torch.device, each matches to the keys data.

  • -
  • post_func (Union[Sequence[Callable], Callable, None]) – post processing for the inverted data, should be a callable function. -It also can be a list of callable, each matches to the keys data.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

SaveClassificationd#

-
-
-class monai.transforms.SaveClassificationd(keys, meta_keys=None, meta_key_postfix='meta_dict', saver=None, output_dir='./', filename='predictions.csv', delimiter=',', overwrite=True, flush=True, allow_missing_keys=False)[source]#
-

Save the classification results and metadata into CSV file or other storage.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, meta_keys=None, meta_key_postfix='meta_dict', saver=None, output_dir='./', filename='predictions.csv', delimiter=',', overwrite=True, flush=True, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output, this transform only supports 1 key. -See also: monai.transforms.compose.MapTransform

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. -for example, for data with key image, the metadata by default is in image_meta_dict. -the metadata is a dictionary object which contains: filename, original_shape, etc. -it can be a sequence of string, map to the keys. -if None, will try to construct meta_keys by key_{meta_key_postfix}. -will extract the filename of input image to save classification results.

  • -
  • meta_key_postfix (str) – key_{postfix} was used to store the metadata in LoadImaged. -so need the key to extract the metadata of input image, like filename, etc. default is meta_dict. -for example, for data with key image, the metadata by default is in image_meta_dict. -the metadata is a dictionary object which contains: filename, original_shape, etc. -this arg only works when meta_keys=None. if no corresponding metadata, set to None.

  • -
  • saver (Optional[CSVSaver, None]) – the saver instance to save classification results, if None, create a CSVSaver internally. -the saver must provide save(data, meta_data) and finalize() APIs.

  • -
  • output_dir (Union[str, PathLike]) – if saver=None, specify the directory to save the CSV file.

  • -
  • filename (str) – if saver=None, specify the name of the saved CSV file.

  • -
  • delimiter (str) – the delimiter character in the saved file, default to “,” as the default output type is csv. -to be consistent with: https://docs.python.org/3/library/csv.html#csv.Dialect.delimiter.

  • -
  • overwrite (bool) – if saver=None, indicate whether to overwriting existing CSV file content, if True, -will clear the file before saving. otherwise, will append new content to the CSV file.

  • -
  • flush (bool) – if saver=None, indicate whether to write the cache data to CSV file immediately -in this transform and clear the cache. default to True. -If False, may need user to call saver.finalize() manually or use ClassificationSaver handler.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-get_saver()[source]#
-

If want to write content into file, may need to call finalize of saver when epoch completed. -Or users can also get the cache content from saver instead of writing into file.

-
- -
- -
-
-

ProbNMSd#

-
-
-class monai.transforms.ProbNMSd(keys, spatial_dims=2, sigma=0.0, prob_threshold=0.5, box_size=48, allow_missing_keys=False)[source]#
-

Performs probability based non-maximum suppression (NMS) on the probabilities map via -iteratively selecting the coordinate with highest probability and then move it as well -as its surrounding values. The remove range is determined by the parameter box_size. -If multiple coordinates have the same highest probability, only one of them will be -selected.

-
-
Parameters:
-
    -
  • spatial_dims (int) – number of spatial dimensions of the input probabilities map. -Defaults to 2.

  • -
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – the standard deviation for gaussian filter. -It could be a single value, or spatial_dims number of values. Defaults to 0.0.

  • -
  • prob_threshold (float) – the probability threshold, the function will stop searching if -the highest probability is no larger than the threshold. The value should be -no less than 0.0. Defaults to 0.5.

  • -
  • box_size (UnionType[int, Sequence[int]]) – the box size (in pixel) to be removed around the pixel with the maximum probability. -It can be an integer that defines the size of a square or cube, -or a list containing different values for each dimensions. Defaults to 48.

  • -
-
-
Returns:
-

a list of selected lists, where inner lists contain probability and coordinates. -For example, for 3D input, the inner lists are in the form of [probability, x, y, z].

-
-
Raises:
-
    -
  • ValueError – When prob_threshold is less than 0.0.

  • -
  • ValueError – When box_size is a list or tuple, and its length is not equal to spatial_dims.

  • -
  • ValueError – When box_size has a less than 1 value.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

SobelGradientsd#

-
-
-class monai.transforms.SobelGradientsd(keys, kernel_size=3, spatial_axes=None, normalize_kernels=True, normalize_gradients=False, padding_mode='reflect', dtype=torch.float32, new_key_prefix=None, allow_missing_keys=False)[source]#
-

Calculate Sobel horizontal and vertical gradients of a grayscale image.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to model output.

  • -
  • kernel_size (int) – the size of the Sobel kernel. Defaults to 3.

  • -
  • spatial_axes (Union[Sequence[int], int, None]) – the axes that define the direction of the gradient to be calculated. It calculate the gradient -along each of the provide axis. By default it calculate the gradient for all spatial axes.

  • -
  • normalize_kernels (bool) – if normalize the Sobel kernel to provide proper gradients. Defaults to True.

  • -
  • normalize_gradients (bool) – if normalize the output gradient to 0 and 1. Defaults to False.

  • -
  • padding_mode (str) – the padding mode of the image when convolving with Sobel kernels. Defaults to “reflect”. -Acceptable values are 'zeros', 'reflect', 'replicate' or 'circular'. -See torch.nn.Conv1d() for more information.

  • -
  • dtype (dtype) – kernel data type (torch.dtype). Defaults to torch.float32.

  • -
  • new_key_prefix (Optional[str, None]) – this prefix be prepended to the key to create a new key for the output and keep the value of -key intact. By default not prefix is set and the corresponding array to the key will be replaced.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-
-

Spatial (Dict)#

-
-

SpatialResampled#

-
-
-class monai.transforms.SpatialResampled(keys, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, dst_keys='dst_affine', allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SpatialResample.

-

This transform assumes the data dictionary has a key for the input -data’s metadata and contains src_affine and dst_affine required by -SpatialResample. The key is formed by key_{meta_key_postfix}. The -transform will swap src_affine and dst_affine affine (with potential data type -changes) in the dictionary so that src_affine always refers to the current -status of affine.

- -
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, dst_keys='dst_affine', allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • align_corners (UnionType[Sequence[bool], bool]) – Geometrically, we consider the pixels of the input as squares rather than points. -See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample -It also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None]], dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32. -It also can be a sequence of dtypes, each element corresponds to a key in keys.

  • -
  • dst_keys (Union[Collection[Hashable], Hashable, None]) – the key of the corresponding dst_affine in the metadata dictionary.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

ResampleToMatchd#

-
-
-class monai.transforms.ResampleToMatchd(keys, key_dst, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ResampleToMatch.

-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, key_dst, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • key_dst (str) – key of image to resample to match.

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • align_corners (UnionType[Sequence[bool], bool]) – Geometrically, we consider the pixels of the input as squares rather than points. -See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample -It also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None]], dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32. -It also can be a sequence of dtypes, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Spacingd#

-example of Spacingd -
-
-class monai.transforms.Spacingd(keys, pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, ensure_same_shape=True, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Spacing.

-

This transform assumes the data dictionary has a key for the input -data’s metadata and contains affine field. The key is formed by key_{meta_key_postfix}.

-

After resampling the input array, this transform will write the new affine -to the affine field of metadata which is formed by key_{meta_key_postfix}.

- -
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, pixdim, diagonal=False, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float64'>, scale_extent=False, recompute_affine=False, min_pixdim=None, max_pixdim=None, ensure_same_shape=True, allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • pixdim (UnionType[Sequence[float], float]) – output voxel spacing. if providing a single number, will use it for the first dimension. -items of the pixdim sequence map to the spatial dimensions of input image, if length -of pixdim sequence is longer than image spatial dimensions, will ignore the longer part, -if shorter, will pad with 1.0. -if the components of the pixdim are non-positive values, the transform will use the -corresponding components of the original pixdim, which is computed from the affine -matrix of input image.

  • -
  • diagonal (bool) –

    whether to resample the input to have a diagonal affine matrix. -If True, the input data is resampled to the following affine:

    -
    np.diag((pixdim_0, pixdim_1, pixdim_2, 1))
    -
    -
    -

    This effectively resets the volume to the world coordinate system (RAS+ in nibabel). -The original orientation, rotation, shearing are not preserved.

    -

    If False, the axes orientation, orthogonal rotation and -translations components from the original affine will be -preserved in the target affine. This option will not flip/swap -axes against the original ones.

    -

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • align_corners (UnionType[Sequence[bool], bool]) – Geometrically, we consider the pixels of the input as squares rather than points. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -It also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None]], dtype, type, str, None]) – data type for resampling computation. Defaults to float64 for best precision. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32. -It also can be a sequence of dtypes, each element corresponds to a key in keys.

  • -
  • scale_extent (bool) – whether the scale is computed based on the spacing or the full extent of voxels, -default False. The option is ignored if output spatial size is specified when calling this transform. -See also: monai.data.utils.compute_shape_offset(). When this is True, align_corners -should be True because compute_shape_offset already provides the corner alignment shift/scaling.

  • -
  • recompute_affine (bool) – whether to recompute affine based on the output shape. The affine computed -analytically does not reflect the potential quantization errors in terms of the output shape. -Set this flag to True to recompute the output affine based on the actual pixdim. Default to False.

  • -
  • min_pixdim (Union[Sequence[float], float, None]) – minimal input spacing to be resampled. If provided, input image with a larger spacing than this -value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the -value of pixdim. Default to None.

  • -
  • max_pixdim (Union[Sequence[float], float, None]) – maximal input spacing to be resampled. If provided, input image with a smaller spacing than this -value will be kept in its original spacing (not be resampled to pixdim). Set it to None to use the -value of pixdim. Default to None.

  • -
  • ensure_same_shape (bool) – when the inputs have the same spatial shape, and almost the same pixdim, -whether to ensure exactly the same output spatial shape. Default to True.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Orientationd#

-example of Orientationd -
-
-class monai.transforms.Orientationd(keys, axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Orientation.

-

This transform assumes the channel-first input format. -In the case of using this transform for normalizing the orientations of images, -it should be used before any anisotropic spatial transforms.

-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, axcodes=None, as_closest_canonical=False, labels=(('L', 'R'), ('P', 'A'), ('I', 'S')), allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • axcodes (Optional[str, None]) – N elements sequence for spatial ND input’s orientation. -e.g. axcodes=’RAS’ represents 3D orientation: -(Left, Right), (Posterior, Anterior), (Inferior, Superior). -default orientation labels options are: ‘L’ and ‘R’ for the first dimension, -‘P’ and ‘A’ for the second, ‘I’ and ‘S’ for the third.

  • -
  • as_closest_canonical (bool) – if True, load the image as closest to canonical axis format.

  • -
  • labels (UnionType[Sequence[tuple[str, str]], None]) – optional, None or sequence of (2,) sequences -(2,) sequences are labels for (beginning, end) of output axis. -Defaults to (('L', 'R'), ('P', 'A'), ('I', 'S')).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-

See also

-

nibabel.orientations.ornt2axcodes.

-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Flipd#

-example of Flipd -
-
-class monai.transforms.Flipd(keys, spatial_axis=None, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Flip.

-

See numpy.flip for additional details. -https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • spatial_axis (Union[Sequence[int], int, None]) – Spatial axes along which to flip over. Default is None.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandFlipd#

-example of RandFlipd -
-
-class monai.transforms.RandFlipd(keys, prob=0.1, spatial_axis=None, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandFlip.

-

See numpy.flip for additional details. -https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • prob (float) – Probability of flipping.

  • -
  • spatial_axis (Union[Sequence[int], int, None]) – Spatial axes along which to flip over. Default is None.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandFlipd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandAxisFlipd#

-example of RandAxisFlipd -
-
-class monai.transforms.RandAxisFlipd(keys, prob=0.1, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandAxisFlip.

-

See numpy.flip for additional details. -https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • prob (float) – Probability of flipping.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandAxisFlipd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

Rotated#

-example of Rotated -
-
-class monai.transforms.Rotated(keys, angle, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Rotate.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • angle (UnionType[Sequence[float], float]) – Rotation angle(s) in radians.

  • -
  • keep_size (bool) – If it is False, the output shape is adapted so that the -input array is contained completely in the output. -If it is True, the output shape is the same as the input. Default is True.

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • align_corners (UnionType[Sequence[bool], bool]) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -It also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32. -It also can be a sequence of dtype or None, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandRotated#

-example of RandRotated -
-
-class monai.transforms.RandRotated(keys, range_x=0.0, range_y=0.0, range_z=0.0, prob=0.1, keep_size=True, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, align_corners=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandRotate -Randomly rotates the input arrays.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • range_x (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and second axes. -If single number, angle is uniformly sampled from (-range_x, range_x).

  • -
  • range_y (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the first and third axes. -If single number, angle is uniformly sampled from (-range_y, range_y). only work for 3D data.

  • -
  • range_z (UnionType[tuple[float, float], float]) – Range of rotation angle in radians in the plane defined by the second and third axes. -If single number, angle is uniformly sampled from (-range_z, range_z). only work for 3D data.

  • -
  • prob (float) – Probability of rotation.

  • -
  • keep_size (bool) – If it is False, the output shape is adapted so that the -input array is contained completely in the output. -If it is True, the output shape is the same as the input. Default is True.

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • align_corners (UnionType[Sequence[bool], bool]) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float64 for best precision. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32. -It also can be a sequence of dtype or None, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandRotated

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

Zoomd#

-example of Zoomd -
-
-class monai.transforms.Zoomd(keys, zoom, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=<class 'numpy.float32'>, keep_size=True, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.Zoom.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • zoom (UnionType[Sequence[float], float]) – The zoom factor along the spatial axes. -If a float, zoom is the same for each spatial axis. -If a sequence, zoom should contain one value for each spatial axis.

  • -
  • mode (Union[Sequence[str], str]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to "area". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "edge". -The mode to pad data after zooming. -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • align_corners (Union[Sequence[UnionType[bool, None]], bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of bool or None, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data.

  • -
  • keep_size (bool) – Should keep original size (pad if needed), default is True.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
  • kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandZoomd#

-example of RandZoomd -
-
-class monai.transforms.RandZoomd(keys, prob=0.1, min_zoom=0.9, max_zoom=1.1, mode=InterpolateMode.AREA, padding_mode=NumpyPadMode.EDGE, align_corners=None, dtype=<class 'numpy.float32'>, keep_size=True, allow_missing_keys=False, lazy=False, **kwargs)[source]#
-

Dict-based version monai.transforms.RandZoom.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – Keys to pick data for transformation.

  • -
  • prob (float) – Probability of zooming.

  • -
  • min_zoom (UnionType[Sequence[float], float]) – Min zoom factor. Can be float or sequence same size as image. -If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims -to keep the original spatial shape ratio. -If a sequence, min_zoom should contain one value for each spatial axis. -If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • -
  • max_zoom (UnionType[Sequence[float], float]) – Max zoom factor. Can be float or sequence same size as image. -If a float, select a random factor from [min_zoom, max_zoom] then apply to all spatial dims -to keep the original spatial shape ratio. -If a sequence, max_zoom should contain one value for each spatial axis. -If 2 values provided for 3D data, use the first value for both H & W dims to keep the same zoom ratio.

  • -
  • mode (Union[Sequence[str], str]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to "area". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – available modes for numpy array:{"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -available modes for PyTorch Tensor: {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. Defaults to "edge". -The mode to pad data after zooming. -See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html

  • -
  • align_corners (Union[Sequence[UnionType[bool, None]], bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of bool or None, each element corresponds to a key in keys.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data.

  • -
  • keep_size (bool) – Should keep original size (pad if needed), default is True.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
  • kwargs – other args for np.pad API, note that np.pad treats channel dimension as the first dimension. -more details: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandZoomd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

GridPatchd#

-
-
-class monai.transforms.GridPatchd(keys, patch_size, offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, allow_missing_keys=False, **pad_kwargs)[source]#
-

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps. -It can sort the patches and return all or a subset of them.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • -
  • offset (Optional[Sequence[int], None]) – starting position in the array, default is 0 for each dimension. -np.random.randint(0, patch_size, 2) creates random start between 0 and patch_size for a 2D image.

  • -
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. -If the requested number of patches is greater than the number of available patches, -padding will be applied to provide exactly num_patches patches unless threshold is set. -When threshold is set, this value is treated as the maximum number of patches. -Defaults to None, which does not limit number of the patches.

  • -
  • overlap (float) – amount of overlap between patches in each dimension. Default to 0.0.

  • -
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), -lowest values (“min”), or in their default order (None). Default to None.

  • -
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. -Defaults to no filtering.

  • -
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. -Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. -Defaults to None, which means no padding will be applied. -See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -requires pytorch >= 1.10 for best compatibility.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Returns:
-

-
dictionary, contains the all the original key/value with the values for keys

replaced by the patches, a MetaTensor with following metadata:

-
    -
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • -
  • PatchKeys.COUNT: total number of patches in the image,

  • -
  • ”spatial_shape”: spatial size of the extracted patch, and

  • -
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • -
-
-
-

-
-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
- -
-
-

RandGridPatchd#

-
-
-class monai.transforms.RandGridPatchd(keys, patch_size, min_offset=None, max_offset=None, num_patches=None, overlap=0.0, sort_fn=None, threshold=None, pad_mode=None, allow_missing_keys=False, **pad_kwargs)[source]#
-

Extract all the patches sweeping the entire image in a row-major sliding-window manner with possible overlaps, -and with random offset for the minimal corner of the image, (0,0) for 2D and (0,0,0) for 3D. -It can sort the patches and return all or a subset of them.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • patch_size (Sequence[int]) – size of patches to generate slices for, 0 or None selects whole dimension

  • -
  • min_offset (Union[Sequence[int], int, None]) – the minimum range of starting position to be selected randomly. Defaults to 0.

  • -
  • max_offset (Union[Sequence[int], int, None]) – the maximum range of starting position to be selected randomly. -Defaults to image size modulo patch size.

  • -
  • num_patches (Optional[int, None]) – number of patches (or maximum number of patches) to return. -If the requested number of patches is greater than the number of available patches, -padding will be applied to provide exactly num_patches patches unless threshold is set. -When threshold is set, this value is treated as the maximum number of patches. -Defaults to None, which does not limit number of the patches.

  • -
  • overlap (float) – the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). -If only one float number is given, it will be applied to all dimensions. Defaults to 0.0.

  • -
  • sort_fn (Optional[str, None]) – when num_patches is provided, it determines if keep patches with highest values (“max”), -lowest values (“min”), or in their default order (None). Default to None.

  • -
  • threshold (Optional[float, None]) – a value to keep only the patches whose sum of intensities are less than the threshold. -Defaults to no filtering.

  • -
  • pad_mode (Optional[str, None]) – the mode for padding the input image by patch_size to include patches that cross boundaries. -Available modes: (Numpy) {"constant", "edge", "linear_ramp", "maximum", -"mean", "median", "minimum", "reflect", "symmetric", "wrap", "empty"} -(PyTorch) {"constant", "reflect", "replicate", "circular"}. -One of the listed string values or a user supplied function. -Defaults to None, which means no padding will be applied. -See also: https://numpy.org/doc/stable/reference/generated/numpy.pad.html -https://pytorch.org/docs/stable/generated/torch.nn.functional.pad.html -requires pytorch >= 1.10 for best compatibility.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • pad_kwargs – other arguments for the np.pad or torch.pad function. -note that np.pad treats channel dimension as the first dimension.

  • -
-
-
Returns:
-

-
dictionary, contains the all the original key/value with the values for keys

replaced by the patches, a MetaTensor with following metadata:

-
    -
  • PatchKeys.LOCATION: the starting location of the patch in the image,

  • -
  • PatchKeys.COUNT: total number of patches in the image,

  • -
  • ”spatial_shape”: spatial size of the extracted patch, and

  • -
  • ”offset”: the amount of offset for the patches in the image (starting position of the first patch)

  • -
-
-
-

-
-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandGridPatchd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

GridSplitd#

-
-
-class monai.transforms.GridSplitd(keys, grid=(2, 2), size=None, allow_missing_keys=False)[source]#
-

Split the image into patches based on the provided grid in 2D.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • grid (tuple[int, int]) – a tuple define the shape of the grid upon which the image is split. Defaults to (2, 2)

  • -
  • size (Union[int, tuple[int, int], dict[Hashable, UnionType[int, tuple[int, int], None]], None]) – a tuple or an integer that defines the output patch sizes, -or a dictionary that define it separately for each key, like {“image”: 3, “mask”, (2, 2)}. -If it’s an integer, the value will be repeated for each dimension. -The default is None, where the patch size will be inferred from the grid shape.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-

Note: This transform currently support only image with two spatial dimensions.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

list[dict[Hashable, Union[ndarray, Tensor]]]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
- -
-
-

RandRotate90d#

-example of RandRotate90d -
-
-class monai.transforms.RandRotate90d(keys, prob=0.1, max_k=3, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based version monai.transforms.RandRotate90. -With probability prob, input arrays are rotated by 90 degrees -in the plane specified by spatial_axes.

-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

Mapping[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, prob=0.1, max_k=3, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • prob (float) – probability of rotating. -(Default 0.1, with 10% probability it returns a rotated array.)

  • -
  • max_k (int) – number of rotations will be sampled from np.random.randint(max_k) + 1. -(Default 3)

  • -
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. -Default: (0, 1), this is the first two axis in spatial dimensions.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

Rotate90d#

-example of Rotate90d -
-
-class monai.transforms.Rotate90d(keys, k=1, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Rotate90.

-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, k=1, spatial_axes=(0, 1), allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • k (int) – number of times to rotate by 90 degrees.

  • -
  • spatial_axes (tuple[int, int]) – 2 int numbers, defines the plane to rotate with 2 spatial axes. -Default: (0, 1), this is the first two axis in spatial dimensions.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Resized#

-example of Resized -
-
-class monai.transforms.Resized(keys, spatial_size, size_mode='all', mode=InterpolateMode.AREA, align_corners=None, anti_aliasing=False, anti_aliasing_sigma=None, dtype=<class 'numpy.float32'>, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Resize.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • spatial_size (UnionType[Sequence[int], int]) – expected shape of spatial dimensions after resize operation. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • size_mode (str) – should be “all” or “longest”, if “all”, will use spatial_size for all the spatial dims, -if “longest”, rescale the image so that only the longest side is equal to specified spatial_size, -which must be an int number in this case, keeping the aspect ratio of the initial image, refer to: -https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ -#albumentations.augmentations.geometric.resize.LongestMaxSize.

  • -
  • mode (Union[Sequence[str], str]) – {"nearest", "nearest-exact", "linear", "bilinear", "bicubic", "trilinear", "area"} -The interpolation mode. Defaults to "area". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • align_corners (Union[Sequence[UnionType[bool, None]], bool, None]) – This only has an effect when mode is -‘linear’, ‘bilinear’, ‘bicubic’ or ‘trilinear’. Default: None. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.interpolate.html -It also can be a sequence of bool or None, each element corresponds to a key in keys.

  • -
  • anti_aliasing (UnionType[Sequence[bool], bool]) – bool -Whether to apply a Gaussian filter to smooth the image prior -to downsampling. It is crucial to filter when downsampling -the image to avoid aliasing artifacts. See also skimage.transform.resize

  • -
  • anti_aliasing_sigma (Union[Sequence[UnionType[Sequence[float], float, None]], Sequence[float], float, None]) – {float, tuple of floats}, optional -Standard deviation for Gaussian filtering used when anti-aliasing. -By default, this value is chosen as (s - 1) / 2 where s is the -downsampling factor, where s > 1. For the up-size case, s < 1, no -anti-aliasing is performed prior to rescaling.

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

Affined#

-example of Affined -
-
-class monai.transforms.Affined(keys, rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, dtype=<class 'numpy.float32'>, align_corners=False, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Affine.

-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, rotate_params=None, shear_params=None, translate_params=None, scale_params=None, affine=None, spatial_size=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, dtype=<class 'numpy.float32'>, align_corners=False, allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • rotate_params (Union[Sequence[float], float, None]) – a rotation angle in radians, a scalar for 2D image, a tuple of 3 floats for 3D. -Defaults to no rotation.

  • -
  • shear_params (Union[Sequence[float], float, None]) –

    shearing factors for affine matrix, take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -a tuple of 2 floats for 2D, a tuple of 6 floats for 3D. Defaults to no shearing.
    -
    -
    -

  • -
  • translate_params (Union[Sequence[float], float, None]) – a tuple of 2 floats for 2D, a tuple of 3 floats for 3D. Translation is in -pixel/voxel relative to the center of the input image. Defaults to no translation.

  • -
  • scale_params (Union[Sequence[float], float, None]) – scale factor for every spatial dims. a tuple of 2 floats for 2D, -a tuple of 3 floats for 3D. Defaults to 1.0.

  • -
  • affine (Union[ndarray, Tensor, None]) – if applied, ignore the params (rotate_params, etc.) and use the -supplied matrix. Should be square with each side = num of image spatial -dimensions + 1.

  • -
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – data type for resampling computation. Defaults to float32. -If None, use the data type of input data. To be compatible with other modules, -the output data type is always float32.

  • -
  • align_corners (bool) – Defaults to False. -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-

See also

-
    -
  • monai.transforms.compose.MapTransform

  • -
  • RandAffineGrid for the random affine parameters configurations.

  • -
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
- -
-
-

RandAffined#

-example of RandAffined -
-
-class monai.transforms.RandAffined(keys, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, allow_missing_keys=False, lazy=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RandAffine.

-
-
-__call__(data, lazy=None)[source]#
-
-
Parameters:
-
    -
  • data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

  • -
  • lazy (Optional[bool, None]) – a flag to indicate whether this transform should execute lazily or not -during this call. Setting this to False or True overrides the lazy flag set -during initialization for this call. Defaults to None.

  • -
-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, cache_grid=False, device=None, allow_missing_keys=False, lazy=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • spatial_size (Union[Sequence[int], int, None]) – output image spatial size. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of returning a randomized affine grid. -defaults to 0.1, with 10% chance returns a randomized grid.

  • -
  • rotate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 2 floats for 2D, a tuple of 6 floats for 3D) for affine matrix, -take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select pixel/voxel to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • cache_grid (bool) – whether to cache the identity sampling grid. -If the spatial size is not dynamically defined by input image, enabling this option could -accelerate the transform.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • lazy (bool) – a flag to indicate whether this transform should execute lazily or not. -Defaults to False

  • -
-
-
-
-

See also

-
    -
  • monai.transforms.compose.MapTransform

  • -
  • RandAffineGrid for the random affine parameters configurations.

  • -
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-property lazy#
-

Get whether lazy evaluation is enabled for this transform instance. -:returns: True if the transform is operating in a lazy fashion, False if not.

-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandAffined

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

Rand2DElasticd#

-example of Rand2DElasticd -
-
-class monai.transforms.Rand2DElasticd(keys, spacing, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Rand2DElastic.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, spacing, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • spacing (UnionType[tuple[float, float], float]) – distance in between the control points.

  • -
  • magnitude_range (tuple[float, float]) – 2 int numbers, the random offsets will be generated from -uniform[magnitude[0], magnitude[1]).

  • -
  • spatial_size (Union[int, tuple[int, int], None]) – specifying output image spatial size [h, w]. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, -1) will be adapted -to (32, 64) if the second spatial dimension size of img is 64.

  • -
  • prob (float) – probability of returning a randomized affine grid. -defaults to 0.1, with 10% chance returns a randomized grid, -otherwise returns a spatial_size centered area extracted from the input image.

  • -
  • rotate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 2 floats for 2D) for affine matrix, take a 2D affine as example:

    -
    [
    -    [1.0, params[0], 0.0],
    -    [params[1], 1.0, 0.0],
    -    [0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select pixel to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-

See also

-
    -
  • RandAffineGrid for the random affine parameters configurations.

  • -
  • Affine for the affine transformation parameters configurations.

  • -
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Rand2DElasticd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

Rand3DElasticd#

-example of Rand3DElasticd -
-
-class monai.transforms.Rand3DElasticd(keys, sigma_range, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Rand3DElastic.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, sigma_range, magnitude_range, spatial_size=None, prob=0.1, rotate_range=None, shear_range=None, translate_range=None, scale_range=None, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.REFLECTION, device=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • sigma_range (tuple[float, float]) – a Gaussian kernel with standard deviation sampled from -uniform[sigma_range[0], sigma_range[1]) will be used to smooth the random offset grid.

  • -
  • magnitude_range (tuple[float, float]) – the random offsets on the grid will be generated from -uniform[magnitude[0], magnitude[1]).

  • -
  • spatial_size (Union[tuple[int, int, int], int, None]) – specifying output image spatial size [h, w, d]. -if spatial_size and self.spatial_size are not defined, or smaller than 1, -the transform will use the spatial size of img. -if some components of the spatial_size are non-positive values, the transform will use the -corresponding components of img size. For example, spatial_size=(32, 32, -1) will be adapted -to (32, 32, 64) if the third spatial dimension size of img is 64.

  • -
  • prob (float) – probability of returning a randomized affine grid. -defaults to 0.1, with 10% chance returns a randomized grid, -otherwise returns a spatial_size centered area extracted from the input image.

  • -
  • rotate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – angle range in radians. If element i is a pair of (min, max) values, then -uniform[-rotate_range[i][0], rotate_range[i][1]) will be used to generate the rotation parameter -for the i`th spatial dimension. If not, `uniform[-rotate_range[i], rotate_range[i]) will be used. -This can be altered on a per-dimension basis. E.g., ((0,3), 1, …): for dim0, rotation will be -in range [0, 3], and for dim1 [-1, 1] will be used. Setting a single value will use [-x, x] -for dim0 and nothing for the remaining dimensions.

  • -
  • shear_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) –

    shear range with format matching rotate_range, it defines the range to randomly select -shearing factors(a tuple of 6 floats for 3D) for affine matrix, take a 3D affine as example:

    -
    [
    -    [1.0, params[0], params[1], 0.0],
    -    [params[2], 1.0, params[3], 0.0],
    -    [params[4], params[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • translate_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – translate range with format matching rotate_range, it defines the range to randomly -select voxel to translate for every spatial dims.

  • -
  • scale_range (Union[Sequence[UnionType[tuple[float, float], float]], float, None]) – scaling range with format matching rotate_range. it defines the range to randomly select -the scale factor to translate for every spatial dims. A value of 1.0 is added to the result. -This allows 0 to correspond to no change (i.e., a scaling of 1.0).

  • -
  • mode (Union[Sequence[str], str]) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (Union[Sequence[str], str]) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "reflection". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-

See also

-
    -
  • RandAffineGrid for the random affine parameters configurations.

  • -
  • Affine for the affine transformation parameters configurations.

  • -
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

Rand3DElasticd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

GridDistortiond#

-example of GridDistortiond -
-
-class monai.transforms.GridDistortiond(keys, num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.GridDistortion.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, num_cells, distort_steps, mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • num_cells (UnionType[tuple[int], int]) – number of grid cells on each dimension.

  • -
  • distort_steps (list[tuple]) – This argument is a list of tuples, where each tuple contains the distort steps of the -corresponding dimensions (in the order of H, W[, D]). The length of each tuple equals to num_cells + 1. -Each value in the tuple represents the distort step of the related cell.

  • -
  • mode (str) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

RandGridDistortiond#

-example of RandGridDistortiond -
-
-class monai.transforms.RandGridDistortiond(keys, num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RandGridDistortion.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Tensor]) – a dictionary containing the tensor-like data to be processed. The keys specified -in this dictionary must be tensor like arrays that are channel first and have at most -three spatial dimensions

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

a dictionary containing the transformed data, as well as any other data present in the dictionary

-
-
-
- -
-
-__init__(keys, num_cells=5, prob=0.1, distort_limit=(-0.03, 0.03), mode=GridSampleMode.BILINEAR, padding_mode=GridSamplePadMode.BORDER, device=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed.

  • -
  • num_cells (UnionType[tuple[int], int]) – number of grid cells on each dimension.

  • -
  • prob (float) – probability of returning a randomized grid distortion transform. Defaults to 0.1.

  • -
  • distort_limit (UnionType[tuple[float, float], float]) – range to randomly distort. -If single number, distort_limit is picked from (-distort_limit, distort_limit). -Defaults to (-0.03, 0.03).

  • -
  • mode (str) – {"bilinear", "nearest"} or spline interpolation order 0-5 (integers). -Interpolation mode to calculate output values. Defaults to "bilinear". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When it’s an integer, the numpy (cpu tensor)/cupy (cuda tensor) backends will be used -and the value represents the order of the spline interpolation. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • padding_mode (str) – {"zeros", "border", "reflection"} -Padding mode for outside grid values. Defaults to "border". -See also: https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html -When mode is an integer, using numpy/cupy backends, this argument accepts -{‘reflect’, ‘grid-mirror’, ‘constant’, ‘grid-constant’, ‘nearest’, ‘mirror’, ‘grid-wrap’, ‘wrap’}. -See also: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.map_coordinates.html -It also can be a sequence, each element corresponds to a key in keys.

  • -
  • device (Optional[device, None]) – device on which the tensor will be allocated.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandGridDistortiond

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-
-

Smooth Field (Dict)#

-
-

RandSmoothFieldAdjustContrastd#

-example of RandSmoothFieldAdjustContrastd -
-
-class monai.transforms.RandSmoothFieldAdjustContrastd(keys, spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.5, 4.5), device=None)[source]#
-

Dictionary version of RandSmoothFieldAdjustContrast.

-

The field is randomized once per invocation by default so the same field is applied to every selected key. The -mode parameter specifying interpolation mode for the field can be a single value or a sequence of values with -one for each key in keys.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – key names to apply the augment to

  • -
  • spatial_size (Sequence[int]) – size of input arrays, all arrays stated in keys must have same dimensions

  • -
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • -
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • -
  • mode (Union[Sequence[str], str]) – interpolation mode to use when upsampling

  • -
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • -
  • prob (float) – probability transform is applied

  • -
  • gamma (UnionType[Sequence[float], float]) – (min, max) range for exponential field

  • -
  • device (Optional[device, None]) – Pytorch device to define field on

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Mapping[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandSmoothFieldAdjustContrastd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandSmoothFieldAdjustIntensityd#

-example of RandSmoothFieldAdjustIntensityd -
-
-class monai.transforms.RandSmoothFieldAdjustIntensityd(keys, spatial_size, rand_size, pad=0, mode=InterpolateMode.AREA, align_corners=None, prob=0.1, gamma=(0.1, 1.0), device=None)[source]#
-

Dictionary version of RandSmoothFieldAdjustIntensity.

-

The field is randomized once per invocation by default so the same field is applied to every selected key. The -mode parameter specifying interpolation mode for the field can be a single value or a sequence of values with -one for each key in keys.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – key names to apply the augment to

  • -
  • spatial_size (Sequence[int]) – size of input arrays, all arrays stated in keys must have same dimensions

  • -
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • -
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • -
  • mode (Union[Sequence[str], str]) – interpolation mode to use when upsampling

  • -
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • -
  • prob (float) – probability transform is applied

  • -
  • gamma (UnionType[Sequence[float], float]) – (min, max) range of intensity multipliers

  • -
  • device (Optional[device, None]) – Pytorch device to define field on

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Mapping[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandSmoothFieldAdjustIntensityd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-

RandSmoothDeformd#

-example of RandSmoothDeformd -
-
-class monai.transforms.RandSmoothDeformd(keys, spatial_size, rand_size, pad=0, field_mode=InterpolateMode.AREA, align_corners=None, prob=0.1, def_range=1.0, grid_dtype=torch.float32, grid_mode=GridSampleMode.NEAREST, grid_padding_mode=GridSamplePadMode.BORDER, grid_align_corners=False, device=None)[source]#
-

Dictionary version of RandSmoothDeform.

-

The field is randomized once per invocation by default so the same field is applied to every selected key. The -field_mode parameter specifying interpolation mode for the field can be a single value or a sequence of values -with one for each key in keys. Similarly the grid_mode parameter can be one value or one per key.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – key names to apply the augment to

  • -
  • spatial_size (Sequence[int]) – input array size to which deformation grid is interpolated

  • -
  • rand_size (Sequence[int]) – size of the randomized field to start from

  • -
  • pad (int) – number of pixels/voxels along the edges of the field to pad with 0

  • -
  • field_mode (Union[Sequence[str], str]) – interpolation mode to use when upsampling the deformation field

  • -
  • align_corners (Optional[bool, None]) – if True align the corners when upsampling field

  • -
  • prob (float) – probability transform is applied

  • -
  • def_range (UnionType[Sequence[float], float]) – value of the deformation range in image size fractions

  • -
  • grid_dtype – type for the deformation grid calculated from the field

  • -
  • grid_mode (Union[Sequence[str], str]) – interpolation mode used for sampling input using deformation grid

  • -
  • grid_padding_mode (str) – padding mode used for sampling input using deformation grid

  • -
  • grid_align_corners (UnionType[bool, None]) – if True align the corners when sampling the deformation grid

  • -
  • device (Optional[device, None]) – Pytorch device to define field on

  • -
-
-
-
-
-__call__(data)[source]#
-

data is an element which often comes from an iteration over an -iterable, such as torch.utils.data.Dataset. This method should -return an updated version of data. -To simplify the input validations, most of the transforms assume that

-
    -
  • data is a Numpy ndarray, PyTorch Tensor or string,

  • -
  • the data shape can be:

    -
      -
    1. string data without shape, LoadImage transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChannel expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirst expects (spatial_dim_1[, spatial_dim_2, …], num_channels),

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-

This method can optionally take additional arguments to help execute transformation operation.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

Mapping[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-randomize(data=None)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Return type:
-

None

-
-
-
- -
-
-set_random_state(seed=None, state=None)[source]#
-

Set the random state locally, to control the randomness, the derived -classes should use self.R instead of np.random to introduce random -factors.

-
-
Parameters:
-
    -
  • seed (Optional[int, None]) – set the random state with an integer seed.

  • -
  • state (Optional[RandomState, None]) – set the random state with a np.random.RandomState object.

  • -
-
-
Raises:
-

TypeError – When state is not an Optional[np.random.RandomState].

-
-
Return type:
-

RandSmoothDeformd

-
-
Returns:
-

a Randomizable instance.

-
-
-
- -
- -
-
-
-

MRI transforms (Dict)#

-
-

Kspace under-sampling (Dict)#

-
-
-class monai.apps.reconstruction.transforms.dictionary.RandomKspaceMaskd(keys, center_fractions, accelerations, spatial_dims=2, is_complex=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.apps.reconstruction.transforms.array.RandomKspacemask. -Other mask transforms can inherit from this class, for example: -monai.apps.reconstruction.transforms.dictionary.EquispacedKspaceMaskd.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • center_fractions (Sequence[float]) – Fraction of low-frequency columns to be retained. -If multiple values are provided, then one of these numbers is -chosen uniformly each time.

  • -
  • accelerations (Sequence[float]) – Amount of under-sampling. This should have the -same length as center_fractions. If multiple values are provided, -then one of these is chosen uniformly each time.

  • -
  • spatial_dims (int) – Number of spatial dims (e.g., it’s 2 for a 2D data; it’s -also 2 for pseudo-3D datasets like the fastMRI dataset). -The last spatial dim is selected for sampling. For the fastMRI -dataset, k-space has the form (…,num_slices,num_coils,H,W) -and sampling is done along W. For a general 3D data with the -shape (…,num_coils,H,W,D), sampling is done along D.

  • -
  • is_complex (bool) – if True, then the last dimension will be reserved -for real/imaginary parts.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from the -loaded dataset

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

the new data dictionary

-
-
-
- -
- -
-
-class monai.apps.reconstruction.transforms.dictionary.EquispacedKspaceMaskd(keys, center_fractions, accelerations, spatial_dims=2, is_complex=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of -monai.apps.reconstruction.transforms.array.EquispacedKspaceMask.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • center_fractions (Sequence[float]) – Fraction of low-frequency columns to be retained. -If multiple values are provided, then one of these numbers is -chosen uniformly each time.

  • -
  • accelerations (Sequence[float]) – Amount of under-sampling. This should have the same -length as center_fractions. If multiple values are provided, -then one of these is chosen uniformly each time.

  • -
  • spatial_dims (int) – Number of spatial dims (e.g., it’s 2 for a 2D data; -it’s also 2 for pseudo-3D datasets like the fastMRI dataset). -The last spatial dim is selected for sampling. For the fastMRI -dataset, k-space has the form (…,num_slices,num_coils,H,W) -and sampling is done along W. For a general 3D data with the shape -(…,num_coils,H,W,D), sampling is done along D.

  • -
  • is_complex (bool) – if True, then the last dimension will be reserved -for real/imaginary parts.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from the -loaded dataset

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

the new data dictionary

-
-
-
- -
- -
-
-

ExtractDataKeyFromMetaKeyd#

-
-
-class monai.apps.reconstruction.transforms.dictionary.ExtractDataKeyFromMetaKeyd(keys, meta_key, allow_missing_keys=False)[source]#
-

Moves keys from meta to data. It is useful when a dataset of paired samples -is loaded and certain keys should be moved from meta to data.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys to be transferred from meta to data

  • -
  • meta_key (str) – the meta key where all the meta-data is stored

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing

  • -
-
-
-

Example

-

When the fastMRI dataset is loaded, “kspace” is stored in the data dictionary, -but the ground-truth image with the key “reconstruction_rss” is stored in the meta data. -In this case, ExtractDataKeyFromMetaKeyd moves “reconstruction_rss” to data.

-
-
-__call__(data)[source]#
-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from the -loaded dataset

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

the new data dictionary

-
-
-
- -
- -
-
-

ReferenceBasedSpatialCropd#

-
-
-class monai.apps.reconstruction.transforms.dictionary.ReferenceBasedSpatialCropd(keys, ref_key, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SpatialCrop. -This is similar to monai.transforms.SpatialCropd which is a -general purpose cropper to produce sub-volume region of interest (ROI). -Their difference is that this transform does cropping according to a reference image.

-

If a dimension of the expected ROI size is larger than the input image size, will not crop that dimension.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • ref_key (str) – key of the item to be used to crop items of “keys”

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-

Example

-

In an image reconstruction task, let keys=[“image”] and ref_key=[“target”]. -Also, let data be the data dictionary. Then, ReferenceBasedSpatialCropd -center-crops data[“image”] based on the spatial size of data[“target”] by -calling monai.transforms.SpatialCrop.

-
-
-__call__(data, lazy=None)[source]#
-

This transform can support to crop ND spatial (channel-first) data. -It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D -data point where C is the number of slices)

-
-
Parameters:
-

data (Mapping[Hashable, Tensor]) – is a dictionary containing (key,value) pairs from -the loaded dataset

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

the new data dictionary

-
-
-
- -
- -
-
-

ReferenceBasedNormalizeIntensityd#

-
-
-class monai.apps.reconstruction.transforms.dictionary.ReferenceBasedNormalizeIntensityd(keys, ref_key, subtrahend=None, divisor=None, nonzero=False, channel_wise=False, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of -monai.transforms.NormalizeIntensity. -This is similar to monai.transforms.NormalizeIntensityd -and can normalize non-zero values or the entire image. The difference -is that this transform does normalization according to a reference image.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • ref_key (str) – key of the item to be used to normalize items of “keys”

  • -
  • subtrahend (Union[ndarray, Tensor, None]) – the amount to subtract by (usually the mean)

  • -
  • divisor (Union[ndarray, Tensor, None]) – the amount to divide by (usually the standard deviation)

  • -
  • nonzero (bool) – whether only normalize non-zero values.

  • -
  • channel_wise (bool) – if True, calculate on each channel separately, -otherwise, calculate on the entire image directly. default -to False.

  • -
  • dtype (Union[dtype, type, str, None]) – output data type, if None, same as input image. defaults -to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-

Example

-

In an image reconstruction task, let keys=[“image”, “target”] and ref_key=[“image”]. -Also, let data be the data dictionary. Then, ReferenceBasedNormalizeIntensityd -normalizes data[“target”] and data[“image”] based on the mean-std of data[“image”] by -calling monai.transforms.NormalizeIntensity.

-
-
-__call__(data)[source]#
-

This transform can support to normalize ND spatial (channel-first) data. -It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D -data point where C is the number of slices)

-
-
Parameters:
-

data (Mapping[Hashable, Union[ndarray, Tensor]]) – is a dictionary containing (key,value) pairs from -the loaded dataset

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

the new data dictionary

-
-
-
- -
- -
-
-
-

Lazy (Dict)#

-
-

ApplyPendingd#

-
-
-class monai.transforms.ApplyPendingd(keys)[source]#
-

ApplyPendingd can be inserted into a pipeline that is being executed lazily in order -to ensure resampling happens before the next transform. It doesn’t do anything itself, -but its presence causes the pipeline to be executed as it doesn’t implement LazyTrait

-

See Compose for a detailed explanation of the lazy resampling feature.

-
-
Parameters:
-

keys (Union[Collection[Hashable], Hashable]) – the keys for tensors that should have their pending transforms executed

-
-
-
-
-__call__(data)[source]#
-

Call self as a function.

-
- -
- -
-
-
-

Utility (Dict)#

-
-

Identityd#

-
-
-class monai.transforms.Identityd(keys, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Identity.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

AsChannelFirstd#

-
-
-class monai.transforms.AsChannelFirstd(keys, channel_dim=-1, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.AsChannelFirst.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, channel_dim=-1, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • channel_dim (int) – which dimension of input image is the channel, default is the last dimension.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

AsChannelLastd#

-
-
-class monai.transforms.AsChannelLastd(keys, channel_dim=0, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.AsChannelLast.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, channel_dim=0, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • channel_dim (int) – which dimension of input image is the channel, default is the first dimension.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

AddChanneld#

-
-
-class monai.transforms.AddChanneld(keys, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.AddChannel.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

EnsureChannelFirstd#

-
-
-class monai.transforms.EnsureChannelFirstd(keys, meta_keys=None, meta_key_postfix='meta_dict', strict_check=True, allow_missing_keys=False, channel_dim=None)[source]#
-

Dictionary-based wrapper of monai.transforms.EnsureChannelFirst.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, meta_keys=None, meta_key_postfix='meta_dict', strict_check=True, allow_missing_keys=False, channel_dim=None)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • strict_check (bool) – whether to raise an error when the meta information is insufficient.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • channel_dim – This argument can be used to specify the original channel dimension (integer) of the input array. -It overrides the original_channel_dim from provided MetaTensor input. -If the input array doesn’t have a channel dim, this value should be 'no_channel'. -If this is set to None, this class relies on img or meta_dict to provide the channel dimension.

  • -
-
-
-
- -
- -
-
-

RepeatChanneld#

-
-
-class monai.transforms.RepeatChanneld(keys, repeats, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RepeatChannel.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, repeats, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • repeats (int) – the number of repetitions for each element.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

SplitDimd#

-
-
-class monai.transforms.SplitDimd(keys, output_postfixes=None, dim=0, keepdim=True, update_meta=True, list_output=False, allow_missing_keys=False)[source]#
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

UnionType[dict[Hashable, Tensor], list[dict[Hashable, Tensor]]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, output_postfixes=None, dim=0, keepdim=True, update_meta=True, list_output=False, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • output_postfixes (Optional[Sequence[str], None]) – the postfixes to construct keys to store split data. -for example: if the key of input data is pred and split 2 classes, the output -data keys will be: pred_(output_postfixes[0]), pred_(output_postfixes[1]) -if None, using the index number: pred_0, pred_1, … pred_N.

  • -
  • dim (int) – which dimension of input image is the channel, default to 0.

  • -
  • keepdim (bool) – if True, output will have singleton in the split dimension. If False, this -dimension will be squeezed.

  • -
  • update_meta (bool) – if True, copy [key]_meta_dict for each output and update affine to -reflect the cropped image

  • -
  • list_output (bool) – it True, the output will be a list of dictionaries with the same keys as original.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

SplitChanneld#

-
-
-class monai.transforms.SplitChanneld(keys, output_postfixes=None, channel_dim=0, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SplitChannel. -All the input specified by keys should be split into same count of data.

-
- -
-
-

CastToTyped#

-
-
-class monai.transforms.CastToTyped(keys, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.CastToType.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • dtype (Union[Sequence[Union[dtype, type, str, None, dtype]], dtype, type, str, None, dtype]) – convert image to this data type, default is np.float32. -it also can be a sequence of dtypes or torch.dtype, -each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

ToTensord#

-
-
-class monai.transforms.ToTensord(keys, dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ToTensor.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • dtype (Optional[dtype, None]) – target data content type to convert, for example: torch.float, etc.

  • -
  • device (Optional[device, None]) – specify the target device to put the Tensor data.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • -
  • track_meta (Optional[bool, None]) – if True convert to MetaTensor, otherwise to Pytorch Tensor, -if None behave according to return value of py:func:monai.data.meta_obj.get_track_meta.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-

ToNumpyd#

-
-
-class monai.transforms.ToNumpyd(keys, dtype=None, wrap_sequence=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ToNumpy.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Any]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, dtype=None, wrap_sequence=True, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • dtype (Union[dtype, type, str, None]) – target data type when converting to numpy array.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

ToPIL#

-
-
-class monai.transforms.ToPIL[source]#
-

Converts the input image (in the form of NumPy array or PyTorch Tensor) to PIL image

-
-
-__call__(img)[source]#
-

Apply the transform to img.

-
- -
- -
-
-

ToCupyd#

-
-
-class monai.transforms.ToCupyd(keys, dtype=None, wrap_sequence=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ToCupy.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • dtype (Optional[dtype, None]) – data type specifier. It is inferred from the input by default. -if not None, must be an argument of numpy.dtype, for more details: -https://docs.cupy.dev/en/stable/reference/generated/cupy.array.html.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [array(1), array(2)], if True, then [1, 2] -> array([1, 2]).

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ToPILd#

-
-
-class monai.transforms.ToPILd(keys, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ToNumpy.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Any]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

DeleteItemsd#

-
-
-class monai.transforms.DeleteItemsd(keys, sep='.', use_re=False)[source]#
-

Delete specified items from data dictionary to release memory. -It will remove the key-values and copy the others to construct a new dictionary.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, sep='.', use_re=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to delete, can be “A{sep}B{sep}C” -to delete key C in nested dictionary, C can be regular expression. -See also: monai.transforms.compose.MapTransform

  • -
  • sep (str) – the separator tag to define nested dictionary keys, default to “.”.

  • -
  • use_re (Union[Sequence[bool], bool]) – whether the specified key is a regular expression, it also can be -a list of bool values, mapping them to keys.

  • -
-
-
-
- -
- -
-
-

SelectItemsd#

-
-
-class monai.transforms.SelectItemsd(keys, allow_missing_keys=False)[source]#
-

Select only specified items from data dictionary to release memory. -It will copy the selected key-values and construct a new dictionary.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

FlattenSubKeysd#

-
-
-class monai.transforms.FlattenSubKeysd(keys, sub_keys=None, delete_keys=True, prefix=None)[source]#
-

If an item is dictionary, it flatten the item by moving the sub-items (defined by sub-keys) to the top level. -{“pred”: {“a”: …, “b”, … }} –> {“a”: …, “b”, … }

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be flatten

  • -
  • sub_keys (Union[Collection[Hashable], Hashable, None]) – the sub-keys of items to be flatten. If not provided all the sub-keys are flattened.

  • -
  • delete_keys (bool) – whether to delete the key of the items that their sub-keys are flattened. Default to True.

  • -
  • prefix (Optional[str, None]) – optional prefix to be added to the sub-keys when moving to the top level. -By default no prefix will be added.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

Transposed#

-
-
-class monai.transforms.Transposed(keys, indices, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Transpose.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Any]

-
-
-
- -
- -
-
-

SqueezeDimd#

-
-
-class monai.transforms.SqueezeDimd(keys, dim=0, update_meta=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SqueezeDim.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, dim=0, update_meta=True, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • dim (int) – dimension to be squeezed. Default: 0 (the first dimension)

  • -
  • update_meta (bool) – whether to update the meta info if the input is a metatensor. Default is True.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

DataStatsd#

-
-
-class monai.transforms.DataStatsd(keys, prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.DataStats.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, prefix='Data', data_type=True, data_shape=True, value_range=True, data_value=False, additional_info=None, name='DataStats', allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • prefix (Union[Sequence[str], str]) – will be printed in format: “{prefix} statistics”. -it also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • data_type (Union[Sequence[bool], bool]) – whether to show the type of input data. -it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • data_shape (Union[Sequence[bool], bool]) – whether to show the shape of input data. -it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • value_range (Union[Sequence[bool], bool]) – whether to show the value range of input data. -it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • data_value (Union[Sequence[bool], bool]) – whether to show the raw value of input data. -it also can be a sequence of bool, each element corresponds to a key in keys. -a typical example is to print some properties of Nifti image: affine, pixdim, etc.

  • -
  • additional_info (Union[Sequence[Callable], Callable, None]) – user can define callable function to extract -additional info from input data. it also can be a sequence of string, each element -corresponds to a key in keys.

  • -
  • name (str) – identifier of logging.logger to use, defaulting to “DataStats”.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

SimulateDelayd#

-
-
-class monai.transforms.SimulateDelayd(keys, delay_time=0.0, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.SimulateDelay.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, delay_time=0.0, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • delay_time (Union[Sequence[float], float]) – The minimum amount of time, in fractions of seconds, to accomplish this identity task. -It also can be a sequence of string, each element corresponds to a key in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

CopyItemsd#

-
-
-class monai.transforms.CopyItemsd(keys, times=1, names=None, allow_missing_keys=False)[source]#
-

Copy specified items from data dictionary and save with different key names. -It can copy several items together and copy several times.

-
-
-__call__(data)[source]#
-
-
Raises:
-

KeyError – When a key in self.names already exists in data.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(keys, times=1, names=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • times (int) – expected copy times, for example, if keys is “img”, times is 3, -it will add 3 copies of “img” data to the dictionary, default to 1.

  • -
  • names (Union[Collection[Hashable], Hashable, None]) – the names corresponding to the newly copied data, -the length should match len(keys) x times. for example, if keys is [“img”, “seg”] -and times is 2, names can be: [“img_1”, “seg_1”, “img_2”, “seg_2”]. -if None, use “{key}_{index}” as key for copy times N, index from 0 to N-1.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
Raises:
-
    -
  • ValueError – When times is nonpositive.

  • -
  • ValueError – When len(names) is not len(keys) * times. Incompatible values.

  • -
-
-
-
- -
- -
-
-

ConcatItemsd#

-
-
-class monai.transforms.ConcatItemsd(keys, name, dim=0, allow_missing_keys=False)[source]#
-

Concatenate specified items from data dictionary together on the first dim to construct a big array. -Expect all the items are numpy array or PyTorch Tensor or MetaTensor. -Return the first input’s meta information when items are MetaTensor.

-
-
-__call__(data)[source]#
-
-
Raises:
-
    -
  • TypeError – When items in data differ in type.

  • -
  • TypeError – When the item type is not in Union[numpy.ndarray, torch.Tensor, MetaTensor].

  • -
-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-__init__(keys, name, dim=0, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be concatenated together. -See also: monai.transforms.compose.MapTransform

  • -
  • name (str) – the name corresponding to the key to store the concatenated data.

  • -
  • dim (int) – on which dimension to concatenate the items, default is 0.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

Lambdad#

-
-
-class monai.transforms.Lambdad(keys, func, inv_func=<function no_collation>, track_meta=True, overwrite=True, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.Lambda.

-

For example:

-
input_data={'image': np.zeros((10, 2, 2)), 'label': np.ones((10, 2, 2))}
-lambd = Lambdad(keys='label', func=lambda x: x[:4, :, :])
-print(lambd(input_data)['label'].shape)
-(4, 2, 2)
-
-
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • func (Union[Sequence[Callable], Callable]) – Lambda/function to be applied. It also can be a sequence of Callable, -each element corresponds to a key in keys.

  • -
  • inv_func (Union[Sequence[Callable], Callable]) – Lambda/function of inverse operation if want to invert transforms, default to lambda x: x. -It also can be a sequence of Callable, each element corresponds to a key in keys.

  • -
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) -as opposed to MONAI’s enhanced objects. By default, this is True.

  • -
  • overwrite (Union[Sequence[bool], bool, Sequence[str], str]) – whether to overwrite the original data in the input dictionary with lambda function output. it -can be bool or str, when setting to str, it will create a new key for the output and keep the value of -key intact. default to True. it also can be a sequence of bool or str, each element corresponds to a key -in keys.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
Note: The inverse operation doesn’t allow to define extra_info or access other information, such as the

image’s original size. If need these complicated information, please write a new InvertibleTransform directly.

-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
-
- -
- -
-
-

RandLambdad#

-
-
-class monai.transforms.RandLambdad(keys, func, inv_func=<function no_collation>, track_meta=True, overwrite=True, prob=1.0, allow_missing_keys=False)[source]#
-

Randomizable version monai.transforms.Lambdad, the input func may contain random logic, -or randomly execute the function based on prob. so CacheDataset will not execute it and cache the results.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • func (Union[Sequence[Callable], Callable]) – Lambda/function to be applied. It also can be a sequence of Callable, -each element corresponds to a key in keys.

  • -
  • inv_func (Union[Sequence[Callable], Callable]) – Lambda/function of inverse operation if want to invert transforms, default to lambda x: x. -It also can be a sequence of Callable, each element corresponds to a key in keys.

  • -
  • track_meta (bool) – If False, then standard data objects will be returned (e.g., torch.Tensor` and np.ndarray) -as opposed to MONAI’s enhanced objects. By default, this is True.

  • -
  • overwrite (Union[Sequence[bool], bool]) – whether to overwrite the original data in the input dictionary with lambda function output. -default to True. it also can be a sequence of bool, each element corresponds to a key in keys.

  • -
  • prob (float) – probability of executing the random function, default to 1.0, with 100% probability to execute. -note that all the data specified by keys will share the same random probability to execute or not.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-

For more details, please check monai.transforms.Lambdad.

-
-
Note: The inverse operation doesn’t allow to define extra_info or access other information, such as the

image’s original size. If need these complicated information, please write a new InvertibleTransform directly.

-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
-
- -
- -
-
-

RemoveRepeatedChanneld#

-
-
-class monai.transforms.RemoveRepeatedChanneld(keys, repeats, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.RemoveRepeatedChannel.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, repeats, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • repeats (int) – the number of repetitions for each element.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

LabelToMaskd#

-
-
-class monai.transforms.LabelToMaskd(keys, select_labels, merge_channels=False, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.LabelToMask.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • select_labels (Union[Sequence[int], int]) – labels to generate mask from. for 1 channel label, the select_labels -is the expected label values, like: [1, 2, 3]. for One-Hot format label, the -select_labels is the expected channel indices.

  • -
  • merge_channels (bool) – whether to use np.any() to merge the result on channel dim. -if yes, will return a single channel mask with binary data.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

FgBgToIndicesd#

-
-
-class monai.transforms.FgBgToIndicesd(keys, fg_postfix='_fg_indices', bg_postfix='_bg_indices', image_key=None, image_threshold=0.0, output_shape=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.FgBgToIndices.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • fg_postfix (str) – postfix to save the computed foreground indices in dict. -for example, if computed on label and postfix = “_fg_indices”, the key will be label_fg_indices.

  • -
  • bg_postfix (str) – postfix to save the computed background indices in dict. -for example, if computed on label and postfix = “_bg_indices”, the key will be label_bg_indices.

  • -
  • image_key (Optional[str, None]) – if image_key is not None, use label == 0 & image > image_threshold to determine -the negative sample(background). so the output items will not map to all the voxels in the label.

  • -
  • image_threshold (float) – if enabled image_key, use image > image_threshold to determine -the valid image content area and select background only in this area.

  • -
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ClassesToIndicesd#

-
-
-class monai.transforms.ClassesToIndicesd(keys, indices_postfix='_cls_indices', num_classes=None, image_key=None, image_threshold=0.0, output_shape=None, max_samples_per_class=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ClassesToIndices.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • indices_postfix (str) – postfix to save the computed indices of all classes in dict. -for example, if computed on label and postfix = “_cls_indices”, the key will be label_cls_indices.

  • -
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • -
  • image_key (Optional[str, None]) – if image_key is not None, use image > image_threshold to define valid region, and only select -the indices within the valid region.

  • -
  • image_threshold (float) – if enabled image_key, use image > image_threshold to determine the valid image content -area and select only the indices of classes in this area.

  • -
  • output_shape (Optional[Sequence[int], None]) – expected shape of output indices. if not None, unravel indices to specified shape.

  • -
  • max_samples_per_class (Optional[int, None]) – maximum length of indices to sample in each class to reduce memory consumption. -Default is None, no subsampling.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ConvertToMultiChannelBasedOnBratsClassesd#

-
-
-class monai.transforms.ConvertToMultiChannelBasedOnBratsClassesd(keys, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.ConvertToMultiChannelBasedOnBratsClasses. -Convert labels to multi channels based on brats18 classes: -label 1 is the necrotic and non-enhancing tumor core -label 2 is the peritumoral edema -label 4 is the GD-enhancing tumor -The possible classes are TC (Tumor core), WT (Whole tumor) -and ET (Enhancing tumor).

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

AddExtremePointsChanneld#

-
-
-class monai.transforms.AddExtremePointsChanneld(keys, label_key, background=0, pert=0.0, sigma=3.0, rescale_min=-1.0, rescale_max=1.0, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.AddExtremePointsChannel.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • label_key (str) – key to label source to get the extreme points.

  • -
  • background (int) – Class index of background label, defaults to 0.

  • -
  • pert (float) – Random perturbation amount to add to the points, defaults to 0.0.

  • -
  • sigma (Union[Sequence[float], float, Sequence[Tensor], Tensor]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

  • -
  • rescale_min (float) – minimum value of output data.

  • -
  • rescale_max (float) – maximum value of output data.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

Call self as a function.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
-
-randomize(label)[source]#
-

Within this method, self.R should be used, instead of np.random, to introduce random factors.

-

all self.R calls happen here so that we have a better chance to -identify errors of sync the random state.

-

This method can generate the random factors based on properties of the input data.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

TorchVisiond#

-
-
-class monai.transforms.TorchVisiond(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.TorchVision for non-randomized transforms. -For randomized transforms of TorchVision use monai.transforms.RandTorchVisiond.

-
-

Note

-

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.

-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • name (str) – The transform name in TorchVision package.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • args – parameters for the TorchVision transform.

  • -
  • kwargs – parameters for the TorchVision transform.

  • -
-
-
-
- -
- -
-
-

RandTorchVisiond#

-
-
-class monai.transforms.RandTorchVisiond(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.TorchVision for randomized transforms. -For deterministic non-randomized transforms of TorchVision use monai.transforms.TorchVisiond.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • name (str) – The transform name in TorchVision package.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • args – parameters for the TorchVision transform.

  • -
  • kwargs – parameters for the TorchVision transform.

  • -
-
-
-
-

Note

-
    -
  • 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 should call ToTensord transform first to convert Numpy to Tensor.

  • -
  • This class inherits the Randomizable purely to prevent any dataset caching to skip the transform -computation. If the random factor of the underlying torchvision transform is not derived from self.R, -the results may not be deterministic. See Also: monai.transforms.Randomizable.

  • -
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

MapLabelValued#

-
-
-class monai.transforms.MapLabelValued(keys, orig_labels, target_labels, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.MapLabelValue.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, orig_labels, target_labels, dtype=<class 'numpy.float32'>, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • orig_labels (Sequence) – original labels that map to others.

  • -
  • target_labels (Sequence) – expected label values, 1: 1 map to the orig_labels.

  • -
  • dtype (Union[dtype, type, str, None]) – convert the output data to dtype, default to float32.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

EnsureTyped#

-
-
-class monai.transforms.EnsureTyped(keys, data_type='tensor', dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.EnsureType.

-

Ensure the input data to be a PyTorch Tensor or numpy array, support: numpy array, PyTorch Tensor, -float, int, bool, string and object keep the original. -If passing a dictionary, list or tuple, still return dictionary, list or tuple and recursively convert -every item to the expected data type if wrap_sequence=False.

-

Note: Currently, we only convert tensor data to numpy array or scalar number in the inverse operation.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, data_type='tensor', dtype=None, device=None, wrap_sequence=True, track_meta=None, allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • data_type (str) – target data type to convert, should be “tensor” or “numpy”.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – target data content type to convert, for example: np.float32, torch.float, etc.

  • -
  • device (Optional[device, None]) – for Tensor data type, specify the target device.

  • -
  • wrap_sequence (bool) – if False, then lists will recursively call this function, default to True. -E.g., if False, [1, 2] -> [tensor(1), tensor(2)], if True, then [1, 2] -> tensor([1, 2]).

  • -
  • track_meta (Optional[bool, None]) – whether to convert to MetaTensor when data_type is “tensor”. -If False, the output data type will be torch.Tensor. Default to the return value of get_track_meta.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
- -
-
-

IntensityStatsd#

-
-
-class monai.transforms.IntensityStatsd(keys, ops, key_prefix, mask_keys=None, channel_wise=False, meta_keys=None, meta_key_postfix='meta_dict', allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.IntensityStats. -Compute statistics for the intensity values of input image and store into the metadata dictionary. -For example: if ops=[lambda x: np.mean(x), “max”] and key_prefix=”orig”, may generate below stats: -{“orig_custom_0”: 1.5, “orig_max”: 3.0}.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • ops (Sequence[UnionType[str, Callable]]) – expected operations to compute statistics for the intensity. -if a string, will map to the predefined operations, supported: [“mean”, “median”, “max”, “min”, “std”] -mapping to np.nanmean, np.nanmedian, np.nanmax, np.nanmin, np.nanstd. -if a callable function, will execute the function on input image.

  • -
  • key_prefix (str) – the prefix to combine with ops name to generate the key to store the results in the -metadata dictionary. if some ops are callable functions, will use “{key_prefix}_custom_{index}” -as the key, where index counts from 0.

  • -
  • mask_keys (Union[Collection[Hashable], Hashable, None]) – if not None, specify the mask array for the image to extract only the interested area to compute -statistics, mask must have the same shape as the image. -it should be a sequence of strings or None, map to the keys.

  • -
  • channel_wise (bool) – whether to compute statistics for every channel of input image separately. -if True, return a list of values for every operation, default to False.

  • -
  • meta_keys (Union[Collection[Hashable], Hashable, None]) – explicitly indicate the key of the corresponding metadata dictionary. -used to store the computed statistics to the meta dict. -for example, for data with key image, the metadata by default is in image_meta_dict. -the metadata is a dictionary object which contains: filename, original_shape, etc. -it can be a sequence of string, map to the keys. -if None, will try to construct meta_keys by key_{meta_key_postfix}.

  • -
  • meta_key_postfix (str) – if meta_keys is None, use key_{postfix} to fetch the metadata according -to the key data, default is meta_dict, the metadata is a dictionary object. -used to store the computed statistics to the meta dict.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ToDeviced#

-
-
-class monai.transforms.ToDeviced(keys, device, allow_missing_keys=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.ToDevice.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Tensor]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, device, allow_missing_keys=False, **kwargs)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • device (UnionType[device, str]) – target device to move the Tensor, for example: “cuda:1”.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
  • kwargs – other args for the PyTorch Tensor.to() API, for more details: -https://pytorch.org/docs/stable/generated/torch.Tensor.to.html.

  • -
-
-
-
- -
- -
-
-

CuCIMd#

-
-
-class monai.transforms.CuCIMd(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.CuCIM for non-randomized transforms. -For randomized transforms of CuCIM use monai.transforms.RandCuCIMd.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • name (str) – The transform name in CuCIM package.

  • -
  • allow_missing_keys (bool) – 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.

-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data – Dict[Hashable, cupy.ndarray]

-
-
Returns:
-

Dict[Hashable, cupy.ndarray]

-
-
-
- -
- -
-
-

RandCuCIMd#

-
-
-class monai.transforms.RandCuCIMd(keys, name, allow_missing_keys=False, *args, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.CuCIM for randomized transforms. -For deterministic non-randomized transforms of CuCIM use monai.transforms.CuCIMd.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • name (str) – The transform name in CuCIM package.

  • -
  • allow_missing_keys (bool) – 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 should call ToCuPy transform first 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: monai.transforms.Randomizable.

  • -
-
-
-
-__call__(data)[source]#
-
-
Parameters:
-

data – Dict[Hashable, cupy.ndarray]

-
-
Returns:
-

Dict[Hashable, cupy.ndarray]

-
-
-
- -
- -
-
-

AddCoordinateChannelsd#

-
-
-class monai.transforms.AddCoordinateChannelsd(keys, spatial_dims, allow_missing_keys=False)[source]#
-

Dictionary-based wrapper of monai.transforms.AddCoordinateChannels.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • spatial_dims (Sequence[int]) – the spatial dimensions that are to have their coordinates encoded in a channel and -appended to the input image. E.g., (0, 1, 2) represents H, W, D dims and append three channels -to the input image, encoding the coordinates of the input’s three spatial dimensions.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
-

Deprecated since version 0.8.0: spatial_channels is deprecated, use spatial_dims instead.

-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

ImageFilterd#

-
-
-class monai.transforms.ImageFilterd(keys, kernel, kernel_size=None, allow_missing_keys=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.ImageFilter.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • kernel (Union[str, ndarray, Tensor]) – A string specifying the kernel or a custom kernel as torch.Tenor or np.ndarray. -Available options are: mean, laplacian, elliptical, sobel_{w,h,d}`

  • -
  • kernel_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic kernel. -Computational complexity increases exponentially with kernel_size, which -should be considered when choosing the kernel size.

  • -
  • allow_missing_keys (bool) – Don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-

RandImageFilterd#

-
-
-class monai.transforms.RandImageFilterd(keys, kernel, kernel_size=None, prob=0.1, allow_missing_keys=False, **kwargs)[source]#
-

Dictionary-based wrapper of monai.transforms.RandomFilterKernel.

-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.MapTransform

  • -
  • kernel (Union[str, ndarray, Tensor]) – A string specifying the kernel or a custom kernel as torch.Tenor or np.ndarray. -Available options are: mean, laplacian, elliptical, sobel_{w,h,d}`

  • -
  • kernel_size (Optional[int, None]) – A single integer value specifying the size of the quadratic or cubic kernel. -Computational complexity increases exponentially with kernel_size, which -should be considered when choosing the kernel size.

  • -
  • prob (float) – Probability the transform is applied to the data

  • -
  • allow_missing_keys (bool) – Don’t raise exception if key is missing.

  • -
-
-
-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
- -
-
-
-

MetaTensor#

-
-

ToMetaTensord#

-
-
-class monai.transforms.ToMetaTensord(keys, allow_missing_keys=False)[source]#
-

Dictionary-based transform to convert a dictionary to MetaTensor.

-

If input is {“a”: torch.Tensor, “a_meta_dict”: dict, “b”: …}, then output will -have the form {“a”: MetaTensor, “b”: MetaTensor}.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-

FromMetaTensord#

-
-
-class monai.transforms.FromMetaTensord(keys, data_type='tensor', allow_missing_keys=False)[source]#
-

Dictionary-based transform to convert MetaTensor to a dictionary.

-

If input is {“a”: MetaTensor, “b”: MetaTensor}, then output will -have the form {“a”: torch.Tensor, “a_meta_dict”: dict, “a_transforms”: list, “b”: …}.

-
-
-__call__(data)[source]#
-

data often comes from an iteration over an iterable, -such as torch.utils.data.Dataset.

-

To simplify the input validations, this method assumes:

-
    -
  • data is a Python dictionary,

  • -
  • data[key] is a Numpy ndarray, PyTorch Tensor or string, where key is an element -of self.keys, the data shape can be:

    -
      -
    1. string data without shape, LoadImaged transform expects file paths,

    2. -
    3. most of the pre-/post-processing transforms expect: (num_channels, spatial_dim_1[, spatial_dim_2, ...]), -except for example: AddChanneld expects (spatial_dim_1[, spatial_dim_2, …]) and -AsChannelFirstd expects (spatial_dim_1[, spatial_dim_2, …], num_channels)

    4. -
    -
  • -
  • the channel dimension is often not omitted even if number of channels is one.

  • -
-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
Returns:
-

An updated dictionary version of data by applying the transform.

-
-
-
- -
-
-__init__(keys, data_type='tensor', allow_missing_keys=False)[source]#
-
-
Parameters:
-
    -
  • keys (Union[Collection[Hashable], Hashable]) – keys of the corresponding items to be transformed. -See also: monai.transforms.compose.MapTransform

  • -
  • data_type (UnionType[Sequence[str], str]) – target data type to convert, should be “tensor” or “numpy”.

  • -
  • allow_missing_keys (bool) – don’t raise exception if key is missing.

  • -
-
-
-
- -
-
-inverse(data)[source]#
-

Inverse of __call__.

-
-
Raises:
-

NotImplementedError – When the subclass does not override this method.

-
-
Return type:
-

dict[Hashable, Union[ndarray, Tensor]]

-
-
-
- -
- -
-
-
-
-

Transform Adaptors#

-
-

How to use the adaptor function#

-

The key to using ‘adaptor’ lies in understanding the function that want to -adapt. The ‘inputs’ and ‘outputs’ parameters take either strings, lists/tuples -of strings or a dictionary mapping strings, depending on call signature of the -function being called.

-

The adaptor function is written to minimise the cognitive load on the caller. -There should be a minimal number of cases where the caller has to set anything -on the input parameter, and for functions that return a single value, it is -only necessary to name the dictionary keyword to which that value is assigned.

-
-

Use of outputs#

-

outputs can take either a string, a list/tuple of string or a dict of string -to string, depending on what the transform being adapted returns:

-
-
    -
  • If the transform returns a single argument, then outputs can be supplied a -string that indicates what key to assign the return value to in the -dictionary

  • -
  • If the transform returns a list/tuple of values, then outputs can be supplied -a list/tuple of the same length. The strings in outputs map the return value -at the corresponding position to a key in the dictionary

  • -
  • If the transform returns a dictionary of values, then outputs must be supplied -a dictionary that maps keys in the function’s return dictionary to the -dictionary being passed between functions

  • -
-
-

Note, the caller is free to use a more complex way of specifying the outputs -parameter than is required. The following are synonymous and will be treated -identically:

-
# single argument
-adaptor(MyTransform(), 'image')
-adaptor(MyTransform(), ['image'])
-adaptor(MyTransform(), {'image': 'image'})
-
-# multiple arguments
-adaptor(MyTransform(), ['image', 'label'])
-adaptor(MyTransform(), {'image': 'image', 'label': 'label'})
-
-
-
-
-

Use of inputs#

-

inputs can usually be omitted when using adaptor. It is only required when a -the function’s parameter names do not match the names in the dictionary that is -used to chain transform calls.

-
class MyTransform1:
-    def __call__(self, image):
-        # do stuff to image
-        return image + 1
-
-
-class MyTransform2:
-    def __call__(self, img_dict):
-        # do stuff to image
-        img_dict["image"] += 1
-        return img_dict
-
-
-xform = Compose([adaptor(MyTransform1(), "image"), MyTransform2()])
-d = {"image": 1}
-print(xform(d))
-
->>> {'image': 3}
-
-
-
class MyTransform3:
-    def __call__(self, img_dict):
-        # do stuff to image
-        img_dict["image"] -= 1
-        img_dict["segment"] = img_dict["image"]
-        return img_dict
-
-
-class MyTransform4:
-    def __call__(self, img, seg):
-        # do stuff to image
-        img -= 1
-        seg -= 1
-        return img, seg
-
-
-xform = Compose([MyTransform3(), adaptor(MyTransform4(), ["img", "seg"], {"image": "img", "segment": "seg"})])
-d = {"image": 1}
-print(xform(d))
-
->>> {'image': 0, 'segment': 0, 'img': -1, 'seg': -1}
-
-
-

Inputs:

-
    -
  • dictionary in: None | Name maps

  • -
  • params in (match): None | Name list | Name maps

  • -
  • params in (mismatch): Name maps

  • -
  • params & **kwargs (match) : None | Name maps

  • -
  • params & **kwargs (mismatch) : Name maps

  • -
-

Outputs:

-
    -
  • dictionary out: None | Name maps

  • -
  • list/tuple out: list/tuple

  • -
  • variable out: string

  • -
-
-
-
-

FunctionSignature#

-
-
-class monai.transforms.adaptors.FunctionSignature(function)[source]#
-
- -
-
-

adaptor#

-
-
-monai.transforms.adaptors.adaptor(function, outputs, inputs=None)[source]#
-
- -
-
-

apply_alias#

-
-
-monai.transforms.adaptors.apply_alias(fn, name_map)[source]#
-
- -
-
-

to_kwargs#

-
-
-monai.transforms.adaptors.to_kwargs(fn)[source]#
-
- -
-
-
-

Utilities#

-
-
-class monai.transforms.utils.Fourier[source]#
-

Helper class storing Fourier mappings

-
-
-static inv_shift_fourier(k, spatial_dims, n_dims=None)[source]#
-

Applies inverse shift and fourier transform. Only the spatial -dimensions are transformed.

-
-
Parameters:
-
    -
  • k (Union[ndarray, Tensor]) – K-space data.

  • -
  • spatial_dims (int) – Number of spatial dimensions.

  • -
-
-
Returns:
-

Tensor in image space.

-
-
Return type:
-

x

-
-
-
- -
-
-static shift_fourier(x, spatial_dims)[source]#
-

Applies fourier transform and shifts the zero-frequency component to the -center of the spectrum. Only the spatial dimensions get transformed.

-
-
Parameters:
-
    -
  • x (Union[ndarray, Tensor]) – Image to transform.

  • -
  • spatial_dims (int) – Number of spatial dimensions.

  • -
-
-
-
-
Returns

k: K-space data.

-
-
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
- -
-
-monai.transforms.utils.allow_missing_keys_mode(transform)[source]#
-

Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states.

-
-
Parameters:
-

transform (UnionType[MapTransform, Compose, tuple[MapTransform], tuple[Compose]]) – either MapTransform or a Compose

-
-
-

Example:

-
data = {"image": np.arange(16, dtype=float).reshape(1, 4, 4)}
-t = SpatialPadd(["image", "label"], 10, allow_missing_keys=False)
-_ = t(data)  # would raise exception
-with allow_missing_keys_mode(t):
-    _ = t(data)  # OK!
-
-
-
- -
-
-monai.transforms.utils.attach_hook(func, hook, mode='pre')[source]#
-

Adds hook before or after a func call. If mode is “pre”, the wrapper will call hook then func. -If the mode is “post”, the wrapper will call func then hook.

-
- -
-
-monai.transforms.utils.check_boundaries(boundaries)[source]#
-

Check boundaries for Signal transforms

-
-
Return type:
-

None

-
-
-
- -
-
-monai.transforms.utils.check_non_lazy_pending_ops(input_array, name=None, raise_error=False)[source]#
-

Check whether the input array has pending operations, raise an error or warn when it has.

-
-
Parameters:
-
    -
  • input_array (Union[ndarray, Tensor]) – input array to be checked.

  • -
  • name (Optional[str, None]) – an optional name to be included in the error message.

  • -
  • raise_error (bool) – whether to raise an error, default to False, a warning message will be issued instead.

  • -
-
-
Return type:
-

None

-
-
-
- -
-
-monai.transforms.utils.compute_divisible_spatial_size(spatial_shape, k)[source]#
-

Compute the target spatial size which should be divisible by k.

-
-
Parameters:
-
    -
  • spatial_shape (Sequence[int]) – original spatial shape.

  • -
  • k (UnionType[Sequence[int], int]) – the target k for each spatial dimension. -if k is negative or 0, the original size is preserved. -if k is an int, the same k be applied to all the input spatial dimensions.

  • -
-
-
-
- -
-
-monai.transforms.utils.convert_applied_interp_mode(trans_info, mode='nearest', align_corners=None)[source]#
-

Recursively change the interpolation mode in the applied operation stacks, default to “nearest”.

-

See also: monai.transform.inverse.InvertibleTransform

-
-
Parameters:
-
    -
  • trans_info – applied operation stack, tracking the previously applied invertible transform.

  • -
  • mode (str) – target interpolation mode to convert, default to “nearest” as it’s usually used to save the mode output.

  • -
  • align_corners (Optional[bool, None]) – target align corner value in PyTorch interpolation API, need to align with the mode.

  • -
-
-
-
- -
-
-monai.transforms.utils.convert_pad_mode(dst, mode)[source]#
-

Utility to convert padding mode between numpy array and PyTorch Tensor.

-
-
Parameters:
-
    -
  • dst (Union[ndarray, Tensor]) – target data to convert padding mode for, should be numpy array or PyTorch Tensor.

  • -
  • mode (UnionType[str, None]) – current padding mode.

  • -
-
-
-
- -
-
-monai.transforms.utils.convert_to_contiguous(data, **kwargs)[source]#
-

Check and ensure the numpy array or PyTorch Tensor in data to be contiguous in memory.

-
-
Parameters:
-
-
-
Return type:
-

Union[ndarray, Tensor, Mapping, Sequence[Any]]

-
-
-
- -
-
-monai.transforms.utils.copypaste_arrays(src_shape, dest_shape, srccenter, destcenter, dims)[source]#
-

Calculate the slices to copy a sliced area of array in src_shape into array in dest_shape.

-

The area has dimensions dims (use 0 or None to copy everything in that dimension), -the source area is centered at srccenter index in src and copied into area centered at destcenter in dest. -The dimensions of the copied area will be clipped to fit within the -source and destination arrays so a smaller area may be copied than expected. Return value is the tuples of slice -objects indexing the copied area in src, and those indexing the copy area in dest.

-

Example

-
src_shape = (6,6)
-src = np.random.randint(0,10,src_shape)
-dest = np.zeros_like(src)
-srcslices, destslices = copypaste_arrays(src_shape, dest.shape, (3, 2),(2, 1),(3, 4))
-dest[destslices] = src[srcslices]
-print(src)
-print(dest)
-
->>> [[9 5 6 6 9 6]
-     [4 3 5 6 1 2]
-     [0 7 3 2 4 1]
-     [3 0 0 1 5 1]
-     [9 4 7 1 8 2]
-     [6 6 5 8 6 7]]
-    [[0 0 0 0 0 0]
-     [7 3 2 4 0 0]
-     [0 0 1 5 0 0]
-     [4 7 1 8 0 0]
-     [0 0 0 0 0 0]
-     [0 0 0 0 0 0]]
-
-
-
-
Return type:
-

tuple[tuple[slice, …], tuple[slice, …]]

-
-
-
- -
-
-monai.transforms.utils.create_control_grid(spatial_shape, spacing, homogeneous=True, dtype=<class 'float'>, device=None, backend=TransformBackends.NUMPY)[source]#
-

control grid with two additional point in each direction

-
- -
-
-monai.transforms.utils.create_grid(spatial_size, spacing=None, homogeneous=True, dtype=<class 'float'>, device=None, backend=TransformBackends.NUMPY)[source]#
-

compute a spatial_size mesh.

-
-
    -
  • when homogeneous=True, the output shape is (N+1, dim_size_1, dim_size_2, …, dim_size_N)

  • -
  • when homogeneous=False, the output shape is (N, dim_size_1, dim_size_2, …, dim_size_N)

  • -
-
-
-
Parameters:
-
    -
  • spatial_size (Sequence[int]) – spatial size of the grid.

  • -
  • spacing (Optional[Sequence[float], None]) – same len as spatial_size, defaults to 1.0 (dense grid).

  • -
  • homogeneous (bool) – whether to make homogeneous coordinates.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – output grid data type, defaults to float.

  • -
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • -
  • backend – APIs to use, numpy or torch.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils.create_rotate(spatial_dims, radians, device=None, backend=TransformBackends.NUMPY)[source]#
-

create a 2D or 3D rotation matrix

-
-
Parameters:
-
    -
  • spatial_dims (int) – {2, 3} spatial rank

  • -
  • radians (UnionType[Sequence[float], float]) – rotation radians -when spatial_dims == 3, the radians sequence corresponds to -rotation in the 1st, 2nd, and 3rd dim respectively.

  • -
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • -
  • backend (str) – APIs to use, numpy or torch.

  • -
-
-
Raises:
-
    -
  • ValueError – When radians is empty.

  • -
  • ValueError – When spatial_dims is not one of [2, 3].

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils.create_scale(spatial_dims, scaling_factor, device=None, backend=TransformBackends.NUMPY)[source]#
-

create a scaling matrix

-
-
Parameters:
-
    -
  • spatial_dims (int) – spatial rank

  • -
  • scaling_factor (UnionType[Sequence[float], float]) – scaling factors for every spatial dim, defaults to 1.

  • -
  • device (Union[device, str, None]) – device to compute and store the output (when the backend is “torch”).

  • -
  • backend – APIs to use, numpy or torch.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils.create_shear(spatial_dims, coefs, device=None, backend=TransformBackends.NUMPY)[source]#
-

create a shearing matrix

-
-
Parameters:
-
    -
  • spatial_dims (int) – spatial rank

  • -
  • coefs (UnionType[Sequence[float], float]) –

    shearing factors, a tuple of 2 floats for 2D, a tuple of 6 floats for 3D), -take a 3D affine as example:

    -
    [
    -    [1.0, coefs[0], coefs[1], 0.0],
    -    [coefs[2], 1.0, coefs[3], 0.0],
    -    [coefs[4], coefs[5], 1.0, 0.0],
    -    [0.0, 0.0, 0.0, 1.0],
    -]
    -
    -
    -

  • -
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • -
  • backend – APIs to use, numpy or torch.

  • -
-
-
Raises:
-

NotImplementedError – When spatial_dims is not one of [2, 3].

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils.create_translate(spatial_dims, shift, device=None, backend=TransformBackends.NUMPY)[source]#
-

create a translation matrix

-
-
Parameters:
-
    -
  • spatial_dims (int) – spatial rank

  • -
  • shift (UnionType[Sequence[float], float]) – translate pixel/voxel for every spatial dim, defaults to 0.

  • -
  • device (Optional[device, None]) – device to compute and store the output (when the backend is “torch”).

  • -
  • backend – APIs to use, numpy or torch.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils.equalize_hist(img, mask=None, num_bins=256, min=0, max=255)[source]#
-

Utility to equalize input image based on the histogram. -If skimage installed, will leverage skimage.exposure.histogram, otherwise, use -np.histogram instead.

-
-
Parameters:
-
    -
  • img (ndarray) – input image to equalize.

  • -
  • mask (Optional[ndarray, None]) – if provided, must be ndarray of bools or 0s and 1s, and same shape as image. -only points at which mask==True are used for the equalization.

  • -
  • num_bins (int) – number of the bins to use in histogram, default to 256. for more details: -https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.

  • -
  • min (int) – the min value to normalize input image, default to 0.

  • -
  • max (int) – the max value to normalize input image, default to 255.

  • -
-
-
Return type:
-

ndarray

-
-
-
- -
-
-monai.transforms.utils.extreme_points_to_image(points, label, sigma=0.0, rescale_min=-1.0, rescale_max=1.0)[source]#
-

Please refer to monai.transforms.AddExtremePointsChannel for the usage.

-

Applies a gaussian filter to the extreme points image. Then the pixel values in points image are rescaled -to range [rescale_min, rescale_max].

-
-
Parameters:
-
    -
  • points (list[tuple[int, …]]) – Extreme points of the object/organ.

  • -
  • label (Union[ndarray, Tensor]) – label image to get extreme points from. Shape must be -(1, spatial_dim1, [, spatial_dim2, …]). Doesn’t support one-hot labels.

  • -
  • sigma (UnionType[Sequence[float], float, Sequence[Tensor], Tensor]) – if a list of values, must match the count of spatial dimensions of input data, -and apply every value in the list to 1 spatial dimension. if only 1 value provided, -use it for all spatial dimensions.

  • -
  • rescale_min (float) – minimum value of output data.

  • -
  • rescale_max (float) – maximum value of output data.

  • -
-
-
Return type:
-

Tensor

-
-
-
- -
-
-monai.transforms.utils.fill_holes(img_arr, applied_labels=None, connectivity=None)[source]#
-

Fill the holes in the provided image.

-

The label 0 will be treated as background and the enclosed holes will be set to the neighboring class label. -What is considered to be an enclosed hole is defined by the connectivity. -Holes on the edge are always considered to be open (not enclosed).

-
-

Note

-

The performance of this method heavily depends on the number of labels. -It is a bit faster if the list of applied_labels is provided. -Limiting the number of applied_labels results in a big decrease in processing time.

-

If the image is one-hot-encoded, then the applied_labels need to match the channel index.

-
-
-
Parameters:
-
    -
  • img_arr (ndarray) – numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

  • -
  • applied_labels (Optional[Iterable[int], None]) – Labels for which to fill holes. Defaults to None, -that is filling holes for all labels.

  • -
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to -consider a pixel/voxel as a neighbor. Accepted values are ranging from 1 to input.ndim. -Defaults to a full connectivity of input.ndim.

  • -
-
-
Return type:
-

ndarray

-
-
Returns:
-

numpy array of shape [C, spatial_dim1[, spatial_dim2, …]].

-
-
-
- -
-
-monai.transforms.utils.generate_label_classes_crop_centers(spatial_size, num_samples, label_spatial_shape, indices, ratios=None, rand_state=None, allow_smaller=False, warn=True)[source]#
-

Generate valid sample locations based on the specified ratios of label classes. -Valid: samples sitting entirely within image, expected input shape: [C, H, W, D] or [C, H, W]

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the ROIs to be sampled.

  • -
  • num_samples (int) – total sample centers to be generated.

  • -
  • label_spatial_shape (Sequence[int]) – spatial shape of the original label data to unravel selected centers.

  • -
  • indices (Sequence[Union[ndarray, Tensor]]) – sequence of pre-computed foreground indices of every class in 1 dimension.

  • -
  • ratios (Optional[list[UnionType[float, int]], None]) – ratios of every class in the label to generate crop centers, including background class. -if None, every class will have the same ratio to generate crop centers.

  • -
  • rand_state (Optional[RandomState, None]) – numpy randomState object to align with other modules.

  • -
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than -the requested ROI in any dimension. If True, any smaller dimensions will be set to -match the cropped size (i.e., no cropping in that dimension).

  • -
  • warn (bool) – if True prints a warning if a class is not present in the label.

  • -
-
-
Return type:
-

tuple[tuple]

-
-
-
- -
-
-monai.transforms.utils.generate_pos_neg_label_crop_centers(spatial_size, num_samples, pos_ratio, label_spatial_shape, fg_indices, bg_indices, rand_state=None, allow_smaller=False)[source]#
-

Generate valid sample locations based on the label with option for specifying foreground ratio -Valid: samples sitting entirely within image, expected input shape: [C, H, W, D] or [C, H, W]

-
-
Parameters:
-
    -
  • spatial_size (UnionType[Sequence[int], int]) – spatial size of the ROIs to be sampled.

  • -
  • num_samples (int) – total sample centers to be generated.

  • -
  • pos_ratio (float) – ratio of total locations generated that have center being foreground.

  • -
  • label_spatial_shape (Sequence[int]) – spatial shape of the original label data to unravel selected centers.

  • -
  • fg_indices (Union[ndarray, Tensor]) – pre-computed foreground indices in 1 dimension.

  • -
  • bg_indices (Union[ndarray, Tensor]) – pre-computed background indices in 1 dimension.

  • -
  • rand_state (Optional[RandomState, None]) – numpy randomState object to align with other modules.

  • -
  • allow_smaller (bool) – if False, an exception will be raised if the image is smaller than -the requested ROI in any dimension. If True, any smaller dimensions will be set to -match the cropped size (i.e., no cropping in that dimension).

  • -
-
-
Raises:
-
    -
  • ValueError – When the proposed roi is larger than the image.

  • -
  • ValueError – When the foreground and background indices lengths are 0.

  • -
-
-
Return type:
-

tuple[tuple]

-
-
-
- -
-
-monai.transforms.utils.generate_spatial_bounding_box(img, select_fn=<function is_positive>, channel_indices=None, margin=0, allow_smaller=True)[source]#
-

Generate the spatial bounding box of foreground in the image with start-end positions (inclusive). -Users can define arbitrary function to select expected foreground from the whole image or specified channels. -And it can also add margin to every dim of the bounding box. -The output format of the coordinates is:

-
-

[1st_spatial_dim_start, 2nd_spatial_dim_start, …, Nth_spatial_dim_start], -[1st_spatial_dim_end, 2nd_spatial_dim_end, …, Nth_spatial_dim_end]

-
-

If allow_smaller, the bounding boxes edges are aligned with the input image edges. -This function returns [0, 0, …], [0, 0, …] if there’s no positive intensity.

-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – a “channel-first” image of shape (C, spatial_dim1[, spatial_dim2, …]) to generate bounding box from.

  • -
  • select_fn (Callable) – function to select expected foreground, default is to select values > 0.

  • -
  • channel_indices (Union[Iterable[int], int, None]) – if defined, select foreground only on the specified channels -of image. if None, select foreground on the whole image.

  • -
  • margin (UnionType[Sequence[int], int]) – add margin value to spatial dims of the bounding box, if only 1 value provided, use it for all dims.

  • -
  • allow_smaller (bool) – when computing box size with margin, whether allow the image size to be smaller -than box size, default to True.

  • -
-
-
Return type:
-

tuple[list[int], list[int]]

-
-
-
- -
-
-monai.transforms.utils.get_extreme_points(img, rand_state=None, background=0, pert=0.0)[source]#
-

Generate extreme points from an image. These are used to generate initial segmentation -for annotation models. An optional perturbation can be passed to simulate user clicks.

-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – Image to generate extreme points from. Expected Shape is (spatial_dim1, [, spatial_dim2, ...]).

  • -
  • rand_state (Optional[RandomState, None]) – np.random.RandomState object used to select random indices.

  • -
  • background (int) – Value to be consider as background, defaults to 0.

  • -
  • pert (float) – Random perturbation amount to add to the points, defaults to 0.0.

  • -
-
-
Return type:
-

list[tuple[int, …]]

-
-
Returns:
-

A list of extreme points, its length is equal to 2 * spatial dimension of input image. -The output format of the coordinates is:

-

[1st_spatial_dim_min, 1st_spatial_dim_max, 2nd_spatial_dim_min, …, Nth_spatial_dim_max]

-

-
-
Raises:
-

ValueError – When the input image does not have any foreground pixel.

-
-
-
- -
-
-monai.transforms.utils.get_largest_connected_component_mask(img, connectivity=None, num_components=1)[source]#
-

Gets the largest connected component mask of an image.

-
-
Parameters:
-
    -
  • img (~NdarrayTensor) – Image to get largest connected component from. Shape is (spatial_dim1 [, spatial_dim2, …])

  • -
  • connectivity (Optional[int, None]) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. If None, a full -connectivity of input.ndim is used. for more details: -https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label.

  • -
  • num_components (int) – The number of largest components to preserve.

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils.get_number_image_type_conversions(transform, test_data, key=None)[source]#
-

Get the number of times that the data need to be converted (e.g., numpy to torch). -Conversions between different devices are also counted (e.g., CPU to GPU).

-
-
Parameters:
-
    -
  • transform (Compose) – composed transforms to be tested

  • -
  • test_data (Any) – data to be used to count the number of conversions

  • -
  • key (Optional[Hashable, None]) – if using dictionary transforms, this key will be used to check the number of conversions.

  • -
-
-
Return type:
-

int

-
-
-
- -
-
-monai.transforms.utils.get_transform_backends()[source]#
-

Get the backends of all MONAI transforms.

-
-
Returns:
-

Dictionary, where each key is a transform, and its -corresponding values are a boolean list, stating -whether that transform supports (1) torch.Tensor, -and (2) np.ndarray as input without needing to -convert.

-
-
-
- -
-
-monai.transforms.utils.get_unique_labels(img, is_onehot, discard=None)[source]#
-

Get list of non-background labels in an image.

-
-
Parameters:
-
    -
  • img (Union[ndarray, Tensor]) – Image to be processed. Shape should be [C, W, H, [D]] with C=1 if not onehot else num_classes.

  • -
  • is_onehot (bool) – Boolean as to whether input image is one-hotted. If one-hotted, only return channels with

  • -
  • discard (Union[Iterable[int], int, None]) – Can be used to remove labels (e.g., background). Can be any value, sequence of values, or -None (nothing is discarded).

  • -
-
-
Return type:
-

set[int]

-
-
Returns:
-

Set of labels

-
-
-
- -
-
-monai.transforms.utils.img_bounds(img)[source]#
-

Returns the minimum and maximum indices of non-zero lines in axis 0 of img, followed by that for axis 1.

-
- -
-
-monai.transforms.utils.in_bounds(x, y, margin, maxx, maxy)[source]#
-

Returns True if (x,y) is within the rectangle (margin, margin, maxx-margin, maxy-margin).

-
-
Return type:
-

bool

-
-
-
- -
-
-monai.transforms.utils.is_empty(img)[source]#
-

Returns True if img is empty, that is its maximum value is not greater than its minimum.

-
-
Return type:
-

bool

-
-
-
- -
-
-monai.transforms.utils.is_positive(img)[source]#
-

Returns a boolean version of img where the positive values are converted into True, the other values are False.

-
- -
-
-monai.transforms.utils.is_tensor_invertible(data)[source]#
-

Checks whether a given tensor is invertible. The rules are as follows:

-
    -
  1. If the tensor is not a MetaTensor, it is not invertible

  2. -
  3. If the tensor is a MetaTensor but it has TraceStatusKeys.PENDING_DURING_APPLY in the TraceKeys.STATUS of any -of its applied_operations it is not invertible

  4. -
  5. Otherwise, it is invertible

  6. -
-

This function also accepts:

-
    -
  • dictionaries of tensors

  • -
  • lists or tuples of tensors

  • -
  • list or tuples of dictionaries of tensors

  • -
-

In any of the above scenarios, it iterates through the collections and executes itself recursively until it is -operating on tensors.

-
-
Parameters:
-

data (Tensor) – a torch.Tensor or MetaTensor or collections of torch.Tensor or MetaTensor, as described above

-
-
Returns:
-

A tuple. The first entry is False or True. The second entry is the status messages that can be used for the -user to help debug their pipelines.

-
-
-
- -
-
-monai.transforms.utils.map_binary_to_indices(label, image=None, image_threshold=0.0)[source]#
-

Compute the foreground and background of input label data, return the indices after fattening. -For example: -label = np.array([[[0, 1, 1], [1, 0, 1], [1, 1, 0]]]) -foreground indices = np.array([1, 2, 3, 5, 6, 7]) and background indices = np.array([0, 4, 8])

-
-
Parameters:
-
    -
  • label (Union[ndarray, Tensor]) – use the label data to get the foreground/background information.

  • -
  • image (Union[ndarray, Tensor, None]) – if image is not None, use label = 0 & image > image_threshold -to define background. so the output items will not map to all the voxels in the label.

  • -
  • image_threshold (float) – if enabled image, use image > image_threshold to -determine the valid image content area and select background only in this area.

  • -
-
-
Return type:
-

tuple[Union[ndarray, Tensor], Union[ndarray, Tensor]]

-
-
-
- -
-
-monai.transforms.utils.map_classes_to_indices(label, num_classes=None, image=None, image_threshold=0.0, max_samples_per_class=None)[source]#
-

Filter out indices of every class of the input label data, return the indices after fattening. -It can handle both One-Hot format label and Argmax format label, must provide num_classes for -Argmax label.

-

For example: -label = np.array([[[0, 1, 2], [2, 0, 1], [1, 2, 0]]]) and num_classes=3, will return a list -which contains the indices of the 3 classes: -[np.array([0, 4, 8]), np.array([1, 5, 6]), np.array([2, 3, 7])]

-
-
Parameters:
-
    -
  • label (Union[ndarray, Tensor]) – use the label data to get the indices of every class.

  • -
  • num_classes (Optional[int, None]) – number of classes for argmax label, not necessary for One-Hot label.

  • -
  • image (Union[ndarray, Tensor, None]) – if image is not None, only return the indices of every class that are within the valid -region of the image (image > image_threshold).

  • -
  • image_threshold (float) – if enabled image, use image > image_threshold to -determine the valid image content area and select class indices only in this area.

  • -
  • max_samples_per_class (Optional[int, None]) – maximum length of indices in each class to reduce memory consumption. -Default is None, no subsampling.

  • -
-
-
Return type:
-

list[Union[ndarray, Tensor]]

-
-
-
- -
-
-monai.transforms.utils.map_spatial_axes(img_ndim, spatial_axes=None, channel_first=True)[source]#
-

Utility to map the spatial axes to real axes in channel first/last shape. -For example: -If channel_first is True, and img has 3 spatial dims, map spatial axes to real axes as below: -None -> [1, 2, 3] -[0, 1] -> [1, 2] -[0, -1] -> [1, -1] -If channel_first is False, and img has 3 spatial dims, map spatial axes to real axes as below: -None -> [0, 1, 2] -[0, 1] -> [0, 1] -[0, -1] -> [0, -2]

-
-
Parameters:
-
    -
  • img_ndim (int) – dimension number of the target image.

  • -
  • spatial_axes (Union[Sequence[int], int, None]) – spatial axes to be converted, default is None. -The default None will convert to all the spatial axes of the image. -If axis is negative it counts from the last to the first axis. -If axis is a tuple of ints.

  • -
  • channel_first (bool) – the image data is channel first or channel last, default to channel first.

  • -
-
-
Return type:
-

list[int]

-
-
-
- -
-
-monai.transforms.utils.print_transform_backends()[source]#
-

Prints a list of backends of all MONAI transforms.

-
- -
-
-monai.transforms.utils.rand_choice(prob=0.5)[source]#
-

Returns True if a randomly chosen number is less than or equal to prob, by default this is a 50/50 chance.

-
-
Return type:
-

bool

-
-
-
- -
-
-monai.transforms.utils.remove_small_objects(img, min_size=64, connectivity=1, independent_channels=True)[source]#
-

Use skimage.morphology.remove_small_objects to remove small objects from images. -See: https://scikit-image.org/docs/dev/api/skimage.morphology.html#remove-small-objects.

-

Data should be one-hotted.

-
-
Parameters:
-
    -
  • img (~NdarrayTensor) – image to process. Expected shape: C, H,W,[D]. Expected to only have singleton channel dimension, -i.e., not be one-hotted. Converted to type int.

  • -
  • min_size (int) – objects smaller than this size are removed.

  • -
  • connectivity (int) – Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. -Accepted values are ranging from 1 to input.ndim. If None, a full -connectivity of input.ndim is used. For more details refer to linked scikit-image -documentation.

  • -
  • independent_channels (bool) – Whether to consider each channel independently.

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils.rescale_array(arr, minv=0.0, maxv=1.0, dtype=<class 'numpy.float32'>)[source]#
-

Rescale the values of numpy array arr to be from minv to maxv. -If either minv or maxv is None, it returns (a - min_a) / (max_a - min_a).

-
-
Parameters:
-
    -
  • arr (Union[ndarray, Tensor]) – input array to rescale.

  • -
  • minv (UnionType[float, None]) – minimum value of target rescaled array.

  • -
  • maxv (UnionType[float, None]) – maxmum value of target rescaled array.

  • -
  • dtype (Union[dtype, type, str, None, dtype]) – if not None, convert input array to dtype before computation.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils.rescale_array_int_max(arr, dtype=<class 'numpy.uint16'>)[source]#
-

Rescale the array arr to be between the minimum and maximum values of the type dtype.

-
-
Return type:
-

ndarray

-
-
-
- -
-
-monai.transforms.utils.rescale_instance_array(arr, minv=0.0, maxv=1.0, dtype=<class 'numpy.float32'>)[source]#
-

Rescale each array slice along the first dimension of arr independently.

-
-
Return type:
-

ndarray

-
-
-
- -
-
-monai.transforms.utils.reset_ops_id(data)[source]#
-

find MetaTensors in list or dict data and (in-place) set TraceKeys.ID to Tracekeys.NONE.

-
- -
-
-monai.transforms.utils.resize_center(img, *resize_dims, fill_value=0.0, inplace=True)[source]#
-

Resize img by cropping or expanding the image from the center. The resize_dims values are the output dimensions -(or None to use original dimension of img). If a dimension is smaller than that of img then the result will be -cropped and if larger padded with zeros, in both cases this is done relative to the center of img. The result is -a new image with the specified dimensions and values from img copied into its center.

-
- -
-
-monai.transforms.utils.resolves_modes(interp_mode='constant', padding_mode='zeros', backend=TransformBackends.TORCH, **kwargs)[source]#
-

Automatically adjust the resampling interpolation mode and padding mode, -so that they are compatible with the corresponding API of the backend. -Depending on the availability of the backends, when there’s no exact -equivalent, a similar mode is returned.

-
-
Parameters:
-
    -
  • interp_mode (UnionType[str, None]) – interpolation mode.

  • -
  • padding_mode – padding mode.

  • -
  • backend – optional backend of TransformBackends. If None, the backend will be decided from interp_mode.

  • -
  • kwargs – additional keyword arguments. currently support torch_interpolate_spatial_nd, to provide -additional information to determine linear, bilinear and trilinear; -use_compiled to use MONAI’s precompiled backend (pytorch c++ extensions), default to False.

  • -
-
-
-
- -
-
-monai.transforms.utils.scale_affine(spatial_size, new_spatial_size, centered=True)[source]#
-

Compute the scaling matrix according to the new spatial size

-
-
Parameters:
-
    -
  • spatial_size – original spatial size.

  • -
  • new_spatial_size – new spatial size.

  • -
  • centered (bool) – whether the scaling is with respect to the image center (True, default) or corner (False).

  • -
-
-
Returns:
-

the scaling matrix.

-
-
-
- -
-
-monai.transforms.utils.sync_meta_info(key, data_dict, t=True)[source]#
-

Given the key, sync up between metatensor data_dict[key] and meta_dict data_dict[key_transforms/meta_dict]. -t=True: the one with more applied_operations in metatensor vs meta_dict is the output, False: less is the output.

-
- -
-
-monai.transforms.utils.weighted_patch_samples(spatial_size, w, n_samples=1, r_state=None)[source]#
-

Computes n_samples of random patch sampling locations, given the sampling weight map w and patch spatial_size.

-
-
Parameters:
-
    -
  • spatial_size (UnionType[int, Sequence[int]]) – length of each spatial dimension of the patch.

  • -
  • w (Union[ndarray, Tensor]) – weight map, the weights must be non-negative. each element denotes a sampling weight of the spatial location. -0 indicates no sampling. -The weight map shape is assumed (spatial_dim_0, spatial_dim_1, ..., spatial_dim_n).

  • -
  • n_samples (int) – number of patch samples

  • -
  • r_state (Optional[RandomState, None]) – a random state container

  • -
-
-
Return type:
-

list

-
-
Returns:
-

a list of n_samples N-D integers representing the spatial sampling location of patches.

-
-
-
- -
-
-monai.transforms.utils.zero_margins(img, margin)[source]#
-

Returns True if the values within margin indices of the edges of img in dimensions 1 and 2 are 0.

-
-
Return type:
-

bool

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.allclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)[source]#
-

np.allclose with equivalent implementation for torch.

-
-
Return type:
-

bool

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.any_np_pt(x, axis)[source]#
-

np.any with equivalent implementation for torch.

-

For pytorch, convert to boolean for compatibility with older versions.

-
-
Parameters:
-
    -
  • x (Union[ndarray, Tensor]) – input array/tensor.

  • -
  • axis (UnionType[int, Sequence[int]]) – axis to perform any over.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Return a contiguous flattened array/tensor.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.argsort(a, axis=-1)[source]#
-

np.argsort with equivalent implementation for torch.

-
-
Parameters:
-
    -
  • a (~NdarrayTensor) – the array/tensor to sort.

  • -
  • axis (UnionType[int, None]) – axis along which to sort.

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
Returns:
-

Array/Tensor of indices that sort a along the specified axis.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.argwhere(a)[source]#
-

np.argwhere with equivalent implementation for torch.

-
-
Parameters:
-

a (~NdarrayTensor) – input data.

-
-
Return type:
-

~NdarrayTensor

-
-
Returns:
-

Indices of elements that are non-zero. Indices are grouped by element. -This array will have shape (N, a.ndim) where N is the number of non-zero items.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.ascontiguousarray(x, **kwargs)[source]#
-

np.ascontiguousarray with equivalent implementation for torch (contiguous).

-
-
Parameters:
-
-
-
Return type:
-

Union[ndarray, Tensor, ~T]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.clip(a, a_min, a_max)[source]#
-

np.clip with equivalent implementation for torch.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.concatenate(to_cat, axis=0, out=None)[source]#
-

np.concatenate with equivalent implementation for torch (torch.cat).

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.cumsum(a, axis=None, **kwargs)[source]#
-

np.cumsum with equivalent implementation for torch.

-
-
Parameters:
-
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.floor_divide(a, b)[source]#
-

np.floor_divide with equivalent implementation for torch.

-

As of pt1.8, use torch.div(…, rounding_mode=”floor”), and -before that, use torch.floor_divide.

-
-
Parameters:
-
    -
  • a (Union[ndarray, Tensor]) – first array/tensor

  • -
  • b – scalar to divide by

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Element-wise floor division between two arrays/tensors.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.in1d(x, y)[source]#
-

np.in1d with equivalent implementation for torch.

-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.isfinite(x)[source]#
-

np.isfinite with equivalent implementation for torch.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.isnan(x)[source]#
-

np.isnan with equivalent implementation for torch.

-
-
Parameters:
-

x (Union[ndarray, Tensor]) – array/tensor.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.max(x, dim=None, **kwargs)[source]#
-

torch.max with equivalent implementation for numpy

-
-
Parameters:
-

x (~NdarrayTensor) – array/tensor.

-
-
Return type:
-

~NdarrayTensor

-
-
Returns:
-

the maximum of x.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.maximum(a, b)[source]#
-

np.maximum with equivalent implementation for torch.

-
-
Parameters:
-
    -
  • a (Union[ndarray, Tensor]) – first array/tensor.

  • -
  • b (Union[ndarray, Tensor]) – second array/tensor.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Element-wise maximum between two arrays/tensors.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.mean(x, dim=None, **kwargs)[source]#
-

torch.mean with equivalent implementation for numpy

-
-
Parameters:
-

x (~NdarrayTensor) – array/tensor.

-
-
Return type:
-

~NdarrayTensor

-
-
Returns:
-

the mean of x

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.median(x, dim=None, **kwargs)[source]#
-

torch.median with equivalent implementation for numpy

-
-
Parameters:
-

x (~NdarrayTensor) – array/tensor.

-
-
-
-
Returns

the median of x.

-
-
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.min(x, dim=None, **kwargs)[source]#
-

torch.min with equivalent implementation for numpy

-
-
Parameters:
-

x (~NdarrayTensor) – array/tensor.

-
-
Return type:
-

~NdarrayTensor

-
-
Returns:
-

the minimum of x.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.mode(x, dim=-1, to_long=True)[source]#
-

torch.mode with equivalent implementation for numpy.

-
-
Parameters:
-
    -
  • x (~NdarrayTensor) – array/tensor.

  • -
  • dim (int) – dimension along which to perform mode (referred to as axis by numpy).

  • -
  • to_long (bool) – convert input to long before performing mode.

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.moveaxis(x, src, dst)[source]#
-

moveaxis for pytorch and numpy

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.nonzero(x)[source]#
-

np.nonzero with equivalent implementation for torch.

-
-
Parameters:
-

x (Union[ndarray, Tensor]) – array/tensor.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Index unravelled for given shape

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.percentile(x, q, dim=None, keepdim=False, **kwargs)[source]#
-

np.percentile with equivalent implementation for torch.

-

Pytorch uses quantile. For more details please refer to: -https://pytorch.org/docs/stable/generated/torch.quantile.html. -https://numpy.org/doc/stable/reference/generated/numpy.percentile.html.

-
-
Parameters:
-
    -
  • x (Union[ndarray, Tensor]) – input data.

  • -
  • q – percentile to compute (should in range 0 <= q <= 100).

  • -
  • dim (Optional[int, None]) – the dim along which the percentiles are computed. default is to compute the percentile -along a flattened version of the array.

  • -
  • keepdim (bool) – whether the output data has dim retained or not.

  • -
  • kwargs – if x is numpy array, additional args for np.percentile, more details: -https://numpy.org/doc/stable/reference/generated/numpy.percentile.html.

  • -
-
-
Return type:
-

Union[ndarray, Tensor, float, int]

-
-
Returns:
-

Resulting value (scalar)

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.ravel(x)[source]#
-

np.ravel with equivalent implementation for torch.

-
-
Parameters:
-

x (Union[ndarray, Tensor]) – array/tensor to ravel.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Return a contiguous flattened array/tensor.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.repeat(a, repeats, axis=None, **kwargs)[source]#
-

np.repeat with equivalent implementation for torch (repeat_interleave).

-
-
Parameters:
-
    -
  • a (Union[ndarray, Tensor]) – input data to repeat.

  • -
  • repeats (int) – number of repetitions for each element, repeats is broadcast to fit the shape of the given axis.

  • -
  • axis (Optional[int, None]) – axis along which to repeat values.

  • -
  • kwargs – if a is PyTorch Tensor, additional args for torch.repeat_interleave, more details: -https://pytorch.org/docs/stable/generated/torch.repeat_interleave.html.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.searchsorted(a, v, right=False, sorter=None, **kwargs)[source]#
-

np.searchsorted with equivalent implementation for torch.

-
-
Parameters:
-
    -
  • a (~NdarrayTensor) – numpy array or tensor, containing monotonically increasing sequence on the innermost dimension.

  • -
  • v (Union[ndarray, Tensor]) – containing the search values.

  • -
  • right – if False, return the first suitable location that is found, if True, return the last such index.

  • -
  • sorter – if a is numpy array, optional array of integer indices that sort array a into ascending order.

  • -
  • kwargs – if a is PyTorch Tensor, additional args for torch.searchsorted, more details: -https://pytorch.org/docs/stable/generated/torch.searchsorted.html.

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.stack(x, dim)[source]#
-

np.stack with equivalent implementation for torch.

-
-
Parameters:
-
    -
  • x (Sequence[~NdarrayTensor]) – array/tensor.

  • -
  • dim (int) – dimension along which to perform the stack (referred to as axis by numpy).

  • -
-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.std(x, dim=None, unbiased=False)[source]#
-

torch.std with equivalent implementation for numpy

-
-
Parameters:
-

x (~NdarrayTensor) – array/tensor.

-
-
Return type:
-

~NdarrayTensor

-
-
Returns:
-

the standard deviation of x.

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.unique(x, **kwargs)[source]#
-

torch.unique with equivalent implementation for numpy.

-
-
Parameters:
-

x (~NdarrayTensor) – array/tensor.

-
-
Return type:
-

~NdarrayTensor

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.unravel_index(idx, shape)[source]#
-

np.unravel_index with equivalent implementation for torch.

-
-
Parameters:
-
    -
  • idx – index to unravel.

  • -
  • shape – shape of array/tensor.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Index unravelled for given shape

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.unravel_indices(idx, shape)[source]#
-

Computing unravel coordinates from indices.

-
-
Parameters:
-
    -
  • idx – a sequence of indices to unravel.

  • -
  • shape – shape of array/tensor.

  • -
-
-
Return type:
-

Union[ndarray, Tensor]

-
-
Returns:
-

Stacked indices unravelled for given shape

-
-
-
- -
-
-monai.transforms.utils_pytorch_numpy_unification.where(condition, x=None, y=None)[source]#
-

Note that torch.where may convert y.dtype to x.dtype.

-
-
Return type:
-

Union[ndarray, Tensor]

-
-
-
- -
-
- - -
- - - - - -
- - - -
- - -
-
- -
- -
-
-
- - - - - -
- - -
- - From 4c8463017ed9321ad0f97ba66ac1cecbae5a0d9e Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 10:42:16 +0100 Subject: [PATCH 157/175] Removing dict check from ApplyPendingd Signed-off-by: Ben Murray --- monai/transforms/lazy/dictionary.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 7abb0ea026..9fc38362cd 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -35,15 +35,9 @@ def __init__(self, keys: KeysCollection): super().__init__(keys) def __call__(self, data): - if not isinstance(data, dict): - raise ValueError(f"'data' must be of type dict but is '{type(data)}'") - return data def inverse(self, data): - if not isinstance(data, dict): - raise ValueError(f"'data' must be of type dict but is '{type(data)}'") - return data From e0210b60759ef00377845256deba895a36d0f735 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 10:51:38 +0100 Subject: [PATCH 158/175] Adding extra documentation for apply_pending_transforms_in_order Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index 26d384f619..e336bbc801 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -142,6 +142,13 @@ def apply_pending_transforms_in_order( pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this is expected to change in future releases. + Evaluation of pending transforms is performed under the following circumstances: + * If the transform is a lazy transform and: + * The transform checks data as part of its execution, or + * the transform is not executing lazily + * If the transform is an ApplyPending[d] transform + * If the transform is not a lazy transform + This method is designed to be used only in the context of implementing lazy resampling functionality. In general you should not need to interact with or use this method directly, and its API may change without warning between releases. See the :ref:`Lazy Resampling topic for more information about lazy resampling. From fcbd9b71b33727f0a070b34118afaf2f6c821eb2 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 11:23:58 +0100 Subject: [PATCH 159/175] Refactoring the code in monai.transforms.lazy.executors into monai.transforms.lazy.functional Signed-off-by: Ben Murray --- monai/transforms/compose.py | 2 +- monai/transforms/lazy/executors.py | 330 ++++++++++++++-------------- monai/transforms/lazy/functional.py | 175 ++++++++++++++- monai/transforms/lazy/utils.py | 3 +- monai/transforms/transform.py | 2 +- 5 files changed, 342 insertions(+), 170 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index d62633fb3a..0a9e91613f 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -27,7 +27,7 @@ from monai.transforms.inverse import InvertibleTransform # For backwards compatibility (so this still works: from monai.transforms.compose import MapTransform) -from monai.transforms.lazy.executors import apply_pending_transforms +from monai.transforms.lazy.functional import apply_pending_transforms from monai.transforms.traits import ThreadUnsafe from monai.transforms.transform import ( # noqa: F401 LazyTransform, diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py index e336bbc801..989a57434f 100644 --- a/monai/transforms/lazy/executors.py +++ b/monai/transforms/lazy/executors.py @@ -22,168 +22,168 @@ from monai.transforms.traits import LazyTrait from monai.transforms.transform import MapTransform -__all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order"] - - -def _log_pending_info( - transform: Any, - data: Any, - activity: str, - *, - lazy: bool | None = None, - key: str | None = None, - logger_name: bool | str = False, -): - if logger_name is False: - return - logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" - logger = get_logger(logger_name) - - tcname = type(transform).__name__ - if isinstance(transform, LazyTrait): - tlazy = f", transform.lazy: {transform.lazy}" - if lazy is not None and lazy != transform.lazy: - tlazy += " (overridden)" - else: - tlazy = ", transform is not lazy" - - msg = f"{activity} - lazy: {lazy}, {{key_msg}}pending: {{pcount}}, upcoming '{tcname}'{tlazy}" - - if isinstance(transform, MapTransform): - transform_keys = transform.keys if key is None else (key,) - for k in transform_keys: - if k in data: - pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 - logger.info(msg.format(pcount=pcount, key_msg=f"key: '{k}', ")) - else: - pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 - logger.info(msg.format(pcount=pcount, key_msg="" if key is None else f"key: '{key}', ")) - - -def _log_applied_info(data: Any, key=None, logger_name: bool | str = False): - if logger_name is False: - return - logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" - logger = get_logger(logger_name) - - key_str = "" if key is None else f"key: '{key}', " - logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") - - -def apply_pending_transforms( - data: NdarrayOrTensor | Sequence[Any | NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], - keys: tuple | None, - overrides: dict | None = None, - logger_name: bool | str = False, -): - """ - apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain - tensors. - - When operating on a dictionary of tensors, the 'keys' parameter determines what tensors should be checked. - If 'keys' is not set, all keys of 'data' are considered. - - This method optionally takes a set of overrides that can be used to change specific parameters on the - transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used - to override the default logger, to provide telemetry during the execution of pending transforms. - - This method is intended primarily for use by ``execute_compose`` and other methods that handle the - underlying execution of transform pipelines. You should not need to use it in the general case, unless - you are developing functionality to perform such operations. - - Args: - data: a ``torch.Tensor`` or ``MetaTensor``, or dictionary of tensors. - keys: an optional tuple of keys that filters the keys on 'data' if it is a dict - overrides: An optional dictionary that specifies parameters that can be used to override transform - arguments when they are called. When 'data' is a dict, this dictionary should contain a dictionary - of overrides for each key that needs them - logger_name: An optional name for a logger to be used when applying pending transforms. If None, - logging is suppressed. - Returns: - an object of the same type as data if pending transforms were applied, or 'data' if they were not - """ - if isinstance(data, list): - return [apply_pending_transforms(d, keys, overrides, logger_name) for d in data] - if isinstance(data, tuple): - return tuple(apply_pending_transforms(d, keys, overrides, logger_name) for d in data) - - if isinstance(data, dict): - # get the keys from 'data' for metatensors with pending operations. If 'keys' is set, select - # only data keys that are in 'keys' - active_keys = [k for k in data.keys() if keys is None or k in keys] - keys_to_update = [k for k in active_keys if isinstance(data[k], MetaTensor) and data[k].has_pending_operations] - - if len(keys_to_update) > 0: - rdata = dict(data) - - for k in keys_to_update: - overrides_ = None if overrides is None else overrides.get(k, None) - rdata[k], _ = apply_pending(data[k], overrides=overrides_) - _log_applied_info(rdata[k], key=k, logger_name=logger_name) - - return rdata - else: - if isinstance(data, MetaTensor) and data.has_pending_operations: - rdata, _ = apply_pending(data, overrides=overrides) - _log_applied_info(rdata, logger_name=logger_name) - return rdata - - return data - - -def apply_pending_transforms_in_order( - transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str = False -): - """ - This method causes "in order" processing of pending transforms to occur. - "in order" processing of pending transforms ensures that all pending transforms have been applied to the - tensor before a non-lazy transform (or lazy transform that is executing non-lazily) is carried out. - It ensures that no operations will be added to a metatensor's apply_operations while there are outstanding - pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this - is expected to change in future releases. - - Evaluation of pending transforms is performed under the following circumstances: - * If the transform is a lazy transform and: - * The transform checks data as part of its execution, or - * the transform is not executing lazily - * If the transform is an ApplyPending[d] transform - * If the transform is not a lazy transform - - This method is designed to be used only in the context of implementing lazy resampling functionality. In general - you should not need to interact with or use this method directly, and its API may change without warning between - releases. See the :ref:`Lazy Resampling topic for more information about lazy resampling. - - Args: - transform: a transform that should be evaluated to determine whether pending transforms should be applied - data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may - need to be applied - lazy: The lazy mode that is being applied (this can be False, True or None) - overrides: An optional dictionary containing overrides to be applied to the pending transforms when they - are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that - needs them - logger_name: An optional name for a logger to be used when applying pending transforms. If None, - logging is suppressed. - Returns: - an object of the same type as data if pending transforms were applied, or 'data' if they were not - - """ - keys = None - if isinstance(transform, LazyTrait): - if transform.checks_data: - apply_pending = True - else: - apply_pending = not (transform.lazy if lazy is None else lazy) - elif isinstance(transform, ApplyPending): - apply_pending = True - elif isinstance(transform, ApplyPendingd): - apply_pending = True - keys = transform.keys - else: - apply_pending = True - - if apply_pending is True: - _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) - return apply_pending_transforms(data, keys, overrides, logger_name) - - _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) - return data +# __all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order"] + + +# def _log_pending_info( +# transform: Any, +# data: Any, +# activity: str, +# *, +# lazy: bool | None = None, +# key: str | None = None, +# logger_name: bool | str = False, +# ): +# if logger_name is False: +# return +# logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" +# logger = get_logger(logger_name) +# +# tcname = type(transform).__name__ +# if isinstance(transform, LazyTrait): +# tlazy = f", transform.lazy: {transform.lazy}" +# if lazy is not None and lazy != transform.lazy: +# tlazy += " (overridden)" +# else: +# tlazy = ", transform is not lazy" +# +# msg = f"{activity} - lazy: {lazy}, {{key_msg}}pending: {{pcount}}, upcoming '{tcname}'{tlazy}" +# +# if isinstance(transform, MapTransform): +# transform_keys = transform.keys if key is None else (key,) +# for k in transform_keys: +# if k in data: +# pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 +# logger.info(msg.format(pcount=pcount, key_msg=f"key: '{k}', ")) +# else: +# pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 +# logger.info(msg.format(pcount=pcount, key_msg="" if key is None else f"key: '{key}', ")) +# +# +# def _log_applied_info(data: Any, key=None, logger_name: bool | str = False): +# if logger_name is False: +# return +# logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" +# logger = get_logger(logger_name) +# +# key_str = "" if key is None else f"key: '{key}', " +# logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") +# +# +# def apply_pending_transforms( +# data: NdarrayOrTensor | Sequence[Any | NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], +# keys: tuple | None, +# overrides: dict | None = None, +# logger_name: bool | str = False, +# ): +# """ +# apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain +# tensors. +# +# When operating on a dictionary of tensors, the 'keys' parameter determines what tensors should be checked. +# If 'keys' is not set, all keys of 'data' are considered. +# +# This method optionally takes a set of overrides that can be used to change specific parameters on the +# transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used +# to override the default logger, to provide telemetry during the execution of pending transforms. +# +# This method is intended primarily for use by ``execute_compose`` and other methods that handle the +# underlying execution of transform pipelines. You should not need to use it in the general case, unless +# you are developing functionality to perform such operations. +# +# Args: +# data: a ``torch.Tensor`` or ``MetaTensor``, or dictionary of tensors. +# keys: an optional tuple of keys that filters the keys on 'data' if it is a dict +# overrides: An optional dictionary that specifies parameters that can be used to override transform +# arguments when they are called. When 'data' is a dict, this dictionary should contain a dictionary +# of overrides for each key that needs them +# logger_name: An optional name for a logger to be used when applying pending transforms. If None, +# logging is suppressed. +# Returns: +# an object of the same type as data if pending transforms were applied, or 'data' if they were not +# """ +# if isinstance(data, list): +# return [apply_pending_transforms(d, keys, overrides, logger_name) for d in data] +# if isinstance(data, tuple): +# return tuple(apply_pending_transforms(d, keys, overrides, logger_name) for d in data) +# +# if isinstance(data, dict): +# # get the keys from 'data' for metatensors with pending operations. If 'keys' is set, select +# # only data keys that are in 'keys' +# active_keys = [k for k in data.keys() if keys is None or k in keys] +# keys_to_update = [k for k in active_keys if isinstance(data[k], MetaTensor) and data[k].has_pending_operations] +# +# if len(keys_to_update) > 0: +# rdata = dict(data) +# +# for k in keys_to_update: +# overrides_ = None if overrides is None else overrides.get(k, None) +# rdata[k], _ = apply_pending(data[k], overrides=overrides_) +# _log_applied_info(rdata[k], key=k, logger_name=logger_name) +# +# return rdata +# else: +# if isinstance(data, MetaTensor) and data.has_pending_operations: +# rdata, _ = apply_pending(data, overrides=overrides) +# _log_applied_info(rdata, logger_name=logger_name) +# return rdata +# +# return data +# +# +# def apply_pending_transforms_in_order( +# transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str = False +# ): +# """ +# This method causes "in order" processing of pending transforms to occur. +# "in order" processing of pending transforms ensures that all pending transforms have been applied to the +# tensor before a non-lazy transform (or lazy transform that is executing non-lazily) is carried out. +# It ensures that no operations will be added to a metatensor's apply_operations while there are outstanding +# pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this +# is expected to change in future releases. +# +# Evaluation of pending transforms is performed under the following circumstances: +# * If the transform is a lazy transform and: +# * The transform checks data as part of its execution, or +# * the transform is not executing lazily +# * If the transform is an ApplyPending[d] transform +# * If the transform is not a lazy transform +# +# This method is designed to be used only in the context of implementing lazy resampling functionality. In general +# you should not need to interact with or use this method directly, and its API may change without warning between +# releases. See the :ref:`Lazy Resampling topic for more information about lazy resampling. +# +# Args: +# transform: a transform that should be evaluated to determine whether pending transforms should be applied +# data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may +# need to be applied +# lazy: The lazy mode that is being applied (this can be False, True or None) +# overrides: An optional dictionary containing overrides to be applied to the pending transforms when they +# are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that +# needs them +# logger_name: An optional name for a logger to be used when applying pending transforms. If None, +# logging is suppressed. +# Returns: +# an object of the same type as data if pending transforms were applied, or 'data' if they were not +# +# """ +# keys = None +# if isinstance(transform, LazyTrait): +# if transform.checks_data: +# apply_pending = True +# else: +# apply_pending = not (transform.lazy if lazy is None else lazy) +# elif isinstance(transform, ApplyPending): +# apply_pending = True +# elif isinstance(transform, ApplyPendingd): +# apply_pending = True +# keys = transform.keys +# else: +# apply_pending = True +# +# if apply_pending is True: +# _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) +# return apply_pending_transforms(data, keys, overrides, logger_name) +# +# _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) +# return data diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index f401d72261..9084423418 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -11,9 +11,10 @@ from __future__ import annotations -from typing import Any +from typing import Any, Sequence, Mapping import torch +from monai.config import NdarrayOrTensor from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd @@ -24,13 +25,183 @@ kwargs_from_pending, resample, ) +from monai.apps.utils import get_logger +from monai.transforms.traits import LazyTrait +from monai.transforms.transform import MapTransform from monai.utils import LazyAttr, look_up_option -__all__ = ["apply_pending"] +__all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order", "apply_pending"] __override_keywords = {"mode", "padding_mode", "dtype", "align_corners", "resample_mode", "device"} +def _log_pending_info( + transform: Any, + data: Any, + activity: str, + *, + lazy: bool | None = None, + key: str | None = None, + logger_name: bool | str = False, +): + if logger_name is False: + return + logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" + logger = get_logger(logger_name) + + tcname = type(transform).__name__ + if isinstance(transform, LazyTrait): + tlazy = f", transform.lazy: {transform.lazy}" + if lazy is not None and lazy != transform.lazy: + tlazy += " (overridden)" + else: + tlazy = ", transform is not lazy" + + msg = f"{activity} - lazy: {lazy}, {{key_msg}}pending: {{pcount}}, upcoming '{tcname}'{tlazy}" + + if isinstance(transform, MapTransform): + transform_keys = transform.keys if key is None else (key,) + for k in transform_keys: + if k in data: + pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 + logger.info(msg.format(pcount=pcount, key_msg=f"key: '{k}', ")) + else: + pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 + logger.info(msg.format(pcount=pcount, key_msg="" if key is None else f"key: '{key}', ")) + + +def _log_applied_info(data: Any, key=None, logger_name: bool | str = False): + if logger_name is False: + return + logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" + logger = get_logger(logger_name) + + key_str = "" if key is None else f"key: '{key}', " + logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") + + +def apply_pending_transforms( + data: NdarrayOrTensor | Sequence[Any | NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], + keys: tuple | None, + overrides: dict | None = None, + logger_name: bool | str = False, +): + """ + apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain + tensors. + + When operating on a dictionary of tensors, the 'keys' parameter determines what tensors should be checked. + If 'keys' is not set, all keys of 'data' are considered. + + This method optionally takes a set of overrides that can be used to change specific parameters on the + transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used + to override the default logger, to provide telemetry during the execution of pending transforms. + + This method is intended primarily for use by ``execute_compose`` and other methods that handle the + underlying execution of transform pipelines. You should not need to use it in the general case, unless + you are developing functionality to perform such operations. + + Args: + data: a ``torch.Tensor`` or ``MetaTensor``, or dictionary of tensors. + keys: an optional tuple of keys that filters the keys on 'data' if it is a dict + overrides: An optional dictionary that specifies parameters that can be used to override transform + arguments when they are called. When 'data' is a dict, this dictionary should contain a dictionary + of overrides for each key that needs them + logger_name: An optional name for a logger to be used when applying pending transforms. If None, + logging is suppressed. + Returns: + an object of the same type as data if pending transforms were applied, or 'data' if they were not + """ + if isinstance(data, list): + return [apply_pending_transforms(d, keys, overrides, logger_name) for d in data] + if isinstance(data, tuple): + return tuple(apply_pending_transforms(d, keys, overrides, logger_name) for d in data) + + if isinstance(data, dict): + # get the keys from 'data' for metatensors with pending operations. If 'keys' is set, select + # only data keys that are in 'keys' + active_keys = [k for k in data.keys() if keys is None or k in keys] + keys_to_update = [k for k in active_keys if isinstance(data[k], MetaTensor) and data[k].has_pending_operations] + + if len(keys_to_update) > 0: + rdata = dict(data) + + for k in keys_to_update: + overrides_ = None if overrides is None else overrides.get(k, None) + rdata[k], _ = apply_pending(data[k], overrides=overrides_) + _log_applied_info(rdata[k], key=k, logger_name=logger_name) + + return rdata + else: + if isinstance(data, MetaTensor) and data.has_pending_operations: + rdata, _ = apply_pending(data, overrides=overrides) + _log_applied_info(rdata, logger_name=logger_name) + return rdata + + return data + + +def apply_pending_transforms_in_order( + transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str = False +): + """ + This method causes "in order" processing of pending transforms to occur. + "in order" processing of pending transforms ensures that all pending transforms have been applied to the + tensor before a non-lazy transform (or lazy transform that is executing non-lazily) is carried out. + It ensures that no operations will be added to a metatensor's apply_operations while there are outstanding + pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this + is expected to change in future releases. + + Evaluation of pending transforms is performed under the following circumstances: + * If the transform is a lazy transform and: + * The transform checks data as part of its execution, or + * the transform is not executing lazily + * If the transform is an ApplyPending[d] transform + * If the transform is not a lazy transform + + This method is designed to be used only in the context of implementing lazy resampling functionality. In general + you should not need to interact with or use this method directly, and its API may change without warning between + releases. See the :ref:`Lazy Resampling topic for more information about lazy resampling. + + Args: + transform: a transform that should be evaluated to determine whether pending transforms should be applied + data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may + need to be applied + lazy: The lazy mode that is being applied (this can be False, True or None) + overrides: An optional dictionary containing overrides to be applied to the pending transforms when they + are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that + needs them + logger_name: An optional name for a logger to be used when applying pending transforms. If None, + logging is suppressed. + Returns: + an object of the same type as data if pending transforms were applied, or 'data' if they were not + + """ + from monai.transforms.lazy.array import ApplyPending + from monai.transforms.lazy.dictionary import ApplyPendingd + + keys = None + if isinstance(transform, LazyTrait): + if transform.checks_data: + must_apply_pending = True + else: + must_apply_pending = not (transform.lazy if lazy is None else lazy) + elif isinstance(transform, ApplyPending): + must_apply_pending = True + elif isinstance(transform, ApplyPendingd): + must_apply_pending = True + keys = transform.keys + else: + must_apply_pending = True + + if must_apply_pending is True: + _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) + return apply_pending_transforms(data, keys, overrides, logger_name) + + _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) + return data + + def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, overrides: dict | None = None): """ This method applies pending transforms to `data` tensors. diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index 359559e319..29f52c462b 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -11,11 +11,12 @@ from __future__ import annotations +from typing import Any, Sequence, Mapping + import warnings import numpy as np import torch - import monai from monai.config import NdarrayOrTensor from monai.data.utils import AFFINE_TOL diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 89a3676b0a..0199e236dc 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -88,7 +88,7 @@ def _apply_transform( Returns: ReturnType: The return type of `transform`. """ - from monai.transforms.lazy.executors import apply_pending_transforms_in_order + from monai.transforms.lazy.functional import apply_pending_transforms_in_order data = apply_pending_transforms_in_order(transform, data, lazy, overrides, logger_name) From 09477b0ac1028c17fcdf1cf859b6c4454ea1acd8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 11:24:23 +0100 Subject: [PATCH 160/175] Removing executors.py as it is no longer required Signed-off-by: Ben Murray --- monai/transforms/lazy/executors.py | 189 ----------------------------- 1 file changed, 189 deletions(-) delete mode 100644 monai/transforms/lazy/executors.py diff --git a/monai/transforms/lazy/executors.py b/monai/transforms/lazy/executors.py deleted file mode 100644 index 989a57434f..0000000000 --- a/monai/transforms/lazy/executors.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (c) 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. - -from __future__ import annotations - -from typing import Any, Mapping, Sequence - -from monai.apps.utils import get_logger -from monai.config import NdarrayOrTensor -from monai.data.meta_tensor import MetaTensor -from monai.transforms.lazy.array import ApplyPending -from monai.transforms.lazy.dictionary import ApplyPendingd -from monai.transforms.lazy.functional import apply_pending -from monai.transforms.traits import LazyTrait -from monai.transforms.transform import MapTransform - -# __all__ = ["apply_pending_transforms", "apply_pending_transforms_in_order"] - - -# def _log_pending_info( -# transform: Any, -# data: Any, -# activity: str, -# *, -# lazy: bool | None = None, -# key: str | None = None, -# logger_name: bool | str = False, -# ): -# if logger_name is False: -# return -# logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" -# logger = get_logger(logger_name) -# -# tcname = type(transform).__name__ -# if isinstance(transform, LazyTrait): -# tlazy = f", transform.lazy: {transform.lazy}" -# if lazy is not None and lazy != transform.lazy: -# tlazy += " (overridden)" -# else: -# tlazy = ", transform is not lazy" -# -# msg = f"{activity} - lazy: {lazy}, {{key_msg}}pending: {{pcount}}, upcoming '{tcname}'{tlazy}" -# -# if isinstance(transform, MapTransform): -# transform_keys = transform.keys if key is None else (key,) -# for k in transform_keys: -# if k in data: -# pcount = len(data[k].pending_operations) if isinstance(data[k], MetaTensor) else 0 -# logger.info(msg.format(pcount=pcount, key_msg=f"key: '{k}', ")) -# else: -# pcount = len(data.pending_operations) if isinstance(data, MetaTensor) else 0 -# logger.info(msg.format(pcount=pcount, key_msg="" if key is None else f"key: '{key}', ")) -# -# -# def _log_applied_info(data: Any, key=None, logger_name: bool | str = False): -# if logger_name is False: -# return -# logger_name = logger_name if isinstance(logger_name, str) else "apply_pending_transforms" -# logger = get_logger(logger_name) -# -# key_str = "" if key is None else f"key: '{key}', " -# logger.info(f"Pending transforms applied: {key_str}applied_operations: {len(data.applied_operations)}") -# -# -# def apply_pending_transforms( -# data: NdarrayOrTensor | Sequence[Any | NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor], -# keys: tuple | None, -# overrides: dict | None = None, -# logger_name: bool | str = False, -# ): -# """ -# apply_pending_transforms is called with either a tensor or a dictionary, some entries of which contain -# tensors. -# -# When operating on a dictionary of tensors, the 'keys' parameter determines what tensors should be checked. -# If 'keys' is not set, all keys of 'data' are considered. -# -# This method optionally takes a set of overrides that can be used to change specific parameters on the -# transform pipeline. See ``Compose`` for more details. This method takes a logger_name that can be used -# to override the default logger, to provide telemetry during the execution of pending transforms. -# -# This method is intended primarily for use by ``execute_compose`` and other methods that handle the -# underlying execution of transform pipelines. You should not need to use it in the general case, unless -# you are developing functionality to perform such operations. -# -# Args: -# data: a ``torch.Tensor`` or ``MetaTensor``, or dictionary of tensors. -# keys: an optional tuple of keys that filters the keys on 'data' if it is a dict -# overrides: An optional dictionary that specifies parameters that can be used to override transform -# arguments when they are called. When 'data' is a dict, this dictionary should contain a dictionary -# of overrides for each key that needs them -# logger_name: An optional name for a logger to be used when applying pending transforms. If None, -# logging is suppressed. -# Returns: -# an object of the same type as data if pending transforms were applied, or 'data' if they were not -# """ -# if isinstance(data, list): -# return [apply_pending_transforms(d, keys, overrides, logger_name) for d in data] -# if isinstance(data, tuple): -# return tuple(apply_pending_transforms(d, keys, overrides, logger_name) for d in data) -# -# if isinstance(data, dict): -# # get the keys from 'data' for metatensors with pending operations. If 'keys' is set, select -# # only data keys that are in 'keys' -# active_keys = [k for k in data.keys() if keys is None or k in keys] -# keys_to_update = [k for k in active_keys if isinstance(data[k], MetaTensor) and data[k].has_pending_operations] -# -# if len(keys_to_update) > 0: -# rdata = dict(data) -# -# for k in keys_to_update: -# overrides_ = None if overrides is None else overrides.get(k, None) -# rdata[k], _ = apply_pending(data[k], overrides=overrides_) -# _log_applied_info(rdata[k], key=k, logger_name=logger_name) -# -# return rdata -# else: -# if isinstance(data, MetaTensor) and data.has_pending_operations: -# rdata, _ = apply_pending(data, overrides=overrides) -# _log_applied_info(rdata, logger_name=logger_name) -# return rdata -# -# return data -# -# -# def apply_pending_transforms_in_order( -# transform, data, lazy: bool | None = None, overrides: dict | None = None, logger_name: bool | str = False -# ): -# """ -# This method causes "in order" processing of pending transforms to occur. -# "in order" processing of pending transforms ensures that all pending transforms have been applied to the -# tensor before a non-lazy transform (or lazy transform that is executing non-lazily) is carried out. -# It ensures that no operations will be added to a metatensor's apply_operations while there are outstanding -# pending_operations. Note that there is only one mechanism for executing lazy resampling at present but this -# is expected to change in future releases. -# -# Evaluation of pending transforms is performed under the following circumstances: -# * If the transform is a lazy transform and: -# * The transform checks data as part of its execution, or -# * the transform is not executing lazily -# * If the transform is an ApplyPending[d] transform -# * If the transform is not a lazy transform -# -# This method is designed to be used only in the context of implementing lazy resampling functionality. In general -# you should not need to interact with or use this method directly, and its API may change without warning between -# releases. See the :ref:`Lazy Resampling topic for more information about lazy resampling. -# -# Args: -# transform: a transform that should be evaluated to determine whether pending transforms should be applied -# data: a tensor / MetaTensor, or dictionary containing tensors / MetaTensors whose pending transforms may -# need to be applied -# lazy: The lazy mode that is being applied (this can be False, True or None) -# overrides: An optional dictionary containing overrides to be applied to the pending transforms when they -# are lazily executed. If data is a dict, it should contain a dictionary of overrides for each key that -# needs them -# logger_name: An optional name for a logger to be used when applying pending transforms. If None, -# logging is suppressed. -# Returns: -# an object of the same type as data if pending transforms were applied, or 'data' if they were not -# -# """ -# keys = None -# if isinstance(transform, LazyTrait): -# if transform.checks_data: -# apply_pending = True -# else: -# apply_pending = not (transform.lazy if lazy is None else lazy) -# elif isinstance(transform, ApplyPending): -# apply_pending = True -# elif isinstance(transform, ApplyPendingd): -# apply_pending = True -# keys = transform.keys -# else: -# apply_pending = True -# -# if apply_pending is True: -# _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) -# return apply_pending_transforms(data, keys, overrides, logger_name) -# -# _log_pending_info(transform, data, "Accumulate pending transforms", lazy=lazy, logger_name=logger_name) -# return data From cf48aafa42ab2437bd47472535c934bf09aa959c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 10:24:58 +0000 Subject: [PATCH 161/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/lazy/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index 29f52c462b..4444437c4c 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -11,7 +11,6 @@ from __future__ import annotations -from typing import Any, Sequence, Mapping import warnings From f990a3084cd066b372979217889b5fc761f9ed97 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 11:58:23 +0100 Subject: [PATCH 162/175] autofixes Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 6 +++--- monai/transforms/lazy/utils.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 9084423418..fabf849554 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -11,11 +11,12 @@ from __future__ import annotations -from typing import Any, Sequence, Mapping +from typing import Any, Mapping, Sequence import torch -from monai.config import NdarrayOrTensor +from monai.apps.utils import get_logger +from monai.config import NdarrayOrTensor from monai.data.meta_tensor import MetaTensor from monai.data.utils import to_affine_nd from monai.transforms.lazy.utils import ( @@ -25,7 +26,6 @@ kwargs_from_pending, resample, ) -from monai.apps.utils import get_logger from monai.transforms.traits import LazyTrait from monai.transforms.transform import MapTransform from monai.utils import LazyAttr, look_up_option diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index 29f52c462b..ba2020da36 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -11,12 +11,12 @@ from __future__ import annotations -from typing import Any, Sequence, Mapping - import warnings +from typing import Any, Mapping, Sequence import numpy as np import torch + import monai from monai.config import NdarrayOrTensor from monai.data.utils import AFFINE_TOL From 769aec3933c5640b9c1d20b9abe1e3cf2173b020 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 11:02:40 +0000 Subject: [PATCH 163/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/lazy/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index ba2020da36..359559e319 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -12,7 +12,6 @@ from __future__ import annotations import warnings -from typing import Any, Mapping, Sequence import numpy as np import torch From 64ed447efaea8a190b45d4d1a452b8356a1740a8 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 15:59:12 +0100 Subject: [PATCH 164/175] Implementation of review points: 1. Making RandRitateBox90d and ReferenceBasedSpatialCropd not inherit from lazy transforms, renamed is_tensor_invertible, renamed checks_data Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 7 +++--- monai/apps/detection/transforms/dictionary.py | 23 +++++++++++------- .../reconstruction/transforms/dictionary.py | 12 ++++------ monai/transforms/compose.py | 7 +++--- monai/transforms/croppad/array.py | 6 ++--- monai/transforms/croppad/dictionary.py | 6 ++--- monai/transforms/lazy/functional.py | 18 ++++---------- monai/transforms/traits.py | 10 ++++---- monai/transforms/transform.py | 2 +- monai/transforms/utils.py | 24 +++++++++---------- tests/test_integration_lazy_samples.py | 6 ++--- 11 files changed, 54 insertions(+), 67 deletions(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index 273f55e9aa..7b9bd91e70 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -221,10 +221,11 @@ lazy property The lazy property allows you to get or set the lazy status of a lazy transform after constructing it. -checks_data property (get only) -""""""""""""""""""""""""""""""" +requires_current_data property (get only) +""""""""""""""""""""""""""""""""""""""""" -The ``checks_data`` property indicates that a transform makes use of the data in one or more of the tensors that it is +The ``requires_current_data`` property indicates that a transform makes use of the data in one or more of the tensors +that it is passed during its execution. Such transforms require that the tensors must therefore be up to date, even if the transform itself is executing lazily. This is required for transforms such as ``CropForeground[d]``, ``RandCropByPosNegLabel[d]``, and ``RandCropByLabelClasses[d]``. This property is implemented to return ``False`` on diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index 853971adc9..52b1a7d15d 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -17,7 +17,6 @@ from __future__ import annotations -import warnings from collections.abc import Hashable, Mapping, Sequence from copy import deepcopy from typing import Any @@ -44,7 +43,7 @@ from monai.data.box_utils import COMPUTE_DTYPE, BoxMode, clip_boxes_to_image from monai.data.meta_tensor import MetaTensor, get_track_meta from monai.data.utils import orientation_ras_lps -from monai.transforms import Flip, RandFlip, RandRotate90d, RandZoom, Rotate90, SpatialCrop, Zoom +from monai.transforms import Flip, RandFlip, RandZoom, Rotate90, SpatialCrop, Zoom from monai.transforms.inverse import InvertibleTransform from monai.transforms.transform import MapTransform, Randomizable, RandomizableTransform from monai.transforms.utils import generate_pos_neg_label_crop_centers, map_binary_to_indices @@ -1292,7 +1291,7 @@ def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch return d -class RandRotateBox90d(RandRotate90d): +class RandRotateBox90d(RandomizableTransform, MapTransform, InvertibleTransform): """ With probability `prob`, input boxes and images are rotated by 90 degrees in the plane specified by `spatial_axes`. @@ -1324,14 +1323,16 @@ def __init__( ) -> None: self.image_keys = ensure_tuple(image_keys) self.box_keys = ensure_tuple(box_keys) - super().__init__(self.image_keys + self.box_keys, prob, max_k, spatial_axes, allow_missing_keys) + + MapTransform.__init__(self, self.image_keys + self.box_keys, allow_missing_keys) + RandomizableTransform.__init__(self, prob) + + self.max_k = max_k + self.spatial_axes = spatial_axes + self._rand_k = 0 self.box_ref_image_keys = ensure_tuple_rep(box_ref_image_keys, len(self.box_keys)) - def __call__( - self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None - ) -> Mapping[Hashable, torch.Tensor]: - if lazy is True: - warnings.warn("RandRotateBox90d cannot currently execute lazily; ignoring lazy=True") + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Mapping[Hashable, torch.Tensor]: self.randomize() d = dict(data) @@ -1369,6 +1370,10 @@ def __call__( self.push_transform(d[key], extra_info=xform) return d + def randomize(self, data: Any | None = None) -> None: + self._rand_k = self.R.randint(self.max_k) + 1 + super().randomize(None) + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) if self._rand_k % 4 == 0: diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 6b96f10cf9..c166740768 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -11,7 +11,6 @@ from __future__ import annotations -import warnings from collections.abc import Hashable, Mapping, Sequence import numpy as np @@ -21,8 +20,8 @@ from monai.apps.reconstruction.transforms.array import EquispacedKspaceMask, RandomKspaceMask from monai.config import DtypeLike, KeysCollection from monai.config.type_definitions import NdarrayOrTensor +from monai.transforms import InvertibleTransform from monai.transforms.croppad.array import SpatialCrop -from monai.transforms.croppad.dictionary import Cropd from monai.transforms.intensity.array import NormalizeIntensity from monai.transforms.transform import MapTransform, RandomizableTransform from monai.utils import FastMRIKeys @@ -191,7 +190,7 @@ def set_random_state( return self -class ReferenceBasedSpatialCropd(Cropd): +class ReferenceBasedSpatialCropd(MapTransform, InvertibleTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.SpatialCrop`. This is similar to :py:class:`monai.transforms.SpatialCropd` which is a @@ -214,10 +213,10 @@ class ReferenceBasedSpatialCropd(Cropd): """ def __init__(self, keys: KeysCollection, ref_key: str, allow_missing_keys: bool = False) -> None: - super().__init__(keys, cropper=None, allow_missing_keys=allow_missing_keys, lazy=False) # type: ignore + MapTransform.__init__(self, keys, allow_missing_keys) self.ref_key = ref_key - def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None = None) -> dict[Hashable, Tensor]: + def __call__(self, data: Mapping[Hashable, Tensor]) -> dict[Hashable, Tensor]: """ This transform can support to crop ND spatial (channel-first) data. It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D @@ -230,9 +229,6 @@ def __call__(self, data: Mapping[Hashable, Tensor], lazy: bool | None = None) -> Returns: the new data dictionary """ - if lazy is True: - warnings.warn("ReferenceBasedSpatialCropd cannot currently execute lazily; ignoring lazy=True") - d = dict(data) # compute roi_size according to self.ref_key diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 0a9e91613f..36517992ff 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -37,7 +37,7 @@ Transform, apply_transform, ) -from monai.utils import MAX_SEED, TraceKeys, ensure_tuple, get_seed +from monai.utils import MAX_SEED, TraceKeys, TraceStatusKeys, ensure_tuple, get_seed logger = get_logger(__name__) @@ -378,9 +378,9 @@ def inverse(self, data): @staticmethod def _raise_if_tensor_is_not_invertible(data: Any): - from monai.transforms.utils import is_tensor_invertible + from monai.transforms.utils import has_status_keys - invertible, reasons = is_tensor_invertible(data) + invertible, reasons = has_status_keys(data, TraceStatusKeys.PENDING_DURING_APPLY) if invertible is False: if reasons is not None: @@ -684,7 +684,6 @@ def __init__( map_items: bool = True, unpack_items: bool = False, log_stats: bool | str = False, - *, num_transforms: int | tuple[int, int] | None = None, replace: bool = False, weights: list[int] | None = None, diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index f4cbe43015..740ea9d8f5 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -870,7 +870,7 @@ def lazy(self, _val: bool): self.padder.lazy = _val @property - def checks_data(self): + def requires_current_data(self): return False def compute_bounding_box(self, img: torch.Tensor) -> tuple[np.ndarray, np.ndarray]: @@ -1176,7 +1176,7 @@ def lazy(self, _val: bool): self._lazy = _val @property - def checks_data(self): + def requires_current_data(self): return False def __call__( @@ -1358,7 +1358,7 @@ def lazy(self, _val: bool): self._lazy = _val @property - def checks_data(self): + def requires_current_data(self): return False def __call__( diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 3a7bef414c..6dc8f10c32 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -788,7 +788,7 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value @property - def checks_data(self): + def requires_current_data(self): return True def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = None) -> dict[Hashable, torch.Tensor]: @@ -989,7 +989,7 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value @property - def checks_data(self): + def requires_current_data(self): return True def __call__( @@ -1147,7 +1147,7 @@ def lazy(self, value: bool) -> None: self.cropper.lazy = value @property - def checks_data(self): + def requires_current_data(self): return True def __call__(self, data: Mapping[Hashable, Any], lazy: bool | None = None) -> list[dict[Hashable, torch.Tensor]]: diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index fabf849554..70052aabed 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -177,22 +177,12 @@ def apply_pending_transforms_in_order( an object of the same type as data if pending transforms were applied, or 'data' if they were not """ - from monai.transforms.lazy.array import ApplyPending from monai.transforms.lazy.dictionary import ApplyPendingd - keys = None - if isinstance(transform, LazyTrait): - if transform.checks_data: - must_apply_pending = True - else: - must_apply_pending = not (transform.lazy if lazy is None else lazy) - elif isinstance(transform, ApplyPending): - must_apply_pending = True - elif isinstance(transform, ApplyPendingd): - must_apply_pending = True - keys = transform.keys - else: - must_apply_pending = True + must_apply_pending = True + keys = transform.keys if isinstance(transform, ApplyPendingd) else None + if isinstance(transform, LazyTrait) and not transform.requires_current_data: + must_apply_pending = not (transform.lazy if lazy is None else lazy) if must_apply_pending is True: _log_pending_info(transform, data, "Apply pending transforms", lazy=lazy, logger_name=logger_name) diff --git a/monai/transforms/traits.py b/monai/transforms/traits.py index a47fc87e79..016effc59d 100644 --- a/monai/transforms/traits.py +++ b/monai/transforms/traits.py @@ -47,14 +47,12 @@ def lazy(self, enabled: bool): raise NotImplementedError() @property - def checks_data(self): + def requires_current_data(self): """ - Get whether the transform checks the sample pixel/voxel data on its inputs or not as part of its - operation. A transform that checks data requires that all of the pending operations on its input - transforms are up to date before it is executed, but it can still execute lazily by adding pending - operations to the input tensors. + Get whether the transform requires the input data to be up to date before the transform executes. + Such transforms can still execute lazily by adding pending operations to the output tensors. Returns: - True if the transform checks data and False if it does not + True if the transform requires its inputs to be up to date and False if it does not """ diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 0199e236dc..71e1718d95 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -314,7 +314,7 @@ def lazy(self, lazy: bool | None): self._lazy = lazy @property - def checks_data(self): + def requires_current_data(self): return False diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 7594a3fa58..8fcefdc657 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -122,7 +122,7 @@ "sync_meta_info", "reset_ops_id", "resolves_modes", - "is_tensor_invertible", + "has_status_keys", ] @@ -2003,7 +2003,7 @@ def check_applied_operations(entry: list | dict, status_key: str, default_messag return [] -def is_tensor_invertible(data: torch.Tensor): +def has_status_keys(data: torch.Tensor, status_key: Any): """ Checks whether a given tensor is invertible. The rules are as follows: @@ -2029,27 +2029,25 @@ def is_tensor_invertible(data: torch.Tensor): user to help debug their pipelines. """ - invert_disabled_reasons = list() + status_key_occurrences = list() if isinstance(data, (list, tuple)): for d in data: - _, reasons = is_tensor_invertible(d) + _, reasons = has_status_keys(d, status_key) if reasons is not None: - invert_disabled_reasons.extend(reasons) + status_key_occurrences.extend(reasons) elif isinstance(data, monai.data.MetaTensor): for op in data.applied_operations: - invert_disabled_reasons.extend( - check_applied_operations( - op, TraceStatusKeys.PENDING_DURING_APPLY, "Pending operations while applying an operation" - ) + status_key_occurrences.extend( + check_applied_operations(op, status_key, "Pending operations while applying an operation") ) elif isinstance(data, dict): for d in data.values(): - _, reasons = is_tensor_invertible(d) + _, reasons = has_status_keys(d, status_key) if reasons is not None: - invert_disabled_reasons.extend(reasons) + status_key_occurrences.extend(reasons) - if len(invert_disabled_reasons) > 0: - return False, invert_disabled_reasons + if len(status_key_occurrences) > 0: + return False, status_key_occurrences return True, None diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index d3d58bda5d..12732ffe40 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -24,8 +24,8 @@ import monai import monai.transforms as mt from monai.data import create_test_image_3d, decollate_batch -from monai.transforms.utils import is_tensor_invertible -from monai.utils import set_determinism +from monai.transforms.utils import has_status_keys +from monai.utils import TraceStatusKeys, set_determinism from tests.utils import HAS_CUPY, DistTestCase, SkipIfBeforePyTorchVersion, skip_if_quick @@ -151,7 +151,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, saver(item) # just testing the saving saver(in_img) saver(in_seg) - invertible, reasons = is_tensor_invertible(batch_data) + invertible, reasons = has_status_keys(batch_data, TraceStatusKeys.PENDING_DURING_APPLY) inverted = [inverter(b_data) for b_data in decollate_batch(batch_data)] # expecting no error return ops From a6fcd964adb7ed125f989e5127a523d50094c320 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 16:20:54 +0100 Subject: [PATCH 165/175] Removing logger_name from Compose and related that already have log_stats Signed-off-by: Ben Murray --- monai/transforms/compose.py | 69 +++++++------------------- monai/transforms/transform.py | 7 ++- tests/test_compose.py | 6 +-- tests/test_integration_lazy_samples.py | 2 +- 4 files changed, 25 insertions(+), 59 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 36517992ff..08b26668f7 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -54,8 +54,7 @@ def execute_compose( lazy: bool | None = False, overrides: dict | None = None, threading: bool = False, - log_stats: bool | str = False, - logger_name: bool | str = False, + log_stats: bool | str = False ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -84,10 +83,7 @@ def execute_compose( {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. threading: whether executing is happening in a threaded environment. If set, copies are made of transforms that have the ``RandomizedTrait`` interface. - log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged. - logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + log_stats: this optional parameter allows you to specify a logger by name for logging of pipeline execution. Setting this to False disables logging. Setting it to True enables logging to the default loggers. Setting a string overrides the logger name to which logging is performed. @@ -119,10 +115,9 @@ def execute_compose( unpack_items, lazy=lazy, overrides=overrides, - log_stats=log_stats, - logger_name=logger_name, + log_stats=log_stats ) - data = apply_pending_transforms(data, None, overrides, logger_name=logger_name) + data = apply_pending_transforms(data, None, overrides, logger_name=log_stats) return data @@ -220,9 +215,9 @@ class Compose(Randomizable, InvertibleTransform): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged. + log_stats: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will @@ -233,9 +228,6 @@ class Compose(Randomizable, InvertibleTransform): :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. - logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. - Setting this to False disables logging. Setting it to True enables logging to the default loggers. - Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -246,7 +238,6 @@ def __init__( log_stats: bool | str = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str = False, ) -> None: if transforms is None: transforms = [] @@ -257,7 +248,6 @@ def __init__( self.set_random_state(seed=get_seed()) self.lazy = lazy self.overrides = overrides - self.logger_name = logger_name def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) @@ -346,7 +336,6 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None overrides=self.overrides, threading=threading, log_stats=self.log_stats, - logger_name=self.logger_name, ) return result @@ -372,7 +361,6 @@ def inverse(self, data): self.unpack_items, lazy=False, log_stats=self.log_stats, - logger_name=self.logger_name, ) return data @@ -403,9 +391,9 @@ class OneOf(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to None or True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged. + log_stats: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will @@ -416,9 +404,6 @@ class OneOf(Compose): :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. - logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. - Setting this to False disables logging. Setting it to True enables logging to the default loggers. - Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -430,9 +415,8 @@ def __init__( log_stats: bool | str = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str = False, ) -> None: - super().__init__(transforms, map_items, unpack_items, log_stats, lazy, overrides, logger_name) + super().__init__(transforms, map_items, unpack_items, log_stats, lazy, overrides) if len(self.transforms) == 0: weights = [] elif weights is None or isinstance(weights, float): @@ -444,7 +428,6 @@ def __init__( ) self.weights = ensure_tuple(self._normalize_probabilities(weights)) self.log_stats = log_stats - self.logger_name = logger_name def _normalize_probabilities(self, weights): if len(weights) == 0: @@ -496,7 +479,6 @@ def __call__(self, data, start=0, end=None, threading=False, lazy: str | bool | overrides=self.overrides, threading=threading, log_stats=self.log_stats, - logger_name=self.logger_name, ) # if the data is a mapping (dictionary), append the OneOf transform to the end @@ -542,9 +524,9 @@ class RandomOrder(Compose): defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. defaults to `False`. - log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged. + log_stats: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. lazy: whether to enable :ref:`Lazy Resampling` for lazy transforms. If False, transforms will be carried out on a transform by transform basis. If True, all lazy transforms will be executed by accumulating changes and resampling as few times as possible. If lazy is None, `Compose` will @@ -555,9 +537,6 @@ class RandomOrder(Compose): :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. - logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. - Setting this to False disables logging. Setting it to True enables logging to the default loggers. - Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -568,11 +547,9 @@ def __init__( log_stats: bool | str = False, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str = False, ) -> None: - super().__init__(transforms, map_items, unpack_items, log_stats, lazy, overrides, logger_name) + super().__init__(transforms, map_items, unpack_items, log_stats, lazy, overrides) self.log_stats = log_stats - self.logger_name = logger_name def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None = None): if start != 0: @@ -596,7 +573,6 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None lazy=self.lazy, threading=threading, log_stats=self.log_stats, - logger_name=self.logger_name, ) # if the data is a mapping (dictionary), append the RandomOrder transform to the end @@ -636,7 +612,6 @@ def inverse(self, data): self.map_items, self.unpack_items, log_stats=self.log_stats, - logger_name=self.logger_name, ) return data @@ -655,9 +630,9 @@ class SomeOf(Compose): Defaults to `True`. unpack_items: whether to unpack input `data` with `*` as parameters for the callable function of transform. Defaults to `False`. - log_stats: log errors when they occur in the processing pipeline. By default, this is set to False, which - disables the logger for processing pipeline errors. Setting it to True will enable logging to the - default logger name. Setting it to a string specifies the logger to which errors should be logged. + log_stats: this optional parameter allows you to specify a logger by name for logging of pipeline execution. + Setting this to False disables logging. Setting it to True enables logging to the default loggers. + Setting a string overrides the logger name to which logging is performed. num_transforms: a 2-tuple, int, or None. The 2-tuple specifies the minimum and maximum (inclusive) number of transforms to sample at each iteration. If an int is given, the lower and upper bounds are set equal. None sets it to `len(transforms)`. Default to `None`. @@ -673,9 +648,6 @@ class SomeOf(Compose): :ref:`Lazy Resampling` is enabled for the pipeline or a given transform. If lazy is False they are ignored. Currently supported args are: {``"mode"``, ``"padding_mode"``, ``"dtype"``, ``"align_corners"``, ``"resample_mode"``, ``device``}. - logger_name: this optional parameter allows you to specify a logger by name for logging of pipeline execution. - Setting this to False disables logging. Setting it to True enables logging to the default loggers. - Setting a string overrides the logger name to which logging is performed. """ def __init__( @@ -689,7 +661,6 @@ def __init__( weights: list[int] | None = None, lazy: bool | None = False, overrides: dict | None = None, - logger_name: bool | str = False, ) -> None: super().__init__( transforms, @@ -698,13 +669,11 @@ def __init__( log_stats=log_stats, lazy=lazy, overrides=overrides, - logger_name=logger_name, ) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) self.log_stats = log_stats - self.logger_name = logger_name def _ensure_valid_num_transforms(self, num_transforms: int | tuple[int, int] | None) -> tuple: if ( @@ -781,7 +750,6 @@ def __call__(self, data, start=0, end=None, threading=False, lazy: bool | None = overrides=self.overrides, threading=threading, log_stats=self.log_stats, - logger_name=self.logger_name, ) if isinstance(data, monai.data.MetaTensor): self.push_transform(data, extra_info={"applied_order": applied_order}) @@ -821,7 +789,6 @@ def inverse(self, data): self.map_items, self.unpack_items, log_stats=self.log_stats, - logger_name=self.logger_name, ) return data diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 71e1718d95..9479e6faea 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -105,8 +105,7 @@ def apply_transform( unpack_items: bool = False, log_stats: bool | str = False, lazy: bool | None = False, - overrides: dict | None = None, - logger_name: bool | str = False, + overrides: dict | None = None ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -138,8 +137,8 @@ def apply_transform( """ try: if isinstance(data, (list, tuple)) and map_items: - return [_apply_transform(transform, item, unpack_items, lazy, overrides, logger_name) for item in data] - return _apply_transform(transform, data, unpack_items, lazy, overrides, logger_name) + return [_apply_transform(transform, item, unpack_items, lazy, overrides, log_stats) for item in data] + return _apply_transform(transform, data, unpack_items, lazy, overrides, log_stats) except Exception as e: # if in debug mode, don't swallow exception so that the breakpoint # appears where the exception was raised. diff --git a/tests/test_compose.py b/tests/test_compose.py index 42667c043b..892e9a23e7 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -344,10 +344,10 @@ def test_compose_execute_empty_range(self, keys, pipeline): self.assertIs(data, result) @parameterized.expand(TEST_COMPOSE_EXECUTE_TEST_CASES) - def test_compose_with_logger_name(self, keys, pipeline): + def test_compose_with_logger(self, keys, pipeline): data = data_from_keys(keys, 12, 16) - c = mt.Compose(deepcopy(pipeline), logger_name="a_logger_name") + c = mt.Compose(deepcopy(pipeline), log_stats="a_logger_name") c(data) @@ -579,7 +579,7 @@ def test_compose_with_logging(self, keys, pipeline, lazy, expected): logger.addHandler(handler) data = data_from_keys(keys, 12, 16) - c = mt.Compose(deepcopy(pipeline), lazy=lazy, logger_name="a_logger_name") + c = mt.Compose(deepcopy(pipeline), lazy=lazy, log_stats="a_logger_name") c(data) handler.flush() diff --git a/tests/test_integration_lazy_samples.py b/tests/test_integration_lazy_samples.py index 12732ffe40..c365616bc8 100644 --- a/tests/test_integration_lazy_samples.py +++ b/tests/test_integration_lazy_samples.py @@ -74,7 +74,7 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=0, readers=(None, ], lazy=lazy, overrides=lazy_kwargs, - logger_name=num_workers > 0, + log_stats=num_workers > 0, ) # create a training data loader From 2658d4b6b318c6e0260f4f58c7c6e81162e41d8d Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Fri, 26 May 2023 16:31:47 +0100 Subject: [PATCH 166/175] autofix Signed-off-by: Ben Murray --- monai/transforms/compose.py | 38 ++++++----------------------------- monai/transforms/transform.py | 2 +- 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 08b26668f7..fbc23c06a7 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -54,7 +54,7 @@ def execute_compose( lazy: bool | None = False, overrides: dict | None = None, threading: bool = False, - log_stats: bool | str = False + log_stats: bool | str = False, ) -> NdarrayOrTensor | Sequence[NdarrayOrTensor] | Mapping[Any, NdarrayOrTensor]: """ ``execute_compose`` provides the implementation that the ``Compose`` class uses to execute a sequence @@ -109,13 +109,7 @@ def execute_compose( if threading: _transform = deepcopy(_transform) if isinstance(_transform, ThreadUnsafe) else _transform data = apply_transform( - _transform, - data, - map_items, - unpack_items, - lazy=lazy, - overrides=overrides, - log_stats=log_stats + _transform, data, map_items, unpack_items, lazy=lazy, overrides=overrides, log_stats=log_stats ) data = apply_pending_transforms(data, None, overrides, logger_name=log_stats) return data @@ -355,12 +349,7 @@ def inverse(self, data): # loop backwards over transforms for t in reversed(invertible_transforms): data = apply_transform( - t.inverse, - data, - self.map_items, - self.unpack_items, - lazy=False, - log_stats=self.log_stats, + t.inverse, data, self.map_items, self.unpack_items, lazy=False, log_stats=self.log_stats ) return data @@ -607,11 +596,7 @@ def inverse(self, data): for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): data = apply_transform( - self.transforms[o].inverse, - data, - self.map_items, - self.unpack_items, - log_stats=self.log_stats, + self.transforms[o].inverse, data, self.map_items, self.unpack_items, log_stats=self.log_stats ) return data @@ -662,14 +647,7 @@ def __init__( lazy: bool | None = False, overrides: dict | None = None, ) -> None: - super().__init__( - transforms, - map_items, - unpack_items, - log_stats=log_stats, - lazy=lazy, - overrides=overrides, - ) + super().__init__(transforms, map_items, unpack_items, log_stats=log_stats, lazy=lazy, overrides=overrides) self.min_num_transforms, self.max_num_transforms = self._ensure_valid_num_transforms(num_transforms) self.replace = replace self.weights = self._normalize_probabilities(weights) @@ -784,11 +762,7 @@ def inverse(self, data): for o in reversed(applied_order): if isinstance(self.transforms[o], InvertibleTransform): data = apply_transform( - self.transforms[o].inverse, - data, - self.map_items, - self.unpack_items, - log_stats=self.log_stats, + self.transforms[o].inverse, data, self.map_items, self.unpack_items, log_stats=self.log_stats ) return data diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 9479e6faea..716970d9be 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -105,7 +105,7 @@ def apply_transform( unpack_items: bool = False, log_stats: bool | str = False, lazy: bool | None = False, - overrides: dict | None = None + overrides: dict | None = None, ) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. From 24a30eb7965ea73bc29629f6d788998c7bec320c Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 30 May 2023 10:13:57 +0100 Subject: [PATCH 167/175] Additional content and doc fixes Signed-off-by: Ben Murray --- docs/source/lazy_resampling.rst | 67 ++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/docs/source/lazy_resampling.rst b/docs/source/lazy_resampling.rst index 7b9bd91e70..7b809965f3 100644 --- a/docs/source/lazy_resampling.rst +++ b/docs/source/lazy_resampling.rst @@ -8,8 +8,6 @@ Lazy Resampling .. toctree:: :maxdepth: 2 - config_syntax.md - Introduction ^^^^^^^^^^^^ @@ -143,14 +141,15 @@ API changes ^^^^^^^^^^^ A number of new arguments have been added to existing properties, which we'll go over in detail here. In particular, -we'll focus on :class:`monai.transforms.compose.Compose` and :class:`LazyTrait`/ -:class:`LazyTransform` and the way that they interact with each other. +we'll focus on :class:`Compose and +:class:`LazyTrait`/ :class:`LazyTransform` +and the way that they interact with each other. Compose +++++++ -:class:`monai.transforms.compose.Compose` gains a number of new arguments that can be used to control +:class:`Compose` gains a number of new arguments that can be used to control resampling behaviour. Each of them is covered in its own section: @@ -161,8 +160,8 @@ lazy * `lazy=False` forces the pipeline to be executed in the standard way with every transform applied immediately * `lazy=True` forces the pipeline to be executed lazily. Every transform that implements - :class:`monai.transforms.traits.LazyTrait` (or inherits - :class:`monai.transforms.transform.LazyTransform`) will be executed lazily + :class:`LazyTrait` (or inherits + :class:`LazyTransform`) will be executed lazily * `lazy=None` means that the pipeline can execute lazily, but only on transforms that have their own `lazy` property set to True. @@ -184,8 +183,8 @@ can omit keys that don't require overrides: } -logger_name -""""""""""" +log_stats +""""""""" Logging of transform execution is provided if you wish to understand exactly how your pipelines execute. It can take a ``bool`` or ``str`` value, and is False by default, which disables logging. Otherwise, you can enable it by passing it @@ -195,7 +194,8 @@ the name of a logger that you wish to use (note, you don't have to construct the LazyTrait / LazyTransform +++++++++++++++++++++++++ -Many transforms now implement either LazyTrait or LazyTransform. Doing so marks the transform for lazy execution. Lazy +Many transforms now implement either `LazyTrait` or +`LazyTransform`. Doing so marks the transform for lazy execution. Lazy transforms have the following in common: @@ -216,17 +216,58 @@ initialisation. It has a default value of ``None``. If it is not ``None``, then default values rather than having to set it on every lazy transform (unless the user sets :class:`Compose.lazy` to ``None``). + lazy property """"""""""""" The lazy property allows you to get or set the lazy status of a lazy transform after constructing it. + requires_current_data property (get only) """"""""""""""""""""""""""""""""""""""""" The ``requires_current_data`` property indicates that a transform makes use of the data in one or more of the tensors -that it is -passed during its execution. Such transforms require that the tensors must therefore be up to date, even if the -transform itself is executing lazily. This is required for transforms such as ``CropForeground[d]``, +that it is passed during its execution. Such transforms require that the tensors must therefore be up to date, even if +the transform itself is executing lazily. This is required for transforms such as ``CropForeground[d]``, ``RandCropByPosNegLabel[d]``, and ``RandCropByLabelClasses[d]``. This property is implemented to return ``False`` on ``LazyTransform`` and must be overridden to return ``True`` by transforms that check data values when executing. + + +Controlling laziness +^^^^^^^^^^^^^^^^^^^^ + +There are two ways that a user can provide more fine-grained control over laziness. One is to make use of lazy=None +when initialising or calling ``Compose`` instances. The other is to use the ``ApplyPending[d]`` transforms. These +techniques can be freely mixed and matched. + + +Using ``lazy=None`` ++++++++++++++++++++ + +``Lazy=None`` tells ``Compose`` to honor the lazy flags set on each lazy transform. These are set to False by default +so the user must set lazy=True on the transforms that they still wish to execute lazily. + + +``lazy=None`` example: +"""""""""""""""""""""" + +.. figure:: ../images/lazy_resampling_none_example.svg + + Figure shwoing the effect of using ``lazy=False`` when ``Compose`` is being executed with ``lazy=None``. Note that + the additional resamples that occur due to ``RandRotate90d`` being executed in a non-lazy fashion. + + +Using ``ApplyPending[d]`` ++++++++++++++++++++++++++ + +``ApplyPending[d]`` causes all pending transforms to be executed before the following transform, regardless of whether +the following transform is a lazy transform, or is configured to execute lazily. + + +``ApplyPending`` Example: +""""""""""""""""""""""""" + +.. figure:: ../images/lazy_resampling_apply_pending_example.svg + + Figure showing the use of :class:`ApplyPendingd` to cause + resampling to occur in the midele of a chain of lazy transforms. From 21c9f84cd9f2d8d3e5f94da2b732aa4d0da1098c Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Tue, 30 May 2023 10:20:58 +0100 Subject: [PATCH 168/175] Missing images for lazy resampling Signed-off-by: Ben Murray --- docs/images/lazy_resampling_apply_pending_example.svg | 1 + docs/images/lazy_resampling_none_example.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 docs/images/lazy_resampling_apply_pending_example.svg create mode 100644 docs/images/lazy_resampling_none_example.svg diff --git a/docs/images/lazy_resampling_apply_pending_example.svg b/docs/images/lazy_resampling_apply_pending_example.svg new file mode 100644 index 0000000000..c065fe77a4 --- /dev/null +++ b/docs/images/lazy_resampling_apply_pending_example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/lazy_resampling_none_example.svg b/docs/images/lazy_resampling_none_example.svg new file mode 100644 index 0000000000..1a6875d72d --- /dev/null +++ b/docs/images/lazy_resampling_none_example.svg @@ -0,0 +1 @@ + \ No newline at end of file From 8bb8b1efc4afc453fc341ce942e379b29671d8da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 09:21:30 +0000 Subject: [PATCH 169/175] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/images/lazy_resampling_apply_pending_example.svg | 2 +- docs/images/lazy_resampling_none_example.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/images/lazy_resampling_apply_pending_example.svg b/docs/images/lazy_resampling_apply_pending_example.svg index c065fe77a4..2e23a10e82 100644 --- a/docs/images/lazy_resampling_apply_pending_example.svg +++ b/docs/images/lazy_resampling_apply_pending_example.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/docs/images/lazy_resampling_none_example.svg b/docs/images/lazy_resampling_none_example.svg index 1a6875d72d..ca83fa5449 100644 --- a/docs/images/lazy_resampling_none_example.svg +++ b/docs/images/lazy_resampling_none_example.svg @@ -1 +1 @@ - \ No newline at end of file + From cd651520a1a863ccee418218630416d9450fcf18 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 31 May 2023 10:02:17 +0100 Subject: [PATCH 170/175] Updating docs for has_status_key, removing dead import statement Signed-off-by: Ben Murray --- monai/transforms/compose.py | 4 +++- monai/transforms/utils.py | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index fbc23c06a7..5edb69068c 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -357,7 +357,9 @@ def inverse(self, data): def _raise_if_tensor_is_not_invertible(data: Any): from monai.transforms.utils import has_status_keys - invertible, reasons = has_status_keys(data, TraceStatusKeys.PENDING_DURING_APPLY) + invertible, reasons = has_status_keys( + data, TraceStatusKeys.PENDING_DURING_APPLY, "Pending operations while applying an operation" + ) if invertible is False: if reasons is not None: diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 8fcefdc657..f5d453596d 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -1345,8 +1345,6 @@ def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransfo with allow_missing_keys_mode(t): _ = t(data) # OK! """ - # from monai.transforms.compose import Compose - # If given a sequence of transforms, Compose them to get a single list if issequenceiterable(transform): transform = Compose(transform) @@ -2003,14 +2001,13 @@ def check_applied_operations(entry: list | dict, status_key: str, default_messag return [] -def has_status_keys(data: torch.Tensor, status_key: Any): +def has_status_keys(data: torch.Tensor, status_key: Any, default_msg: str): """ - Checks whether a given tensor is invertible. The rules are as follows: + Checks whether a given tensor is has a particular status key message on any of its + applied operations. If it doesn't, it returns the tuple `(False, None)`. If it does + it returns a tuple of True and a list of status messages for that status key. - #. If the tensor is not a MetaTensor, it is not invertible - #. If the tensor is a MetaTensor but it has `TraceStatusKeys.PENDING_DURING_APPLY` in the `TraceKeys.STATUS` of any - of its `applied_operations` it is not invertible - #. Otherwise, it is invertible + Status keys are defined in :class:`TraceStatusKeys`. This function also accepts: @@ -2023,6 +2020,8 @@ def has_status_keys(data: torch.Tensor, status_key: Any): Args: data: a `torch.Tensor` or `MetaTensor` or collections of torch.Tensor or MetaTensor, as described above + status_key: the status key to look for, from `TraceStatusKeys` + default_msg: a default message to use if the status key entry doesn't have a message set Returns: A tuple. The first entry is `False` or `True`. The second entry is the status messages that can be used for the @@ -2032,17 +2031,17 @@ def has_status_keys(data: torch.Tensor, status_key: Any): status_key_occurrences = list() if isinstance(data, (list, tuple)): for d in data: - _, reasons = has_status_keys(d, status_key) + _, reasons = has_status_keys(d, status_key, default_msg) if reasons is not None: status_key_occurrences.extend(reasons) elif isinstance(data, monai.data.MetaTensor): for op in data.applied_operations: status_key_occurrences.extend( - check_applied_operations(op, status_key, "Pending operations while applying an operation") + check_applied_operations(op, status_key, default_msg) ) elif isinstance(data, dict): for d in data.values(): - _, reasons = has_status_keys(d, status_key) + _, reasons = has_status_keys(d, status_key, default_msg) if reasons is not None: status_key_occurrences.extend(reasons) From 2e5f23d942093a111de3164af5dcf65809f5ac72 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 31 May 2023 10:25:39 +0100 Subject: [PATCH 171/175] autofix Signed-off-by: Ben Murray --- monai/transforms/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index f5d453596d..09a13945bb 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -2036,9 +2036,7 @@ def has_status_keys(data: torch.Tensor, status_key: Any, default_msg: str): status_key_occurrences.extend(reasons) elif isinstance(data, monai.data.MetaTensor): for op in data.applied_operations: - status_key_occurrences.extend( - check_applied_operations(op, status_key, default_msg) - ) + status_key_occurrences.extend(check_applied_operations(op, status_key, default_msg)) elif isinstance(data, dict): for d in data.values(): _, reasons = has_status_keys(d, status_key, default_msg) From 5a754c83384eb308060ebae67b15799b2cbdc5c5 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 31 May 2023 11:27:01 +0100 Subject: [PATCH 172/175] Removing erroneous arg from docstring Signed-off-by: Ben Murray --- monai/transforms/lazy/functional.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 70052aabed..6b95027832 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -242,9 +242,6 @@ def apply_pending(data: torch.Tensor | MetaTensor, pending: list | None = None, - device: device for resampling computation. Defaults to ``None``. - resample_mode: the mode of resampling, currently support ``"auto"``. Setting to other values will use the :py:class:`monai.transforms.SpatialResample` for resampling (instead of potentially crop/pad). - logger_name: A logger name that is used to log output generated while applying pending transforms. You can - suppress logging by setting this to None (default). - """ overrides = (overrides or {}).copy() for k in overrides: From c3b8c1ffddb929387b81a70e7cc3979fc11523d9 Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Wed, 31 May 2023 17:02:04 +0100 Subject: [PATCH 173/175] Renaming _raise_if_tensor_is_not_invertible Signed-off-by: Ben Murray --- monai/transforms/compose.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 5edb69068c..d6b948ea71 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -335,7 +335,7 @@ def __call__(self, input_, start=0, end=None, threading=False, lazy: bool | None return result def inverse(self, data): - self._raise_if_tensor_is_not_invertible(data) + self._raise_if_not_invertible(data) invertible_transforms = [t for t in self.flatten().transforms if isinstance(t, InvertibleTransform)] if not invertible_transforms: @@ -354,7 +354,7 @@ def inverse(self, data): return data @staticmethod - def _raise_if_tensor_is_not_invertible(data: Any): + def _raise_if_not_invertible(data: Any): from monai.transforms.utils import has_status_keys invertible, reasons = has_status_keys( From 0d4143c98f0ff778e0adc6326212aedcc0e88d49 Mon Sep 17 00:00:00 2001 From: monai-bot Date: Thu, 1 Jun 2023 01:29:45 +0000 Subject: [PATCH 174/175] [MONAI] code formatting Signed-off-by: monai-bot --- monai/transforms/lazy/array.py | 64 ++++++++++----------- monai/transforms/lazy/dictionary.py | 88 ++++++++++++++--------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/monai/transforms/lazy/array.py b/monai/transforms/lazy/array.py index aa635eeda9..6e6797c6e1 100644 --- a/monai/transforms/lazy/array.py +++ b/monai/transforms/lazy/array.py @@ -1,32 +1,32 @@ -# Copyright (c) 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. - -from __future__ import annotations - -from monai.transforms.traits import InvertibleTrait - -__all__ = ["ApplyPending"] - - -class ApplyPending(InvertibleTrait): - """ - ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure - resampling happens before the next transform. It doesn't do anything itself, but its presence - causes the pipeline to be executed as ApplyPending doesn't implement ```LazyTrait``. - - See ``Compose`` for a detailed explanation of the lazy resampling feature. - """ - - def __call__(self, data): - return data - - def inverse(self, data): - return data +# Copyright (c) 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. + +from __future__ import annotations + +from monai.transforms.traits import InvertibleTrait + +__all__ = ["ApplyPending"] + + +class ApplyPending(InvertibleTrait): + """ + ApplyPending can be inserted into a pipeline that is being executed lazily in order to ensure + resampling happens before the next transform. It doesn't do anything itself, but its presence + causes the pipeline to be executed as ApplyPending doesn't implement ```LazyTrait``. + + See ``Compose`` for a detailed explanation of the lazy resampling feature. + """ + + def __call__(self, data): + return data + + def inverse(self, data): + return data diff --git a/monai/transforms/lazy/dictionary.py b/monai/transforms/lazy/dictionary.py index 9fc38362cd..384fc9bf98 100644 --- a/monai/transforms/lazy/dictionary.py +++ b/monai/transforms/lazy/dictionary.py @@ -1,44 +1,44 @@ -# Copyright (c) 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. - - -from __future__ import annotations - -from monai.config import KeysCollection -from monai.transforms.traits import InvertibleTrait -from monai.transforms.transform import MapTransform - -__all__ = ["ApplyPendingd", "ApplyPendingD", "ApplyPendingDict"] - - -class ApplyPendingd(InvertibleTrait, MapTransform): - """ - ApplyPendingd can be inserted into a pipeline that is being executed lazily in order - to ensure resampling happens before the next transform. It doesn't do anything itself, - but its presence causes the pipeline to be executed as it doesn't implement ``LazyTrait`` - - See ``Compose`` for a detailed explanation of the lazy resampling feature. - - Args: - keys: the keys for tensors that should have their pending transforms executed - """ - - def __init__(self, keys: KeysCollection): - super().__init__(keys) - - def __call__(self, data): - return data - - def inverse(self, data): - return data - - -ApplyPendingD = ApplyPendingDict = ApplyPendingd +# Copyright (c) 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. + + +from __future__ import annotations + +from monai.config import KeysCollection +from monai.transforms.traits import InvertibleTrait +from monai.transforms.transform import MapTransform + +__all__ = ["ApplyPendingd", "ApplyPendingD", "ApplyPendingDict"] + + +class ApplyPendingd(InvertibleTrait, MapTransform): + """ + ApplyPendingd can be inserted into a pipeline that is being executed lazily in order + to ensure resampling happens before the next transform. It doesn't do anything itself, + but its presence causes the pipeline to be executed as it doesn't implement ``LazyTrait`` + + See ``Compose`` for a detailed explanation of the lazy resampling feature. + + Args: + keys: the keys for tensors that should have their pending transforms executed + """ + + def __init__(self, keys: KeysCollection): + super().__init__(keys) + + def __call__(self, data): + return data + + def inverse(self, data): + return data + + +ApplyPendingD = ApplyPendingDict = ApplyPendingd From e766652c134452831a800337df0701c717add1af Mon Sep 17 00:00:00 2001 From: Ben Murray Date: Thu, 1 Jun 2023 10:25:38 +0100 Subject: [PATCH 175/175] Removing type: ignore from auto3dseg/data_analyser.py Signed-off-by: Ben Murray --- monai/apps/auto3dseg/data_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/apps/auto3dseg/data_analyzer.py b/monai/apps/auto3dseg/data_analyzer.py index 9e8b081104..2df043e506 100644 --- a/monai/apps/auto3dseg/data_analyzer.py +++ b/monai/apps/auto3dseg/data_analyzer.py @@ -212,7 +212,7 @@ def get_all_case_stats(self, key="training", transform_list=None): manager_list = manager.list() processes = [] for rank in range(nprocs): - p = tmp_ctx.Process( # type: ignore[attr-defined] + p = tmp_ctx.Process( target=self._get_all_case_stats, args=(rank, nprocs, manager_list, key, transform_list) ) processes.append(p)