Loading docs/source/api_doc/data/index.rst +1 −0 Original line number Diff line number Diff line Loading @@ -15,4 +15,5 @@ imgutils.data decode image layer pad url docs/source/api_doc/data/pad.rst 0 → 100644 +15 −0 Original line number Diff line number Diff line imgutils.data.pad ========================== .. currentmodule:: imgutils.data.pad .. automodule:: imgutils.data.pad pad_image_to_size --------------------------- .. autofunction:: pad_image_to_size imgutils/data/__init__.py +2 −1 Original line number Diff line number Diff line Loading @@ -8,4 +8,5 @@ from .decode import * from .encode import * from .image import * from .layer import * from .pad import * from .url import * imgutils/data/pad.py 0 → 100644 +134 −0 Original line number Diff line number Diff line """ Image padding and resizing utilities. This module provides functions for padding and resizing images to specified dimensions while maintaining aspect ratio. It includes utilities for parsing size specifications, color values, and handling different image modes. """ from typing import Union, Tuple, Literal from PIL import ImageColor, Image from .image import ImageTyping, load_image __all__ = [ 'pad_image_to_size', ] def _parse_size(size: Union[Tuple[int, int], int]): """ Parse size parameter into a tuple of width and height. :param size: Size specification as an integer or tuple of two integers :type size: Union[Tuple[int, int], int] :return: Tuple containing width and height :rtype: Tuple[int, int] :raises TypeError: If size is not an int or tuple/list of two ints """ if isinstance(size, int): return size, size elif isinstance(size, (list, tuple)) and len(size) == 2: return int(size[0]), int(size[1]) else: raise TypeError("Size must be int or tuple of two ints") def _parse_color_to_rgba(color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]]): """ Convert various color formats to RGBA tuple. :param color: Color specification (string, integer, or tuple/list) :type color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] :return: RGBA color tuple :rtype: Tuple[int, int, int, int] :raises TypeError: If color format is not supported """ if isinstance(color, str): rgba = ImageColor.getrgb(color) + (255,) rgba = tuple([*rgba, *((255,) * (4 - len(rgba)))]) elif isinstance(color, int): rgba = (color, color, color, 255) elif isinstance(color, (list, tuple)): rgba = tuple([*color, *((255,) * (4 - len(color)))]) else: raise TypeError(f"Invalid color type: {type(color)}") return rgba def _parse_color_to_mode(color, mode: Literal['RGB', 'RGBA', 'L', 'LA']): """ Convert color to the specified image mode format. :param color: Color specification (string, integer, or tuple/list) :type color: Union[str, int, Tuple, list] :param mode: Target image mode ('RGB', 'RGBA', 'L', or 'LA') :type mode: Literal['RGB', 'RGBA', 'L', 'LA'] :return: Color value in the specified mode format :rtype: Union[int, Tuple[int, int], Tuple[int, int, int], Tuple[int, int, int, int]] :raises ValueError: If the specified image mode is not supported """ rgba = _parse_color_to_rgba(color) if mode == 'L': return int(0.299 * rgba[0] + 0.587 * rgba[1] + 0.114 * rgba[2]) elif mode == "LA": gray = int(0.299 * rgba[0] + 0.587 * rgba[1] + 0.114 * rgba[2]) return gray, rgba[3] elif mode == "RGB": return rgba[:3] elif mode == "RGBA": return rgba else: raise ValueError(f"Unsupported image mode: {mode!r}") def pad_image_to_size(pic: ImageTyping, size: Union[int, Tuple[int, int]], background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] = 'white', interpolation: int = Image.BILINEAR): """ Resize and pad an image to the specified size while maintaining aspect ratio. The function first resizes the image to fit within the target dimensions while preserving the aspect ratio, then pads the result with the specified background color to reach the exact target size. :param pic: Input image (PIL Image, file path, or other supported format) :type pic: ImageTyping :param size: Target size as an integer or tuple of (width, height) :type size: Union[int, Tuple[int, int]] :param background_color: Color to use for padding (name, RGB tuple, etc.) :type background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] :param interpolation: PIL interpolation method for resizing :type interpolation: int :return: Resized and padded image :rtype: PIL.Image.Image :example: >>> from PIL import Image >>> img = Image.new('RGB', (100, 50)) >>> padded = pad_image_to_size(img, (200, 200), background_color='blue') >>> padded.size (200, 200) """ pic = load_image(pic, force_background=None, mode=None) target_w, target_h = _parse_size(size) original_w, original_h = pic.size ratio = min(target_w / original_w, target_h / original_h) new_w, new_h = round(original_w * ratio), round(original_h * ratio) resized = pic.resize((new_w, new_h), interpolation) bg_color = _parse_color_to_mode(background_color, pic.mode) canvas = Image.new(pic.mode, (target_w, target_h), bg_color) canvas.paste(resized, ((target_w - new_w) // 2, (target_h - new_h) // 2)) return canvas imgutils/preprocess/pillow.py +105 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ import numpy as np from PIL import Image from .base import NotParseTarget from ..data import load_image from ..data import load_image, pad_image_to_size # noinspection PyUnresolvedReferences _INT_TO_PILLOW = { Loading Loading @@ -800,6 +800,110 @@ def _parse_rescale(obj): } class PillowPadToSize: """ A class for padding images to a specified size. This class provides functionality to pad images to a target size while maintaining the original image content. It supports various padding colors and interpolation methods. :param size: Target size as (width, height) tuple or single integer for square :param background_color: Color for padding area (RGB, RGBA, string color name, or integer) :param interpolation: PIL interpolation method for resizing :type interpolation: int """ def __init__(self, size: Union[Tuple[int, int], int], background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] = 'white', interpolation: int = Image.BILINEAR): from ..data.pad import _parse_size, _parse_color_to_rgba self.size = _parse_size(size) self.background_color = (tuple(background_color) if isinstance(background_color, (list, tuple)) else background_color) self.interpolation = interpolation _parse_color_to_rgba(self.background_color) def __call__(self, pic): """ Apply padding transformation to the input image. :param pic: Input PIL Image :type pic: PIL.Image.Image :return: Padded image :rtype: PIL.Image.Image :raises TypeError: If input is not a PIL Image """ if not isinstance(pic, Image.Image): raise TypeError('pic should be PIL Image. Got {}'.format(type(pic))) return pad_image_to_size( pic=pic, size=self.size, background_color=self.background_color, interpolation=self.interpolation, ) def __repr__(self) -> str: """ Return string representation of the class. :return: String representation :rtype: str """ interpolate_str = _PILLOW_TO_STR[self.interpolation] detail = f"(size={self.size}, interpolation={interpolate_str}, background_color={self.background_color})" return f"{self.__class__.__name__}{detail}" @register_pillow_transform('pad_to_size') def _create_pad_to_size(size: Union[Tuple[int, int], int], background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] = 'white', interpolation: str = 'bilinear'): """ Factory function to create PillowPadToSize instance. :param size: Target size for padding :type size: Union[Tuple[int, int], int] :param background_color: Color for padding area :type background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] :param interpolation: Interpolation method name :type interpolation: str :return: PillowPadToSize instance :rtype: PillowPadToSize """ return PillowPadToSize( size=size, background_color=background_color, interpolation=_STR_TO_PILLOW[interpolation], ) @register_pillow_parse('pad_to_size') def _parse_pad_to_size(obj): """ Parse PillowPadToSize object to dictionary configuration. :param obj: Object to parse :type obj: Any :return: Configuration dictionary :rtype: dict :raises NotParseTarget: If object is not PillowPadToSize instance """ if not isinstance(obj, PillowPadToSize): raise NotParseTarget obj: PillowPadToSize return { 'size': list(obj.size), 'background_color': (list(obj.background_color) if isinstance(obj.background_color, (list, tuple)) else obj.background_color), 'interpolation': _PILLOW_TO_STR[obj.interpolation], } class PillowCompose: """ Composes several transforms together into a single transform. Loading Loading
docs/source/api_doc/data/index.rst +1 −0 Original line number Diff line number Diff line Loading @@ -15,4 +15,5 @@ imgutils.data decode image layer pad url
docs/source/api_doc/data/pad.rst 0 → 100644 +15 −0 Original line number Diff line number Diff line imgutils.data.pad ========================== .. currentmodule:: imgutils.data.pad .. automodule:: imgutils.data.pad pad_image_to_size --------------------------- .. autofunction:: pad_image_to_size
imgutils/data/__init__.py +2 −1 Original line number Diff line number Diff line Loading @@ -8,4 +8,5 @@ from .decode import * from .encode import * from .image import * from .layer import * from .pad import * from .url import *
imgutils/data/pad.py 0 → 100644 +134 −0 Original line number Diff line number Diff line """ Image padding and resizing utilities. This module provides functions for padding and resizing images to specified dimensions while maintaining aspect ratio. It includes utilities for parsing size specifications, color values, and handling different image modes. """ from typing import Union, Tuple, Literal from PIL import ImageColor, Image from .image import ImageTyping, load_image __all__ = [ 'pad_image_to_size', ] def _parse_size(size: Union[Tuple[int, int], int]): """ Parse size parameter into a tuple of width and height. :param size: Size specification as an integer or tuple of two integers :type size: Union[Tuple[int, int], int] :return: Tuple containing width and height :rtype: Tuple[int, int] :raises TypeError: If size is not an int or tuple/list of two ints """ if isinstance(size, int): return size, size elif isinstance(size, (list, tuple)) and len(size) == 2: return int(size[0]), int(size[1]) else: raise TypeError("Size must be int or tuple of two ints") def _parse_color_to_rgba(color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]]): """ Convert various color formats to RGBA tuple. :param color: Color specification (string, integer, or tuple/list) :type color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] :return: RGBA color tuple :rtype: Tuple[int, int, int, int] :raises TypeError: If color format is not supported """ if isinstance(color, str): rgba = ImageColor.getrgb(color) + (255,) rgba = tuple([*rgba, *((255,) * (4 - len(rgba)))]) elif isinstance(color, int): rgba = (color, color, color, 255) elif isinstance(color, (list, tuple)): rgba = tuple([*color, *((255,) * (4 - len(color)))]) else: raise TypeError(f"Invalid color type: {type(color)}") return rgba def _parse_color_to_mode(color, mode: Literal['RGB', 'RGBA', 'L', 'LA']): """ Convert color to the specified image mode format. :param color: Color specification (string, integer, or tuple/list) :type color: Union[str, int, Tuple, list] :param mode: Target image mode ('RGB', 'RGBA', 'L', or 'LA') :type mode: Literal['RGB', 'RGBA', 'L', 'LA'] :return: Color value in the specified mode format :rtype: Union[int, Tuple[int, int], Tuple[int, int, int], Tuple[int, int, int, int]] :raises ValueError: If the specified image mode is not supported """ rgba = _parse_color_to_rgba(color) if mode == 'L': return int(0.299 * rgba[0] + 0.587 * rgba[1] + 0.114 * rgba[2]) elif mode == "LA": gray = int(0.299 * rgba[0] + 0.587 * rgba[1] + 0.114 * rgba[2]) return gray, rgba[3] elif mode == "RGB": return rgba[:3] elif mode == "RGBA": return rgba else: raise ValueError(f"Unsupported image mode: {mode!r}") def pad_image_to_size(pic: ImageTyping, size: Union[int, Tuple[int, int]], background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] = 'white', interpolation: int = Image.BILINEAR): """ Resize and pad an image to the specified size while maintaining aspect ratio. The function first resizes the image to fit within the target dimensions while preserving the aspect ratio, then pads the result with the specified background color to reach the exact target size. :param pic: Input image (PIL Image, file path, or other supported format) :type pic: ImageTyping :param size: Target size as an integer or tuple of (width, height) :type size: Union[int, Tuple[int, int]] :param background_color: Color to use for padding (name, RGB tuple, etc.) :type background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] :param interpolation: PIL interpolation method for resizing :type interpolation: int :return: Resized and padded image :rtype: PIL.Image.Image :example: >>> from PIL import Image >>> img = Image.new('RGB', (100, 50)) >>> padded = pad_image_to_size(img, (200, 200), background_color='blue') >>> padded.size (200, 200) """ pic = load_image(pic, force_background=None, mode=None) target_w, target_h = _parse_size(size) original_w, original_h = pic.size ratio = min(target_w / original_w, target_h / original_h) new_w, new_h = round(original_w * ratio), round(original_h * ratio) resized = pic.resize((new_w, new_h), interpolation) bg_color = _parse_color_to_mode(background_color, pic.mode) canvas = Image.new(pic.mode, (target_w, target_h), bg_color) canvas.paste(resized, ((target_w - new_w) // 2, (target_h - new_h) // 2)) return canvas
imgutils/preprocess/pillow.py +105 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ import numpy as np from PIL import Image from .base import NotParseTarget from ..data import load_image from ..data import load_image, pad_image_to_size # noinspection PyUnresolvedReferences _INT_TO_PILLOW = { Loading Loading @@ -800,6 +800,110 @@ def _parse_rescale(obj): } class PillowPadToSize: """ A class for padding images to a specified size. This class provides functionality to pad images to a target size while maintaining the original image content. It supports various padding colors and interpolation methods. :param size: Target size as (width, height) tuple or single integer for square :param background_color: Color for padding area (RGB, RGBA, string color name, or integer) :param interpolation: PIL interpolation method for resizing :type interpolation: int """ def __init__(self, size: Union[Tuple[int, int], int], background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] = 'white', interpolation: int = Image.BILINEAR): from ..data.pad import _parse_size, _parse_color_to_rgba self.size = _parse_size(size) self.background_color = (tuple(background_color) if isinstance(background_color, (list, tuple)) else background_color) self.interpolation = interpolation _parse_color_to_rgba(self.background_color) def __call__(self, pic): """ Apply padding transformation to the input image. :param pic: Input PIL Image :type pic: PIL.Image.Image :return: Padded image :rtype: PIL.Image.Image :raises TypeError: If input is not a PIL Image """ if not isinstance(pic, Image.Image): raise TypeError('pic should be PIL Image. Got {}'.format(type(pic))) return pad_image_to_size( pic=pic, size=self.size, background_color=self.background_color, interpolation=self.interpolation, ) def __repr__(self) -> str: """ Return string representation of the class. :return: String representation :rtype: str """ interpolate_str = _PILLOW_TO_STR[self.interpolation] detail = f"(size={self.size}, interpolation={interpolate_str}, background_color={self.background_color})" return f"{self.__class__.__name__}{detail}" @register_pillow_transform('pad_to_size') def _create_pad_to_size(size: Union[Tuple[int, int], int], background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] = 'white', interpolation: str = 'bilinear'): """ Factory function to create PillowPadToSize instance. :param size: Target size for padding :type size: Union[Tuple[int, int], int] :param background_color: Color for padding area :type background_color: Union[str, int, Tuple[int, int, int], Tuple[int, int, int, int]] :param interpolation: Interpolation method name :type interpolation: str :return: PillowPadToSize instance :rtype: PillowPadToSize """ return PillowPadToSize( size=size, background_color=background_color, interpolation=_STR_TO_PILLOW[interpolation], ) @register_pillow_parse('pad_to_size') def _parse_pad_to_size(obj): """ Parse PillowPadToSize object to dictionary configuration. :param obj: Object to parse :type obj: Any :return: Configuration dictionary :rtype: dict :raises NotParseTarget: If object is not PillowPadToSize instance """ if not isinstance(obj, PillowPadToSize): raise NotParseTarget obj: PillowPadToSize return { 'size': list(obj.size), 'background_color': (list(obj.background_color) if isinstance(obj.background_color, (list, tuple)) else obj.background_color), 'interpolation': _PILLOW_TO_STR[obj.interpolation], } class PillowCompose: """ Composes several transforms together into a single transform. Loading