Teoman21's picture
-done mosaic generator
4376584
from __future__ import annotations
import numpy as np
from PIL import Image
def pil_to_np(img: Image.Image) -> np.ndarray:
if img.mode not in ("RGB", "RGBA", "L"):
img = img.convert("RGB")
if img.mode == "L":
img = img.convert("RGB")
arr = np.asarray(img).astype(np.float32)
if arr.ndim == 2:
arr = np.repeat(arr[..., None], 3, axis=2)
if arr.shape[2] == 4:
arr = arr[..., :3]
return arr / 255.0
def np_to_pil(arr: np.ndarray) -> Image.Image:
return Image.fromarray(np.clip(arr * 255.0, 0, 255).astype(np.uint8))
def resize_and_crop_to_grid(img: Image.Image, width: int, height: int, grid: int) -> Image.Image:
img = img.convert("RGB").resize((width, height), Image.LANCZOS)
H, W = img.height, img.width
H2, W2 = (H // grid) * grid, (W // grid) * grid
if H2 != H or W2 != W:
left = (W - W2) // 2
top = (H - H2) // 2
img = img.crop((left, top, left + W2, top + H2))
return img
def block_view(arr: np.ndarray, bh: int, bw: int) -> np.ndarray:
H, W, C = arr.shape
assert H % bh == 0 and W % bw == 0, "Dims must be divisible by block."
shape = (H//bh, W//bw, bh, bw, C)
strides = (arr.strides[0]*bh, arr.strides[1]*bw, arr.strides[0], arr.strides[1], arr.strides[2])
return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)
def cell_means(arr: np.ndarray, grid: int) -> np.ndarray:
H, W, _ = arr.shape
bh, bw = H//grid, W//grid
blocks = block_view(arr, bh, bw)
# Use weighted mean with center bias for better detail preservation
# Create a weight matrix that emphasizes the center of each block
center_h, center_w = bh // 2, bw // 2
weights = np.zeros((bh, bw))
for i in range(bh):
for j in range(bw):
# Distance from center (normalized)
dist_from_center = np.sqrt((i - center_h)**2 + (j - center_w)**2)
max_dist = np.sqrt(center_h**2 + center_w**2)
# Higher weight for pixels closer to center
weights[i, j] = 1.0 - (dist_from_center / max_dist) * 0.5
# Normalize weights
weights = weights / np.sum(weights)
# Apply weighted mean
weighted_means = np.zeros((grid, grid, 3))
for i in range(grid):
for j in range(grid):
block = blocks[i, j]
for c in range(3):
weighted_means[i, j, c] = np.sum(block[:, :, c] * weights)
return weighted_means