Repository: Project-MONAI/MONAI
File: monai/transforms/croppad/functional.py (Python)

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

    Args:
        img: data to be transformed, assuming `img` is channel-first and padding doesn't apply to the channel dim.
        to_pad: the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), ...].
            default to `self.to_pad`.
        mode: 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.
    """
    if mode in {"linear_ramp", "maximum", "mean", "median", "minimum", "symmetric", "empty"}:
        return _np_pad(img, pad_width=to_pad, mode=mode, **kwargs)
    try:
        _pad = _np_pad
        if mode in {"constant", "reflect", "edge", "replicate", "wrap", "circular"}:
            # Try PyTorch pad for these modes; fallback to NumPy on error.
            _pad = _pt_pad
        call_kwargs = dict(kwargs)
        if mode != "constant":
            call_kwargs.pop("value", None)
        return _pad(img, pad_width=to_pad, mode=mode, **call_kwargs)
    except NotImplementedError:
        # PyTorch does not support this combination, fall back to NumPy
        return _np_pad(img, pad_width=to_pad, mode=mode, **call_kwargs)
    except (ValueError, TypeError, RuntimeError) as err:
        # PyTorch may raise generic errors for unsupported modes/dtypes or kwargs.
        # Since there are no stable exception types for these cases, we fall back
        # to NumPy by matching known error message patterns.
        if any(
            k in str(err) for k in ("supported", "unexpected keyword", "implemented", "value")
        ):
            return _np_pad(img, pad_width=to_pad, mode=mode, **call_kwargs)
        raise ValueError(
            f"{img.shape} {to_pad} {mode} {kwargs} {img.dtype} {img.device if isinstance(img, torch.Tensor) else None}"
        ) from err


def crop_or_pad_nd(img: torch.Tensor, translation_mat, spatial_size: tuple[int, ...], mode: str, **kwargs):
    """
    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 :py:class:`monai.transforms.SpatialResample`.

    Args:
        img: 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,

Review comment (suggestion):
If this condition isn't present, the effect of providing a `value` argument and not use "constant" mode is to raise an exception in the pad routine used, I think this is intended behaviour. Here if the `value` argument is removed this silently allows unintended arguments to be ignored, it's better to raise an exception instead.

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

    Args:
        img: data to be transformed, assuming `img` is channel-first and padding doesn't apply to the channel dim.
        to_pad: the amount to be padded in each dimension [(low_H, high_H), (low_W, high_W), ...].
            default to `self.to_pad`.
        mode: 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.
    Raises:
        ValueError: If `value` is provided when `mode` is not ``"constant"``.
    """
    if mode != "constant" and "value" in kwargs:
        raise ValueError("'value' argument is only valid when mode='constant'")
    if mode in {"linear_ramp", "maximum", "mean", "median", "minimum", "symmetric", "empty"}:
        return _np_pad(img, pad_width=to_pad, mode=mode, **kwargs)
    try:
        _pad = _np_pad
        if mode in {"constant", "reflect", "edge", "replicate", "wrap", "circular"}:
            # Try PyTorch pad for these modes; fallback to NumPy on error.
            _pad = _pt_pad
        return _pad(img, pad_width=to_pad, mode=mode, **kwargs)
    except NotImplementedError:
        # PyTorch does not support this combination, fall back to NumPy
        return _np_pad(img, pad_width=to_pad, mode=mode, **kwargs)
    except (ValueError, TypeError, RuntimeError) as err:
        # PyTorch may raise generic errors for unsupported modes/dtypes or kwargs.
        # Since there are no stable exception types for these cases, we fall back
        # to NumPy by matching known error message patterns.
        if any(k in str(err) for k in ("supported", "unexpected keyword", "implemented", "value")):
            return _np_pad(img, pad_width=to_pad, mode=mode, **kwargs)
        raise ValueError(
            f"{img.shape} {to_pad} {mode} {kwargs} {img.dtype} {img.device if isinstance(img, torch.Tensor) else None}"
        ) from err


def crop_or_pad_nd(img: torch.Tensor, translation_mat, spatial_size: tuple[int, ...], mode: str, **kwargs):
    """
    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 :py:class:`monai.transforms.SpatialResample`.

    Args:
        img: 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, :py:func:`monai.transforms.utils.create_translate`. The translation coefficients are rounded