| from typing import Optional, Tuple |
|
|
| import gradio as gr |
| import numpy as np |
| from PIL import Image, ImageChops, ImageOps |
|
|
| NEGATIVE_DEFAULT = ( |
| "blurry, ugly, bad anatomy, deformed, distorted, extra fingers, " |
| "extra limbs, poorly drawn hands, poorly drawn face, low quality" |
| ) |
|
|
| def round_to_multiple_of_8(x: int) -> int: |
| x = int(x) |
| return max(64, x - (x % 8)) |
|
|
| def resize_keep_aspect(img: Image.Image, max_side: int, resample) -> Image.Image: |
| w, h = img.size |
| longest = max(w, h) |
| if longest <= max_side: |
| return img |
|
|
| scale = max_side / float(longest) |
| new_w = round_to_multiple_of_8(int(w * scale)) |
| new_h = round_to_multiple_of_8(int(h * scale)) |
| return img.resize((new_w, new_h), resample) |
|
|
| def merge_layer_alphas(layers) -> Optional[Image.Image]: |
| if not layers: |
| return None |
|
|
| merged = None |
| for layer in layers: |
| if layer is None: |
| continue |
|
|
| if not isinstance(layer, Image.Image): |
| layer = Image.fromarray(np.array(layer)) |
|
|
| rgba = layer.convert("RGBA") |
| alpha = rgba.getchannel("A") |
|
|
| if merged is None: |
| merged = alpha |
| else: |
| merged = ImageChops.lighter(merged, alpha) |
|
|
| return merged |
|
|
| def threshold_mask(mask_l: Image.Image, threshold: int = 10) -> Image.Image: |
| arr = np.array(mask_l, dtype=np.uint8) |
| arr = np.where(arr > threshold, 255, 0).astype(np.uint8) |
| return Image.fromarray(arr, mode="L") |
|
|
| def extract_image_and_mask(editor_value) -> Tuple[Image.Image, Image.Image]: |
| if editor_value is None: |
| raise gr.Error("Upload an image first.") |
|
|
| bg = editor_value.get("background", None) |
| layers = editor_value.get("layers", None) |
|
|
| if bg is None: |
| raise gr.Error("Upload an image first.") |
|
|
| if not isinstance(bg, Image.Image): |
| bg = Image.fromarray(np.array(bg)) |
|
|
| bg = ImageOps.exif_transpose(bg).convert("RGB") |
|
|
| mask = merge_layer_alphas(layers) |
| if mask is None: |
| raise gr.Error("Paint a mask first.") |
|
|
| if mask.size != bg.size: |
| mask = mask.resize(bg.size, Image.NEAREST) |
|
|
| mask = threshold_mask(mask.convert("L")) |
|
|
| if mask.getbbox() is None: |
| raise gr.Error("Paint a mask first.") |
|
|
| return bg, mask |
|
|
| def prepare_inputs_for_cpu( |
| image: Image.Image, |
| mask: Image.Image, |
| max_side: int, |
| ) -> Tuple[Image.Image, Image.Image]: |
| image = resize_keep_aspect(image, max_side=max_side, resample=Image.LANCZOS) |
| mask = mask.resize(image.size, Image.NEAREST) |
|
|
| w, h = image.size |
| w = round_to_multiple_of_8(w) |
| h = round_to_multiple_of_8(h) |
|
|
| if image.size != (w, h): |
| image = image.resize((w, h), Image.LANCZOS) |
|
|
| if mask.size != (w, h): |
| mask = mask.resize((w, h), Image.NEAREST) |
|
|
| return image, mask |