Bases: Transform
Wrap a MONAI transform for use in TorchIO pipelines.
Both dictionary transforms (subclasses of MONAI's
MapTransform, e.g., NormalizeIntensityd) and array
transforms (e.g., NormalizeIntensity) are supported.
Dictionary transforms operate on the full subject dictionary:
only the keys specified in the MONAI transform are modified.
Array transforms are applied to each
ScalarImage in the subject individually,
respecting the include / exclude parameters.
Parameters:
| Name |
Type |
Description |
Default |
monai_transform
|
Callable
|
A MONAI transform or any callable. Requires
MONAI to be installed: pip install torchio[monai].
|
required
|
**kwargs
|
Any
|
See Transform for additional
keyword arguments.
|
{}
|
Examples:
>>> import torchio as tio
>>> from monai.transforms import NormalizeIntensity
>>> # Array transform: applied to each ScalarImage
>>> adapter = tio.MonaiAdapter(NormalizeIntensity())
>>> result = adapter(subject)
>>> # Inside a Compose pipeline
>>> pipeline = tio.Compose([
... tio.MonaiAdapter(NormalizeIntensity()),
... tio.Noise(std=0.1),
... ])
Note
MonaiAdapter does not record itself in the subject's
transform history, because MONAI transform objects are not
serializable.
Source code in src/torchio/transforms/monai_adapter.py
| class MonaiAdapter(Transform):
"""Wrap a MONAI transform for use in TorchIO pipelines.
Both **dictionary transforms** (subclasses of MONAI's
`MapTransform`, e.g., `NormalizeIntensityd`) and **array
transforms** (e.g., `NormalizeIntensity`) are supported.
Dictionary transforms operate on the full subject dictionary:
only the keys specified in the MONAI transform are modified.
Array transforms are applied to each
[`ScalarImage`][torchio.ScalarImage] in the subject individually,
respecting the `include` / `exclude` parameters.
Args:
monai_transform: A MONAI transform or any callable. Requires
MONAI to be installed: `pip install torchio[monai]`.
**kwargs: See [`Transform`][torchio.Transform] for additional
keyword arguments.
Examples:
>>> import torchio as tio
>>> from monai.transforms import NormalizeIntensity
>>> # Array transform: applied to each ScalarImage
>>> adapter = tio.MonaiAdapter(NormalizeIntensity())
>>> result = adapter(subject)
>>> # Inside a Compose pipeline
>>> pipeline = tio.Compose([
... tio.MonaiAdapter(NormalizeIntensity()),
... tio.Noise(std=0.1),
... ])
Note:
`MonaiAdapter` does **not** record itself in the subject's
transform history, because MONAI transform objects are not
serializable.
"""
def __init__(self, monai_transform: Callable, **kwargs: Any) -> None:
super().__init__(**kwargs)
if not callable(monai_transform):
msg = (
"monai_transform must be callable, "
f"got {type(monai_transform).__name__}"
)
raise TypeError(msg)
self.monai_transform = monai_transform
def forward(self, data):
"""Apply without recording history (MONAI transforms are opaque)."""
batch, unwrap = self._wrap(data)
if self.copy:
batch = _copy.deepcopy(batch)
if torch.rand(1).item() > self.p:
return unwrap(batch)
# MONAI transforms operate per-subject
monai = get_monai()
subjects = batch.unbatch()
for subject in subjects:
is_dict = isinstance(
self.monai_transform,
monai.transforms.MapTransform,
)
if is_dict:
_apply_dict_transform(subject, self.monai_transform, monai)
else:
images = self._get_subject_images(subject)
_apply_array_transform(images, self.monai_transform, monai)
from ..data.batch import SubjectsBatch
result = SubjectsBatch.from_subjects(subjects)
result.applied_transforms = batch.applied_transforms
return unwrap(result)
def apply_transform(self, batch: Any, params: dict[str, Any]) -> Any:
# Not used: MonaiAdapter overrides forward directly
return batch
def _get_subject_images(self, subject: Subject) -> dict[str, Image]:
"""Filter to ScalarImage, then apply include/exclude."""
images: dict[str, Image] = {
k: v for k, v in subject.images.items() if isinstance(v, ScalarImage)
}
if self.include is not None:
images = {k: v for k, v in images.items() if k in self.include}
if self.exclude is not None:
images = {k: v for k, v in images.items() if k not in self.exclude}
return images
|
invertible
property
Whether this transform can be inverted.
make_params(batch)
Sample random parameters for this transform.
Override in subclasses that have random behavior.
Parameters:
Returns:
| Type |
Description |
dict[str, Any]
|
Dict of sampled parameters.
|
Source code in src/torchio/transforms/transform.py
| def make_params(self, batch: SubjectsBatch) -> dict[str, Any]:
"""Sample random parameters for this transform.
Override in subclasses that have random behavior.
Args:
batch: A `SubjectsBatch`.
Returns:
Dict of sampled parameters.
"""
return {}
|
inverse(params)
Return a transform that undoes this one.
Override in invertible subclasses. The returned transform,
when applied, reverses the effect of the forward pass with
the given parameters.
Parameters:
| Name |
Type |
Description |
Default |
params
|
dict[str, Any]
|
The parameters recorded in the forward pass.
|
required
|
Returns:
| Type |
Description |
Transform
|
A new Transform instance that inverts this one.
|
Source code in src/torchio/transforms/transform.py
| def inverse(self, params: dict[str, Any]) -> Transform:
"""Return a transform that undoes this one.
Override in invertible subclasses. The returned transform,
when applied, reverses the effect of the forward pass with
the given parameters.
Args:
params: The parameters recorded in the forward pass.
Returns:
A new `Transform` instance that inverts this one.
"""
msg = f"{type(self).__name__} is not invertible"
raise NotImplementedError(msg)
|
to_hydra()
Export as a Hydra-compatible config dict.
Returns a dict with _target_ set to the fully qualified
class name and only non-default field values included.
Returns:
| Type |
Description |
dict[str, Any]
|
Dict suitable for hydra.utils.instantiate().
|
Source code in src/torchio/transforms/transform.py
| def to_hydra(self) -> dict[str, Any]:
"""Export as a Hydra-compatible config dict.
Returns a dict with `_target_` set to the fully qualified
class name and only non-default field values included.
Returns:
Dict suitable for `hydra.utils.instantiate()`.
"""
from .parameter_range import _ParameterRange
cls = type(self)
target = f"torchio.{cls.__qualname__}"
cfg: dict[str, Any] = {"_target_": target}
for name, default in _collect_init_params(cls).items():
value = getattr(self, name, default)
if isinstance(value, _ParameterRange):
if value._original == default:
continue
value = _hydra_value(value._original)
elif value == default:
continue
else:
value = _hydra_value(value)
cfg[name] = value
return cfg
|
forward(data)
Apply without recording history (MONAI transforms are opaque).
Source code in src/torchio/transforms/monai_adapter.py
| def forward(self, data):
"""Apply without recording history (MONAI transforms are opaque)."""
batch, unwrap = self._wrap(data)
if self.copy:
batch = _copy.deepcopy(batch)
if torch.rand(1).item() > self.p:
return unwrap(batch)
# MONAI transforms operate per-subject
monai = get_monai()
subjects = batch.unbatch()
for subject in subjects:
is_dict = isinstance(
self.monai_transform,
monai.transforms.MapTransform,
)
if is_dict:
_apply_dict_transform(subject, self.monai_transform, monai)
else:
images = self._get_subject_images(subject)
_apply_array_transform(images, self.monai_transform, monai)
from ..data.batch import SubjectsBatch
result = SubjectsBatch.from_subjects(subjects)
result.applied_transforms = batch.applied_transforms
return unwrap(result)
|