convert image to weezer collage
| from argparse import ArgumentParser | |
| from pathlib import Path | |
| import cv2 | |
| import numpy as np | |
| import numpy.typing as npt | |
| def generate_collage( | |
| image: npt.NDArray, | |
| red: npt.NDArray, | |
| green: npt.NDArray, | |
| blue: npt.NDArray, | |
| tile_size: int, | |
| upscale: int, | |
| ) -> npt.NDArray: | |
| scale = (tile_size, tile_size) | |
| image = cv2.resize( | |
| image, | |
| (np.array(image.shape[:2][::-1]) * upscale // tile_size).tolist(), | |
| interpolation=cv2.INTER_AREA, | |
| ) | |
| red_small = cv2.resize(red, scale) | |
| green_small = cv2.resize(green, scale) | |
| blue_small = cv2.resize(blue, scale) | |
| red_average = np.mean(red, axis=(0, 1)) | |
| green_average = np.mean(green, axis=(0, 1)) | |
| blue_average = np.mean(blue, axis=(0, 1)) | |
| mat = np.column_stack([red_average, green_average, blue_average]) | |
| inv = np.linalg.inv(mat) | |
| weights = np.clip(np.einsum("ij,klj->kli", inv, image), 0, 1) | |
| tiles = np.einsum( | |
| "ijk,lmnk->iljmn", | |
| weights, | |
| np.stack([red_small, green_small, blue_small], axis=-1), | |
| ) | |
| return ( | |
| tiles.reshape((*np.multiply(image.shape[:2], scale), 3)) | |
| .clip(0, 255) | |
| .astype(np.uint8) | |
| ) | |
| def validate_input_image(filename: str, name: str) -> npt.NDArray: | |
| path = Path(filename) | |
| if not path.exists(): | |
| raise FileNotFoundError(f"File for {name} does not exist") | |
| elif not path.is_file(): | |
| raise OSError(f"Path for {name} is not a file") | |
| ret = cv2.imread(path, cv2.IMREAD_COLOR_RGB) | |
| if ret is None: | |
| raise OSError(f"Could not read image file for {name}") | |
| return ret | |
| if __name__ == "__main__": | |
| parser = ArgumentParser("Generates Weezer collage from input image") | |
| # you'll have to download the album cover images yourself first | |
| parser.add_argument("--red", default="red.png", help="Red Album cover image path") | |
| parser.add_argument( | |
| "--green", default="green.png", help="Green Album cover image path" | |
| ) | |
| parser.add_argument( | |
| "--blue", default="blue.png", help="Blue Album cover image path" | |
| ) | |
| parser.add_argument("-i", "--input", help="input image path") | |
| parser.add_argument("-o", "--output", help="output image path") | |
| parser.add_argument( | |
| "--tile-size", default=100, type=int, help="size of individual tiles" | |
| ) | |
| parser.add_argument("--upscale", default=4, type=int, help="upscale factor") | |
| args = parser.parse_args() | |
| output_path = Path(args.output) | |
| if output_path.exists() and not output_path.is_file(): | |
| raise OSError("Output path is not a file") | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| red = validate_input_image(args.red, "Red Album cover") | |
| green = validate_input_image(args.green, "Green Album cover") | |
| blue = validate_input_image(args.blue, "Blue Album cover") | |
| image = validate_input_image(args.input, "input") | |
| collage = generate_collage(image, red, green, blue, args.tile_size, args.upscale) | |
| cv2.imwrite(output_path, collage[..., ::-1]) |
参与讨论