import numpy as np
import warnings
from sharpedge._utils.utility import Utility
[docs]
def _image_resize(img, h_border, w_border, inside):
"""
Private function to be invoked by frame_image().
Resize the image by adding borders either inside or outside.
This function handles resizing the image, ensuring that if borders are added inside,
the image size is reduced, and if borders are added outside, the image size increases.
Parameters
----------
img : ndarray
The input image array (either 2D or 3D).
h_border : int
The height of the border in pixels.
w_border : int
The width of the border in pixels.
inside : bool
If True, the border is added **inside** the image (maintaining the image size).
If False, the border is added **outside** the image (increasing the image size).
Returns
-------
img : ndarray
The resized image, with borders applied.
Raises
------
ValueError
If the inside border is too large for the image dimensions.
If the `h_border` or `w_border` is negative or invalid.
TypeError
If the `h_border` or `w_border` is not an integer.
"""
# Check the *_border inputs are correct: integers and non-negative
if not isinstance(h_border, int) or not isinstance(w_border, int):
raise TypeError("Both h_border and w_border must be integers.")
if h_border < 0 or w_border < 0:
raise ValueError("Both h_border and w_border must be non-negative integers.")
# Warning: when any border size is 0
if h_border == 0 or w_border == 0:
warnings.warn("Border size of 0 doesn't add any visual effect to the image.", UserWarning)
# Handle image resizing (inside or outside border)
if inside:
# Error: the image is too small to fit the inside border
if img.shape[0] <= 2 * h_border or img.shape[1] <= 2 * w_border:
raise ValueError("The inside border is too large for this small image. The image cannot be processed.")
# Warning: when the inside border is greater than 50% of image size
elif (2 * h_border > 0.5 * img.shape[0]) or (2 * w_border > 0.5 * img.shape[1]):
warnings.warn("The inside border exceeds 50% image size and may shrink the image significantly.", UserWarning)
# Handle slicing for inside padding (keeping size constant)
img = img[h_border:-h_border or None, w_border:-w_border or None, :]
# Warning: when single side outside border is larger than the image dimensions
elif not inside and (h_border >= img.shape[0] or w_border >= img.shape[1]):
warnings.warn("Single side border size exceeds image size.", UserWarning)
return img
[docs]
def _color_padding(img, h_border, w_border, color):
"""
Private function to be invoked by frame_image().
Apply color padding by adding a border around the image.
This function adds a border of the specified color around the image, either inside
or outside the image.
Parameters
----------
img : ndarray
The input image array (3D array with 3 color channels).
h_border : int
The height of the border in pixels.
w_border : int
The width of the border in pixels.
color : tuple of int
The RGB color for the border.
Returns
-------
frame : ndarray
The image with the color border applied.
"""
# Calculate the new shape for the image with the border
new_height = img.shape[0] + 2 * h_border
new_width = img.shape[1] + 2 * w_border
# Create the border: a full array of the border color
frame = np.full((new_height, new_width, 3), color, dtype=np.uint8)
# Insert the image in the center of the border
frame[h_border:h_border + img.shape[0], w_border:w_border + img.shape[1]] = img
return frame
[docs]
def frame_image(img, h_border=20, w_border=20, inside=False, color=0):
"""
Add a decorative frame around the image with a customizable color.
This function adds a border around the input image, either inside the image
(preserving its original size) or outside (increasing its size). The border
color can be specified for both grayscale and RGB images.
Parameters
----------
img : ndarray
The input image as a 2D numpy array (grayscale) or 3D numpy array (RGB).
h_border : int, optional
The height of the border in pixels. Default is 20.
w_border : int, optional
The width of the border in pixels. Default is 20.
inside : bool, optional
If True, the border is added **inside** the image (maintaining the image size).
If False, the border is added **outside** the image (increasing the image size).
Default is False.
color : int or tuple of int, optional
The color of the border. Can be:
- A single value for grayscale frames (e.g., 0 for black, 255 for white).
- A tuple of 3 values for RGB frames (e.g., (0, 0, 0) for black).
Default is 0 (black) for grayscale frames.
Returns
-------
ndarray
The framed image with the applied border.
Examples
--------
>>> img = np.random.rand(100, 100)
>>> framed_img = frame_image(img, h_border=30, w_border=30, inside=True, color=255)
>>> framed_img_rgb = frame_image(img_rgb, h_border=20, w_border=20, inside=False, color=(255, 0, 0))
"""
# Input validation
Utility._input_checker(img)
# Warning: when image size is below 3 x 3
if img.shape[0] < 3 or img.shape[1] < 3:
warnings.warn("The image is too small for meaningful visual information. Proceeding may not yield interpretable results.", UserWarning)
# Handle format conversion (grayscale to RGB) and color padding validation
img, color = _format_conversion(img, color)
# Handle image resizing (inside or outside border)
img = _image_resize(img, h_border, w_border, inside)
# Apply color padding (create the framed image with border)
framed_img = _color_padding(img, h_border, w_border, color)
return framed_img