Source code for oumi.utils.image_utils

# Copyright 2025 - Oumi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import io
from pathlib import Path
from typing import Final, Optional, Union

import PIL.Image
import requests

from oumi.utils.logging import logger

# For details on image modes, see
# https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes
DEFAULT_IMAGE_MODE: Final[str] = "RGB"

_FILE_URL_PREFIX: Final[str] = "file://"


[docs] def create_png_bytes_from_image(pil_image: PIL.Image.Image) -> bytes: """Encodes PIL image into PNG format, and returns PNG image bytes. Args: pil_image: An input image. Returns: bytes: PNG bytes representation of the image. """ try: output = io.BytesIO() pil_image.save(output, format="PNG") return output.getvalue() except Exception: logger.error("Failed to convert an image to PNG bytes.") raise
[docs] def convert_pil_image_mode( image: PIL.Image.Image, *, mode: Optional[str] ) -> PIL.Image.Image: """Converts a PIL image to the requested mode (if it's not in that mode already) . Args: image: An input image. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: An image in the requested mode . If an input image was already in the correct mode then return it for efficiency. Otherwise, a different image object is returned. """ if not mode or image.mode == mode: # Return the original object for better performance. return image old_mode = image.mode try: return image.convert(mode) except Exception as e: raise RuntimeError( f"Failed to convert an image from {old_mode} to {mode} mode!" ) from e
[docs] def load_pil_image_from_path( input_image_filepath: Union[str, Path], mode: str = DEFAULT_IMAGE_MODE ) -> PIL.Image.Image: """Loads an image from a path. Args: input_image_filepath: A file path of an image. The image can be in any format supported by PIL. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: bytes: PNG bytes representation of the image. """ if not input_image_filepath: raise ValueError("Empty image file path.") if isinstance( input_image_filepath, str ) and input_image_filepath.lower().startswith(_FILE_URL_PREFIX): input_image_filepath = input_image_filepath[len(_FILE_URL_PREFIX) :] input_image_filepath = Path(input_image_filepath) if not input_image_filepath.is_file(): raise ValueError( f"Image path is not a file: {input_image_filepath}" if input_image_filepath.exists() else f"Image path doesn't exist: {input_image_filepath}" ) try: pil_image = convert_pil_image_mode( PIL.Image.open(input_image_filepath), mode=mode ) except Exception: logger.error(f"Failed to load an image from path: {input_image_filepath}") raise return pil_image
[docs] def load_pil_image_from_url( input_image_url: str, mode: str = DEFAULT_IMAGE_MODE ) -> PIL.Image.Image: """Loads a PIL image from a URL. Args: input_image_url: An image URL. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: bytes: PNG bytes representation of the image. """ if not input_image_url: raise ValueError("Empty image URL!") try: response = requests.get(input_image_url, stream=True) response.raise_for_status() except requests.exceptions.RequestException: logger.exception(f"Failed to download image: '{input_image_url}'") raise return load_pil_image_from_bytes(response.content, mode=mode)
[docs] def load_pil_image_from_bytes( image_bytes: Optional[bytes], mode: str = DEFAULT_IMAGE_MODE ) -> PIL.Image.Image: """Loads an image from raw image bytes. Args: image_bytes: A input image bytes. Can be in any image format supported by PIL. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: PIL.Image.Image: PIL representation of the image. """ if image_bytes is None or len(image_bytes) == 0: raise ValueError("No image bytes.") try: pil_image = convert_pil_image_mode( PIL.Image.open(io.BytesIO(image_bytes)), mode=mode ) except Exception: logger.error( f"Failed to load an image from raw image bytes ({len(image_bytes)} bytes)." ) raise return pil_image
[docs] def create_png_bytes_from_image_bytes( image_bytes: Optional[bytes], mode: str = DEFAULT_IMAGE_MODE ) -> bytes: """Loads an image from raw image bytes, and converts to PNG image bytes. Args: image_bytes: A input image bytes. Can be in any image format supported by PIL. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: bytes: PNG bytes representation of the image. """ pil_image = load_pil_image_from_bytes(image_bytes, mode=mode) return create_png_bytes_from_image(pil_image)
[docs] def load_image_png_bytes_from_path( input_image_filepath: Union[str, Path], mode: str = DEFAULT_IMAGE_MODE ) -> bytes: """Loads an image from a path, converts it to PNG, and returns image bytes. Args: input_image_filepath: A file path of an image. The image can be in any format supported by PIL. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: bytes: PNG bytes representation of the image. """ pil_image = load_pil_image_from_path(input_image_filepath, mode=mode) return create_png_bytes_from_image(pil_image)
[docs] def load_image_png_bytes_from_url( input_image_url: str, mode: str = DEFAULT_IMAGE_MODE ) -> bytes: """Loads an image from a URL, converts it to PNG, and returns image bytes. Args: input_image_url: An image URL. mode: The requested image mode e.g., "RGB", "HSV", "RGBA", "P" (8-bit pixels, using a color palette). For details, see https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes Returns: bytes: PNG bytes representation of the image. """ pil_image = load_pil_image_from_url(input_image_url, mode=mode) return create_png_bytes_from_image(pil_image)