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

Code before:
        for op in data.applied_operations:
            status_key_occurrences.extend(check_applied_operations(op, status_key, default_message))
    elif isinstance(data, dict):
        for d in data.values():
            _, reasons = has_status_keys(d, status_key, default_message)
            if reasons is not None:
                status_key_occurrences.extend(reasons)

    if len(status_key_occurrences) > 0:
        return False, status_key_occurrences
    return True, None


def distance_transform_edt(
    img: NdarrayOrTensor, sampling: None | float | list[float] = None, force_scipy: bool = False
) -> NdarrayOrTensor:
    """
    Euclidean distance transform, either GPU based with CuPy / cuCIM
    or CPU based with scipy.ndimage.
    Choice depends on cuCIM being available or scipy can be forced with the ``force_scipy`` flag.

    Note that the runtime running on the CPU may be really depending on the inputs size.

    Args:
        img: Input image on which the distance transform shall be run.
            For CuPy shape must be ([channel, ]spatial_dim1, spatial_dim2 [, spatial_dim3]), so 2D, 3D and 4D input only.
            If you need to run the transform on other shapes, use the ``force_scipy`` flag.
            4D input gets passed channel-wise to cupy.
        sampling: Spacing of elements along each dimension. If a sequence, must be of length equal to the input rank;
            if a single number, this is used for all axes. If not specified, a grid spacing of unity is implied.
        force_scipy: Force the CPU based scipy implementation of the euclidean distance transform
    """
    distance_transform_edt, has_cucim = optional_import(
        "cucim.core.operations.morphology", name="distance_transform_edt"
    )

    if has_cp and has_cucim and not force_scipy:
        img_ = convert_to_cupy(img)
        if img_.ndim == 4:
            out = []
            for channel in img_:
                out.append(distance_transform_edt(channel, sampling=sampling))
            distance = cp.stack(out)
        else:
            distance = distance_transform_edt(img_, sampling=sampling)
    else:
        if not has_ndimage:
            raise RuntimeError("scipy.ndimage required if cupy is not available")
        img_ = convert_to_numpy(img)
        distance = ndimage.distance_transform_edt(img_, sampling=sampling)


Review comment (suggestion):
```suggestion
            channel first array, must have shape: (num_channels, H[, W, ..., ])
```

Code after:
        for op in data.applied_operations:
            status_key_occurrences.extend(check_applied_operations(op, status_key, default_message))
    elif isinstance(data, dict):
        for d in data.values():
            _, reasons = has_status_keys(d, status_key, default_message)
            if reasons is not None:
                status_key_occurrences.extend(reasons)

    if len(status_key_occurrences) > 0:
        return False, status_key_occurrences
    return True, None


def distance_transform_edt(
    img: NdarrayOrTensor,
    sampling: None | float | list[float] = None,
    return_distances: bool = True,
    return_indices: bool = False,
    distances: NdarrayOrTensor | None = None,
    indices: NdarrayOrTensor | None = None,
    *,
    block_params: tuple[int, int, int] | None = None,
    float64_distances: bool = False,
) -> None | NdarrayOrTensor | tuple[NdarrayOrTensor, NdarrayOrTensor]:
    """
    Euclidean distance transform, either GPU based with CuPy / cuCIM or CPU based with scipy.
    To use the GPU implementation, make sure cuCIM is available and that the data is a `torch.tensor` on a GPU device.

    Note that the results of the libraries can differ, so stick to one if possible.
    For details, check out the `SciPy`_ and `cuCIM`_ documentation.

    .. _SciPy: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.distance_transform_edt.html
    .. _cuCIM: https://docs.rapids.ai/api/cucim/nightly/api/#cucim.core.operations.morphology.distance_transform_edt

    Args:
        img: Input image on which the distance transform shall be run.
            Has to be a channel first array, must have shape: (num_channels, H, W [,D]).
            Can be of any type but will be converted into binary: 1 wherever image equates to True, 0 elsewhere.
            Input gets passed channel-wise to the distance-transform, thus results from this function will differ
            from directly calling ``distance_transform_edt()`` in CuPy or SciPy.
        sampling: Spacing of elements along each dimension. If a sequence, must be of length equal to the input rank -1;
            if a single number, this is used for all axes. If not specified, a grid spacing of unity is implied.
        return_distances: Whether to calculate the distance transform.
        return_indices: Whether to calculate the feature transform.
        distances: An output array to store the calculated distance transform, instead of returning it.
            `return_distances` must be True.
        indices: An output array to store the calculated feature transform, instead of returning it. `return_indicies` must be True.
        block_params: This parameter is specific to cuCIM and does not exist in SciPy. For details, look into `cuCIM`_.
        float64_distances: This parameter is specific to cuCIM and does not exist in SciPy.
            If True, use double precision in the distance computation (to match SciPy behavior).
            Otherwise, single precision will be used for efficiency.