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])
参与讨论