Instructions to use CatSnowdrop/universal_onnxruntime with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- TensorRT
How to use CatSnowdrop/universal_onnxruntime with TensorRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Download onnxruntime/transformers/models/stable_diffusion/demo_utils.py from CatSnowdrop/universal_onnxruntime: direct link, hf CLI and curl.
- Browser
- Download file 28.6 kB
-
https://huggingface.co/CatSnowdrop/universal_onnxruntime/resolve/main/onnxruntime/transformers/models/stable_diffusion/demo_utils.py
- Command line
-
hf download hf://CatSnowdrop/universal_onnxruntime/onnxruntime/transformers/models/stable_diffusion/demo_utils.py
-
curl -L -o demo_utils.py https://huggingface.co/CatSnowdrop/universal_onnxruntime/resolve/main/onnxruntime/transformers/models/stable_diffusion/demo_utils.py
28.6 kB
| # ------------------------------------------------------------------------- | |
| # Copyright (c) Microsoft Corporation. All rights reserved. | |
| # Licensed under the MIT License. | |
| # -------------------------------------------------------------------------- | |
| # Modified from TensorRT demo diffusion, which has the following license: | |
| # | |
| # 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 argparse | |
| import os | |
| import sys | |
| from importlib.metadata import PackageNotFoundError, version | |
| from typing import Any | |
| import controlnet_aux | |
| import cv2 | |
| import numpy as np | |
| import torch | |
| from cuda import cudart | |
| from diffusion_models import PipelineInfo | |
| from engine_builder import EngineType, get_engine_paths, get_engine_type | |
| from PIL import Image | |
| from pipeline_stable_diffusion import StableDiffusionPipeline | |
| class RawTextArgumentDefaultsHelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawTextHelpFormatter): | |
| pass | |
| def arg_parser(description: str): | |
| return argparse.ArgumentParser( | |
| description=description, | |
| formatter_class=RawTextArgumentDefaultsHelpFormatter, | |
| ) | |
| def set_default_arguments(args): | |
| # set default value for some arguments if not provided | |
| if args.height is None: | |
| args.height = PipelineInfo.default_resolution(args.version) | |
| if args.width is None: | |
| args.width = PipelineInfo.default_resolution(args.version) | |
| is_lcm = (args.version == "xl-1.0" and args.lcm) or "lcm" in args.lora_weights | |
| is_turbo = args.version in ["sd-turbo", "xl-turbo"] | |
| if args.denoising_steps is None: | |
| args.denoising_steps = 4 if is_turbo else 8 if is_lcm else (30 if args.version == "xl-1.0" else 50) | |
| if args.scheduler is None: | |
| args.scheduler = "LCM" if (is_lcm or is_turbo) else ("EulerA" if args.version == "xl-1.0" else "DDIM") | |
| if args.guidance is None: | |
| args.guidance = 0.0 if (is_lcm or is_turbo) else (5.0 if args.version == "xl-1.0" else 7.5) | |
| def parse_arguments(is_xl: bool, parser): | |
| engines = ["ORT_CUDA", "ORT_TRT", "TRT", "TORCH"] | |
| parser.add_argument( | |
| "-e", | |
| "--engine", | |
| type=str, | |
| default=engines[0], | |
| choices=engines, | |
| help="Backend engine in {engines}. " | |
| "ORT_CUDA is CUDA execution provider; ORT_TRT is Tensorrt execution provider; TRT is TensorRT", | |
| ) | |
| supported_versions = PipelineInfo.supported_versions(is_xl) | |
| parser.add_argument( | |
| "-v", | |
| "--version", | |
| type=str, | |
| default="xl-1.0" if is_xl else "1.5", | |
| choices=supported_versions, | |
| help="Version of Stable Diffusion" + (" XL." if is_xl else "."), | |
| ) | |
| parser.add_argument( | |
| "-y", | |
| "--height", | |
| type=int, | |
| default=None, | |
| help="Height of image to generate (must be multiple of 8).", | |
| ) | |
| parser.add_argument( | |
| "-x", "--width", type=int, default=None, help="Height of image to generate (must be multiple of 8)." | |
| ) | |
| parser.add_argument( | |
| "-s", | |
| "--scheduler", | |
| type=str, | |
| default=None, | |
| choices=["DDIM", "EulerA", "UniPC", "LCM"], | |
| help="Scheduler for diffusion process" + " of base" if is_xl else "", | |
| ) | |
| parser.add_argument( | |
| "-wd", | |
| "--work-dir", | |
| default=".", | |
| help="Root Directory to store torch or ONNX models, built engines and output images etc.", | |
| ) | |
| parser.add_argument( | |
| "-i", | |
| "--engine-dir", | |
| default=None, | |
| help="Root Directory to store built engines or optimized ONNX models etc.", | |
| ) | |
| parser.add_argument("prompt", nargs="*", default=[""], help="Text prompt(s) to guide image generation.") | |
| parser.add_argument( | |
| "-n", | |
| "--negative-prompt", | |
| nargs="*", | |
| default=[""], | |
| help="Optional negative prompt(s) to guide the image generation.", | |
| ) | |
| parser.add_argument( | |
| "-b", | |
| "--batch-size", | |
| type=int, | |
| default=1, | |
| choices=[1, 2, 4, 8, 16], | |
| help="Number of times to repeat the prompt (batch size multiplier).", | |
| ) | |
| parser.add_argument( | |
| "-d", | |
| "--denoising-steps", | |
| type=int, | |
| default=None, | |
| help="Number of denoising steps" + (" in base." if is_xl else "."), | |
| ) | |
| parser.add_argument( | |
| "-g", | |
| "--guidance", | |
| type=float, | |
| default=None, | |
| help="Higher guidance scale encourages to generate images that are closely linked to the text prompt.", | |
| ) | |
| parser.add_argument( | |
| "-ls", "--lora-scale", type=float, default=1, help="Scale of LoRA weights, default 1 (must between 0 and 1)" | |
| ) | |
| parser.add_argument("-lw", "--lora-weights", type=str, default="", help="LoRA weights to apply in the base model") | |
| if is_xl: | |
| parser.add_argument( | |
| "--lcm", | |
| action="store_true", | |
| help="Use fine-tuned latent consistency model to replace the UNet in base.", | |
| ) | |
| parser.add_argument( | |
| "-rs", | |
| "--refiner-scheduler", | |
| type=str, | |
| default="EulerA", | |
| choices=["DDIM", "EulerA", "UniPC"], | |
| help="Scheduler for diffusion process of refiner.", | |
| ) | |
| parser.add_argument( | |
| "-rg", | |
| "--refiner-guidance", | |
| type=float, | |
| default=5.0, | |
| help="Guidance scale used in refiner.", | |
| ) | |
| parser.add_argument( | |
| "-rd", | |
| "--refiner-denoising-steps", | |
| type=int, | |
| default=30, | |
| help="Number of denoising steps in refiner. Note that actual steps is refiner_denoising_steps * strength.", | |
| ) | |
| parser.add_argument( | |
| "--strength", | |
| type=float, | |
| default=0.3, | |
| help="A value between 0 and 1. The higher the value less the final image similar to the seed image.", | |
| ) | |
| parser.add_argument( | |
| "-r", | |
| "--enable-refiner", | |
| action="store_true", | |
| help="Enable SDXL refiner to refine image from base pipeline.", | |
| ) | |
| # ONNX export | |
| parser.add_argument( | |
| "--onnx-opset", | |
| type=int, | |
| default=None, | |
| choices=range(14, 18), | |
| help="Select ONNX opset version to target for exported models.", | |
| ) | |
| # Engine build options. | |
| parser.add_argument( | |
| "-db", | |
| "--build-dynamic-batch", | |
| action="store_true", | |
| help="Build TensorRT engines to support dynamic batch size.", | |
| ) | |
| parser.add_argument( | |
| "-ds", | |
| "--build-dynamic-shape", | |
| action="store_true", | |
| help="Build TensorRT engines to support dynamic image sizes.", | |
| ) | |
| parser.add_argument("--max-batch-size", type=int, default=None, choices=[1, 2, 4, 8, 16, 32], help="Max batch size") | |
| # Inference related options | |
| parser.add_argument( | |
| "-nw", "--num-warmup-runs", type=int, default=5, help="Number of warmup runs before benchmarking performance." | |
| ) | |
| parser.add_argument("--nvtx-profile", action="store_true", help="Enable NVTX markers for performance profiling.") | |
| parser.add_argument("--seed", type=int, default=None, help="Seed for random generator to get consistent results.") | |
| parser.add_argument("--deterministic", action="store_true", help="use deterministic algorithms.") | |
| parser.add_argument("-dc", "--disable-cuda-graph", action="store_true", help="Disable cuda graph.") | |
| parser.add_argument("--framework-model-dir", default=None, help="framework model directory") | |
| group = parser.add_argument_group("Options for ORT_CUDA engine only") | |
| group.add_argument("--enable-vae-slicing", action="store_true", help="True will feed only one image to VAE once.") | |
| group.add_argument("--max-cuda-graphs", type=int, default=1, help="Max number of cuda graphs to use. Default 1.") | |
| group.add_argument("--user-compute-stream", action="store_true", help="Use user compute stream.") | |
| # TensorRT only options | |
| group = parser.add_argument_group("Options for TensorRT (--engine=TRT) only") | |
| group.add_argument( | |
| "--build-all-tactics", action="store_true", help="Build TensorRT engines using all tactic sources." | |
| ) | |
| args = parser.parse_args() | |
| set_default_arguments(args) | |
| # Validate image dimensions | |
| if args.height % 64 != 0 or args.width % 64 != 0: | |
| raise ValueError( | |
| f"Image height and width have to be divisible by 64 but specified as: {args.height} and {args.width}." | |
| ) | |
| if (args.build_dynamic_batch or args.build_dynamic_shape) and not args.disable_cuda_graph: | |
| print("[I] CUDA Graph is disabled since dynamic input shape is configured.") | |
| args.disable_cuda_graph = True | |
| if args.onnx_opset is None: | |
| args.onnx_opset = 14 if args.engine == "ORT_CUDA" else 17 | |
| if is_xl: | |
| if args.version == "xl-turbo": | |
| if args.lcm: | |
| print("[I] sdxl-turbo cannot use with LCM.") | |
| args.lcm = False | |
| assert args.strength > 0.0 and args.strength < 1.0 | |
| assert not (args.lcm and args.lora_weights), "it is not supported to use both lcm unet and Lora together" | |
| if args.scheduler == "LCM": | |
| if args.guidance > 2.0: | |
| print("[I] Use --guidance=0.0 (no more than 2.0) when LCM scheduler is used.") | |
| args.guidance = 0.0 | |
| if args.denoising_steps > 16: | |
| print("[I] Use --denoising_steps=8 (no more than 16) when LCM scheduler is used.") | |
| args.denoising_steps = 8 | |
| print(args) | |
| return args | |
| def max_batch(args): | |
| if args.max_batch_size: | |
| max_batch_size = args.max_batch_size | |
| else: | |
| do_classifier_free_guidance = args.guidance > 1.0 | |
| batch_multiplier = 2 if do_classifier_free_guidance else 1 | |
| max_batch_size = 32 // batch_multiplier | |
| if args.engine != "ORT_CUDA" and (args.build_dynamic_shape or args.height > 512 or args.width > 512): | |
| max_batch_size = 8 // batch_multiplier | |
| return max_batch_size | |
| def get_metadata(args, is_xl: bool = False) -> dict[str, Any]: | |
| metadata = { | |
| "command": " ".join(['"' + x + '"' if " " in x else x for x in sys.argv]), | |
| "args.prompt": args.prompt, | |
| "args.negative_prompt": args.negative_prompt, | |
| "args.batch_size": args.batch_size, | |
| "height": args.height, | |
| "width": args.width, | |
| "cuda_graph": not args.disable_cuda_graph, | |
| "vae_slicing": args.enable_vae_slicing, | |
| "engine": args.engine, | |
| } | |
| if args.lora_weights: | |
| metadata["lora_weights"] = args.lora_weights | |
| metadata["lora_scale"] = args.lora_scale | |
| if args.controlnet_type: | |
| metadata["controlnet_type"] = args.controlnet_type | |
| metadata["controlnet_scale"] = args.controlnet_scale | |
| if is_xl and args.enable_refiner: | |
| metadata["base.scheduler"] = args.scheduler | |
| metadata["base.denoising_steps"] = args.denoising_steps | |
| metadata["base.guidance"] = args.guidance | |
| metadata["refiner.strength"] = args.strength | |
| metadata["refiner.scheduler"] = args.refiner_scheduler | |
| metadata["refiner.denoising_steps"] = args.refiner_denoising_steps | |
| metadata["refiner.guidance"] = args.refiner_guidance | |
| else: | |
| metadata["scheduler"] = args.scheduler | |
| metadata["denoising_steps"] = args.denoising_steps | |
| metadata["guidance"] = args.guidance | |
| # Version of installed python packages | |
| packages = "" | |
| for name in [ | |
| "onnxruntime-gpu", | |
| "torch", | |
| "tensorrt", | |
| "transformers", | |
| "diffusers", | |
| "onnx", | |
| "onnx-graphsurgeon", | |
| "polygraphy", | |
| "controlnet_aux", | |
| ]: | |
| try: | |
| packages += (" " if packages else "") + f"{name}=={version(name)}" | |
| except PackageNotFoundError: | |
| continue | |
| metadata["packages"] = packages | |
| metadata["device"] = torch.cuda.get_device_name() | |
| metadata["torch.version.cuda"] = torch.version.cuda | |
| return metadata | |
| def repeat_prompt(args): | |
| if not isinstance(args.prompt, list): | |
| raise ValueError(f"`prompt` must be of type `str` or `str` list, but is {type(args.prompt)}") | |
| prompt = args.prompt * args.batch_size | |
| if not isinstance(args.negative_prompt, list): | |
| raise ValueError( | |
| f"`--negative-prompt` must be of type `str` or `str` list, but is {type(args.negative_prompt)}" | |
| ) | |
| if len(args.negative_prompt) == 1: | |
| negative_prompt = args.negative_prompt * len(prompt) | |
| else: | |
| negative_prompt = args.negative_prompt | |
| return prompt, negative_prompt | |
| def initialize_pipeline( | |
| version="xl-turbo", | |
| is_refiner: bool = False, | |
| is_inpaint: bool = False, | |
| engine_type=EngineType.ORT_CUDA, | |
| work_dir: str = ".", | |
| engine_dir=None, | |
| onnx_opset: int = 17, | |
| scheduler="EulerA", | |
| height=512, | |
| width=512, | |
| nvtx_profile=False, | |
| use_cuda_graph=True, | |
| build_dynamic_batch=False, | |
| build_dynamic_shape=False, | |
| min_image_size: int = 512, | |
| max_image_size: int = 1024, | |
| max_batch_size: int = 16, | |
| opt_batch_size: int = 1, | |
| build_all_tactics: bool = False, | |
| do_classifier_free_guidance: bool = False, | |
| lcm: bool = False, | |
| controlnet=None, | |
| lora_weights=None, | |
| lora_scale: float = 1.0, | |
| use_fp16_vae: bool = True, | |
| use_vae: bool = True, | |
| framework_model_dir: str | None = None, | |
| max_cuda_graphs: int = 1, | |
| ): | |
| pipeline_info = PipelineInfo( | |
| version, | |
| is_refiner=is_refiner, | |
| is_inpaint=is_inpaint, | |
| use_vae=use_vae, | |
| min_image_size=min_image_size, | |
| max_image_size=max_image_size, | |
| use_fp16_vae=use_fp16_vae, | |
| use_lcm=lcm, | |
| do_classifier_free_guidance=do_classifier_free_guidance, | |
| controlnet=controlnet, | |
| lora_weights=lora_weights, | |
| lora_scale=lora_scale, | |
| ) | |
| input_engine_dir = engine_dir | |
| onnx_dir, engine_dir, output_dir, framework_model_dir, timing_cache = get_engine_paths( | |
| work_dir=work_dir, pipeline_info=pipeline_info, engine_type=engine_type, framework_model_dir=framework_model_dir | |
| ) | |
| pipeline = StableDiffusionPipeline( | |
| pipeline_info, | |
| scheduler=scheduler, | |
| output_dir=output_dir, | |
| verbose=False, | |
| nvtx_profile=nvtx_profile, | |
| max_batch_size=max_batch_size, | |
| use_cuda_graph=use_cuda_graph, | |
| framework_model_dir=framework_model_dir, | |
| engine_type=engine_type, | |
| ) | |
| import_engine_dir = None | |
| if input_engine_dir: | |
| if not os.path.exists(input_engine_dir): | |
| raise RuntimeError(f"--engine_dir directory does not exist: {input_engine_dir}") | |
| # Support importing from optimized diffusers onnx pipeline | |
| if engine_type == EngineType.ORT_CUDA and os.path.exists(os.path.join(input_engine_dir, "model_index.json")): | |
| import_engine_dir = input_engine_dir | |
| else: | |
| engine_dir = input_engine_dir | |
| opt_image_height = pipeline_info.default_image_size() if build_dynamic_shape else height | |
| opt_image_width = pipeline_info.default_image_size() if build_dynamic_shape else width | |
| if engine_type == EngineType.ORT_CUDA: | |
| pipeline.backend.build_engines( | |
| engine_dir=engine_dir, | |
| framework_model_dir=framework_model_dir, | |
| onnx_dir=onnx_dir, | |
| tmp_dir=os.path.join(work_dir or ".", engine_type.name, pipeline_info.short_name(), "tmp"), | |
| device_id=torch.cuda.current_device(), | |
| import_engine_dir=import_engine_dir, | |
| max_cuda_graphs=max_cuda_graphs, | |
| ) | |
| elif engine_type == EngineType.ORT_TRT: | |
| pipeline.backend.build_engines( | |
| engine_dir, | |
| framework_model_dir, | |
| onnx_dir, | |
| onnx_opset, | |
| opt_image_height=opt_image_height, | |
| opt_image_width=opt_image_width, | |
| opt_batch_size=opt_batch_size, | |
| static_batch=not build_dynamic_batch, | |
| static_image_shape=not build_dynamic_shape, | |
| max_workspace_size=0, | |
| device_id=torch.cuda.current_device(), | |
| timing_cache=timing_cache, | |
| ) | |
| elif engine_type == EngineType.TRT: | |
| pipeline.backend.load_engines( | |
| engine_dir, | |
| framework_model_dir, | |
| onnx_dir, | |
| onnx_opset, | |
| opt_batch_size=opt_batch_size, | |
| opt_image_height=opt_image_height, | |
| opt_image_width=opt_image_width, | |
| static_batch=not build_dynamic_batch, | |
| static_shape=not build_dynamic_shape, | |
| enable_all_tactics=build_all_tactics, | |
| timing_cache=timing_cache, | |
| ) | |
| elif engine_type == EngineType.TORCH: | |
| pipeline.backend.build_engines(framework_model_dir) | |
| else: | |
| raise RuntimeError("invalid engine type") | |
| return pipeline | |
| def load_pipelines(args, batch_size=None): | |
| engine_type = get_engine_type(args.engine) | |
| # Register TensorRT plugins | |
| if engine_type == EngineType.TRT: | |
| from trt_utilities import init_trt_plugins # noqa: PLC0415 | |
| init_trt_plugins() | |
| max_batch_size = max_batch(args) | |
| if batch_size is None: | |
| assert isinstance(args.prompt, list) | |
| batch_size = len(args.prompt) * args.batch_size | |
| if batch_size > max_batch_size: | |
| raise ValueError(f"Batch size {batch_size} is larger than allowed {max_batch_size}.") | |
| # For TensorRT, performance of engine built with dynamic shape is very sensitive to the range of image size. | |
| # Here, we reduce the range of image size for TensorRT to trade-off flexibility and performance. | |
| # This range can cover most frequent shape of landscape (832x1216), portrait (1216x832) or square (1024x1024). | |
| if args.version == "xl-turbo": | |
| min_image_size = 512 | |
| max_image_size = 768 if args.engine != "ORT_CUDA" else 1024 | |
| elif args.version == "xl-1.0": | |
| min_image_size = 832 if args.engine != "ORT_CUDA" else 512 | |
| max_image_size = 1216 if args.engine != "ORT_CUDA" else 2048 | |
| else: | |
| # This range can cover common used shape of landscape 512x768, portrait 768x512, or square 512x512 and 768x768. | |
| min_image_size = 512 if args.engine != "ORT_CUDA" else 256 | |
| max_image_size = 768 if args.engine != "ORT_CUDA" else 1024 | |
| params = { | |
| "version": args.version, | |
| "is_refiner": False, | |
| "is_inpaint": False, | |
| "engine_type": engine_type, | |
| "work_dir": args.work_dir, | |
| "engine_dir": args.engine_dir, | |
| "onnx_opset": args.onnx_opset, | |
| "scheduler": args.scheduler, | |
| "height": args.height, | |
| "width": args.width, | |
| "nvtx_profile": args.nvtx_profile, | |
| "use_cuda_graph": not args.disable_cuda_graph, | |
| "build_dynamic_batch": args.build_dynamic_batch, | |
| "build_dynamic_shape": args.build_dynamic_shape, | |
| "min_image_size": min_image_size, | |
| "max_image_size": max_image_size, | |
| "max_batch_size": max_batch_size, | |
| "opt_batch_size": 1 if args.build_dynamic_batch else batch_size, | |
| "build_all_tactics": args.build_all_tactics, | |
| "do_classifier_free_guidance": args.guidance > 1.0, | |
| "controlnet": args.controlnet_type, | |
| "lora_weights": args.lora_weights, | |
| "lora_scale": args.lora_scale, | |
| "use_fp16_vae": "xl" in args.version, | |
| "use_vae": True, | |
| "framework_model_dir": args.framework_model_dir, | |
| "max_cuda_graphs": args.max_cuda_graphs, | |
| } | |
| if "xl" in args.version: | |
| params["lcm"] = args.lcm | |
| params["use_vae"] = not args.enable_refiner | |
| base = initialize_pipeline(**params) | |
| refiner = None | |
| if "xl" in args.version and args.enable_refiner: | |
| params["version"] = "xl-1.0" # Allow SDXL Turbo to use refiner. | |
| params["is_refiner"] = True | |
| params["scheduler"] = args.refiner_scheduler | |
| params["do_classifier_free_guidance"] = args.refiner_guidance > 1.0 | |
| params["lcm"] = False | |
| params["controlnet"] = None | |
| params["lora_weights"] = None | |
| params["use_vae"] = True | |
| params["use_fp16_vae"] = True | |
| refiner = initialize_pipeline(**params) | |
| if engine_type == EngineType.TRT: | |
| max_device_memory = max(base.backend.max_device_memory(), (refiner or base).backend.max_device_memory()) | |
| _, shared_device_memory = cudart.cudaMalloc(max_device_memory) | |
| base.backend.activate_engines(shared_device_memory) | |
| if refiner: | |
| refiner.backend.activate_engines(shared_device_memory) | |
| if engine_type == EngineType.ORT_CUDA: | |
| enable_vae_slicing = args.enable_vae_slicing | |
| if batch_size > 4 and not enable_vae_slicing and (args.height >= 1024 and args.width >= 1024): | |
| print( | |
| "Updating enable_vae_slicing to be True to avoid cuDNN error for batch size > 4 and resolution >= 1024." | |
| ) | |
| enable_vae_slicing = True | |
| if enable_vae_slicing: | |
| (refiner or base).backend.enable_vae_slicing() | |
| return base, refiner | |
| def get_depth_image(image): | |
| """ | |
| Create depth map for SDXL depth control net. | |
| """ | |
| from transformers import DPTFeatureExtractor, DPTForDepthEstimation # noqa: PLC0415 | |
| depth_estimator = DPTForDepthEstimation.from_pretrained("Intel/dpt-hybrid-midas").to("cuda") | |
| feature_extractor = DPTFeatureExtractor.from_pretrained("Intel/dpt-hybrid-midas") | |
| image = feature_extractor(images=image, return_tensors="pt").pixel_values.to("cuda") | |
| with torch.no_grad(), torch.autocast("cuda"): | |
| depth_map = depth_estimator(image).predicted_depth | |
| # The depth map is 384x384 by default, here we interpolate to the default output size. | |
| # Note that it will be resized to output image size later. May change the size here to avoid interpolate twice. | |
| depth_map = torch.nn.functional.interpolate( | |
| depth_map.unsqueeze(1), | |
| size=(1024, 1024), | |
| mode="bicubic", | |
| align_corners=False, | |
| ) | |
| depth_min = torch.amin(depth_map, dim=[1, 2, 3], keepdim=True) | |
| depth_max = torch.amax(depth_map, dim=[1, 2, 3], keepdim=True) | |
| depth_map = (depth_map - depth_min) / (depth_max - depth_min) | |
| image = torch.cat([depth_map] * 3, dim=1) | |
| image = image.permute(0, 2, 3, 1).cpu().numpy()[0] | |
| image = Image.fromarray((image * 255.0).clip(0, 255).astype(np.uint8)) | |
| return image | |
| def get_canny_image(image) -> Image.Image: | |
| """ | |
| Create canny image for SDXL control net. | |
| """ | |
| image = np.array(image) | |
| image = cv2.Canny(image, 100, 200) | |
| image = image[:, :, None] | |
| image = np.concatenate([image, image, image], axis=2) | |
| image = Image.fromarray(image) | |
| return image | |
| def process_controlnet_images_xl(args) -> list[Image.Image]: | |
| """ | |
| Process control image for SDXL control net. | |
| """ | |
| assert len(args.controlnet_image) == 1 | |
| image = Image.open(args.controlnet_image[0]).convert("RGB") | |
| controlnet_images = [] | |
| if args.controlnet_type[0] == "canny": | |
| controlnet_images.append(get_canny_image(image)) | |
| elif args.controlnet_type[0] == "depth": | |
| controlnet_images.append(get_depth_image(image)) | |
| else: | |
| raise ValueError(f"This controlnet type is not supported for SDXL or Turbo: {args.controlnet_type}.") | |
| return controlnet_images | |
| def add_controlnet_arguments(parser, is_xl: bool = False): | |
| """ | |
| Add control net related arguments. | |
| """ | |
| group = parser.add_argument_group("Options for ControlNet (supports 1.5, sd-turbo, xl-turbo, xl-1.0).") | |
| group.add_argument( | |
| "-ci", | |
| "--controlnet-image", | |
| nargs="*", | |
| type=str, | |
| default=[], | |
| help="Path to the input regular RGB image/images for controlnet", | |
| ) | |
| group.add_argument( | |
| "-ct", | |
| "--controlnet-type", | |
| nargs="*", | |
| type=str, | |
| default=[], | |
| choices=list(PipelineInfo.supported_controlnet("xl-1.0" if is_xl else "1.5").keys()), | |
| help="A list of controlnet type", | |
| ) | |
| group.add_argument( | |
| "-cs", | |
| "--controlnet-scale", | |
| nargs="*", | |
| type=float, | |
| default=[], | |
| help="The outputs of the controlnet are multiplied by `controlnet_scale` before they are added to the residual in the original unet. Default is 0.5 for SDXL, or 1.0 for SD 1.5", | |
| ) | |
| def process_controlnet_image(controlnet_type: str, image: Image.Image, height, width): | |
| """ | |
| Process control images of control net v1.1 for Stable Diffusion 1.5. | |
| """ | |
| control_image = None | |
| shape = (height, width) | |
| image = image.convert("RGB") | |
| if controlnet_type == "canny": | |
| canny_image = controlnet_aux.CannyDetector()(image) | |
| control_image = canny_image.resize(shape) | |
| elif controlnet_type == "normalbae": | |
| normal_image = controlnet_aux.NormalBaeDetector.from_pretrained("lllyasviel/Annotators")(image) | |
| control_image = normal_image.resize(shape) | |
| elif controlnet_type == "depth": | |
| depth_image = controlnet_aux.LeresDetector.from_pretrained("lllyasviel/Annotators")(image) | |
| control_image = depth_image.resize(shape) | |
| elif controlnet_type == "mlsd": | |
| mlsd_image = controlnet_aux.MLSDdetector.from_pretrained("lllyasviel/Annotators")(image) | |
| control_image = mlsd_image.resize(shape) | |
| elif controlnet_type == "openpose": | |
| openpose_image = controlnet_aux.OpenposeDetector.from_pretrained("lllyasviel/Annotators")(image) | |
| control_image = openpose_image.resize(shape) | |
| elif controlnet_type == "scribble": | |
| scribble_image = controlnet_aux.HEDdetector.from_pretrained("lllyasviel/Annotators")(image, scribble=True) | |
| control_image = scribble_image.resize(shape) | |
| elif controlnet_type == "seg": | |
| seg_image = controlnet_aux.SamDetector.from_pretrained("ybelkada/segment-anything", subfolder="checkpoints")( | |
| image | |
| ) | |
| control_image = seg_image.resize(shape) | |
| else: | |
| raise ValueError(f"There is no demo image of this controlnet_type: {controlnet_type}") | |
| return control_image | |
| def process_controlnet_arguments(args): | |
| """ | |
| Process control net arguments, and returns a list of control images and a tensor of control net scales. | |
| """ | |
| assert isinstance(args.controlnet_type, list) | |
| assert isinstance(args.controlnet_scale, list) | |
| assert isinstance(args.controlnet_image, list) | |
| if len(args.controlnet_image) != len(args.controlnet_type): | |
| raise ValueError( | |
| f"Numbers of controlnet_image {len(args.controlnet_image)} should be equal to number of controlnet_type {len(args.controlnet_type)}." | |
| ) | |
| if len(args.controlnet_type) == 0: | |
| return None, None | |
| if args.version not in ["1.5", "xl-1.0", "xl-turbo", "sd-turbo"]: | |
| raise ValueError("This demo only supports ControlNet in Stable Diffusion 1.5, XL or Turbo.") | |
| is_xl = "xl" in args.version | |
| if is_xl and len(args.controlnet_type) > 1: | |
| raise ValueError("This demo only support one ControlNet for Stable Diffusion XL or Turbo.") | |
| if len(args.controlnet_scale) == 0: | |
| args.controlnet_scale = [0.5 if is_xl else 1.0] * len(args.controlnet_type) | |
| elif len(args.controlnet_type) != len(args.controlnet_scale): | |
| raise ValueError( | |
| f"Numbers of controlnet_type {len(args.controlnet_type)} should be equal to number of controlnet_scale {len(args.controlnet_scale)}." | |
| ) | |
| # Convert controlnet scales to tensor | |
| controlnet_scale = torch.FloatTensor(args.controlnet_scale) | |
| if is_xl: | |
| images = process_controlnet_images_xl(args) | |
| else: | |
| images = [] | |
| for i, image in enumerate(args.controlnet_image): | |
| images.append(process_controlnet_image(args.controlnet_type[i], Image.open(image), args.height, args.width)) | |
| return images, controlnet_scale | |