init commit
upload
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
|
||||
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
@@ -0,0 +1,555 @@
|
||||
import os
|
||||
import sys
|
||||
from .streamdiffusion.pipeline import StreamDiffusion
|
||||
from .streamdiffusion.wrapper import StreamDiffusionWrapper
|
||||
from diffusers import AutoencoderTiny, StableDiffusionPipeline
|
||||
from pathlib import Path
|
||||
import traceback
|
||||
from typing import List, Literal, Optional, Union, Dict
|
||||
import torch
|
||||
import gc
|
||||
# Get the absolute path of various directories
|
||||
my_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
custom_nodes_dir = os.path.abspath(os.path.join(my_dir, '..'))
|
||||
comfy_dir = os.path.abspath(os.path.join(my_dir, '..', '..'))
|
||||
|
||||
# Construct the path to the font file
|
||||
font_path = os.path.join(my_dir, 'arial.ttf')
|
||||
|
||||
# Append comfy_dir to sys.path & import files
|
||||
sys.path.append(comfy_dir)
|
||||
import folder_paths
|
||||
|
||||
|
||||
import comfy.sample
|
||||
import comfy.samplers
|
||||
import comfy.sd
|
||||
import comfy.utils
|
||||
import comfy.latent_formats
|
||||
import comfy.model_management
|
||||
|
||||
# Append my_dir to sys.path & import files
|
||||
sys.path.append(my_dir)
|
||||
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
|
||||
MAX_RESOLUTION=8192
|
||||
|
||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
def get_async_loop():
|
||||
loop = None
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
except:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
return loop
|
||||
|
||||
|
||||
class StreamDiffusion_LoRA_Stacker:
|
||||
modes = ["simple", "advanced"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
loras = ["None"] + folder_paths.get_filename_list("loras")
|
||||
inputs = {
|
||||
"required": {
|
||||
"input_mode": (cls.modes,),
|
||||
"lora_count": ("INT", {"default": 3, "min": 0, "max": 50, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
for i in range(1, 50):
|
||||
inputs["required"][f"lora_name_{i}"] = (loras,)
|
||||
inputs["required"][f"lora_wt_{i}"] = ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})
|
||||
inputs["required"][f"model_str_{i}"] = ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})
|
||||
inputs["required"][f"clip_str_{i}"] = ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})
|
||||
|
||||
inputs["optional"] = {
|
||||
"lora_stack": ("LORA_STACK",)
|
||||
}
|
||||
return inputs
|
||||
|
||||
RETURN_TYPES = ("LORA_STACK",)
|
||||
RETURN_NAMES = ("LORA_STACK",)
|
||||
FUNCTION = "lora_stacker"
|
||||
CATEGORY = "StreamDiffusion/Stackers"
|
||||
|
||||
def lora_stacker(self, input_mode, lora_count, lora_stack=None, **kwargs):
|
||||
|
||||
# Extract values from kwargs
|
||||
loras = [kwargs.get(f"lora_name_{i}") for i in range(1, lora_count + 1)]
|
||||
|
||||
# Create a list of tuples using provided parameters, exclude tuples with lora_name as "None"
|
||||
if input_mode == "simple":
|
||||
weights = [kwargs.get(f"lora_wt_{i}") for i in range(1, lora_count + 1)]
|
||||
loras = [(lora_name, lora_weight, lora_weight) for lora_name, lora_weight in zip(loras, weights) if
|
||||
lora_name != "None"]
|
||||
else:
|
||||
model_strs = [kwargs.get(f"model_str_{i}") for i in range(1, lora_count + 1)]
|
||||
clip_strs = [kwargs.get(f"clip_str_{i}") for i in range(1, lora_count + 1)]
|
||||
loras = [(lora_name, model_str, clip_str) for lora_name, model_str, clip_str in
|
||||
zip(loras, model_strs, clip_strs) if lora_name != "None"]
|
||||
|
||||
# If lora_stack is not None, extend the loras list with lora_stack
|
||||
if lora_stack is not None:
|
||||
loras.extend([l for l in lora_stack if l[0] != "None"])
|
||||
|
||||
return (loras,)
|
||||
|
||||
|
||||
class StreamDiffusion_Loader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
loras = ["None"] + folder_paths.get_filename_list("loras")
|
||||
return {
|
||||
"required": {
|
||||
"ckpt_name": (["Baked ckpt"]+folder_paths.get_filename_list("checkpoints"), ),
|
||||
"vae_name": (["Baked VAE"] + folder_paths.get_filename_list("vae"),),
|
||||
"lcm_lora": (loras,),
|
||||
"acceleration": (["none", "xfomers", "sfast", "tensorrt"],),
|
||||
|
||||
"use_tiny_vae": ("BOOLEAN", { "default": True }),
|
||||
"use_lcm_lora": ("BOOLEAN", { "default": True }),
|
||||
},
|
||||
"optional": {
|
||||
"lora_stack": ("LORA_STACK", ),
|
||||
},
|
||||
}
|
||||
RETURN_TYPES = ("MODEL",)
|
||||
RETURN_NAMES = ("MODEL",)
|
||||
|
||||
FUNCTION = "efficientloader"
|
||||
CATEGORY = "StreamDiffusion/Loader"
|
||||
|
||||
def efficientloader(self,ckpt_name,vae_name,lcm_lora,acceleration,use_tiny_vae,use_lcm_lora,lora_stack=None):
|
||||
|
||||
device = comfy.model_management.get_torch_device()
|
||||
device_name = comfy.model_management.get_torch_device_name(device)
|
||||
vae_dtype=comfy.model_management.vae_dtype()
|
||||
|
||||
if ckpt_name =='Baked ckpt':
|
||||
ckpt_path="KBlueLeaf/kohaku-v2.1"
|
||||
else:
|
||||
ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
|
||||
|
||||
if vae_name=='Baked VAE':
|
||||
vae_id = None
|
||||
else:
|
||||
vae_id=vae_name
|
||||
|
||||
if lcm_lora=='None':
|
||||
lcm_lora_id=None
|
||||
else:
|
||||
lcm_lora_id= folder_paths.get_full_path("loras", lcm_lora)
|
||||
|
||||
lora_dict =None
|
||||
if lora_stack is not None:
|
||||
lora_dict={}
|
||||
for lora_name, lora_scale, strength_clip in lora_stack:
|
||||
full_lora_name=folder_paths.get_full_path("loras", lora_name)
|
||||
lora_dict[full_lora_name]=lora_scale
|
||||
|
||||
t_index_list=[32,40,45]
|
||||
|
||||
stream = StreamDiffusionWrapper(
|
||||
model_id_or_path=ckpt_path,
|
||||
lora_dict=lora_dict,
|
||||
t_index_list=t_index_list,
|
||||
frame_buffer_size=1,
|
||||
width=512,
|
||||
height=512,
|
||||
warmup=10,
|
||||
acceleration=acceleration,
|
||||
use_tiny_vae =use_tiny_vae,
|
||||
device=device,
|
||||
use_lcm_lora = use_lcm_lora,
|
||||
output_type = 'pt',
|
||||
dtype = torch.float16,
|
||||
lcm_lora_id=lcm_lora_id,
|
||||
vae_id =vae_id,
|
||||
)
|
||||
|
||||
return (stream,)
|
||||
|
||||
|
||||
class StreamDiffusion_Sampler:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required":{
|
||||
"model": ("MODEL",),
|
||||
"positive": ("STRING", {"default": "CLIP_POSITIVE","multiline": True}),
|
||||
"negative": ("STRING", {"default": "CLIP_NEGATIVE", "multiline": True}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"steps": ("INT", {"default": 50, "min": 1, "max": 10000}),
|
||||
"cfg": ("FLOAT", {"default": 1.2, "min": 0.0, "max": 100.0}),
|
||||
"delta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0}),
|
||||
|
||||
"width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
||||
"height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
||||
"batch_size": ("INT", {"default": 1, "min": 1, "max": 10000, "step": 1}),#The frame buffer size for denoising batch, by default 1.
|
||||
|
||||
"index_list": ("STRING", {"default": "32,40,45","multiline": False}),
|
||||
|
||||
"cfg_type": (["none", "full", "self", "initialize"],),
|
||||
|
||||
"add_noise": ("BOOLEAN", { "default": True }),
|
||||
"use_denoising_batch": ("BOOLEAN", { "default": True }),
|
||||
"enable_similar_image_filter": ("BOOLEAN", { "default": False }),
|
||||
"use_safety_checker": ("BOOLEAN", { "default": False }),
|
||||
|
||||
},
|
||||
"optional": {
|
||||
"similar_image_filter_threshold": ("FLOAT", {"default": 0.98, "min": 0.0, "max": 100.0,"step": 0.01}),
|
||||
"similar_image_filter_max_skip_frame": ("INT", {"default": 10, "min": 0, "max": 100}),
|
||||
"latent": ("LATENT",),
|
||||
"image": ("IMAGE",),
|
||||
"lora_stack": ("LORA_STACK", ),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
|
||||
FUNCTION = "sample"
|
||||
CATEGORY = "StreamDiffusion/Sampler"
|
||||
|
||||
@torch.no_grad()
|
||||
def sample(self,model,positive,negative,seed,steps,cfg,delta,width,height,batch_size,index_list,cfg_type,add_noise,use_denoising_batch,enable_similar_image_filter=False,use_safety_checker=False,similar_image_filter_threshold= 0.98,similar_image_filter_max_skip_frame=10,latent=None,image=None,lora_stack=None):
|
||||
device = comfy.model_management.get_torch_device()
|
||||
device_name = comfy.model_management.get_torch_device_name(device)
|
||||
vae_dtype=comfy.model_management.vae_dtype()
|
||||
|
||||
# latent_image = latent["samples"]
|
||||
# latent_image = latent_image.to(model.device)
|
||||
# batch_size,channel,latent_height,latent_width,=latent_image.shape
|
||||
# width=latent_width*8
|
||||
# height =latent_height*8
|
||||
|
||||
t_index_list=[32,40,45]
|
||||
t_index_list =[int(i) for i in index_list.split(',')]
|
||||
|
||||
if image ==None:
|
||||
mode = "txt2img"
|
||||
else:
|
||||
mode = "img2img"
|
||||
image = image.movedim(-1,1)
|
||||
|
||||
if cfg <= 1.0:
|
||||
cfg_type = "none"
|
||||
|
||||
if batch_size>1 and mode=="txt2img":
|
||||
use_denoising_batch=False
|
||||
|
||||
|
||||
# stream.set_sampler_param(t_index_list=t_index_list,
|
||||
# width=width,
|
||||
# height=height,
|
||||
# do_add_noise= add_noise=='enable',
|
||||
# frame_buffer_size=frame_buffer_size,
|
||||
# use_denoising_batch=use_denoising_batch,
|
||||
# cfg_type=cfg_type,)
|
||||
|
||||
model.prepare(
|
||||
positive,
|
||||
negative,
|
||||
steps,
|
||||
cfg,
|
||||
delta,
|
||||
t_index_list,
|
||||
add_noise,
|
||||
enable_similar_image_filter,
|
||||
similar_image_filter_threshold,
|
||||
similar_image_filter_max_skip_frame,
|
||||
use_denoising_batch,
|
||||
cfg_type,
|
||||
seed,
|
||||
batch_size,
|
||||
use_safety_checker,
|
||||
)
|
||||
# latent_image = self.predict_x0_batch(
|
||||
# torch.randn((stream.batch_size, 4, stream.latent_height, stream.latent_width)).to(
|
||||
# device=stream.device, dtype=stream.dtype
|
||||
# )
|
||||
# )
|
||||
|
||||
# if batch_size==1:
|
||||
# for _ in range(stream.batch_size - 1):
|
||||
# stream()
|
||||
|
||||
output = model.sample(image).permute(0, 2, 3, 1)
|
||||
|
||||
return (output,)
|
||||
|
||||
|
||||
|
||||
def original(
|
||||
output: str = r"G:\Workspace\AI\ComfyUI\output\output.png",
|
||||
model_id_or_path: str = "KBlueLeaf/kohaku-v2.1",
|
||||
lora_dict: Optional[Dict[str, float]] = None,
|
||||
prompt: str = "a dog is running in the garden",
|
||||
width: int = 512,
|
||||
height: int = 512,
|
||||
acceleration: Literal["none", "xformers", "tensorrt"] = "xformers",
|
||||
use_denoising_batch: bool = False,
|
||||
seed: int = 2,
|
||||
):
|
||||
|
||||
"""
|
||||
Process for generating images based on a prompt using a specified model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
output : str, optional
|
||||
The output image file to save images to.
|
||||
model_id_or_path : str
|
||||
The name of the model to use for image generation.
|
||||
lora_dict : Optional[Dict[str, float]], optional
|
||||
The lora_dict to load, by default None.
|
||||
Keys are the LoRA names and values are the LoRA scales.
|
||||
Example: {'LoRA_1' : 0.5 , 'LoRA_2' : 0.7 ,...}
|
||||
prompt : str
|
||||
The prompt to generate images from.
|
||||
width : int, optional
|
||||
The width of the image, by default 512.
|
||||
height : int, optional
|
||||
The height of the image, by default 512.
|
||||
acceleration : Literal["none", "xformers", "tensorrt"]
|
||||
The type of acceleration to use for image generation.
|
||||
use_denoising_batch : bool, optional
|
||||
Whether to use denoising batch or not, by default False.
|
||||
seed : int, optional
|
||||
The seed, by default 2. if -1, use random seed.
|
||||
"""
|
||||
|
||||
stream = StreamDiffusionWrapper(
|
||||
model_id_or_path=model_id_or_path,
|
||||
lora_dict=lora_dict,
|
||||
t_index_list=[0, 16, 32, 45],
|
||||
frame_buffer_size=1,
|
||||
width=width,
|
||||
height=height,
|
||||
warmup=10,
|
||||
acceleration=acceleration,
|
||||
mode="txt2img",
|
||||
use_denoising_batch=use_denoising_batch,
|
||||
cfg_type="none",
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
stream.prepare(
|
||||
prompt=prompt,
|
||||
num_inference_steps=50,
|
||||
)
|
||||
|
||||
for _ in range(stream.batch_size - 1):
|
||||
stream()
|
||||
|
||||
output_image = stream()
|
||||
output_image.save(output)
|
||||
|
||||
class StreamDiffusion_Wrapper:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
loras = ["None"] + folder_paths.get_filename_list("loras")
|
||||
|
||||
return {
|
||||
"required":{
|
||||
|
||||
"ckpt_name": (["Baked ckpt"]+folder_paths.get_filename_list("checkpoints"), ),
|
||||
"vae_name": (["Baked VAE"] + folder_paths.get_filename_list("vae"),),
|
||||
"lcm_lora": (loras,),
|
||||
"acceleration": (["none", "xfomers", "sfast", "tensorrt"],),
|
||||
|
||||
"use_tiny_vae": ("BOOLEAN", { "default": True }),
|
||||
"use_lcm_lora": ("BOOLEAN", { "default": True }),
|
||||
|
||||
|
||||
"positive": ("STRING", {"default": "CLIP_POSITIVE","multiline": True}),
|
||||
"negative": ("STRING", {"default": "CLIP_NEGATIVE", "multiline": True}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"steps": ("INT", {"default": 50, "min": 1, "max": 10000}),
|
||||
"cfg": ("FLOAT", {"default": 1.2, "min": 0.0, "max": 100.0}),
|
||||
"delta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0}),
|
||||
|
||||
"width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
||||
"height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
||||
"batch_size": ("INT", {"default": 1, "min": 1, "max": 10000, "step": 1}),#The frame buffer size for denoising batch, by default 1.
|
||||
|
||||
"index_list": ("STRING", {"default": "32,40,45","multiline": False}),
|
||||
|
||||
"cfg_type": (["none", "full", "self", "initialize"],),
|
||||
|
||||
"add_noise": ("BOOLEAN", { "default": True }),
|
||||
"use_denoising_batch": ("BOOLEAN", { "default": True }),
|
||||
"enable_similar_image_filter": ("BOOLEAN", { "default": True }),
|
||||
"use_safety_checker": ("BOOLEAN", { "default": True }),
|
||||
|
||||
},
|
||||
"optional": {
|
||||
"similar_image_filter_threshold": ("FLOAT", {"default": 0.98, "min": 0.0, "max": 100.0,"step": 0.01}),
|
||||
"similar_image_filter_max_skip_frame": ("INT", {"default": 10, "min": 0, "max": 100}),
|
||||
"image": ("IMAGE",),
|
||||
"lora_stack": ("LORA_STACK", ),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
|
||||
FUNCTION = "sample"
|
||||
CATEGORY = "StreamDiffusion/Wrapper"
|
||||
|
||||
@torch.no_grad()
|
||||
def sample(self,ckpt_name,vae_name,lcm_lora,acceleration,use_tiny_vae,use_lcm_lora,positive,negative,seed,steps,cfg,delta,width,height,batch_size,index_list,cfg_type,add_noise,use_denoising_batch,enable_similar_image_filter=False,use_safety_checker=False,similar_image_filter_threshold= 0.98,similar_image_filter_max_skip_frame=10,image=None,lora_stack=None):
|
||||
|
||||
device = comfy.model_management.get_torch_device()
|
||||
device_name = comfy.model_management.get_torch_device_name(device)
|
||||
vae_dtype=comfy.model_management.vae_dtype()
|
||||
unet_dtype=comfy.model_management.unet_dtype
|
||||
text_encoder_dtype=comfy.model_management.text_encoder_dtype
|
||||
output_device = comfy.model_management.intermediate_device()
|
||||
|
||||
if ckpt_name =='Baked ckpt':
|
||||
ckpt_path="KBlueLeaf/kohaku-v2.1"
|
||||
else:
|
||||
ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
|
||||
|
||||
if vae_name=='Baked VAE':
|
||||
vae_id = None
|
||||
else:
|
||||
vae_id=vae_name
|
||||
|
||||
if lcm_lora=='None':
|
||||
lcm_lora_id=None
|
||||
else:
|
||||
lcm_lora_id= folder_paths.get_full_path("loras", lcm_lora)
|
||||
|
||||
lora_dict =None
|
||||
if lora_stack is not None:
|
||||
lora_dict={}
|
||||
for lora_name, lora_scale, strength_clip in lora_stack:
|
||||
full_lora_name=folder_paths.get_full_path("loras", lora_name)
|
||||
lora_dict[full_lora_name]=lora_scale
|
||||
|
||||
|
||||
t_index_list=[32,40,45]
|
||||
t_index_list =[int(i) for i in index_list.split(',')]
|
||||
|
||||
if image ==None:
|
||||
mode = "txt2img"
|
||||
else:
|
||||
mode = "img2img"
|
||||
image = image.movedim(-1,1)
|
||||
|
||||
if cfg <= 1.0:
|
||||
cfg_type = "none"
|
||||
|
||||
if batch_size>1 and mode=="txt2img":
|
||||
use_denoising_batch=False
|
||||
|
||||
stream = StreamDiffusionWrapper(
|
||||
model_id_or_path=ckpt_path,
|
||||
lora_dict=lora_dict,
|
||||
t_index_list=t_index_list,
|
||||
frame_buffer_size=batch_size,
|
||||
width=width,
|
||||
height=height,
|
||||
warmup=10,
|
||||
acceleration=acceleration,
|
||||
mode=mode,
|
||||
use_denoising_batch=use_denoising_batch,
|
||||
cfg_type=cfg_type,
|
||||
seed=seed,
|
||||
use_tiny_vae =use_tiny_vae,
|
||||
do_add_noise = add_noise,
|
||||
device=device,
|
||||
use_lcm_lora = use_lcm_lora,
|
||||
output_type = 'pt',
|
||||
dtype = torch.float16,
|
||||
lcm_lora_id=lcm_lora_id,
|
||||
vae_id =vae_id,
|
||||
enable_similar_image_filter = enable_similar_image_filter,
|
||||
similar_image_filter_threshold =similar_image_filter_threshold,
|
||||
similar_image_filter_max_skip_frame=similar_image_filter_max_skip_frame,
|
||||
use_safety_checker = use_safety_checker,
|
||||
)
|
||||
|
||||
# stream.set_sampler_param(t_index_list=t_index_list,
|
||||
# width=width,
|
||||
# height=height,
|
||||
# do_add_noise= add_noise=='enable',
|
||||
# frame_buffer_size=frame_buffer_size,
|
||||
# use_denoising_batch=use_denoising_batch,
|
||||
# cfg_type=cfg_type,)
|
||||
|
||||
stream.prepare(
|
||||
positive,
|
||||
negative,
|
||||
num_inference_steps=steps,
|
||||
guidance_scale=cfg,
|
||||
delta=delta,
|
||||
# generator=torch.manual_seed(seed),
|
||||
# seed=seed,
|
||||
)
|
||||
# latent_image = self.predict_x0_batch(
|
||||
# torch.randn((stream.batch_size, 4, stream.latent_height, stream.latent_width)).to(
|
||||
# device=stream.device, dtype=stream.dtype
|
||||
# )
|
||||
# )
|
||||
|
||||
# if batch_size==1:
|
||||
# for _ in range(stream.batch_size - 1):
|
||||
# stream()
|
||||
|
||||
output = stream.sample(image).permute(0, 2, 3, 1)
|
||||
|
||||
# if not isinstance(output,list):
|
||||
# output_pils=[output]
|
||||
# else:
|
||||
# output_pils=output
|
||||
|
||||
# output_images=[]
|
||||
# output_masks=[]
|
||||
|
||||
# for i in output_pils:
|
||||
# i = ImageOps.exif_transpose(i)
|
||||
# image = i.convert("RGB")
|
||||
# image = np.array(image).astype(np.float32) / 255.0
|
||||
# image = torch.from_numpy(image)[None,]
|
||||
# if 'A' in i.getbands():
|
||||
# mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
|
||||
# mask = 1. - torch.from_numpy(mask)
|
||||
# else:
|
||||
# mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
|
||||
# output_images.append(image)
|
||||
# output_masks.append(mask.unsqueeze(0))
|
||||
|
||||
# if len(output_images) > 1:
|
||||
# output_image = torch.cat(output_images, dim=0)
|
||||
# output_mask = torch.cat(output_masks, dim=0)
|
||||
# else:
|
||||
# output_image = output_images[0]
|
||||
# output_mask = output_masks[0]
|
||||
|
||||
return (output,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"StreamDiffusion_Loader": StreamDiffusion_Loader,
|
||||
"StreamDiffusion_Sampler":StreamDiffusion_Sampler,
|
||||
"StreamDiffusion_LoRA_Stacker":StreamDiffusion_LoRA_Stacker,
|
||||
"StreamDiffusion_Wrapper":StreamDiffusion_Wrapper
|
||||
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"StreamDiffusion_Loader": "StreamDiffusion_Loader",
|
||||
"StreamDiffusion_Sampler":"StreamDiffusion_Sampler",
|
||||
"StreamDiffusion_LoRA_Stacker":"StreamDiffusion_LoRA_Stacker",
|
||||
"StreamDiffusion_Wrapper":"StreamDiffusion_Wrapper",
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import Literal, Dict, Optional
|
||||
|
||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
def main(
|
||||
output: str = os.path.join(CURRENT_DIR, "..", "..", "images", "outputs", "output.png"),
|
||||
model_id_or_path: str = "KBlueLeaf/kohaku-v2.1",
|
||||
lora_dict: Optional[Dict[str, float]] = None,
|
||||
prompt: str = "a dog is running in the garden",
|
||||
width: int = 512,
|
||||
height: int = 512,
|
||||
acceleration: Literal["none", "xformers", "tensorrt"] = "xformers",
|
||||
use_denoising_batch: bool = False,
|
||||
seed: int = 2,
|
||||
):
|
||||
|
||||
"""
|
||||
Process for generating images based on a prompt using a specified model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
output : str, optional
|
||||
The output image file to save images to.
|
||||
model_id_or_path : str
|
||||
The name of the model to use for image generation.
|
||||
lora_dict : Optional[Dict[str, float]], optional
|
||||
The lora_dict to load, by default None.
|
||||
Keys are the LoRA names and values are the LoRA scales.
|
||||
Example: {'LoRA_1' : 0.5 , 'LoRA_2' : 0.7 ,...}
|
||||
prompt : str
|
||||
The prompt to generate images from.
|
||||
width : int, optional
|
||||
The width of the image, by default 512.
|
||||
height : int, optional
|
||||
The height of the image, by default 512.
|
||||
acceleration : Literal["none", "xformers", "tensorrt"]
|
||||
The type of acceleration to use for image generation.
|
||||
use_denoising_batch : bool, optional
|
||||
Whether to use denoising batch or not, by default False.
|
||||
seed : int, optional
|
||||
The seed, by default 2. if -1, use random seed.
|
||||
"""
|
||||
|
||||
stream = StreamDiffusionWrapper(
|
||||
model_id_or_path=model_id_or_path,
|
||||
lora_dict=lora_dict,
|
||||
t_index_list=[0, 16, 32, 45],
|
||||
frame_buffer_size=1,
|
||||
width=width,
|
||||
height=height,
|
||||
warmup=10,
|
||||
acceleration=acceleration,
|
||||
mode="txt2img",
|
||||
use_denoising_batch=use_denoising_batch,
|
||||
cfg_type="none",
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
stream.prepare(
|
||||
prompt=prompt,
|
||||
num_inference_steps=50,
|
||||
)
|
||||
|
||||
for _ in range(stream.batch_size - 1):
|
||||
stream()
|
||||
|
||||
output_image = stream()
|
||||
output_image.save(output)
|
||||
@@ -0,0 +1 @@
|
||||
from .pipeline import StreamDiffusion
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
from typing import Optional
|
||||
|
||||
from sfast.compilers.stable_diffusion_pipeline_compiler import CompilationConfig, compile
|
||||
|
||||
from ...pipeline import StreamDiffusion
|
||||
|
||||
|
||||
def accelerate_with_stable_fast(
|
||||
stream: StreamDiffusion,
|
||||
config: Optional[CompilationConfig] = None,
|
||||
):
|
||||
if config is None:
|
||||
config = CompilationConfig.Default()
|
||||
# xformers and Triton are suggested for achieving best performance.
|
||||
try:
|
||||
import xformers
|
||||
|
||||
config.enable_xformers = True
|
||||
except ImportError:
|
||||
print("xformers not installed, skip")
|
||||
try:
|
||||
import triton
|
||||
|
||||
config.enable_triton = True
|
||||
except ImportError:
|
||||
print("Triton not installed, skip")
|
||||
# CUDA Graph is suggested for small batch sizes and small resolutions to reduce CPU overhead.
|
||||
config.enable_cuda_graph = True
|
||||
stream.pipe = compile(stream.pipe, config)
|
||||
stream.unet = stream.pipe.unet
|
||||
stream.vae = stream.pipe.vae
|
||||
stream.text_encoder = stream.pipe.text_encoder
|
||||
return stream
|
||||
@@ -0,0 +1,187 @@
|
||||
import gc
|
||||
import os
|
||||
|
||||
import torch
|
||||
from diffusers import AutoencoderKL, UNet2DConditionModel
|
||||
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img import (
|
||||
retrieve_latents,
|
||||
)
|
||||
from polygraphy import cuda
|
||||
|
||||
from ...pipeline import StreamDiffusion
|
||||
from .builder import EngineBuilder, create_onnx_path
|
||||
from .engine import AutoencoderKLEngine, UNet2DConditionModelEngine
|
||||
from .models import VAE, BaseModel, UNet, VAEEncoder
|
||||
|
||||
|
||||
class TorchVAEEncoder(torch.nn.Module):
|
||||
def __init__(self, vae: AutoencoderKL):
|
||||
super().__init__()
|
||||
self.vae = vae
|
||||
|
||||
def forward(self, x: torch.Tensor):
|
||||
return retrieve_latents(self.vae.encode(x))
|
||||
|
||||
|
||||
def compile_vae_encoder(
|
||||
vae: TorchVAEEncoder,
|
||||
model_data: BaseModel,
|
||||
onnx_path: str,
|
||||
onnx_opt_path: str,
|
||||
engine_path: str,
|
||||
opt_batch_size: int = 1,
|
||||
engine_build_options: dict = {},
|
||||
):
|
||||
builder = EngineBuilder(model_data, vae, device=torch.device("cuda"))
|
||||
builder.build(
|
||||
onnx_path,
|
||||
onnx_opt_path,
|
||||
engine_path,
|
||||
opt_batch_size=opt_batch_size,
|
||||
**engine_build_options,
|
||||
)
|
||||
|
||||
|
||||
def compile_vae_decoder(
|
||||
vae: AutoencoderKL,
|
||||
model_data: BaseModel,
|
||||
onnx_path: str,
|
||||
onnx_opt_path: str,
|
||||
engine_path: str,
|
||||
opt_batch_size: int = 1,
|
||||
engine_build_options: dict = {},
|
||||
):
|
||||
vae = vae.to(torch.device("cuda"))
|
||||
builder = EngineBuilder(model_data, vae, device=torch.device("cuda"))
|
||||
builder.build(
|
||||
onnx_path,
|
||||
onnx_opt_path,
|
||||
engine_path,
|
||||
opt_batch_size=opt_batch_size,
|
||||
**engine_build_options,
|
||||
)
|
||||
|
||||
|
||||
def compile_unet(
|
||||
unet: UNet2DConditionModel,
|
||||
model_data: BaseModel,
|
||||
onnx_path: str,
|
||||
onnx_opt_path: str,
|
||||
engine_path: str,
|
||||
opt_batch_size: int = 1,
|
||||
engine_build_options: dict = {},
|
||||
):
|
||||
unet = unet.to(torch.device("cuda"), dtype=torch.float16)
|
||||
builder = EngineBuilder(model_data, unet, device=torch.device("cuda"))
|
||||
builder.build(
|
||||
onnx_path,
|
||||
onnx_opt_path,
|
||||
engine_path,
|
||||
opt_batch_size=opt_batch_size,
|
||||
**engine_build_options,
|
||||
)
|
||||
|
||||
|
||||
def accelerate_with_tensorrt(
|
||||
stream: StreamDiffusion,
|
||||
engine_dir: str,
|
||||
max_batch_size: int = 2,
|
||||
min_batch_size: int = 1,
|
||||
use_cuda_graph: bool = False,
|
||||
engine_build_options: dict = {},
|
||||
):
|
||||
if "opt_batch_size" not in engine_build_options or engine_build_options["opt_batch_size"] is None:
|
||||
engine_build_options["opt_batch_size"] = max_batch_size
|
||||
text_encoder = stream.text_encoder
|
||||
unet = stream.unet
|
||||
vae = stream.vae
|
||||
|
||||
del stream.unet, stream.vae, stream.pipe.unet, stream.pipe.vae
|
||||
|
||||
vae_config = vae.config
|
||||
vae_dtype = vae.dtype
|
||||
|
||||
unet.to(torch.device("cpu"))
|
||||
vae.to(torch.device("cpu"))
|
||||
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
onnx_dir = os.path.join(engine_dir, "onnx")
|
||||
os.makedirs(onnx_dir, exist_ok=True)
|
||||
|
||||
unet_engine_path = f"{engine_dir}/unet.engine"
|
||||
vae_encoder_engine_path = f"{engine_dir}/vae_encoder.engine"
|
||||
vae_decoder_engine_path = f"{engine_dir}/vae_decoder.engine"
|
||||
|
||||
unet_model = UNet(
|
||||
fp16=True,
|
||||
device=stream.device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
embedding_dim=text_encoder.config.hidden_size,
|
||||
unet_dim=unet.config.in_channels,
|
||||
)
|
||||
vae_decoder_model = VAE(
|
||||
device=stream.device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
)
|
||||
vae_encoder_model = VAEEncoder(
|
||||
device=stream.device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
)
|
||||
|
||||
if not os.path.exists(unet_engine_path):
|
||||
compile_unet(
|
||||
unet,
|
||||
unet_model,
|
||||
create_onnx_path("unet", onnx_dir, opt=False),
|
||||
create_onnx_path("unet", onnx_dir, opt=True),
|
||||
opt_batch_size=max_batch_size,
|
||||
**engine_build_options,
|
||||
)
|
||||
else:
|
||||
del unet
|
||||
|
||||
if not os.path.exists(vae_decoder_engine_path):
|
||||
compile_vae_decoder(
|
||||
vae,
|
||||
vae_decoder_model,
|
||||
create_onnx_path("vae_decoder", onnx_dir, opt=False),
|
||||
create_onnx_path("vae_decoder", onnx_dir, opt=True),
|
||||
opt_batch_size=max_batch_size,
|
||||
**engine_build_options,
|
||||
)
|
||||
|
||||
if not os.path.exists(vae_encoder_engine_path):
|
||||
vae_encoder = TorchVAEEncoder(vae).to(torch.device("cuda"))
|
||||
compile_vae_encoder(
|
||||
vae_encoder,
|
||||
vae_encoder_model,
|
||||
create_onnx_path("vae_encoder", onnx_dir, opt=False),
|
||||
create_onnx_path("vae_encoder", onnx_dir, opt=True),
|
||||
opt_batch_size=max_batch_size,
|
||||
**engine_build_options,
|
||||
)
|
||||
|
||||
del vae
|
||||
|
||||
cuda_steram = cuda.Stream()
|
||||
|
||||
stream.unet = UNet2DConditionModelEngine(unet_engine_path, cuda_steram, use_cuda_graph=use_cuda_graph)
|
||||
stream.vae = AutoencoderKLEngine(
|
||||
vae_encoder_engine_path,
|
||||
vae_decoder_engine_path,
|
||||
cuda_steram,
|
||||
stream.pipe.vae_scale_factor,
|
||||
use_cuda_graph=use_cuda_graph,
|
||||
)
|
||||
setattr(stream.vae, "config", vae_config)
|
||||
setattr(stream.vae, "dtype", vae_dtype)
|
||||
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
return stream
|
||||
@@ -0,0 +1,94 @@
|
||||
import gc
|
||||
import os
|
||||
from typing import *
|
||||
|
||||
import torch
|
||||
|
||||
from .models import BaseModel
|
||||
from .utilities import (
|
||||
build_engine,
|
||||
export_onnx,
|
||||
optimize_onnx,
|
||||
)
|
||||
|
||||
|
||||
def create_onnx_path(name, onnx_dir, opt=True):
|
||||
return os.path.join(onnx_dir, name + (".opt" if opt else "") + ".onnx")
|
||||
|
||||
|
||||
class EngineBuilder:
|
||||
def __init__(
|
||||
self,
|
||||
model: BaseModel,
|
||||
network: Any,
|
||||
device=torch.device("cuda"),
|
||||
):
|
||||
self.device = device
|
||||
|
||||
self.model = model
|
||||
self.network = network
|
||||
|
||||
def build(
|
||||
self,
|
||||
onnx_path: str,
|
||||
onnx_opt_path: str,
|
||||
engine_path: str,
|
||||
opt_image_height: int = 512,
|
||||
opt_image_width: int = 512,
|
||||
opt_batch_size: int = 1,
|
||||
min_image_resolution: int = 256,
|
||||
max_image_resolution: int = 1024,
|
||||
build_enable_refit: bool = False,
|
||||
build_static_batch: bool = False,
|
||||
build_dynamic_shape: bool = False,
|
||||
build_all_tactics: bool = False,
|
||||
onnx_opset: int = 17,
|
||||
force_engine_build: bool = False,
|
||||
force_onnx_export: bool = False,
|
||||
force_onnx_optimize: bool = False,
|
||||
):
|
||||
if not force_onnx_export and os.path.exists(onnx_path):
|
||||
print(f"Found cached model: {onnx_path}")
|
||||
else:
|
||||
print(f"Exporting model: {onnx_path}")
|
||||
export_onnx(
|
||||
self.network,
|
||||
onnx_path=onnx_path,
|
||||
model_data=self.model,
|
||||
opt_image_height=opt_image_height,
|
||||
opt_image_width=opt_image_width,
|
||||
opt_batch_size=opt_batch_size,
|
||||
onnx_opset=onnx_opset,
|
||||
)
|
||||
del self.network
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
if not force_onnx_optimize and os.path.exists(onnx_opt_path):
|
||||
print(f"Found cached model: {onnx_opt_path}")
|
||||
else:
|
||||
print(f"Generating optimizing model: {onnx_opt_path}")
|
||||
optimize_onnx(
|
||||
onnx_path=onnx_path,
|
||||
onnx_opt_path=onnx_opt_path,
|
||||
model_data=self.model,
|
||||
)
|
||||
self.model.min_latent_shape = min_image_resolution // 8
|
||||
self.model.max_latent_shape = max_image_resolution // 8
|
||||
if not force_engine_build and os.path.exists(engine_path):
|
||||
print(f"Found cached engine: {engine_path}")
|
||||
else:
|
||||
build_engine(
|
||||
engine_path=engine_path,
|
||||
onnx_opt_path=onnx_opt_path,
|
||||
model_data=self.model,
|
||||
opt_image_height=opt_image_height,
|
||||
opt_image_width=opt_image_width,
|
||||
opt_batch_size=opt_batch_size,
|
||||
build_static_batch=build_static_batch,
|
||||
build_dynamic_shape=build_dynamic_shape,
|
||||
build_all_tactics=build_all_tactics,
|
||||
build_enable_refit=build_enable_refit,
|
||||
)
|
||||
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
@@ -0,0 +1,123 @@
|
||||
from typing import *
|
||||
|
||||
import torch
|
||||
from diffusers.models.autoencoder_tiny import AutoencoderTinyOutput
|
||||
from diffusers.models.unet_2d_condition import UNet2DConditionOutput
|
||||
from diffusers.models.vae import DecoderOutput
|
||||
from polygraphy import cuda
|
||||
|
||||
from .utilities import Engine
|
||||
|
||||
|
||||
class UNet2DConditionModelEngine:
|
||||
def __init__(self, filepath: str, stream: cuda.Stream, use_cuda_graph: bool = False):
|
||||
self.engine = Engine(filepath)
|
||||
self.stream = stream
|
||||
self.use_cuda_graph = use_cuda_graph
|
||||
|
||||
self.engine.load()
|
||||
self.engine.activate()
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
latent_model_input: torch.Tensor,
|
||||
timestep: torch.Tensor,
|
||||
encoder_hidden_states: torch.Tensor,
|
||||
**kwargs,
|
||||
) -> Any:
|
||||
if timestep.dtype != torch.float32:
|
||||
timestep = timestep.float()
|
||||
|
||||
self.engine.allocate_buffers(
|
||||
shape_dict={
|
||||
"sample": latent_model_input.shape,
|
||||
"timestep": timestep.shape,
|
||||
"encoder_hidden_states": encoder_hidden_states.shape,
|
||||
"latent": latent_model_input.shape,
|
||||
},
|
||||
device=latent_model_input.device,
|
||||
)
|
||||
|
||||
noise_pred = self.engine.infer(
|
||||
{
|
||||
"sample": latent_model_input,
|
||||
"timestep": timestep,
|
||||
"encoder_hidden_states": encoder_hidden_states,
|
||||
},
|
||||
self.stream,
|
||||
use_cuda_graph=self.use_cuda_graph,
|
||||
)["latent"]
|
||||
return UNet2DConditionOutput(sample=noise_pred)
|
||||
|
||||
def to(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def forward(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class AutoencoderKLEngine:
|
||||
def __init__(
|
||||
self,
|
||||
encoder_path: str,
|
||||
decoder_path: str,
|
||||
stream: cuda.Stream,
|
||||
scaling_factor: int,
|
||||
use_cuda_graph: bool = False,
|
||||
):
|
||||
self.encoder = Engine(encoder_path)
|
||||
self.decoder = Engine(decoder_path)
|
||||
self.stream = stream
|
||||
self.vae_scale_factor = scaling_factor
|
||||
self.use_cuda_graph = use_cuda_graph
|
||||
|
||||
self.encoder.load()
|
||||
self.decoder.load()
|
||||
self.encoder.activate()
|
||||
self.decoder.activate()
|
||||
|
||||
def encode(self, images: torch.Tensor, **kwargs):
|
||||
self.encoder.allocate_buffers(
|
||||
shape_dict={
|
||||
"images": images.shape,
|
||||
"latent": (
|
||||
images.shape[0],
|
||||
4,
|
||||
images.shape[2] // self.vae_scale_factor,
|
||||
images.shape[3] // self.vae_scale_factor,
|
||||
),
|
||||
},
|
||||
device=images.device,
|
||||
)
|
||||
latents = self.encoder.infer(
|
||||
{"images": images},
|
||||
self.stream,
|
||||
use_cuda_graph=self.use_cuda_graph,
|
||||
)["latent"]
|
||||
return AutoencoderTinyOutput(latents=latents)
|
||||
|
||||
def decode(self, latent: torch.Tensor, **kwargs):
|
||||
self.decoder.allocate_buffers(
|
||||
shape_dict={
|
||||
"latent": latent.shape,
|
||||
"images": (
|
||||
latent.shape[0],
|
||||
3,
|
||||
latent.shape[2] * self.vae_scale_factor,
|
||||
latent.shape[3] * self.vae_scale_factor,
|
||||
),
|
||||
},
|
||||
device=latent.device,
|
||||
)
|
||||
images = self.decoder.infer(
|
||||
{"latent": latent},
|
||||
self.stream,
|
||||
use_cuda_graph=self.use_cuda_graph,
|
||||
)["images"]
|
||||
return DecoderOutput(sample=images)
|
||||
|
||||
def to(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def forward(self, *args, **kwargs):
|
||||
pass
|
||||
@@ -0,0 +1,434 @@
|
||||
#! fork: https://github.com/NVIDIA/TensorRT/blob/main/demo/Diffusion/models.py
|
||||
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import onnx_graphsurgeon as gs
|
||||
import torch
|
||||
from onnx import shape_inference
|
||||
from polygraphy.backend.onnx.loader import fold_constants
|
||||
|
||||
|
||||
class Optimizer:
|
||||
def __init__(self, onnx_graph, verbose=False):
|
||||
self.graph = gs.import_onnx(onnx_graph)
|
||||
self.verbose = verbose
|
||||
|
||||
def info(self, prefix):
|
||||
if self.verbose:
|
||||
print(
|
||||
f"{prefix} .. {len(self.graph.nodes)} nodes, {len(self.graph.tensors().keys())} tensors, {len(self.graph.inputs)} inputs, {len(self.graph.outputs)} outputs"
|
||||
)
|
||||
|
||||
def cleanup(self, return_onnx=False):
|
||||
self.graph.cleanup().toposort()
|
||||
if return_onnx:
|
||||
return gs.export_onnx(self.graph)
|
||||
|
||||
def select_outputs(self, keep, names=None):
|
||||
self.graph.outputs = [self.graph.outputs[o] for o in keep]
|
||||
if names:
|
||||
for i, name in enumerate(names):
|
||||
self.graph.outputs[i].name = name
|
||||
|
||||
def fold_constants(self, return_onnx=False):
|
||||
onnx_graph = fold_constants(gs.export_onnx(self.graph), allow_onnxruntime_shape_inference=True)
|
||||
self.graph = gs.import_onnx(onnx_graph)
|
||||
if return_onnx:
|
||||
return onnx_graph
|
||||
|
||||
def infer_shapes(self, return_onnx=False):
|
||||
onnx_graph = gs.export_onnx(self.graph)
|
||||
if onnx_graph.ByteSize() > 2147483648:
|
||||
raise TypeError("ERROR: model size exceeds supported 2GB limit")
|
||||
else:
|
||||
onnx_graph = shape_inference.infer_shapes(onnx_graph)
|
||||
|
||||
self.graph = gs.import_onnx(onnx_graph)
|
||||
if return_onnx:
|
||||
return onnx_graph
|
||||
|
||||
|
||||
class BaseModel:
|
||||
def __init__(
|
||||
self,
|
||||
fp16=False,
|
||||
device="cuda",
|
||||
verbose=True,
|
||||
max_batch_size=16,
|
||||
min_batch_size=1,
|
||||
embedding_dim=768,
|
||||
text_maxlen=77,
|
||||
):
|
||||
self.name = "SD Model"
|
||||
self.fp16 = fp16
|
||||
self.device = device
|
||||
self.verbose = verbose
|
||||
|
||||
self.min_batch = min_batch_size
|
||||
self.max_batch = max_batch_size
|
||||
self.min_image_shape = 256 # min image resolution: 256x256
|
||||
self.max_image_shape = 1024 # max image resolution: 1024x1024
|
||||
self.min_latent_shape = self.min_image_shape // 8
|
||||
self.max_latent_shape = self.max_image_shape // 8
|
||||
|
||||
self.embedding_dim = embedding_dim
|
||||
self.text_maxlen = text_maxlen
|
||||
|
||||
def get_model(self):
|
||||
pass
|
||||
|
||||
def get_input_names(self):
|
||||
pass
|
||||
|
||||
def get_output_names(self):
|
||||
pass
|
||||
|
||||
def get_dynamic_axes(self):
|
||||
return None
|
||||
|
||||
def get_sample_input(self, batch_size, image_height, image_width):
|
||||
pass
|
||||
|
||||
def get_input_profile(self, batch_size, image_height, image_width, static_batch, static_shape):
|
||||
return None
|
||||
|
||||
def get_shape_dict(self, batch_size, image_height, image_width):
|
||||
return None
|
||||
|
||||
def optimize(self, onnx_graph):
|
||||
opt = Optimizer(onnx_graph, verbose=self.verbose)
|
||||
opt.info(self.name + ": original")
|
||||
opt.cleanup()
|
||||
opt.info(self.name + ": cleanup")
|
||||
opt.fold_constants()
|
||||
opt.info(self.name + ": fold constants")
|
||||
opt.infer_shapes()
|
||||
opt.info(self.name + ": shape inference")
|
||||
onnx_opt_graph = opt.cleanup(return_onnx=True)
|
||||
opt.info(self.name + ": finished")
|
||||
return onnx_opt_graph
|
||||
|
||||
def check_dims(self, batch_size, image_height, image_width):
|
||||
assert batch_size >= self.min_batch and batch_size <= self.max_batch
|
||||
assert image_height % 8 == 0 or image_width % 8 == 0
|
||||
latent_height = image_height // 8
|
||||
latent_width = image_width // 8
|
||||
assert latent_height >= self.min_latent_shape and latent_height <= self.max_latent_shape
|
||||
assert latent_width >= self.min_latent_shape and latent_width <= self.max_latent_shape
|
||||
return (latent_height, latent_width)
|
||||
|
||||
def get_minmax_dims(self, batch_size, image_height, image_width, static_batch, static_shape):
|
||||
min_batch = batch_size if static_batch else self.min_batch
|
||||
max_batch = batch_size if static_batch else self.max_batch
|
||||
latent_height = image_height // 8
|
||||
latent_width = image_width // 8
|
||||
min_image_height = image_height if static_shape else self.min_image_shape
|
||||
max_image_height = image_height if static_shape else self.max_image_shape
|
||||
min_image_width = image_width if static_shape else self.min_image_shape
|
||||
max_image_width = image_width if static_shape else self.max_image_shape
|
||||
min_latent_height = latent_height if static_shape else self.min_latent_shape
|
||||
max_latent_height = latent_height if static_shape else self.max_latent_shape
|
||||
min_latent_width = latent_width if static_shape else self.min_latent_shape
|
||||
max_latent_width = latent_width if static_shape else self.max_latent_shape
|
||||
return (
|
||||
min_batch,
|
||||
max_batch,
|
||||
min_image_height,
|
||||
max_image_height,
|
||||
min_image_width,
|
||||
max_image_width,
|
||||
min_latent_height,
|
||||
max_latent_height,
|
||||
min_latent_width,
|
||||
max_latent_width,
|
||||
)
|
||||
|
||||
|
||||
class CLIP(BaseModel):
|
||||
def __init__(self, device, max_batch_size, embedding_dim, min_batch_size=1):
|
||||
super(CLIP, self).__init__(
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
embedding_dim=embedding_dim,
|
||||
)
|
||||
self.name = "CLIP"
|
||||
|
||||
def get_input_names(self):
|
||||
return ["input_ids"]
|
||||
|
||||
def get_output_names(self):
|
||||
return ["text_embeddings", "pooler_output"]
|
||||
|
||||
def get_dynamic_axes(self):
|
||||
return {"input_ids": {0: "B"}, "text_embeddings": {0: "B"}}
|
||||
|
||||
def get_input_profile(self, batch_size, image_height, image_width, static_batch, static_shape):
|
||||
self.check_dims(batch_size, image_height, image_width)
|
||||
min_batch, max_batch, _, _, _, _, _, _, _, _ = self.get_minmax_dims(
|
||||
batch_size, image_height, image_width, static_batch, static_shape
|
||||
)
|
||||
return {
|
||||
"input_ids": [
|
||||
(min_batch, self.text_maxlen),
|
||||
(batch_size, self.text_maxlen),
|
||||
(max_batch, self.text_maxlen),
|
||||
]
|
||||
}
|
||||
|
||||
def get_shape_dict(self, batch_size, image_height, image_width):
|
||||
self.check_dims(batch_size, image_height, image_width)
|
||||
return {
|
||||
"input_ids": (batch_size, self.text_maxlen),
|
||||
"text_embeddings": (batch_size, self.text_maxlen, self.embedding_dim),
|
||||
}
|
||||
|
||||
def get_sample_input(self, batch_size, image_height, image_width):
|
||||
self.check_dims(batch_size, image_height, image_width)
|
||||
return torch.zeros(batch_size, self.text_maxlen, dtype=torch.int32, device=self.device)
|
||||
|
||||
def optimize(self, onnx_graph):
|
||||
opt = Optimizer(onnx_graph)
|
||||
opt.info(self.name + ": original")
|
||||
opt.select_outputs([0]) # delete graph output#1
|
||||
opt.cleanup()
|
||||
opt.info(self.name + ": remove output[1]")
|
||||
opt.fold_constants()
|
||||
opt.info(self.name + ": fold constants")
|
||||
opt.infer_shapes()
|
||||
opt.info(self.name + ": shape inference")
|
||||
opt.select_outputs([0], names=["text_embeddings"]) # rename network output
|
||||
opt.info(self.name + ": remove output[0]")
|
||||
opt_onnx_graph = opt.cleanup(return_onnx=True)
|
||||
opt.info(self.name + ": finished")
|
||||
return opt_onnx_graph
|
||||
|
||||
|
||||
class UNet(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
fp16=False,
|
||||
device="cuda",
|
||||
max_batch_size=16,
|
||||
min_batch_size=1,
|
||||
embedding_dim=768,
|
||||
text_maxlen=77,
|
||||
unet_dim=4,
|
||||
):
|
||||
super(UNet, self).__init__(
|
||||
fp16=fp16,
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
embedding_dim=embedding_dim,
|
||||
text_maxlen=text_maxlen,
|
||||
)
|
||||
self.unet_dim = unet_dim
|
||||
self.name = "UNet"
|
||||
|
||||
def get_input_names(self):
|
||||
return ["sample", "timestep", "encoder_hidden_states"]
|
||||
|
||||
def get_output_names(self):
|
||||
return ["latent"]
|
||||
|
||||
def get_dynamic_axes(self):
|
||||
return {
|
||||
"sample": {0: "2B", 2: "H", 3: "W"},
|
||||
"timestep": {0: "2B"},
|
||||
"encoder_hidden_states": {0: "2B"},
|
||||
"latent": {0: "2B", 2: "H", 3: "W"},
|
||||
}
|
||||
|
||||
def get_input_profile(self, batch_size, image_height, image_width, static_batch, static_shape):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
(
|
||||
min_batch,
|
||||
max_batch,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
min_latent_height,
|
||||
max_latent_height,
|
||||
min_latent_width,
|
||||
max_latent_width,
|
||||
) = self.get_minmax_dims(batch_size, image_height, image_width, static_batch, static_shape)
|
||||
return {
|
||||
"sample": [
|
||||
(min_batch, self.unet_dim, min_latent_height, min_latent_width),
|
||||
(batch_size, self.unet_dim, latent_height, latent_width),
|
||||
(max_batch, self.unet_dim, max_latent_height, max_latent_width),
|
||||
],
|
||||
"timestep": [(min_batch,), (batch_size,), (max_batch,)],
|
||||
"encoder_hidden_states": [
|
||||
(min_batch, self.text_maxlen, self.embedding_dim),
|
||||
(batch_size, self.text_maxlen, self.embedding_dim),
|
||||
(max_batch, self.text_maxlen, self.embedding_dim),
|
||||
],
|
||||
}
|
||||
|
||||
def get_shape_dict(self, batch_size, image_height, image_width):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
return {
|
||||
"sample": (2 * batch_size, self.unet_dim, latent_height, latent_width),
|
||||
"timestep": (2 * batch_size,),
|
||||
"encoder_hidden_states": (2 * batch_size, self.text_maxlen, self.embedding_dim),
|
||||
"latent": (2 * batch_size, 4, latent_height, latent_width),
|
||||
}
|
||||
|
||||
def get_sample_input(self, batch_size, image_height, image_width):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
dtype = torch.float16 if self.fp16 else torch.float32
|
||||
return (
|
||||
torch.randn(
|
||||
2 * batch_size, self.unet_dim, latent_height, latent_width, dtype=torch.float32, device=self.device
|
||||
),
|
||||
torch.ones((2 * batch_size,), dtype=torch.float32, device=self.device),
|
||||
torch.randn(2 * batch_size, self.text_maxlen, self.embedding_dim, dtype=dtype, device=self.device),
|
||||
)
|
||||
|
||||
|
||||
class VAE(BaseModel):
|
||||
def __init__(self, device, max_batch_size, min_batch_size=1):
|
||||
super(VAE, self).__init__(
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
embedding_dim=None,
|
||||
)
|
||||
self.name = "VAE decoder"
|
||||
|
||||
def get_input_names(self):
|
||||
return ["latent"]
|
||||
|
||||
def get_output_names(self):
|
||||
return ["images"]
|
||||
|
||||
def get_dynamic_axes(self):
|
||||
return {
|
||||
"latent": {0: "B", 2: "H", 3: "W"},
|
||||
"images": {0: "B", 2: "8H", 3: "8W"},
|
||||
}
|
||||
|
||||
def get_input_profile(self, batch_size, image_height, image_width, static_batch, static_shape):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
(
|
||||
min_batch,
|
||||
max_batch,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
min_latent_height,
|
||||
max_latent_height,
|
||||
min_latent_width,
|
||||
max_latent_width,
|
||||
) = self.get_minmax_dims(batch_size, image_height, image_width, static_batch, static_shape)
|
||||
return {
|
||||
"latent": [
|
||||
(min_batch, 4, min_latent_height, min_latent_width),
|
||||
(batch_size, 4, latent_height, latent_width),
|
||||
(max_batch, 4, max_latent_height, max_latent_width),
|
||||
]
|
||||
}
|
||||
|
||||
def get_shape_dict(self, batch_size, image_height, image_width):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
return {
|
||||
"latent": (batch_size, 4, latent_height, latent_width),
|
||||
"images": (batch_size, 3, image_height, image_width),
|
||||
}
|
||||
|
||||
def get_sample_input(self, batch_size, image_height, image_width):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
return torch.randn(
|
||||
batch_size,
|
||||
4,
|
||||
latent_height,
|
||||
latent_width,
|
||||
dtype=torch.float32,
|
||||
device=self.device,
|
||||
)
|
||||
|
||||
|
||||
class VAEEncoder(BaseModel):
|
||||
def __init__(self, device, max_batch_size, min_batch_size=1):
|
||||
super(VAEEncoder, self).__init__(
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
min_batch_size=min_batch_size,
|
||||
embedding_dim=None,
|
||||
)
|
||||
self.name = "VAE encoder"
|
||||
|
||||
def get_input_names(self):
|
||||
return ["images"]
|
||||
|
||||
def get_output_names(self):
|
||||
return ["latent"]
|
||||
|
||||
def get_dynamic_axes(self):
|
||||
return {
|
||||
"images": {0: "B", 2: "8H", 3: "8W"},
|
||||
"latent": {0: "B", 2: "H", 3: "W"},
|
||||
}
|
||||
|
||||
def get_input_profile(self, batch_size, image_height, image_width, static_batch, static_shape):
|
||||
assert batch_size >= self.min_batch and batch_size <= self.max_batch
|
||||
min_batch = batch_size if static_batch else self.min_batch
|
||||
max_batch = batch_size if static_batch else self.max_batch
|
||||
self.check_dims(batch_size, image_height, image_width)
|
||||
(
|
||||
min_batch,
|
||||
max_batch,
|
||||
min_image_height,
|
||||
max_image_height,
|
||||
min_image_width,
|
||||
max_image_width,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
) = self.get_minmax_dims(batch_size, image_height, image_width, static_batch, static_shape)
|
||||
|
||||
return {
|
||||
"images": [
|
||||
(min_batch, 3, min_image_height, min_image_width),
|
||||
(batch_size, 3, image_height, image_width),
|
||||
(max_batch, 3, max_image_height, max_image_width),
|
||||
],
|
||||
}
|
||||
|
||||
def get_shape_dict(self, batch_size, image_height, image_width):
|
||||
latent_height, latent_width = self.check_dims(batch_size, image_height, image_width)
|
||||
return {
|
||||
"images": (batch_size, 3, image_height, image_width),
|
||||
"latent": (batch_size, 4, latent_height, latent_width),
|
||||
}
|
||||
|
||||
def get_sample_input(self, batch_size, image_height, image_width):
|
||||
self.check_dims(batch_size, image_height, image_width)
|
||||
return torch.randn(
|
||||
batch_size,
|
||||
3,
|
||||
image_height,
|
||||
image_width,
|
||||
dtype=torch.float32,
|
||||
device=self.device,
|
||||
)
|
||||
@@ -0,0 +1,441 @@
|
||||
#! fork: https://github.com/NVIDIA/TensorRT/blob/main/demo/Diffusion/utilities.py
|
||||
|
||||
#
|
||||
# Copyright 2022 The HuggingFace Inc. team.
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import gc
|
||||
from collections import OrderedDict
|
||||
from typing import *
|
||||
|
||||
import numpy as np
|
||||
import onnx
|
||||
import onnx_graphsurgeon as gs
|
||||
import tensorrt as trt
|
||||
import torch
|
||||
from cuda import cudart
|
||||
from PIL import Image
|
||||
from polygraphy import cuda
|
||||
from polygraphy.backend.common import bytes_from_path
|
||||
from polygraphy.backend.trt import (
|
||||
CreateConfig,
|
||||
Profile,
|
||||
engine_from_bytes,
|
||||
engine_from_network,
|
||||
network_from_onnx_path,
|
||||
save_engine,
|
||||
)
|
||||
from polygraphy.backend.trt import util as trt_util
|
||||
|
||||
from .models import CLIP, VAE, BaseModel, UNet, VAEEncoder
|
||||
|
||||
|
||||
TRT_LOGGER = trt.Logger(trt.Logger.ERROR)
|
||||
|
||||
# Map of numpy dtype -> torch dtype
|
||||
numpy_to_torch_dtype_dict = {
|
||||
np.uint8: torch.uint8,
|
||||
np.int8: torch.int8,
|
||||
np.int16: torch.int16,
|
||||
np.int32: torch.int32,
|
||||
np.int64: torch.int64,
|
||||
np.float16: torch.float16,
|
||||
np.float32: torch.float32,
|
||||
np.float64: torch.float64,
|
||||
np.complex64: torch.complex64,
|
||||
np.complex128: torch.complex128,
|
||||
}
|
||||
if np.version.full_version >= "1.24.0":
|
||||
numpy_to_torch_dtype_dict[np.bool_] = torch.bool
|
||||
else:
|
||||
numpy_to_torch_dtype_dict[np.bool] = torch.bool
|
||||
|
||||
# Map of torch dtype -> numpy dtype
|
||||
torch_to_numpy_dtype_dict = {value: key for (key, value) in numpy_to_torch_dtype_dict.items()}
|
||||
|
||||
|
||||
def CUASSERT(cuda_ret):
|
||||
err = cuda_ret[0]
|
||||
if err != cudart.cudaError_t.cudaSuccess:
|
||||
raise RuntimeError(
|
||||
f"CUDA ERROR: {err}, error code reference: https://nvidia.github.io/cuda-python/module/cudart.html#cuda.cudart.cudaError_t"
|
||||
)
|
||||
if len(cuda_ret) > 1:
|
||||
return cuda_ret[1]
|
||||
return None
|
||||
|
||||
|
||||
class Engine:
|
||||
def __init__(
|
||||
self,
|
||||
engine_path,
|
||||
):
|
||||
self.engine_path = engine_path
|
||||
self.engine = None
|
||||
self.context = None
|
||||
self.buffers = OrderedDict()
|
||||
self.tensors = OrderedDict()
|
||||
self.cuda_graph_instance = None # cuda graph
|
||||
|
||||
def __del__(self):
|
||||
[buf.free() for buf in self.buffers.values() if isinstance(buf, cuda.DeviceArray)]
|
||||
del self.engine
|
||||
del self.context
|
||||
del self.buffers
|
||||
del self.tensors
|
||||
|
||||
def refit(self, onnx_path, onnx_refit_path):
|
||||
def convert_int64(arr):
|
||||
# TODO: smarter conversion
|
||||
if len(arr.shape) == 0:
|
||||
return np.int32(arr)
|
||||
return arr
|
||||
|
||||
def add_to_map(refit_dict, name, values):
|
||||
if name in refit_dict:
|
||||
assert refit_dict[name] is None
|
||||
if values.dtype == np.int64:
|
||||
values = convert_int64(values)
|
||||
refit_dict[name] = values
|
||||
|
||||
print(f"Refitting TensorRT engine with {onnx_refit_path} weights")
|
||||
refit_nodes = gs.import_onnx(onnx.load(onnx_refit_path)).toposort().nodes
|
||||
|
||||
# Construct mapping from weight names in refit model -> original model
|
||||
name_map = {}
|
||||
for n, node in enumerate(gs.import_onnx(onnx.load(onnx_path)).toposort().nodes):
|
||||
refit_node = refit_nodes[n]
|
||||
assert node.op == refit_node.op
|
||||
# Constant nodes in ONNX do not have inputs but have a constant output
|
||||
if node.op == "Constant":
|
||||
name_map[refit_node.outputs[0].name] = node.outputs[0].name
|
||||
# Handle scale and bias weights
|
||||
elif node.op == "Conv":
|
||||
if node.inputs[1].__class__ == gs.Constant:
|
||||
name_map[refit_node.name + "_TRTKERNEL"] = node.name + "_TRTKERNEL"
|
||||
if node.inputs[2].__class__ == gs.Constant:
|
||||
name_map[refit_node.name + "_TRTBIAS"] = node.name + "_TRTBIAS"
|
||||
# For all other nodes: find node inputs that are initializers (gs.Constant)
|
||||
else:
|
||||
for i, inp in enumerate(node.inputs):
|
||||
if inp.__class__ == gs.Constant:
|
||||
name_map[refit_node.inputs[i].name] = inp.name
|
||||
|
||||
def map_name(name):
|
||||
if name in name_map:
|
||||
return name_map[name]
|
||||
return name
|
||||
|
||||
# Construct refit dictionary
|
||||
refit_dict = {}
|
||||
refitter = trt.Refitter(self.engine, TRT_LOGGER)
|
||||
all_weights = refitter.get_all()
|
||||
for layer_name, role in zip(all_weights[0], all_weights[1]):
|
||||
# for speciailized roles, use a unique name in the map:
|
||||
if role == trt.WeightsRole.KERNEL:
|
||||
name = layer_name + "_TRTKERNEL"
|
||||
elif role == trt.WeightsRole.BIAS:
|
||||
name = layer_name + "_TRTBIAS"
|
||||
else:
|
||||
name = layer_name
|
||||
|
||||
assert name not in refit_dict, "Found duplicate layer: " + name
|
||||
refit_dict[name] = None
|
||||
|
||||
for n in refit_nodes:
|
||||
# Constant nodes in ONNX do not have inputs but have a constant output
|
||||
if n.op == "Constant":
|
||||
name = map_name(n.outputs[0].name)
|
||||
print(f"Add Constant {name}\n")
|
||||
add_to_map(refit_dict, name, n.outputs[0].values)
|
||||
|
||||
# Handle scale and bias weights
|
||||
elif n.op == "Conv":
|
||||
if n.inputs[1].__class__ == gs.Constant:
|
||||
name = map_name(n.name + "_TRTKERNEL")
|
||||
add_to_map(refit_dict, name, n.inputs[1].values)
|
||||
|
||||
if n.inputs[2].__class__ == gs.Constant:
|
||||
name = map_name(n.name + "_TRTBIAS")
|
||||
add_to_map(refit_dict, name, n.inputs[2].values)
|
||||
|
||||
# For all other nodes: find node inputs that are initializers (AKA gs.Constant)
|
||||
else:
|
||||
for inp in n.inputs:
|
||||
name = map_name(inp.name)
|
||||
if inp.__class__ == gs.Constant:
|
||||
add_to_map(refit_dict, name, inp.values)
|
||||
|
||||
for layer_name, weights_role in zip(all_weights[0], all_weights[1]):
|
||||
if weights_role == trt.WeightsRole.KERNEL:
|
||||
custom_name = layer_name + "_TRTKERNEL"
|
||||
elif weights_role == trt.WeightsRole.BIAS:
|
||||
custom_name = layer_name + "_TRTBIAS"
|
||||
else:
|
||||
custom_name = layer_name
|
||||
|
||||
# Skip refitting Trilu for now; scalar weights of type int64 value 1 - for clip model
|
||||
if layer_name.startswith("onnx::Trilu"):
|
||||
continue
|
||||
|
||||
if refit_dict[custom_name] is not None:
|
||||
refitter.set_weights(layer_name, weights_role, refit_dict[custom_name])
|
||||
else:
|
||||
print(f"[W] No refit weights for layer: {layer_name}")
|
||||
|
||||
if not refitter.refit_cuda_engine():
|
||||
print("Failed to refit!")
|
||||
exit(0)
|
||||
|
||||
def build(
|
||||
self,
|
||||
onnx_path,
|
||||
fp16,
|
||||
input_profile=None,
|
||||
enable_refit=False,
|
||||
enable_all_tactics=False,
|
||||
timing_cache=None,
|
||||
workspace_size=0,
|
||||
):
|
||||
print(f"Building TensorRT engine for {onnx_path}: {self.engine_path}")
|
||||
p = Profile()
|
||||
if input_profile:
|
||||
for name, dims in input_profile.items():
|
||||
assert len(dims) == 3
|
||||
p.add(name, min=dims[0], opt=dims[1], max=dims[2])
|
||||
|
||||
config_kwargs = {}
|
||||
|
||||
if workspace_size > 0:
|
||||
config_kwargs["memory_pool_limits"] = {trt.MemoryPoolType.WORKSPACE: workspace_size}
|
||||
if not enable_all_tactics:
|
||||
config_kwargs["tactic_sources"] = []
|
||||
|
||||
engine = engine_from_network(
|
||||
network_from_onnx_path(onnx_path, flags=[trt.OnnxParserFlag.NATIVE_INSTANCENORM]),
|
||||
config=CreateConfig(
|
||||
fp16=fp16, refittable=enable_refit, profiles=[p], load_timing_cache=timing_cache, **config_kwargs
|
||||
),
|
||||
save_timing_cache=timing_cache,
|
||||
)
|
||||
save_engine(engine, path=self.engine_path)
|
||||
|
||||
def load(self):
|
||||
print(f"Loading TensorRT engine: {self.engine_path}")
|
||||
self.engine = engine_from_bytes(bytes_from_path(self.engine_path))
|
||||
|
||||
def activate(self, reuse_device_memory=None):
|
||||
if reuse_device_memory:
|
||||
self.context = self.engine.create_execution_context_without_device_memory()
|
||||
self.context.device_memory = reuse_device_memory
|
||||
else:
|
||||
self.context = self.engine.create_execution_context()
|
||||
|
||||
def allocate_buffers(self, shape_dict=None, device="cuda"):
|
||||
for idx in range(trt_util.get_bindings_per_profile(self.engine)):
|
||||
binding = self.engine[idx]
|
||||
if shape_dict and binding in shape_dict:
|
||||
shape = shape_dict[binding]
|
||||
else:
|
||||
shape = self.engine.get_binding_shape(binding)
|
||||
dtype = trt.nptype(self.engine.get_binding_dtype(binding))
|
||||
if self.engine.binding_is_input(binding):
|
||||
self.context.set_binding_shape(idx, shape)
|
||||
tensor = torch.empty(tuple(shape), dtype=numpy_to_torch_dtype_dict[dtype]).to(device=device)
|
||||
self.tensors[binding] = tensor
|
||||
|
||||
def infer(self, feed_dict, stream, use_cuda_graph=False):
|
||||
for name, buf in feed_dict.items():
|
||||
self.tensors[name].copy_(buf)
|
||||
|
||||
for name, tensor in self.tensors.items():
|
||||
self.context.set_tensor_address(name, tensor.data_ptr())
|
||||
|
||||
if use_cuda_graph:
|
||||
if self.cuda_graph_instance is not None:
|
||||
CUASSERT(cudart.cudaGraphLaunch(self.cuda_graph_instance, stream.ptr))
|
||||
CUASSERT(cudart.cudaStreamSynchronize(stream.ptr))
|
||||
else:
|
||||
# do inference before CUDA graph capture
|
||||
noerror = self.context.execute_async_v3(stream.ptr)
|
||||
if not noerror:
|
||||
raise ValueError("ERROR: inference failed.")
|
||||
# capture cuda graph
|
||||
CUASSERT(
|
||||
cudart.cudaStreamBeginCapture(stream.ptr, cudart.cudaStreamCaptureMode.cudaStreamCaptureModeGlobal)
|
||||
)
|
||||
self.context.execute_async_v3(stream.ptr)
|
||||
self.graph = CUASSERT(cudart.cudaStreamEndCapture(stream.ptr))
|
||||
self.cuda_graph_instance = CUASSERT(cudart.cudaGraphInstantiate(self.graph, 0))
|
||||
else:
|
||||
noerror = self.context.execute_async_v3(stream.ptr)
|
||||
if not noerror:
|
||||
raise ValueError("ERROR: inference failed.")
|
||||
|
||||
return self.tensors
|
||||
|
||||
|
||||
def decode_images(images: torch.Tensor):
|
||||
images = (
|
||||
((images + 1) * 255 / 2).clamp(0, 255).detach().permute(0, 2, 3, 1).round().type(torch.uint8).cpu().numpy()
|
||||
)
|
||||
return [Image.fromarray(x) for x in images]
|
||||
|
||||
|
||||
def preprocess_image(image: Image.Image):
|
||||
w, h = image.size
|
||||
w, h = map(lambda x: x - x % 32, (w, h)) # resize to integer multiple of 32
|
||||
image = image.resize((w, h))
|
||||
init_image = np.array(image).astype(np.float32) / 255.0
|
||||
init_image = init_image[None].transpose(0, 3, 1, 2)
|
||||
init_image = torch.from_numpy(init_image).contiguous()
|
||||
return 2.0 * init_image - 1.0
|
||||
|
||||
|
||||
def prepare_mask_and_masked_image(image: Image.Image, mask: Image.Image):
|
||||
if isinstance(image, Image.Image):
|
||||
image = np.array(image.convert("RGB"))
|
||||
image = image[None].transpose(0, 3, 1, 2)
|
||||
image = torch.from_numpy(image).to(dtype=torch.float32).contiguous() / 127.5 - 1.0
|
||||
if isinstance(mask, Image.Image):
|
||||
mask = np.array(mask.convert("L"))
|
||||
mask = mask.astype(np.float32) / 255.0
|
||||
mask = mask[None, None]
|
||||
mask[mask < 0.5] = 0
|
||||
mask[mask >= 0.5] = 1
|
||||
mask = torch.from_numpy(mask).to(dtype=torch.float32).contiguous()
|
||||
|
||||
masked_image = image * (mask < 0.5)
|
||||
|
||||
return mask, masked_image
|
||||
|
||||
|
||||
def create_models(
|
||||
model_id: str,
|
||||
use_auth_token: Optional[str],
|
||||
device: Union[str, torch.device],
|
||||
max_batch_size: int,
|
||||
unet_in_channels: int = 4,
|
||||
embedding_dim: int = 768,
|
||||
):
|
||||
models = {
|
||||
"clip": CLIP(
|
||||
hf_token=use_auth_token,
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
embedding_dim=embedding_dim,
|
||||
),
|
||||
"unet": UNet(
|
||||
hf_token=use_auth_token,
|
||||
fp16=True,
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
embedding_dim=embedding_dim,
|
||||
unet_dim=unet_in_channels,
|
||||
),
|
||||
"vae": VAE(
|
||||
hf_token=use_auth_token,
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
embedding_dim=embedding_dim,
|
||||
),
|
||||
"vae_encoder": VAEEncoder(
|
||||
hf_token=use_auth_token,
|
||||
device=device,
|
||||
max_batch_size=max_batch_size,
|
||||
embedding_dim=embedding_dim,
|
||||
),
|
||||
}
|
||||
return models
|
||||
|
||||
|
||||
def build_engine(
|
||||
engine_path: str,
|
||||
onnx_opt_path: str,
|
||||
model_data: BaseModel,
|
||||
opt_image_height: int,
|
||||
opt_image_width: int,
|
||||
opt_batch_size: int,
|
||||
build_static_batch: bool = False,
|
||||
build_dynamic_shape: bool = False,
|
||||
build_all_tactics: bool = False,
|
||||
build_enable_refit: bool = False,
|
||||
):
|
||||
_, free_mem, _ = cudart.cudaMemGetInfo()
|
||||
GiB = 2**30
|
||||
if free_mem > 6 * GiB:
|
||||
activation_carveout = 4 * GiB
|
||||
max_workspace_size = free_mem - activation_carveout
|
||||
else:
|
||||
max_workspace_size = 0
|
||||
engine = Engine(engine_path)
|
||||
input_profile = model_data.get_input_profile(
|
||||
opt_batch_size,
|
||||
opt_image_height,
|
||||
opt_image_width,
|
||||
static_batch=build_static_batch,
|
||||
static_shape=not build_dynamic_shape,
|
||||
)
|
||||
engine.build(
|
||||
onnx_opt_path,
|
||||
fp16=True,
|
||||
input_profile=input_profile,
|
||||
enable_refit=build_enable_refit,
|
||||
enable_all_tactics=build_all_tactics,
|
||||
workspace_size=max_workspace_size,
|
||||
)
|
||||
|
||||
return engine
|
||||
|
||||
|
||||
def export_onnx(
|
||||
model,
|
||||
onnx_path: str,
|
||||
model_data: BaseModel,
|
||||
opt_image_height: int,
|
||||
opt_image_width: int,
|
||||
opt_batch_size: int,
|
||||
onnx_opset: int,
|
||||
):
|
||||
with torch.inference_mode(), torch.autocast("cuda"):
|
||||
inputs = model_data.get_sample_input(opt_batch_size, opt_image_height, opt_image_width)
|
||||
torch.onnx.export(
|
||||
model,
|
||||
inputs,
|
||||
onnx_path,
|
||||
export_params=True,
|
||||
opset_version=onnx_opset,
|
||||
do_constant_folding=True,
|
||||
input_names=model_data.get_input_names(),
|
||||
output_names=model_data.get_output_names(),
|
||||
dynamic_axes=model_data.get_dynamic_axes(),
|
||||
)
|
||||
del model
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
|
||||
def optimize_onnx(
|
||||
onnx_path: str,
|
||||
onnx_opt_path: str,
|
||||
model_data: BaseModel,
|
||||
):
|
||||
onnx_opt_graph = model_data.optimize(onnx.load(onnx_path))
|
||||
onnx.save(onnx_opt_graph, onnx_opt_path)
|
||||
del onnx_opt_graph
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
@@ -0,0 +1,45 @@
|
||||
from typing import Optional
|
||||
import random
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
class SimilarImageFilter:
|
||||
def __init__(self, threshold: float = 0.98, max_skip_frame: float = 10) -> None:
|
||||
self.threshold = threshold
|
||||
self.prev_tensor = None
|
||||
self.cos = torch.nn.CosineSimilarity(dim=0, eps=1e-6)
|
||||
self.max_skip_frame = max_skip_frame
|
||||
self.skip_count = 0
|
||||
|
||||
def __call__(self, x: torch.Tensor) -> Optional[torch.Tensor]:
|
||||
if self.prev_tensor is None:
|
||||
self.prev_tensor = x.detach().clone()
|
||||
return x
|
||||
else:
|
||||
cos_sim = self.cos(self.prev_tensor.reshape(-1), x.reshape(-1)).item()
|
||||
sample = random.uniform(0, 1)
|
||||
if self.threshold >= 1:
|
||||
skip_prob = 0
|
||||
else:
|
||||
skip_prob = max(0, 1 - (1 - cos_sim) / (1 - self.threshold))
|
||||
|
||||
# not skip frame
|
||||
if skip_prob < sample:
|
||||
self.prev_tensor = x.detach().clone()
|
||||
return x
|
||||
# skip frame
|
||||
else:
|
||||
if self.skip_count > self.max_skip_frame:
|
||||
self.skip_count = 0
|
||||
self.prev_tensor = x.detach().clone()
|
||||
return x
|
||||
else:
|
||||
self.skip_count += 1
|
||||
return None
|
||||
|
||||
def set_threshold(self, threshold: float) -> None:
|
||||
self.threshold = threshold
|
||||
|
||||
def set_max_skip_frame(self, max_skip_frame: float) -> None:
|
||||
self.max_skip_frame = max_skip_frame
|
||||
@@ -0,0 +1,98 @@
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
import PIL.Image
|
||||
import torch
|
||||
import torchvision
|
||||
|
||||
|
||||
def denormalize(images: Union[torch.Tensor, np.ndarray]) -> torch.Tensor:
|
||||
"""
|
||||
Denormalize an image array to [0,1].
|
||||
"""
|
||||
return (images / 2 + 0.5).clamp(0, 1)
|
||||
|
||||
|
||||
def pt_to_numpy(images: torch.Tensor) -> np.ndarray:
|
||||
"""
|
||||
Convert a PyTorch tensor to a NumPy image.
|
||||
"""
|
||||
images = images.cpu().permute(0, 2, 3, 1).float().numpy()
|
||||
return images
|
||||
|
||||
|
||||
def numpy_to_pil(images: np.ndarray) -> PIL.Image.Image:
|
||||
"""
|
||||
Convert a NumPy image or a batch of images to a PIL image.
|
||||
"""
|
||||
if images.ndim == 3:
|
||||
images = images[None, ...]
|
||||
images = (images * 255).round().astype("uint8")
|
||||
if images.shape[-1] == 1:
|
||||
# special case for grayscale (single channel) images
|
||||
pil_images = [
|
||||
PIL.Image.fromarray(image.squeeze(), mode="L") for image in images
|
||||
]
|
||||
else:
|
||||
pil_images = [PIL.Image.fromarray(image) for image in images]
|
||||
|
||||
return pil_images
|
||||
|
||||
|
||||
def postprocess_image(
|
||||
image: torch.Tensor,
|
||||
output_type: str = "pil",
|
||||
do_denormalize: Optional[List[bool]] = None,
|
||||
) -> Union[torch.Tensor, np.ndarray, PIL.Image.Image]:
|
||||
if not isinstance(image, torch.Tensor):
|
||||
raise ValueError(
|
||||
f"Input for postprocessing is in incorrect format: {type(image)}. We only support pytorch tensor"
|
||||
)
|
||||
|
||||
if output_type == "latent":
|
||||
return image
|
||||
|
||||
do_normalize_flg = True
|
||||
if do_denormalize is None:
|
||||
do_denormalize = [do_normalize_flg] * image.shape[0]
|
||||
|
||||
image = torch.stack(
|
||||
[
|
||||
denormalize(image[i]) if do_denormalize[i] else image[i]
|
||||
for i in range(image.shape[0])
|
||||
]
|
||||
)
|
||||
|
||||
if output_type == "pt":
|
||||
return image
|
||||
|
||||
image = pt_to_numpy(image)
|
||||
|
||||
if output_type == "np":
|
||||
return image
|
||||
|
||||
if output_type == "pil":
|
||||
return numpy_to_pil(image)
|
||||
|
||||
|
||||
def process_image(
|
||||
image_pil: PIL.Image.Image, range: Tuple[int, int] = (-1, 1)
|
||||
) -> Tuple[torch.Tensor, PIL.Image.Image]:
|
||||
image = torchvision.transforms.ToTensor()(image_pil)
|
||||
r_min, r_max = range[0], range[1]
|
||||
image = image * (r_max - r_min) + r_min
|
||||
return image[None, ...], image_pil
|
||||
|
||||
|
||||
def pil2tensor(image_pil: PIL.Image.Image) -> torch.Tensor:
|
||||
height = image_pil.height
|
||||
width = image_pil.width
|
||||
imgs = []
|
||||
img, _ = process_image(image_pil)
|
||||
imgs.append(img)
|
||||
imgs = torch.vstack(imgs)
|
||||
images = torch.nn.functional.interpolate(
|
||||
imgs, size=(height, width), mode="bilinear"
|
||||
)
|
||||
image_tensors = images.to(torch.float16)
|
||||
return image_tensors
|
||||
@@ -0,0 +1,52 @@
|
||||
import importlib
|
||||
import importlib.util
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Dict, Optional
|
||||
|
||||
from packaging.version import Version
|
||||
|
||||
|
||||
python = sys.executable
|
||||
index_url = os.environ.get("INDEX_URL", "")
|
||||
|
||||
|
||||
def version(package: str) -> Optional[Version]:
|
||||
try:
|
||||
return Version(importlib.import_module(package).__version__)
|
||||
except ModuleNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
def is_installed(package: str) -> bool:
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
|
||||
return spec is not None
|
||||
|
||||
|
||||
def run_python(command: str, env: Dict[str, str] = None) -> str:
|
||||
run_kwargs = {
|
||||
"args": f"{python} {command}",
|
||||
"shell": True,
|
||||
"env": os.environ if env is None else env,
|
||||
"encoding": "utf8",
|
||||
"errors": "ignore",
|
||||
}
|
||||
|
||||
print(run_kwargs["args"])
|
||||
|
||||
result = subprocess.run(**run_kwargs)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"Error running command: {command}", file=sys.stderr)
|
||||
raise RuntimeError(f"Error running command: {command}")
|
||||
|
||||
return result.stdout or ""
|
||||
|
||||
|
||||
def run_pip(command: str, env: Dict[str, str] = None) -> str:
|
||||
return run_python(f"-m pip {command}", env)
|
||||
@@ -0,0 +1,575 @@
|
||||
import time
|
||||
from typing import List, Optional, Union, Any, Dict, Tuple, Literal
|
||||
|
||||
import numpy as np
|
||||
import PIL.Image
|
||||
import torch
|
||||
from diffusers import LCMScheduler, StableDiffusionPipeline
|
||||
from diffusers.image_processor import VaeImageProcessor
|
||||
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img import (
|
||||
retrieve_latents,
|
||||
)
|
||||
|
||||
from .image_filter import SimilarImageFilter
|
||||
from .image_utils import postprocess_image
|
||||
|
||||
|
||||
class StreamDiffusion:
|
||||
def __init__(
|
||||
self,
|
||||
pipe: StableDiffusionPipeline,
|
||||
t_index_list: List[int],
|
||||
torch_dtype: torch.dtype = torch.float16,
|
||||
width: int = 512,
|
||||
height: int = 512,
|
||||
do_add_noise: bool = True,
|
||||
use_denoising_batch: bool = True,
|
||||
frame_buffer_size: int = 1,
|
||||
cfg_type: Literal["none", "full", "self", "initialize"] = "self",
|
||||
) -> None:
|
||||
self.device = pipe.device
|
||||
self.dtype = torch_dtype
|
||||
self.generator = None
|
||||
|
||||
self.height = height
|
||||
self.width = width
|
||||
|
||||
self.latent_height = int(height // pipe.vae_scale_factor)
|
||||
self.latent_width = int(width // pipe.vae_scale_factor)
|
||||
|
||||
self.frame_bff_size = frame_buffer_size
|
||||
self.denoising_steps_num = len(t_index_list)
|
||||
|
||||
self.cfg_type = cfg_type
|
||||
|
||||
if use_denoising_batch:
|
||||
self.batch_size = self.denoising_steps_num * frame_buffer_size
|
||||
if self.cfg_type == "initialize":
|
||||
self.trt_unet_batch_size = (
|
||||
self.denoising_steps_num + 1
|
||||
) * self.frame_bff_size
|
||||
elif self.cfg_type == "full":
|
||||
self.trt_unet_batch_size = (
|
||||
2 * self.denoising_steps_num * self.frame_bff_size
|
||||
)
|
||||
else:
|
||||
self.trt_unet_batch_size = self.denoising_steps_num * frame_buffer_size
|
||||
else:
|
||||
self.trt_unet_batch_size = self.frame_bff_size
|
||||
self.batch_size = frame_buffer_size
|
||||
|
||||
self.t_list = t_index_list
|
||||
|
||||
self.do_add_noise = do_add_noise
|
||||
self.use_denoising_batch = use_denoising_batch
|
||||
|
||||
self.similar_image_filter = False
|
||||
self.similar_filter = SimilarImageFilter()
|
||||
self.prev_image_result = None
|
||||
|
||||
self.pipe = pipe
|
||||
self.image_processor = VaeImageProcessor(pipe.vae_scale_factor)
|
||||
|
||||
self.scheduler = LCMScheduler.from_config(self.pipe.scheduler.config)
|
||||
self.text_encoder = pipe.text_encoder
|
||||
self.unet = pipe.unet
|
||||
self.vae = pipe.vae
|
||||
|
||||
self.inference_time_ema = 0
|
||||
|
||||
def set_sampler_param(self,
|
||||
t_index_list: List[int],
|
||||
width: int = 512,
|
||||
height: int = 512,
|
||||
do_add_noise: bool = True,
|
||||
use_denoising_batch: bool = True,
|
||||
frame_buffer_size: int = 1,
|
||||
cfg_type: Literal["none", "full", "self", "initialize"] = "self",):
|
||||
self.height = height
|
||||
self.width = width
|
||||
|
||||
self.latent_height = int(height // self.pipe.vae_scale_factor)
|
||||
self.latent_width = int(width // self.pipe.vae_scale_factor)
|
||||
|
||||
self.frame_bff_size = frame_buffer_size
|
||||
|
||||
self.cfg_type = cfg_type
|
||||
self.t_list = t_index_list
|
||||
|
||||
self.do_add_noise = do_add_noise
|
||||
self.use_denoising_batch = use_denoising_batch
|
||||
|
||||
self.inference_time_ema = 0
|
||||
|
||||
self.denoising_steps_num = len(self.t_list)
|
||||
if self.use_denoising_batch:
|
||||
self.batch_size = self.denoising_steps_num * self.frame_bff_size
|
||||
if self.cfg_type == "initialize":
|
||||
self.trt_unet_batch_size = (
|
||||
self.denoising_steps_num + 1
|
||||
) * self.frame_bff_size
|
||||
elif self.cfg_type == "full":
|
||||
self.trt_unet_batch_size = (
|
||||
2 * self.denoising_steps_num * self.frame_bff_size
|
||||
)
|
||||
else:
|
||||
self.trt_unet_batch_size = self.denoising_steps_num * self.frame_bff_size
|
||||
else:
|
||||
self.trt_unet_batch_size = self.frame_bff_size
|
||||
self.batch_size = self.frame_bff_size
|
||||
|
||||
def load_lcm_lora(
|
||||
self,
|
||||
pretrained_model_name_or_path_or_dict: Union[
|
||||
str, Dict[str, torch.Tensor]
|
||||
] = "latent-consistency/lcm-lora-sdv1-5",
|
||||
adapter_name: Optional[Any] = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.pipe.load_lora_weights(
|
||||
pretrained_model_name_or_path_or_dict, adapter_name, **kwargs
|
||||
)
|
||||
|
||||
def load_lora(
|
||||
self,
|
||||
pretrained_lora_model_name_or_path_or_dict: Union[str, Dict[str, torch.Tensor]],
|
||||
adapter_name: Optional[Any] = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.pipe.load_lora_weights(
|
||||
pretrained_lora_model_name_or_path_or_dict, adapter_name, **kwargs
|
||||
)
|
||||
|
||||
def fuse_lora(
|
||||
self,
|
||||
fuse_unet: bool = True,
|
||||
fuse_text_encoder: bool = True,
|
||||
lora_scale: float = 1.0,
|
||||
safe_fusing: bool = False,
|
||||
) -> None:
|
||||
self.pipe.fuse_lora(
|
||||
fuse_unet=fuse_unet,
|
||||
fuse_text_encoder=fuse_text_encoder,
|
||||
lora_scale=lora_scale,
|
||||
safe_fusing=safe_fusing,
|
||||
)
|
||||
|
||||
def enable_similar_image_filter(self, threshold: float = 0.98, max_skip_frame: float = 10) -> None:
|
||||
self.similar_image_filter = True
|
||||
self.similar_filter.set_threshold(threshold)
|
||||
self.similar_filter.set_max_skip_frame(max_skip_frame)
|
||||
|
||||
def disable_similar_image_filter(self) -> None:
|
||||
self.similar_image_filter = False
|
||||
|
||||
@torch.no_grad()
|
||||
def prepare(
|
||||
self,
|
||||
prompt: str,
|
||||
negative_prompt: str = "",
|
||||
num_inference_steps: int = 50,
|
||||
guidance_scale: float = 1.2,
|
||||
delta: float = 1.0,
|
||||
generator: Optional[torch.Generator] = torch.Generator(),
|
||||
seed: int = 2,
|
||||
) -> None:
|
||||
self.generator = generator
|
||||
self.generator.manual_seed(seed)
|
||||
# initialize x_t_latent (it can be any random tensor)
|
||||
if self.denoising_steps_num > 1:
|
||||
self.x_t_latent_buffer = torch.zeros(
|
||||
(
|
||||
(self.denoising_steps_num - 1) * self.frame_bff_size,
|
||||
4,
|
||||
self.latent_height,
|
||||
self.latent_width,
|
||||
),
|
||||
dtype=self.dtype,
|
||||
device=self.device,
|
||||
)
|
||||
else:
|
||||
self.x_t_latent_buffer = None
|
||||
|
||||
if self.cfg_type == "none":
|
||||
self.guidance_scale = 1.0
|
||||
else:
|
||||
self.guidance_scale = guidance_scale
|
||||
self.delta = delta
|
||||
|
||||
do_classifier_free_guidance = False
|
||||
if self.guidance_scale > 1.0:
|
||||
do_classifier_free_guidance = True
|
||||
|
||||
encoder_output = self.pipe.encode_prompt(
|
||||
prompt=prompt,
|
||||
device=self.device,
|
||||
num_images_per_prompt=1,
|
||||
do_classifier_free_guidance=do_classifier_free_guidance,
|
||||
negative_prompt=negative_prompt,
|
||||
)
|
||||
self.prompt_embeds = encoder_output[0].repeat(self.batch_size, 1, 1)
|
||||
|
||||
if self.use_denoising_batch and self.cfg_type == "full":
|
||||
uncond_prompt_embeds = encoder_output[1].repeat(self.batch_size, 1, 1)
|
||||
elif self.cfg_type == "initialize":
|
||||
uncond_prompt_embeds = encoder_output[1].repeat(self.frame_bff_size, 1, 1)
|
||||
|
||||
if self.guidance_scale > 1.0 and (
|
||||
self.cfg_type == "initialize" or self.cfg_type == "full"
|
||||
):
|
||||
self.prompt_embeds = torch.cat(
|
||||
[uncond_prompt_embeds, self.prompt_embeds], dim=0
|
||||
)
|
||||
|
||||
self.scheduler.set_timesteps(num_inference_steps, self.device)
|
||||
self.timesteps = self.scheduler.timesteps.to(self.device)
|
||||
|
||||
# make sub timesteps list based on the indices in the t_list list and the values in the timesteps list
|
||||
self.sub_timesteps = []
|
||||
for t in self.t_list:
|
||||
self.sub_timesteps.append(self.timesteps[t])
|
||||
|
||||
sub_timesteps_tensor = torch.tensor(
|
||||
self.sub_timesteps, dtype=torch.long, device=self.device
|
||||
)
|
||||
self.sub_timesteps_tensor = torch.repeat_interleave(
|
||||
sub_timesteps_tensor,
|
||||
repeats=self.frame_bff_size if self.use_denoising_batch else 1,
|
||||
dim=0,
|
||||
)
|
||||
|
||||
self.init_noise = torch.randn(
|
||||
(self.batch_size, 4, self.latent_height, self.latent_width),
|
||||
generator=generator,
|
||||
).to(device=self.device, dtype=self.dtype)
|
||||
|
||||
self.stock_noise = torch.zeros_like(self.init_noise)
|
||||
|
||||
c_skip_list = []
|
||||
c_out_list = []
|
||||
for timestep in self.sub_timesteps:
|
||||
c_skip, c_out = self.scheduler.get_scalings_for_boundary_condition_discrete(
|
||||
timestep
|
||||
)
|
||||
c_skip_list.append(c_skip)
|
||||
c_out_list.append(c_out)
|
||||
|
||||
self.c_skip = (
|
||||
torch.stack(c_skip_list)
|
||||
.view(len(self.t_list), 1, 1, 1)
|
||||
.to(dtype=self.dtype, device=self.device)
|
||||
)
|
||||
self.c_out = (
|
||||
torch.stack(c_out_list)
|
||||
.view(len(self.t_list), 1, 1, 1)
|
||||
.to(dtype=self.dtype, device=self.device)
|
||||
)
|
||||
|
||||
alpha_prod_t_sqrt_list = []
|
||||
beta_prod_t_sqrt_list = []
|
||||
for timestep in self.sub_timesteps:
|
||||
alpha_prod_t_sqrt = self.scheduler.alphas_cumprod[timestep].sqrt()
|
||||
beta_prod_t_sqrt = (1 - self.scheduler.alphas_cumprod[timestep]).sqrt()
|
||||
alpha_prod_t_sqrt_list.append(alpha_prod_t_sqrt)
|
||||
beta_prod_t_sqrt_list.append(beta_prod_t_sqrt)
|
||||
alpha_prod_t_sqrt = (
|
||||
torch.stack(alpha_prod_t_sqrt_list)
|
||||
.view(len(self.t_list), 1, 1, 1)
|
||||
.to(dtype=self.dtype, device=self.device)
|
||||
)
|
||||
beta_prod_t_sqrt = (
|
||||
torch.stack(beta_prod_t_sqrt_list)
|
||||
.view(len(self.t_list), 1, 1, 1)
|
||||
.to(dtype=self.dtype, device=self.device)
|
||||
)
|
||||
self.alpha_prod_t_sqrt = torch.repeat_interleave(
|
||||
alpha_prod_t_sqrt,
|
||||
repeats=self.frame_bff_size if self.use_denoising_batch else 1,
|
||||
dim=0,
|
||||
)
|
||||
self.beta_prod_t_sqrt = torch.repeat_interleave(
|
||||
beta_prod_t_sqrt,
|
||||
repeats=self.frame_bff_size if self.use_denoising_batch else 1,
|
||||
dim=0,
|
||||
)
|
||||
|
||||
@torch.no_grad()
|
||||
def update_prompt(self, prompt: str,negative_prompt: Optional[str] = None) -> None:
|
||||
encoder_output = self.pipe.encode_prompt(
|
||||
prompt=prompt,
|
||||
negative_prompt=negative_prompt,
|
||||
device=self.device,
|
||||
num_images_per_prompt=1,
|
||||
do_classifier_free_guidance=False,
|
||||
)
|
||||
self.prompt_embeds = encoder_output[0].repeat(self.batch_size, 1, 1)
|
||||
|
||||
def add_noise(
|
||||
self,
|
||||
original_samples: torch.Tensor,
|
||||
noise: torch.Tensor,
|
||||
t_index: int,
|
||||
) -> torch.Tensor:
|
||||
noisy_samples = (
|
||||
self.alpha_prod_t_sqrt[t_index] * original_samples
|
||||
+ self.beta_prod_t_sqrt[t_index] * noise
|
||||
)
|
||||
return noisy_samples
|
||||
|
||||
def scheduler_step_batch(
|
||||
self,
|
||||
model_pred_batch: torch.Tensor,
|
||||
x_t_latent_batch: torch.Tensor,
|
||||
idx: Optional[int] = None,
|
||||
) -> torch.Tensor:
|
||||
# TODO: use t_list to select beta_prod_t_sqrt
|
||||
if idx is None:
|
||||
F_theta = (
|
||||
x_t_latent_batch - self.beta_prod_t_sqrt * model_pred_batch
|
||||
) / self.alpha_prod_t_sqrt
|
||||
denoised_batch = self.c_out * F_theta + self.c_skip * x_t_latent_batch
|
||||
else:
|
||||
F_theta = (
|
||||
x_t_latent_batch - self.beta_prod_t_sqrt[idx] * model_pred_batch
|
||||
) / self.alpha_prod_t_sqrt[idx]
|
||||
denoised_batch = (
|
||||
self.c_out[idx] * F_theta + self.c_skip[idx] * x_t_latent_batch
|
||||
)
|
||||
|
||||
return denoised_batch
|
||||
|
||||
def unet_step(
|
||||
self,
|
||||
x_t_latent: torch.Tensor,
|
||||
t_list: Union[torch.Tensor, list[int]],
|
||||
idx: Optional[int] = None,
|
||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||
if self.guidance_scale > 1.0 and (self.cfg_type == "initialize"):
|
||||
x_t_latent_plus_uc = torch.concat([x_t_latent[0:1], x_t_latent], dim=0)
|
||||
t_list = torch.concat([t_list[0:1], t_list], dim=0)
|
||||
elif self.guidance_scale > 1.0 and (self.cfg_type == "full"):
|
||||
x_t_latent_plus_uc = torch.concat([x_t_latent, x_t_latent], dim=0)
|
||||
t_list = torch.concat([t_list, t_list], dim=0)
|
||||
else:
|
||||
x_t_latent_plus_uc = x_t_latent
|
||||
|
||||
model_pred = self.unet(
|
||||
x_t_latent_plus_uc,
|
||||
t_list,
|
||||
encoder_hidden_states=self.prompt_embeds,
|
||||
return_dict=False,
|
||||
)[0]
|
||||
|
||||
if self.guidance_scale > 1.0 and (self.cfg_type == "initialize"):
|
||||
noise_pred_text = model_pred[1:]
|
||||
self.stock_noise = torch.concat(
|
||||
[model_pred[0:1], self.stock_noise[1:]], dim=0
|
||||
) # ここコメントアウトでself out cfg
|
||||
elif self.guidance_scale > 1.0 and (self.cfg_type == "full"):
|
||||
noise_pred_uncond, noise_pred_text = model_pred.chunk(2)
|
||||
else:
|
||||
noise_pred_text = model_pred
|
||||
if self.guidance_scale > 1.0 and (
|
||||
self.cfg_type == "self" or self.cfg_type == "initialize"
|
||||
):
|
||||
noise_pred_uncond = self.stock_noise * self.delta
|
||||
if self.guidance_scale > 1.0 and self.cfg_type != "none":
|
||||
model_pred = noise_pred_uncond + self.guidance_scale * (
|
||||
noise_pred_text - noise_pred_uncond
|
||||
)
|
||||
else:
|
||||
model_pred = noise_pred_text
|
||||
|
||||
# compute the previous noisy sample x_t -> x_t-1
|
||||
if self.use_denoising_batch:
|
||||
denoised_batch = self.scheduler_step_batch(model_pred, x_t_latent, idx)
|
||||
if self.cfg_type == "self" or self.cfg_type == "initialize":
|
||||
scaled_noise = self.beta_prod_t_sqrt * self.stock_noise
|
||||
delta_x = self.scheduler_step_batch(model_pred, scaled_noise, idx)
|
||||
alpha_next = torch.concat(
|
||||
[
|
||||
self.alpha_prod_t_sqrt[1:],
|
||||
torch.ones_like(self.alpha_prod_t_sqrt[0:1]),
|
||||
],
|
||||
dim=0,
|
||||
)
|
||||
delta_x = alpha_next * delta_x
|
||||
beta_next = torch.concat(
|
||||
[
|
||||
self.beta_prod_t_sqrt[1:],
|
||||
torch.ones_like(self.beta_prod_t_sqrt[0:1]),
|
||||
],
|
||||
dim=0,
|
||||
)
|
||||
delta_x = delta_x / beta_next
|
||||
init_noise = torch.concat(
|
||||
[self.init_noise[1:], self.init_noise[0:1]], dim=0
|
||||
)
|
||||
self.stock_noise = init_noise + delta_x
|
||||
|
||||
else:
|
||||
# denoised_batch = self.scheduler.step(model_pred, t_list[0], x_t_latent).denoised
|
||||
denoised_batch = self.scheduler_step_batch(model_pred, x_t_latent, idx)
|
||||
|
||||
return denoised_batch, model_pred
|
||||
|
||||
def encode_image(self, image_tensors: torch.Tensor) -> torch.Tensor:
|
||||
image_tensors = image_tensors.to(
|
||||
device=self.device,
|
||||
dtype=self.vae.dtype,
|
||||
)
|
||||
img_latent = retrieve_latents(self.vae.encode(image_tensors), self.generator)
|
||||
img_latent = img_latent * self.vae.config.scaling_factor
|
||||
x_t_latent = self.add_noise(img_latent, self.init_noise[0], 0)
|
||||
return x_t_latent
|
||||
|
||||
def decode_image(self, x_0_pred_out: torch.Tensor) -> torch.Tensor:
|
||||
output_latents = self.vae.decode(
|
||||
x_0_pred_out / self.vae.config.scaling_factor, return_dict=False
|
||||
)
|
||||
output_latent =output_latents[0]
|
||||
return output_latent
|
||||
|
||||
def predict_x0_batch(self, x_t_latent: torch.Tensor) -> torch.Tensor:
|
||||
prev_latent_batch = self.x_t_latent_buffer
|
||||
|
||||
if self.use_denoising_batch:
|
||||
t_list = self.sub_timesteps_tensor
|
||||
if self.denoising_steps_num > 1:
|
||||
x_t_latent = torch.cat((x_t_latent, prev_latent_batch), dim=0)
|
||||
self.stock_noise = torch.cat(
|
||||
(self.init_noise[0:1], self.stock_noise[:-1]), dim=0
|
||||
)
|
||||
x_0_pred_batch, model_pred = self.unet_step(x_t_latent, t_list)
|
||||
|
||||
if self.denoising_steps_num > 1:
|
||||
x_0_pred_out = x_0_pred_batch[-1].unsqueeze(0)
|
||||
if self.do_add_noise:
|
||||
self.x_t_latent_buffer = (
|
||||
self.alpha_prod_t_sqrt[1:] * x_0_pred_batch[:-1]
|
||||
+ self.beta_prod_t_sqrt[1:] * self.init_noise[1:]
|
||||
)
|
||||
else:
|
||||
self.x_t_latent_buffer = (
|
||||
self.alpha_prod_t_sqrt[1:] * x_0_pred_batch[:-1]
|
||||
)
|
||||
else:
|
||||
x_0_pred_out = x_0_pred_batch
|
||||
self.x_t_latent_buffer = None
|
||||
else:
|
||||
self.init_noise = x_t_latent
|
||||
for idx, t in enumerate(self.sub_timesteps_tensor):
|
||||
t = t.view(
|
||||
1,
|
||||
).repeat(
|
||||
self.frame_bff_size,
|
||||
)
|
||||
x_0_pred, model_pred = self.unet_step(x_t_latent, t, idx)
|
||||
if idx < len(self.sub_timesteps_tensor) - 1:
|
||||
if self.do_add_noise:
|
||||
x_t_latent = self.alpha_prod_t_sqrt[
|
||||
idx + 1
|
||||
] * x_0_pred + self.beta_prod_t_sqrt[
|
||||
idx + 1
|
||||
] * torch.randn_like(
|
||||
x_0_pred, device=self.device, dtype=self.dtype
|
||||
)
|
||||
else:
|
||||
x_t_latent = self.alpha_prod_t_sqrt[idx + 1] * x_0_pred
|
||||
x_0_pred_out = x_0_pred
|
||||
|
||||
return x_0_pred_out
|
||||
|
||||
@torch.no_grad()
|
||||
def __call__(
|
||||
self, x: Union[torch.Tensor, PIL.Image.Image, np.ndarray] = None
|
||||
) -> torch.Tensor:
|
||||
start = torch.cuda.Event(enable_timing=True)
|
||||
end = torch.cuda.Event(enable_timing=True)
|
||||
start.record()
|
||||
if x is not None:
|
||||
x = self.image_processor.preprocess(x, self.height, self.width).to(
|
||||
device=self.device, dtype=self.dtype
|
||||
)
|
||||
if self.similar_image_filter:
|
||||
x = self.similar_filter(x)
|
||||
if x is None:
|
||||
time.sleep(self.inference_time_ema)
|
||||
return self.prev_image_result
|
||||
x_t_latent = self.encode_image(x)
|
||||
else:
|
||||
# TODO: check the dimension of x_t_latent
|
||||
x_t_latent = torch.randn((1, 4, self.latent_height, self.latent_width)).to(
|
||||
device=self.device, dtype=self.dtype
|
||||
)
|
||||
x_0_pred_out = self.predict_x0_batch(x_t_latent)
|
||||
x_output = self.decode_image(x_0_pred_out).detach().clone()
|
||||
|
||||
self.prev_image_result = x_output
|
||||
end.record()
|
||||
torch.cuda.synchronize()
|
||||
inference_time = start.elapsed_time(end) / 1000
|
||||
self.inference_time_ema = 0.9 * self.inference_time_ema + 0.1 * inference_time
|
||||
return x_output
|
||||
|
||||
@torch.no_grad()
|
||||
def sample(
|
||||
self, x: Union[torch.Tensor, PIL.Image.Image, np.ndarray] = None
|
||||
) -> torch.Tensor:
|
||||
start = torch.cuda.Event(enable_timing=True)
|
||||
end = torch.cuda.Event(enable_timing=True)
|
||||
start.record()
|
||||
if x is not None:
|
||||
x = self.image_processor.preprocess(x, self.height, self.width).to(
|
||||
device=self.device, dtype=self.dtype
|
||||
)
|
||||
if self.similar_image_filter:
|
||||
x = self.similar_filter(x)
|
||||
if x is None:
|
||||
time.sleep(self.inference_time_ema)
|
||||
return self.prev_image_result
|
||||
x_t_latent = self.encode_image(x)
|
||||
b,c,h,w=x_t_latent.shape
|
||||
|
||||
# x_t_latent=x_t_latent.repeat((2, 1,1,1))
|
||||
else:
|
||||
# TODO: check the dimension of x_t_latent
|
||||
x_t_latent = torch.randn((self.frame_bff_size, 4, self.latent_height, self.latent_width)).to(
|
||||
device=self.device, dtype=self.dtype
|
||||
)
|
||||
x_0_pred_out = self.predict_x0_batch(x_t_latent)
|
||||
x_output = self.decode_image(x_0_pred_out).detach().clone()
|
||||
self.prev_image_result = x_output
|
||||
end.record()
|
||||
torch.cuda.synchronize()
|
||||
inference_time = start.elapsed_time(end) / 1000
|
||||
self.inference_time_ema = 0.9 * self.inference_time_ema + 0.1 * inference_time
|
||||
return x_output
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def txt2img(self, batch_size: int = 1) -> torch.Tensor:
|
||||
x_0_pred_out = self.predict_x0_batch(
|
||||
torch.randn((batch_size, 4, self.latent_height, self.latent_width)).to(
|
||||
device=self.device, dtype=self.dtype
|
||||
)
|
||||
)
|
||||
x_output = self.decode_image(x_0_pred_out).detach().clone()
|
||||
return x_output
|
||||
|
||||
def txt2img_sd_turbo(self, batch_size: int = 1) -> torch.Tensor:
|
||||
x_t_latent = torch.randn(
|
||||
(batch_size, 4, self.latent_height, self.latent_width),
|
||||
device=self.device,
|
||||
dtype=self.dtype,
|
||||
)
|
||||
model_pred = self.unet(
|
||||
x_t_latent,
|
||||
self.sub_timesteps_tensor,
|
||||
encoder_hidden_states=self.prompt_embeds,
|
||||
return_dict=False,
|
||||
)[0]
|
||||
x_0_pred_out = (
|
||||
x_t_latent - self.beta_prod_t_sqrt * model_pred
|
||||
) / self.alpha_prod_t_sqrt
|
||||
return self.decode_image(x_0_pred_out)
|
||||
@@ -0,0 +1,49 @@
|
||||
from typing import Literal, Optional
|
||||
|
||||
import fire
|
||||
from packaging.version import Version
|
||||
|
||||
from ..pip_utils import is_installed, run_pip, version
|
||||
|
||||
|
||||
def get_cuda_version_from_torch() -> Optional[Literal["11", "12"]]:
|
||||
try:
|
||||
import torch
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
return torch.version.cuda.split(".")[0]
|
||||
|
||||
|
||||
def install(cu: Optional[Literal["11", "12"]] = get_cuda_version_from_torch()):
|
||||
if cu is None or cu not in ["11", "12"]:
|
||||
print("Could not detect CUDA version. Please specify manually.")
|
||||
return
|
||||
print("Installing TensorRT requirements...")
|
||||
|
||||
if is_installed("tensorrt"):
|
||||
if version("tensorrt") < Version("9.0.0"):
|
||||
run_pip("uninstall -y tensorrt")
|
||||
|
||||
cudnn_name = f"nvidia-cudnn-cu{cu}==8.9.4.25"
|
||||
|
||||
if not is_installed("tensorrt"):
|
||||
run_pip(f"install {cudnn_name} --no-cache-dir")
|
||||
run_pip(
|
||||
"install --pre --extra-index-url https://pypi.nvidia.com tensorrt==9.0.1.post11.dev4 --no-cache-dir"
|
||||
)
|
||||
|
||||
if not is_installed("polygraphy"):
|
||||
run_pip(
|
||||
"install polygraphy==0.47.1 --extra-index-url https://pypi.ngc.nvidia.com"
|
||||
)
|
||||
if not is_installed("onnx_graphsurgeon"):
|
||||
run_pip(
|
||||
"install onnx-graphsurgeon==0.3.26 --extra-index-url https://pypi.ngc.nvidia.com"
|
||||
)
|
||||
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(install)
|
||||
@@ -0,0 +1,773 @@
|
||||
import gc
|
||||
import os
|
||||
from pathlib import Path
|
||||
import traceback
|
||||
from typing import List, Literal, Optional, Union, Dict
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from diffusers import AutoencoderTiny, StableDiffusionPipeline
|
||||
from PIL import Image
|
||||
|
||||
from .pipeline import StreamDiffusion
|
||||
from .image_utils import postprocess_image
|
||||
|
||||
|
||||
torch.set_grad_enabled(False)
|
||||
torch.backends.cuda.matmul.allow_tf32 = True
|
||||
torch.backends.cudnn.allow_tf32 = True
|
||||
|
||||
|
||||
class StreamDiffusionWrapper:
|
||||
def __init__(
|
||||
self,
|
||||
model_id_or_path: str,
|
||||
t_index_list: List[int],
|
||||
lora_dict: Optional[Dict[str, float]] = None,
|
||||
mode: Literal["img2img", "txt2img"] = "img2img",
|
||||
output_type: Literal["pil", "pt", "np", "latent"] = "pil",
|
||||
lcm_lora_id: Optional[str] = None,
|
||||
vae_id: Optional[str] = None,
|
||||
device: Literal["cpu", "cuda"] = "cuda",
|
||||
dtype: torch.dtype = torch.float16,
|
||||
frame_buffer_size: int = 1,
|
||||
width: int = 512,
|
||||
height: int = 512,
|
||||
warmup: int = 10,
|
||||
acceleration: Literal["none", "xformers", "tensorrt"] = "tensorrt",
|
||||
do_add_noise: bool = True,
|
||||
device_ids: Optional[List[int]] = None,
|
||||
use_lcm_lora: bool = True,
|
||||
use_tiny_vae: bool = True,
|
||||
enable_similar_image_filter: bool = False,
|
||||
similar_image_filter_threshold: float = 0.98,
|
||||
similar_image_filter_max_skip_frame: int = 10,
|
||||
use_denoising_batch: bool = True,
|
||||
cfg_type: Literal["none", "full", "self", "initialize"] = "self",
|
||||
seed: int = 2,
|
||||
use_safety_checker: bool = False,
|
||||
):
|
||||
"""
|
||||
Initializes the StreamDiffusionWrapper.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model_id_or_path : str
|
||||
The model id or path to load.
|
||||
t_index_list : List[int]
|
||||
The t_index_list to use for inference.
|
||||
lora_dict : Optional[Dict[str, float]], optional
|
||||
The lora_dict to load, by default None.
|
||||
Keys are the LoRA names and values are the LoRA scales.
|
||||
Example: {'LoRA_1' : 0.5 , 'LoRA_2' : 0.7 ,...}
|
||||
mode : Literal["img2img", "txt2img"], optional
|
||||
txt2img or img2img, by default "img2img".
|
||||
output_type : Literal["pil", "pt", "np", "latent"], optional
|
||||
The output type of image, by default "pil".
|
||||
lcm_lora_id : Optional[str], optional
|
||||
The lcm_lora_id to load, by default None.
|
||||
If None, the default LCM-LoRA
|
||||
("latent-consistency/lcm-lora-sdv1-5") will be used.
|
||||
vae_id : Optional[str], optional
|
||||
The vae_id to load, by default None.
|
||||
If None, the default TinyVAE
|
||||
("madebyollin/taesd") will be used.
|
||||
device : Literal["cpu", "cuda"], optional
|
||||
The device to use for inference, by default "cuda".
|
||||
dtype : torch.dtype, optional
|
||||
The dtype for inference, by default torch.float16.
|
||||
frame_buffer_size : int, optional
|
||||
The frame buffer size for denoising batch, by default 1.
|
||||
width : int, optional
|
||||
The width of the image, by default 512.
|
||||
height : int, optional
|
||||
The height of the image, by default 512.
|
||||
warmup : int, optional
|
||||
The number of warmup steps to perform, by default 10.
|
||||
acceleration : Literal["none", "xformers", "tensorrt"], optional
|
||||
The acceleration method, by default "tensorrt".
|
||||
do_add_noise : bool, optional
|
||||
Whether to add noise for following denoising steps or not,
|
||||
by default True.
|
||||
device_ids : Optional[List[int]], optional
|
||||
The device ids to use for DataParallel, by default None.
|
||||
use_lcm_lora : bool, optional
|
||||
Whether to use LCM-LoRA or not, by default True.
|
||||
use_tiny_vae : bool, optional
|
||||
Whether to use TinyVAE or not, by default True.
|
||||
enable_similar_image_filter : bool, optional
|
||||
Whether to enable similar image filter or not,
|
||||
by default False.
|
||||
similar_image_filter_threshold : float, optional
|
||||
The threshold for similar image filter, by default 0.98.
|
||||
similar_image_filter_max_skip_frame : int, optional
|
||||
The max skip frame for similar image filter, by default 10.
|
||||
use_denoising_batch : bool, optional
|
||||
Whether to use denoising batch or not, by default True.
|
||||
cfg_type : Literal["none", "full", "self", "initialize"],
|
||||
optional
|
||||
The cfg_type for img2img mode, by default "self".
|
||||
You cannot use anything other than "none" for txt2img mode.
|
||||
seed : int, optional
|
||||
The seed, by default 2.
|
||||
use_safety_checker : bool, optional
|
||||
Whether to use safety checker or not, by default False.
|
||||
"""
|
||||
self.sd_turbo = "turbo" in model_id_or_path
|
||||
|
||||
if mode == "txt2img":
|
||||
if cfg_type != "none":
|
||||
raise ValueError(
|
||||
f"txt2img mode accepts only cfg_type = 'none', but got {cfg_type}"
|
||||
)
|
||||
if use_denoising_batch and frame_buffer_size > 1:
|
||||
if not self.sd_turbo:
|
||||
raise ValueError(
|
||||
"txt2img mode cannot use denoising batch with frame_buffer_size > 1."
|
||||
)
|
||||
|
||||
if mode == "img2img":
|
||||
if not use_denoising_batch:
|
||||
raise NotImplementedError(
|
||||
"img2img mode must use denoising batch for now."
|
||||
)
|
||||
|
||||
self.device = device
|
||||
self.dtype = dtype
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.mode = mode
|
||||
self.output_type = output_type
|
||||
self.frame_buffer_size = frame_buffer_size
|
||||
self.batch_size = (
|
||||
len(t_index_list) * frame_buffer_size
|
||||
if use_denoising_batch
|
||||
else frame_buffer_size
|
||||
)
|
||||
self.t_index_list =t_index_list
|
||||
self.cfg_type =cfg_type
|
||||
self.use_denoising_batch = use_denoising_batch
|
||||
self.use_safety_checker = use_safety_checker
|
||||
self.do_add_noise =do_add_noise
|
||||
self.seed=seed
|
||||
|
||||
self.stream: StreamDiffusion = self._load_model(
|
||||
model_id_or_path=model_id_or_path,
|
||||
lora_dict=lora_dict,
|
||||
lcm_lora_id=lcm_lora_id,
|
||||
vae_id=vae_id,
|
||||
t_index_list=t_index_list,
|
||||
acceleration=acceleration,
|
||||
warmup=warmup,
|
||||
do_add_noise=do_add_noise,
|
||||
use_lcm_lora=use_lcm_lora,
|
||||
use_tiny_vae=use_tiny_vae,
|
||||
cfg_type=cfg_type,
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
if device_ids is not None:
|
||||
self.stream.unet = torch.nn.DataParallel(
|
||||
self.stream.unet, device_ids=device_ids
|
||||
)
|
||||
|
||||
if enable_similar_image_filter:
|
||||
self.stream.enable_similar_image_filter(similar_image_filter_threshold, similar_image_filter_max_skip_frame)
|
||||
|
||||
def prepare(
|
||||
self,
|
||||
prompt: str,
|
||||
negative_prompt: str = "",
|
||||
num_inference_steps: int = 50,
|
||||
guidance_scale: float = 1.2,
|
||||
delta: float = 1.0,
|
||||
t_index_list: List[int]=[16,32,45],
|
||||
do_add_noise: bool = True,
|
||||
enable_similar_image_filter: bool = False,
|
||||
similar_image_filter_threshold: float = 0.98,
|
||||
similar_image_filter_max_skip_frame: int = 10,
|
||||
use_denoising_batch: bool = True,
|
||||
cfg_type: Literal["none", "full", "self", "initialize"] = "self",
|
||||
seed: int = 2,
|
||||
frame_buffer_size:int=1,
|
||||
use_safety_checker: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Prepares the model for inference.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
prompt : str
|
||||
The prompt to generate images from.
|
||||
num_inference_steps : int, optional
|
||||
The number of inference steps to perform, by default 50.
|
||||
guidance_scale : float, optional
|
||||
The guidance scale to use, by default 1.2.
|
||||
delta : float, optional
|
||||
The delta multiplier of virtual residual noise,
|
||||
by default 1.0.
|
||||
"""
|
||||
# self.stream.prepare(
|
||||
# prompt,
|
||||
# negative_prompt,
|
||||
# num_inference_steps=num_inference_steps,
|
||||
# guidance_scale=guidance_scale,
|
||||
# delta=delta,
|
||||
# )
|
||||
self.prompt =prompt
|
||||
self.negative_prompt=negative_prompt
|
||||
self.num_inference_steps=num_inference_steps
|
||||
self.guidance_scale=guidance_scale
|
||||
self.delta=delta
|
||||
|
||||
self.frame_buffer_size = frame_buffer_size
|
||||
self.batch_size = (
|
||||
len(t_index_list) * frame_buffer_size
|
||||
if use_denoising_batch
|
||||
else frame_buffer_size
|
||||
)
|
||||
self.t_index_list =t_index_list
|
||||
self.cfg_type =cfg_type
|
||||
self.use_denoising_batch = use_denoising_batch
|
||||
self.use_safety_checker = use_safety_checker
|
||||
self.do_add_noise =do_add_noise
|
||||
self.seed=seed
|
||||
|
||||
if enable_similar_image_filter:
|
||||
self.stream.enable_similar_image_filter(similar_image_filter_threshold, similar_image_filter_max_skip_frame)
|
||||
else:
|
||||
self.stream.disable_similar_image_filter()
|
||||
|
||||
if self.use_safety_checker:
|
||||
if self.safety_checker==None or self.feature_extractor==None:
|
||||
from transformers import CLIPFeatureExtractor
|
||||
from diffusers.pipelines.stable_diffusion.safety_checker import (
|
||||
StableDiffusionSafetyChecker,
|
||||
)
|
||||
|
||||
self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
||||
"CompVis/stable-diffusion-safety-checker"
|
||||
).to(self.stream.device)
|
||||
self.feature_extractor = CLIPFeatureExtractor.from_pretrained(
|
||||
"openai/clip-vit-base-patch32"
|
||||
)
|
||||
self.nsfw_fallback_img = Image.new("RGB", (512, 512), (0, 0, 0))
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
image: Optional[Union[str, Image.Image, torch.Tensor]] = None,
|
||||
prompt: Optional[str] = None,
|
||||
) -> Union[Image.Image, List[Image.Image]]:
|
||||
"""
|
||||
Performs img2img or txt2img based on the mode.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : Optional[Union[str, Image.Image, torch.Tensor]]
|
||||
The image to generate from.
|
||||
prompt : Optional[str]
|
||||
The prompt to generate images from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[Image.Image, List[Image.Image]]
|
||||
The generated image.
|
||||
"""
|
||||
if self.mode == "img2img":
|
||||
return self.img2img(image)
|
||||
else:
|
||||
return self.txt2img(prompt)
|
||||
|
||||
def sample(self, image: Optional[Union[str, Image.Image, torch.Tensor]] = None,
|
||||
prompt: Optional[str] = None,negative_prompt: Optional[str] = None)-> List[Image.Image]:
|
||||
|
||||
use_denoising_batch=self.use_denoising_batch
|
||||
if not image == None:
|
||||
#图生图
|
||||
if isinstance(image, str) or isinstance(image, Image.Image):
|
||||
image = self.preprocess_image(image)
|
||||
|
||||
use_denoising_batch = True
|
||||
self.stream.set_sampler_param(t_index_list=self.t_index_list,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
do_add_noise=self.do_add_noise,
|
||||
use_denoising_batch=use_denoising_batch,
|
||||
frame_buffer_size=self.frame_buffer_size,
|
||||
cfg_type=self.cfg_type)
|
||||
else:
|
||||
#文生图
|
||||
if self.frame_buffer_size >1 and self.use_denoising_batch:
|
||||
use_denoising_batch = False
|
||||
self.stream.set_sampler_param(t_index_list=self.t_index_list,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
do_add_noise=self.do_add_noise,
|
||||
use_denoising_batch=use_denoising_batch,
|
||||
frame_buffer_size=self.frame_buffer_size,
|
||||
cfg_type='none')
|
||||
|
||||
self.stream.prepare(
|
||||
prompt=self.prompt,
|
||||
negative_prompt=self.negative_prompt,
|
||||
num_inference_steps=self.num_inference_steps,
|
||||
guidance_scale=self.guidance_scale,
|
||||
delta=self.delta,
|
||||
seed=self.seed,
|
||||
)
|
||||
|
||||
if prompt is not None:
|
||||
self.stream.update_prompt(prompt,negative_prompt)
|
||||
|
||||
self.batch_size = (
|
||||
len(self.t_index_list) * self.frame_buffer_size
|
||||
if use_denoising_batch
|
||||
else self.frame_buffer_size
|
||||
)
|
||||
|
||||
if self.frame_buffer_size==1:
|
||||
for _ in range(self.batch_size):
|
||||
self.stream.sample(image)
|
||||
|
||||
image_tensor = self.stream.sample(image)
|
||||
image = postprocess_image(image_tensor.cpu(), output_type=self.output_type)
|
||||
|
||||
if self.use_safety_checker:
|
||||
safety_checker_input = self.feature_extractor(
|
||||
image, return_tensors="pt"
|
||||
).to(self.device)
|
||||
_, has_nsfw_concept = self.safety_checker(
|
||||
images=image_tensor.to(self.dtype),
|
||||
clip_input=safety_checker_input.pixel_values.to(self.dtype),
|
||||
)
|
||||
image = self.nsfw_fallback_img if has_nsfw_concept[0] else image
|
||||
return image
|
||||
|
||||
def txt2img(
|
||||
self, prompt: Optional[str] = None
|
||||
) -> Union[Image.Image, List[Image.Image], torch.Tensor, np.ndarray]:
|
||||
"""
|
||||
Performs txt2img.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
prompt : Optional[str]
|
||||
The prompt to generate images from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[Image.Image, List[Image.Image]]
|
||||
The generated image.
|
||||
"""
|
||||
if prompt is not None:
|
||||
self.stream.update_prompt(prompt)
|
||||
|
||||
if self.sd_turbo:
|
||||
image_tensor = self.stream.txt2img_sd_turbo(self.batch_size)
|
||||
else:
|
||||
image_tensor = self.stream.txt2img(self.frame_buffer_size)
|
||||
image = self.postprocess_image(image_tensor, output_type=self.output_type)
|
||||
|
||||
if self.use_safety_checker:
|
||||
safety_checker_input = self.feature_extractor(
|
||||
image, return_tensors="pt"
|
||||
).to(self.device)
|
||||
_, has_nsfw_concept = self.safety_checker(
|
||||
images=image_tensor.to(self.dtype),
|
||||
clip_input=safety_checker_input.pixel_values.to(self.dtype),
|
||||
)
|
||||
image = self.nsfw_fallback_img if has_nsfw_concept[0] else image
|
||||
|
||||
return image
|
||||
|
||||
def img2img(
|
||||
self, image: Union[str, Image.Image, torch.Tensor]
|
||||
) -> Union[Image.Image, List[Image.Image], torch.Tensor, np.ndarray]:
|
||||
"""
|
||||
Performs img2img.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : Union[str, Image.Image, torch.Tensor]
|
||||
The image to generate from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Image.Image
|
||||
The generated image.
|
||||
"""
|
||||
if isinstance(image, str) or isinstance(image, Image.Image):
|
||||
image = self.preprocess_image(image)
|
||||
|
||||
image_tensor = self.stream(image)
|
||||
image = self.postprocess_image(image_tensor, output_type=self.output_type)
|
||||
|
||||
if self.use_safety_checker:
|
||||
safety_checker_input = self.feature_extractor(
|
||||
image, return_tensors="pt"
|
||||
).to(self.device)
|
||||
_, has_nsfw_concept = self.safety_checker(
|
||||
images=image_tensor.to(self.dtype),
|
||||
clip_input=safety_checker_input.pixel_values.to(self.dtype),
|
||||
)
|
||||
image = self.nsfw_fallback_img if has_nsfw_concept[0] else image
|
||||
|
||||
return image
|
||||
|
||||
def preprocess_image(self, image: Union[str, Image.Image]) -> torch.Tensor:
|
||||
"""
|
||||
Preprocesses the image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : Union[str, Image.Image, torch.Tensor]
|
||||
The image to preprocess.
|
||||
|
||||
Returns
|
||||
-------
|
||||
torch.Tensor
|
||||
The preprocessed image.
|
||||
"""
|
||||
if isinstance(image, str):
|
||||
image = Image.open(image).convert("RGB").resize((self.width, self.height))
|
||||
if isinstance(image, Image.Image):
|
||||
image = image.convert("RGB").resize((self.width, self.height))
|
||||
|
||||
return self.stream.image_processor.preprocess(
|
||||
image, self.height, self.width
|
||||
).to(device=self.device, dtype=self.dtype)
|
||||
|
||||
def postprocess_image(
|
||||
self, image_tensor: torch.Tensor, output_type: str = "pil"
|
||||
) -> Union[Image.Image, List[Image.Image], torch.Tensor, np.ndarray]:
|
||||
"""
|
||||
Postprocesses the image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image_tensor : torch.Tensor
|
||||
The image tensor to postprocess.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[Image.Image, List[Image.Image]]
|
||||
The postprocessed image.
|
||||
"""
|
||||
if self.frame_buffer_size > 1:
|
||||
return postprocess_image(image_tensor.cpu(), output_type=output_type)
|
||||
else:
|
||||
return postprocess_image(image_tensor.cpu(), output_type=output_type)[0]
|
||||
|
||||
def _load_model(
|
||||
self,
|
||||
model_id_or_path: str,
|
||||
t_index_list: List[int],
|
||||
lora_dict: Optional[Dict[str, float]] = None,
|
||||
lcm_lora_id: Optional[str] = None,
|
||||
vae_id: Optional[str] = None,
|
||||
acceleration: Literal["none", "xformers", "tensorrt"] = "tensorrt",
|
||||
warmup: int = 10,
|
||||
do_add_noise: bool = True,
|
||||
use_lcm_lora: bool = True,
|
||||
use_tiny_vae: bool = True,
|
||||
cfg_type: Literal["none", "full", "self", "initialize"] = "self",
|
||||
seed: int = 2,
|
||||
) -> StreamDiffusion:
|
||||
"""
|
||||
Loads the model.
|
||||
|
||||
This method does the following:
|
||||
|
||||
1. Loads the model from the model_id_or_path.
|
||||
2. Loads and fuses the LCM-LoRA model from the lcm_lora_id if needed.
|
||||
3. Loads the VAE model from the vae_id if needed.
|
||||
4. Enables acceleration if needed.
|
||||
5. Prepares the model for inference.
|
||||
6. Load the safety checker if needed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model_id_or_path : str
|
||||
The model id or path to load.
|
||||
t_index_list : List[int]
|
||||
The t_index_list to use for inference.
|
||||
lora_dict : Optional[Dict[str, float]], optional
|
||||
The lora_dict to load, by default None.
|
||||
Keys are the LoRA names and values are the LoRA scales.
|
||||
Example: {'LoRA_1' : 0.5 , 'LoRA_2' : 0.7 ,...}
|
||||
lcm_lora_id : Optional[str], optional
|
||||
The lcm_lora_id to load, by default None.
|
||||
vae_id : Optional[str], optional
|
||||
The vae_id to load, by default None.
|
||||
acceleration : Literal["none", "xfomers", "sfast", "tensorrt"], optional
|
||||
The acceleration method, by default "tensorrt".
|
||||
warmup : int, optional
|
||||
The number of warmup steps to perform, by default 10.
|
||||
do_add_noise : bool, optional
|
||||
Whether to add noise for following denoising steps or not,
|
||||
by default True.
|
||||
use_lcm_lora : bool, optional
|
||||
Whether to use LCM-LoRA or not, by default True.
|
||||
use_tiny_vae : bool, optional
|
||||
Whether to use TinyVAE or not, by default True.
|
||||
cfg_type : Literal["none", "full", "self", "initialize"],
|
||||
optional
|
||||
The cfg_type for img2img mode, by default "self".
|
||||
You cannot use anything other than "none" for txt2img mode.
|
||||
seed : int, optional
|
||||
The seed, by default 2.
|
||||
|
||||
Returns
|
||||
-------
|
||||
StreamDiffusion
|
||||
The loaded model.
|
||||
"""
|
||||
|
||||
try: # Load from local directory
|
||||
pipe: StableDiffusionPipeline = StableDiffusionPipeline.from_pretrained(
|
||||
model_id_or_path,
|
||||
).to(device=self.device, dtype=self.dtype)
|
||||
|
||||
except ValueError: # Load from huggingface
|
||||
pipe: StableDiffusionPipeline = StableDiffusionPipeline.from_single_file(
|
||||
model_id_or_path,
|
||||
).to(device=self.device, dtype=self.dtype)
|
||||
except Exception: # No model found
|
||||
traceback.print_exc()
|
||||
print("Model load has failed. Doesn't exist.")
|
||||
exit()
|
||||
|
||||
stream = StreamDiffusion(
|
||||
pipe=pipe,
|
||||
t_index_list=t_index_list,
|
||||
torch_dtype=self.dtype,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
do_add_noise=do_add_noise,
|
||||
frame_buffer_size=self.frame_buffer_size,
|
||||
use_denoising_batch=self.use_denoising_batch,
|
||||
cfg_type=cfg_type,
|
||||
)
|
||||
if not self.sd_turbo:
|
||||
if use_lcm_lora:
|
||||
if lcm_lora_id is not None:
|
||||
stream.load_lcm_lora(
|
||||
pretrained_model_name_or_path_or_dict=lcm_lora_id
|
||||
)
|
||||
else:
|
||||
stream.load_lcm_lora()
|
||||
stream.fuse_lora()
|
||||
|
||||
if lora_dict is not None:
|
||||
for lora_name, lora_scale in lora_dict.items():
|
||||
stream.load_lora(lora_name)
|
||||
stream.fuse_lora(lora_scale=lora_scale)
|
||||
print(f"Use LoRA: {lora_name} in weights {lora_scale}")
|
||||
|
||||
if use_tiny_vae:
|
||||
if vae_id is not None:
|
||||
stream.vae = AutoencoderTiny.from_pretrained(vae_id).to(
|
||||
device=pipe.device, dtype=pipe.dtype
|
||||
)
|
||||
else:
|
||||
stream.vae = AutoencoderTiny.from_pretrained("madebyollin/taesd").to(
|
||||
device=pipe.device, dtype=pipe.dtype
|
||||
)
|
||||
|
||||
try:
|
||||
if acceleration == "xformers":
|
||||
stream.pipe.enable_xformers_memory_efficient_attention()
|
||||
if acceleration == "tensorrt":
|
||||
from polygraphy import cuda
|
||||
from streamdiffusion.acceleration.tensorrt import (
|
||||
TorchVAEEncoder,
|
||||
compile_unet,
|
||||
compile_vae_decoder,
|
||||
compile_vae_encoder,
|
||||
)
|
||||
from streamdiffusion.acceleration.tensorrt.engine import (
|
||||
AutoencoderKLEngine,
|
||||
UNet2DConditionModelEngine,
|
||||
)
|
||||
from streamdiffusion.acceleration.tensorrt.models import (
|
||||
VAE,
|
||||
UNet,
|
||||
VAEEncoder,
|
||||
)
|
||||
|
||||
def create_prefix(
|
||||
model_id_or_path: str,
|
||||
max_batch_size: int,
|
||||
min_batch_size: int,
|
||||
):
|
||||
maybe_path = Path(model_id_or_path)
|
||||
if maybe_path.exists():
|
||||
return f"{maybe_path.stem}--lcm_lora-{use_lcm_lora}--tiny_vae-{use_tiny_vae}--max_batch-{max_batch_size}--min_batch-{min_batch_size}--mode-{self.mode}"
|
||||
else:
|
||||
return f"{model_id_or_path}--lcm_lora-{use_lcm_lora}--tiny_vae-{use_tiny_vae}--max_batch-{max_batch_size}--min_batch-{min_batch_size}--mode-{self.mode}"
|
||||
|
||||
engine_dir = os.path.join("engines")
|
||||
unet_path = os.path.join(
|
||||
engine_dir,
|
||||
create_prefix(
|
||||
model_id_or_path=model_id_or_path,
|
||||
max_batch_size=stream.trt_unet_batch_size,
|
||||
min_batch_size=stream.trt_unet_batch_size,
|
||||
),
|
||||
"unet.engine",
|
||||
)
|
||||
vae_encoder_path = os.path.join(
|
||||
engine_dir,
|
||||
create_prefix(
|
||||
model_id_or_path=model_id_or_path,
|
||||
max_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
min_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
),
|
||||
"vae_encoder.engine",
|
||||
)
|
||||
vae_decoder_path = os.path.join(
|
||||
engine_dir,
|
||||
create_prefix(
|
||||
model_id_or_path=model_id_or_path,
|
||||
max_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
min_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
),
|
||||
"vae_decoder.engine",
|
||||
)
|
||||
|
||||
if not os.path.exists(unet_path):
|
||||
os.makedirs(os.path.dirname(unet_path), exist_ok=True)
|
||||
unet_model = UNet(
|
||||
fp16=True,
|
||||
device=stream.device,
|
||||
max_batch_size=stream.trt_unet_batch_size,
|
||||
min_batch_size=stream.trt_unet_batch_size,
|
||||
embedding_dim=stream.text_encoder.config.hidden_size,
|
||||
unet_dim=stream.unet.config.in_channels,
|
||||
)
|
||||
compile_unet(
|
||||
stream.unet,
|
||||
unet_model,
|
||||
unet_path + ".onnx",
|
||||
unet_path + ".opt.onnx",
|
||||
unet_path,
|
||||
opt_batch_size=stream.trt_unet_batch_size,
|
||||
)
|
||||
|
||||
if not os.path.exists(vae_decoder_path):
|
||||
os.makedirs(os.path.dirname(vae_decoder_path), exist_ok=True)
|
||||
stream.vae.forward = stream.vae.decode
|
||||
vae_decoder_model = VAE(
|
||||
device=stream.device,
|
||||
max_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
min_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
)
|
||||
compile_vae_decoder(
|
||||
stream.vae,
|
||||
vae_decoder_model,
|
||||
vae_decoder_path + ".onnx",
|
||||
vae_decoder_path + ".opt.onnx",
|
||||
vae_decoder_path,
|
||||
opt_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
)
|
||||
delattr(stream.vae, "forward")
|
||||
|
||||
if not os.path.exists(vae_encoder_path):
|
||||
os.makedirs(os.path.dirname(vae_encoder_path), exist_ok=True)
|
||||
vae_encoder = TorchVAEEncoder(stream.vae).to(torch.device("cuda"))
|
||||
vae_encoder_model = VAEEncoder(
|
||||
device=stream.device,
|
||||
max_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
min_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
)
|
||||
compile_vae_encoder(
|
||||
vae_encoder,
|
||||
vae_encoder_model,
|
||||
vae_encoder_path + ".onnx",
|
||||
vae_encoder_path + ".opt.onnx",
|
||||
vae_encoder_path,
|
||||
opt_batch_size=self.batch_size
|
||||
if self.mode == "txt2img"
|
||||
else stream.frame_bff_size,
|
||||
)
|
||||
|
||||
cuda_steram = cuda.Stream()
|
||||
|
||||
vae_config = stream.vae.config
|
||||
vae_dtype = stream.vae.dtype
|
||||
|
||||
stream.unet = UNet2DConditionModelEngine(
|
||||
unet_path, cuda_steram, use_cuda_graph=False
|
||||
)
|
||||
stream.vae = AutoencoderKLEngine(
|
||||
vae_encoder_path,
|
||||
vae_decoder_path,
|
||||
cuda_steram,
|
||||
stream.pipe.vae_scale_factor,
|
||||
use_cuda_graph=False,
|
||||
)
|
||||
setattr(stream.vae, "config", vae_config)
|
||||
setattr(stream.vae, "dtype", vae_dtype)
|
||||
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
print("TensorRT acceleration enabled.")
|
||||
if acceleration == "sfast":
|
||||
from streamdiffusion.acceleration.sfast import (
|
||||
accelerate_with_stable_fast,
|
||||
)
|
||||
|
||||
stream = accelerate_with_stable_fast(stream)
|
||||
print("StableFast acceleration enabled.")
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
print("Acceleration has failed. Falling back to normal mode.")
|
||||
|
||||
if seed < 0: # Random seed
|
||||
seed = np.random.randint(0, 1000000)
|
||||
|
||||
stream.prepare(
|
||||
"",
|
||||
"",
|
||||
num_inference_steps=50,
|
||||
guidance_scale=1.1
|
||||
if stream.cfg_type in ["full", "self", "initialize"]
|
||||
else 1.0,
|
||||
generator=torch.manual_seed(seed),
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
if self.use_safety_checker:
|
||||
from transformers import CLIPFeatureExtractor
|
||||
from diffusers.pipelines.stable_diffusion.safety_checker import (
|
||||
StableDiffusionSafetyChecker,
|
||||
)
|
||||
|
||||
self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
||||
"CompVis/stable-diffusion-safety-checker"
|
||||
).to(pipe.device)
|
||||
self.feature_extractor = CLIPFeatureExtractor.from_pretrained(
|
||||
"openai/clip-vit-base-patch32"
|
||||
)
|
||||
self.nsfw_fallback_img = Image.new("RGB", (512, 512), (0, 0, 0))
|
||||
|
||||
return stream
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 582 KiB |
Reference in New Issue
Block a user