Repository: Project-MONAI/MONAI
File: monai/data/image_writer.py (Python)

Code before:
    Resolves to a tuple of available ``ImageWriter`` in ``SUPPORTED_WRITERS``
    according to the filename extension key ``ext_name``.

    Args:
        ext_name: the filename extension of the image.
            As an indexing key it will be converted to a lower case string.
        error_if_not_found: whether to raise an error if no suitable image writer is found.
            if True , raise an ``OptionalImportError``, otherwise return an empty tuple. Default is ``True``.
    """
    if not SUPPORTED_WRITERS:
        init()
    fmt = f"{ext_name}".lower()
    if fmt.startswith("."):
        fmt = fmt[1:]
    avail_writers = []
    default_writers = SUPPORTED_WRITERS.get(EXT_WILDCARD, ())
    for _writer in look_up_option(fmt, SUPPORTED_WRITERS, default=default_writers):
        try:
            _writer()  # this triggers `monai.utils.module.require_pkg` to check the system availability
            avail_writers.append(_writer)
        except OptionalImportError:
            continue
        except Exception:  # other writer init errors indicating it exists
            avail_writers.append(_writer)
    if not avail_writers and error_if_not_found:
        recommended_packages = {
            "png": "Pillow",
            "jpg": "Pillow",
            "jpeg": "Pillow",
            "nii": "nibabel or SimpleITK",
            "nii.gz": "nibabel or SimpleITK",
            "nrrd": "pynrrd",
            "tif": "Pillow or tifffile",
            "tiff": "Pillow or tifffile",
        }

        fmt_clean = fmt.lower()
        package_hint = recommended_packages.get(fmt_clean, "")

        msg = f"No ImageWriter backend found for {fmt}."
        if package_hint:
            msg += f" Please install '{package_hint}' (e.g., pip install {package_hint})."

        raise OptionalImportError(msg)

    writer_tuple = ensure_tuple(avail_writers)
    SUPPORTED_WRITERS[fmt] = writer_tuple
    return writer_tuple


class ImageWriter:

Review comment (suggestion):
I would suggest making this a global variable at the top of the file as `RECOMMENDED_PACKAGES`, this would allow future code to possibly add items here.

Code after:
    Resolves to a tuple of available ``ImageWriter`` in ``SUPPORTED_WRITERS``
    according to the filename extension key ``ext_name``.

    Args:
        ext_name: the filename extension of the image.
            As an indexing key it will be converted to a lower case string.
        error_if_not_found: whether to raise an error if no suitable image writer is found.
            if True , raise an ``OptionalImportError``, otherwise return an empty tuple. Default is ``True``.
    """
    if not SUPPORTED_WRITERS:
        init()
    fmt = f"{ext_name}".lower()
    if fmt.startswith("."):
        fmt = fmt[1:]
    avail_writers = []
    default_writers = SUPPORTED_WRITERS.get(EXT_WILDCARD, ())
    for _writer in look_up_option(fmt, SUPPORTED_WRITERS, default=default_writers):
        try:
            _writer()  # this triggers `monai.utils.module.require_pkg` to check the system availability
            avail_writers.append(_writer)
        except OptionalImportError:
            continue
        except Exception:  # other writer init errors indicating it exists
            avail_writers.append(_writer)
    if not avail_writers and error_if_not_found:
        raise OptionalImportError(f"No ImageWriter backend found for {fmt}.")
    writer_tuple = ensure_tuple(avail_writers)
    SUPPORTED_WRITERS[fmt] = writer_tuple
    return writer_tuple


class ImageWriter:
    """
    The class is a collection of utilities to write images to disk.

    Main aspects to be considered are:

        - dimensionality of the data array, arrangements of spatial dimensions and channel/time dimensions
            - ``convert_to_channel_last()``
        - metadata of the current affine and output affine, the data array should be converted accordingly
            - ``get_meta_info()``
            - ``resample_if_needed()``
        - data type handling of the output image (as part of ``resample_if_needed()``)

    Subclasses of this class should implement the backend-specific functions:

        - ``set_data_array()`` to set the data array (input must be numpy array or torch tensor)
            - this method sets the backend object's data part
        - ``set_metadata()`` to set the metadata and output affine
            - this method sets the metadata including affine handling and image resampling
        - backend-specific data object ``create_backend_obj()``