diff --git a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 index 50f5f8894..2473d6b00 160000 --- a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 +++ b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 @@ -1 +1 @@ -Subproject commit 50f5f88944427a1f7e1321917790dbd9a5ddbed8 +Subproject commit 2473d6b005a516fb4bd51b331abd04b0289fdf07 diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index a83a26060..0d1c252ca 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit a83a260605fe3da01bc15993c6a7f7d1aa82865d +Subproject commit 0d1c252cad9c37a75e839d52f9ea8207adb8aa46 diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser index 7da8aec62..c61fae964 160000 --- a/extensions-builtin/stable-diffusion-webui-images-browser +++ b/extensions-builtin/stable-diffusion-webui-images-browser @@ -1 +1 @@ -Subproject commit 7da8aec62bc263acd47d76ec9cabdb658b01fc91 +Subproject commit c61fae964ac94bc369fd0e346805e3e2885c69b4 diff --git a/javascript/ui.js b/javascript/ui.js index 7035f7497..9313c75c6 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -1,6 +1,7 @@ /* global gradioApp, onUiUpdate, opts */ window.opts = {}; +window.localization = {}; let tabSelected = ''; function set_theme(theme) { @@ -192,6 +193,22 @@ function recalculate_prompts_inpaint(...args) { return args_to_array(args); } +function register_drag_drop() { + const qs = gradioApp().getElementById('quicksettings'); + if (!qs) return; + qs.addEventListener('dragover', (evt) => { + evt.preventDefault(); + evt.dataTransfer.dropEffect = 'copy'; + }); + qs.addEventListener('drop', (evt) => { + evt.preventDefault(); + evt.dataTransfer.dropEffect = 'copy'; + for (const f of evt.dataTransfer.files) { + console.log('QuickSettingsDrop', f); + } + }); +} + onUiUpdate(() => { sort_ui_elements(); if (Object.keys(opts).length !== 0) return; @@ -202,6 +219,7 @@ onUiUpdate(() => { const jsdata = textarea.value; opts = JSON.parse(jsdata); executeCallbacks(optionsChangedCallbacks); + register_drag_drop(); Object.defineProperty(textarea, 'value', { set(newValue) { diff --git a/modules/call_queue.py b/modules/call_queue.py index 3cfe3c50d..0de523f66 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -65,10 +65,7 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False): if shared.cmd_opts.profile: pr.disable() s = io.StringIO() - ps = pstats.Stats(pr, stream=s) - ps.sort_stats(pstats.SortKey.CUMULATIVE) - # ps.strip_dirs() - ps.print_stats(15) + pstats.Stats(pr, stream=s).sort_stats(pstats.SortKey.CUMULATIVE).print_stats(15) print('Profile Exec:', s.getvalue()) except Exception as e: errors.display(e, 'gradio call') diff --git a/modules/errors.py b/modules/errors.py index 6b1efabc7..067cccdb7 100644 --- a/modules/errors.py +++ b/modules/errors.py @@ -34,7 +34,7 @@ def print_error_explanation(message): def display(e: Exception, task, suppress=[]): log.error(f"{task or 'error'}: {type(e).__name__}") - console.print_exception(show_locals=False, max_frames=2, extra_lines=1, suppress=suppress, theme="ansi_dark", word_wrap=False, width=min([console.width, 200])) + console.print_exception(show_locals=False, max_frames=5, extra_lines=1, suppress=suppress, theme="ansi_dark", word_wrap=False, width=min([console.width, 200])) def display_once(e: Exception, task): diff --git a/modules/images.py b/modules/images.py index 3065205bb..40c369652 100644 --- a/modules/images.py +++ b/modules/images.py @@ -642,18 +642,27 @@ Steps: {json_info["steps"]}, Sampler: {sampler}, CFG scale: {json_info["scale"]} def image_data(data): import gradio as gr + if data is None: + return gr.update(), None + err1 = None + err2 = None try: image = Image.open(io.BytesIO(data)) + errors.log.debug(f'Decoded object: image={image}') textinfo, _ = read_info_from_image(image) return textinfo, None - except Exception: - pass + except Exception as e: + err1 = e try: + if len(data) > 1024 * 10: + errors.log.warning(f'Error decoding object: data too long: {len(data)}') + return gr.update(), None text = data.decode('utf8') - assert len(text) < 10000 + errors.log.debug(f'Decoded object: size={len(text)}') return text, None - except Exception: - pass + except Exception as e: + err2 = e + errors.log.error(f'Error decoding object: {err1 or err2}') return gr.update(), None diff --git a/modules/img2img.py b/modules/img2img.py index 9433fd899..9a6370760 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -70,6 +70,9 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s if shared.sd_model is None: shared.log.warning('Model not loaded') return + if init_img is None: + shared.log.warning('Init image not set') + return shared.log.debug(f'img2img: id_task={id_task}|mode={mode}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|init_img={init_img}|sketch={sketch}|init_img_with_mask={init_img_with_mask}|inpaint_color_sketch={inpaint_color_sketch}|inpaint_color_sketch_orig={inpaint_color_sketch_orig}|init_img_inpaint={init_img_inpaint}|init_mask_inpaint={init_mask_inpaint}|steps={steps}|sampler_index={sampler_index}|mask_blur={mask_blur}|mask_alpha={mask_alpha}|inpainting_fill={inpainting_fill}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|image_cfg_scale={image_cfg_scale}|clip_skip={clip_skip}|denoising_strength={denoising_strength}|seed={seed}|subseed{subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}|seed_enable_extras={seed_enable_extras}|selected_scale_tab={selected_scale_tab}|height={height}|width={width}|scale_by={scale_by}|resize_mode={resize_mode}|inpaint_full_res={inpaint_full_res}|inpaint_full_res_padding={inpaint_full_res_padding}|inpainting_mask_invert={inpainting_mask_invert}|img2img_batch_input_dir={img2img_batch_input_dir}|img2img_batch_output_dir={img2img_batch_output_dir}|img2img_batch_inpaint_mask_dir={img2img_batch_inpaint_mask_dir}|override_settings_texts={override_settings_texts}|args={args}') if sampler_index is None: diff --git a/modules/modelloader.py b/modules/modelloader.py index 09a3a3f3e..fc8dc7ab4 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -8,7 +8,7 @@ from modules.upscaler import Upscaler, UpscalerLanczos, UpscalerNearest, Upscale from modules.paths import script_path, models_path -def load_models(model_path: str, model_url: str = None, command_path: str = None, ext_filter=None, download_name=None, ext_blacklist=None) -> list: +def load_models(model_path: str, model_url: str = None, command_path: str = None, ext_filter=None, download_name=None, ext_blacklist=None, diffusors=False) -> list: """ A one-and done loader to try finding the desired models in specified directories. @@ -19,32 +19,45 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None @param ext_filter: An optional list of filename extensions to filter by @return: A list of paths containing the desired model(s) """ - output = [] - try: - places = [] - places.append(model_path) - if command_path is not None and command_path != model_path and os.path.isdir(command_path): - places.append(command_path) - for place in places: - for full_path in shared.walk_files(place, allowed_extensions=ext_filter): - if os.path.islink(full_path) and not os.path.exists(full_path): - print(f"Skipping broken symlink: {full_path}") - continue - if ext_blacklist is not None and any([full_path.endswith(x) for x in ext_blacklist]): - continue - if full_path not in output: - output.append(full_path) - if model_url is not None and len(output) == 0: - if download_name is not None: - from basicsr.utils.download_util import load_file_from_url - dl = load_file_from_url(model_url, model_path, True, download_name) - output.append(dl) - else: - output.append(model_url) - except Exception: - pass + places = [] + places.append(model_path) + if command_path is not None and command_path != model_path and os.path.isdir(command_path): + places.append(command_path) - return output + def get_checkpoints(): + output = [] + try: + for place in places: + for full_path in shared.walk_files(place, allowed_extensions=ext_filter): + if os.path.islink(full_path) and not os.path.exists(full_path): + print(f"Skipping broken symlink: {full_path}") + continue + if ext_blacklist is not None and any([full_path.endswith(x) for x in ext_blacklist]): + continue + if full_path not in output: + output.append(full_path) + if model_url is not None and len(output) == 0: + if download_name is not None: + from basicsr.utils.download_util import load_file_from_url + dl = load_file_from_url(model_url, model_path, True, download_name) + output.append(dl) + else: + output.append(model_url) + except Exception: + pass + return output + + def get_diffusors(): + output = [] + for place in places: + output = os.listdir(place) + output = [os.path.join(place, x) for x in output] + return output + + if not diffusors: + return get_checkpoints() + else: + return get_diffusors() def friendly_name(file: str): diff --git a/modules/processing.py b/modules/processing.py index 7ba5f6f20..f46eb65fe 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -3,11 +3,13 @@ import math import os import hashlib import random +from contextlib import nullcontext from typing import Any, Dict, List import torch import numpy as np from PIL import Image, ImageFilter, ImageOps import cv2 +import tomesd from skimage import exposure from ldm.data.util import AddMiDaS from ldm.models.diffusion.ddpm import LatentDepth2ImageDiffusion @@ -25,7 +27,7 @@ import modules.images as images import modules.styles import modules.sd_models as sd_models import modules.sd_vae as sd_vae -import tomesd # pylint: disable=wrong-import-order + opt_C = 4 opt_f = 8 @@ -218,6 +220,8 @@ class StableDiffusionProcessing: source_image = devices.cond_cast_float(source_image) # HACK: Using introspection as the Depth2Image model doesn't appear to uniquely # identify itself with a field common to all models. The conditioning_key is also hybrid. + if opts.sd_backend == 'Diffusers': # TODO: img2img_image_conditioning + return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1) if isinstance(self.sd_model, LatentDepth2ImageDiffusion): return self.depth2img_image_conditioning(source_image) if self.sd_model.cond_stage_key == "edit": @@ -456,9 +460,19 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip() +def print_profile(profile, msg: str): + try: + from rich import print # pylint: disable=redefined-builtin + except: + pass + lines = profile.key_averages().table(sort_by="cuda_time_total", row_limit=20) + lines = lines.split('\n') + lines = [l for l in lines if '/profiler' not in l] + print(f'Profile {msg}:', '\n'.join(lines)) + + def process_images(p: StableDiffusionProcessing) -> Processed: stored_opts = {k: opts.data[k] for k in p.override_settings.keys()} - try: # if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint if p.override_settings.get('sd_model_checkpoint', None) is not None and sd_models.checkpoint_aliases.get(p.override_settings.get('sd_model_checkpoint')) is None: @@ -471,28 +485,24 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if k == 'sd_vae': sd_vae.reload_vae_weights() - """ - import torch.profiler - with torch.profiler.profile(activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA], record_shapes=True, with_modules=True) as prof: - with torch.profiler.record_function("process_images"): - res = process_images_inner(p) - print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=15)) - """ - if (opts.token_merging or cmd_opts.token_merging) and not opts.token_merging_hr_only: sd_models.apply_token_merging(sd_model=p.sd_model, hr=False) log.debug('Token merging applied') - res = process_images_inner(p) - + if cmd_opts.profile: + import torch.profiler # pylint: disable=redefined-outer-name + # activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA] + with torch.profiler.profile(profile_memory=True, with_modules=True) as prof: + with torch.profiler.record_function("process_images"): + res = process_images_inner(p) + print_profile(prof, 'process_images') + else: + res = process_images_inner(p) finally: - # undo model optimizations made by tomesd if opts.token_merging or cmd_opts.token_merging: tomesd.remove_patch(p.sd_model) log.debug('Token merging model optimizations removed') - - # restore opts to original state - if p.override_settings_restore_afterwards: + if p.override_settings_restore_afterwards: # restore opts to original state for k, v in stored_opts.items(): setattr(opts, k, v) if k == 'sd_model_checkpoint': @@ -500,7 +510,6 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if k == 'sd_vae': sd_vae.reload_vae_weights() - return res @@ -513,8 +522,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: assert p.prompt is not None seed = get_fixed_seed(p.seed) subseed = get_fixed_seed(p.subseed) - modules.sd_hijack.model_hijack.apply_circular(p.tiling) - modules.sd_hijack.model_hijack.clear_comments() + if opts.sd_backend == 'Original': + modules.sd_hijack.model_hijack.apply_circular(p.tiling) + modules.sd_hijack.model_hijack.clear_comments() comments = {} if type(p.prompt) == list: p.all_prompts = [shared.prompt_styles.apply_styles_to_prompt(x, p.styles) for x in p.prompt] @@ -563,10 +573,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: cache[0] = (required_prompts, steps) return cache[1] - with torch.no_grad(), p.sd_model.ema_scope(): + ema_scope_context = p.sd_model.ema_scope if opts.sd_backend == 'Original' else nullcontext + with torch.no_grad(), ema_scope_context(): with devices.autocast(): p.init(p.all_prompts, p.all_seeds, p.all_subseeds) - if shared.opts.live_previews_enable and opts.show_progress_type == "Approx NN": + if shared.opts.live_previews_enable and opts.show_progress_type == "Approx NN" and opts.sd_backend == 'Original': sd_vae_approx.model() if state.job_count == -1: state.job_count = p.n_iter @@ -604,42 +615,67 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: step_multiplier = 2 if sd_samplers.all_samplers_map.get(p.sampler_name).aliases[0] in ['k_dpmpp_2s_a', 'k_dpmpp_2s_a_ka', 'k_dpmpp_sde', 'k_dpmpp_sde_ka', 'k_dpm_2', 'k_dpm_2_a', 'k_heun'] else 1 except: pass - uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, negative_prompts, p.steps * step_multiplier, cached_uc) - c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, prompts, p.steps * step_multiplier, cached_c) - if len(model_hijack.comments) > 0: - for comment in model_hijack.comments: - comments[comment] = 1 if p.n_iter > 1: shared.state.job = f"Batch {n+1} out of {p.n_iter}" - with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast(): - samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, prompts=prompts) - x_samples_ddim = [decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))] - try: - for x in x_samples_ddim: - devices.test_for_nans(x, "vae") - except devices.NansException as e: - if not shared.opts.no_half and not shared.opts.no_half_vae and shared.cmd_opts.rollback_vae: - log.warning('Tensor with all NaNs was produced in VAE') - devices.dtype_vae = torch.bfloat16 - vae_file, vae_source = sd_vae.resolve_vae(p.sd_model.sd_model_checkpoint) - sd_vae.load_vae(p.sd_model, vae_file, vae_source) - x_samples_ddim = [decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))] + + if opts.sd_backend == 'Original': + uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, negative_prompts, p.steps * step_multiplier, cached_uc) + c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, prompts, p.steps * step_multiplier, cached_c) + if len(model_hijack.comments) > 0: + for comment in model_hijack.comments: + comments[comment] = 1 + with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast(): + samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, prompts=prompts) + x_samples_ddim = [decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))] + try: for x in x_samples_ddim: devices.test_for_nans(x, "vae") - else: - raise e - x_samples_ddim = torch.stack(x_samples_ddim).float() - x_samples_ddim = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) - del samples_ddim - if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: - lowvram.send_everything_to_cpu() - devices.torch_gc() - if p.scripts is not None: - p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n) + except devices.NansException as e: + if not shared.opts.no_half and not shared.opts.no_half_vae and shared.cmd_opts.rollback_vae: + log.warning('Tensor with all NaNs was produced in VAE') + devices.dtype_vae = torch.bfloat16 + vae_file, vae_source = sd_vae.resolve_vae(p.sd_model.sd_model_checkpoint) + sd_vae.load_vae(p.sd_model, vae_file, vae_source) + x_samples_ddim = [decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))] + for x in x_samples_ddim: + devices.test_for_nans(x, "vae") + else: + raise e + x_samples_ddim = torch.stack(x_samples_ddim).float() + x_samples_ddim = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) + del samples_ddim + if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: + lowvram.send_everything_to_cpu() + devices.torch_gc() + if p.scripts is not None: + p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n) + else: # TODO Diffusers + generator = [torch.Generator(device="cpu").manual_seed(s) for s in seeds] + if shared.sd_model.scheduler.name != p.sampler_name: + sampler = sd_samplers.all_samplers_map.get(p.sampler_name, None) + if sampler is None: + sampler = sd_samplers.all_samplers_map.get("UniPC") + scheduler = sampler.constructor(shared.sd_model.sd_checkpoint_info.filename) + shared.sd_model.scheduler = scheduler.sampler + output = shared.sd_model( + prompt=prompts, + negative_prompt=negative_prompts, + num_inference_steps=p.steps, + guidance_scale=p.cfg_scale, + height=p.height, + width=p.width, + generator=generator, + output_type="np", + ) + x_samples_ddim = output.images + for i, x_sample in enumerate(x_samples_ddim): p.batch_index = i - x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2) - x_sample = x_sample.astype(np.uint8) + if opts.sd_backend == 'Original': + x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2) + x_sample = x_sample.astype(np.uint8) + else: + x_sample = (255. * x_sample).astype(np.uint8) if p.restore_faces: if opts.save and not p.do_not_save_samples and opts.save_images_before_face_restoration: orig = p.restore_faces @@ -891,7 +927,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.init_images = init_images self.resize_mode: int = resize_mode self.denoising_strength: float = denoising_strength - self.image_cfg_scale: float = image_cfg_scale if shared.sd_model.cond_stage_key == "edit" else None + self.image_cfg_scale: float = image_cfg_scale if (shared.sd_model is not None) and hasattr(shared.sd_model, 'cond_stage_key') and (shared.sd_model.cond_stage_key == "edit") else None self.init_latent = None self.image_mask = mask self.latent_mask = None diff --git a/modules/script_loading.py b/modules/script_loading.py index 2bc9b6fda..6827515fc 100644 --- a/modules/script_loading.py +++ b/modules/script_loading.py @@ -12,10 +12,7 @@ def load_module(path, detailed=False): try: module_spec.loader.exec_module(module) except Exception as e: - if detailed: - errors.display(e, f'Module load: {path}') - else: - errors.log.error(f'Module load: {path}') + errors.display(e, f'Module load: {path}') return module @@ -31,11 +28,8 @@ def preload_extensions(extensions_dir, parser, detailed=False): if not os.path.isfile(preload_script): continue try: - module = load_module(preload_script) + module = load_module(preload_script, detailed) if hasattr(module, 'preload'): module.preload(parser) except Exception as e: - if detailed: - errors.display(e, f'Extension preload: {preload_script}') - else: - errors.log.error(f'Extension preload: {preload_script}') + errors.display(e, f'Extension preload: {preload_script}') diff --git a/modules/sd_hijack_clip.py b/modules/sd_hijack_clip.py index 4d79cd3fa..aa38c3f77 100644 --- a/modules/sd_hijack_clip.py +++ b/modules/sd_hijack_clip.py @@ -136,7 +136,6 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module): position += embedding_length_in_tokens if len(chunk.tokens) > 0 or len(chunks) == 0: next_chunk(is_last=True) - # print('CHUNKS', [vars(c) for c in chunks]) # TODO return chunks, token_count def process_texts(self, texts): diff --git a/modules/sd_models.py b/modules/sd_models.py index cc460fb52..bfbb7eb82 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -27,7 +27,7 @@ checkpoints_loaded = collections.OrderedDict() skip_next_load = False -class CheckpointInfo: +class CheckpointInfo: # TODO Diffusers def __init__(self, filename): self.filename = filename abspath = os.path.abspath(filename) @@ -42,8 +42,13 @@ class CheckpointInfo: self.name = name self.name_for_extra = os.path.splitext(os.path.basename(filename))[0] self.model_name = os.path.splitext(name.replace("/", "_").replace("\\", "_"))[0] - self.hash = model_hash(filename) - self.sha256 = hashes.sha256_from_cache(self.filename, f"checkpoint/{name}") + if shared.opts.sd_backend == 'Original': + self.hash = model_hash(self.filename) + self.sha256 = hashes.sha256_from_cache(self.filename, f"checkpoint/{name}") + else: # TODO Diffusers calculate hash + # sd_model.unet.config._name_or_path.split("/")[-2] + self.hash = 'ABCDEFGH' + self.sha256 = 'ABCDEFGH' self.shorthash = self.sha256[0:10] if self.sha256 else None self.title = name if self.shorthash is None else f'{name} [{self.shorthash}]' self.ids = [self.hash, self.model_name, self.title, name, f'{name} [{self.hash}]'] + ([self.shorthash, self.sha256, f'{self.name} [{self.shorthash}]'] if self.shorthash else []) @@ -99,9 +104,12 @@ def checkpoint_tiles(): def list_models(): checkpoints_list.clear() checkpoint_aliases.clear() - model_list = modelloader.load_models(model_path=os.path.join(models_path, 'Stable-diffusion'), model_url=None, command_path=shared.opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name=None, ext_blacklist=[".vae.ckpt", ".vae.safetensors"]) + if shared.opts.sd_backend == 'Original': + model_list = modelloader.load_models(model_path=os.path.join(models_path, 'Stable-diffusion'), model_url=None, command_path=shared.opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name=None, ext_blacklist=[".vae.ckpt", ".vae.safetensors"]) + else: + model_list = modelloader.load_models(model_path=os.path.join(models_path, 'Diffusers'), model_url=None, command_path=shared.opts.diffusers_dir, ext_filter=[".ckpt", ".safetensors"], download_name=None, ext_blacklist=[".vae.ckpt", ".vae.safetensors"]) if shared.cmd_opts.ckpt is not None: - if not os.path.exists(shared.cmd_opts.ckpt): + if not os.path.exists(shared.cmd_opts.ckpt) and shared.opts.sd_backend == 'Original': if shared.cmd_opts.ckpt.lower() != "none": shared.log.warning(f"Requested checkpoint not found: {shared.cmd_opts.ckpt}") else: @@ -363,7 +371,12 @@ class SdModelData: if self.sd_model is None: with self.lock: try: - load_model() + if shared.opts.sd_backend == 'Original': + load_model() + elif shared.opts.sd_backend == 'Diffusers': + load_diffusers() + else: + shared.log.error(f"Unknown Stable Diffusion backend: {shared.opts.sd_backend}") except Exception as e: shared.log.error("Failed to load stable diffusion model") errors.display(e, "loading stable diffusion model") @@ -377,6 +390,39 @@ class SdModelData: model_data = SdModelData() +def load_diffusers(checkpoint_info=None, already_loaded_state_dict=None, timer=None): + if timer is None: + timer = Timer() + import diffusers + timer.record("diffusers") + diffusor_config = { + "force_download": False, + "safety_checker": None, + "resume_download": True, + "low_cpu_mem_usage": True, + "use_safetensors": True, + "cache_dir": shared.opts.diffusers_dir, + "torch_dtype": devices.dtype, + } + shared.log.warning("Using experimental Diffusers backend for Stable Diffusion") + if shared.opts.data['sd_model_checkpoint'] == 'model.ckpt': + shared.opts.data['sd_model_checkpoint'] = "runwayml/stable-diffusion-v1-5" + sd_model = None + try: + checkpoint_info = checkpoint_info or select_checkpoint() + scheduler = diffusers.UniPCMultistepScheduler.from_pretrained(checkpoint_info.filename, subfolder="scheduler") + scheduler.name = 'UniPC' + sd_model = diffusers.DiffusionPipeline.from_pretrained(checkpoint_info.filename, scheduler=scheduler, **diffusor_config) + sd_model.to(devices.device) + sd_model.sd_checkpoint_info = checkpoint_info + sd_model.sd_model_hash = checkpoint_info.hash + except Exception as e: + shared.log.error("Failed to load diffusers model") + errors.display(e, "loading Diffusers model") + shared.sd_model = sd_model + timer.record("load") + + def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None): from modules import lowvram, sd_hijack checkpoint_info = checkpoint_info or select_checkpoint() diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index bc82371c8..2a13f5030 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -1,10 +1,16 @@ -from modules import sd_samplers_compvis, sd_samplers_kdiffusion, shared +from modules import sd_samplers_compvis, sd_samplers_kdiffusion, sd_samplers_diffusors, shared from modules.sd_samplers_common import samples_to_image_grid, sample_to_image # pylint: disable=unused-import +from modules.shared import opts -all_samplers = [ - *sd_samplers_kdiffusion.samplers_data_k_diffusion, - *sd_samplers_compvis.samplers_data_compvis, -] +if opts.sd_backend == 'Original': + all_samplers = [ + *sd_samplers_kdiffusion.samplers_data_k_diffusion, + *sd_samplers_compvis.samplers_data_compvis, + ] +else: + all_samplers = [ + *sd_samplers_diffusors.samplers_data_diffusors, + ] all_samplers_map = {x.name: x for x in all_samplers} samplers = all_samplers samplers_for_img2img = all_samplers @@ -17,9 +23,14 @@ def create_sampler(name, model): else: config = all_samplers[0] assert config is not None, f'bad sampler name: {name}' - sampler = config.constructor(model) - sampler.config = config - return sampler + if opts.sd_backend == 'Original': + sampler = config.constructor(model) + sampler.config = config + return sampler + else: + sampler = config.constructor(model.sd_checkpoint_info.filename) + model.scheduler = sampler.sampler + return sampler.sampler def set_samplers(): diff --git a/modules/sd_samplers_diffusors.py b/modules/sd_samplers_diffusors.py new file mode 100644 index 000000000..3d102dca4 --- /dev/null +++ b/modules/sd_samplers_diffusors.py @@ -0,0 +1,45 @@ +from diffusers import ( + DDIMScheduler, + DDPMScheduler, + DEISMultistepScheduler, + DPMSolverMultistepScheduler, + EulerAncestralDiscreteScheduler, + EulerDiscreteScheduler, + HeunDiscreteScheduler, + IPNDMScheduler, + KDPM2AncestralDiscreteScheduler, + PNDMScheduler, + UniPCMultistepScheduler, + # KarrasVeScheduler, + # RePaintScheduler, + # ScoreSdeVeScheduler, + # UnCLIPScheduler, + # VQDiffusionScheduler, +) +from modules import sd_samplers_common + # scheduler = diffusers.UniPCMultistepScheduler.from_pretrained(shared.cmd_opts.ckpt, subfolder="scheduler") + +samplers_data_diffusors = [ + sd_samplers_common.SamplerData('UniPC', lambda model: DiffusionSampler('UniPC', UniPCMultistepScheduler, model), [], {}), + sd_samplers_common.SamplerData('DDIM', lambda model: DiffusionSampler('DDIM', DDIMScheduler, model), [], {}), + sd_samplers_common.SamplerData('DDPMS', lambda model: DiffusionSampler('DDPMS', DDPMScheduler, model), [], {}), + sd_samplers_common.SamplerData('DEIS', lambda model: DiffusionSampler('DEIS', DEISMultistepScheduler, model), [], {}), + sd_samplers_common.SamplerData('DPMSolver', lambda model: DiffusionSampler('DPMSolver', DPMSolverMultistepScheduler, model), [], {}), + sd_samplers_common.SamplerData('Euler', lambda model: DiffusionSampler('Euler', EulerDiscreteScheduler, model), [], {}), + sd_samplers_common.SamplerData('EulerAncestral', lambda model: DiffusionSampler('EulerAncestral', EulerAncestralDiscreteScheduler, model), [], {}), + sd_samplers_common.SamplerData('Heun', lambda model: DiffusionSampler('Heun', HeunDiscreteScheduler, model), [], {}), + sd_samplers_common.SamplerData('IPNDM', lambda model: DiffusionSampler('IPNDM', IPNDMScheduler, model), [], {}), + sd_samplers_common.SamplerData('KDPM2Ancestral', lambda model: DiffusionSampler('KDPM2Ancestral', KDPM2AncestralDiscreteScheduler, model), [], {}), + sd_samplers_common.SamplerData('PNDMS', lambda model: DiffusionSampler('PNDMS', PNDMScheduler, model), [], {}), + # sd_samplers_common.SamplerData('KarrasVe', lambda model: DiffusionSampler('KarrasVe', KarrasVeScheduler, model), [], {}), + # sd_samplers_common.SamplerData('RePaint', lambda model: DiffusionSampler('RePaint', RePaintScheduler, model), [], {}), + # sd_samplers_common.SamplerData('ScoreSdeVe', lambda model: DiffusionSampler('ScoreSdeVe', ScoreSdeVeScheduler, model), [], {}), + # sd_samplers_common.SamplerData('UnCLIP', lambda model: DiffusionSampler('UnCLIP', UnCLIPScheduler, model), [], {}), + # sd_samplers_common.SamplerData('VQDiffusion', lambda model: DiffusionSampler('VQDiffusion', VQDiffusionScheduler, model), [], {}), +] + + +class DiffusionSampler: + def __init__(self, name, constructor, sd_model): + self.sampler = constructor.from_pretrained(sd_model, subfolder="scheduler") + self.sampler.name = name diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 3dbc9498f..bb8ad4d06 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -82,7 +82,7 @@ class CFGDenoiser(torch.nn.Module): # at self.image_cfg_scale == 1.0 produced results for edit model are the same as with normal sampling, # so is_edit_model is set to False to support AND composition. - is_edit_model = shared.sd_model.cond_stage_key == "edit" and self.image_cfg_scale is not None and self.image_cfg_scale != 1.0 + is_edit_model = (shared.sd_model is not None) and hasattr(shared.sd_model, 'cond_stage_key') and (shared.sd_model.cond_stage_key == "edit") and (self.image_cfg_scale is not None) and (self.image_cfg_scale != 1.0) conds_list, tensor = prompt_parser.reconstruct_multicond_batch(cond, self.step) uncond = prompt_parser.reconstruct_cond_batch(uncond, self.step) diff --git a/modules/shared.py b/modules/shared.py index a24fb3631..40f7e6a8d 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -192,6 +192,7 @@ def list_checkpoint_tiles(): import modules.sd_models # pylint: disable=W0621 return modules.sd_models.checkpoint_tiles() + default_checkpoint = list_checkpoint_tiles()[0] if len(list_checkpoint_tiles()) > 0 else "model.ckpt" @@ -251,29 +252,24 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "sd_vae": OptionInfo("Automatic", "Select VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list), "stream_load": OptionInfo(False, "When loading models attempt stream loading optimized for slow or network storage"), "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary"), - "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), - "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.1, "maximum": 1.5, "step": 0.01}), - "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors"), - "img2img_fix_steps": OptionInfo(False, "For image processing do exactly the amount of steps as specified"), - "img2img_background_color": OptionInfo("#ffffff", "With img2img fill image's transparent parts with this color", ui_components.FormColorPicker, {}), - "enable_quantization": OptionInfo(True, "Enable quantization in K samplers for sharper and cleaner results"), - "comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }), - "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 8, "step": 1, "visible": False}), - "upcast_attn": OptionInfo(False, "Upcast cross attention layer to FP32"), "cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }), "cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}), "sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size for the layer optimization to use", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}), "sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attentionkv chunk size for the sub-quadratic cross-attention layer optimization to use", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}), "sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention percentage of VRAM chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}), - "always_batch_cond_uncond": OptionInfo(False, "Disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram"), "prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }), "prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"), + "always_batch_cond_uncond": OptionInfo(False, "Disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram"), + "enable_quantization": OptionInfo(True, "Enable quantization in K samplers for sharper and cleaner results"), + "comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }), + "sd_backend": OptionInfo("Original", "Stable Diffusion backend (experimental)", gr.Radio, lambda: {"choices": ["Original", "Diffusers"] }), })) options_templates.update(options_section(('system-paths', "System Paths"), { - "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), + "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), "clean_temp_dir_at_start": OptionInfo(True, "Cleanup non-default temporary directory when starting webui"), "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Path to directory with stable diffusion checkpoints"), + "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Path to directory with stable diffusion diffusers"), "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Path to directory with VAE files"), "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Embeddings directory for textual inversion"), "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Hypernetwork directory"), @@ -289,10 +285,9 @@ options_templates.update(options_section(('system-paths', "System Paths"), { "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Path to directory with Lora network(s)"), "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Path to directory with LyCORIS network(s)"), "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "Path to user-defined styles file"), - # "gfpgan_model": OptionInfo("", "GFPGAN model file name"), })) -options_templates.update(options_section(('saving-images', "Image options"), { +options_templates.update(options_section(('saving-images', "Image Options"), { "samples_save": OptionInfo(True, "Always save all generated images"), "samples_format": OptionInfo('jpg', 'File format for images'), "samples_filename_pattern": OptionInfo("[seed]-[prompt_spaces]", "Images filename pattern", component_args=hide_dirs), @@ -324,6 +319,16 @@ options_templates.update(options_section(('saving-images', "Image options"), { "directories_max_prompt_words": OptionInfo(8, "Max prompt words for [prompt_words] pattern", gr.Slider, {"minimum": 1, "maximum": 20, "step": 1, **hide_dirs}), })) +options_templates.update(options_section(('image-processing', "Image Processing"), { + "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors"), + "img2img_fix_steps": OptionInfo(False, "For image processing do exactly the amount of steps as specified"), + "img2img_background_color": OptionInfo("#ffffff", "With img2img fill image's transparent parts with this color", ui_components.FormColorPicker, {}), + "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.1, "maximum": 1.5, "step": 0.01}), + "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 8, "step": 1, "visible": False}), +})) + + options_templates.update(options_section(('saving-paths', "Image Paths"), { "outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to three directories below", component_args=hide_dirs), "outdir_txt2img_samples": OptionInfo("outputs/text", 'Output directory for txt2img images', component_args=hide_dirs), @@ -342,11 +347,12 @@ options_templates.update(options_section(('cuda', "Compute Settings"), { "cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}), "no_half": OptionInfo(False, "Use full precision for model (--no-half)", None, None, None), "no_half_vae": OptionInfo(False, "Use full precision for VAE (--no-half-vae)"), - "upcast_sampling": OptionInfo(True if sys.platform == "darwin" or cmd_opts.use_ipex else False, "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory"), - "disable_nan_check": OptionInfo(True, "Do not check if produced images/latent spaces have NaN values"), + "upcast_sampling": OptionInfo(True if sys.platform == "darwin" or cmd_opts.use_ipex else False, "Enable upcast sampling"), + "upcast_attn": OptionInfo(False, "Enable upcast cross attention layer"), + "disable_nan_check": OptionInfo(True, "Disable NaN check in produced images/latent spaces"), "rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"), "opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "), - "cudnn_benchmark": OptionInfo(False, "Enable cuDNN benchmark feature"), + "cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"), "cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"), "cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"), "cuda_compile": OptionInfo(False, "Enable model compile (experimental)"), @@ -778,7 +784,7 @@ def get_version(): try: import subprocess res = subprocess.run('git log --pretty=format:"%h %ad" -1 --date=short', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True) - ver = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else '' + ver = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ' ' githash, updated = ver.split(' ') res = subprocess.run('git remote get-url origin', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True) origin = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else '' @@ -792,7 +798,6 @@ def get_version(): } except: version = { 'app': 'sd.next' } - pass return version diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 485bf0b23..137309d4d 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -207,6 +207,8 @@ class EmbeddingDatabase: continue def load_textual_inversion_embeddings(self, force_reload=False): + if shared.opts.sd_backend == 'Diffusers': # TODO Diffusers + return if not force_reload: need_reload = False for _path, embdir in self.embedding_dirs.items(): diff --git a/modules/ui.py b/modules/ui.py index cae271429..5b61ae83b 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -28,6 +28,7 @@ import modules.textual_inversion.ui import modules.sd_samplers from modules.textual_inversion import textual_inversion + modules.errors.install() mimetypes.init() mimetypes.add_type('application/javascript', '.js') @@ -203,7 +204,13 @@ def update_token_counter(text, steps): prompt_schedules = [[[steps, text]]] flat_prompts = reduce(lambda list1, list2: list1+list2, prompt_schedules) prompts = [prompt_text for step, prompt_text in flat_prompts] - token_count, max_length = max([sd_hijack.model_hijack.get_prompt_lengths(prompt) for prompt in prompts], key=lambda args: args[0]) + if opts.sd_backend == 'Original': + token_count, max_length = max([sd_hijack.model_hijack.get_prompt_lengths(prompt) for prompt in prompts], key=lambda args: args[0]) + else: + tokenizer = modules.shared.sd_model.tokenizer + has_bos_token, has_eos_token = tokenizer.bos_token_id is not None, tokenizer.eos_token_id is not None + token_count = max([len(modules.shared.sd_model.tokenizer(prompt)) for prompt in prompts]) - int(has_bos_token) - int(has_eos_token) + max_length = tokenizer.model_max_length - int(has_bos_token) - int(has_eos_token) return f"{token_count}/{max_length}" @@ -299,13 +306,12 @@ def create_output_panel(tabname, outdir): def create_sampler_and_steps_selection(choices, tabname): with FormRow(elem_id=f"sampler_selection_{tabname}"): if 'UniPC' in [sampler.name for sampler in choices]: - chosen_sampler_name = 'UniPC' + default_sampler_name = 'UniPC' elif 'Euler a' in [sampler.name for sampler in choices]: - chosen_sampler_name = 'Euler a' + default_sampler_name = 'Euler a' else: - chosen_sampler_name = modules.sd_samplers.samplers[0].name - - sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value=chosen_sampler_name if tabname == 'txt2img' else "Euler a", type="index") + default_sampler_name = modules.sd_samplers.samplers[0].name + sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value=default_sampler_name, type="index") steps = gr.Slider(minimum=1, maximum=99, step=1, elem_id=f"{tabname}_steps", label="Sampling steps", value=20) return steps, sampler_index @@ -1452,9 +1458,9 @@ def create_ui(): show_progress=info.refresh is not None, ) - update_image_cfg_scale_visibility = lambda: gr.update(visible=modules.shared.sd_model and modules.shared.sd_model.cond_stage_key == "edit") # pylint: disable=unnecessary-lambda-assignment - text_settings.change(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale]) - demo.load(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale]) + image_cfg_scale_visibility = (modules.shared.sd_model is not None) and hasattr(modules.shared.sd_model, 'cond_stage_key') and (modules.shared.sd_model.cond_stage_key == "edit") # pix2pix + text_settings.change(fn=lambda: gr.update(visible=image_cfg_scale_visibility), inputs=[], outputs=[image_cfg_scale]) + demo.load(fn=lambda: gr.update(visible=image_cfg_scale_visibility), inputs=[], outputs=[image_cfg_scale]) button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False) button_set_checkpoint.click( diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index 55c43fc41..fd9ccd893 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -79,29 +79,25 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): return image def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_mode=1, upscale_by=2.0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0): # pylint: disable=arguments-differ + if upscaler_1_name == "None": upscaler_1_name = None - upscaler1 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_1_name]), None) if not upscaler1: - shared.log.warning(f"Could not find upscaler: {upscaler_1_name or ''}") + if upscaler_1_name is not None: + shared.log.warning(f"Could not find upscaler: {upscaler_1_name or ''}") return - - if upscaler_2_name == "None": - upscaler_2_name = None - - upscaler2 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_2_name and x.name != "None"]), None) - if not upscaler2 and (upscaler_2_name is not None): - shared.log.warning(f"Could not find upscaler: {upscaler_2_name or ''}") - return - upscaled_image = self.upscale(pp.image, pp.info, upscaler1, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop) pp.info["Postprocess upscaler"] = upscaler1.name + if upscaler_2_name == "None": + upscaler_2_name = None + upscaler2 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_2_name and x.name != "None"]), None) + if not upscaler2 and (upscaler_2_name is not None): + shared.log.warning(f"Could not find upscaler: {upscaler_2_name or ''}") if upscaler2 and upscaler_2_visibility > 0: second_upscale = self.upscale(pp.image, pp.info, upscaler2, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop) upscaled_image = Image.blend(upscaled_image, second_upscale, upscaler_2_visibility) - pp.info["Postprocess upscaler 2"] = upscaler2.name pp.image = upscaled_image @@ -130,7 +126,7 @@ class ScriptPostprocessingUpscaleSimple(ScriptPostprocessingUpscale): upscaler1 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_name]), None) if upscaler1 is None: - shared.log.warning(f"Could not find upscaler: {upscaler_name or ''}") + shared.log.debug(f"Upscaler not found: {upscaler_name}") pp.image = self.upscale(pp.image, pp.info, upscaler1, 0, upscale_by, 0, 0, False) pp.info["Postprocess upscaler"] = upscaler1.name diff --git a/webui.py b/webui.py index dee75fa4e..89f1b13a9 100644 --- a/webui.py +++ b/webui.py @@ -150,25 +150,13 @@ def initialize(): def load_model(): shared.state.begin() shared.state.job = 'load model' - - """ - try: - modules.sd_models.load_model() - modules.sd_models.skip_next_load = True - except Exception as e: - errors.display(e, "loading stable diffusion model") - log.error("Stable diffusion model failed to load") - exit(1) - """ Thread(target=lambda: shared.sd_model).start() - if shared.sd_model is None: log.warning("No stable diffusion model loaded") # exit(1) else: shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights()), call=False) - shared.state.end() startup_timer.record("checkpoint")