2023-01-14 21:19:41 +00:00
|
|
|
from numpy import random
|
2023-01-15 15:54:55 +00:00
|
|
|
from PIL import Image, ImageChops, ImageFilter
|
2023-01-14 21:19:41 +00:00
|
|
|
from typing import Tuple
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
2023-01-15 03:46:14 +00:00
|
|
|
|
2023-01-14 22:18:37 +00:00
|
|
|
def blend_mult(a):
|
|
|
|
return float(a) / 256
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-01-14 22:18:37 +00:00
|
|
|
|
|
|
|
def blend_imult(a):
|
|
|
|
return 1.0 - blend_mult(a)
|
|
|
|
|
|
|
|
|
2023-01-15 15:21:09 +00:00
|
|
|
def blend_mask_source(source: Tuple[int, int, int], mask: Tuple[int, int, int], noise: Tuple[int, int, int]) -> Tuple[int, int, int]:
|
|
|
|
'''
|
|
|
|
Blend operation, linear interpolation from noise to source based on mask: `(s * (1 - m)) + (n * m)`
|
|
|
|
Black = noise
|
|
|
|
White = source
|
|
|
|
'''
|
|
|
|
return (
|
|
|
|
int((source[0] * blend_mult(mask[0])) +
|
|
|
|
(noise[0] * blend_imult(mask[0]))),
|
|
|
|
int((source[1] * blend_mult(mask[1])) +
|
|
|
|
(noise[1] * blend_imult(mask[1]))),
|
|
|
|
int((source[2] * blend_mult(mask[2])) +
|
|
|
|
(noise[2] * blend_imult(mask[2]))),
|
|
|
|
)
|
|
|
|
|
|
|
|
def blend_source_mask(source: Tuple[int, int, int], mask: Tuple[int, int, int], noise: Tuple[int, int, int]) -> Tuple[int, int, int]:
|
2023-01-15 03:46:14 +00:00
|
|
|
'''
|
|
|
|
Blend operation, linear interpolation from source to noise based on mask: `(s * (1 - m)) + (n * m)`
|
2023-01-15 15:21:09 +00:00
|
|
|
Black = source
|
|
|
|
White = noise
|
2023-01-15 03:46:14 +00:00
|
|
|
'''
|
2023-01-14 21:19:41 +00:00
|
|
|
return (
|
2023-01-15 03:46:14 +00:00
|
|
|
int((source[0] * blend_imult(mask[0])) +
|
|
|
|
(noise[0] * blend_mult(mask[0]))),
|
|
|
|
int((source[1] * blend_imult(mask[1])) +
|
|
|
|
(noise[1] * blend_mult(mask[1]))),
|
|
|
|
int((source[2] * blend_imult(mask[2])) +
|
|
|
|
(noise[2] * blend_mult(mask[2]))),
|
2023-01-14 21:19:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-01-15 15:54:55 +00:00
|
|
|
def mask_filter_gaussian(mask_image: Image, dims: Tuple[int, int], origin: Tuple[int, int], rounds=3) -> Tuple[float, float, float]:
|
|
|
|
'''
|
|
|
|
Gaussian blur, source image centered on white canvas.
|
|
|
|
'''
|
|
|
|
width, height = dims
|
|
|
|
|
|
|
|
noise = Image.new('RGB', (width, height), 'white')
|
|
|
|
noise.paste(mask_image, origin)
|
|
|
|
|
|
|
|
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-01-15 15:21:09 +00:00
|
|
|
def noise_source_fill(source_image: Image, dims: Tuple[int, int], origin: Tuple[int, int], fill='white') -> Tuple[float, float, float]:
|
|
|
|
'''
|
|
|
|
Identity transform, source image centered on white canvas.
|
|
|
|
'''
|
2023-01-15 06:14:05 +00:00
|
|
|
width, height = dims
|
|
|
|
|
2023-01-15 15:21:09 +00:00
|
|
|
noise = Image.new('RGB', (width, height), fill)
|
2023-01-15 06:14:05 +00:00
|
|
|
noise.paste(source_image, origin)
|
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-01-15 15:21:09 +00:00
|
|
|
def noise_source_gaussian(source_image: Image, dims: Tuple[int, int], origin: Tuple[int, int], rounds=3) -> Tuple[float, float, float]:
|
|
|
|
'''
|
|
|
|
Gaussian blur, source image centered on white canvas.
|
|
|
|
'''
|
2023-01-15 16:49:09 +00:00
|
|
|
noise = noise_source_uniform(source_image, dims, origin)
|
2023-01-15 06:14:05 +00:00
|
|
|
noise.paste(source_image, origin)
|
2023-01-15 15:21:09 +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-01-15 15:40:08 +00:00
|
|
|
def noise_source_uniform(source_image: Image, dims: Tuple[int, int], origin: Tuple[int, int]) -> Tuple[float, float, float]:
|
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)
|
|
|
|
|
|
|
|
noise = Image.new('RGB', (width, height))
|
|
|
|
|
|
|
|
for x in range(width):
|
|
|
|
for y in range(height):
|
|
|
|
i = x * y
|
|
|
|
noise.putpixel((x, y), (
|
|
|
|
int(noise_r[i]),
|
|
|
|
int(noise_g[i]),
|
|
|
|
int(noise_b[i])
|
|
|
|
))
|
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-01-15 15:40:08 +00:00
|
|
|
def noise_source_normal(source_image: Image, dims: Tuple[int, int], origin: Tuple[int, int]) -> Tuple[float, float, float]:
|
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)
|
|
|
|
|
|
|
|
noise = Image.new('RGB', (width, height))
|
|
|
|
|
|
|
|
for x in range(width):
|
|
|
|
for y in range(height):
|
|
|
|
i = x * y
|
|
|
|
noise.putpixel((x, y), (
|
|
|
|
int(noise_r[i]),
|
|
|
|
int(noise_g[i]),
|
|
|
|
int(noise_b[i])
|
|
|
|
))
|
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
2023-01-15 15:40:08 +00:00
|
|
|
def noise_source_histogram(source_image: Image, dims: Tuple[int, int], origin: Tuple[int, int]) -> Tuple[float, float, float]:
|
2023-01-14 21:19:41 +00:00
|
|
|
r, g, b = source_image.split()
|
|
|
|
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-01-15 03:46:14 +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-01-14 22:14:37 +00:00
|
|
|
noise = Image.new('RGB', (width, height))
|
|
|
|
|
|
|
|
for x in range(width):
|
|
|
|
for y in range(height):
|
|
|
|
i = x * y
|
|
|
|
noise.putpixel((x, y), (
|
|
|
|
noise_r[i],
|
|
|
|
noise_g[i],
|
|
|
|
noise_b[i]
|
|
|
|
))
|
2023-01-14 21:19:41 +00:00
|
|
|
|
|
|
|
return noise
|
|
|
|
|
|
|
|
|
|
|
|
# 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(
|
|
|
|
source_image: Image,
|
|
|
|
mask_image: Image,
|
|
|
|
expand_by: Tuple[int, int, int, int],
|
|
|
|
fill='white',
|
|
|
|
noise_source=noise_source_histogram,
|
2023-01-15 15:54:55 +00:00
|
|
|
mask_filter=mask_filter_gaussian,
|
2023-01-15 15:21:09 +00:00
|
|
|
blend_op=blend_source_mask
|
2023-01-15 03:46:14 +00:00
|
|
|
):
|
|
|
|
left, right, top, bottom = expand_by
|
2023-01-14 21:19:41 +00:00
|
|
|
|
|
|
|
full_width = left + source_image.width + right
|
|
|
|
full_height = top + source_image.height + bottom
|
|
|
|
|
2023-01-15 15:54:55 +00:00
|
|
|
dims = (full_width, full_height)
|
|
|
|
origin = (top, left)
|
|
|
|
|
|
|
|
full_source = Image.new('RGB', dims, fill)
|
|
|
|
full_source.paste(source_image, origin)
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-01-15 15:54:55 +00:00
|
|
|
full_mask = Image.new('RGB', dims, fill)
|
|
|
|
full_mask = mask_filter_gaussian(full_mask, dims, origin)
|
|
|
|
full_mask.paste(mask_image, origin)
|
2023-01-14 21:19:41 +00:00
|
|
|
|
2023-01-15 15:54:55 +00:00
|
|
|
full_noise = noise_source(source_image, dims, origin)
|
2023-01-14 21:19:41 +00:00
|
|
|
|
|
|
|
for x in range(full_source.width):
|
|
|
|
for y in range(full_source.height):
|
|
|
|
mask_color = full_mask.getpixel((x, y))
|
|
|
|
noise_color = full_noise.getpixel((x, y))
|
|
|
|
source_color = full_source.getpixel((x, y))
|
|
|
|
|
|
|
|
if mask_color[0] > 0:
|
2023-01-15 03:46:14 +00:00
|
|
|
full_source.putpixel((x, y), blend_op(
|
|
|
|
source_color, mask_color, noise_color))
|
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))
|