Skip to content

Resize

Bases: SpatialTransform

Resize images to a target spatial shape.

The field of view is preserved; voxel spacing is adjusted to fit the new shape.

Warning

In most medical image applications, this transform should not be used as it scales anisotropically. Prefer Resample (change spacing) combined with CropOrPad (change shape) instead.

Parameters:

Name Type Description Default
target_shape int | tuple[int, int, int]

Target spatial shape \((I, J, K)\). A single integer \(N\) means \((N, N, N)\).

required
image_interpolation str

"linear" (default) for intensity images.

'linear'
label_interpolation str

"nearest" (default) for label maps.

'nearest'
**kwargs Any

See Transform.

{}

Examples:

>>> import torchio as tio
>>> transform = tio.Resize(128)
>>> transform = tio.Resize((256, 256, 64))
Source code in src/torchio/transforms/spatial/resize.py
class Resize(SpatialTransform):
    r"""Resize images to a target spatial shape.

    The field of view is preserved; voxel spacing is adjusted to fit
    the new shape.

    Warning:
        In most medical image applications, this transform should
        **not** be used as it scales anisotropically.  Prefer
        [`Resample`][torchio.Resample] (change spacing) combined with
        [`CropOrPad`][torchio.CropOrPad] (change shape) instead.

    Args:
        target_shape: Target spatial shape $(I, J, K)$.  A single
            integer $N$ means $(N, N, N)$.
        image_interpolation: `"linear"` (default) for intensity
            images.
        label_interpolation: `"nearest"` (default) for label maps.
        **kwargs: See [`Transform`][torchio.Transform].

    Examples:
        >>> import torchio as tio
        >>> transform = tio.Resize(128)
        >>> transform = tio.Resize((256, 256, 64))
    """

    def __init__(
        self,
        target_shape: int | tuple[int, int, int],
        *,
        image_interpolation: str = "linear",
        label_interpolation: str = "nearest",
        **kwargs: Any,
    ) -> None:
        super().__init__(**kwargs)
        if isinstance(target_shape, int):
            target_shape = (target_shape, target_shape, target_shape)
        self.target_shape = target_shape
        self.image_interpolation = image_interpolation
        self.label_interpolation = label_interpolation

    def make_params(self, batch: SubjectsBatch) -> dict[str, Any]:
        """No random parameters."""
        return {"target_shape": self.target_shape}

    def apply_transform(
        self,
        batch: SubjectsBatch,
        params: dict[str, Any],
    ) -> SubjectsBatch:
        """Resize each image to the target shape."""
        target = list(params["target_shape"])
        for _name, img_batch in batch.images.items():
            is_label = issubclass(img_batch._image_class, LabelMap)
            mode = self.label_interpolation if is_label else self.image_interpolation
            torch_mode = "nearest" if mode == "nearest" else "trilinear"
            old_shape = img_batch.data.shape[2:]
            img_batch.data = functional.interpolate(
                img_batch.data.float(),
                size=target,
                mode=torch_mode,
                align_corners=None if torch_mode == "nearest" else True,
            ).to(img_batch.data.dtype)
            # Update affines: spacing changes to fit new shape in same FOV.
            for affine in img_batch.affines:
                for axis in range(3):
                    factor = old_shape[axis] / target[axis]
                    affine._matrix[:3, axis] *= factor
        return batch

invertible property

Whether this transform can be inverted.

forward(data)

forward(data: Subject) -> Subject
forward(data: Image) -> Image
forward(data: Tensor) -> Tensor
forward(data: np.ndarray) -> np.ndarray
forward(data: sitk.Image) -> sitk.Image
forward(data: nib.Nifti1Image) -> nib.Nifti1Image
forward(data: dict) -> dict
forward(data: ImagesBatch) -> ImagesBatch
forward(data: SubjectsBatch) -> SubjectsBatch

Apply the transform.

The output type always matches the input type.

Parameters:

Name Type Description Default
data Any

Input data to transform.

required
Source code in src/torchio/transforms/transform.py
def forward(self, data: Any) -> Any:
    """Apply the transform.

    The output type always matches the input type.

    Args:
        data: Input data to transform.
    """
    if self.copy:
        data = _copy.deepcopy(data)
    batch, unwrap = self._wrap(data)
    if torch.rand(1).item() > self.p:
        return unwrap(batch)
    params = self.make_params(batch)
    batch = self.apply_transform(batch, params)
    # Record history on the batch
    trace = AppliedTransform(name=type(self).__name__, params=params)
    if not hasattr(batch, "applied_transforms"):
        batch.applied_transforms = []
    batch.applied_transforms.append(trace)
    result = unwrap(batch)
    # Propagate history to outputs that can carry it
    if (
        hasattr(batch, "applied_transforms")
        and not isinstance(result, (SubjectsBatch, Tensor, np.ndarray))
        and not isinstance(result, dict)
    ):
        with contextlib.suppress(AttributeError):
            result.applied_transforms = list(batch.applied_transforms)
    return result

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

make_params(batch)

No random parameters.

Source code in src/torchio/transforms/spatial/resize.py
def make_params(self, batch: SubjectsBatch) -> dict[str, Any]:
    """No random parameters."""
    return {"target_shape": self.target_shape}

apply_transform(batch, params)

Resize each image to the target shape.

Source code in src/torchio/transforms/spatial/resize.py
def apply_transform(
    self,
    batch: SubjectsBatch,
    params: dict[str, Any],
) -> SubjectsBatch:
    """Resize each image to the target shape."""
    target = list(params["target_shape"])
    for _name, img_batch in batch.images.items():
        is_label = issubclass(img_batch._image_class, LabelMap)
        mode = self.label_interpolation if is_label else self.image_interpolation
        torch_mode = "nearest" if mode == "nearest" else "trilinear"
        old_shape = img_batch.data.shape[2:]
        img_batch.data = functional.interpolate(
            img_batch.data.float(),
            size=target,
            mode=torch_mode,
            align_corners=None if torch_mode == "nearest" else True,
        ).to(img_batch.data.dtype)
        # Update affines: spacing changes to fit new shape in same FOV.
        for affine in img_batch.affines:
            for axis in range(3):
                factor = old_shape[axis] / target[axis]
                affine._matrix[:3, axis] *= factor
    return batch