stouputils.image module#
This module provides little utilities for image processing.
image_resize: Resize an image while preserving its aspect ratio by default.
auto_crop: Automatically crop an image to remove zero/uniform regions.
See stouputils.data_science.data_processing for lots more image processing utilities.
- image_resize(image: Image.Image | NDArray[np.number], max_result_size: int, resampling: Image.Resampling | None = None, min_or_max: ~collections.abc.Callable[[int, int], int] = <built-in function max>, return_type: type[Image.Image | NDArray[np.number]] | str = 'same', keep_aspect_ratio: bool = True) Any[source]#
Resize an image while preserving its aspect ratio by default. Scales the image so that its largest dimension equals max_result_size.
- Parameters:
image (Image.Image | np.ndarray) – The image to resize.
max_result_size (int) – Maximum size for the largest dimension.
resampling (Image.Resampling | None) – PIL resampling filter to use (default: Image.Resampling.LANCZOS).
min_or_max (Callable) – Function to use to get the minimum or maximum of the two ratios.
return_type (type | str) – Type of the return value (Image.Image, np.ndarray, or “same” to match input type).
keep_aspect_ratio (bool) – Whether to keep the aspect ratio.
- Returns:
The resized image with preserved aspect ratio.
- Return type:
Image.Image | NDArray[np.number]
Examples
>>> # Test with (height x width x channels) numpy array >>> import numpy as np >>> array = np.random.randint(0, 255, (100, 50, 3), dtype=np.uint8) >>> image_resize(array, 100).shape (100, 50, 3) >>> image_resize(array, 100, min_or_max=max).shape (100, 50, 3) >>> image_resize(array, 100, min_or_max=min).shape (200, 100, 3)
>>> # Test with PIL Image >>> from PIL import Image >>> pil_image: Image.Image = Image.new('RGB', (200, 100)) >>> image_resize(pil_image, 50).size (50, 25) >>> # Test with different return types >>> resized_array = image_resize(array, 50, return_type=np.ndarray) >>> isinstance(resized_array, np.ndarray) True >>> resized_array.shape (50, 25, 3) >>> # Test with different resampling methods >>> image_resize(pil_image, 50, resampling=Image.Resampling.NEAREST).size (50, 25)
- auto_crop(
- image: Image.Image | NDArray[np.number],
- mask: NDArray[np.bool_] | None = None,
- threshold: int | float | Callable[[NDArray[np.number]], int | float] | None = None,
- return_type: type[Image.Image | NDArray[np.number]] | str = 'same',
- contiguous: bool = True,
Automatically crop an image to remove zero or uniform regions.
This function crops the image to keep only the region where pixels are non-zero (or above a threshold). It can work with a mask or directly analyze the image.
- Parameters:
image (Image.Image | NDArray) – The image to crop.
mask (NDArray[np.bool_] | None) – Optional binary mask indicating regions to keep.
threshold (int | float | Callable) – Threshold value or function (default: np.min).
return_type (type | str) – Type of the return value (Image.Image, NDArray[np.number], or “same” to match input type).
contiguous (bool) – If True (default), crop to bounding box. If False, remove entire rows/columns with no content.
- Returns:
The cropped image.
- Return type:
Image.Image | NDArray[np.number]
Examples
>>> # Test with numpy array with zeros on edges >>> import numpy as np >>> array = np.zeros((100, 100, 3), dtype=np.uint8) >>> array[20:80, 30:70] = 255 # White rectangle in center >>> cropped = auto_crop(array, return_type=np.ndarray) >>> cropped.shape (60, 40, 3)
>>> # Test with custom mask >>> mask = np.zeros((100, 100), dtype=bool) >>> mask[10:90, 10:90] = True >>> cropped_with_mask = auto_crop(array, mask=mask, return_type=np.ndarray) >>> cropped_with_mask.shape (80, 80, 3)
>>> # Test with PIL Image >>> from PIL import Image >>> pil_image = Image.new('RGB', (100, 100), (0, 0, 0)) >>> from PIL import ImageDraw >>> draw = ImageDraw.Draw(pil_image) >>> draw.rectangle([25, 25, 75, 75], fill=(255, 255, 255)) >>> cropped_pil = auto_crop(pil_image) >>> cropped_pil.size (51, 51)
>>> # Test with threshold >>> array_gray = np.ones((100, 100), dtype=np.uint8) * 10 >>> array_gray[20:80, 30:70] = 255 >>> cropped_threshold = auto_crop(array_gray, threshold=50, return_type=np.ndarray) >>> cropped_threshold.shape (60, 40)
>>> # Test with callable threshold (using lambda to avoid min value) >>> array_gray2 = np.ones((100, 100), dtype=np.uint8) * 10 >>> array_gray2[20:80, 30:70] = 255 >>> cropped_max = auto_crop(array_gray2, threshold=lambda x: 50, return_type=np.ndarray) >>> cropped_max.shape (60, 40)
>>> # Test with non-contiguous crop >>> array_sparse = np.zeros((100, 100, 3), dtype=np.uint8) >>> array_sparse[10, 10] = 255 >>> array_sparse[50, 50] = 255 >>> array_sparse[90, 90] = 255 >>> cropped_contiguous = auto_crop(array_sparse, contiguous=True, return_type=np.ndarray) >>> cropped_contiguous.shape # Bounding box from (10,10) to (90,90) (81, 81, 3) >>> cropped_non_contiguous = auto_crop(array_sparse, contiguous=False, return_type=np.ndarray) >>> cropped_non_contiguous.shape # Only rows/cols 10, 50, 90 (3, 3, 3)