mirror of
https://github.com/vladmandic/automatic
synced 2026-09-15 02:58:44 +02:00
Merge branch 'dev' into Dev/DirectoryCacheing
This commit is contained in:
+21
-4
@@ -1,6 +1,6 @@
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2023-01-05
|
||||
## Update for 2023-01-06
|
||||
|
||||
Following-up on a major release, some more functionality in new Control module
|
||||
And it also includes fixes for all reported issues so far
|
||||
@@ -21,6 +21,8 @@ And it also includes fixes for all reported issues so far
|
||||
- add **openpose xl** controlnet
|
||||
- configurable output folder in settings
|
||||
- auto-refresh available models on tab activate
|
||||
- add image preview for override images set per-unit
|
||||
- more compact unit layout
|
||||
- reduce usage of temp files
|
||||
- add context menu to action buttons
|
||||
- resize by now applies to input image or frame individually
|
||||
@@ -30,10 +32,21 @@ And it also includes fixes for all reported issues so far
|
||||
- fix correct image mode
|
||||
- fix batch/folder/video modes
|
||||
- fix pipeline switching between different modes
|
||||
- [FaceID](https://huggingface.co/h94/IP-Adapter-FaceID)
|
||||
full implementation for *SD15* and *SD-XL*, to use simply select from *Scripts*
|
||||
- **Base** (93MB) uses *InsightFace* to generate face embeds and *OpenCLIP-ViT-H-14* (2.5GB) as image encoder
|
||||
- **SXDL** (1022MB) uses *InsightFace* to generate face embeds and *OpenCLIP-ViT-bigG-14* (3.7GB) as image encoder
|
||||
- **Plus** (150MB) uses *InsightFace* to generate face embeds and *CLIP-ViT-H-14-laion2B* (3.8GB) as image encoder
|
||||
*note*: all models are downloaded on first use
|
||||
- [IPAdapter](https://huggingface.co/h94/IP-Adapter)
|
||||
additional models for *SD15* and *SD-XL*, to use simply select from *Scripts*:
|
||||
- **SD15**: Base, Base ViT-G, Light, Plus, Plus Face, Full Face
|
||||
- **SDXL**: Base SXDL, Base ViT-H SXDL, Plus ViT-H SXDL, Plus Face ViT-H SXDL
|
||||
- **Improvements**
|
||||
- **server startup**: performance
|
||||
- faster extension load
|
||||
- faster json parsing
|
||||
- faster extension load
|
||||
- faster json parsing
|
||||
- faster lora indexing
|
||||
- **offline deployment**: allow deployment without git clone
|
||||
for example, you can now deploy a zip of the sdnext folder
|
||||
- **latent upscale**: updated latent upscalers (some are new)
|
||||
@@ -44,7 +57,7 @@ And it also includes fixes for all reported issues so far
|
||||
- enable vae tiling
|
||||
- add autodetect optimial value
|
||||
set tile size to 0 to use autodetected value
|
||||
- **cli**:
|
||||
- **cli**
|
||||
- `sdapi.py` allow manual api invoke
|
||||
example: `python cli/sdapi.py /sdapi/v1/sd-models`
|
||||
- `image-exif.py` improve metadata parsing
|
||||
@@ -67,6 +80,7 @@ And it also includes fixes for all reported issues so far
|
||||
requires nightly versions of `torch` and `torchao`
|
||||
> pip install -U --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu121
|
||||
> pip install -U git+https://github.com/pytorch-labs/ao
|
||||
- new option: **compile text encoder** (experimental)
|
||||
- **IPEX**, thanks @disty0
|
||||
- rewrite ipex hijacks without CondFunc
|
||||
improves compatibilty and performance
|
||||
@@ -77,6 +91,8 @@ And it also includes fixes for all reported issues so far
|
||||
- **4-bit support with NNCF**
|
||||
enable *Compress Model weights with NNCF* from *Compute Settings* and set a 4-bit NNCF mode
|
||||
4-bit and 8-bit with OpenVINO is CPU only for now
|
||||
- experimental support for *Text Encoder* compiling
|
||||
OpenVINO is faster than IPEX now
|
||||
- reduce system memory usage after compile
|
||||
- fix cache loading with multiple models
|
||||
- **Fixes**
|
||||
@@ -91,6 +107,7 @@ And it also includes fixes for all reported issues so far
|
||||
- processing: correct display metadata
|
||||
- live preview: fix when using `bfloat16`
|
||||
- upscale: fix ldsr
|
||||
- cli: fix cmd args parsing
|
||||
|
||||
## Update for 2023-12-29
|
||||
|
||||
|
||||
@@ -24,20 +24,8 @@ class NetworkOnDisk:
|
||||
self.metadata = {}
|
||||
self.is_safetensors = os.path.splitext(filename)[1].lower() == ".safetensors"
|
||||
|
||||
def read_metadata(): # # pylint: disable=W0612
|
||||
metadata = sd_models.read_metadata_from_safetensors(filename)
|
||||
metadata.pop('ssmd_cover_images', None) # those are cover images, and they are too big to display in UI as text
|
||||
return metadata
|
||||
|
||||
if self.is_safetensors:
|
||||
self.metadata = sd_models.read_metadata_from_safetensors(filename)
|
||||
"""
|
||||
try:
|
||||
self.metadata = cache.cached_data_for_file('safetensors-metadata', "lora/" + self.name, filename, read_metadata)
|
||||
except Exception as e:
|
||||
errors.display(e, f"reading lora {filename}")
|
||||
"""
|
||||
|
||||
if self.metadata:
|
||||
m = {}
|
||||
for k, v in sorted(self.metadata.items(), key=lambda x: metadata_tags_order.get(x[0], 999)):
|
||||
@@ -46,11 +34,7 @@ class NetworkOnDisk:
|
||||
self.alias = self.metadata.get('ss_output_name', self.name)
|
||||
self.hash = None
|
||||
self.shorthash = None
|
||||
self.set_hash(
|
||||
self.metadata.get('sshs_model_hash') or
|
||||
hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or
|
||||
''
|
||||
)
|
||||
self.set_hash(self.metadata.get('sshs_model_hash') or hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or '')
|
||||
self.sd_version = self.detect_version()
|
||||
|
||||
def detect_version(self):
|
||||
@@ -65,9 +49,6 @@ class NetworkOnDisk:
|
||||
def set_hash(self, v):
|
||||
self.hash = v
|
||||
self.shorthash = self.hash[0:12]
|
||||
if self.shorthash:
|
||||
import networks
|
||||
networks.available_network_hash_lookup[self.shorthash] = self
|
||||
|
||||
def read_hash(self):
|
||||
if not self.hash:
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import Union, List
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from threading import Thread
|
||||
import concurrent
|
||||
import lora_patches
|
||||
import network
|
||||
import network_lora
|
||||
@@ -441,18 +441,25 @@ def list_available_networks():
|
||||
shared.log.warning('LoRA directory not found: path="{shared.cmd_opts.lora_dir}"')
|
||||
if os.path.exists(shared.cmd_opts.lyco_dir):
|
||||
directories.append(shared.cmd_opts.lyco_dir)
|
||||
for filename in files_cache.list_files(*directories, ext_filter=[".pt", ".ckpt", ".safetensors"]):
|
||||
def add_network(filename):
|
||||
if os.path.isdir(filename):
|
||||
return
|
||||
name = os.path.splitext(os.path.basename(filename))[0]
|
||||
try:
|
||||
entry = network.NetworkOnDisk(name, filename)
|
||||
available_networks[entry.name] = entry
|
||||
if entry.alias in available_network_aliases:
|
||||
forbidden_network_aliases[entry.alias.lower()] = 1
|
||||
available_network_aliases[entry.name] = entry
|
||||
available_network_aliases[entry.alias] = entry
|
||||
if entry.shorthash:
|
||||
available_network_hash_lookup[entry.shorthash] = entry
|
||||
except OSError as e: # should catch FileNotFoundError and PermissionError etc.
|
||||
shared.log.error(f"Failed to load network {name} from {filename} {e}")
|
||||
continue
|
||||
available_networks[name] = entry
|
||||
if entry.alias in available_network_aliases:
|
||||
forbidden_network_aliases[entry.alias.lower()] = 1
|
||||
available_network_aliases[name] = entry
|
||||
available_network_aliases[entry.alias] = entry
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=shared.max_workers) as executor:
|
||||
for fn in files_cache.list_files(*directories, ext_filter=[".pt", ".ckpt", ".safetensors"]):
|
||||
executor.submit(add_network, fn)
|
||||
print(f'Lora/LyCORIS Networks: networks={len(available_networks)} directories={directories}')
|
||||
|
||||
|
||||
@@ -478,5 +485,4 @@ def infotext_pasted(infotext, params): # pylint: disable=W0613
|
||||
params["Prompt"] += "\n" + "".join(added)
|
||||
|
||||
|
||||
thread_lora = Thread(target=list_available_networks)
|
||||
thread_lora.start()
|
||||
list_available_networks()
|
||||
|
||||
Submodule extensions-builtin/sd-webui-controlnet updated: 82e4069287...94c93c6708
@@ -261,7 +261,9 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
|
||||
#control_input_type { max-width: 18em }
|
||||
#control_settings .small-accordion .form { min-width: 350px !important }
|
||||
.control-button { min-height: 42px; max-height: 42px; line-height: 1em; }
|
||||
.control-tabs>.tab-nav { margin-bottom: 0; margin-top: 0; }
|
||||
.control-tabs > .tab-nav { margin-bottom: 0; margin-top: 0; }
|
||||
.control-unit { max-width: 1200px; padding: 0 !important; margin-top: -10px !important; }
|
||||
.control-unit > .label-wrap { margin-bottom: 0 !important; }
|
||||
.processor-settings { padding: 0 !important; max-width: 300px; }
|
||||
.processor-group>div { flex-flow: wrap;gap: 1em; }
|
||||
|
||||
|
||||
+5
-5
@@ -80,12 +80,12 @@ def compatibility_args(opts, args):
|
||||
group.add_argument("--swinir-models-path", help=argparse.SUPPRESS, default=opts.swinir_models_path)
|
||||
group.add_argument("--ldsr-models-path", help=argparse.SUPPRESS, default=opts.ldsr_models_path)
|
||||
group.add_argument("--clip-models-path", type=str, help=argparse.SUPPRESS, default=opts.clip_models_path)
|
||||
group.add_argument("--opt-channelslast", help=argparse.SUPPRESS, default=opts.opt_channelslast)
|
||||
group.add_argument("--opt-channelslast", help=argparse.SUPPRESS, action='store_true', default=opts.opt_channelslast)
|
||||
group.add_argument("--xformers", default=(opts.cross_attention_optimization == "xFormers"), action='store_true', help=argparse.SUPPRESS)
|
||||
group.add_argument("--disable-nan-check", help=argparse.SUPPRESS, default=opts.disable_nan_check)
|
||||
group.add_argument("--disable-nan-check", help=argparse.SUPPRESS, action='store_true', default=opts.disable_nan_check)
|
||||
group.add_argument("--rollback-vae", help=argparse.SUPPRESS, default=opts.rollback_vae)
|
||||
group.add_argument("--no-half", help=argparse.SUPPRESS, default=opts.no_half)
|
||||
group.add_argument("--no-half-vae", help=argparse.SUPPRESS, default=opts.no_half_vae)
|
||||
group.add_argument("--no-half", help=argparse.SUPPRESS, action='store_true', default=opts.no_half)
|
||||
group.add_argument("--no-half-vae", help=argparse.SUPPRESS, action='store_true', default=opts.no_half_vae)
|
||||
group.add_argument("--precision", help=argparse.SUPPRESS, default=opts.precision)
|
||||
group.add_argument("--sub-quad-q-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_q_chunk_size)
|
||||
group.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size)
|
||||
@@ -94,7 +94,7 @@ def compatibility_args(opts, args):
|
||||
group.add_argument("--lyco-dir", help=argparse.SUPPRESS, default=opts.lyco_dir)
|
||||
group.add_argument("--embeddings-dir", help=argparse.SUPPRESS, default=opts.embeddings_dir)
|
||||
group.add_argument("--hypernetwork-dir", help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
|
||||
group.add_argument("--lyco-patch-lora", help=argparse.SUPPRESS, default=False)
|
||||
group.add_argument("--lyco-patch-lora", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group.add_argument("--lyco-debug", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group.add_argument("--enable-console-prompts", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group.add_argument("--safe", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
|
||||
@@ -467,11 +467,9 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_
|
||||
# pipeline
|
||||
output = None
|
||||
if pipe is not None: # run new pipeline
|
||||
debug(f'Control exec pipeline: class={pipe.__class__}')
|
||||
debug(f'Control exec pipeline: task={sd_models.get_diffusers_task(pipe)}')
|
||||
debug(f'Control exec pipeline: task={sd_models.get_diffusers_task(pipe)} class={pipe.__class__}')
|
||||
debug(f'Control exec pipeline: p={vars(p)}')
|
||||
debug(f'Control exec pipeline: args={p.task_args}')
|
||||
debug(f'Control exec pipeline: image={p.task_args.get("image", None)} control={p.task_args.get("control_image", None)} mask={p.task_args.get("mask_image", None)} ref={p.task_args.get("ref_image", None)}')
|
||||
debug(f'Control exec pipeline: args={p.task_args} image={p.task_args.get("image", None)} control={p.task_args.get("control_image", None)} mask={p.task_args.get("mask_image", None)} ref={p.task_args.get("ref_image", None)}')
|
||||
processed: processing.Processed = processing.process_images(p) # run actual pipeline
|
||||
output = processed.images if processed is not None else None
|
||||
# output = pipe(**vars(p)).images # alternative direct pipe exec call
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Union
|
||||
from PIL import Image
|
||||
import gradio as gr
|
||||
from modules.shared import log
|
||||
from modules.control import processors
|
||||
from modules.control.units import controlnet
|
||||
@@ -31,6 +32,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
image_input = None,
|
||||
preview_process = None,
|
||||
image_upload = None,
|
||||
image_preview = None,
|
||||
control_start = None,
|
||||
control_end = None,
|
||||
result_txt = None,
|
||||
@@ -101,8 +103,10 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
self.process.override = Image.open(image_file.name)
|
||||
self.override = self.process.override
|
||||
log.debug(f'Control process upload image: path="{image_file.name}" image={self.process.override}')
|
||||
return gr.update(visible=self.process.override is not None, value=self.process.override)
|
||||
except Exception as e:
|
||||
log.error(f'Control process upload image failed: path="{image_file.name}" error={e}')
|
||||
return gr.update(visible=False, value=None)
|
||||
|
||||
# actual init
|
||||
if self.type == 'adapter':
|
||||
@@ -157,7 +161,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c
|
||||
if preview_btn is not None:
|
||||
preview_btn.click(fn=self.process.preview, inputs=[self.input], outputs=[preview_process]) # return list of images for gallery
|
||||
if image_upload is not None:
|
||||
image_upload.upload(fn=upload_image, inputs=[image_upload], outputs=[]) # return list of images for gallery
|
||||
image_upload.upload(fn=upload_image, inputs=[image_upload], outputs=[image_preview]) # return list of images for gallery
|
||||
if control_start is not None and control_end is not None:
|
||||
control_start.change(fn=control_change, inputs=[control_start, control_end])
|
||||
control_end.change(fn=control_change, inputs=[control_start, control_end])
|
||||
|
||||
@@ -3,7 +3,7 @@ import time
|
||||
from typing import Union
|
||||
from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline, ControlNetModel, StableDiffusionControlNetPipeline, StableDiffusionXLControlNetPipeline
|
||||
from modules.control.units import detect
|
||||
from modules.shared import log, opts
|
||||
from modules.shared import log, opts, listdir
|
||||
from modules import errors
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ cache_dir = 'models/control/controlnet'
|
||||
|
||||
def find_models():
|
||||
path = os.path.join(opts.control_dir, 'controlnet')
|
||||
files = os.listdir(path)
|
||||
files = listdir(path)
|
||||
files = [f for f in files if f.endswith('.safetensors')]
|
||||
downloaded_models = {}
|
||||
for f in files:
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Union
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline
|
||||
from modules.shared import log, opts
|
||||
from modules.shared import log, opts, listdir
|
||||
from modules import errors
|
||||
from modules.control.units.lite_model import ControlNetLLLite
|
||||
|
||||
@@ -31,7 +31,7 @@ cache_dir = 'models/control/lite'
|
||||
|
||||
def find_models():
|
||||
path = os.path.join(opts.control_dir, 'lite')
|
||||
files = os.listdir(path)
|
||||
files = listdir(path)
|
||||
files = [f for f in files if f.endswith('.safetensors')]
|
||||
downloaded_models = {}
|
||||
for f in files:
|
||||
|
||||
@@ -3,7 +3,7 @@ import time
|
||||
from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline
|
||||
from modules.control.proc.reference_sd15 import StableDiffusionReferencePipeline
|
||||
from modules.control.proc.reference_sdxl import StableDiffusionXLReferencePipeline
|
||||
from modules.shared import log
|
||||
from modules.shared import log, opts
|
||||
from modules.control.units import detect
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ class ReferencePipeline():
|
||||
if pipeline is None:
|
||||
log.error(f'Control {what} model pipeline: model not loaded')
|
||||
return
|
||||
if opts.diffusers_fuse_projections and hasattr(pipeline, 'unfuse_qkv_projections'):
|
||||
pipeline.unfuse_qkv_projections()
|
||||
if detect.is_sdxl(pipeline):
|
||||
self.pipeline = StableDiffusionXLReferencePipeline(
|
||||
vae=pipeline.vae,
|
||||
|
||||
@@ -2,7 +2,7 @@ import os
|
||||
import time
|
||||
from typing import Union
|
||||
from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline
|
||||
from modules.shared import log, opts
|
||||
from modules.shared import log, opts, listdir
|
||||
from modules import errors
|
||||
from modules.control.units.xs_model import ControlNetXSModel
|
||||
from modules.control.units.xs_pipe import StableDiffusionControlNetXSPipeline, StableDiffusionXLControlNetXSPipeline
|
||||
@@ -27,7 +27,7 @@ cache_dir = 'models/control/xs'
|
||||
|
||||
def find_models():
|
||||
path = os.path.join(opts.control_dir, 'xs')
|
||||
files = os.listdir(path)
|
||||
files = listdir(path)
|
||||
files = [f for f in files if f.endswith('.safetensors')]
|
||||
downloaded_models = {}
|
||||
for f in files:
|
||||
|
||||
+1
-1
@@ -504,7 +504,7 @@ def get_next_sequence_number(path, basename):
|
||||
prefix_length = len(basename)
|
||||
if not os.path.isdir(path):
|
||||
return 0
|
||||
for p in os.listdir(path):
|
||||
for p in shared.listdir(path):
|
||||
if p.startswith(basename):
|
||||
parts = os.path.splitext(p[prefix_length:])[0].split('-') # splits the filename (removing the basename first if one is defined, so the sequence number is always the first element)
|
||||
try:
|
||||
|
||||
+2
-2
@@ -22,10 +22,10 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args)
|
||||
if not os.path.isdir(input_dir):
|
||||
shared.log.error(f"Process batch: directory not found: {input_dir}")
|
||||
return
|
||||
image_files = shared.listfiles(input_dir)
|
||||
image_files = shared.listdir(input_dir)
|
||||
is_inpaint_batch = False
|
||||
if inpaint_mask_dir:
|
||||
inpaint_masks = shared.listfiles(inpaint_mask_dir)
|
||||
inpaint_masks = shared.listdir(inpaint_mask_dir)
|
||||
is_inpaint_batch = len(inpaint_masks) > 0
|
||||
if is_inpaint_batch:
|
||||
shared.log.info(f"Process batch: inpaint batch masks={len(inpaint_masks)}")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import contextlib
|
||||
from functools import wraps
|
||||
from contextlib import nullcontext
|
||||
import torch
|
||||
import intel_extension_for_pytorch as ipex # pylint: disable=import-error, unused-import
|
||||
from modules import devices
|
||||
@@ -12,7 +13,7 @@ class DummyDataParallel(torch.nn.Module): # pylint: disable=missing-class-docstr
|
||||
return module.to(devices.device)
|
||||
|
||||
def return_null_context(*args, **kwargs): # pylint: disable=unused-argument
|
||||
return contextlib.nullcontext()
|
||||
return nullcontext()
|
||||
|
||||
@property
|
||||
def is_cuda(self):
|
||||
@@ -27,6 +28,7 @@ def return_xpu(device):
|
||||
|
||||
# Autocast
|
||||
original_autocast = torch.autocast
|
||||
@wraps(torch.autocast)
|
||||
def ipex_autocast(*args, **kwargs):
|
||||
if len(args) > 0 and (args[0] == "cuda" or args[0] == "xpu"):
|
||||
if "dtype" in kwargs:
|
||||
@@ -38,6 +40,7 @@ def ipex_autocast(*args, **kwargs):
|
||||
|
||||
# Latent Antialias CPU Offload:
|
||||
original_interpolate = torch.nn.functional.interpolate
|
||||
@wraps(torch.nn.functional.interpolate)
|
||||
def interpolate(tensor, size=None, scale_factor=None, mode='nearest', align_corners=None, recompute_scale_factor=None, antialias=False): # pylint: disable=too-many-arguments
|
||||
if antialias or align_corners is not None:
|
||||
return_device = tensor.device
|
||||
@@ -50,6 +53,7 @@ def interpolate(tensor, size=None, scale_factor=None, mode='nearest', align_corn
|
||||
|
||||
# Diffusers Float64 (Alchemist GPUs doesn't support 64 bit):
|
||||
original_from_numpy = torch.from_numpy
|
||||
@wraps(torch.from_numpy)
|
||||
def from_numpy(ndarray):
|
||||
if ndarray.dtype == float:
|
||||
return original_from_numpy(ndarray.astype('float32'))
|
||||
@@ -70,11 +74,13 @@ else:
|
||||
|
||||
|
||||
# Data Type Errors:
|
||||
@wraps(torch.bmm)
|
||||
def torch_bmm(input, mat2, *, out=None):
|
||||
if input.dtype != mat2.dtype:
|
||||
mat2 = mat2.to(input.dtype)
|
||||
return original_torch_bmm(input, mat2, out=out)
|
||||
|
||||
@wraps(torch.nn.functional.scaled_dot_product_attention)
|
||||
def scaled_dot_product_attention(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False):
|
||||
if query.dtype != key.dtype:
|
||||
key = key.to(dtype=query.dtype)
|
||||
@@ -84,6 +90,7 @@ def scaled_dot_product_attention(query, key, value, attn_mask=None, dropout_p=0.
|
||||
|
||||
# A1111 FP16
|
||||
original_functional_group_norm = torch.nn.functional.group_norm
|
||||
@wraps(torch.nn.functional.group_norm)
|
||||
def functional_group_norm(input, num_groups, weight=None, bias=None, eps=1e-05):
|
||||
if weight is not None and input.dtype != weight.data.dtype:
|
||||
input = input.to(dtype=weight.data.dtype)
|
||||
@@ -93,6 +100,7 @@ def functional_group_norm(input, num_groups, weight=None, bias=None, eps=1e-05):
|
||||
|
||||
# A1111 BF16
|
||||
original_functional_layer_norm = torch.nn.functional.layer_norm
|
||||
@wraps(torch.nn.functional.layer_norm)
|
||||
def functional_layer_norm(input, normalized_shape, weight=None, bias=None, eps=1e-05):
|
||||
if weight is not None and input.dtype != weight.data.dtype:
|
||||
input = input.to(dtype=weight.data.dtype)
|
||||
@@ -102,6 +110,7 @@ def functional_layer_norm(input, normalized_shape, weight=None, bias=None, eps=1
|
||||
|
||||
# Training
|
||||
original_functional_linear = torch.nn.functional.linear
|
||||
@wraps(torch.nn.functional.linear)
|
||||
def functional_linear(input, weight, bias=None):
|
||||
if input.dtype != weight.data.dtype:
|
||||
input = input.to(dtype=weight.data.dtype)
|
||||
@@ -110,6 +119,7 @@ def functional_linear(input, weight, bias=None):
|
||||
return original_functional_linear(input, weight, bias=bias)
|
||||
|
||||
original_functional_conv2d = torch.nn.functional.conv2d
|
||||
@wraps(torch.nn.functional.conv2d)
|
||||
def functional_conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1):
|
||||
if input.dtype != weight.data.dtype:
|
||||
input = input.to(dtype=weight.data.dtype)
|
||||
@@ -119,6 +129,7 @@ def functional_conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1,
|
||||
|
||||
# A1111 Embedding BF16
|
||||
original_torch_cat = torch.cat
|
||||
@wraps(torch.cat)
|
||||
def torch_cat(tensor, *args, **kwargs):
|
||||
if len(tensor) == 3 and (tensor[0].dtype != tensor[1].dtype or tensor[2].dtype != tensor[1].dtype):
|
||||
return original_torch_cat([tensor[0].to(tensor[1].dtype), tensor[1], tensor[2].to(tensor[1].dtype)], *args, **kwargs)
|
||||
@@ -127,6 +138,7 @@ def torch_cat(tensor, *args, **kwargs):
|
||||
|
||||
# SwinIR BF16:
|
||||
original_functional_pad = torch.nn.functional.pad
|
||||
@wraps(torch.nn.functional.pad)
|
||||
def functional_pad(input, pad, mode='constant', value=None):
|
||||
if mode == 'reflect' and input.dtype == torch.bfloat16:
|
||||
return original_functional_pad(input.to(torch.float32), pad, mode=mode, value=value).to(dtype=torch.bfloat16)
|
||||
@@ -135,6 +147,7 @@ def functional_pad(input, pad, mode='constant', value=None):
|
||||
|
||||
|
||||
original_torch_tensor = torch.tensor
|
||||
@wraps(torch.tensor)
|
||||
def torch_tensor(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_torch_tensor(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -142,6 +155,7 @@ def torch_tensor(*args, device=None, **kwargs):
|
||||
return original_torch_tensor(*args, device=device, **kwargs)
|
||||
|
||||
original_Tensor_to = torch.Tensor.to
|
||||
@wraps(torch.Tensor.to)
|
||||
def Tensor_to(self, device=None, *args, **kwargs):
|
||||
if check_device(device):
|
||||
return original_Tensor_to(self, return_xpu(device), *args, **kwargs)
|
||||
@@ -149,6 +163,7 @@ def Tensor_to(self, device=None, *args, **kwargs):
|
||||
return original_Tensor_to(self, device, *args, **kwargs)
|
||||
|
||||
original_Tensor_cuda = torch.Tensor.cuda
|
||||
@wraps(torch.Tensor.cuda)
|
||||
def Tensor_cuda(self, device=None, *args, **kwargs):
|
||||
if check_device(device):
|
||||
return original_Tensor_cuda(self, return_xpu(device), *args, **kwargs)
|
||||
@@ -156,6 +171,7 @@ def Tensor_cuda(self, device=None, *args, **kwargs):
|
||||
return original_Tensor_cuda(self, device, *args, **kwargs)
|
||||
|
||||
original_UntypedStorage_init = torch.UntypedStorage.__init__
|
||||
@wraps(torch.UntypedStorage.__init__)
|
||||
def UntypedStorage_init(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_UntypedStorage_init(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -163,6 +179,7 @@ def UntypedStorage_init(*args, device=None, **kwargs):
|
||||
return original_UntypedStorage_init(*args, device=device, **kwargs)
|
||||
|
||||
original_UntypedStorage_cuda = torch.UntypedStorage.cuda
|
||||
@wraps(torch.UntypedStorage.cuda)
|
||||
def UntypedStorage_cuda(self, device=None, *args, **kwargs):
|
||||
if check_device(device):
|
||||
return original_UntypedStorage_cuda(self, return_xpu(device), *args, **kwargs)
|
||||
@@ -170,6 +187,7 @@ def UntypedStorage_cuda(self, device=None, *args, **kwargs):
|
||||
return original_UntypedStorage_cuda(self, device, *args, **kwargs)
|
||||
|
||||
original_torch_empty = torch.empty
|
||||
@wraps(torch.empty)
|
||||
def torch_empty(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_torch_empty(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -177,6 +195,7 @@ def torch_empty(*args, device=None, **kwargs):
|
||||
return original_torch_empty(*args, device=device, **kwargs)
|
||||
|
||||
original_torch_randn = torch.randn
|
||||
@wraps(torch.randn)
|
||||
def torch_randn(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_torch_randn(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -184,6 +203,7 @@ def torch_randn(*args, device=None, **kwargs):
|
||||
return original_torch_randn(*args, device=device, **kwargs)
|
||||
|
||||
original_torch_ones = torch.ones
|
||||
@wraps(torch.ones)
|
||||
def torch_ones(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_torch_ones(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -191,6 +211,7 @@ def torch_ones(*args, device=None, **kwargs):
|
||||
return original_torch_ones(*args, device=device, **kwargs)
|
||||
|
||||
original_torch_zeros = torch.zeros
|
||||
@wraps(torch.zeros)
|
||||
def torch_zeros(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_torch_zeros(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -198,6 +219,7 @@ def torch_zeros(*args, device=None, **kwargs):
|
||||
return original_torch_zeros(*args, device=device, **kwargs)
|
||||
|
||||
original_torch_linspace = torch.linspace
|
||||
@wraps(torch.linspace)
|
||||
def torch_linspace(*args, device=None, **kwargs):
|
||||
if check_device(device):
|
||||
return original_torch_linspace(*args, device=return_xpu(device), **kwargs)
|
||||
@@ -205,6 +227,7 @@ def torch_linspace(*args, device=None, **kwargs):
|
||||
return original_torch_linspace(*args, device=device, **kwargs)
|
||||
|
||||
original_torch_Generator = torch.Generator
|
||||
@wraps(torch.Generator)
|
||||
def torch_Generator(device=None):
|
||||
if check_device(device):
|
||||
return original_torch_Generator(return_xpu(device))
|
||||
@@ -212,6 +235,7 @@ def torch_Generator(device=None):
|
||||
return original_torch_Generator(device)
|
||||
|
||||
original_torch_load = torch.load
|
||||
@wraps(torch.load)
|
||||
def torch_load(f, map_location=None, pickle_module=None, *, weights_only=False, mmap=None, **kwargs):
|
||||
if check_device(map_location):
|
||||
return original_torch_load(f, map_location=return_xpu(map_location), pickle_module=pickle_module, weights_only=weights_only, mmap=mmap, **kwargs)
|
||||
|
||||
@@ -229,8 +229,8 @@ def openvino_compile(gm: GraphModule, *args, model_hash_str: str = None, file_na
|
||||
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 shared.opts.nncf_compress_weights and not (shared.compiled_model_state.compiling_vae and not shared.opts.nncf_compress_vae_weights):
|
||||
if shared.compiled_model_state.compiling_vae or shared.opts.nncf_compress_weights_mode == "INT8":
|
||||
if shared.opts.nncf_compress_weights and not (shared.compiled_model_state.compile_dont_use_4bit and not shared.opts.nncf_compress_vae_weights):
|
||||
if shared.compiled_model_state.compile_dont_use_4bit or shared.opts.nncf_compress_weights_mode == "INT8":
|
||||
om = nncf.compress_weights(om)
|
||||
else:
|
||||
om = nncf.compress_weights(om, mode=getattr(nncf.CompressWeightsMode, shared.opts.nncf_compress_weights_mode), group_size=8, ratio=shared.opts.nncf_compress_weights_raito)
|
||||
@@ -238,7 +238,7 @@ def openvino_compile(gm: GraphModule, *args, model_hash_str: str = None, file_na
|
||||
if model_hash_str is not None:
|
||||
core.set_property({'CACHE_DIR': cache_root + '/blob'})
|
||||
|
||||
shared.compiled_model_state.compiling_vae = False
|
||||
shared.compiled_model_state.compile_dont_use_4bit = False
|
||||
compiled_model = core.compile_model(om, device)
|
||||
return compiled_model
|
||||
|
||||
@@ -261,15 +261,15 @@ def openvino_compile_cached_model(cached_model_path, *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()
|
||||
if shared.opts.nncf_compress_weights and not (shared.compiled_model_state.compiling_vae and not shared.opts.nncf_compress_vae_weights):
|
||||
if shared.compiled_model_state.compiling_vae or shared.opts.nncf_compress_weights_mode == "INT8":
|
||||
if shared.opts.nncf_compress_weights and not (shared.compiled_model_state.compile_dont_use_4bit and not shared.opts.nncf_compress_vae_weights):
|
||||
if shared.compiled_model_state.compile_dont_use_4bit or shared.opts.nncf_compress_weights_mode == "INT8":
|
||||
om = nncf.compress_weights(om)
|
||||
else:
|
||||
om = nncf.compress_weights(om, mode=getattr(nncf.CompressWeightsMode, shared.opts.nncf_compress_weights_mode), group_size=8, ratio=shared.opts.nncf_compress_weights_raito)
|
||||
|
||||
core.set_property({'CACHE_DIR': shared.opts.openvino_cache_path + '/blob'})
|
||||
|
||||
shared.compiled_model_state.compiling_vae = False
|
||||
shared.compiled_model_state.compile_dont_use_4bit = False
|
||||
compiled_model = core.compile_model(om, get_device())
|
||||
return compiled_model
|
||||
|
||||
@@ -344,7 +344,11 @@ def partition_graph(gm: GraphModule, use_python_fusion_cache: bool, model_hash_s
|
||||
|
||||
def generate_subgraph_str(tensor):
|
||||
if hasattr(tensor, "weight"):
|
||||
shared.compiled_model_state.model_str = shared.compiled_model_state.model_str + str(tensor.weight)
|
||||
shared.compiled_model_state.model_str = shared.compiled_model_state.model_str + sha256(str(tensor.weight).encode('utf-8')).hexdigest()
|
||||
return tensor
|
||||
|
||||
def get_subgraph_type(tensor):
|
||||
shared.compiled_model_state.subgraph_type.append(type(tensor))
|
||||
return tensor
|
||||
|
||||
@register_backend
|
||||
@@ -353,12 +357,28 @@ def openvino_fx(subgraph, example_inputs):
|
||||
executor_parameters = None
|
||||
inputs_reversed = False
|
||||
maybe_fs_cached_name = None
|
||||
|
||||
shared.compiled_model_state.subgraph_type = []
|
||||
subgraph.apply(get_subgraph_type)
|
||||
|
||||
# SD 1.5 / SDXL VAE
|
||||
if (shared.compiled_model_state.subgraph_type[0] is torch.nn.modules.conv.Conv2d and
|
||||
shared.compiled_model_state.subgraph_type[1] is torch.nn.modules.conv.Conv2d and
|
||||
shared.compiled_model_state.subgraph_type[2] is torch.nn.modules.normalization.GroupNorm and
|
||||
shared.compiled_model_state.subgraph_type[3] is torch.nn.modules.activation.SiLU):
|
||||
|
||||
shared.compiled_model_state.compile_dont_use_4bit = True
|
||||
|
||||
if not shared.opts.openvino_disable_model_caching:
|
||||
os.environ.setdefault('OPENVINO_TORCH_MODEL_CACHING', "1")
|
||||
# Create a hash to be used for caching
|
||||
shared.compiled_model_state.model_str = ""
|
||||
|
||||
# Create a hash to be used for caching
|
||||
subgraph.apply(generate_subgraph_str)
|
||||
shared.compiled_model_state.model_str = shared.compiled_model_state.model_str + sha256(subgraph.code.encode('utf-8')).hexdigest()
|
||||
model_hash_str = sha256(shared.compiled_model_state.model_str.encode('utf-8')).hexdigest()
|
||||
shared.compiled_model_state.model_str = ""
|
||||
|
||||
if (shared.compiled_model_state.cn_model != [] and shared.compiled_model_state.partition_id == 0):
|
||||
model_hash_str = model_hash_str + str(shared.compiled_model_state.cn_model)
|
||||
|
||||
@@ -383,9 +403,17 @@ def openvino_fx(subgraph, example_inputs):
|
||||
example_inputs_reordered.append(example_inputs[idx1])
|
||||
example_inputs = example_inputs_reordered
|
||||
|
||||
# Delete unused subgraphs
|
||||
subgraph = subgraph.apply(sd_models.convert_to_faketensors)
|
||||
devices.torch_gc(force=True)
|
||||
# SD 1.5 / SDXL Text Encoder
|
||||
if (shared.compiled_model_state.subgraph_type[0] is torch.nn.modules.sparse.Embedding and
|
||||
shared.compiled_model_state.subgraph_type[1] is torch.nn.modules.sparse.Embedding and
|
||||
shared.compiled_model_state.subgraph_type[2] is torch.nn.modules.normalization.LayerNorm and
|
||||
shared.compiled_model_state.subgraph_type[3] is torch.nn.modules.linear.Linear):
|
||||
|
||||
pass # Fails with FakeTensors or Downcast
|
||||
else:
|
||||
# Delete unused subgraphs
|
||||
subgraph = subgraph.apply(sd_models.convert_to_faketensors)
|
||||
devices.torch_gc(force=True)
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -230,7 +230,7 @@ def load_diffusers_models(model_path: str, command_path: str = None, clear=True)
|
||||
if not os.path.isfile(os.path.join(cache_path, "hidden")):
|
||||
output.append(str(r.repo_id))
|
||||
"""
|
||||
for folder in os.listdir(place):
|
||||
for folder in shared.listdir(place):
|
||||
try:
|
||||
if "--" not in folder:
|
||||
continue
|
||||
@@ -240,7 +240,7 @@ def load_diffusers_models(model_path: str, command_path: str = None, clear=True)
|
||||
name = name.replace("--", "/")
|
||||
folder = os.path.join(place, folder)
|
||||
friendly = os.path.join(place, name)
|
||||
snapshots = os.listdir(os.path.join(folder, "snapshots"))
|
||||
snapshots = shared.listdir(os.path.join(folder, "snapshots"))
|
||||
if len(snapshots) == 0:
|
||||
shared.log.warning(f"Diffusers folder has no snapshots: location={place} folder={folder} name={name}")
|
||||
continue
|
||||
@@ -450,7 +450,7 @@ def move_files(src_path: str, dest_path: str, ext_filter: str = None):
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
if os.path.exists(src_path):
|
||||
for file in os.listdir(src_path):
|
||||
for file in shared.listdir(src_path):
|
||||
fullpath = os.path.join(src_path, file)
|
||||
if os.path.isfile(fullpath):
|
||||
if ext_filter is not None:
|
||||
|
||||
@@ -38,7 +38,7 @@ def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemp
|
||||
elif extras_mode == 2:
|
||||
assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled'
|
||||
assert input_dir, 'input directory not selected'
|
||||
image_list = shared.listfiles(input_dir)
|
||||
image_list = shared.listdir(input_dir)
|
||||
for filename in image_list:
|
||||
try:
|
||||
image = Image.open(filename)
|
||||
|
||||
@@ -48,13 +48,9 @@ def full_vae_decode(latents, model):
|
||||
model.upcast_vae()
|
||||
latents = latents.to(next(iter(model.vae.post_quant_conv.parameters())).dtype)
|
||||
|
||||
# OpenVINO with INT4 doesn't work with VAE decode so we pass that we are using VAE right now to OpenVINO
|
||||
if shared.opts.cuda_compile and shared.opts.cuda_compile_backend == "openvino_fx" and shared.compiled_model_state.first_pass_vae:
|
||||
shared.compiled_model_state.compiling_vae = True
|
||||
|
||||
decoded = model.vae.decode(latents / model.vae.config.scaling_factor, return_dict=False)[0]
|
||||
|
||||
# Downcast VAE after OpenVINO compile
|
||||
# Delete PyTorch VAE after OpenVINO compile
|
||||
if shared.opts.cuda_compile and shared.opts.cuda_compile_backend == "openvino_fx" and shared.compiled_model_state.first_pass_vae:
|
||||
shared.compiled_model_state.first_pass_vae = False
|
||||
if hasattr(shared.sd_model, "vae"):
|
||||
|
||||
@@ -7,6 +7,7 @@ from installer import setup_logging, args
|
||||
|
||||
|
||||
preloaded = []
|
||||
debug = os.environ.get('SD_SCRIPT_DEBUG', None)
|
||||
|
||||
|
||||
def load_module(path):
|
||||
@@ -20,9 +21,12 @@ def load_module(path):
|
||||
if '/sd-extension-' in path: # safe extensions without stdout intercept
|
||||
module_spec.loader.exec_module(module)
|
||||
else:
|
||||
# stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(io.StringIO()) as stdout:
|
||||
if debug:
|
||||
module_spec.loader.exec_module(module)
|
||||
stdout = io.StringIO()
|
||||
else:
|
||||
with contextlib.redirect_stdout(io.StringIO()) as stdout:
|
||||
module_spec.loader.exec_module(module)
|
||||
setup_logging() # reset since scripts can hijaack logging
|
||||
for line in stdout.getvalue().splitlines():
|
||||
if len(line) > 0:
|
||||
|
||||
@@ -190,7 +190,7 @@ def context_hypertile_vae(p):
|
||||
# shared.log.warning('Hypertile VAE is enabled but no VAE model was found')
|
||||
return nullcontext()
|
||||
else:
|
||||
tile_size = shared.opts.hypertile_vae_tile if shared.opts.hypertile_vae_tile > 0 else max(256, 64 * min(p.width // 128, p.height // 128))
|
||||
tile_size = shared.opts.hypertile_vae_tile if shared.opts.hypertile_vae_tile > 0 else max(128, 64 * min(p.width // 128, p.height // 128))
|
||||
shared.log.info(f'Applying hypertile: vae={tile_size}')
|
||||
p.extra_generation_params['Hypertile VAE'] = tile_size
|
||||
return split_attention(vae, tile_size=tile_size, min_tile_size=128, swap_size=1)
|
||||
@@ -216,7 +216,7 @@ def context_hypertile_unet(p):
|
||||
# shared.log.warning('Hypertile UNet is enabled but no Unet model was found')
|
||||
return nullcontext()
|
||||
else:
|
||||
tile_size = shared.opts.hypertile_unet_tile if shared.opts.hypertile_unet_tile > 0 else max(256, 64 * min(p.width // 128, p.height // 128))
|
||||
tile_size = shared.opts.hypertile_unet_tile if shared.opts.hypertile_unet_tile > 0 else max(128, 64 * min(p.width // 128, p.height // 128))
|
||||
shared.log.info(f'Applying hypertile: unet={tile_size}')
|
||||
p.extra_generation_params['Hypertile UNet'] = tile_size
|
||||
return split_attention(unet, tile_size=tile_size, min_tile_size=128, swap_size=1)
|
||||
|
||||
+12
-5
@@ -1036,6 +1036,10 @@ def set_diffuser_pipe(pipe, new_pipe_type):
|
||||
image_encoder = getattr(pipe, "image_encoder", None)
|
||||
feature_extractor = getattr(pipe, "feature_extractor", None)
|
||||
|
||||
# skip specific pipelines
|
||||
if pipe.__class__.__name__ == 'StableDiffusionReferencePipeline' or pipe.__class__.__name__ == 'StableDiffusionAdapterPipeline':
|
||||
return pipe
|
||||
|
||||
try:
|
||||
if new_pipe_type == DiffusersTaskType.TEXT_2_IMAGE:
|
||||
new_pipe = diffusers.AutoPipelineForText2Image.from_pipe(pipe)
|
||||
@@ -1044,7 +1048,7 @@ def set_diffuser_pipe(pipe, new_pipe_type):
|
||||
elif new_pipe_type == DiffusersTaskType.INPAINTING:
|
||||
new_pipe = diffusers.AutoPipelineForInpainting.from_pipe(pipe)
|
||||
except Exception as e: # pylint: disable=unused-variable
|
||||
shared.log.error(f'Failed to change: type={new_pipe_type} pipeline={pipe.__class__.__name__} {e}')
|
||||
shared.log.warning(f'Failed to change: type={new_pipe_type} pipeline={pipe.__class__.__name__} {e}')
|
||||
return pipe
|
||||
|
||||
if pipe.__class__ == new_pipe.__class__:
|
||||
@@ -1263,10 +1267,10 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model')
|
||||
|
||||
|
||||
def convert_to_faketensors(tensor):
|
||||
fake = torch._subclasses.fake_tensor.FakeTensorMode()
|
||||
if hasattr(tensor, "weight"):
|
||||
tensor.weight = torch.nn.Parameter(fake.from_tensor(tensor.weight))
|
||||
return tensor
|
||||
fake_module = torch._subclasses.fake_tensor.FakeTensorMode(allow_non_fake_inputs=True) # pylint: disable=protected-access
|
||||
if hasattr(tensor, "weight"):
|
||||
tensor.weight = torch.nn.Parameter(fake_module.from_tensor(tensor.weight))
|
||||
return tensor
|
||||
|
||||
|
||||
def disable_offload(sd_model):
|
||||
@@ -1280,6 +1284,9 @@ def disable_offload(sd_model):
|
||||
|
||||
|
||||
def unload_model_weights(op='model', change_from='none'):
|
||||
if shared.compiled_model_state is not None:
|
||||
shared.compiled_model_state.compiled_cache.clear()
|
||||
shared.compiled_model_state.partitioned_modules.clear()
|
||||
if op == 'model' or op == 'dict':
|
||||
if model_data.sd_model:
|
||||
if (shared.backend == shared.Backend.ORIGINAL and change_from != shared.Backend.DIFFUSERS) or change_from == shared.Backend.ORIGINAL:
|
||||
|
||||
@@ -21,7 +21,8 @@ class CompiledModelState:
|
||||
self.lora_compile = False
|
||||
self.compiled_cache = {}
|
||||
self.partitioned_modules = {}
|
||||
self.compiling_vae = False
|
||||
self.subgraph_type = []
|
||||
self.compile_dont_use_4bit = False
|
||||
|
||||
|
||||
def ipex_optimize(sd_model):
|
||||
@@ -72,13 +73,13 @@ def optimize_openvino():
|
||||
if shared.compiled_model_state is None:
|
||||
shared.compiled_model_state = CompiledModelState()
|
||||
else:
|
||||
if not shared.compiled_model_state.lora_compile:
|
||||
shared.compiled_model_state.lora_compile = False
|
||||
shared.compiled_model_state.lora_model = []
|
||||
shared.compiled_model_state.compiled_cache.clear()
|
||||
shared.compiled_model_state.partitioned_modules.clear()
|
||||
shared.compiled_model_state.partition_id = 0
|
||||
shared.compiled_model_state.model_str = ""
|
||||
backup_lora_model = []
|
||||
if shared.compiled_model_state.lora_compile:
|
||||
backup_lora_model = shared.compiled_model_state.lora_model
|
||||
shared.compiled_model_state = CompiledModelState()
|
||||
shared.compiled_model_state.lora_model = backup_lora_model
|
||||
shared.compiled_model_state.first_pass = True if not shared.opts.cuda_compile_precompile else False
|
||||
shared.compiled_model_state.first_pass_vae = True if not shared.opts.cuda_compile_precompile else False
|
||||
shared.compiled_model_state.first_pass_refiner = True if not shared.opts.cuda_compile_precompile else False
|
||||
@@ -149,10 +150,10 @@ def compile_torch(sd_model):
|
||||
|
||||
t0 = time.time()
|
||||
if shared.opts.cuda_compile:
|
||||
if shared.opts.cuda_compile and (not hasattr(sd_model, 'unet') or not hasattr(sd_model.unet, 'config')):
|
||||
shared.log.warning('Model compile enabled but model has no Unet')
|
||||
else:
|
||||
if hasattr(sd_model, 'unet') and hasattr(sd_model.unet, 'config'):
|
||||
sd_model.unet = torch.compile(sd_model.unet, mode=shared.opts.cuda_compile_mode, backend=shared.opts.cuda_compile_backend, fullgraph=shared.opts.cuda_compile_fullgraph)
|
||||
else:
|
||||
shared.log.warning('Model compile enabled but model has no Unet')
|
||||
if shared.opts.cuda_compile_vae:
|
||||
if hasattr(sd_model, 'vae') and hasattr(sd_model.vae, 'decode'):
|
||||
sd_model.vae.decode = torch.compile(sd_model.vae.decode, mode=shared.opts.cuda_compile_mode, backend=shared.opts.cuda_compile_backend, fullgraph=shared.opts.cuda_compile_fullgraph)
|
||||
@@ -160,6 +161,13 @@ def compile_torch(sd_model):
|
||||
sd_model.movq.decode = torch.compile(sd_model.movq.decode, mode=shared.opts.cuda_compile_mode, backend=shared.opts.cuda_compile_backend, fullgraph=shared.opts.cuda_compile_fullgraph)
|
||||
else:
|
||||
shared.log.warning('Model compile enabled but model has no VAE')
|
||||
if shared.opts.cuda_compile_text_encoder:
|
||||
if hasattr(sd_model, 'text_encoder'):
|
||||
sd_model.text_encoder = torch.compile(sd_model.text_encoder, mode=shared.opts.cuda_compile_mode, backend=shared.opts.cuda_compile_backend, fullgraph=shared.opts.cuda_compile_fullgraph)
|
||||
if hasattr(sd_model, 'text_encoder_2'):
|
||||
sd_model.text_encoder_2 = torch.compile(sd_model.text_encoder_2, mode=shared.opts.cuda_compile_mode, backend=shared.opts.cuda_compile_backend, fullgraph=shared.opts.cuda_compile_fullgraph)
|
||||
else:
|
||||
shared.log.warning('Text Encoder compile enabled but model has no Text Encoder')
|
||||
setup_logging() # compile messes with logging so reset is needed
|
||||
if shared.opts.cuda_compile_precompile:
|
||||
sd_model("dummy prompt")
|
||||
|
||||
+14
-4
@@ -70,6 +70,8 @@ restricted_opts = {
|
||||
resize_modes = ["None", "Fixed", "Crop", "Fill", "Latent"]
|
||||
compatibility_opts = ['clip_skip', 'uni_pc_lower_order_final', 'uni_pc_order']
|
||||
console = Console(log_time=True, log_time_format='%H:%M:%S-%f')
|
||||
dir_timestamps = {}
|
||||
dir_cache = {}
|
||||
|
||||
|
||||
class Backend(Enum):
|
||||
@@ -327,7 +329,8 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
|
||||
"cuda_compile_sep": OptionInfo("<h2>Model Compile</h2>", "", gr.HTML),
|
||||
"cuda_compile": OptionInfo(False if not cmd_opts.use_openvino else True, "Compile UNet"),
|
||||
"cuda_compile_vae": OptionInfo(False if not cmd_opts.use_openvino else True, "Compile VAE"),
|
||||
"cuda_compile_upscaler": OptionInfo(False if not cmd_opts.use_openvino else True, "Compile upscaler"),
|
||||
"cuda_compile_text_encoder": OptionInfo(False, "Compile Text Encoder"),
|
||||
"cuda_compile_upscaler": OptionInfo(False if not cmd_opts.use_openvino else True, "Compile Upscaler"),
|
||||
"cuda_compile_backend": OptionInfo("none" if not cmd_opts.use_openvino else "openvino_fx", "Model compile backend", gr.Radio, {"choices": ['none', 'inductor', 'cudagraphs', 'aot_ts_nvfuser', 'hidet', 'ipex', 'openvino_fx', 'stable-fast']}),
|
||||
"cuda_compile_mode": OptionInfo("default", "Model compile mode", gr.Radio, {"choices": ['default', 'reduce-overhead', 'max-autotune', 'max-autotune-no-cudagraphs']}),
|
||||
"cuda_compile_fullgraph": OptionInfo(False, "Model compile fullgraph"),
|
||||
@@ -887,9 +890,16 @@ def restore_defaults(restart=True):
|
||||
restart_server(restart)
|
||||
|
||||
|
||||
def listfiles(dirname):
|
||||
filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname), key=str.lower) if not x.startswith(".")]
|
||||
return [file for file in filenames if os.path.isfile(file)]
|
||||
def listdir(path):
|
||||
if not os.path.exists(path):
|
||||
return []
|
||||
mtime = os.path.getmtime(path)
|
||||
if path in dir_timestamps and mtime == dir_timestamps[path]:
|
||||
return dir_cache[path]
|
||||
else:
|
||||
dir_cache[path] = [os.path.join(path, f) for f in os.listdir(path)]
|
||||
dir_timestamps[path] = mtime
|
||||
return dir_cache[path]
|
||||
|
||||
|
||||
def walk_files(path, allowed_extensions=None):
|
||||
|
||||
@@ -25,10 +25,6 @@ def preprocess(id_task, process_src, process_dst, process_width, process_height,
|
||||
deepbooru.model.stop()
|
||||
|
||||
|
||||
def listfiles(dirname):
|
||||
return os.listdir(dirname)
|
||||
|
||||
|
||||
class PreprocessParams:
|
||||
src = None
|
||||
dstdir = None
|
||||
@@ -137,17 +133,12 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
|
||||
dst = os.path.abspath(process_dst)
|
||||
split_threshold = max(0.0, min(1.0, split_threshold))
|
||||
overlap_ratio = max(0.0, min(0.9, overlap_ratio))
|
||||
|
||||
assert src != dst, 'same directory specified as source and destination'
|
||||
|
||||
os.makedirs(dst, exist_ok=True)
|
||||
|
||||
files = listfiles(src)
|
||||
|
||||
files = shared.listdir(src)
|
||||
shared.state.job = "preprocess"
|
||||
shared.state.textinfo = "Preprocessing..."
|
||||
shared.state.job_count = len(files)
|
||||
|
||||
params = PreprocessParams()
|
||||
params.dstdir = dst
|
||||
params.flip = process_flip
|
||||
@@ -155,7 +146,6 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
|
||||
params.process_caption = process_caption
|
||||
params.process_caption_deepbooru = process_caption_deepbooru
|
||||
params.preprocess_txt_action = preprocess_txt_action
|
||||
|
||||
pbar = tqdm(files)
|
||||
for index, imagefile in enumerate(pbar):
|
||||
params.subindex = 0
|
||||
@@ -171,9 +161,7 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
|
||||
description = f"Preprocessing image {index + 1}/{len(files)}"
|
||||
pbar.set_description(description)
|
||||
shared.state.textinfo = description
|
||||
|
||||
params.src = filename
|
||||
|
||||
existing_caption = None
|
||||
existing_caption_filename = f"{os.path.splitext(filename)[0]}.txt"
|
||||
if os.path.exists(existing_caption_filename):
|
||||
@@ -181,32 +169,25 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
|
||||
existing_caption = file.read()
|
||||
else:
|
||||
existing_caption_filename = None
|
||||
|
||||
if shared.state.interrupted:
|
||||
break
|
||||
|
||||
if img.height > img.width:
|
||||
ratio = (img.width * height) / (img.height * width)
|
||||
inverse_xy = False
|
||||
else:
|
||||
ratio = (img.height * width) / (img.width * height)
|
||||
inverse_xy = True
|
||||
|
||||
process_default_resize = True
|
||||
|
||||
if process_split and ratio < 1.0 and ratio <= split_threshold:
|
||||
for splitted in split_pic(img, inverse_xy, width, height, overlap_ratio):
|
||||
save_pic(splitted, index, params, existing_caption=existing_caption, existing_caption_filename=existing_caption_filename)
|
||||
process_default_resize = False
|
||||
|
||||
if process_focal_crop and img.height != img.width:
|
||||
|
||||
dnn_model_path = None
|
||||
try:
|
||||
dnn_model_path = autocrop.download_and_cache_models(os.path.join(paths.models_path, "opencv"))
|
||||
except Exception as e:
|
||||
print("Unable to load face detection model for auto crop selection. Falling back to lower quality haar method.", e)
|
||||
|
||||
autocrop_settings = autocrop.Settings(
|
||||
crop_width = width,
|
||||
crop_height = height,
|
||||
@@ -227,13 +208,10 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
|
||||
else:
|
||||
print(f"skipped {img.width}x{img.height} image {filename} (can't find suitable size within error threshold)")
|
||||
process_default_resize = False
|
||||
|
||||
if process_keep_original_size:
|
||||
save_pic(img, index, params, existing_caption=existing_caption)
|
||||
process_default_resize = False
|
||||
|
||||
if process_default_resize:
|
||||
img = images.resize_image(1, img, width, height)
|
||||
save_pic(img, index, params, existing_caption=existing_caption)
|
||||
|
||||
shared.state.nextjob()
|
||||
|
||||
+89
-89
@@ -421,20 +421,19 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
num_controlnet_units = gr.Slider(label="Units", minimum=1, maximum=max_units, step=1, value=1, scale=1)
|
||||
controlnet_ui_units = [] # list of hidable accordions
|
||||
for i in range(max_units):
|
||||
with gr.Accordion(f'Control unit {i+1}', visible= i < num_controlnet_units.value) as unit_ui:
|
||||
with gr.Accordion(f'Control unit {i+1}', visible= i < num_controlnet_units.value, elem_classes='control-unit') as unit_ui:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
with gr.Row():
|
||||
enabled_cb = gr.Checkbox(value= i==0, label="")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="ControlNet", choices=controlnet.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, controlnet.list_models, lambda: {"choices": controlnet.list_models(refresh=True)}, 'refresh_controlnet_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
control_start = gr.Slider(label="Start", minimum=0.0, maximum=1.0, step=0.05, value=0)
|
||||
control_end = gr.Slider(label="End", minimum=0.0, maximum=1.0, step=0.05, value=1.0)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
enabled_cb = gr.Checkbox(value= i==0, label="")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="ControlNet", choices=controlnet.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, controlnet.list_models, lambda: {"choices": controlnet.list_models(refresh=True)}, 'refresh_controlnet_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
control_start = gr.Slider(label="Start", minimum=0.0, maximum=1.0, step=0.05, value=0)
|
||||
control_end = gr.Slider(label="End", minimum=0.0, maximum=1.0, step=0.05, value=1.0)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", source="upload", interactive=False, height=128, width=128, visible=False)
|
||||
controlnet_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'controlnet',
|
||||
@@ -448,6 +447,7 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
preview_process = preview_process,
|
||||
preview_btn = process_btn,
|
||||
image_upload = image_upload,
|
||||
image_preview = image_preview,
|
||||
control_start = control_start,
|
||||
control_end = control_end,
|
||||
extra_controls = extra_controls,
|
||||
@@ -457,51 +457,6 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
units[-1].enabled = True # enable first unit in group
|
||||
num_controlnet_units.change(fn=display_units, inputs=[num_controlnet_units], outputs=controlnet_ui_units)
|
||||
|
||||
with gr.Tab('XS') as _tab_controlnetxs:
|
||||
gr.HTML('<a href="https://vislearn.github.io/ControlNet-XS/">ControlNet XS</a>')
|
||||
with gr.Row():
|
||||
extra_controls = [
|
||||
gr.Slider(label="Time embedding mix", minimum=0.0, maximum=1.0, step=0.05, value=0.0, scale=3)
|
||||
]
|
||||
num_controlnet_units = gr.Slider(label="Units", minimum=1, maximum=max_units, step=1, value=1, scale=1)
|
||||
controlnetxs_ui_units = [] # list of hidable accordions
|
||||
for i in range(max_units):
|
||||
with gr.Accordion(f'Control unit {i+1}', visible= i < num_controlnet_units.value) as unit_ui:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
with gr.Row():
|
||||
enabled_cb = gr.Checkbox(value= i==0, label="")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="ControlNet-XS", choices=xs.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, xs.list_models, lambda: {"choices": xs.list_models(refresh=True)}, 'refresh_xs_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
control_start = gr.Slider(label="Start", minimum=0.0, maximum=1.0, step=0.05, value=0)
|
||||
control_end = gr.Slider(label="End", minimum=0.0, maximum=1.0, step=0.05, value=1.0)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
controlnetxs_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'xs',
|
||||
result_txt = result_txt,
|
||||
image_input = input_image,
|
||||
enabled_cb = enabled_cb,
|
||||
reset_btn = reset_btn,
|
||||
process_id = process_id,
|
||||
model_id = model_id,
|
||||
model_strength = model_strength,
|
||||
preview_process = preview_process,
|
||||
preview_btn = process_btn,
|
||||
image_upload = image_upload,
|
||||
control_start = control_start,
|
||||
control_end = control_end,
|
||||
extra_controls = extra_controls,
|
||||
)
|
||||
)
|
||||
if i == 0:
|
||||
units[-1].enabled = True # enable first unit in group
|
||||
num_controlnet_units.change(fn=display_units, inputs=[num_controlnet_units], outputs=controlnetxs_ui_units)
|
||||
|
||||
with gr.Tab('Adapter') as _tab_adapter:
|
||||
gr.HTML('<a href="https://github.com/TencentARC/T2I-Adapter">T2I-Adapter</a>')
|
||||
with gr.Row():
|
||||
@@ -511,18 +466,17 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
num_adapter_units = gr.Slider(label="Units", minimum=1, maximum=max_units, step=1, value=1, scale=1)
|
||||
adapter_ui_units = [] # list of hidable accordions
|
||||
for i in range(max_units):
|
||||
with gr.Accordion(f'Adapter unit {i+1}', visible= i < num_adapter_units.value) as unit_ui:
|
||||
with gr.Accordion(f'Adapter unit {i+1}', visible= i < num_adapter_units.value, elem_classes='control-unit') as unit_ui:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
with gr.Row():
|
||||
enabled_cb = gr.Checkbox(value= i == 0, label="Enabled")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="Adapter", choices=t2iadapter.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, t2iadapter.list_models, lambda: {"choices": t2iadapter.list_models(refresh=True)}, 'refresh_adapter_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
enabled_cb = gr.Checkbox(value= i == 0, label="Enabled")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="Adapter", choices=t2iadapter.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, t2iadapter.list_models, lambda: {"choices": t2iadapter.list_models(refresh=True)}, 'refresh_adapter_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", source="upload", interactive=False, height=128, width=128, visible=False)
|
||||
adapter_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'adapter',
|
||||
@@ -536,6 +490,7 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
preview_process = preview_process,
|
||||
preview_btn = process_btn,
|
||||
image_upload = image_upload,
|
||||
image_preview = image_preview,
|
||||
extra_controls = extra_controls,
|
||||
)
|
||||
)
|
||||
@@ -543,6 +498,51 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
units[-1].enabled = True # enable first unit in group
|
||||
num_adapter_units.change(fn=display_units, inputs=[num_adapter_units], outputs=adapter_ui_units)
|
||||
|
||||
with gr.Tab('XS') as _tab_controlnetxs:
|
||||
gr.HTML('<a href="https://vislearn.github.io/ControlNet-XS/">ControlNet XS</a>')
|
||||
with gr.Row():
|
||||
extra_controls = [
|
||||
gr.Slider(label="Time embedding mix", minimum=0.0, maximum=1.0, step=0.05, value=0.0, scale=3)
|
||||
]
|
||||
num_controlnet_units = gr.Slider(label="Units", minimum=1, maximum=max_units, step=1, value=1, scale=1)
|
||||
controlnetxs_ui_units = [] # list of hidable accordions
|
||||
for i in range(max_units):
|
||||
with gr.Accordion(f'Control unit {i+1}', visible= i < num_controlnet_units.value, elem_classes='control-unit') as unit_ui:
|
||||
with gr.Row():
|
||||
enabled_cb = gr.Checkbox(value= i==0, label="")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="ControlNet-XS", choices=xs.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, xs.list_models, lambda: {"choices": xs.list_models(refresh=True)}, 'refresh_xs_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
control_start = gr.Slider(label="Start", minimum=0.0, maximum=1.0, step=0.05, value=0)
|
||||
control_end = gr.Slider(label="End", minimum=0.0, maximum=1.0, step=0.05, value=1.0)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", source="upload", interactive=False, height=128, width=128, visible=False)
|
||||
controlnetxs_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'xs',
|
||||
result_txt = result_txt,
|
||||
image_input = input_image,
|
||||
enabled_cb = enabled_cb,
|
||||
reset_btn = reset_btn,
|
||||
process_id = process_id,
|
||||
model_id = model_id,
|
||||
model_strength = model_strength,
|
||||
preview_process = preview_process,
|
||||
preview_btn = process_btn,
|
||||
image_upload = image_upload,
|
||||
image_preview = image_preview,
|
||||
control_start = control_start,
|
||||
control_end = control_end,
|
||||
extra_controls = extra_controls,
|
||||
)
|
||||
)
|
||||
if i == 0:
|
||||
units[-1].enabled = True # enable first unit in group
|
||||
num_controlnet_units.change(fn=display_units, inputs=[num_controlnet_units], outputs=controlnetxs_ui_units)
|
||||
|
||||
with gr.Tab('Lite') as _tab_lite:
|
||||
gr.HTML('<a href="https://huggingface.co/kohya-ss/controlnet-lllite">Control LLLite</a>')
|
||||
with gr.Row():
|
||||
@@ -551,18 +551,17 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
num_lite_units = gr.Slider(label="Units", minimum=1, maximum=max_units, step=1, value=1, scale=1)
|
||||
lite_ui_units = [] # list of hidable accordions
|
||||
for i in range(max_units):
|
||||
with gr.Accordion(f'Control unit {i+1}', visible= i < num_lite_units.value) as unit_ui:
|
||||
with gr.Accordion(f'Control unit {i+1}', visible= i < num_lite_units.value, elem_classes='control-unit') as unit_ui:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
with gr.Row():
|
||||
enabled_cb = gr.Checkbox(value= i == 0, label="Enabled")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="Model", choices=lite.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, lite.list_models, lambda: {"choices": lite.list_models(refresh=True)}, 'refresh_lite_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
enabled_cb = gr.Checkbox(value= i == 0, label="Enabled")
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None')
|
||||
model_id = gr.Dropdown(label="Model", choices=lite.list_models(), value='None')
|
||||
ui_common.create_refresh_button(model_id, lite.list_models, lambda: {"choices": lite.list_models(refresh=True)}, 'refresh_lite_models')
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0-i/10)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", source="upload", interactive=False, height=128, width=128, visible=False)
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
lite_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'lite',
|
||||
@@ -576,6 +575,7 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
preview_process = preview_process,
|
||||
preview_btn = process_btn,
|
||||
image_upload = image_upload,
|
||||
image_preview = image_preview,
|
||||
extra_controls = extra_controls,
|
||||
)
|
||||
)
|
||||
@@ -593,16 +593,15 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
gr.Slider(label="Reference adain weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True),
|
||||
]
|
||||
for i in range(1): # can only have one reference unit
|
||||
with gr.Accordion(f'Reference unit {i+1}', visible=True) as unit_ui:
|
||||
with gr.Accordion(f'Reference unit {i+1}', visible=True, elem_classes='control-unit') as unit_ui:
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
with gr.Row():
|
||||
enabled_cb = gr.Checkbox(value= i == 0, label="Enabled", visible=False)
|
||||
model_id = gr.Dropdown(label="Reference", choices=reference.list_models(), value='Reference', visible=False)
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, visible=False)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
enabled_cb = gr.Checkbox(value= i == 0, label="Enabled", visible=False)
|
||||
model_id = gr.Dropdown(label="Reference", choices=reference.list_models(), value='Reference', visible=False)
|
||||
model_strength = gr.Slider(label="Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, visible=False)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", source="upload", interactive=False, height=128, width=128, visible=False)
|
||||
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'reference',
|
||||
result_txt = result_txt,
|
||||
@@ -615,6 +614,7 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
preview_process = preview_process,
|
||||
preview_btn = process_btn,
|
||||
image_upload = image_upload,
|
||||
image_preview = image_preview,
|
||||
extra_controls = extra_controls,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ def process_interrogate(interrogation_function, mode, ii_input_files, ii_input_d
|
||||
if not os.path.isdir(ii_input_dir):
|
||||
shared.log.error(f"Interrogate: Input directory not found: {ii_input_dir}")
|
||||
return [gr.update(), None]
|
||||
images = shared.listfiles(ii_input_dir)
|
||||
images = shared.listdir(ii_input_dir)
|
||||
if ii_output_dir != "":
|
||||
os.makedirs(ii_output_dir, exist_ok=True)
|
||||
else:
|
||||
|
||||
@@ -119,7 +119,7 @@ def batch_process(batch_files, batch_folder, batch_str, mode, clip_model, write)
|
||||
if batch_folder is not None:
|
||||
files += [f.name for f in batch_folder]
|
||||
if batch_str is not None and len(batch_str) > 0 and os.path.exists(batch_str) and os.path.isdir(batch_str):
|
||||
files += [os.path.join(batch_str, f) for f in os.listdir(batch_str) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||
files += [os.path.join(batch_str, f) for f in shared.listdir(batch_str) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||
if len(files) == 0:
|
||||
shared.log.error('Interrogate batch no images')
|
||||
return ''
|
||||
|
||||
+1
-14
@@ -52,7 +52,7 @@ class Upscaler:
|
||||
pass
|
||||
|
||||
def find_folder(self, folder, scalers, loaded):
|
||||
for fn in os.listdir(folder): # from folder
|
||||
for fn in modules.shared.listdir(folder): # from folder
|
||||
file_name = os.path.join(folder, fn)
|
||||
if os.path.isdir(file_name):
|
||||
self.find_folder(file_name, scalers, loaded)
|
||||
@@ -83,19 +83,6 @@ class Upscaler:
|
||||
if not os.path.exists(self.user_path):
|
||||
return scalers
|
||||
self.find_folder(self.user_path, scalers, loaded)
|
||||
"""
|
||||
for fn in os.listdir(self.user_path): # from folder
|
||||
if not fn.endswith('.pth') and not fn.endswith('.pt'):
|
||||
continue
|
||||
file_name = os.path.join(self.user_path, fn)
|
||||
if file_name not in loaded:
|
||||
model_name = os.path.splitext(fn)[0]
|
||||
scaler = UpscalerData(name=f'{self.name} {model_name}', path=file_name, upscaler=self)
|
||||
scaler.custom = True
|
||||
scalers.append(scaler)
|
||||
loaded.append(file_name)
|
||||
# modules.shared.log.debug(f'Upscaler type={self.name} folder="{self.user_path}" model="{model_name}" path="{file_name}"')
|
||||
"""
|
||||
return scalers
|
||||
|
||||
@abstractmethod
|
||||
|
||||
+112
-27
@@ -6,11 +6,30 @@ import gradio as gr
|
||||
import diffusers
|
||||
import huggingface_hub as hf
|
||||
from modules import scripts, processing, shared, devices
|
||||
from installer import installed
|
||||
|
||||
|
||||
MODELS = {
|
||||
'FaceID Base': 'h94/IP-Adapter-FaceID/ip-adapter-faceid_sd15.bin',
|
||||
'FaceID Plus': 'h94/IP-Adapter-FaceID/ip-adapter-faceid-plus_sd15.bin',
|
||||
'FaceID Plus v2': 'h94/IP-Adapter-FaceID/ip-adapter-faceid-plusv2_sd15.bin',
|
||||
'FaceID XL': 'h94/IP-Adapter-FaceID/ip-adapter-faceid_sdxl.bin'
|
||||
}
|
||||
app = None
|
||||
ok = installed('insightface', reload=False, quiet=True) and installed('ip_adapter', reload=False, quiet=True)
|
||||
ip_model = None
|
||||
ip_model_name = None
|
||||
ip_model_tokens = None
|
||||
ip_model_rank = None
|
||||
|
||||
|
||||
def dependencies():
|
||||
from installer import installed, install
|
||||
packages = [
|
||||
('insightface', 'insightface'),
|
||||
('git+https://github.com/tencent-ailab/IP-Adapter.git', 'ip_adapter'),
|
||||
]
|
||||
for pkg in packages:
|
||||
if not installed(pkg[1], reload=False, quiet=True):
|
||||
install(pkg[0], pkg[1], ignore=True)
|
||||
|
||||
|
||||
class Script(scripts.Script):
|
||||
@@ -18,32 +37,43 @@ class Script(scripts.Script):
|
||||
return 'FaceID'
|
||||
|
||||
def show(self, is_img2img):
|
||||
return ok if shared.backend == shared.Backend.DIFFUSERS else False
|
||||
return True if shared.backend == shared.Backend.DIFFUSERS else False
|
||||
|
||||
# return signature is array of gradio components
|
||||
def ui(self, _is_img2img):
|
||||
with gr.Row():
|
||||
scale = gr.Slider(label='Scale', minimum=0.0, maximum=1.0, step=0.01, value=1.0)
|
||||
model = gr.Dropdown(choices=list(MODELS), label='Model', value='FaceID Base')
|
||||
with gr.Row(visible=True):
|
||||
override = gr.Checkbox(label='Override sampler', value=True)
|
||||
cache = gr.Checkbox(label='Cache model', value=True)
|
||||
with gr.Row(visible=True):
|
||||
scale = gr.Slider(label='Strength', minimum=0.0, maximum=1.0, step=0.01, value=1.0)
|
||||
structure = gr.Slider(label='Structure', minimum=0.0, maximum=1.0, step=0.01, value=1.0)
|
||||
with gr.Row(visible=False):
|
||||
rank = gr.Slider(label='Rank', minimum=4, maximum=256, step=4, value=128)
|
||||
tokens = gr.Slider(label='Tokens', minimum=1, maximum=16, step=1, value=4)
|
||||
with gr.Row():
|
||||
image = gr.Image(image_mode='RGB', label='Image', source='upload', type='pil', width=512)
|
||||
return [scale, image]
|
||||
return [model, scale, image, override, rank, tokens, structure, cache]
|
||||
|
||||
def run(self, p: processing.StableDiffusionProcessing, scale, image): # pylint: disable=arguments-differ, unused-argument
|
||||
def run(self, p: processing.StableDiffusionProcessing, model, scale, image, override, rank, tokens, structure, cache): # pylint: disable=arguments-differ, unused-argument
|
||||
dependencies()
|
||||
try:
|
||||
import onnxruntime
|
||||
from insightface.app import FaceAnalysis
|
||||
from ip_adapter.ip_adapter_faceid import IPAdapterFaceID
|
||||
from insightface.utils import face_align
|
||||
from ip_adapter.ip_adapter_faceid import IPAdapterFaceID, IPAdapterFaceIDPlus, IPAdapterFaceIDXL
|
||||
except Exception as e:
|
||||
shared.log.error(f'FaceID: {e}')
|
||||
return None
|
||||
if image is None:
|
||||
shared.log.error('FaceID: no init_images')
|
||||
return None
|
||||
if shared.sd_model_type != 'sd':
|
||||
if shared.sd_model_type != 'sd' and shared.sd_model_type != 'sdxl':
|
||||
shared.log.error('FaceID: base model not supported')
|
||||
return None
|
||||
|
||||
global app # pylint: disable=global-statement
|
||||
global app, ip_model, ip_model_name, ip_model_tokens, ip_model_rank # pylint: disable=global-statement
|
||||
if app is None:
|
||||
shared.log.debug(f"ONNX: device={onnxruntime.get_device()} providers={onnxruntime.get_available_providers()}")
|
||||
app = FaceAnalysis(name="buffalo_l", providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
|
||||
@@ -57,28 +87,68 @@ class Script(scripts.Script):
|
||||
return None
|
||||
for face in faces:
|
||||
shared.log.debug(f'FaceID face: score={face.det_score:.2f} gender={"female" if face.gender==0 else "male"} age={face.age} bbox={face.bbox}')
|
||||
embeds = torch.from_numpy(faces[0].normed_embedding).unsqueeze(0)
|
||||
face_embeds = torch.from_numpy(faces[0].normed_embedding).unsqueeze(0)
|
||||
face_image = face_align.norm_crop(image, landmark=faces[0].kps, image_size=224) # you can also segment the face
|
||||
|
||||
ip_ckpt = "h94/IP-Adapter-FaceID/ip-adapter-faceid_sd15.bin"
|
||||
shared.log.debug(f'FaceID model load: {ip_ckpt}')
|
||||
ip_ckpt = MODELS[model]
|
||||
folder, filename = os.path.split(ip_ckpt)
|
||||
basename, _ext = os.path.splitext(filename)
|
||||
model_path = hf.hf_hub_download(repo_id=folder, filename=filename, cache_dir=shared.opts.diffusers_dir)
|
||||
if model_path is None:
|
||||
shared.log.error(f'FaceID: model download failed: {ip_ckpt}')
|
||||
shared.log.error(f'FaceID download failed: model={model} file={ip_ckpt}')
|
||||
return None
|
||||
|
||||
processing.process_init(p)
|
||||
shared.sd_model.scheduler = diffusers.DDIMScheduler(
|
||||
num_train_timesteps=1000,
|
||||
beta_start=0.00085,
|
||||
beta_end=0.012,
|
||||
beta_schedule="scaled_linear",
|
||||
clip_sample=False,
|
||||
set_alpha_to_one=False,
|
||||
steps_offset=1,
|
||||
)
|
||||
ip_model = IPAdapterFaceID(shared.sd_model, model_path, devices.device)
|
||||
if override:
|
||||
shared.sd_model.scheduler = diffusers.DDIMScheduler(
|
||||
num_train_timesteps=1000,
|
||||
beta_start=0.00085,
|
||||
beta_end=0.012,
|
||||
beta_schedule="scaled_linear",
|
||||
clip_sample=False,
|
||||
set_alpha_to_one=False,
|
||||
steps_offset=1,
|
||||
)
|
||||
shortcut = None
|
||||
if ip_model is None or ip_model_name != model or ip_model_tokens != tokens or ip_model_rank != rank or not cache:
|
||||
shared.log.debug(f'FaceID load: model={model} file={ip_ckpt} tokens={tokens} rank={rank}')
|
||||
if 'Plus' in model:
|
||||
image_encoder_path = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K"
|
||||
ip_model = IPAdapterFaceIDPlus(
|
||||
sd_pipe=shared.sd_model,
|
||||
image_encoder_path=image_encoder_path,
|
||||
ip_ckpt=model_path,
|
||||
lora_rank=rank,
|
||||
num_tokens=tokens,
|
||||
device=devices.device,
|
||||
torch_dtype=devices.dtype,
|
||||
)
|
||||
shortcut = 'v2' in model
|
||||
elif 'XL' in model:
|
||||
ip_model = IPAdapterFaceIDXL(
|
||||
sd_pipe=shared.sd_model,
|
||||
ip_ckpt=model_path,
|
||||
lora_rank=rank,
|
||||
num_tokens=tokens,
|
||||
device=devices.device,
|
||||
torch_dtype=devices.dtype,
|
||||
)
|
||||
else:
|
||||
ip_model = IPAdapterFaceID(
|
||||
sd_pipe=shared.sd_model,
|
||||
ip_ckpt=model_path,
|
||||
lora_rank=rank,
|
||||
num_tokens=tokens,
|
||||
device=devices.device,
|
||||
torch_dtype=devices.dtype,
|
||||
)
|
||||
ip_model_name = model
|
||||
ip_model_tokens = tokens
|
||||
ip_model_rank = rank
|
||||
else:
|
||||
shared.log.debug(f'FaceID cached: model={model} file={ip_ckpt} tokens={tokens} rank={rank}')
|
||||
|
||||
# main generate dict
|
||||
ip_model_dict = {
|
||||
'prompt': p.all_prompts[0],
|
||||
'negative_prompt': p.all_negative_prompts[0],
|
||||
@@ -89,18 +159,34 @@ class Script(scripts.Script):
|
||||
'scale': scale,
|
||||
'guidance_scale': p.cfg_scale,
|
||||
'seed': int(p.all_seeds[0]),
|
||||
'faceid_embeds': None,
|
||||
'faceid_embeds': face_embeds.shape,
|
||||
}
|
||||
|
||||
# optional generate dict
|
||||
if shortcut is not None:
|
||||
ip_model_dict['shortcut'] = shortcut
|
||||
if 'Plus' in model:
|
||||
ip_model_dict['s_scale'] = structure
|
||||
ip_model_dict['face_image'] = face_image.shape
|
||||
shared.log.debug(f'FaceID args: {ip_model_dict}')
|
||||
ip_model_dict['faceid_embeds'] = embeds
|
||||
if 'Plus' in model:
|
||||
ip_model_dict['face_image'] = face_image
|
||||
ip_model_dict['faceid_embeds'] = face_embeds
|
||||
|
||||
# run generate
|
||||
images = ip_model.generate(**ip_model_dict)
|
||||
|
||||
ip_model = None
|
||||
if not cache:
|
||||
ip_model = None
|
||||
ip_model_name = None
|
||||
devices.torch_gc()
|
||||
|
||||
p.extra_generation_params["IP Adapter"] = f'{basename}:{scale}'
|
||||
for i, face in enumerate(faces):
|
||||
p.extra_generation_params[f"FaceID {i} score"] = f'{face.det_score:.2f}'
|
||||
p.extra_generation_params[f"FaceID {i} gender"] = "female" if face.gender==0 else "male"
|
||||
p.extra_generation_params[f"FaceID {i} age"] = face.age
|
||||
|
||||
processed = processing.Processed(
|
||||
p,
|
||||
images_list=images,
|
||||
@@ -110,5 +196,4 @@ class Script(scripts.Script):
|
||||
)
|
||||
processed.info = processed.infotext(p, 0)
|
||||
processed.infotexts = [processed.info]
|
||||
devices.torch_gc()
|
||||
return processed
|
||||
|
||||
+42
-25
@@ -15,20 +15,22 @@ from modules import scripts, processing, shared, devices
|
||||
|
||||
image_encoder = None
|
||||
image_encoder_type = None
|
||||
image_encoder_name = None
|
||||
loaded = None
|
||||
checkpoint = None
|
||||
base_repo = "h94/IP-Adapter"
|
||||
ADAPTERS = {
|
||||
'None': 'none',
|
||||
'Base': 'ip-adapter_sd15',
|
||||
'Light': 'ip-adapter_sd15_light',
|
||||
'Plus': 'ip-adapter-plus_sd15',
|
||||
'Plus Face': 'ip-adapter-plus-face_sd15',
|
||||
'Full face': 'ip-adapter-full-face_sd15',
|
||||
'Base SXDL': 'ip-adapter_sdxl',
|
||||
# 'models/ip-adapter_sd15_vit-G', # RuntimeError: mat1 and mat2 shapes cannot be multiplied (2x1024 and 1280x3072)
|
||||
# 'sdxl_models/ip-adapter_sdxl_vit-h',
|
||||
# 'sdxl_models/ip-adapter-plus_sdxl_vit-h',
|
||||
# 'sdxl_models/ip-adapter-plus-face_sdxl_vit-h',
|
||||
'Base': 'ip-adapter_sd15.safetensors',
|
||||
'Base ViT-G': 'ip-adapter_sd15_vit-G.safetensors',
|
||||
'Light': 'ip-adapter_sd15_light.safetensors',
|
||||
'Plus': 'ip-adapter-plus_sd15.safetensors',
|
||||
'Plus Face': 'ip-adapter-plus-face_sd15.safetensors',
|
||||
'Full Face': 'ip-adapter-full-face_sd15.safetensors',
|
||||
'Base SXDL': 'ip-adapter_sdxl.safetensors',
|
||||
'Base ViT-H SXDL': 'ip-adapter_sdxl_vit-h.safetensors',
|
||||
'Plus ViT-H SXDL': 'ip-adapter-plus_sdxl_vit-h.safetensors',
|
||||
'Plus Face ViT-H SXDL': 'ip-adapter-plus-face_sdxl_vit-h.safetensors',
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +50,9 @@ class Script(scripts.Script):
|
||||
image = gr.Image(image_mode='RGB', label='Image', source='upload', type='pil', width=512)
|
||||
return [adapter, scale, image]
|
||||
|
||||
def process(self, p: processing.StableDiffusionProcessing, adapter, scale, image): # pylint: disable=arguments-differ
|
||||
def process(self, p: processing.StableDiffusionProcessing, adapter_name, scale, image): # pylint: disable=arguments-differ
|
||||
# overrides
|
||||
adapter = ADAPTERS.get(adapter, None)
|
||||
adapter = ADAPTERS.get(adapter_name, None)
|
||||
if hasattr(p, 'ip_adapter_name'):
|
||||
adapter = p.ip_adapter_name
|
||||
if hasattr(p, 'ip_adapter_scale'):
|
||||
@@ -60,7 +62,7 @@ class Script(scripts.Script):
|
||||
if adapter is None:
|
||||
return
|
||||
# init code
|
||||
global loaded, checkpoint, image_encoder, image_encoder_type # pylint: disable=global-statement
|
||||
global loaded, checkpoint, image_encoder, image_encoder_type, image_encoder_name # pylint: disable=global-statement
|
||||
if shared.sd_model is None:
|
||||
return
|
||||
if shared.backend != shared.Backend.DIFFUSERS:
|
||||
@@ -80,25 +82,39 @@ class Script(scripts.Script):
|
||||
if not hasattr(shared.sd_model, 'load_ip_adapter'):
|
||||
shared.log.error(f'IP adapter: pipeline not supported: {shared.sd_model.__class__.__name__}')
|
||||
return
|
||||
if getattr(shared.sd_model, 'image_encoder', None) is None:
|
||||
if shared.sd_model_type == 'sd':
|
||||
subfolder = 'models/image_encoder'
|
||||
elif shared.sd_model_type == 'sdxl':
|
||||
subfolder = 'sdxl_models/image_encoder'
|
||||
else:
|
||||
shared.log.error(f'IP adapter: unsupported model type: {shared.sd_model_type}')
|
||||
return
|
||||
if image_encoder is None or image_encoder_type != shared.sd_model_type or checkpoint != shared.opts.sd_model_checkpoint:
|
||||
|
||||
# which clip to use
|
||||
if 'ViT' not in adapter_name:
|
||||
clip_repo = base_repo
|
||||
subfolder = 'models/image_encoder' if shared.sd_model_type == 'sd' else 'sdxl_models/image_encoder' # defaults per model
|
||||
elif 'ViT-H' in adapter_name:
|
||||
clip_repo = base_repo
|
||||
subfolder = 'models/image_encoder' # this is vit-h
|
||||
elif 'ViT-G' in adapter_name:
|
||||
clip_repo = base_repo
|
||||
subfolder = 'sdxl_models/image_encoder' # this is vit-g
|
||||
else:
|
||||
shared.log.error(f'IP adapter: unknown model type: {adapter_name}')
|
||||
return
|
||||
|
||||
# load image encoder used by ip adapter
|
||||
if getattr(shared.sd_model, 'image_encoder', None) is None or image_encoder_name != clip_repo + '/' + subfolder:
|
||||
if image_encoder is None or image_encoder_type != shared.sd_model_type or checkpoint != shared.opts.sd_model_checkpoint or image_encoder_name != clip_repo + '/' + subfolder:
|
||||
if shared.sd_model_type != 'sd' and shared.sd_model_type != 'sdxl':
|
||||
shared.log.error(f'IP adapter: unsupported model type: {shared.sd_model_type}')
|
||||
return
|
||||
try:
|
||||
from transformers import CLIPVisionModelWithProjection
|
||||
image_encoder = CLIPVisionModelWithProjection.from_pretrained("h94/IP-Adapter", subfolder=subfolder, torch_dtype=devices.dtype, cache_dir=shared.opts.diffusers_dir, use_safetensors=True).to(devices.device)
|
||||
shared.log.debug(f'IP adapter: load image encoder: {clip_repo}/{subfolder}')
|
||||
image_encoder = CLIPVisionModelWithProjection.from_pretrained(clip_repo, subfolder=subfolder, torch_dtype=devices.dtype, cache_dir=shared.opts.diffusers_dir, use_safetensors=True).to(devices.device)
|
||||
image_encoder_type = shared.sd_model_type
|
||||
image_encoder_name = clip_repo + '/' + subfolder
|
||||
except Exception as e:
|
||||
shared.log.error(f'IP adapter: failed to load image encoder: {e}')
|
||||
return
|
||||
|
||||
# main code
|
||||
subfolder = 'models' if 'sd15' in adapter else 'sdxl_models'
|
||||
# subfolder = 'models' if 'sd15' in adapter else 'sdxl_models'
|
||||
if adapter != loaded or getattr(shared.sd_model.unet.config, 'encoder_hid_dim_type', None) is None or checkpoint != shared.opts.sd_model_checkpoint:
|
||||
t0 = time.time()
|
||||
if loaded is not None:
|
||||
@@ -107,7 +123,8 @@ class Script(scripts.Script):
|
||||
else:
|
||||
shared.log.debug('IP adapter: load attention processor')
|
||||
shared.sd_model.image_encoder = image_encoder
|
||||
shared.sd_model.load_ip_adapter("h94/IP-Adapter", subfolder=subfolder, weight_name=f'{adapter}.safetensors')
|
||||
subfolder = 'models' if shared.sd_model_type == 'sd' else 'sdxl_models'
|
||||
shared.sd_model.load_ip_adapter(base_repo, subfolder=subfolder, weight_name=adapter)
|
||||
t1 = time.time()
|
||||
shared.log.info(f'IP adapter load: adapter="{adapter}" scale={scale} image={image} time={t1-t0:.2f}')
|
||||
loaded = adapter
|
||||
|
||||
Reference in New Issue
Block a user