diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c21ff32..beecb380e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log for SD.Next -## Update for 2024-05-29 +## Update for 2024-05-30 - fix textual inversion loading - fix gallery mtime display @@ -12,6 +12,7 @@ - improve xformers installer - improve ultralytics installer - improve triton installer +- improve insightface installer ## Update for 2024-05-28 diff --git a/installer.py b/installer.py index da955d49f..4c268cac8 100644 --- a/installer.py +++ b/installer.py @@ -1,3 +1,4 @@ +from functools import lru_cache import os import sys import json @@ -171,6 +172,7 @@ def print_profile(profiler: cProfile.Profile, msg: str): # check if package is installed +@lru_cache() def installed(package, friendly: str = None, reload = False, quiet = False): ok = True try: @@ -201,12 +203,12 @@ def installed(package, friendly: str = None, reload = False, quiet = False): # log.debug(f"Package version found: {p[0]} {package_version}") if len(p) > 1: exact = package_version == p[1] - ok = ok and (exact or args.experimental) if not exact and not quiet: if args.experimental: log.warning(f"Package allowing experimental: {p[0]} {package_version} required {p[1]}") else: log.warning(f"Package version mismatch: {p[0]} {package_version} required {p[1]}") + ok = ok and (exact or args.experimental) else: if not quiet: log.debug(f"Package not found: {p[0]}") @@ -227,6 +229,7 @@ def uninstall(package, quiet = False): return res +@lru_cache() def pip(arg: str, ignore: bool = False, quiet: bool = False): arg = arg.replace('>=', '==') if not quiet: @@ -248,12 +251,13 @@ def pip(arg: str, ignore: bool = False, quiet: bool = False): # install package using pip if not already installed -def install(package, friendly: str = None, ignore: bool = False): +@lru_cache() +def install(package, friendly: str = None, ignore: bool = False, reinstall: bool = False): res = '' if args.reinstall or args.upgrade: global quick_allowed # pylint: disable=global-statement quick_allowed = False - if args.reinstall or not installed(package, friendly, quiet=True): + if args.reinstall or reinstall or not installed(package, friendly, quiet=False): res = pip(f"install --upgrade {package}", ignore=ignore) try: import imp # pylint: disable=deprecated-module @@ -264,6 +268,7 @@ def install(package, friendly: str = None, ignore: bool = False): # execute git command +@lru_cache() def git(arg: str, folder: str = None, ignore: bool = False): if args.skip_git: return '' @@ -858,7 +863,7 @@ def install_requirements(): with open('requirements.txt', 'r', encoding='utf8') as f: lines = [line.strip() for line in f.readlines() if line.strip() != '' and not line.startswith('#') and line is not None] for line in lines: - install(line) + _res = install(line) if args.profile: print_profile(pr, 'Requirements') diff --git a/modules/face/faceid.py b/modules/face/faceid.py index 283594f54..4bfa9a94b 100644 --- a/modules/face/faceid.py +++ b/modules/face/faceid.py @@ -75,7 +75,6 @@ def face_id( script_callbacks.before_process_callback(p) with context_hypertile_vae(p), context_hypertile_unet(p), devices.inference_context(): - p.init(p.all_prompts, p.all_seeds, p.all_subseeds) ip_ckpt = FACEID_MODELS[model] folder, filename = os.path.split(ip_ckpt) basename, _ext = os.path.splitext(filename) @@ -83,23 +82,13 @@ def face_id( if model_path is None: shared.log.error(f"FaceID download failed: model={model} file={ip_ckpt}") return None - 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, - ) if faceid_model_weights is None or faceid_model_name != model or not cache: shared.log.debug(f"FaceID load: model={model} file={ip_ckpt}") faceid_model_weights = torch.load(model_path, map_location="cpu") else: shared.log.debug(f"FaceID cached: model={model} file={ip_ckpt}") - if "XL Plus" in model: + if "XL Plus" in model and shared.sd_model_type == 'sd': image_encoder_path = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" original_load_ip_adapter = IPAdapterFaceIDPlusXL.load_ip_adapter IPAdapterFaceIDPlusXL.load_ip_adapter = hijack_load_ip_adapter @@ -112,7 +101,7 @@ def face_id( device=devices.device, torch_dtype=devices.dtype, ) - elif "XL" in model: + elif "XL" in model and shared.sd_model_type == 'sdxl': original_load_ip_adapter = IPAdapterFaceIDXL.load_ip_adapter IPAdapterFaceIDXL.load_ip_adapter = hijack_load_ip_adapter faceid_model = IPAdapterFaceIDXL( @@ -123,7 +112,7 @@ def face_id( device=devices.device, torch_dtype=devices.dtype, ) - elif "Plus" in model: + elif "Plus" in model and shared.sd_model_type == 'sd': original_load_ip_adapter = IPAdapterFaceIDPlus.load_ip_adapter IPAdapterFaceIDPlus.load_ip_adapter = hijack_load_ip_adapter image_encoder_path = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" @@ -136,7 +125,7 @@ def face_id( device=devices.device, torch_dtype=devices.dtype, ) - elif "Portrait" in model: + elif "Portrait" in model and shared.sd_model_type == 'sd': original_load_ip_adapter = IPAdapterFaceIDPortrait.load_ip_adapter IPAdapterFaceIDPortrait.load_ip_adapter = hijack_load_ip_adapter faceid_model = IPAdapterFaceIDPortrait( @@ -147,7 +136,7 @@ def face_id( device=devices.device, torch_dtype=devices.dtype, ) - else: + elif "Base" in model and shared.sd_model_type == 'sd': original_load_ip_adapter = IPAdapterFaceID.load_ip_adapter IPAdapterFaceID.load_ip_adapter = hijack_load_ip_adapter faceid_model = IPAdapterFaceID( @@ -158,11 +147,26 @@ def face_id( device=devices.device, torch_dtype=devices.dtype, ) + else: + shared.log.error(f'FaceID model not supported: model="{model}" class={shared.sd_model.__class__.__name__}') + return None + + 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 = "v2" in model faceid_model_name = model face_embeds = [] face_images = [] + for i, source_image in enumerate(source_images): np_image = cv2.cvtColor(np.array(source_image), cv2.COLOR_RGB2BGR) faces = app.get(np_image) @@ -201,19 +205,23 @@ def face_id( faceid_model.set_scale(scale) extra_network_data = None - for i in range(p.n_iter): - p.iteration = i - p.prompts = p.all_prompts[i * p.batch_size:(i + 1) * p.batch_size] - p.negative_prompts = p.all_negative_prompts[i * p.batch_size:(i + 1) * p.batch_size] + processing.process_init(p) + p.init(p.all_prompts, p.all_seeds, p.all_subseeds) + for n in range(p.n_iter): + p.iteration = n + 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] p.prompts, extra_network_data = extra_networks.parse_prompts(p.prompts) - p.seeds = p.all_seeds[i * p.batch_size:(i + 1) * p.batch_size] + if not p.disable_extra_networks: with devices.autocast(): extra_networks.activate(p, extra_network_data) ip_model_dict.update({ - "prompt": p.prompts, - "negative_prompt": p.negative_prompts, - "seed": int(p.seeds[0]), + "prompt": p.prompts[0], + "negative_prompt": p.negative_prompts[0], + "seed": p.seeds[0], }) debug(f"FaceID: {ip_model_dict}") res = faceid_model.generate(**ip_model_dict) diff --git a/modules/face/insightface.py b/modules/face/insightface.py index 655dd72e6..3eb7171bf 100644 --- a/modules/face/insightface.py +++ b/modules/face/insightface.py @@ -9,14 +9,15 @@ instightface_mp = None def get_app(mp_name): global insightface_app, instightface_mp # pylint: disable=global-statement - 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=False) + + from installer import install, installed + if not installed('insightface', reload=False, quiet=True): + install('insightface', 'insightface', ignore=False) + install('albumentations==1.4.3', 'albumentations', ignore=False, reinstall=True) + install('pydantic==1.10.15', 'pydantic', ignore=False, reinstall=True) + if not installed('ip_adapter', reload=False, quiet=True): + install('git+https://github.com/tencent-ailab/IP-Adapter.git', 'ip_adapter', ignore=False) + if insightface_app is None or mp_name != instightface_mp: from insightface.app import FaceAnalysis import huggingface_hub as hf diff --git a/modules/face/instantid.py b/modules/face/instantid.py index 8c2d91002..1835e8e80 100644 --- a/modules/face/instantid.py +++ b/modules/face/instantid.py @@ -71,8 +71,8 @@ def instant_id(p: processing.StableDiffusionProcessing, app, source_images, stre p.task_args['controlnet_conditioning_scale'] = float(conditioning) p.task_args['ip_adapter_scale'] = float(strength) shared.log.debug(f"InstantID args: {p.task_args}") - p.task_args['prompt'] = p.all_prompts[0] # override all logic - p.task_args['negative_prompt'] = p.all_negative_prompts[0] + p.task_args['prompt'] = p.all_prompts[0] if p.all_prompts is not None else p.prompt + p.task_args['negative_prompt'] = p.all_negative_prompts[0] if p.all_negative_prompts is not None else p.negative_prompt p.task_args['image_embeds'] = face_embeds[0] # overwrite placeholder # run processing diff --git a/modules/face/photomaker.py b/modules/face/photomaker.py index 9e86316b0..219b6497d 100644 --- a/modules/face/photomaker.py +++ b/modules/face/photomaker.py @@ -49,7 +49,7 @@ def photo_maker(p: processing.StableDiffusionProcessing, input_images, trigger, shared.opts.data['prompt_attention'] = 'Fixed attention' # otherwise need to deal with class_tokens_mask p.task_args['input_id_images'] = input_images p.task_args['start_merge_step'] = int(start * p.steps) - p.task_args['prompt'] = p.all_prompts[0] # override all logic + p.task_args['prompt'] = p.all_prompts[0] if p.all_prompts is not None else p.prompt photomaker_path = hf.hf_hub_download(repo_id="TencentARC/PhotoMaker", filename="photomaker-v1.bin", repo_type="model", cache_dir=shared.opts.diffusers_dir) shared.log.debug(f'PhotoMaker: model={photomaker_path} images={len(input_images)} trigger={trigger} args={p.task_args}') diff --git a/modules/processing.py b/modules/processing.py index ae81ce833..8a44d9477 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -223,7 +223,7 @@ def process_init(p: StableDiffusionProcessing): if p.all_seeds is None: reset_prompts = True if type(seed) == list: - p.all_seeds = seed + p.all_seeds = [int(s) for s in seed] else: if shared.opts.sequential_seed: p.all_seeds = [int(seed) + (x if p.subseed_strength == 0 else 0) for x in range(len(p.all_prompts))] diff --git a/modules/ui_gallery.py b/modules/ui_gallery.py index eead66c8d..a1f317caa 100644 --- a/modules/ui_gallery.py +++ b/modules/ui_gallery.py @@ -1,10 +1,10 @@ import os from datetime import datetime +from urllib.parse import unquote import gradio as gr from PIL import Image from modules import shared, ui_symbols, ui_common, images, ui_control_helpers from modules.ui_components import ToolButton -from urllib.parse import unquote def read_media(fn): fn = unquote(fn).replace('%3A', ':')