Spaces:
Runtime error
Runtime error
# Prediction interface for Cog ⚙️ | |
# https://github.com/replicate/cog/blob/main/docs/python.md | |
import os | |
import sys | |
sys.path.append(os.path.join(os.path.dirname(__file__), "..")) | |
sys.path.append(os.path.join(os.path.dirname(__file__), "../gradio_demo")) | |
import cv2 | |
import time | |
import torch | |
import mimetypes | |
import subprocess | |
import numpy as np | |
from typing import List | |
from cog import BasePredictor, Input, Path | |
import PIL | |
from PIL import Image | |
import diffusers | |
from diffusers import LCMScheduler | |
from diffusers.utils import load_image | |
from diffusers.models import ControlNetModel | |
from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel | |
from model_util import get_torch_device | |
from insightface.app import FaceAnalysis | |
from transformers import CLIPImageProcessor | |
from controlnet_util import openpose, get_depth_map, get_canny_image | |
from diffusers.pipelines.stable_diffusion.safety_checker import ( | |
StableDiffusionSafetyChecker, | |
) | |
from pipeline_stable_diffusion_xl_instantid_full import ( | |
StableDiffusionXLInstantIDPipeline, | |
draw_kps, | |
) | |
mimetypes.add_type("image/webp", ".webp") | |
# GPU global variables | |
DEVICE = get_torch_device() | |
DTYPE = torch.float16 if str(DEVICE).__contains__("cuda") else torch.float32 | |
# for `ip-adapter`, `ControlNetModel`, and `stable-diffusion-xl-base-1.0` | |
CHECKPOINTS_CACHE = "./checkpoints" | |
CHECKPOINTS_URL = "https://weights.replicate.delivery/default/InstantID/checkpoints.tar" | |
# for `models/antelopev2` | |
MODELS_CACHE = "./models" | |
MODELS_URL = "https://weights.replicate.delivery/default/InstantID/models.tar" | |
# for the safety checker | |
SAFETY_CACHE = "./safety-cache" | |
FEATURE_EXTRACTOR = "./feature-extractor" | |
SAFETY_URL = "https://weights.replicate.delivery/default/playgroundai/safety-cache.tar" | |
SDXL_NAME_TO_PATHLIKE = { | |
# These are all huggingface models that we host via gcp + pget | |
"stable-diffusion-xl-base-1.0": { | |
"slug": "stabilityai/stable-diffusion-xl-base-1.0", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stabilityai--stable-diffusion-xl-base-1.0.tar", | |
"path": "checkpoints/models--stabilityai--stable-diffusion-xl-base-1.0", | |
}, | |
"afrodite-xl-v2": { | |
"slug": "stablediffusionapi/afrodite-xl-v2", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--afrodite-xl-v2.tar", | |
"path": "checkpoints/models--stablediffusionapi--afrodite-xl-v2", | |
}, | |
"albedobase-xl-20": { | |
"slug": "stablediffusionapi/albedobase-xl-20", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--albedobase-xl-20.tar", | |
"path": "checkpoints/models--stablediffusionapi--albedobase-xl-20", | |
}, | |
"albedobase-xl-v13": { | |
"slug": "stablediffusionapi/albedobase-xl-v13", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--albedobase-xl-v13.tar", | |
"path": "checkpoints/models--stablediffusionapi--albedobase-xl-v13", | |
}, | |
"animagine-xl-30": { | |
"slug": "stablediffusionapi/animagine-xl-30", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--animagine-xl-30.tar", | |
"path": "checkpoints/models--stablediffusionapi--animagine-xl-30", | |
}, | |
"anime-art-diffusion-xl": { | |
"slug": "stablediffusionapi/anime-art-diffusion-xl", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--anime-art-diffusion-xl.tar", | |
"path": "checkpoints/models--stablediffusionapi--anime-art-diffusion-xl", | |
}, | |
"anime-illust-diffusion-xl": { | |
"slug": "stablediffusionapi/anime-illust-diffusion-xl", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--anime-illust-diffusion-xl.tar", | |
"path": "checkpoints/models--stablediffusionapi--anime-illust-diffusion-xl", | |
}, | |
"dreamshaper-xl": { | |
"slug": "stablediffusionapi/dreamshaper-xl", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--dreamshaper-xl.tar", | |
"path": "checkpoints/models--stablediffusionapi--dreamshaper-xl", | |
}, | |
"dynavision-xl-v0610": { | |
"slug": "stablediffusionapi/dynavision-xl-v0610", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--dynavision-xl-v0610.tar", | |
"path": "checkpoints/models--stablediffusionapi--dynavision-xl-v0610", | |
}, | |
"guofeng4-xl": { | |
"slug": "stablediffusionapi/guofeng4-xl", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--guofeng4-xl.tar", | |
"path": "checkpoints/models--stablediffusionapi--guofeng4-xl", | |
}, | |
"juggernaut-xl-v8": { | |
"slug": "stablediffusionapi/juggernaut-xl-v8", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--juggernaut-xl-v8.tar", | |
"path": "checkpoints/models--stablediffusionapi--juggernaut-xl-v8", | |
}, | |
"nightvision-xl-0791": { | |
"slug": "stablediffusionapi/nightvision-xl-0791", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--nightvision-xl-0791.tar", | |
"path": "checkpoints/models--stablediffusionapi--nightvision-xl-0791", | |
}, | |
"omnigen-xl": { | |
"slug": "stablediffusionapi/omnigen-xl", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--omnigen-xl.tar", | |
"path": "checkpoints/models--stablediffusionapi--omnigen-xl", | |
}, | |
"pony-diffusion-v6-xl": { | |
"slug": "stablediffusionapi/pony-diffusion-v6-xl", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--pony-diffusion-v6-xl.tar", | |
"path": "checkpoints/models--stablediffusionapi--pony-diffusion-v6-xl", | |
}, | |
"protovision-xl-high-fidel": { | |
"slug": "stablediffusionapi/protovision-xl-high-fidel", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--stablediffusionapi--protovision-xl-high-fidel.tar", | |
"path": "checkpoints/models--stablediffusionapi--protovision-xl-high-fidel", | |
}, | |
"RealVisXL_V3.0_Turbo": { | |
"slug": "SG161222/RealVisXL_V3.0_Turbo", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--SG161222--RealVisXL_V3.0_Turbo.tar", | |
"path": "checkpoints/models--SG161222--RealVisXL_V3.0_Turbo", | |
}, | |
"RealVisXL_V4.0_Lightning": { | |
"slug": "SG161222/RealVisXL_V4.0_Lightning", | |
"url": "https://weights.replicate.delivery/default/InstantID/models--SG161222--RealVisXL_V4.0_Lightning.tar", | |
"path": "checkpoints/models--SG161222--RealVisXL_V4.0_Lightning", | |
}, | |
} | |
def convert_from_cv2_to_image(img: np.ndarray) -> Image: | |
return Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) | |
def convert_from_image_to_cv2(img: Image) -> np.ndarray: | |
return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) | |
def resize_img( | |
input_image, | |
max_side=1280, | |
min_side=1024, | |
size=None, | |
pad_to_max_side=False, | |
mode=PIL.Image.BILINEAR, | |
base_pixel_number=64, | |
): | |
w, h = input_image.size | |
if size is not None: | |
w_resize_new, h_resize_new = size | |
else: | |
ratio = min_side / min(h, w) | |
w, h = round(ratio * w), round(ratio * h) | |
ratio = max_side / max(h, w) | |
input_image = input_image.resize([round(ratio * w), round(ratio * h)], mode) | |
w_resize_new = (round(ratio * w) // base_pixel_number) * base_pixel_number | |
h_resize_new = (round(ratio * h) // base_pixel_number) * base_pixel_number | |
input_image = input_image.resize([w_resize_new, h_resize_new], mode) | |
if pad_to_max_side: | |
res = np.ones([max_side, max_side, 3], dtype=np.uint8) * 255 | |
offset_x = (max_side - w_resize_new) // 2 | |
offset_y = (max_side - h_resize_new) // 2 | |
res[offset_y : offset_y + h_resize_new, offset_x : offset_x + w_resize_new] = ( | |
np.array(input_image) | |
) | |
input_image = Image.fromarray(res) | |
return input_image | |
def download_weights(url, dest): | |
start = time.time() | |
print("[!] Initiating download from URL: ", url) | |
print("[~] Destination path: ", dest) | |
command = ["pget", "-vf", url, dest] | |
if ".tar" in url: | |
command.append("-x") | |
try: | |
subprocess.check_call(command, close_fds=False) | |
except subprocess.CalledProcessError as e: | |
print( | |
f"[ERROR] Failed to download weights. Command '{' '.join(e.cmd)}' returned non-zero exit status {e.returncode}." | |
) | |
raise | |
print("[+] Download completed in: ", time.time() - start, "seconds") | |
class Predictor(BasePredictor): | |
def setup(self) -> None: | |
"""Load the model into memory to make running multiple predictions efficient""" | |
if not os.path.exists(CHECKPOINTS_CACHE): | |
download_weights(CHECKPOINTS_URL, CHECKPOINTS_CACHE) | |
if not os.path.exists(MODELS_CACHE): | |
download_weights(MODELS_URL, MODELS_CACHE) | |
self.face_detection_input_width, self.face_detection_input_height = 640, 640 | |
self.app = FaceAnalysis( | |
name="antelopev2", | |
root="./", | |
providers=["CUDAExecutionProvider", "CPUExecutionProvider"], | |
) | |
self.app.prepare(ctx_id=0, det_size=(self.face_detection_input_width, self.face_detection_input_height)) | |
# Path to InstantID models | |
self.face_adapter = f"./checkpoints/ip-adapter.bin" | |
controlnet_path = f"./checkpoints/ControlNetModel" | |
# Load pipeline face ControlNetModel | |
self.controlnet_identitynet = ControlNetModel.from_pretrained( | |
controlnet_path, | |
torch_dtype=DTYPE, | |
cache_dir=CHECKPOINTS_CACHE, | |
local_files_only=True, | |
) | |
self.setup_extra_controlnets() | |
self.load_weights("stable-diffusion-xl-base-1.0") | |
self.setup_safety_checker() | |
def setup_safety_checker(self): | |
print(f"[~] Seting up safety checker") | |
if not os.path.exists(SAFETY_CACHE): | |
download_weights(SAFETY_URL, SAFETY_CACHE) | |
self.safety_checker = StableDiffusionSafetyChecker.from_pretrained( | |
SAFETY_CACHE, | |
torch_dtype=DTYPE, | |
local_files_only=True, | |
) | |
self.safety_checker.to(DEVICE) | |
self.feature_extractor = CLIPImageProcessor.from_pretrained(FEATURE_EXTRACTOR) | |
def run_safety_checker(self, image): | |
safety_checker_input = self.feature_extractor(image, return_tensors="pt").to( | |
DEVICE | |
) | |
np_image = np.array(image) | |
image, has_nsfw_concept = self.safety_checker( | |
images=[np_image], | |
clip_input=safety_checker_input.pixel_values.to(DTYPE), | |
) | |
return image, has_nsfw_concept | |
def load_weights(self, sdxl_weights): | |
self.base_weights = sdxl_weights | |
weights_info = SDXL_NAME_TO_PATHLIKE[self.base_weights] | |
download_url = weights_info["url"] | |
path_to_weights_dir = weights_info["path"] | |
if not os.path.exists(path_to_weights_dir): | |
download_weights(download_url, path_to_weights_dir) | |
is_hugging_face_model = "slug" in weights_info.keys() | |
path_to_weights_file = os.path.join( | |
path_to_weights_dir, | |
weights_info.get("file", ""), | |
) | |
print(f"[~] Loading new SDXL weights: {path_to_weights_file}") | |
if is_hugging_face_model: | |
self.pipe = StableDiffusionXLInstantIDPipeline.from_pretrained( | |
weights_info["slug"], | |
controlnet=[self.controlnet_identitynet], | |
torch_dtype=DTYPE, | |
cache_dir=CHECKPOINTS_CACHE, | |
local_files_only=True, | |
safety_checker=None, | |
feature_extractor=None, | |
) | |
self.pipe.scheduler = diffusers.EulerDiscreteScheduler.from_config( | |
self.pipe.scheduler.config | |
) | |
else: # e.g. .safetensors, NOTE: This functionality is not being used right now | |
self.pipe.from_single_file( | |
path_to_weights_file, | |
controlnet=self.controlnet_identitynet, | |
torch_dtype=DTYPE, | |
cache_dir=CHECKPOINTS_CACHE, | |
) | |
self.pipe.load_ip_adapter_instantid(self.face_adapter) | |
self.setup_lcm_lora() | |
self.pipe.cuda() | |
def setup_lcm_lora(self): | |
print(f"[~] Seting up LCM (just in case)") | |
lcm_lora_key = "models--latent-consistency--lcm-lora-sdxl" | |
lcm_lora_path = f"checkpoints/{lcm_lora_key}" | |
if not os.path.exists(lcm_lora_path): | |
download_weights( | |
f"https://weights.replicate.delivery/default/InstantID/{lcm_lora_key}.tar", | |
lcm_lora_path, | |
) | |
self.pipe.load_lora_weights( | |
"latent-consistency/lcm-lora-sdxl", | |
cache_dir=CHECKPOINTS_CACHE, | |
local_files_only=True, | |
weight_name="pytorch_lora_weights.safetensors", | |
) | |
self.pipe.disable_lora() | |
def setup_extra_controlnets(self): | |
print(f"[~] Seting up pose, canny, depth ControlNets") | |
controlnet_pose_model = "thibaud/controlnet-openpose-sdxl-1.0" | |
controlnet_canny_model = "diffusers/controlnet-canny-sdxl-1.0" | |
controlnet_depth_model = "diffusers/controlnet-depth-sdxl-1.0-small" | |
for controlnet_key in [ | |
"models--diffusers--controlnet-canny-sdxl-1.0", | |
"models--diffusers--controlnet-depth-sdxl-1.0-small", | |
"models--thibaud--controlnet-openpose-sdxl-1.0", | |
]: | |
controlnet_path = f"checkpoints/{controlnet_key}" | |
if not os.path.exists(controlnet_path): | |
download_weights( | |
f"https://weights.replicate.delivery/default/InstantID/{controlnet_key}.tar", | |
controlnet_path, | |
) | |
controlnet_pose = ControlNetModel.from_pretrained( | |
controlnet_pose_model, | |
torch_dtype=DTYPE, | |
cache_dir=CHECKPOINTS_CACHE, | |
local_files_only=True, | |
).to(DEVICE) | |
controlnet_canny = ControlNetModel.from_pretrained( | |
controlnet_canny_model, | |
torch_dtype=DTYPE, | |
cache_dir=CHECKPOINTS_CACHE, | |
local_files_only=True, | |
).to(DEVICE) | |
controlnet_depth = ControlNetModel.from_pretrained( | |
controlnet_depth_model, | |
torch_dtype=DTYPE, | |
cache_dir=CHECKPOINTS_CACHE, | |
local_files_only=True, | |
).to(DEVICE) | |
self.controlnet_map = { | |
"pose": controlnet_pose, | |
"canny": controlnet_canny, | |
"depth": controlnet_depth, | |
} | |
self.controlnet_map_fn = { | |
"pose": openpose, | |
"canny": get_canny_image, | |
"depth": get_depth_map, | |
} | |
def generate_image( | |
self, | |
face_image_path, | |
pose_image_path, | |
prompt, | |
negative_prompt, | |
num_steps, | |
identitynet_strength_ratio, | |
adapter_strength_ratio, | |
pose_strength, | |
canny_strength, | |
depth_strength, | |
controlnet_selection, | |
guidance_scale, | |
seed, | |
scheduler, | |
enable_LCM, | |
enhance_face_region, | |
num_images_per_prompt, | |
): | |
if enable_LCM: | |
self.pipe.enable_lora() | |
self.pipe.scheduler = LCMScheduler.from_config(self.pipe.scheduler.config) | |
else: | |
self.pipe.disable_lora() | |
scheduler_class_name = scheduler.split("-")[0] | |
add_kwargs = {} | |
if len(scheduler.split("-")) > 1: | |
add_kwargs["use_karras_sigmas"] = True | |
if len(scheduler.split("-")) > 2: | |
add_kwargs["algorithm_type"] = "sde-dpmsolver++" | |
scheduler = getattr(diffusers, scheduler_class_name) | |
self.pipe.scheduler = scheduler.from_config( | |
self.pipe.scheduler.config, | |
**add_kwargs, | |
) | |
if face_image_path is None: | |
raise Exception( | |
f"Cannot find any input face `image`! Please upload the face `image`" | |
) | |
face_image = load_image(face_image_path) | |
face_image = resize_img(face_image) | |
face_image_cv2 = convert_from_image_to_cv2(face_image) | |
height, width, _ = face_image_cv2.shape | |
# Extract face features | |
face_info = self.app.get(face_image_cv2) | |
if len(face_info) == 0: | |
raise Exception( | |
"Face detector could not find a face in the `image`. Please use a different `image` as input." | |
) | |
face_info = sorted( | |
face_info, | |
key=lambda x: (x["bbox"][2] - x["bbox"][0]) * x["bbox"][3] - x["bbox"][1], | |
)[ | |
-1 | |
] # only use the maximum face | |
face_emb = face_info["embedding"] | |
face_kps = draw_kps(convert_from_cv2_to_image(face_image_cv2), face_info["kps"]) | |
img_controlnet = face_image | |
if pose_image_path is not None: | |
pose_image = load_image(pose_image_path) | |
pose_image = resize_img(pose_image, max_side=1024) | |
img_controlnet = pose_image | |
pose_image_cv2 = convert_from_image_to_cv2(pose_image) | |
face_info = self.app.get(pose_image_cv2) | |
if len(face_info) == 0: | |
raise Exception( | |
"Face detector could not find a face in the `pose_image`. Please use a different `pose_image` as input." | |
) | |
face_info = face_info[-1] | |
face_kps = draw_kps(pose_image, face_info["kps"]) | |
width, height = face_kps.size | |
if enhance_face_region: | |
control_mask = np.zeros([height, width, 3]) | |
x1, y1, x2, y2 = face_info["bbox"] | |
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2) | |
control_mask[y1:y2, x1:x2] = 255 | |
control_mask = Image.fromarray(control_mask.astype(np.uint8)) | |
else: | |
control_mask = None | |
if len(controlnet_selection) > 0: | |
controlnet_scales = { | |
"pose": pose_strength, | |
"canny": canny_strength, | |
"depth": depth_strength, | |
} | |
self.pipe.controlnet = MultiControlNetModel( | |
[self.controlnet_identitynet] | |
+ [self.controlnet_map[s] for s in controlnet_selection] | |
) | |
control_scales = [float(identitynet_strength_ratio)] + [ | |
controlnet_scales[s] for s in controlnet_selection | |
] | |
control_images = [face_kps] + [ | |
self.controlnet_map_fn[s](img_controlnet).resize((width, height)) | |
for s in controlnet_selection | |
] | |
else: | |
self.pipe.controlnet = self.controlnet_identitynet | |
control_scales = float(identitynet_strength_ratio) | |
control_images = face_kps | |
generator = torch.Generator(device=DEVICE).manual_seed(seed) | |
print("Start inference...") | |
print(f"[Debug] Prompt: {prompt}, \n[Debug] Neg Prompt: {negative_prompt}") | |
self.pipe.set_ip_adapter_scale(adapter_strength_ratio) | |
images = self.pipe( | |
prompt=prompt, | |
negative_prompt=negative_prompt, | |
image_embeds=face_emb, | |
image=control_images, | |
control_mask=control_mask, | |
controlnet_conditioning_scale=control_scales, | |
num_inference_steps=num_steps, | |
guidance_scale=guidance_scale, | |
height=height, | |
width=width, | |
generator=generator, | |
num_images_per_prompt=num_images_per_prompt, | |
).images | |
return images | |
def predict( | |
self, | |
image: Path = Input( | |
description="Input face image", | |
), | |
pose_image: Path = Input( | |
description="(Optional) reference pose image", | |
default=None, | |
), | |
prompt: str = Input( | |
description="Input prompt", | |
default="a person", | |
), | |
negative_prompt: str = Input( | |
description="Input Negative Prompt", | |
default="", | |
), | |
sdxl_weights: str = Input( | |
description="Pick which base weights you want to use", | |
default="stable-diffusion-xl-base-1.0", | |
choices=[ | |
"stable-diffusion-xl-base-1.0", | |
"juggernaut-xl-v8", | |
"afrodite-xl-v2", | |
"albedobase-xl-20", | |
"albedobase-xl-v13", | |
"animagine-xl-30", | |
"anime-art-diffusion-xl", | |
"anime-illust-diffusion-xl", | |
"dreamshaper-xl", | |
"dynavision-xl-v0610", | |
"guofeng4-xl", | |
"nightvision-xl-0791", | |
"omnigen-xl", | |
"pony-diffusion-v6-xl", | |
"protovision-xl-high-fidel", | |
"RealVisXL_V3.0_Turbo", | |
"RealVisXL_V4.0_Lightning", | |
], | |
), | |
face_detection_input_width: int = Input( | |
description="Width of the input image for face detection", | |
default=640, | |
ge=640, | |
le=4096, | |
), | |
face_detection_input_height: int = Input( | |
description="Height of the input image for face detection", | |
default=640, | |
ge=640, | |
le=4096, | |
), | |
scheduler: str = Input( | |
description="Scheduler", | |
choices=[ | |
"DEISMultistepScheduler", | |
"HeunDiscreteScheduler", | |
"EulerDiscreteScheduler", | |
"DPMSolverMultistepScheduler", | |
"DPMSolverMultistepScheduler-Karras", | |
"DPMSolverMultistepScheduler-Karras-SDE", | |
], | |
default="EulerDiscreteScheduler", | |
), | |
num_inference_steps: int = Input( | |
description="Number of denoising steps", | |
default=30, | |
ge=1, | |
le=500, | |
), | |
guidance_scale: float = Input( | |
description="Scale for classifier-free guidance", | |
default=7.5, | |
ge=1, | |
le=50, | |
), | |
ip_adapter_scale: float = Input( | |
description="Scale for image adapter strength (for detail)", # adapter_strength_ratio | |
default=0.8, | |
ge=0, | |
le=1.5, | |
), | |
controlnet_conditioning_scale: float = Input( | |
description="Scale for IdentityNet strength (for fidelity)", # identitynet_strength_ratio | |
default=0.8, | |
ge=0, | |
le=1.5, | |
), | |
enable_pose_controlnet: bool = Input( | |
description="Enable Openpose ControlNet, overrides strength if set to false", | |
default=True, | |
), | |
pose_strength: float = Input( | |
description="Openpose ControlNet strength, effective only if `enable_pose_controlnet` is true", | |
default=0.4, | |
ge=0, | |
le=1, | |
), | |
enable_canny_controlnet: bool = Input( | |
description="Enable Canny ControlNet, overrides strength if set to false", | |
default=False, | |
), | |
canny_strength: float = Input( | |
description="Canny ControlNet strength, effective only if `enable_canny_controlnet` is true", | |
default=0.3, | |
ge=0, | |
le=1, | |
), | |
enable_depth_controlnet: bool = Input( | |
description="Enable Depth ControlNet, overrides strength if set to false", | |
default=False, | |
), | |
depth_strength: float = Input( | |
description="Depth ControlNet strength, effective only if `enable_depth_controlnet` is true", | |
default=0.5, | |
ge=0, | |
le=1, | |
), | |
enable_lcm: bool = Input( | |
description="Enable Fast Inference with LCM (Latent Consistency Models) - speeds up inference steps, trade-off is the quality of the generated image. Performs better with close-up portrait face images", | |
default=False, | |
), | |
lcm_num_inference_steps: int = Input( | |
description="Only used when `enable_lcm` is set to True, Number of denoising steps when using LCM", | |
default=5, | |
ge=1, | |
le=10, | |
), | |
lcm_guidance_scale: float = Input( | |
description="Only used when `enable_lcm` is set to True, Scale for classifier-free guidance when using LCM", | |
default=1.5, | |
ge=1, | |
le=20, | |
), | |
enhance_nonface_region: bool = Input( | |
description="Enhance non-face region", default=True | |
), | |
output_format: str = Input( | |
description="Format of the output images", | |
choices=["webp", "jpg", "png"], | |
default="webp", | |
), | |
output_quality: int = Input( | |
description="Quality of the output images, from 0 to 100. 100 is best quality, 0 is lowest quality.", | |
default=80, | |
ge=0, | |
le=100, | |
), | |
seed: int = Input( | |
description="Random seed. Leave blank to randomize the seed", | |
default=None, | |
), | |
num_outputs: int = Input( | |
description="Number of images to output", | |
default=1, | |
ge=1, | |
le=8, | |
), | |
disable_safety_checker: bool = Input( | |
description="Disable safety checker for generated images", | |
default=False, | |
), | |
) -> List[Path]: | |
"""Run a single prediction on the model""" | |
# If no seed is provided, generate a random seed | |
if seed is None: | |
seed = int.from_bytes(os.urandom(2), "big") | |
print(f"Using seed: {seed}") | |
# Load the weights if they are different from the base weights | |
if sdxl_weights != self.base_weights: | |
self.load_weights(sdxl_weights) | |
# Resize the output if the provided dimensions are different from the current ones | |
if self.face_detection_input_width != face_detection_input_width or self.face_detection_input_height != face_detection_input_height: | |
print(f"[!] Resizing output to {face_detection_input_width}x{face_detection_input_height}") | |
self.face_detection_input_width = face_detection_input_width | |
self.face_detection_input_height = face_detection_input_height | |
self.app.prepare(ctx_id=0, det_size=(self.face_detection_input_width, self.face_detection_input_height)) | |
# Set up ControlNet selection and their respective strength values (if any) | |
controlnet_selection = [] | |
if pose_strength > 0 and enable_pose_controlnet: | |
controlnet_selection.append("pose") | |
if canny_strength > 0 and enable_canny_controlnet: | |
controlnet_selection.append("canny") | |
if depth_strength > 0 and enable_depth_controlnet: | |
controlnet_selection.append("depth") | |
# Switch to LCM inference steps and guidance scale if LCM is enabled | |
if enable_lcm: | |
num_inference_steps = lcm_num_inference_steps | |
guidance_scale = lcm_guidance_scale | |
# Generate | |
images = self.generate_image( | |
face_image_path=str(image), | |
pose_image_path=str(pose_image) if pose_image else None, | |
prompt=prompt, | |
negative_prompt=negative_prompt, | |
num_steps=num_inference_steps, | |
identitynet_strength_ratio=controlnet_conditioning_scale, | |
adapter_strength_ratio=ip_adapter_scale, | |
pose_strength=pose_strength, | |
canny_strength=canny_strength, | |
depth_strength=depth_strength, | |
controlnet_selection=controlnet_selection, | |
scheduler=scheduler, | |
guidance_scale=guidance_scale, | |
seed=seed, | |
enable_LCM=enable_lcm, | |
enhance_face_region=enhance_nonface_region, | |
num_images_per_prompt=num_outputs, | |
) | |
# Save the generated images and check for NSFW content | |
output_paths = [] | |
for i, output_image in enumerate(images): | |
if not disable_safety_checker: | |
_, has_nsfw_content_list = self.run_safety_checker(output_image) | |
has_nsfw_content = any(has_nsfw_content_list) | |
print(f"NSFW content detected: {has_nsfw_content}") | |
if has_nsfw_content: | |
raise Exception( | |
"NSFW content detected. Try running it again, or try a different prompt." | |
) | |
extension = output_format.lower() | |
extension = "jpeg" if extension == "jpg" else extension | |
output_path = f"/tmp/out_{i}.{extension}" | |
print(f"[~] Saving to {output_path}...") | |
print(f"[~] Output format: {extension.upper()}") | |
if output_format != "png": | |
print(f"[~] Output quality: {output_quality}") | |
save_params = {"format": extension.upper()} | |
if output_format != "png": | |
save_params["quality"] = output_quality | |
save_params["optimize"] = True | |
output_image.save(output_path, **save_params) | |
output_paths.append(Path(output_path)) | |
return output_paths | |