2023-02-18 15:25:01 +00:00
|
|
|
from typing import Tuple, Union
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
import numpy as np
|
2023-01-14 21:19:41 +00:00
|
|
|
from numpy import random
|
2023-02-18 11:35:53 +00:00
|
|
|
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-02-18 11:47:34 +00:00
|
|
|
from .params import Border, Point, Size
|
2023-01-15 03:46:14 +00:00
|
|
|
|
2023-01-16 01:14:58 +00:00
|
|
|
|
2023-01-16 13:49:25 +00:00
|
|
|
def get_pixel_index(x: int, y: int, width: int) -> int:
|
|
|
|
return (y * width) + x
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def mask_filter_none(
|
2023-02-18 22:35:57 +00:00
|
|
|
mask: Image.Image, dims: Point, origin: Point, fill="white", **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
2023-01-15 15:54:55 +00:00
|
|
|
width, height = dims
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise = Image.new("RGB", (width, height), fill)
|
2023-02-18 22:35:57 +00:00
|
|
|
noise.paste(mask, origin)
|
2023-01-15 15:54:55 +00:00
|
|
|
|
2023-01-15 17:09:47 +00:00
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def mask_filter_gaussian_multiply(
|
2023-02-18 22:35:57 +00:00
|
|
|
mask: Image.Image, dims: Point, origin: Point, rounds=3, **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
|
|
|
"""
|
2023-01-15 20:04:54 +00:00
|
|
|
Gaussian blur with multiply, source image centered on white canvas.
|
2023-02-05 13:53:26 +00:00
|
|
|
"""
|
2023-02-18 22:35:57 +00:00
|
|
|
noise = mask_filter_none(mask, dims, origin)
|
2023-01-15 20:04:54 +00:00
|
|
|
|
2023-02-20 04:10:35 +00:00
|
|
|
for _i in range(rounds):
|
2023-01-15 20:04:54 +00:00
|
|
|
blur = noise.filter(ImageFilter.GaussianBlur(5))
|
|
|
|
noise = ImageChops.multiply(noise, blur)
|
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def mask_filter_gaussian_screen(
|
2023-02-18 22:35:57 +00:00
|
|
|
mask: Image.Image, dims: Point, origin: Point, rounds=3, **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
|
|
|
"""
|
2023-01-15 17:09:47 +00:00
|
|
|
Gaussian blur, source image centered on white canvas.
|
2023-02-05 13:53:26 +00:00
|
|
|
"""
|
2023-02-18 22:35:57 +00:00
|
|
|
noise = mask_filter_none(mask, dims, origin)
|
2023-01-15 17:09:47 +00:00
|
|
|
|
2023-02-20 04:10:35 +00:00
|
|
|
for _i in range(rounds):
|
2023-01-15 16:54:17 +00:00
|
|
|
blur = noise.filter(ImageFilter.GaussianBlur(5))
|
2023-01-15 15:54:55 +00:00
|
|
|
noise = ImageChops.screen(noise, blur)
|
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def noise_source_fill_edge(
|
2023-02-18 22:35:57 +00:00
|
|
|
source: Image.Image, dims: Point, origin: Point, fill="white", **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
|
|
|
"""
|
2023-01-15 15:21:09 +00:00
|
|
|
Identity transform, source image centered on white canvas.
|
2023-02-05 13:53:26 +00:00
|
|
|
"""
|
2023-01-15 06:14:05 +00:00
|
|
|
width, height = dims
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise = Image.new("RGB", (width, height), fill)
|
2023-02-18 22:35:57 +00:00
|
|
|
noise.paste(source, origin)
|
2023-01-15 06:14:05 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def noise_source_fill_mask(
|
2023-02-19 13:54:27 +00:00
|
|
|
_source: Image.Image, dims: Point, _origin: Point, fill="white", **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
|
|
|
"""
|
2023-01-15 20:26:04 +00:00
|
|
|
Fill the whole canvas, no source or noise.
|
2023-02-05 13:53:26 +00:00
|
|
|
"""
|
2023-01-15 20:26:04 +00:00
|
|
|
width, height = dims
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise = Image.new("RGB", (width, height), fill)
|
2023-01-15 20:26:04 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def noise_source_gaussian(
|
2023-02-18 22:35:57 +00:00
|
|
|
source: Image.Image, dims: Point, origin: Point, rounds=3, **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
|
|
|
"""
|
2023-01-15 15:21:09 +00:00
|
|
|
Gaussian blur, source image centered on white canvas.
|
2023-02-05 13:53:26 +00:00
|
|
|
"""
|
2023-02-18 22:35:57 +00:00
|
|
|
noise = noise_source_uniform(source, dims, origin)
|
|
|
|
noise.paste(source, origin)
|
2023-01-15 15:21:09 +00:00
|
|
|
|
2023-02-20 04:10:35 +00:00
|
|
|
for _i in range(rounds):
|
2023-01-15 16:54:17 +00:00
|
|
|
noise = noise.filter(ImageFilter.GaussianBlur(5))
|
2023-01-15 06:14:05 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def noise_source_uniform(
|
2023-02-19 13:54:27 +00:00
|
|
|
_source: Image.Image, dims: Point, _origin: Point, **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
2023-01-15 03:46:14 +00:00
|
|
|
width, height = dims
|
|
|
|
size = width * height
|
|
|
|
|
|
|
|
noise_r = random.uniform(0, 256, size=size)
|
|
|
|
noise_g = random.uniform(0, 256, size=size)
|
|
|
|
noise_b = random.uniform(0, 256, size=size)
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise = Image.new("RGB", (width, height))
|
2023-01-15 03:46:14 +00:00
|
|
|
|
|
|
|
for x in range(width):
|
|
|
|
for y in range(height):
|
2023-01-16 13:49:25 +00:00
|
|
|
i = get_pixel_index(x, y, width)
|
2023-02-05 13:53:26 +00:00
|
|
|
noise.putpixel((x, y), (int(noise_r[i]), int(noise_g[i]), int(noise_b[i])))
|
2023-01-15 03:46:14 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def noise_source_normal(
|
2023-02-19 13:54:27 +00:00
|
|
|
_source: Image.Image, dims: Point, _origin: Point, **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
2023-01-15 03:46:14 +00:00
|
|
|
width, height = dims
|
|
|
|
size = width * height
|
|
|
|
|
|
|
|
noise_r = random.normal(128, 32, size=size)
|
|
|
|
noise_g = random.normal(128, 32, size=size)
|
|
|
|
noise_b = random.normal(128, 32, size=size)
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise = Image.new("RGB", (width, height))
|
2023-01-15 03:46:14 +00:00
|
|
|
|
|
|
|
for x in range(width):
|
|
|
|
for y in range(height):
|
2023-01-16 13:49:25 +00:00
|
|
|
i = get_pixel_index(x, y, width)
|
2023-02-05 13:53:26 +00:00
|
|
|
noise.putpixel((x, y), (int(noise_r[i]), int(noise_g[i]), int(noise_b[i])))
|
2023-01-15 03:46:14 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
def noise_source_histogram(
|
2023-02-19 13:54:27 +00:00
|
|
|
source: Image.Image, dims: Point, _origin: Point, **kw
|
2023-02-05 13:53:26 +00:00
|
|
|
) -> Image.Image:
|
2023-02-18 22:35:57 +00:00
|
|
|
r, g, b = source.split()
|
2023-01-14 21:19:41 +00:00
|
|
|
width, height = dims
|
2023-01-14 21:44:19 +00:00
|
|
|
size = width * height
|
2023-01-14 21:19:41 +00:00
|
|
|
|
|
|
|
hist_r = r.histogram()
|
|
|
|
hist_g = g.histogram()
|
|
|
|
hist_b = b.histogram()
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise_r = random.choice(
|
|
|
|
256, p=np.divide(np.copy(hist_r), np.sum(hist_r)), size=size
|
|
|
|
)
|
|
|
|
noise_g = random.choice(
|
|
|
|
256, p=np.divide(np.copy(hist_g), np.sum(hist_g)), size=size
|
|
|
|
)
|
|
|
|
noise_b = random.choice(
|
|
|
|
256, p=np.divide(np.copy(hist_b), np.sum(hist_b)), size=size
|
|
|
|
)
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
noise = Image.new("RGB", (width, height))
|
2023-01-14 22:14:37 +00:00
|
|
|
|
|
|
|
for x in range(width):
|
|
|
|
for y in range(height):
|
2023-01-16 13:49:25 +00:00
|
|
|
i = get_pixel_index(x, y, width)
|
2023-02-05 13:53:26 +00:00
|
|
|
noise.putpixel((x, y), (noise_r[i], noise_g[i], noise_b[i]))
|
2023-01-14 21:19:41 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-01-16 15:57:59 +00:00
|
|
|
# very loosely based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/scripts/outpainting_mk_2.py#L175-L232
|
2023-01-15 03:46:14 +00:00
|
|
|
def expand_image(
|
2023-02-18 22:35:57 +00:00
|
|
|
source: Image.Image,
|
|
|
|
mask: Image.Image,
|
2023-02-05 13:53:26 +00:00
|
|
|
expand: Border,
|
|
|
|
fill="white",
|
|
|
|
noise_source=noise_source_histogram,
|
|
|
|
mask_filter=mask_filter_none,
|
2023-01-15 03:46:14 +00:00
|
|
|
):
|
2023-02-18 22:35:57 +00:00
|
|
|
full_width = expand.left + source.width + expand.right
|
|
|
|
full_height = expand.top + source.height + expand.bottom
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-01-15 15:54:55 +00:00
|
|
|
dims = (full_width, full_height)
|
2023-01-23 04:09:39 +00:00
|
|
|
origin = (expand.left, expand.top)
|
2023-01-15 15:54:55 +00:00
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
full_source = Image.new("RGB", dims, fill)
|
2023-02-18 22:35:57 +00:00
|
|
|
full_source.paste(source, origin)
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-02-12 00:55:22 +00:00
|
|
|
# new mask pixels need to be filled with white so they will be replaced
|
2023-02-18 22:35:57 +00:00
|
|
|
full_mask = mask_filter(mask, dims, origin, fill="white")
|
|
|
|
full_noise = noise_source(source, dims, origin, fill=fill)
|
2023-01-17 23:49:46 +00:00
|
|
|
full_noise = ImageChops.multiply(full_noise, full_mask)
|
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
full_source = Image.composite(full_noise, full_source, full_mask.convert("L"))
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-01-14 22:03:56 +00:00
|
|
|
return (full_source, full_mask, full_noise, (full_width, full_height))
|
2023-02-18 11:35:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
def valid_image(
|
|
|
|
image: Image.Image,
|
2023-02-18 11:47:34 +00:00
|
|
|
min_dims: Union[Size, Tuple[int, int]] = [512, 512],
|
|
|
|
max_dims: Union[Size, Tuple[int, int]] = [512, 512],
|
2023-02-18 11:35:53 +00:00
|
|
|
) -> Image.Image:
|
|
|
|
min_x, min_y = min_dims
|
|
|
|
max_x, max_y = max_dims
|
|
|
|
|
|
|
|
if image.width > max_x or image.height > max_y:
|
2023-02-18 11:47:34 +00:00
|
|
|
image = ImageOps.contain(image, (max_x, max_y))
|
2023-02-18 11:35:53 +00:00
|
|
|
|
|
|
|
if image.width < min_x or image.height < min_y:
|
2023-02-18 11:47:34 +00:00
|
|
|
blank = Image.new(image.mode, (min_x, min_y), "black")
|
2023-02-18 11:35:53 +00:00
|
|
|
blank.paste(image)
|
|
|
|
image = blank
|
|
|
|
|
|
|
|
# check for square
|
|
|
|
|
2023-02-18 15:25:01 +00:00
|
|
|
return image
|