Skip to content

CornucopiaAdapter

Bases: Transform

Wrap a Cornucopia transform for use in TorchIO pipelines.

Cornucopia <https://cornucopia.readthedocs.io/>_ transforms operate on (C, I, J, K) tensors and support passing multiple tensors to share spatial parameters (e.g., the same elastic deformation is applied to an image and its segmentation).

The adapter extracts image tensors from the subject, passes them to the Cornucopia transform as positional arguments (scalar images first, then label maps), and writes the results back.

Parameters:

Name Type Description Default
cornucopia_transform Callable

A Cornucopia transform (any callable accepting one or more (C, I, J, K) tensors). Requires cornucopia to be installed: pip install cornucopia.

required
**kwargs Any

See Transform.

{}

Examples:

>>> import torchio as tio
>>> import cornucopia as cc
>>> adapter = tio.CornucopiaAdapter(
...     cc.ElasticTransform(),
... )
>>> result = adapter(subject)
Note

CornucopiaAdapter does not record itself in the subject's transform history, because Cornucopia transform objects are not guaranteed to be serializable.

Source code in src/torchio/transforms/cornucopia_adapter.py
class CornucopiaAdapter(Transform):
    """Wrap a Cornucopia transform for use in TorchIO pipelines.

    `Cornucopia <https://cornucopia.readthedocs.io/>`_ transforms
    operate on `(C, I, J, K)` tensors and support passing multiple
    tensors to share spatial parameters (e.g., the same elastic
    deformation is applied to an image and its segmentation).

    The adapter extracts image tensors from the subject, passes them
    to the Cornucopia transform as positional arguments (scalar images
    first, then label maps), and writes the results back.

    Args:
        cornucopia_transform: A Cornucopia transform (any callable
            accepting one or more `(C, I, J, K)` tensors).
            Requires `cornucopia` to be installed:
            `pip install cornucopia`.
        **kwargs: See [`Transform`][torchio.Transform].

    Examples:
        >>> import torchio as tio
        >>> import cornucopia as cc  # doctest: +SKIP
        >>> adapter = tio.CornucopiaAdapter(
        ...     cc.ElasticTransform(),
        ... )  # doctest: +SKIP
        >>> result = adapter(subject)  # doctest: +SKIP

    Note:
        `CornucopiaAdapter` does **not** record itself in the
        subject's transform history, because Cornucopia transform
        objects are not guaranteed to be serializable.
    """

    def __init__(
        self,
        cornucopia_transform: Callable,
        **kwargs: Any,
    ) -> None:
        super().__init__(**kwargs)
        if not callable(cornucopia_transform):
            msg = (
                "cornucopia_transform must be callable, "
                f"got {type(cornucopia_transform).__name__}"
            )
            raise TypeError(msg)
        self.cornucopia_transform = cornucopia_transform

    def forward(self, data: Any) -> Any:
        """Apply without recording history."""
        batch, unwrap = self._wrap(data)
        if self.copy:
            batch = _copy.deepcopy(batch)
        if torch.rand(1).item() > self.p:
            return unwrap(batch)
        subjects = batch.unbatch()
        for subject in subjects:
            _apply_cornucopia(subject, self.cornucopia_transform, self)
        from ..data.batch import SubjectsBatch

        result = SubjectsBatch.from_subjects(subjects)
        result.applied_transforms = batch.applied_transforms
        return unwrap(result)

    def apply_transform(
        self,
        batch: SubjectsBatch,
        params: dict[str, Any],
    ) -> SubjectsBatch:
        """Not used: CornucopiaAdapter overrides forward directly."""
        return batch

    def add_transform_to_subject_history(self, *args: Any) -> None:
        """No-op: Cornucopia transforms are opaque."""

    @property
    def invertible(self) -> bool:
        """Cornucopia transforms are not invertible through TorchIO."""
        return False

invertible property

Cornucopia transforms are not invertible through TorchIO.

make_params(batch)

Sample random parameters for this transform.

Override in subclasses that have random behavior.

Parameters:

Name Type Description Default
batch SubjectsBatch

A SubjectsBatch.

required

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.

Source code in src/torchio/transforms/cornucopia_adapter.py
def forward(self, data: Any) -> Any:
    """Apply without recording history."""
    batch, unwrap = self._wrap(data)
    if self.copy:
        batch = _copy.deepcopy(batch)
    if torch.rand(1).item() > self.p:
        return unwrap(batch)
    subjects = batch.unbatch()
    for subject in subjects:
        _apply_cornucopia(subject, self.cornucopia_transform, self)
    from ..data.batch import SubjectsBatch

    result = SubjectsBatch.from_subjects(subjects)
    result.applied_transforms = batch.applied_transforms
    return unwrap(result)

apply_transform(batch, params)

Not used: CornucopiaAdapter overrides forward directly.

Source code in src/torchio/transforms/cornucopia_adapter.py
def apply_transform(
    self,
    batch: SubjectsBatch,
    params: dict[str, Any],
) -> SubjectsBatch:
    """Not used: CornucopiaAdapter overrides forward directly."""
    return batch

add_transform_to_subject_history(*args)

No-op: Cornucopia transforms are opaque.

Source code in src/torchio/transforms/cornucopia_adapter.py
def add_transform_to_subject_history(self, *args: Any) -> None:
    """No-op: Cornucopia transforms are opaque."""