def visualize_mask(
    mask: Image.Image | torch.Tensor,
    *,
    mode: Literal["contour", "fill", "binary", "L", "1"] = "binary",
    alpha: float = 0.5,
    color: tuple[int, int, int] = (255, 0, 0),
    background_color: tuple[int, int, int, int] = (0, 0, 0, 0),
) -> Image.Image:
    """Visualize a mask with different modes.

    This function takes a binary mask and visualizes it in different styles based on the
    specified mode.

    Args:
        mask (:class:`PIL.Image.Image` | :class:`torch.Tensor`): Input mask to visualize.
            Can be a PIL Image or PyTorch tensor. If tensor, should be 2D with values in
            [0, 1] or [0, 255].
        mode (Literal["contour", "fill", "binary", "L", "1"]): Visualization mode:

            - ``"contour"``: Draw contours around masked regions
            - ``"fill"``: Fill masked regions with semi-transparent color
            - ``"binary"``: Return original binary mask
            - ``"L"``: Return original grayscale mask
            - ``"1"``: Return original binary mask

        alpha (float, optional): Alpha value for blending in ``"fill"`` mode.
            Should be between 0.0 and 1.0. Defaults to ``0.5``.
        color (tuple[int, int, int], optional): RGB color to apply to mask.
            Each value should be 0-255. Defaults to ``(255, 0, 0)`` (red).
        background_color (tuple[int, int, int, int], optional): RGBA background color.
            Each value should be 0-255. Defaults to ``(0, 0, 0, 0)`` (transparent).

    Returns:
        :class:`PIL.Image.Image`: Visualized mask as a PIL Image. The output mode
        depends on the visualization mode:

        - ``"contour"`` and ``"fill"``: Returns RGBA image
        - ``"binary"``, ``"L"``, ``"1"``: Returns grayscale image

    Raises:
        TypeError: If ``mask`` is not a PIL Image or PyTorch tensor.
        ValueError: If ``mode`` is not one of the allowed values.

    Examples:
        Create a random binary mask:

        >>> import numpy as np
        >>> from PIL import Image
        >>> mask_array = np.random.randint(0, 2, size=(100, 100), dtype=np.uint8) * 255
        >>> mask_image = Image.fromarray(mask_array, mode='L')

        Visualize mask contours in red:

        >>> contour_vis = visualize_mask(
        ...     mask_image,
        ...     mode="contour",
        ...     color=(255, 0, 0)
        ... )
        >>> isinstance(contour_vis, Image.Image)
        True

        Fill mask regions with semi-transparent green:

        >>> fill_vis = visualize_mask(
        ...     mask_image,
        ...     mode="fill",
        ...     color=(0, 255, 0),
        ...     alpha=0.3
        ... )
        >>> isinstance(fill_vis, Image.Image)
        True

        Return original binary mask:

        >>> binary_vis = visualize_mask(mask_image, mode="binary")
        >>> binary_vis.mode
        'L'

    Note:
        - Input tensors are automatically converted to PIL Images
        - Binary masks are expected to have values of 0 and 255 (or 0 and 1 for tensors)
        - The function preserves the original mask when using ``"binary"``, ``"L"`` or
          ``"1"`` modes
        - ``"contour"`` mode uses edge detection to find mask boundaries
        - ``"fill"`` mode creates a semi-transparent overlay using the specified color
    """
    # Convert torch.Tensor to PIL Image if necessary
    if isinstance(mask, torch.Tensor):
        if mask.dtype == torch.bool:
            mask = mask.to(torch.uint8) * 255
        mask = to_pil_image(mask)

    if not isinstance(mask, Image.Image):
        msg = "Mask must be a PIL Image or PyTorch tensor"
        raise TypeError(msg)

    # Ensure mask is in binary mode
    mask = mask.convert("L")
    if mode in {"binary", "L", "1"}:
        return mask

    # Create a background image
    background = Image.new("RGBA", mask.size, background_color)

    match mode:
        case "contour":
            # Find edges of the mask
            edges = mask.filter(ImageFilter.FIND_EDGES)

            # Create a colored version of the edges
            colored_edges = Image.new("RGBA", mask.size, (*color, 255))
            colored_edges.putalpha(edges)

            # Composite the colored edges onto the background
            return Image.alpha_composite(background, colored_edges)

        case "fill":
            # Create a solid color image for the overlay
            overlay = Image.new("RGBA", mask.size, (*color, int(255 * alpha)))

            # Use the mask to blend the overlay with the background
            return Image.composite(overlay, background, mask)

        case _:
            msg = f"Invalid mode: {mode}. Allowed modes are 'contour', 'binary', or 'fill'."
            raise ValueError(msg)
Visualize a mask with different modes.

    This function takes a binary mask and visualizes it in different styles based on the
    specified mode.

    Args:
        mask (:class:`PIL.Image.Image` | :class:`torch.Tensor`): Input mask to visualize.
            Can be a PIL Image or PyTorch tensor. If tensor, should be 2D with values in
            [0, 1] or [0, 255].
        mode (Literal["contour", "fill", "binary", "L", "1"]): Visualization mode:

            - ``"contour"``: Draw contours around masked regions
            - ``"fill"``: Fill masked regions with semi-transparent color
            - ``"binary"``: Return original binary mask
            - ``"L"``: Return original grayscale mask
            - ``"1"``: Return original binary mask

        alpha (float, optional): Alpha value for blending in ``"fill"`` mode.
            Should be between 0.0 and 1.0. Defaults to ``0.5``.
        color (tuple[int, int, int], optional): RGB color to apply to mask.
            Each value should be 0-255. Defaults to ``(255, 0, 0)`` (red).
        background_color (tuple[int, int, int, int], optional): RGBA background color.
            Each value should be 0-255. Defaults to ``(0, 0, 0, 0)`` (transparent).

    Returns:
        :class:`PIL.Image.Image`: Visualized mask as a PIL Image. The output mode
        depends on the visualization mode:

        - ``"contour"`` and ``"fill"``: Returns RGBA image
        - ``"binary"``, ``"L"``, ``"1"``: Returns grayscale image

    Raises:
        TypeError: If ``mask`` is not a PIL Image or PyTorch tensor.
        ValueError: If ``mode`` is not one of the allowed values.

    Examples:
        Create a random binary mask:

        >>> import numpy as np
        >>> from PIL import Image
        >>> mask_array = np.random.randint(0, 2, size=(100, 100), dtype=np.uint8) * 255
        >>> mask_image = Image.fromarray(mask_array, mode='L')

        Visualize mask contours in red:

        >>> contour_vis = visualize_mask(
        ...     mask_image,
        ...     mode="contour",
        ...     color=(255, 0, 0)
        ... )
        >>> isinstance(contour_vis, Image.Image)
        True

        Fill mask regions with semi-transparent green:

        >>> fill_vis = visualize_mask(
        ...     mask_image,
        ...     mode="fill",
        ...     color=(0, 255, 0),
        ...     alpha=0.3
        ... )
        >>> isinstance(fill_vis, Image.Image)
        True

        Return original binary mask:

        >>> binary_vis = visualize_mask(mask_image, mode="binary")
        >>> binary_vis.mode
        'L'

    Note:
        - Input tensors are automatically converted to PIL Images
        - Binary masks are expected to have values of 0 and 255 (or 0 and 1 for tensors)
        - The function preserves the original mask when using ``"binary"``, ``"L"`` or
          ``"1"`` modes
        - ``"contour"`` mode uses edge detection to find mask boundaries
        - ``"fill"`` mode creates a semi-transparent overlay using the specified color
    
src/anomalib/visualization/image/functional.py