mirror of
https://github.com/openvinotoolkit/stable-diffusion-webui.git
synced 2024-12-14 14:45:06 +03:00
1199 lines
53 KiB
Python
1199 lines
53 KiB
Python
# Copyright (C) 2023 Intel Corporation
|
|
# SPDX-License-Identifier: AGPL-3.0
|
|
|
|
import cv2
|
|
import os
|
|
import torch
|
|
import time
|
|
import hashlib
|
|
import functools
|
|
import gradio as gr
|
|
import numpy as np
|
|
import sys
|
|
|
|
import modules
|
|
import modules.paths as paths
|
|
import modules.scripts as scripts
|
|
|
|
from modules import images, devices, extra_networks, masking, shared, sd_models_config, prompt_parser
|
|
from modules.processing import (
|
|
StableDiffusionProcessing, Processed, apply_overlay, apply_color_correction,
|
|
get_fixed_seed, create_infotext, setup_color_correction
|
|
)
|
|
from modules.sd_models import CheckpointInfo, get_checkpoint_state_dict
|
|
from modules.shared import opts, state
|
|
from modules.ui_common import create_refresh_button
|
|
from modules.timer import Timer
|
|
|
|
from PIL import Image, ImageOps
|
|
from types import MappingProxyType
|
|
from typing import Optional
|
|
|
|
from openvino.frontend import FrontEndManager
|
|
from openvino.frontend.pytorch.fx_decoder import TorchFXPythonDecoder
|
|
from openvino.frontend.pytorch.torchdynamo import backend # noqa: F401
|
|
from openvino.frontend.pytorch.torchdynamo.partition import Partitioner
|
|
from openvino.runtime import Core, Type, PartialShape, serialize
|
|
|
|
from torch._dynamo.backends.common import fake_tensor_unsupported
|
|
from torch._dynamo.backends.registry import register_backend
|
|
from torch._inductor.compile_fx import compile_fx
|
|
from torch.fx.experimental.proxy_tensor import make_fx
|
|
from torch.fx import GraphModule
|
|
from torch.utils._pytree import tree_flatten
|
|
|
|
from hashlib import sha256
|
|
|
|
from diffusers import (
|
|
StableDiffusionPipeline,
|
|
StableDiffusionImg2ImgPipeline,
|
|
StableDiffusionInpaintPipeline,
|
|
StableDiffusionControlNetPipeline,
|
|
ControlNetModel,
|
|
StableDiffusionXLPipeline,
|
|
StableDiffusionXLImg2ImgPipeline,
|
|
StableDiffusionXLInpaintPipeline,
|
|
StableDiffusionXLControlNetPipeline,
|
|
ControlNetModel,
|
|
DDIMScheduler,
|
|
DPMSolverMultistepScheduler,
|
|
EulerAncestralDiscreteScheduler,
|
|
EulerDiscreteScheduler,
|
|
HeunDiscreteScheduler,
|
|
LMSDiscreteScheduler,
|
|
PNDMScheduler,
|
|
AutoencoderKL,
|
|
)
|
|
|
|
|
|
## hack for pytorch
|
|
def BUILD_MAP_UNPACK(self, inst):
|
|
items = self.popn(inst.argval)
|
|
# ensure everything is a dict
|
|
items = [BuiltinVariable(dict).call_function(self, [x], {}) for x in items] # noqa: F821
|
|
result = dict()
|
|
for x in items:
|
|
assert isinstance(x, ConstDictVariable) # noqa: F821
|
|
result.update(x.items)
|
|
self.push(
|
|
ConstDictVariable( # noqa: F821
|
|
result,
|
|
dict,
|
|
mutable_local=MutableLocal(), # noqa: F821
|
|
**VariableTracker.propagate(items), # noqa: F821
|
|
)
|
|
)
|
|
tmp_torch = sys.modules["torch"]
|
|
tmp_torch.BUILD_MAP_UNPACK_WITH_CALL = BUILD_MAP_UNPACK
|
|
|
|
class ModelState:
|
|
def __init__(self):
|
|
self.recompile = 1
|
|
self.device = "CPU"
|
|
self.height = 512
|
|
self.width = 512
|
|
self.batch_size = 1
|
|
self.mode = 0
|
|
self.partition_id = 0
|
|
self.model_hash = ""
|
|
self.control_models = []
|
|
self.is_sdxl = False
|
|
self.cn_model = "None"
|
|
self.lora_model = "None"
|
|
self.vae_ckpt = "None"
|
|
self.refiner_ckpt = "None"
|
|
|
|
|
|
model_state = ModelState()
|
|
|
|
DEFAULT_OPENVINO_PYTHON_CONFIG = MappingProxyType(
|
|
{
|
|
"use_python_fusion_cache": True,
|
|
"allow_single_op_fusion": True,
|
|
},
|
|
)
|
|
|
|
compiled_cache = {}
|
|
max_openvino_partitions = 0
|
|
partitioned_modules = {}
|
|
|
|
@register_backend
|
|
@fake_tensor_unsupported
|
|
def openvino_fx(subgraph, example_inputs):
|
|
try:
|
|
executor_parameters = None
|
|
inputs_reversed = False
|
|
if os.getenv("OPENVINO_TORCH_MODEL_CACHING") is not None:
|
|
# Create a hash to be used for caching
|
|
model_hash_str = sha256(subgraph.code.encode('utf-8')).hexdigest()
|
|
if (len(model_state.control_models) > 0 and model_state.partition_id == 0): #scn_model != "None" and model_state.partition_id == 0):
|
|
for cn_model in model_state.control_models:
|
|
model_hash_str = model_hash_str + "_" + cn_model
|
|
|
|
if (model_state.lora_model != "None"):
|
|
model_hash_str = model_hash_str + model_state.lora_model
|
|
|
|
executor_parameters = {"model_hash_str": model_hash_str}
|
|
|
|
# Check if the model was fully supported and already cached
|
|
example_inputs.reverse()
|
|
inputs_reversed = True
|
|
maybe_fs_cached_name = cached_model_name(model_hash_str + "_fs", get_device(), example_inputs, cache_root_path())
|
|
|
|
if os.path.isfile(maybe_fs_cached_name + ".xml") and os.path.isfile(maybe_fs_cached_name + ".bin"):
|
|
if (len(model_state.control_models) > 0 and model_state.control_models[0] in maybe_fs_cached_name):
|
|
example_inputs_reordered = []
|
|
if (os.path.isfile(maybe_fs_cached_name + ".txt")):
|
|
f = open(maybe_fs_cached_name + ".txt", "r")
|
|
reordered_idx = []
|
|
for input_data in example_inputs:
|
|
shape = f.readline()
|
|
if (str(input_data.size()) != shape):
|
|
for idx1, input_data1 in enumerate(example_inputs):
|
|
if (str(input_data1.size()).strip() == str(shape).strip()):
|
|
if idx1 not in reordered_idx:
|
|
reordered_idx.append(idx1)
|
|
example_inputs_reordered.append(example_inputs[idx1])
|
|
break
|
|
example_inputs = example_inputs_reordered
|
|
|
|
# Model is fully supported and already cached. Run the cached OV model directly.
|
|
compiled_model = openvino_compile_cached_model(maybe_fs_cached_name, *example_inputs)
|
|
|
|
def _call(*args):
|
|
if (len(model_state.control_models) > 0 and model_state.control_models[0] in maybe_fs_cached_name): #if (model_state.cn_model != "None" and model_state.cn_model in maybe_fs_cached_name):
|
|
args_reordered = []
|
|
if (os.path.isfile(maybe_fs_cached_name + ".txt")):
|
|
f = open(maybe_fs_cached_name + ".txt", "r")
|
|
reordered_idx = []
|
|
for input_data in args:
|
|
shape = f.readline()
|
|
if (str(input_data.size()) != shape):
|
|
for idx1, input_data1 in enumerate(args):
|
|
if (str(input_data1.size()).strip() == str(shape).strip()):
|
|
if idx1 not in reordered_idx:
|
|
reordered_idx.append(idx1)
|
|
args_reordered.append(args[idx1])
|
|
break
|
|
args = args_reordered
|
|
|
|
res = execute_cached(compiled_model, *args)
|
|
model_state.partition_id = model_state.partition_id + 1
|
|
return res
|
|
return _call
|
|
|
|
if (len(model_state.control_models) == 0):
|
|
compiled_model = openvino_compile_cached_model(maybe_fs_cached_name, *example_inputs)
|
|
def _call(*args):
|
|
|
|
res = execute_cached(compiled_model, *args)
|
|
model_state.partition_id = model_state.partition_id + 1
|
|
return res
|
|
return _call
|
|
|
|
if inputs_reversed:
|
|
example_inputs.reverse()
|
|
|
|
model = make_fx(subgraph)(*example_inputs)
|
|
for node in model.graph.nodes:
|
|
if node.target == torch.ops.aten.mul_.Tensor:
|
|
node.target = torch.ops.aten.mul.Tensor
|
|
with torch.no_grad():
|
|
model.eval()
|
|
partitioner = Partitioner()
|
|
compiled_model = partitioner.make_partitions(model)
|
|
|
|
if executor_parameters is not None and 'model_hash_str' in executor_parameters:
|
|
# Check if the model is fully supported.
|
|
fully_supported = partitioner.check_fully_supported(compiled_model)
|
|
if fully_supported:
|
|
executor_parameters["model_hash_str"] += "_fs"
|
|
|
|
def _call(*args):
|
|
res = execute(compiled_model, *args, executor="openvino",
|
|
executor_parameters=executor_parameters, file_name=maybe_fs_cached_name)
|
|
return res
|
|
return _call
|
|
except Exception as e:
|
|
print(e)
|
|
return compile_fx(subgraph, example_inputs)
|
|
|
|
def check_fully_supported(self, graph_module: GraphModule) -> bool:
|
|
num_fused = 0
|
|
for node in graph_module.graph.nodes:
|
|
if node.op == "call_module" and "fused_" in node.name:
|
|
num_fused += 1
|
|
elif node.op != "placeholder" and node.op != "output":
|
|
return False
|
|
if num_fused == 1:
|
|
return True
|
|
return False
|
|
|
|
Partitioner.check_fully_supported = functools.partial(check_fully_supported, Partitioner)
|
|
|
|
def execute(
|
|
gm: GraphModule,
|
|
*args,
|
|
executor: str = "openvino",
|
|
executor_parameters: Optional[dict] = None,
|
|
file_name = ""
|
|
):
|
|
if executor == "openvino":
|
|
return openvino_execute_partitioned(gm, *args, executor_parameters=executor_parameters, file_name=file_name)
|
|
elif executor == "strictly_openvino":
|
|
return openvino_execute(gm, *args, executor_parameters=executor_parameters, file_name=file_name)
|
|
|
|
msg = "Received unexpected value for 'executor': {0}. Allowed values are: openvino, strictly_openvino.".format(executor)
|
|
raise ValueError(msg)
|
|
|
|
|
|
class OpenVINOGraphModule(torch.nn.Module):
|
|
def __init__(self, gm, partition_id, use_python_fusion_cache, model_hash_str: str = None, file_name=""):
|
|
super().__init__()
|
|
self.gm = gm
|
|
self.partition_id = partition_id
|
|
self.executor_parameters = {"use_python_fusion_cache": use_python_fusion_cache,
|
|
"model_hash_str": model_hash_str}
|
|
self.file_name = file_name
|
|
self.perm_fallback = False
|
|
|
|
def __call__(self, *args):
|
|
#if self.perm_fallback:
|
|
# return self.gm(*args)
|
|
|
|
#try:
|
|
result = openvino_execute(self.gm, *args, executor_parameters=self.executor_parameters, partition_id=self.partition_id, file_name=self.file_name)
|
|
#except Exception:
|
|
# self.perm_fallback = True
|
|
# return self.gm(*args)
|
|
|
|
return result
|
|
|
|
|
|
def partition_graph(gm: GraphModule, use_python_fusion_cache: bool, model_hash_str: str = None, file_name=""):
|
|
global max_openvino_partitions
|
|
for node in gm.graph.nodes:
|
|
if node.op == "call_module" and "fused_" in node.name:
|
|
openvino_submodule = getattr(gm, node.name)
|
|
gm.delete_submodule(node.target)
|
|
gm.add_submodule(
|
|
node.target,
|
|
OpenVINOGraphModule(openvino_submodule, model_state.partition_id, use_python_fusion_cache,
|
|
model_hash_str=model_hash_str, file_name=file_name),
|
|
)
|
|
model_state.partition_id = model_state.partition_id + 1
|
|
|
|
return gm
|
|
|
|
|
|
def openvino_execute(gm: GraphModule, *args, executor_parameters=None, partition_id, file_name=""):
|
|
executor_parameters = executor_parameters or DEFAULT_OPENVINO_PYTHON_CONFIG
|
|
|
|
use_cache = executor_parameters.get(
|
|
"use_python_fusion_cache",
|
|
DEFAULT_OPENVINO_PYTHON_CONFIG["use_python_fusion_cache"],
|
|
)
|
|
global compiled_cache
|
|
|
|
model_hash_str = executor_parameters.get("model_hash_str", None)
|
|
if model_hash_str is not None:
|
|
model_hash_str = model_hash_str + str(partition_id)
|
|
|
|
if use_cache and (partition_id in compiled_cache):
|
|
compiled = compiled_cache[partition_id]
|
|
else:
|
|
if (len(model_state.control_models)> 0 and file_name is not None
|
|
and os.path.isfile(file_name + ".xml") and os.path.isfile(file_name + ".bin") and model_state.control_models[0] in file_name):
|
|
|
|
compiled = openvino_compile_cached_model(file_name, *args)
|
|
else:
|
|
compiled = openvino_compile(gm, *args, model_hash_str=model_hash_str, file_name=file_name)
|
|
compiled_cache[partition_id] = compiled
|
|
|
|
flat_args, _ = tree_flatten(args)
|
|
ov_inputs = [a.detach().cpu().numpy() for a in flat_args]
|
|
|
|
res = compiled(ov_inputs)
|
|
|
|
results1 = [torch.from_numpy(res[out]) for out in compiled.outputs]
|
|
if len(results1) == 1:
|
|
return results1[0]
|
|
return results1
|
|
|
|
def openvino_execute_partitioned(gm: GraphModule, *args, executor_parameters=None, file_name=""):
|
|
executor_parameters = executor_parameters or DEFAULT_OPENVINO_PYTHON_CONFIG
|
|
|
|
global partitioned_modules
|
|
|
|
use_python_fusion_cache = executor_parameters.get(
|
|
"use_python_fusion_cache",
|
|
DEFAULT_OPENVINO_PYTHON_CONFIG["use_python_fusion_cache"],
|
|
)
|
|
model_hash_str = executor_parameters.get("model_hash_str", None)
|
|
|
|
signature = str(id(gm))
|
|
for idx, input_data in enumerate(args):
|
|
if isinstance(input_data, torch.Tensor):
|
|
signature = signature + "_" + str(idx) + ":" + str(input_data.type())[6:] + ":" + str(input_data.size())[11:-1].replace(" ", "")
|
|
else:
|
|
signature = signature + "_" + str(idx) + ":" + type(input_data).__name__ + ":val(" + str(input_data) + ")"
|
|
|
|
if signature not in partitioned_modules:
|
|
partitioned_modules[signature] = partition_graph(gm, use_python_fusion_cache=use_python_fusion_cache,
|
|
model_hash_str=model_hash_str, file_name=file_name)
|
|
|
|
return partitioned_modules[signature](*args)
|
|
|
|
def execute_cached(compiled_model, *args):
|
|
flat_args, _ = tree_flatten(args)
|
|
ov_inputs = [a.detach().cpu().numpy() for a in flat_args]
|
|
|
|
if (len(model_state.control_models) == 0):
|
|
ov_inputs.reverse()
|
|
|
|
res = compiled_model(ov_inputs)
|
|
result = [torch.from_numpy(res[out]) for out in compiled_model.outputs]
|
|
return result
|
|
|
|
def cached_model_name(model_hash_str, device, args, cache_root, reversed = False):
|
|
if model_hash_str is None:
|
|
return None
|
|
|
|
model_cache_dir = cache_root + "/model/"
|
|
|
|
try:
|
|
os.makedirs(model_cache_dir, exist_ok=True)
|
|
file_name = model_cache_dir + model_hash_str + "_" + device
|
|
except OSError as error:
|
|
print("Cache directory ", cache_root, " cannot be created. Model caching is disabled. Error: ", error)
|
|
return None
|
|
|
|
inputs_str = ""
|
|
for input_data in args:
|
|
if reversed:
|
|
inputs_str = "_" + str(input_data.type()) + str(input_data.size())[11:-1].replace(" ", "") + inputs_str
|
|
else:
|
|
inputs_str += "_" + str(input_data.type()) + str(input_data.size())[11:-1].replace(" ", "")
|
|
inputs_str = sha256(inputs_str.encode('utf-8')).hexdigest()
|
|
file_name += inputs_str
|
|
|
|
return file_name
|
|
|
|
def cache_root_path():
|
|
cache_root = "./cache/"
|
|
if os.getenv("OPENVINO_TORCH_CACHE_DIR") is not None:
|
|
cache_root = os.getenv("OPENVINO_TORCH_CACHE_DIR")
|
|
return cache_root
|
|
|
|
def get_device():
|
|
device = "CPU"
|
|
core = Core()
|
|
if os.getenv("OPENVINO_TORCH_BACKEND_DEVICE") is not None:
|
|
device = os.getenv("OPENVINO_TORCH_BACKEND_DEVICE")
|
|
assert device in core.available_devices, "Specified device " + device + " is not in the list of OpenVINO Available Devices"
|
|
return device
|
|
|
|
def openvino_compile_cached_model(cached_model_path, *example_inputs):
|
|
core = Core()
|
|
om = core.read_model(cached_model_path + ".xml")
|
|
|
|
dtype_mapping = {
|
|
torch.float32: Type.f32,
|
|
torch.float64: Type.f64,
|
|
torch.float16: Type.f16,
|
|
torch.int64: Type.i64,
|
|
torch.int32: Type.i32,
|
|
torch.uint8: Type.u8,
|
|
torch.int8: Type.i8,
|
|
torch.bool: Type.boolean
|
|
}
|
|
|
|
for idx, input_data in enumerate(example_inputs):
|
|
om.inputs[idx].get_node().set_element_type(dtype_mapping[input_data.dtype])
|
|
om.inputs[idx].get_node().set_partial_shape(PartialShape(list(input_data.shape)))
|
|
om.validate_nodes_and_infer_types()
|
|
|
|
core.set_property({'CACHE_DIR': cache_root_path() + '/blob'})
|
|
|
|
compiled_model = core.compile_model(om, get_device())
|
|
|
|
return compiled_model
|
|
|
|
def openvino_compile(gm: GraphModule, *args, model_hash_str: str = None, file_name=""):
|
|
core = Core()
|
|
|
|
device = get_device()
|
|
cache_root = cache_root_path()
|
|
|
|
if (file_name is not None and os.path.isfile(file_name + ".xml") and os.path.isfile(file_name + ".bin")):
|
|
om = core.read_model(file_name + ".xml")
|
|
else:
|
|
fe_manager = FrontEndManager()
|
|
fe = fe_manager.load_by_framework("pytorch")
|
|
|
|
input_shapes = []
|
|
input_types = []
|
|
for input_data in args:
|
|
input_types.append(input_data.type())
|
|
input_shapes.append(input_data.size())
|
|
|
|
decoder = TorchFXPythonDecoder(gm, gm, input_shapes=input_shapes, input_types=input_types)
|
|
|
|
im = fe.load(decoder)
|
|
|
|
om = fe.convert(im)
|
|
|
|
if (file_name is not None):
|
|
serialize(om, file_name + ".xml", file_name + ".bin")
|
|
if (len(model_state.control_models) > 0):
|
|
f = open(file_name + ".txt", "w")
|
|
for input_data in args:
|
|
f.write(str(input_data.size()))
|
|
f.write("\n")
|
|
f.close()
|
|
|
|
dtype_mapping = {
|
|
torch.float32: Type.f32,
|
|
torch.float64: Type.f64,
|
|
torch.float16: Type.f16,
|
|
torch.int64: Type.i64,
|
|
torch.int32: Type.i32,
|
|
torch.uint8: Type.u8,
|
|
torch.int8: Type.i8,
|
|
torch.bool: Type.boolean
|
|
}
|
|
|
|
for idx, input_data in enumerate(args):
|
|
om.inputs[idx].get_node().set_element_type(dtype_mapping[input_data.dtype])
|
|
om.inputs[idx].get_node().set_partial_shape(PartialShape(list(input_data.shape)))
|
|
om.validate_nodes_and_infer_types()
|
|
|
|
if model_hash_str is not None:
|
|
core.set_property({'CACHE_DIR': cache_root + '/blob'})
|
|
|
|
compiled = core.compile_model(om, device)
|
|
return compiled
|
|
|
|
|
|
def openvino_clear_caches():
|
|
global partitioned_modules
|
|
global compiled_cache
|
|
|
|
compiled_cache.clear()
|
|
partitioned_modules.clear()
|
|
|
|
def sd_diffusers_model(self):
|
|
import modules.sd_models
|
|
return modules.sd_models.model_data.get_sd_model()
|
|
|
|
def cond_stage_key(self):
|
|
return None
|
|
|
|
shared.sd_diffusers_model = sd_diffusers_model
|
|
shared.sd_refiner_model = None
|
|
|
|
def set_scheduler(sd_model, sampler_name):
|
|
if (sampler_name == "Euler a"):
|
|
sd_model.scheduler = EulerAncestralDiscreteScheduler.from_config(sd_model.scheduler.config)
|
|
elif (sampler_name == "Euler"):
|
|
sd_model.scheduler = EulerDiscreteScheduler.from_config(sd_model.scheduler.config)
|
|
elif (sampler_name == "LMS"):
|
|
sd_model.scheduler = LMSDiscreteScheduler.from_config(sd_model.scheduler.config)
|
|
elif (sampler_name == "Heun"):
|
|
sd_model.scheduler = HeunDiscreteScheduler.from_config(sd_model.scheduler.config)
|
|
elif (sampler_name == "DPM++ 2M"):
|
|
sd_model.scheduler = DPMSolverMultistepScheduler.from_config(sd_model.scheduler.config, algorithm_type="dpmsolver++", use_karras_sigmas=False)
|
|
elif (sampler_name == "LMS Karras"):
|
|
sd_model.scheduler = LMSDiscreteScheduler.from_config(sd_model.scheduler.config, use_karras_sigmas=True)
|
|
elif (sampler_name == "DPM++ 2M Karras"):
|
|
sd_model.scheduler = DPMSolverMultistepScheduler.from_config(sd_model.scheduler.config, algorithm_type="dpmsolver++", use_karras_sigmas=True)
|
|
elif (sampler_name == "DDIM"):
|
|
sd_model.scheduler = DDIMScheduler.from_config(sd_model.scheduler.config)
|
|
elif (sampler_name == "PLMS"):
|
|
sd_model.scheduler = PNDMScheduler.from_config(sd_model.scheduler.config)
|
|
else:
|
|
sd_model.scheduler = EulerAncestralDiscreteScheduler.from_config(sd_model.scheduler.config)
|
|
|
|
return sd_model.scheduler
|
|
|
|
#sdxl invisible-watermark pixel artifact workaround
|
|
class NoWatermark:
|
|
def apply_watermark(self, img):
|
|
return img
|
|
|
|
def get_diffusers_sd_model(model_config, vae_ckpt, sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac):
|
|
if (model_state.recompile == 1):
|
|
model_state.partition_id = 0
|
|
torch._dynamo.reset()
|
|
openvino_clear_caches()
|
|
curr_dir_path = os.getcwd()
|
|
checkpoint_name = shared.opts.sd_model_checkpoint.split(" ")[0]
|
|
checkpoint_path = os.path.join(curr_dir_path, 'models', 'Stable-diffusion', checkpoint_name)
|
|
checkpoint_info = CheckpointInfo(checkpoint_path)
|
|
timer = Timer()
|
|
state_dict = get_checkpoint_state_dict(checkpoint_info, timer)
|
|
checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info)
|
|
print("OpenVINO Script: created model from config : " + checkpoint_config)
|
|
if(is_xl_ckpt):
|
|
if model_config != "None":
|
|
local_config_file = os.path.join(curr_dir_path, 'configs', model_config)
|
|
sd_model = StableDiffusionXLPipeline.from_single_file(checkpoint_path, original_config_file=local_config_file, use_safetensors=True, add_watermark=False, variant="fp32", dtype=torch.float32)
|
|
else:
|
|
sd_model = StableDiffusionXLPipeline.from_single_file(checkpoint_path, original_config_file=checkpoint_config, use_safetensors=True, add_watermark=False, variant="fp32", dtype=torch.float32)
|
|
if (mode == 1):
|
|
sd_model = StableDiffusionXLImg2ImgPipeline(**sd_model.components)
|
|
elif (mode == 2):
|
|
sd_model = StableDiffusionXLInpaintPipeline(**sd_model.components)
|
|
elif (mode == 3):
|
|
cn_model_dir_path = os.path.join(curr_dir_path, 'extensions', 'sd-webui-controlnet', 'models')
|
|
cn_model_path = os.path.join(cn_model_dir_path, model_state.cn_model)
|
|
if os.path.isfile(cn_model_path + '.pt'):
|
|
cn_model_path = cn_model_path + '.pt'
|
|
elif os.path.isfile(cn_model_path + '.safetensors'):
|
|
cn_model_path = cn_model_path + '.safetensors'
|
|
elif os.path.isfile(cn_model_path + '.pth'):
|
|
cn_model_path = cn_model_path + '.pth'
|
|
controlnet = ControlNetModel.from_single_file(cn_model_path, local_files_only=True)
|
|
sd_model = StableDiffusionXLControlNetPipeline(**sd_model.components, controlnet=controlnet)
|
|
sd_model.controlnet = torch.compile(sd_model.controlnet, backend="openvino_fx")
|
|
else:
|
|
if model_config != "None":
|
|
local_config_file = os.path.join(curr_dir_path, 'configs', model_config)
|
|
sd_model = StableDiffusionPipeline.from_single_file(checkpoint_path, original_config_file=local_config_file, use_safetensors=True, variant="fp32", dtype=torch.float32)
|
|
else:
|
|
sd_model = StableDiffusionPipeline.from_single_file(checkpoint_path, original_config_file=checkpoint_config, use_safetensors=True, variant="fp32", dtype=torch.float32)
|
|
|
|
if (mode == 1):
|
|
sd_model = StableDiffusionImg2ImgPipeline(**sd_model.components)
|
|
elif (mode == 2):
|
|
sd_model = StableDiffusionInpaintPipeline(**sd_model.components)
|
|
elif (mode == 3):
|
|
cn_model_dir_path = os.path.join(curr_dir_path, 'extensions', 'sd-webui-controlnet', 'models')
|
|
cn_model_path = os.path.join(cn_model_dir_path, model_state.cn_model)
|
|
if os.path.isfile(cn_model_path + '.pt'):
|
|
cn_model_path = cn_model_path + '.pt'
|
|
elif os.path.isfile(cn_model_path + '.safetensors'):
|
|
cn_model_path = cn_model_path + '.safetensors'
|
|
elif os.path.isfile(cn_model_path + '.pth'):
|
|
cn_model_path = cn_model_path + '.pth'
|
|
controlnet = ControlNetModel.from_single_file(cn_model_path, local_files_only=True)
|
|
sd_model = StableDiffusionControlNetPipeline(**sd_model.components, controlnet=controlnet)
|
|
sd_model.controlnet = torch.compile(sd_model.controlnet, backend="openvino_fx")
|
|
|
|
#load lora
|
|
|
|
|
|
if ('lora' in modules.extra_networks.extra_network_registry):
|
|
import lora
|
|
if lora.loaded_loras:
|
|
lora_model = lora.loaded_loras[0]
|
|
sd_model.load_lora_weights(os.path.join(os.getcwd(), "models", "Lora"), weight_name=lora_model.name + ".safetensors", low_cpu_mem_usage=True)
|
|
sd_model.watermark = NoWatermark()
|
|
sd_model.sd_checkpoint_info = checkpoint_info
|
|
sd_model.sd_model_hash = checkpoint_info.calculate_shorthash()
|
|
sd_model.safety_checker = None
|
|
sd_model.cond_stage_key = functools.partial(cond_stage_key, shared.sd_model)
|
|
sd_model.scheduler = set_scheduler(sd_model, sampler_name)
|
|
sd_model.unet = torch.compile(sd_model.unet, backend="openvino_fx")
|
|
## VAE
|
|
if vae_ckpt == "Disable-VAE-Acceleration":
|
|
sd_model.vae.decode = sd_model.vae.decode
|
|
elif vae_ckpt == "None":
|
|
sd_model.vae.decode = torch.compile(sd_model.vae.decode, backend="openvino_fx")
|
|
else:
|
|
vae_path = os.path.join(curr_dir_path, 'models', 'VAE', vae_ckpt)
|
|
print("OpenVINO Script: loading vae from : " + vae_path)
|
|
sd_model.vae = AutoencoderKL.from_single_file(vae_path, local_files_only=True)
|
|
sd_model.vae.decode = torch.compile(sd_model.vae.decode, backend="openvino_fx")
|
|
shared.sd_diffusers_model = sd_model
|
|
del sd_model
|
|
return shared.sd_diffusers_model
|
|
|
|
|
|
##get refiner model
|
|
def get_diffusers_sd_refiner_model(model_config, vae_ckpt, sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac):
|
|
if (model_state.recompile == 1):
|
|
curr_dir_path = os.getcwd()
|
|
if refiner_ckpt != "None":
|
|
refiner_checkpoint_path= os.path.join(curr_dir_path, 'models', 'Stable-diffusion', refiner_ckpt)
|
|
refiner_checkpoint_info = CheckpointInfo(refiner_checkpoint_path)
|
|
refiner_model = StableDiffusionXLImg2ImgPipeline.from_single_file(refiner_checkpoint_path, use_safetensors=True, torch_dtype=torch.float32)
|
|
refiner_model.watermark = NoWatermark()
|
|
refiner_model.sd_checkpoint_info = refiner_checkpoint_info
|
|
refiner_model.sd_model_hash = refiner_checkpoint_info.calculate_shorthash()
|
|
refiner_model.unet = torch.compile(refiner_model.unet, backend="openvino_fx")
|
|
## VAE
|
|
if vae_ckpt == "Disable-VAE-Acceleration":
|
|
refiner_model.vae.decode = refiner_model.vae.decode
|
|
elif vae_ckpt == "None":
|
|
refiner_model.vae.decode = torch.compile(refiner_model.vae.decode, backend="openvino_fx")
|
|
else:
|
|
vae_path = os.path.join(curr_dir_path, 'models', 'VAE', vae_ckpt)
|
|
print("OpenVINO Script: loading vae from : " + vae_path)
|
|
refiner_model.vae = AutoencoderKL.from_single_file(vae_path, local_files_only=True)
|
|
refiner_model.vae.decode = torch.compile(refiner_model.vae.decode, backend="openvino_fx")
|
|
shared.sd_refiner_model = refiner_model
|
|
del refiner_model
|
|
return shared.sd_refiner_model
|
|
|
|
def init_new(self, all_prompts, all_seeds, all_subseeds):
|
|
crop_region = None
|
|
|
|
image_mask = self.image_mask
|
|
|
|
if image_mask is not None:
|
|
image_mask = image_mask.convert('L')
|
|
|
|
if self.inpainting_mask_invert:
|
|
image_mask = ImageOps.invert(image_mask)
|
|
|
|
if self.mask_blur_x > 0:
|
|
np_mask = np.array(image_mask)
|
|
kernel_size = 2 * int(4 * self.mask_blur_x + 0.5) + 1
|
|
np_mask = cv2.GaussianBlur(np_mask, (kernel_size, 1), self.mask_blur_x)
|
|
image_mask = Image.fromarray(np_mask)
|
|
|
|
if self.mask_blur_y > 0:
|
|
np_mask = np.array(image_mask)
|
|
kernel_size = 2 * int(4 * self.mask_blur_y + 0.5) + 1
|
|
np_mask = cv2.GaussianBlur(np_mask, (1, kernel_size), self.mask_blur_y)
|
|
image_mask = Image.fromarray(np_mask)
|
|
|
|
if self.inpaint_full_res:
|
|
self.mask_for_overlay = image_mask
|
|
mask = image_mask.convert('L')
|
|
crop_region = masking.get_crop_region(np.array(mask), self.inpaint_full_res_padding)
|
|
crop_region = masking.expand_crop_region(crop_region, self.width, self.height, mask.width, mask.height)
|
|
x1, y1, x2, y2 = crop_region
|
|
|
|
mask = mask.crop(crop_region)
|
|
image_mask = images.resize_image(2, mask, self.width, self.height)
|
|
self.paste_to = (x1, y1, x2-x1, y2-y1)
|
|
else:
|
|
image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height)
|
|
np_mask = np.array(image_mask)
|
|
np_mask = np.clip((np_mask.astype(np.float32)) * 2, 0, 255).astype(np.uint8)
|
|
self.mask_for_overlay = Image.fromarray(np_mask)
|
|
|
|
self.overlay_images = []
|
|
|
|
latent_mask = self.latent_mask if self.latent_mask is not None else image_mask
|
|
|
|
add_color_corrections = opts.img2img_color_correction and self.color_corrections is None
|
|
if add_color_corrections:
|
|
self.color_corrections = []
|
|
imgs = []
|
|
for img in self.init_images:
|
|
# Save init image
|
|
if opts.save_init_img:
|
|
self.init_img_hash = hashlib.md5(img.tobytes()).hexdigest()
|
|
images.save_image(img, path=opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False)
|
|
|
|
image = images.flatten(img, opts.img2img_background_color)
|
|
|
|
if crop_region is None and self.resize_mode != 3:
|
|
image = images.resize_image(self.resize_mode, image, self.width, self.height)
|
|
|
|
if image_mask is not None:
|
|
image_masked = Image.new('RGBa', (image.width, image.height))
|
|
image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L')))
|
|
self.mask = image_mask
|
|
self.overlay_images.append(image_masked.convert('RGBA'))
|
|
|
|
# crop_region is not None if we are doing inpaint full res
|
|
if crop_region is not None:
|
|
image = image.crop(crop_region)
|
|
image = images.resize_image(2, image, self.width, self.height)
|
|
|
|
self.init_images = image
|
|
if image_mask is not None:
|
|
if self.inpainting_fill != 1:
|
|
image = masking.fill(image, latent_mask)
|
|
|
|
if add_color_corrections:
|
|
self.color_corrections.append(setup_color_correction(image))
|
|
|
|
image = np.array(image).astype(np.float32) / 255.0
|
|
image = np.moveaxis(image, 2, 0)
|
|
|
|
imgs.append(image)
|
|
|
|
if len(imgs) == 1:
|
|
if self.overlay_images is not None:
|
|
self.overlay_images = self.overlay_images * self.batch_size
|
|
|
|
if self.color_corrections is not None and len(self.color_corrections) == 1:
|
|
self.color_corrections = self.color_corrections * self.batch_size
|
|
|
|
elif len(imgs) <= self.batch_size:
|
|
self.batch_size = len(imgs)
|
|
else:
|
|
raise RuntimeError(f"bad number of images passed: {len(imgs)}; expecting {self.batch_size} or less")
|
|
|
|
def process_images_openvino(p: StableDiffusionProcessing, model_config, vae_ckpt, sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac) -> Processed:
|
|
|
|
"""this is the main loop that both txt2img and img2img use; it calls func_init once inside all the scopes and func_sample once per batch"""
|
|
if (mode == 0 and p.enable_hr):
|
|
print(p.hr_upscaler)
|
|
return
|
|
|
|
if type(p.prompt) == list:
|
|
assert(len(p.prompt) > 0)
|
|
else:
|
|
assert p.prompt is not None
|
|
|
|
devices.torch_gc()
|
|
|
|
seed = get_fixed_seed(p.seed)
|
|
subseed = get_fixed_seed(p.subseed)
|
|
|
|
comments = {}
|
|
custom_inputs = {}
|
|
|
|
p.setup_prompts()
|
|
|
|
if type(seed) == list:
|
|
p.all_seeds = seed
|
|
else:
|
|
p.all_seeds = [int(seed) + (x if p.subseed_strength == 0 else 0) for x in range(len(p.all_prompts))]
|
|
|
|
if type(subseed) == list:
|
|
p.all_subseeds = subseed
|
|
else:
|
|
p.all_subseeds = [int(subseed) + x for x in range(len(p.all_prompts))]
|
|
|
|
def infotext(iteration=0, position_in_batch=0):
|
|
return create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, comments, iteration, position_in_batch)
|
|
|
|
if p.scripts is not None:
|
|
p.scripts.process(p)
|
|
cn_model="None"
|
|
control_models = []
|
|
control_images = []
|
|
|
|
for key in p.extra_generation_params.keys():
|
|
if key.startswith('ControlNet'):
|
|
control_images_cn = []
|
|
cn_params = p.extra_generation_params[key]
|
|
cn_param_elements = [part.strip() for part in cn_params.split(', ')]
|
|
for element in cn_param_elements:
|
|
if (element.split(':')[0] == "Model"):
|
|
cn_model = (element.split(':')[1]).split(' ')[1]
|
|
|
|
if (cn_model != "None"):
|
|
control_models.append(cn_model)
|
|
control_res = Processed(
|
|
p,
|
|
images_list=control_images_cn,
|
|
)
|
|
p.scripts.postprocess(p, control_res)
|
|
mode = 3
|
|
for cn_image in control_images_cn:
|
|
control_images.append(cn_image)
|
|
|
|
model_state.control_models = control_models
|
|
|
|
|
|
infotexts = []
|
|
output_images = []
|
|
|
|
|
|
with torch.no_grad():
|
|
with devices.autocast():
|
|
p.init(p.all_prompts, p.all_seeds, p.all_subseeds)
|
|
|
|
if state.job_count == -1:
|
|
state.job_count = p.n_iter
|
|
|
|
extra_network_data = None
|
|
for n in range(p.n_iter):
|
|
p.iteration = n
|
|
|
|
if state.skipped:
|
|
state.skipped = False
|
|
|
|
if state.interrupted:
|
|
break
|
|
|
|
p.prompts = p.all_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
|
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n + 1) * p.batch_size]
|
|
p.seeds = p.all_seeds[n * p.batch_size:(n + 1) * p.batch_size]
|
|
p.subseeds = p.all_subseeds[n * p.batch_size:(n + 1) * p.batch_size]
|
|
|
|
if p.scripts is not None:
|
|
p.scripts.before_process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
|
|
|
if len(p.prompts) == 0:
|
|
break
|
|
|
|
extra_network_data = p.parse_extra_network_prompts()
|
|
|
|
if not p.disable_extra_networks:
|
|
with devices.autocast():
|
|
extra_networks.activate(p, p.extra_network_data)
|
|
|
|
lora_model_name = "None"
|
|
if ('lora' in modules.extra_networks.extra_network_registry):
|
|
import lora
|
|
if lora.loaded_loras:
|
|
lora_model = lora.loaded_loras[0]
|
|
lora_model_name = lora_model.name
|
|
custom_inputs.update(cross_attention_kwargs={"scale" : lora_model.te_multiplier})
|
|
|
|
if (model_state.height != p.height or model_state.width != p.width or model_state.batch_size != p.batch_size or model_state.lora_model != lora_model_name
|
|
or model_state.mode != mode or model_state.model_hash != shared.sd_model.sd_model_hash or model_state.cn_model != cn_model):
|
|
model_state.recompile = 1
|
|
model_state.height = p.height
|
|
model_state.width = p.width
|
|
model_state.batch_size = p.batch_size
|
|
model_state.mode = mode
|
|
model_state.cn_model = cn_model
|
|
model_state.model_hash = shared.sd_model.sd_model_hash
|
|
model_state.lora_model = lora_model_name
|
|
model_state.vae_ckpt = vae_ckpt
|
|
|
|
shared.sd_diffusers_model = get_diffusers_sd_model(model_config, vae_ckpt, sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac)
|
|
shared.sd_diffusers_model.scheduler = set_scheduler(shared.sd_diffusers_model, sampler_name)
|
|
|
|
if refiner_ckpt != "None":
|
|
shared.sd_refiner_model = get_diffusers_sd_refiner_model(model_config, vae_ckpt, sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac)
|
|
shared.sd_refiner_model.scheduler = set_scheduler(shared.sd_refiner_model, sampler_name)
|
|
|
|
|
|
if p.scripts is not None:
|
|
p.scripts.process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
|
|
|
# params.txt should be saved after scripts.process_batch, since the
|
|
# infotext could be modified by that callback
|
|
# Example: a wildcard processed by process_batch sets an extra model
|
|
# strength, which is saved as "Model Strength: 1.0" in the infotext
|
|
if n == 0:
|
|
with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file:
|
|
file.write(create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, comments=[], position_in_batch=0 % p.batch_size, iteration=0 // p.batch_size))
|
|
|
|
if p.n_iter > 1:
|
|
shared.state.job = f"Batch {n+1} out of {p.n_iter}"
|
|
|
|
generator = [torch.Generator(device="cpu").manual_seed(s) for s in p.seeds]
|
|
time_stamps = []
|
|
|
|
def callback(iter, t, latents):
|
|
time_stamps.append(time.time()) # noqa: B023
|
|
|
|
time_stamps.append(time.time())
|
|
|
|
|
|
if (mode == 0):
|
|
custom_inputs.update({
|
|
'width': p.width,
|
|
'height': p.height,
|
|
})
|
|
elif (mode == 1):
|
|
custom_inputs.update({
|
|
'image': p.init_images,
|
|
'strength':p.denoising_strength,
|
|
})
|
|
elif (mode == 2):
|
|
custom_inputs.update({
|
|
'image': p.init_images,
|
|
'strength':p.denoising_strength,
|
|
'mask_image': p.mask,
|
|
})
|
|
elif (mode == 3):
|
|
custom_inputs.update({
|
|
'image': control_images,
|
|
'width': p.width,
|
|
'height': p.height,
|
|
})
|
|
|
|
if refiner_ckpt != "None" and is_xl_ckpt is True:
|
|
base_output_type = "latent"
|
|
custom_inputs.update({
|
|
'denoising_end': refiner_frac
|
|
})
|
|
else:
|
|
base_output_type = "np"
|
|
|
|
#apply A1111 styled prompt weighting
|
|
cond = prompt_parser.SdConditioning(p.prompts, width=p.width, height=p.height)
|
|
prompt_embeds = p.sd_model.get_learned_conditioning(cond)
|
|
negative_cond = prompt_parser.SdConditioning(p.negative_prompts, width=p.width, height=p.height, is_negative_prompt=True)
|
|
negative_prompt_embeds = p.sd_model.get_learned_conditioning(negative_cond)
|
|
|
|
# temp workaround to disable prompt weighting for SDXL
|
|
if is_xl_ckpt is True :
|
|
custom_inputs.update(
|
|
{
|
|
'prompt': p.prompts,
|
|
'negative_prompt': p.negative_prompts
|
|
}
|
|
)
|
|
else:
|
|
custom_inputs.update(
|
|
{
|
|
'prompt_embeds' : prompt_embeds,
|
|
'negative_prompt_embeds' : negative_prompt_embeds
|
|
}
|
|
)
|
|
|
|
output = shared.sd_diffusers_model(
|
|
num_inference_steps=p.steps,
|
|
guidance_scale=p.cfg_scale,
|
|
generator=generator,
|
|
output_type=base_output_type,
|
|
callback = callback,
|
|
callback_steps = 1,
|
|
**custom_inputs
|
|
)
|
|
if refiner_ckpt != "None":
|
|
refiner_output = shared.sd_refiner_model(
|
|
prompt=p.prompts,
|
|
negative_prompt=p.negative_prompts,
|
|
num_inference_steps=p.steps,
|
|
denoising_start=refiner_frac,
|
|
image=output.images[0][None, :],
|
|
output_type="np"
|
|
)
|
|
|
|
model_state.recompile = 0
|
|
|
|
warmup_duration = time_stamps[1] - time_stamps[0]
|
|
generation_rate = (p.steps - 1) / (time_stamps[-1] - time_stamps[1])
|
|
|
|
if refiner_ckpt != "None":
|
|
x_samples_ddim = refiner_output.images
|
|
else:
|
|
x_samples_ddim = output.images
|
|
|
|
for i, x_sample in enumerate(x_samples_ddim):
|
|
p.batch_index = i
|
|
|
|
x_sample = (255. * x_sample).astype(np.uint8)
|
|
|
|
if p.restore_faces:
|
|
if opts.save and not p.do_not_save_samples and opts.save_images_before_face_restoration:
|
|
images.save_image(Image.fromarray(x_sample), p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(n, i), p=p, suffix="-before-face-restoration")
|
|
|
|
devices.torch_gc()
|
|
|
|
x_sample = modules.face_restoration.restore_faces(x_sample)
|
|
devices.torch_gc()
|
|
|
|
image = Image.fromarray(x_sample)
|
|
|
|
if p.scripts is not None:
|
|
pp = scripts.PostprocessImageArgs(image)
|
|
p.scripts.postprocess_image(p, pp)
|
|
image = pp.image
|
|
|
|
if p.color_corrections is not None and i < len(p.color_corrections):
|
|
if opts.save and not p.do_not_save_samples and opts.save_images_before_color_correction:
|
|
image_without_cc = apply_overlay(image, p.paste_to, i, p.overlay_images)
|
|
images.save_image(image_without_cc, p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(n, i), p=p, suffix="-before-color-correction")
|
|
image = apply_color_correction(p.color_corrections[i], image)
|
|
|
|
image = apply_overlay(image, p.paste_to, i, p.overlay_images)
|
|
|
|
if opts.samples_save and not p.do_not_save_samples:
|
|
images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(n, i), p=p)
|
|
|
|
text = infotext(n, i)
|
|
infotexts.append(text)
|
|
if opts.enable_pnginfo:
|
|
image.info["parameters"] = text
|
|
output_images.append(image)
|
|
|
|
for cn_image in control_images:
|
|
output_images.append(cn_image)
|
|
|
|
|
|
if hasattr(p, 'mask_for_overlay') and p.mask_for_overlay and any([opts.save_mask, opts.save_mask_composite, opts.return_mask, opts.return_mask_composite]):
|
|
image_mask = p.mask_for_overlay.convert('RGB')
|
|
image_mask_composite = Image.composite(image.convert('RGBA').convert('RGBa'), Image.new('RGBa', image.size), images.resize_image(2, p.mask_for_overlay, image.width, image.height).convert('L')).convert('RGBA')
|
|
|
|
if opts.save_mask:
|
|
images.save_image(image_mask, p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(n, i), p=p, suffix="-mask")
|
|
|
|
if opts.save_mask_composite:
|
|
images.save_image(image_mask_composite, p.outpath_samples, "", p.seeds[i], p.prompts[i], opts.samples_format, info=infotext(n, i), p=p, suffix="-mask-composite")
|
|
|
|
if opts.return_mask:
|
|
output_images.append(image_mask)
|
|
|
|
if opts.return_mask_composite:
|
|
output_images.append(image_mask_composite)
|
|
|
|
del x_samples_ddim
|
|
|
|
devices.torch_gc()
|
|
|
|
state.nextjob()
|
|
|
|
p.color_corrections = None
|
|
|
|
index_of_first_image = 0
|
|
unwanted_grid_because_of_img_count = len(output_images) < 2 and opts.grid_only_if_multiple
|
|
if (opts.return_grid or opts.grid_save) and not p.do_not_save_grid and not unwanted_grid_because_of_img_count:
|
|
grid = images.image_grid(output_images, p.batch_size)
|
|
|
|
if opts.return_grid:
|
|
text = infotext()
|
|
infotexts.insert(0, text)
|
|
if opts.enable_pnginfo:
|
|
grid.info["parameters"] = text
|
|
output_images.insert(0, grid)
|
|
index_of_first_image = 1
|
|
|
|
if opts.grid_save:
|
|
images.save_image(grid, p.outpath_grids, "grid", p.all_seeds[0], p.all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename, p=p, grid=True)
|
|
|
|
if not p.disable_extra_networks and extra_network_data:
|
|
extra_networks.deactivate(p, p.extra_network_data)
|
|
|
|
devices.torch_gc()
|
|
|
|
res = Processed(
|
|
p,
|
|
images_list=output_images,
|
|
seed=p.all_seeds[0],
|
|
info=infotext(),
|
|
comments="".join(f"{comment}\n" for comment in comments),
|
|
subseed=p.all_subseeds[0],
|
|
index_of_first_image=index_of_first_image,
|
|
infotexts=infotexts,
|
|
)
|
|
|
|
res.info = res.info + ", Warm up time: " + str(round(warmup_duration, 2)) + " secs "
|
|
|
|
if (generation_rate >= 1.0):
|
|
res.info = res.info + ", Performance: " + str(round(generation_rate, 2)) + " it/s "
|
|
else:
|
|
res.info = res.info + ", Performance: " + str(round(1/generation_rate, 2)) + " s/it "
|
|
|
|
|
|
if p.scripts is not None:
|
|
p.scripts.postprocess(p, res)
|
|
|
|
return res
|
|
|
|
class Script(scripts.Script):
|
|
def title(self):
|
|
return "Accelerate with OpenVINO"
|
|
|
|
def show(self, is_img2img):
|
|
return True
|
|
|
|
def ui(self, is_img2img):
|
|
core = Core()
|
|
|
|
def get_config_list():
|
|
config_dir_list = os.listdir(os.path.join(os.getcwd(), 'configs'))
|
|
config_list = []
|
|
config_list.append("None")
|
|
for file in config_dir_list:
|
|
if file.endswith('.yaml'):
|
|
config_list.append(file)
|
|
return config_list
|
|
def get_vae_list():
|
|
vae_dir_list = os.listdir(os.path.join(os.getcwd(), 'models', 'VAE'))
|
|
vae_list = []
|
|
vae_list.append("None")
|
|
vae_list.append("Disable-VAE-Acceleration")
|
|
for file in vae_dir_list:
|
|
if file.endswith('.safetensors') or file.endswith('.ckpt') or file.endswith('.pt'):
|
|
vae_list.append(file)
|
|
return vae_list
|
|
def get_refiner_list():
|
|
refiner_dir_list = os.listdir(os.path.join(os.getcwd(), 'models', 'Stable-diffusion'))
|
|
refiner_list = []
|
|
refiner_list.append("None")
|
|
for file in refiner_dir_list:
|
|
if file.endswith('.safetensors') or file.endswith('.ckpt') or file.endswith('.pt'):
|
|
refiner_list.append(file)
|
|
return refiner_list
|
|
|
|
with gr.Group():
|
|
with gr.Row():
|
|
with gr.Row():
|
|
model_config = gr.Dropdown(label="Select a local config for the model from the configs directory of the webui root", choices=get_config_list(), value="None", visible=True)
|
|
create_refresh_button(model_config, get_config_list, lambda: {"choices": get_config_list()},"refresh_model_config")
|
|
with gr.Row():
|
|
vae_ckpt = gr.Dropdown(label="Custom VAE", choices=get_vae_list(), value="None", visible=True)
|
|
create_refresh_button(vae_ckpt, get_vae_list, lambda: {"choices": get_vae_list()},"refresh_vae_directory")
|
|
openvino_device = gr.Dropdown(label="Select a device", choices=list(core.available_devices), value=model_state.device)
|
|
is_xl_ckpt= gr.Checkbox(label="Loaded checkpoint is a SDXL checkpoint", value=False)
|
|
with gr.Row():
|
|
refiner_ckpt = gr.Dropdown(label="Refiner Model", choices=get_refiner_list(), value="None")
|
|
create_refresh_button(refiner_ckpt, get_refiner_list,lambda: {"choices": get_refiner_list()},"refresh_refiner_directory" )
|
|
refiner_frac = gr.Slider(minimum=0, maximum=1, step=0.1, label='Refiner Denosing Fraction:', value=0.8)
|
|
|
|
override_sampler = gr.Checkbox(label="Override the sampling selection from the main UI (Recommended as only below sampling methods have been validated for OpenVINO)", value=True)
|
|
sampler_name = gr.Radio(label="Select a sampling method", choices=["Euler a", "Euler", "LMS", "Heun", "DPM++ 2M", "LMS Karras", "DPM++ 2M Karras", "DDIM", "PLMS"], value="Euler a")
|
|
enable_caching = gr.Checkbox(label="Cache the compiled models on disk for faster model load in subsequent launches (Recommended)", value=True, elem_id=self.elem_id("enable_caching"))
|
|
warmup_status = gr.Textbox(label="Device", interactive=False, visible=False)
|
|
vae_status = gr.Textbox(label="VAE", interactive=False, visible=False)
|
|
gr.Markdown(
|
|
"""
|
|
###
|
|
### Note:
|
|
- First inference involves compilation of the model for best performance.
|
|
Since compilation happens only on the first run, the first inference (or warm up inference) will be slower than subsequent inferences.
|
|
- For accurate performance measurements, it is recommended to exclude this slower first inference, as it doesn't reflect normal running time.
|
|
- Model is recompiled when resolution, batchsize, device, or samplers like DPM++ or Karras are changed.
|
|
After recompiling, later inferences will reuse the newly compiled model and achieve faster running times.
|
|
So it's normal for the first inference after a settings change to be slower, while subsequent inferences use the optimized compiled model and run faster.
|
|
""")
|
|
|
|
def device_change(choice):
|
|
if (model_state.device == choice):
|
|
return gr.update(value="Device selected is " + choice, visible=True)
|
|
else:
|
|
model_state.device = choice
|
|
model_state.recompile = 1
|
|
return gr.update(value="Device changed to " + choice + ". Model will be re-compiled", visible=True)
|
|
openvino_device.change(device_change, openvino_device, warmup_status)
|
|
def vae_change(choice):
|
|
if (model_state.vae_ckpt == choice):
|
|
return gr.update(value="vae_ckpt selected is " + choice, visible=True)
|
|
else:
|
|
model_state.vae_ckpt = choice
|
|
model_state.recompile = 1
|
|
return gr.update(value="Custom VAE changed to " + choice + ". Model will be re-compiled", visible=True)
|
|
vae_ckpt.change(vae_change, vae_ckpt, vae_status)
|
|
def refiner_ckpt_change(choice):
|
|
if (model_state.refiner_ckpt == choice):
|
|
return gr.update(value="Custom Refiner selected is " + choice, visible=True)
|
|
else:
|
|
model_state.refiner_ckpt = choice
|
|
refiner_ckpt.change(refiner_ckpt_change, refiner_ckpt)
|
|
return [model_config, vae_ckpt, openvino_device, override_sampler, sampler_name, enable_caching, is_xl_ckpt, refiner_ckpt, refiner_frac]
|
|
|
|
def run(self, p, model_config, vae_ckpt, openvino_device, override_sampler, sampler_name, enable_caching, is_xl_ckpt, refiner_ckpt, refiner_frac):
|
|
os.environ["OPENVINO_TORCH_BACKEND_DEVICE"] = str(openvino_device)
|
|
|
|
if enable_caching:
|
|
os.environ["OPENVINO_TORCH_MODEL_CACHING"] = "1"
|
|
|
|
if override_sampler:
|
|
p.sampler_name = sampler_name
|
|
else:
|
|
supported_samplers = ["Euler a", "Euler", "LMS", "Heun", "DPM++ 2M", "LMS Karras", "DPM++ 2M Karras", "DDIM", "PLMS"]
|
|
if (p.sampler_name not in supported_samplers):
|
|
p.sampler_name = "Euler a"
|
|
|
|
# mode can be 0, 1, 2 corresponding to txt2img, img2img, inpaint respectively
|
|
mode = 0
|
|
if self.is_txt2img:
|
|
mode = 0
|
|
processed = process_images_openvino(p, model_config, vae_ckpt, p.sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac)
|
|
else:
|
|
if p.image_mask is None:
|
|
mode = 1
|
|
else:
|
|
mode = 2
|
|
p.init = functools.partial(init_new, p)
|
|
processed = process_images_openvino(p, model_config, vae_ckpt, p.sampler_name, enable_caching, openvino_device, mode, is_xl_ckpt, refiner_ckpt, refiner_frac)
|
|
return processed
|
|
|
|
|