from typing import Tuple import numpy as np from PIL import Image def infill_mosaic( image: Image.Image, tile_shape: Tuple[int, int] = (64, 64), min_color: Tuple[int, int, int, int] = (0, 0, 0, 0), max_color: Tuple[int, int, int, int] = (255, 255, 255, 0), ) -> Image.Image: """ image:PIL - A PIL Image tile_shape: Tuple[int,int] - Tile width & Tile Height min_color: Tuple[int,int,int] - RGB values for the lowest color to clip to (0-255) max_color: Tuple[int,int,int] - RGB values for the highest color to clip to (0-255) """ np_image = np.array(image) # Convert image to np array alpha = np_image[:, :, 3] # Get the mask from the alpha channel of the image non_transparent_pixels = np_image[alpha != 0, :3] # List of non-transparent pixels # Create color tiles to paste in the empty areas of the image tile_width, tile_height = tile_shape # Clip the range of colors in the image to a particular spectrum only r_min, g_min, b_min, _ = min_color r_max, g_max, b_max, _ = max_color non_transparent_pixels[:, 0] = np.clip(non_transparent_pixels[:, 0], r_min, r_max) non_transparent_pixels[:, 1] = np.clip(non_transparent_pixels[:, 1], g_min, g_max) non_transparent_pixels[:, 2] = np.clip(non_transparent_pixels[:, 2], b_min, b_max) tiles = [] for _ in range(256): color = non_transparent_pixels[np.random.randint(len(non_transparent_pixels))] tile = np.zeros((tile_height, tile_width, 3), dtype=np.uint8) tile[:, :] = color tiles.append(tile) # Fill the transparent area with tiles filled_image = np.zeros((image.height, image.width, 3), dtype=np.uint8) for x in range(image.width): for y in range(image.height): tile = tiles[np.random.randint(len(tiles))] try: filled_image[ y - (y % tile_height) : y - (y % tile_height) + tile_height, x - (x % tile_width) : x - (x % tile_width) + tile_width, ] = tile except ValueError: # Need to handle edge cases - literally pass filled_image = Image.fromarray(filled_image) # Convert the filled tiles image to PIL image = Image.composite( image, filled_image, image.split()[-1] ) # Composite the original image on top of the filled tiles return image