ImageMosaicGenerator / logic /imgPreprocess.py
meryadri's picture
code improvements
388efcc
import numpy as np
import cv2
from sklearn.cluster import KMeans
from PIL import Image
from logic.validation import ValidationError, ensure_positive_int
IMAGE_SIZE = 400
SAMPLE_SIZE = 50000
def resize_img(image: Image.Image, size: int = IMAGE_SIZE) -> Image.Image:
"""Resize an image to a square of the requested size.
Args:
image (PIL.Image.Image): Image to resize.
size (int, optional): Target side length in pixels. Defaults to ``IMAGE_SIZE``.
Returns:
PIL.Image.Image: Cropped and resized copy in RGB.
Raises:
ValidationError: If ``size`` is not positive.
Example:
>>> resized = resize_img(Image.open('input.png'), 512)
"""
ensure_positive_int(size, "Image size", minimum=32)
width, height = image.size
if width != height:
min_size = min(width, height)
left = (width - min_size) // 2
top = (height - min_size) // 2
image = image.crop((left, top, left + min_size, top + min_size))
resized_img = image.resize((size, size), Image.Resampling.LANCZOS)
return resized_img
def color_quantize(image: Image.Image, n_colors: int = 16):
"""Reduce the number of colors with KMeans clustering.
Args:
image (PIL.Image.Image): Image to quantize.
n_colors (int, optional): Number of clusters. Defaults to 16.
Returns:
tuple[PIL.Image.Image, np.ndarray]: Quantized image and cluster centers in BGR order.
Raises:
ValidationError: If ``n_colors`` is outside ``[2, 64]``.
"""
ensure_positive_int(n_colors, "Color palette size", minimum=2, maximum=64)
img_np = np.array(image)
original_shape = img_np.shape
img_flat = img_np.reshape(-1, 3)
sample_size = min(SAMPLE_SIZE, img_flat.shape[0])
indices = np.random.choice(img_flat.shape[0], sample_size, replace=False)
img_sample = img_flat[indices]
kmeans = KMeans(
n_clusters=n_colors,
random_state=42,
init='k-means++',
n_init=5,
max_iter=100,
tol=1e-3,
algorithm='lloyd'
)
kmeans.fit(img_sample)
labels = kmeans.predict(img_flat)
quantized_flat = kmeans.cluster_centers_[labels].astype(np.uint8)
quantized_img_np = quantized_flat.reshape(original_shape)
quantized_img = Image.fromarray(quantized_img_np)
color_centers = kmeans.cluster_centers_.astype(np.uint8)
color_centers = color_centers[:, [2, 1, 0]] # Swap R and B channels
return quantized_img, color_centers