|
from __future__ import annotations |
|
|
|
from dataclasses import dataclass |
|
|
|
import cv2 |
|
import numpy as np |
|
from diffusers.utils import BaseOutput |
|
from PIL import Image, ImageFilter, ImageOps |
|
|
|
|
|
@dataclass |
|
class ADOutput(BaseOutput): |
|
images: list[Image.Image] |
|
init_images: list[Image.Image] |
|
|
|
|
|
def mask_dilate(image: Image.Image, value: int = 4) -> Image.Image: |
|
if value <= 0: |
|
return image |
|
|
|
arr = np.array(image) |
|
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (value, value)) |
|
dilated = cv2.dilate(arr, kernel, iterations=1) |
|
return Image.fromarray(dilated) |
|
|
|
|
|
def mask_gaussian_blur(image: Image.Image, value: int = 4) -> Image.Image: |
|
if value <= 0: |
|
return image |
|
|
|
blur = ImageFilter.GaussianBlur(value) |
|
return image.filter(blur) |
|
|
|
|
|
def bbox_padding( |
|
bbox: tuple[int, int, int, int], image_size: tuple[int, int], value: int = 32 |
|
) -> tuple[int, int, int, int]: |
|
if value <= 0: |
|
return bbox |
|
|
|
arr = np.array(bbox).reshape(2, 2) |
|
arr[0] -= value |
|
arr[1] += value |
|
arr = np.clip(arr, (0, 0), image_size) |
|
return tuple(arr.flatten()) |
|
|
|
|
|
def composite( |
|
init: Image.Image, |
|
mask: Image.Image, |
|
gen: Image.Image, |
|
bbox_padded: tuple[int, int, int, int], |
|
) -> Image.Image: |
|
img_masked = Image.new("RGBa", init.size) |
|
img_masked.paste( |
|
init.convert("RGBA").convert("RGBa"), |
|
mask=ImageOps.invert(mask), |
|
) |
|
img_masked = img_masked.convert("RGBA") |
|
|
|
size = ( |
|
bbox_padded[2] - bbox_padded[0], |
|
bbox_padded[3] - bbox_padded[1], |
|
) |
|
resized = gen.resize(size) |
|
|
|
output = Image.new("RGBA", init.size) |
|
output.paste(resized, bbox_padded) |
|
output.alpha_composite(img_masked) |
|
return output.convert("RGB") |
|
|