1
0
Fork 0
onnx-web/api/onnx_web/image.py

215 lines
5.7 KiB
Python

from typing import Tuple, Union
import numpy as np
from numpy import random
from PIL import Image, ImageChops, ImageFilter, ImageOps
from .params import Border, Point, Size
def get_pixel_index(x: int, y: int, width: int) -> int:
return (y * width) + x
def mask_filter_none(
mask: Image.Image, dims: Point, origin: Point, fill="white", **kw
) -> Image.Image:
width, height = dims
noise = Image.new("RGB", (width, height), fill)
noise.paste(mask, origin)
return noise
def mask_filter_gaussian_multiply(
mask: Image.Image, dims: Point, origin: Point, rounds=3, **kw
) -> Image.Image:
"""
Gaussian blur with multiply, source image centered on white canvas.
"""
noise = mask_filter_none(mask, dims, origin)
for _i in range(rounds):
blur = noise.filter(ImageFilter.GaussianBlur(5))
noise = ImageChops.multiply(noise, blur)
return noise
def mask_filter_gaussian_screen(
mask: Image.Image, dims: Point, origin: Point, rounds=3, **kw
) -> Image.Image:
"""
Gaussian blur, source image centered on white canvas.
"""
noise = mask_filter_none(mask, dims, origin)
for _i in range(rounds):
blur = noise.filter(ImageFilter.GaussianBlur(5))
noise = ImageChops.screen(noise, blur)
return noise
def noise_source_fill_edge(
source: Image.Image, dims: Point, origin: Point, fill="white", **kw
) -> Image.Image:
"""
Identity transform, source image centered on white canvas.
"""
width, height = dims
noise = Image.new("RGB", (width, height), fill)
noise.paste(source, origin)
return noise
def noise_source_fill_mask(
_source: Image.Image, dims: Point, _origin: Point, fill="white", **kw
) -> Image.Image:
"""
Fill the whole canvas, no source or noise.
"""
width, height = dims
noise = Image.new("RGB", (width, height), fill)
return noise
def noise_source_gaussian(
source: Image.Image, dims: Point, origin: Point, rounds=3, **kw
) -> Image.Image:
"""
Gaussian blur, source image centered on white canvas.
"""
noise = noise_source_uniform(source, dims, origin)
noise.paste(source, origin)
for _i in range(rounds):
noise = noise.filter(ImageFilter.GaussianBlur(5))
return noise
def noise_source_uniform(
_source: Image.Image, dims: Point, _origin: Point, **kw
) -> Image.Image:
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 = get_pixel_index(x, y, width)
noise.putpixel((x, y), (int(noise_r[i]), int(noise_g[i]), int(noise_b[i])))
return noise
def noise_source_normal(
_source: Image.Image, dims: Point, _origin: Point, **kw
) -> Image.Image:
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 = get_pixel_index(x, y, width)
noise.putpixel((x, y), (int(noise_r[i]), int(noise_g[i]), int(noise_b[i])))
return noise
def noise_source_histogram(
source: Image.Image, dims: Point, _origin: Point, **kw
) -> Image.Image:
r, g, b = source.split()
width, height = dims
size = width * height
hist_r = r.histogram()
hist_g = g.histogram()
hist_b = b.histogram()
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
)
noise = Image.new("RGB", (width, height))
for x in range(width):
for y in range(height):
i = get_pixel_index(x, y, width)
noise.putpixel((x, y), (noise_r[i], noise_g[i], noise_b[i]))
return noise
# very loosely based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/scripts/outpainting_mk_2.py#L175-L232
def expand_image(
source: Image.Image,
mask: Image.Image,
expand: Border,
fill="white",
noise_source=noise_source_histogram,
mask_filter=mask_filter_none,
):
full_width = expand.left + source.width + expand.right
full_height = expand.top + source.height + expand.bottom
dims = (full_width, full_height)
origin = (expand.left, expand.top)
full_source = Image.new("RGB", dims, fill)
full_source.paste(source, origin)
# new mask pixels need to be filled with white so they will be replaced
full_mask = mask_filter(mask, dims, origin, fill="white")
full_noise = noise_source(source, dims, origin, fill=fill)
full_noise = ImageChops.multiply(full_noise, full_mask)
full_source = Image.composite(full_noise, full_source, full_mask.convert("L"))
return (full_source, full_mask, full_noise, (full_width, full_height))
def valid_image(
image: Image.Image,
min_dims: Union[Size, Tuple[int, int]] = [512, 512],
max_dims: Union[Size, Tuple[int, int]] = [512, 512],
) -> Image.Image:
min_x, min_y = min_dims
max_x, max_y = max_dims
if image.width > max_x or image.height > max_y:
image = ImageOps.contain(image, (max_x, max_y))
if image.width < min_x or image.height < min_y:
blank = Image.new(image.mode, (min_x, min_y), "black")
blank.paste(image)
image = blank
# check for square
return image