From 2abd89acc66419abf2eee9b03fd093f2737670de Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 28 Jan 2023 20:04:35 +0300 Subject: [PATCH 01/95] index on master: 91c8d0d Merge pull request #7231 from EllangoK/master --- extensions-builtin/Lora/lora.py | 21 +++++++++++++++++-- .../Lora/scripts/lora_script.py | 5 +++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index cb8f1d36..568a7675 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -12,7 +12,7 @@ re_unet_up_blocks = re.compile(r"lora_unet_up_blocks_(\d+)_attentions_(\d+)_(.+) re_text_block = re.compile(r"lora_te_text_model_encoder_layers_(\d+)_(.+)") -def convert_diffusers_name_to_compvis(key): +def convert_diffusers_name_to_compvis(key, is_sd2): def match(match_list, regex): r = re.match(regex, key) if not r: @@ -34,6 +34,14 @@ def convert_diffusers_name_to_compvis(key): return f"diffusion_model_output_blocks_{m[0] * 3 + m[1]}_1_{m[2]}" if match(m, re_text_block): + if is_sd2: + if 'mlp_fc1' in m[1]: + return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}" + elif 'mlp_fc2' in m[1]: + return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}" + elif 'self_attn': + return f"model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}" + return f"transformer_text_model_encoder_layers_{m[0]}_{m[1]}" return key @@ -83,9 +91,10 @@ def load_lora(name, filename): sd = sd_models.read_state_dict(filename) keys_failed_to_match = [] + is_sd2 = 'model_transformer_resblocks' in shared.sd_model.lora_layer_mapping for key_diffusers, weight in sd.items(): - fullkey = convert_diffusers_name_to_compvis(key_diffusers) + fullkey = convert_diffusers_name_to_compvis(key_diffusers, is_sd2) key, lora_key = fullkey.split(".", 1) sd_module = shared.sd_model.lora_layer_mapping.get(key, None) @@ -104,9 +113,13 @@ def load_lora(name, filename): if type(sd_module) == torch.nn.Linear: module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) + elif type(sd_module) == torch.nn.modules.linear.NonDynamicallyQuantizableLinear: + module = torch.nn.modules.linear.NonDynamicallyQuantizableLinear(weight.shape[1], weight.shape[0], bias=False) elif type(sd_module) == torch.nn.Conv2d: module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False) else: + print(f'Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}') + continue assert False, f'Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}' with torch.no_grad(): @@ -182,6 +195,10 @@ def lora_Conv2d_forward(self, input): return lora_forward(self, input, torch.nn.Conv2d_forward_before_lora(self, input)) +def lora_NonDynamicallyQuantizableLinear_forward(self, input): + return lora_forward(self, input, torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora(self, input)) + + def list_available_loras(): available_loras.clear() diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index 2e860160..a385ae94 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -10,6 +10,7 @@ from modules import script_callbacks, ui_extra_networks, extra_networks, shared def unload(): torch.nn.Linear.forward = torch.nn.Linear_forward_before_lora torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora + torch.nn.modules.linear.NonDynamicallyQuantizableLinear.forward = torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora def before_ui(): @@ -23,8 +24,12 @@ if not hasattr(torch.nn, 'Linear_forward_before_lora'): if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): torch.nn.Conv2d_forward_before_lora = torch.nn.Conv2d.forward +if not hasattr(torch.nn, 'NonDynamicallyQuantizableLinear_forward_before_lora'): + torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora = torch.nn.modules.linear.NonDynamicallyQuantizableLinear.forward + torch.nn.Linear.forward = lora.lora_Linear_forward torch.nn.Conv2d.forward = lora.lora_Conv2d_forward +torch.nn.modules.linear.NonDynamicallyQuantizableLinear.forward = lora.lora_NonDynamicallyQuantizableLinear_forward script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) From 04924241218bb51bee255bebc6c66ef1de449f4a Mon Sep 17 00:00:00 2001 From: bluelovers Date: Sun, 12 Mar 2023 10:18:33 +0800 Subject: [PATCH 02/95] feat: try sort as ignore-case https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/8368 --- extensions-builtin/Lora/lora.py | 2 +- modules/shared.py | 2 +- modules/textual_inversion/textual_inversion.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index cb8f1d36..7d3c0f90 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -192,7 +192,7 @@ def list_available_loras(): glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.safetensors'), recursive=True) + \ glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.ckpt'), recursive=True) - for filename in sorted(candidates): + for filename in sorted(candidates, key=str.lower): if os.path.isdir(filename): continue diff --git a/modules/shared.py b/modules/shared.py index 805f9cc1..1322b96d 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -702,7 +702,7 @@ mem_mon.start() def listfiles(dirname): - filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname)) if not x.startswith(".")] + 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)] diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index c63c7d1d..3d21b9fe 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -129,7 +129,7 @@ class EmbeddingDatabase: if first_id not in self.ids_lookup: self.ids_lookup[first_id] = [] - self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True) + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True, cmp=lambda x, y: x.lower() > y.lower()) return embedding @@ -196,7 +196,7 @@ class EmbeddingDatabase: return for root, dirs, fns in os.walk(embdir.path, followlinks=True): - for fn in fns: + for fn in sorted(fns, key=str.lower): try: fullfn = os.path.join(root, fn) From 9e23bacfbcb35f46f28539f71b2bc917276634b8 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sun, 12 Mar 2023 17:07:03 -0600 Subject: [PATCH 03/95] Make extra networks button togglable --- modules/ui_extra_networks.py | 4 ++-- style.css | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 01df5e90..50e2093e 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -219,10 +219,10 @@ def create_ui(container, button, tabname): def toggle_visibility(is_visible): is_visible = not is_visible - return is_visible, gr.update(visible=is_visible) + return is_visible, gr.update(visible=is_visible), gr.update(variant=("primary" if is_visible else "tool")) state_visible = gr.State(value=False) - button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) + button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button]) def refresh(): res = [] diff --git a/style.css b/style.css index 2f26ad02..e82aebf3 100644 --- a/style.css +++ b/style.css @@ -968,3 +968,10 @@ footer { [id*='_prompt_container'] > div { margin: 0!important; } + +button[id$='_extra_networks'] { + margin: 0.6em 0em 0.55em 0; + max-width: 2.5em; + min-width: 2.5em !important; + height: 2.4em; +} From 1823526c103ee1d2232dfa65f908636daa22a342 Mon Sep 17 00:00:00 2001 From: Mikhail Gribanov Date: Tue, 14 Mar 2023 13:05:45 +0200 Subject: [PATCH 04/95] Update README.md --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 24f8e799..b67e2296 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ A browser interface based on Gradio library for Stable Diffusion. - Prompt Matrix - Stable Diffusion Upscale - Attention, specify parts of text that the model should pay more attention to - - a man in a ((tuxedo)) - will pay more attention to tuxedo - - a man in a (tuxedo:1.21) - alternative syntax - - select text and press ctrl+up or ctrl+down to automatically adjust attention to selected text (code contributed by anonymous user) + - a man in a `((tuxedo))` - will pay more attention to tuxedo + - a man in a `(tuxedo:1.21)` - alternative syntax + - select text and press `Ctrl+Up` or `Ctrl+Down` to automatically adjust attention to selected text (code contributed by anonymous user) - Loopback, run img2img processing multiple times - X/Y/Z plot, a way to draw a 3 dimensional plot of images with different parameters - Textual Inversion @@ -28,7 +28,7 @@ A browser interface based on Gradio library for Stable Diffusion. - CodeFormer, face restoration tool as an alternative to GFPGAN - RealESRGAN, neural network upscaler - ESRGAN, neural network upscaler with a lot of third party models - - SwinIR and Swin2SR([see here](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/2092)), neural network upscalers + - SwinIR and Swin2SR ([see here](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/2092)), neural network upscalers - LDSR, Latent diffusion super resolution upscaling - Resizing aspect ratio options - Sampling method selection @@ -46,7 +46,7 @@ A browser interface based on Gradio library for Stable Diffusion. - drag and drop an image/text-parameters to promptbox - Read Generation Parameters Button, loads parameters in promptbox to UI - Settings page -- Running arbitrary python code from UI (must run with --allow-code to enable) +- Running arbitrary python code from UI (must run with `--allow-code` to enable) - Mouseover hints for most UI elements - Possible to change defaults/mix/max/step values for UI elements via text config - Tiling support, a checkbox to create images that can be tiled like textures @@ -69,7 +69,7 @@ A browser interface based on Gradio library for Stable Diffusion. - also supports weights for prompts: `a cat :1.2 AND a dog AND a penguin :2.2` - No token limit for prompts (original stable diffusion lets you use up to 75 tokens) - DeepDanbooru integration, creates danbooru style tags for anime prompts -- [xformers](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers), major speed increase for select cards: (add --xformers to commandline args) +- [xformers](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers), major speed increase for select cards: (add `--xformers` to commandline args) - via extension: [History tab](https://github.com/yfszzx/stable-diffusion-webui-images-browser): view, direct and delete images conveniently within the UI - Generate forever option - Training tab @@ -78,11 +78,11 @@ A browser interface based on Gradio library for Stable Diffusion. - Clip skip - Hypernetworks - Loras (same as Hypernetworks but more pretty) -- A sparate UI where you can choose, with preview, which embeddings, hypernetworks or Loras to add to your prompt. +- A sparate UI where you can choose, with preview, which embeddings, hypernetworks or Loras to add to your prompt - Can select to load a different VAE from settings screen - Estimated completion time in progress bar - API -- Support for dedicated [inpainting model](https://github.com/runwayml/stable-diffusion#inpainting-with-stable-diffusion) by RunwayML. +- Support for dedicated [inpainting model](https://github.com/runwayml/stable-diffusion#inpainting-with-stable-diffusion) by RunwayML - via extension: [Aesthetic Gradients](https://github.com/AUTOMATIC1111/stable-diffusion-webui-aesthetic-gradients), a way to generate images with a specific aesthetic by using clip images embeds (implementation of [https://github.com/vicgalle/stable-diffusion-aesthetic-gradients](https://github.com/vicgalle/stable-diffusion-aesthetic-gradients)) - [Stable Diffusion 2.0](https://github.com/Stability-AI/stablediffusion) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#stable-diffusion-20) for instructions - [Alt-Diffusion](https://arxiv.org/abs/2211.06679) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#alt-diffusion) for instructions @@ -91,7 +91,6 @@ A browser interface based on Gradio library for Stable Diffusion. - Eased resolution restriction: generated image's domension must be a multiple of 8 rather than 64 - Now with a license! - Reorder elements in the UI from settings screen -- ## Installation and Running Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Dependencies) are met and follow the instructions available for both [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended) and [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs. @@ -101,7 +100,7 @@ Alternatively, use online services (like Google Colab): - [List of Online Services](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Online-Services) ### Automatic Installation on Windows -1. Install [Python 3.10.6](https://www.python.org/downloads/windows/), checking "Add Python to PATH" +1. Install [Python 3.10.6](https://www.python.org/downloads/windows/), checking "Add Python to PATH". 2. Install [git](https://git-scm.com/download/win). 3. Download the stable-diffusion-webui repository, for example by running `git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git`. 4. Run `webui-user.bat` from Windows Explorer as normal, non-administrator, user. @@ -159,4 +158,4 @@ Licenses for borrowed code can be found in `Settings -> Licenses` screen, and al - Security advice - RyotaK - UniPC sampler - Wenliang Zhao - https://github.com/wl-zhao/UniPC - Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user. -- (You) +- (You) \ No newline at end of file From fd672a79afc912be46a2a01133269b8c6842a90d Mon Sep 17 00:00:00 2001 From: bluelovers Date: Wed, 15 Mar 2023 13:17:09 +0800 Subject: [PATCH 05/95] fix: remove cmp by ChatGPT --- modules/textual_inversion/textual_inversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 3d21b9fe..8b5bb6ce 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -129,7 +129,7 @@ class EmbeddingDatabase: if first_id not in self.ids_lookup: self.ids_lookup[first_id] = [] - self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True, cmp=lambda x, y: x.lower() > y.lower()) + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: (len(x[0]), x[0].casefold()), reverse=True) return embedding From 575c17a8f9bc6471a7a0891b665ec42073a18049 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Wed, 15 Mar 2023 16:56:27 -0600 Subject: [PATCH 06/95] Update tooltip per Kilvoctu's suggestion --- javascript/hints.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/hints.js b/javascript/hints.js index 7f4101b2..83128497 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -21,8 +21,7 @@ titles = { "\u{1f5d1}": "Clear prompt", "\u{1f4cb}": "Apply selected styles to current prompt", "\u{1f4d2}": "Paste available values into the field", - "\u{1f3b4}": "Show extra networks", - + "\u{1f3b4}": "Show/hide extra networks", "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back", From dfa258de5f87ea7f5ff49e29ddf6e7b34ff8ebff Mon Sep 17 00:00:00 2001 From: Vespinian Date: Wed, 15 Mar 2023 22:17:32 -0400 Subject: [PATCH 07/95] Made copies of global scriptrunners, now we clear the copied scriptrunner of alwayson_scripts and only add back the ones that that were requested --- modules/api/api.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 35e17afc..afbc202a 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -3,6 +3,7 @@ import io import time import datetime import uvicorn +import copy from threading import Lock from io import BytesIO from gradio.processing_utils import decode_base64_to_file @@ -202,6 +203,7 @@ class Api: script_args[0] = 0 # Now check for always on scripts + alwayson_script_to_run = [] # list to replace the one from the global ScriptRunner we copied if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): for alwayson_script_name in request.alwayson_scripts.keys(): alwayson_script = self.get_script(alwayson_script_name, script_runner) @@ -210,13 +212,21 @@ class Api: # Selectable script in always on script param check if alwayson_script.alwayson == False: raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - # always on script with no arg should always run so you don't really need to add them to the requests + # all good, so add to run list and set its args if any + alwayson_script_to_run.append(alwayson_script) if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] + + # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner + script_runner.alwayson_scripts.clear() + script_runner.alwayson_scripts = alwayson_script_to_run + script_runner.scripts.clear() + script_runner.scripts = alwayson_script_to_run + script_runner.selectable_scripts + return script_args def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): - script_runner = scripts.scripts_txt2img + script_runner = copy.copy(scripts.scripts_txt2img) # copy so we don't overwrite our globals if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() @@ -268,7 +278,7 @@ class Api: if mask: mask = decode_base64_to_image(mask) - script_runner = scripts.scripts_img2img + script_runner = copy.copy(scripts.scripts_img2img) # copy so we don't overwrite our globals if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() From f04bd037a51de3c65072581d9a7dfed1d0d2887e Mon Sep 17 00:00:00 2001 From: Vespinian Date: Wed, 15 Mar 2023 22:27:54 -0400 Subject: [PATCH 08/95] Comment fix --- modules/api/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index afbc202a..8c06cf20 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -217,7 +217,7 @@ class Api: if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] - # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner + # Remove always on scripts that were not included in the request by resetting the script list in our ScriptRunner script_runner.alwayson_scripts.clear() script_runner.alwayson_scripts = alwayson_script_to_run script_runner.scripts.clear() From 68999d0b15d612965e7bc7feb62d6b4d55e112fa Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 12:52:14 -0400 Subject: [PATCH 09/95] Add upscale slider to img2img --- javascript/ui.js | 25 +- modules/generation_parameters_copypaste.py | 3 + modules/img2img.py | 3 +- modules/processing.py | 18 +- modules/ui.py | 67 +- style.css | 814 ++++++++++++++------- 6 files changed, 644 insertions(+), 286 deletions(-) diff --git a/javascript/ui.js b/javascript/ui.js index fcaf5608..8aa4a459 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -8,8 +8,8 @@ function set_theme(theme){ } function selected_gallery_index(){ - var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item') - var button = gradioApp().querySelector('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item.\\!ring-2') + var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery] .thumbnails > .thumbnail-item') + var button = gradioApp().querySelector('[style="display: block;"].tabitem div[id$=_gallery] .thumbnails > .thumbnail-item.selected') var result = -1 buttons.forEach(function(v, i){ if(v==button) { result = i } }) @@ -111,6 +111,14 @@ function get_img2img_tab_index() { return res } +function get_img2img_tab_index_for_res_preview() { + let res = args_to_array(arguments) + res.splice(-1) // gradio also sends outputs to the arguments, pop them off + res[0] = get_tab_index('mode_img2img') + debugger; + return res +} + function create_submit_args(args){ res = [] for(var i=0;i 1) + setInactive(i2iHeight, scale > 1) + + return [init_img, width, height, scale, resize_mode] +} diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 6df76858..459de080 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -282,6 +282,9 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model res["Hires resize-1"] = 0 res["Hires resize-2"] = 0 + if "Img2Img Upscale" not in res: + res["Img2Img Upscale"] = 1 + restore_old_hires_fix_params(res) return res diff --git a/modules/img2img.py b/modules/img2img.py index c973b770..d05fa750 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -78,7 +78,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args): processed_image.save(os.path.join(output_dir, filename)) -def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): +def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, scale: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): override_settings = create_override_settings_dict(override_settings_texts) is_batch = mode == 5 @@ -149,6 +149,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s inpaint_full_res_padding=inpaint_full_res_padding, inpainting_mask_invert=inpainting_mask_invert, override_settings=override_settings, + scale=scale, ) p.scripts = modules.scripts.scripts_txt2img diff --git a/modules/processing.py b/modules/processing.py index 2e5a363f..fc4b166c 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -929,7 +929,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): sampler = None - def __init__(self, init_images: list = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, **kwargs): + def __init__(self, init_images: Optional[list] = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: Optional[float] = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: Optional[float] = None, scale: float = 0, **kwargs): super().__init__(**kwargs) self.init_images = init_images @@ -949,11 +949,27 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.mask = None self.nmask = None self.image_conditioning = None + self.scale = scale + + def get_final_size(self): + if self.scale > 1: + img = self.init_images[0] + width = int(img.width * self.scale) + height = int(img.height * self.scale) + return width, height + else: + return self.width, self.height + def init(self, all_prompts, all_seeds, all_subseeds): self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) crop_region = None + if self.scale > 1: + self.extra_generation_params["Img2Img Upscale"] = self.scale + + self.width, self.height = self.get_final_size() + image_mask = self.image_mask if image_mask is not None: diff --git a/modules/ui.py b/modules/ui.py index af8546c2..bb548f92 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -15,6 +15,7 @@ import warnings import gradio as gr import gradio.routes import gradio.utils +from gradio.events import Releaseable import numpy as np from PIL import Image, PngImagePlugin from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call @@ -138,6 +139,26 @@ def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resiz return f"resize: from {p.width}x{p.height} to {p.hr_resize_x or p.hr_upscale_to_x}x{p.hr_resize_y or p.hr_upscale_to_y}" +def calc_resolution_img2img(mode, scale, resize_x, resize_y, resize_mode, *i2i_images): + init_img = None + if mode in {0, 1, 3, 4}: + init_img = i2i_images[mode] + elif mode == 2: + init_img = i2i_images[mode]["image"] + + if not init_img: + return "" + + if scale > 1: + width = int(init_img.width * scale) + height = int(init_img.height * scale) + else: + width = resize_x + height = resize_y + + return f"resize: from {init_img.width}x{init_img.height} to {width}x{height}" + + def apply_styles(prompt, prompt_neg, styles): prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles) prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, styles) @@ -755,8 +776,13 @@ def create_ui(): elif category == "dimensions": with FormRow(): with gr.Column(elem_id="img2img_column_size", scale=4): - width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") - height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") + with FormRow(variant="compact"): + final_resolution = FormHTML(value="", elem_id="img2img_finalres", label="Upscaled resolution", interactive=False) + with FormRow(variant="compact"): + scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=1.0, elem_id="img2img_scale") + with FormRow(variant="compact"): + width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") + height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") with gr.Column(elem_id="img2img_dimensions_row", scale=1, elem_classes="dimensions-tools"): res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") @@ -824,6 +850,41 @@ def create_ui(): outputs=[inpaint_controls, mask_alpha], ) + img2img_resolution_preview_inputs = [dummy_component, # filled in by selected img2img tab index in _js + scale, width, height, resize_mode, + init_img, sketch, init_img_with_mask, inpaint_color_sketch, init_img_inpaint] + for input in img2img_resolution_preview_inputs: + if isinstance(input, Releaseable): + input.release( + fn=calc_resolution_img2img, + _js="get_img2img_tab_index_for_res_preview", + inputs=img2img_resolution_preview_inputs, + outputs=[final_resolution], + show_progress=False, + ) + input.release( + None, + _js="onCalcResolutionImg2Img", + inputs=img2img_resolution_preview_inputs, + outputs=[], + show_progress=False, + ) + else: + input.change( + fn=calc_resolution_img2img, + _js="get_img2img_tab_index_for_res_preview", + inputs=img2img_resolution_preview_inputs, + outputs=[final_resolution], + show_progress=False, + ) + input.change( + None, + _js="onCalcResolutionImg2Img", + inputs=img2img_resolution_preview_inputs, + outputs=[], + show_progress=False, + ) + img2img_gallery, generation_info, html_info, html_log = create_output_panel("img2img", opts.outdir_img2img_samples) connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) @@ -872,6 +933,7 @@ def create_ui(): subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox, height, width, + scale, resize_mode, inpaint_full_res, inpaint_full_res_padding, @@ -957,6 +1019,7 @@ def create_ui(): (seed, "Seed"), (width, "Size-1"), (height, "Size-2"), + (scale, "Img2Img Upscale"), (batch_size, "Batch size"), (subseed, "Variation seed"), (subseed_strength, "Variation seed strength"), diff --git a/style.css b/style.css index 0dcc3e25..7d58b3b2 100644 --- a/style.css +++ b/style.css @@ -1,196 +1,48 @@ - -/* general gradio fixes */ - -:root, .dark{ - --checkbox-label-gap: 0.25em 0.1em; - --section-header-text-size: 12pt; - --block-background-fill: transparent; +.container { + max-width: 100%; } -.block.padded{ - padding: 0 !important; -} - -div.gradio-container{ - max-width: unset !important; -} - -.hidden{ - display: none; -} - -.compact{ - background: transparent !important; - padding: 0 !important; -} - -div.form{ - border-width: 0; - box-shadow: none; - background: transparent; - overflow: visible; - gap: 0.5em; -} - -.block.gradio-dropdown, -.block.gradio-slider, -.block.gradio-checkbox, -.block.gradio-textbox, -.block.gradio-radio, -.block.gradio-checkboxgroup, -.block.gradio-number, -.block.gradio-colorpicker -{ - border-width: 0 !important; - box-shadow: none !important; -} - -.gap.compact{ - padding: 0; - gap: 0.2em 0; -} - -div.compact{ - gap: 1em; -} - -.gradio-dropdown ul.options{ - z-index: 3000; -} - -.gradio-dropdown label span:not(.has-info), -.gradio-textbox label span:not(.has-info), -.gradio-number label span:not(.has-info) -{ - margin-bottom: 0; -} - -.gradio-dropdown div.wrap.wrap.wrap.wrap{ - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); -} - -.gradio-dropdown .wrap-inner.wrap-inner.wrap-inner{ - flex-wrap: unset; -} - -.gradio-dropdown .single-select{ - white-space: nowrap; - overflow: hidden; -} - -.gradio-dropdown .token-remove.remove-all.remove-all{ - display: none; -} - -.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{ - display: flex; -} - -.gradio-slider input[type="number"]{ - width: 6em; -} - -.block.gradio-checkbox { - margin: 0.75em 1.5em 0 0; -} - -.gradio-html div.wrap{ - height: 100%; -} -div.gradio-html.min{ - min-height: 0; -} - -.block.gradio-gallery{ - background: var(--input-background-fill); -} - -.gradio-container .prose a, .gradio-container .prose a:visited{ - color: unset; - text-decoration: none; -} - - - -/* general styled components */ - -.gradio-button.tool{ - max-width: 2.2em; - min-width: 2.2em !important; - height: 2.4em; - align-self: end; - line-height: 1em; - border-radius: 0.5em; -} - -.checkboxes-row{ - margin-bottom: 0.5em; - margin-left: 0em; -} -.checkboxes-row > div{ - flex: 0; - white-space: nowrap; - min-width: auto; -} - -button.custom-button{ - border-radius: var(--button-large-radius); - padding: var(--button-large-padding); - font-weight: var(--button-large-text-weight); - border: var(--button-border-width) solid var(--button-secondary-border-color); - background: var(--button-secondary-background-fill); - color: var(--button-secondary-text-color); - font-size: var(--button-large-text-size); - display: inline-flex; - justify-content: center; - align-items: center; - transition: var(--button-transition); - box-shadow: var(--button-shadow); - text-align: center; -} - - -/* txt2img/img2img specific */ - -.block.token-counter{ +.token-counter{ position: absolute; display: inline-block; - right: 1em; + right: 2em; min-width: 0 !important; width: auto; z-index: 100; - top: -0.75em; } -.block.token-counter span{ - background: var(--input-background-fill) !important; - box-shadow: 0 0 0.0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075); - border: 2px solid rgba(192,192,192,0.4) !important; - border-radius: 0.4em; -} - -.block.token-counter.error span{ +.token-counter.error span{ box-shadow: 0 0 0.0 0.3em rgba(255,0,0,0.15), inset 0 0 0.6em rgba(255,0,0,0.075); border: 2px solid rgba(255,0,0,0.4) !important; } -.block.token-counter div{ +.token-counter div{ display: inline; } -.block.token-counter span{ +.token-counter span{ padding: 0.1em 0.75em; } -[id$=_subseed_show]{ - min-width: auto !important; - flex-grow: 0 !important; - display: flex; +#sh{ + min-width: 2em; + min-height: 2em; + max-width: 2em; + max-height: 2em; + flex-grow: 0; + padding-left: 0.25em; + padding-right: 0.25em; + margin: 0.1em 0; + opacity: 0%; + cursor: default; } -[id$=_subseed_show] label{ - margin-bottom: 0.5em; - align-self: end; +.output-html p {margin: 0 0.5em;} + +.row > *, +.row > .gr-form > * { + min-width: min(120px, 100%); + flex: 1 1 0%; } .performance { @@ -223,94 +75,196 @@ button.custom-button{ object-fit: scale-down; } #txt2img_actions_column, #img2img_actions_column { - gap: 0.5em; + margin: 0.35rem 0.75rem 0.35rem 0; } +#script_list { + padding: .625rem .75rem 0 .625rem; +} +.justify-center.overflow-x-scroll { + justify-content: left; +} + +.justify-center.overflow-x-scroll button:first-of-type { + margin-left: auto; +} + +.justify-center.overflow-x-scroll button:last-of-type { + margin-right: auto; +} + +[id$=_random_seed], [id$=_random_subseed], [id$=_reuse_seed], [id$=_reuse_subseed], #open_folder{ + min-width: 2.3em; + height: 2.5em; + flex-grow: 0; + padding-left: 0.25em; + padding-right: 0.25em; +} + +#hidden_element{ + display: none; +} + +[id$=_seed_row], [id$=_subseed_row]{ + gap: 0.5rem; + padding: 0.6em; +} + +[id$=_subseed_show_box]{ + min-width: auto; + flex-grow: 0; +} + +[id$=_subseed_show_box] > div{ + border: 0; + height: 100%; +} + +[id$=_subseed_show]{ + min-width: auto; + flex-grow: 0; + padding: 0; +} + +[id$=_subseed_show] label{ + height: 100%; +} + +#txt2img_actions_column, #img2img_actions_column{ + gap: 0; + margin-right: .75rem; +} + #txt2img_tools, #img2img_tools{ gap: 0.4em; } -.interrogate-col{ +#interrogate_col{ min-width: 0 !important; - max-width: fit-content; - gap: 0.5em; + max-width: 8em !important; + margin-right: 1em; + gap: 0; } -.interrogate-col > button{ - flex: 1; +#interrogate, #deepbooru{ + margin: 0em 0.25em 0.5em 0.25em; + min-width: 8em; + max-width: 8em; } -.generate-box{ - position: relative; +#style_pos_col, #style_neg_col{ + min-width: 8em !important; } -.gradio-button.generate-box-skip, .gradio-button.generate-box-interrupt{ + +#txt2img_styles_row, #img2img_styles_row{ + gap: 0.25em; + margin-top: 0.3em; +} + +#txt2img_styles_row > button, #img2img_styles_row > button{ + margin: 0; +} + +#txt2img_styles, #img2img_styles{ + padding: 0; +} + +#txt2img_styles > label > div, #img2img_styles > label > div{ + min-height: 3.2em; +} + +ul.list-none{ + max-height: 35em; + z-index: 2000; +} + +.gr-form{ + background: transparent; +} + +.my-4{ + margin-top: 0; + margin-bottom: 0; +} + +#resize_mode{ + flex: 1.5; +} + +button{ + align-self: stretch !important; +} + +.overflow-hidden, .gr-panel{ + overflow: visible !important; +} + +#x_type, #y_type{ + max-width: 10em; +} + +#txt2img_preview, #img2img_preview, #ti_preview{ position: absolute; - width: 50%; - height: 100%; - display: none; - background: #b4c0cc; -} -.gradio-button.generate-box-skip:hover, .gradio-button.generate-box-interrupt:hover{ - background: #c2cfdb; -} -.gradio-button.generate-box-interrupt{ + width: 320px; left: 0; - border-radius: 0.5rem 0 0 0.5rem; -} -.gradio-button.generate-box-skip{ right: 0; - border-radius: 0 0.5rem 0.5rem 0; + margin-left: auto; + margin-right: auto; + margin-top: 34px; + z-index: 100; + border: none; + border-top-left-radius: 0; + border-top-right-radius: 0; } -#txtimg_hr_finalres{ - min-height: 0 !important; - padding: .625rem .75rem; - margin-left: -0.75em +@media screen and (min-width: 768px) { + #txt2img_preview, #img2img_preview, #ti_preview { + position: absolute; + } } -#txtimg_hr_finalres .resolution{ - font-weight: bold; +@media screen and (max-width: 767px) { + #txt2img_preview, #img2img_preview, #ti_preview { + position: relative; + } } -.inactive{ - opacity: 0.5; +#txt2img_preview div.left-0.top-0, #img2img_preview div.left-0.top-0, #ti_preview div.left-0.top-0{ + display: none; } -[id$=_column_batch]{ +fieldset span.text-gray-500, .gr-block.gr-box span.text-gray-500, label.block span{ + position: absolute; + top: -0.7em; + line-height: 1.2em; + padding: 0; + margin: 0 0.5em; + + background-color: white; + box-shadow: 6px 0 6px 0px white, -6px 0 6px 0px white; + + z-index: 300; +} + +.dark fieldset span.text-gray-500, .dark .gr-block.gr-box span.text-gray-500, .dark label.block span{ + background-color: rgb(31, 41, 55); + box-shadow: none; + border: 1px solid rgba(128, 128, 128, 0.1); + border-radius: 6px; + padding: 0.1em 0.5em; +} + +#txt2img_column_batch, #img2img_column_batch{ min-width: min(13.5em, 100%) !important; } -div.dimensions-tools{ - min-width: 0 !important; - max-width: fit-content; - flex-direction: row; - align-content: center; -} - -#mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{ - height: 480px !important; - max-height: 480px !important; - min-height: 480px !important; -} - -.image-buttons button{ - min-width: auto; -} - -.infotext { - overflow-wrap: break-word; -} - -/* settings */ -#quicksettings { - width: fit-content; -} - -#quicksettings > div, #quicksettings > fieldset{ - max-width: 24em; - min-width: 24em; - padding: 0; +#settings fieldset span.text-gray-500, #settings .gr-block.gr-box span.text-gray-500, #settings label.block span{ + position: relative; border: none; - box-shadow: none; - background: none; + margin-right: 8em; +} + +#settings .gr-panel div.flex-col div.justify-between div{ + position: relative; + z-index: 200; } #settings{ @@ -322,18 +276,17 @@ div.dimensions-tools{ margin-left: 10em; } -#settings > div.tab-nav{ +#settings > div.flex-wrap{ float: left; display: block; margin-left: 0; width: 10em; } -#settings > div.tab-nav button{ +#settings > div.flex-wrap button{ display: block; border: none; text-align: left; - white-space: initial; } #settings_result{ @@ -341,8 +294,29 @@ div.dimensions-tools{ margin: 0 1.2em; } +input[type="range"]{ + margin: 0.5em 0 -0.3em 0; +} + +#mask_bug_info { + text-align: center; + display: block; + margin-top: -0.75em; + margin-bottom: -0.75em; +} + +#txt2img_negative_prompt, #img2img_negative_prompt{ +} + +/* gradio 3.8 adds opacity to progressbar which makes it blink; disable it here */ +.transition.opacity-20 { + opacity: 1 !important; +} + +/* more gradio's garbage cleanup */ +.min-h-\[4rem\] { min-height: unset !important; } +.min-h-\[6rem\] { min-height: unset !important; } -/* live preview */ .progressDiv{ position: relative; height: 20px; @@ -388,8 +362,6 @@ div.dimensions-tools{ height: 100%; } -/* fullscreen popup (ie in Lora's (i) button) */ - .popup-metadata{ color: black; background: white; @@ -430,54 +402,87 @@ div.dimensions-tools{ padding: 2em; } -/* fullpage image viewer */ - #lightboxModal{ - display: none; - position: fixed; - z-index: 1001; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(20, 20, 20, 0.95); - user-select: none; - -webkit-user-select: none; - flex-direction: column; + display: none; + position: fixed; + z-index: 1001; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(20, 20, 20, 0.95); + user-select: none; + -webkit-user-select: none; } .modalControls { - display: flex; - gap: 1em; - padding: 1em; + display: grid; + grid-template-columns: 32px 32px 32px 1fr 32px; + grid-template-areas: "zoom tile save space close"; + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 16px; + gap: 16px; background-color: rgba(0,0,0,0.2); } + .modalClose { - margin-left: auto; + grid-area: close; } -.modalControls span{ + +.modalZoom { + grid-area: zoom; +} + +.modalSave { + grid-area: save; +} + +.modalTileImage { + grid-area: tile; +} + +.modalClose, +.modalZoom, +.modalTileImage { + color: white; + font-size: 35px; + font-weight: bold; + cursor: pointer; +} + +.modalSave { color: white; - font-size: 35px; + font-size: 28px; + margin-top: 8px; font-weight: bold; cursor: pointer; - width: 1em; } -.modalControls span:hover, .modalControls span:focus{ - color: #999; - text-decoration: none; +.modalClose:hover, +.modalClose:focus, +.modalSave:hover, +.modalSave:focus, +.modalZoom:hover, +.modalZoom:focus { + color: #999; + text-decoration: none; + cursor: pointer; } -#lightboxModal > img { +#modalImage { display: block; margin: auto; width: auto; } -#lightboxModal > img.modalImageFullscreen{ +.modalImageFullscreen { object-fit: contain; - height: 100%; + height: 90%; } .modalPrev, @@ -507,7 +512,45 @@ div.dimensions-tools{ background-color: rgba(0, 0, 0, 0.8); } -/* context menu (ie for the generate button) */ +#imageARPreview{ + position:absolute; + top:0px; + left:0px; + border:2px solid red; + background:rgba(255, 0, 0, 0.3); + z-index: 900; + pointer-events:none; + display:none +} + +#txt2img_generate_box, #img2img_generate_box{ + position: relative; +} + +#txt2img_interrupt, #img2img_interrupt, #txt2img_skip, #img2img_skip{ + position: absolute; + width: 50%; + height: 100%; + background: #b4c0cc; + display: none; +} + +#txt2img_interrupt, #img2img_interrupt{ + left: 0; + border-radius: 0.5rem 0 0 0.5rem; +} +#txt2img_skip, #img2img_skip{ + right: 0; + border-radius: 0 0.5rem 0.5rem 0; +} + +.red { + color: red; +} + +.gallery-item { + --tw-bg-opacity: 0 !important; +} #context-menu{ z-index:9999; @@ -536,8 +579,61 @@ div.dimensions-tools{ background: #a55000; } +#quicksettings { + width: fit-content; +} -/* extensions */ +#quicksettings > div, #quicksettings > fieldset{ + max-width: 24em; + min-width: 24em; + padding: 0; + border: none; + box-shadow: none; + background: none; + margin-right: 10px; +} + +#quicksettings > div > div > div > label > span { + position: relative; + margin-right: 9em; + margin-bottom: -1em; +} + +canvas[key="mask"] { + z-index: 12 !important; + filter: invert(); + mix-blend-mode: multiply; + pointer-events: none; +} + + +/* gradio 3.4.1 stuff for editable scrollbar values */ +.gr-box > div > div > input.gr-text-input{ + position: absolute; + right: 0.5em; + top: -0.6em; + z-index: 400; + width: 6em; +} +#quicksettings .gr-box > div > div > input.gr-text-input { + top: -1.12em; +} + +.row.gr-compact{ + overflow: visible; +} + +#img2img_image, #img2img_image > .h-60, #img2img_image > .h-60 > div, #img2img_image > .h-60 > div > img, +#img2img_sketch, #img2img_sketch > .h-60, #img2img_sketch > .h-60 > div, #img2img_sketch > .h-60 > div > img, +#img2maskimg, #img2maskimg > .h-60, #img2maskimg > .h-60 > div, #img2maskimg > .h-60 > div > img, +#inpaint_sketch, #inpaint_sketch > .h-60, #inpaint_sketch > .h-60 > div, #inpaint_sketch > .h-60 > div > img +{ + height: 480px !important; + max-height: 480px !important; + min-height: 480px !important; +} + +/* Extensions */ #tab_extensions table{ border-collapse: collapse; @@ -550,7 +646,6 @@ div.dimensions-tools{ #tab_extensions table input[type="checkbox"]{ margin-right: 0.5em; - appearance: checkbox; } #tab_extensions button{ @@ -575,7 +670,74 @@ div.dimensions-tools{ font-size: 90%; } -/* replace original footer with ours */ +#image_buttons_txt2img button, #image_buttons_img2img button, #image_buttons_extras button{ + min-width: auto; + padding-left: 0.5em; + padding-right: 0.5em; +} + +.gr-form{ + background-color: white; +} + +.dark .gr-form{ + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.gr-button-tool, .gr-button-tool-top{ + max-width: 2.5em; + min-width: 2.5em !important; + height: 2.4em; +} + +.gr-button-tool{ + margin: 0.6em 0em 0.55em 0; +} + +.gr-button-tool-top, #settings .gr-button-tool{ + margin: 1.6em 0.7em 0.55em 0; +} + + +#modelmerger_results_container{ + margin-top: 1em; + overflow: visible; +} + +#modelmerger_models{ + gap: 0; +} + + +#quicksettings .gr-button-tool{ + margin: 0; + border-color: unset; + background-color: unset; +} + +#modelmerger_interp_description>p { + margin: 0!important; + text-align: center; +} +#modelmerger_interp_description { + margin: 0.35rem 0.75rem 1.23rem; +} +#img2img_settings > div.gr-form, #txt2img_settings > div.gr-form { + padding-top: 0.9em; + padding-bottom: 0.9em; +} +#txt2img_settings { + padding-top: 1.16em; + padding-bottom: 0.9em; +} +#img2img_settings { + padding-bottom: 0.9em; +} + +#img2img_settings div.gr-form .gr-form, #txt2img_settings div.gr-form .gr-form, #train_tabs div.gr-form .gr-form{ + border: none; + padding-bottom: 0.5em; +} footer { display: none !important; @@ -594,7 +756,99 @@ footer { opacity: 0.85; } -/* extra networks UI */ +#txtimg_hr_finalres{ + min-height: 0 !important; + padding: .625rem .75rem; + margin-left: -0.75em +} + +#txtimg_hr_finalres .resolution{ + font-weight: bold; +} + +#txt2img_checkboxes, #img2img_checkboxes{ + margin-bottom: 0.5em; + margin-left: 0em; +} +#txt2img_checkboxes > div, #img2img_checkboxes > div{ + flex: 0; + white-space: nowrap; + min-width: auto; +} + +#img2img_finalres{ + min-height: 0 !important; + padding: .625rem .75rem; + margin-left: -0.75em +} + +#img2img_finalres .resolution{ + font-weight: bold; +} + +#img2img_copy_to_img2img, #img2img_copy_to_sketch, #img2img_copy_to_inpaint, #img2img_copy_to_inpaint_sketch{ + margin-left: 0em; +} + +#axis_options { + margin-left: 0em; +} + +.inactive{ + opacity: 0.5; +} + +[id*='_prompt_container']{ + gap: 0; +} + +[id*='_prompt_container'] > div{ + margin: -0.4em 0 0 0; +} + +.gr-compact { + border: none; +} + +.dark .gr-compact{ + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); + margin-left: 0; +} + +.gr-compact{ + overflow: visible; +} + +.gr-compact > *{ +} + +.gr-compact .gr-block, .gr-compact .gr-form{ + border: none; + box-shadow: none; +} + +.gr-compact .gr-box{ + border-radius: .5rem !important; + border-width: 1px !important; +} + +#mode_img2img > div > div{ + gap: 0 !important; +} + +[id*='img2img_copy_to_'] { + border: none; +} + +[id*='img2img_copy_to_'] > button { +} + +[id*='img2img_label_copy_to_'] { + font-size: 1.0em; + font-weight: bold; + text-align: center; + line-height: 2.4em; +} .extra-networks > div > [id *= '_extra_']{ margin: 0.3em; @@ -607,12 +861,12 @@ footer { .extra-network-subdirs button{ margin: 0 0.15em; } -.extra-networks .tab-nav .search{ + +#txt2img_extra_networks .search, #img2img_extra_networks .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; - width: 16em; } #txt2img_extra_view, #img2img_extra_view { @@ -644,7 +898,6 @@ footer { text-shadow: 2px 2px 3px black; padding: 0.25em; font-size: 22pt; - width: 1.5em; } .extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; @@ -738,15 +991,12 @@ footer { left: 0; right: 0; padding: 0.5em; + color: white; background: rgba(0,0,0,0.5); box-shadow: 0 0 0.25em 0.25em rgba(0,0,0,0.5); text-shadow: 0 0 0.2em black; } -.extra-network-cards .card .actions *{ - color: white; -} - .extra-network-cards .card .actions:hover{ box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; } @@ -784,3 +1034,7 @@ footer { .extra-network-cards .card ul a:hover{ color: red; } + +[id*='_prompt_container'] > div { + margin: 0!important; +} From 7ea5d395c44be208f654b07ec7993aa2952f2510 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sun, 19 Feb 2023 03:45:43 -0800 Subject: [PATCH 10/95] Add upscaler to img2img --- modules/generation_parameters_copypaste.py | 4 ++-- modules/img2img.py | 3 ++- modules/processing.py | 23 ++++++++++++++++------ modules/ui.py | 10 +++++++--- scripts/xyz_grid.py | 1 + style.css | 2 +- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 459de080..0ad2ad4f 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -282,8 +282,8 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model res["Hires resize-1"] = 0 res["Hires resize-2"] = 0 - if "Img2Img Upscale" not in res: - res["Img2Img Upscale"] = 1 + if "Img2Img upscale" not in res: + res["Img2Img upscale"] = 1 restore_old_hires_fix_params(res) diff --git a/modules/img2img.py b/modules/img2img.py index d05fa750..959dd96e 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -78,7 +78,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args): processed_image.save(os.path.join(output_dir, filename)) -def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, scale: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): +def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, scale: float, upscaler: str, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): override_settings = create_override_settings_dict(override_settings_texts) is_batch = mode == 5 @@ -150,6 +150,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s inpainting_mask_invert=inpainting_mask_invert, override_settings=override_settings, scale=scale, + upscaler=upscaler, ) p.scripts = modules.scripts.scripts_txt2img diff --git a/modules/processing.py b/modules/processing.py index fc4b166c..afb8cfd1 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -929,7 +929,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): sampler = None - def __init__(self, init_images: Optional[list] = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: Optional[float] = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: Optional[float] = None, scale: float = 0, **kwargs): + def __init__(self, init_images: Optional[list] = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: Optional[float] = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: Optional[float] = None, scale: float = 0, upscaler: Optional[str] = None, **kwargs): super().__init__(**kwargs) self.init_images = init_images @@ -950,6 +950,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.nmask = None self.image_conditioning = None self.scale = scale + self.upscaler = upscaler def get_final_size(self): if self.scale > 1: @@ -966,7 +967,16 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): crop_region = None if self.scale > 1: - self.extra_generation_params["Img2Img Upscale"] = self.scale + self.extra_generation_params["Img2Img upscale"] = self.scale + + # Non-latent upscalers are run before sampling + # Latent upscalers are run during sampling + init_upscaler = None + if self.upscaler is not None: + self.extra_generation_params["Img2Img upscaler"] = self.upscaler + if self.upscaler not in shared.latent_upscale_modes: + assert len([x for x in shared.sd_upscalers if x.name == self.upscaler]) > 0, f"could not find upscaler named {self.upscaler}" + init_upscaler = self.upscaler self.width, self.height = self.get_final_size() @@ -992,7 +1002,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image_mask = images.resize_image(2, mask, self.width, self.height) self.paste_to = (x1, y1, x2-x1, y2-y1) else: - image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height) + image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height, init_upscaler) np_mask = np.array(image_mask) np_mask = np.clip((np_mask.astype(np.float32)) * 2, 0, 255).astype(np.uint8) self.mask_for_overlay = Image.fromarray(np_mask) @@ -1009,7 +1019,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image = images.flatten(img, opts.img2img_background_color) if crop_region is None and self.resize_mode != 3: - image = images.resize_image(self.resize_mode, image, self.width, self.height) + image = images.resize_image(self.resize_mode, image, self.width, self.height, init_upscaler) if image_mask is not None: image_masked = Image.new('RGBa', (image.width, image.height)) @@ -1054,8 +1064,9 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image)) - if self.resize_mode == 3: - self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") + latent_scale_mode = shared.latent_upscale_modes.get(self.upscaler, None) if self.upscaler is not None else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "nearest") + if latent_scale_mode is not None: + self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // opt_f, self.width // opt_f), mode=latent_scale_mode["mode"], antialias=latent_scale_mode["antialias"]) if image_mask is not None: init_mask = latent_mask diff --git a/modules/ui.py b/modules/ui.py index bb548f92..24ab0af7 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -767,7 +767,7 @@ def create_ui(): ) with FormRow(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize") + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize") for category in ordered_ui_categories(): if category == "sampler": @@ -797,7 +797,9 @@ def create_ui(): with FormRow(): cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="img2img_cfg_scale") image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit") - denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + with FormRow(): + upscaler = gr.Dropdown(label="Upscaler", elem_id="img2img_upscaler", choices=[*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]], value=shared.latent_upscale_default_mode) + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") elif category == "seed": seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('img2img') @@ -934,6 +936,7 @@ def create_ui(): height, width, scale, + upscaler, resize_mode, inpaint_full_res, inpaint_full_res_padding, @@ -1019,7 +1022,8 @@ def create_ui(): (seed, "Seed"), (width, "Size-1"), (height, "Size-2"), - (scale, "Img2Img Upscale"), + (scale, "Img2Img upscale"), + (upscaler, "Img2Img upscaler"), (batch_size, "Batch size"), (subseed, "Variation seed"), (subseed_strength, "Variation seed strength"), diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 3895a795..3f6c1997 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -220,6 +220,7 @@ axis_options = [ AxisOption("Clip skip", int, apply_clip_skip), AxisOption("Denoising", float, apply_field("denoising_strength")), AxisOptionTxt2Img("Hires upscaler", str, apply_field("hr_upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]), + AxisOptionImg2Img("Upscaler", str, apply_field("upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]), AxisOptionImg2Img("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight")), AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: list(sd_vae.vae_dict)), AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)), diff --git a/style.css b/style.css index 7d58b3b2..e824256f 100644 --- a/style.css +++ b/style.css @@ -779,7 +779,7 @@ footer { #img2img_finalres{ min-height: 0 !important; padding: .625rem .75rem; - margin-left: -0.75em + margin-left: 0.25em } #img2img_finalres .resolution{ From 75e7eb9172fb62eb6fbbcaf71bdd4273b44acc52 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:36:15 -0400 Subject: [PATCH 11/95] img2img resolution preview should use currently selected tab's image --- javascript/ui.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/javascript/ui.js b/javascript/ui.js index 8aa4a459..e564aabb 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -106,7 +106,14 @@ function create_tab_index_args(tabId, args){ function get_img2img_tab_index() { let res = args_to_array(arguments) - res.splice(-2) + res.splice(-2) // gradio also sends outputs to the arguments, pop them off + res[0] = get_tab_index('mode_img2img') + return res +} + +function get_img2img_tab_index_for_res_preview() { + let res = args_to_array(arguments) + res.splice(-1) // gradio also sends outputs to the arguments, pop them off res[0] = get_tab_index('mode_img2img') return res } @@ -345,7 +352,7 @@ function selectCheckpoint(name){ } -function onCalcResolutionImg2Img(init_img, scale, width, height, resize_mode){ +function onCalcResolutionImg2Img(mode, scale, width, height, resize_mode, init_img, sketch, init_img_with_mask, inpaint_color_sketch, init_img_inpaint){ i2iScale = gradioApp().getElementById('img2img_scale') i2iWidth = gradioApp().getElementById('img2img_width') i2iHeight = gradioApp().getElementById('img2img_height') @@ -354,5 +361,5 @@ function onCalcResolutionImg2Img(init_img, scale, width, height, resize_mode){ setInactive(i2iWidth, scale > 1) setInactive(i2iHeight, scale > 1) - return [init_img, width, height, scale, resize_mode] + return []; } From 5c7ab90a4ea0ae907bb915c9ddbe40c529b7bcc9 Mon Sep 17 00:00:00 2001 From: LipeCarmel <44252177+LipeCarmel@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:48:22 -0300 Subject: [PATCH 12/95] loopback.py Colab compatibility and bug fix This code (suggested by @abvgdeabvgde2 ) literally does the same thing and it does not break with Python 3.9, making it helpful for Google Colab users (me included). fixes #8927 Also a partial fix for #8902 but it does not resolve the unresponsive UI problem faced by @Archon332 --- scripts/loopback.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts/loopback.py b/scripts/loopback.py index 9c388aa8..d3065fe6 100644 --- a/scripts/loopback.py +++ b/scripts/loopback.py @@ -54,15 +54,12 @@ class Script(scripts.Script): return strength progress = loop / (loops - 1) - match denoising_curve: - case "Aggressive": - strength = math.sin((progress) * math.pi * 0.5) - - case "Lazy": - strength = 1 - math.cos((progress) * math.pi * 0.5) - - case _: - strength = progress + if denoising_curve == "Aggressive": + strength = math.sin((progress) * math.pi * 0.5) + elif denoising_curve == "Lazy": + strength = 1 - math.cos((progress) * math.pi * 0.5) + else: + strength = progress change = (final_denoising_strength - initial_denoising_strength) * strength return initial_denoising_strength + change From db602b100e271e902e120fa8aa5c859e3ef3962f Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 25 Mar 2023 14:11:38 -0400 Subject: [PATCH 13/95] Revert "Comment fix" This reverts commit f04bd037a51de3c65072581d9a7dfed1d0d2887e. --- modules/api/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index 8c06cf20..afbc202a 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -217,7 +217,7 @@ class Api: if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] - # Remove always on scripts that were not included in the request by resetting the script list in our ScriptRunner + # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner script_runner.alwayson_scripts.clear() script_runner.alwayson_scripts = alwayson_script_to_run script_runner.scripts.clear() From f3715795710b3b39029325b88a2c101279c2dd2c Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 25 Mar 2023 14:11:46 -0400 Subject: [PATCH 14/95] Revert "Made copies of global scriptrunners, now we clear the copied scriptrunner of alwayson_scripts and only add back the ones that that were requested" This reverts commit dfa258de5f87ea7f5ff49e29ddf6e7b34ff8ebff. --- modules/api/api.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index afbc202a..35e17afc 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -3,7 +3,6 @@ import io import time import datetime import uvicorn -import copy from threading import Lock from io import BytesIO from gradio.processing_utils import decode_base64_to_file @@ -203,7 +202,6 @@ class Api: script_args[0] = 0 # Now check for always on scripts - alwayson_script_to_run = [] # list to replace the one from the global ScriptRunner we copied if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): for alwayson_script_name in request.alwayson_scripts.keys(): alwayson_script = self.get_script(alwayson_script_name, script_runner) @@ -212,21 +210,13 @@ class Api: # Selectable script in always on script param check if alwayson_script.alwayson == False: raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - # all good, so add to run list and set its args if any - alwayson_script_to_run.append(alwayson_script) + # always on script with no arg should always run so you don't really need to add them to the requests if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] - - # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner - script_runner.alwayson_scripts.clear() - script_runner.alwayson_scripts = alwayson_script_to_run - script_runner.scripts.clear() - script_runner.scripts = alwayson_script_to_run + script_runner.selectable_scripts - return script_args def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): - script_runner = copy.copy(scripts.scripts_txt2img) # copy so we don't overwrite our globals + script_runner = scripts.scripts_txt2img if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() @@ -278,7 +268,7 @@ class Api: if mask: mask = decode_base64_to_image(mask) - script_runner = copy.copy(scripts.scripts_img2img) # copy so we don't overwrite our globals + script_runner = scripts.scripts_img2img if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() From 23f6dfce4cd3969dab7e707142e7da7c389704fa Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 25 Mar 2023 14:16:35 -0400 Subject: [PATCH 15/95] Reworked this PR, now we have 2 default arg list (one for each tab) that will be initialized on the first api request and then reused afterwards. The init_script_args copies the corresponding default list and applies the modifications asked by the api request. --- modules/api/api.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 35e17afc..596b20b2 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -3,6 +3,7 @@ import io import time import datetime import uvicorn +import gradio as gr from threading import Lock from io import BytesIO from gradio.processing_utils import decode_base64_to_file @@ -152,6 +153,9 @@ class Api: self.add_api_route("/sdapi/v1/memory", self.get_memory, methods=["GET"], response_model=MemoryResponse) self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=ScriptsList) + self.default_script_arg_txt2img = [] + self.default_script_arg_img2img = [] + def add_api_route(self, path: str, endpoint, **kwargs): if shared.cmd_opts.api_auth: return self.app.add_api_route(path, endpoint, dependencies=[Depends(self.auth)], **kwargs) @@ -185,7 +189,7 @@ class Api: script_idx = script_name_to_index(script_name, script_runner.scripts) return script_runner.scripts[script_idx] - def init_script_args(self, request, selectable_scripts, selectable_idx, script_runner): + def init_default_script_args(self, script_runner): #find max idx from the scripts in runner and generate a none array to init script_args last_arg_index = 1 for script in script_runner.scripts: @@ -193,13 +197,24 @@ class Api: last_arg_index = script.args_to # None everywhere except position 0 to initialize script args script_args = [None]*last_arg_index + script_args[0] = 0 + + # get default values + with gr.Blocks(): # will throw errors calling ui function without this + for script in script_runner.scripts: + if script.ui(script.is_img2img): + ui_default_values = [] + for elem in script.ui(script.is_img2img): + ui_default_values.append(elem.value) + script_args[script.args_from:script.args_to] = ui_default_values + return script_args + + def init_script_args(self, request, default_script_args, selectable_scripts, selectable_idx, script_runner): + script_args = default_script_args.copy() # position 0 in script_arg is the idx+1 of the selectable script that is going to be run when using scripts.scripts_*2img.run() if selectable_scripts: script_args[selectable_scripts.args_from:selectable_scripts.args_to] = request.script_args script_args[0] = selectable_idx + 1 - else: - # when [0] = 0 no selectable script to run - script_args[0] = 0 # Now check for always on scripts if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): @@ -220,6 +235,8 @@ class Api: if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() + if not self.default_script_arg_txt2img: + self.default_script_arg_txt2img = self.init_default_script_args(script_runner) selectable_scripts, selectable_script_idx = self.get_selectable_script(txt2imgreq.script_name, script_runner) populate = txt2imgreq.copy(update={ # Override __init__ params @@ -235,7 +252,7 @@ class Api: args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them args.pop('alwayson_scripts', None) - script_args = self.init_script_args(txt2imgreq, selectable_scripts, selectable_script_idx, script_runner) + script_args = self.init_script_args(txt2imgreq, self.default_script_arg_txt2img, selectable_scripts, selectable_script_idx, script_runner) send_images = args.pop('send_images', True) args.pop('save_images', None) @@ -272,6 +289,8 @@ class Api: if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() + if not self.default_script_arg_img2img: + self.default_script_arg_img2img = self.init_default_script_args(script_runner) selectable_scripts, selectable_script_idx = self.get_selectable_script(img2imgreq.script_name, script_runner) populate = img2imgreq.copy(update={ # Override __init__ params @@ -289,7 +308,7 @@ class Api: args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them args.pop('alwayson_scripts', None) - script_args = self.init_script_args(img2imgreq, selectable_scripts, selectable_script_idx, script_runner) + script_args = self.init_script_args(img2imgreq, self.default_script_arg_img2img, selectable_scripts, selectable_script_idx, script_runner) send_images = args.pop('send_images', True) args.pop('save_images', None) From c5f9f7c23759f9a74fa2b563451569c8926604ba Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:26:36 -0400 Subject: [PATCH 16/95] Use .success() callback on img2img preview inputs change --- javascript/ui.js | 8 - modules/ui.py | 8 +- style.css | 818 ++++++++++++++++------------------------------- 3 files changed, 285 insertions(+), 549 deletions(-) diff --git a/javascript/ui.js b/javascript/ui.js index e564aabb..7aa30dc1 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -118,14 +118,6 @@ function get_img2img_tab_index_for_res_preview() { return res } -function get_img2img_tab_index_for_res_preview() { - let res = args_to_array(arguments) - res.splice(-1) // gradio also sends outputs to the arguments, pop them off - res[0] = get_tab_index('mode_img2img') - debugger; - return res -} - function create_submit_args(args){ res = [] for(var i=0;i div{ + flex: 0; + white-space: nowrap; + min-width: auto; +} + +button.custom-button{ + border-radius: var(--button-large-radius); + padding: var(--button-large-padding); + font-weight: var(--button-large-text-weight); + border: var(--button-border-width) solid var(--button-secondary-border-color); + background: var(--button-secondary-background-fill); + color: var(--button-secondary-text-color); + font-size: var(--button-large-text-size); + display: inline-flex; + justify-content: center; + align-items: center; + transition: var(--button-transition); + box-shadow: var(--button-shadow); + text-align: center; +} + + +/* txt2img/img2img specific */ + +.block.token-counter{ position: absolute; display: inline-block; - right: 2em; + right: 1em; min-width: 0 !important; width: auto; z-index: 100; + top: -0.75em; } -.token-counter.error span{ +.block.token-counter span{ + background: var(--input-background-fill) !important; + box-shadow: 0 0 0.0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075); + border: 2px solid rgba(192,192,192,0.4) !important; + border-radius: 0.4em; +} + +.block.token-counter.error span{ box-shadow: 0 0 0.0 0.3em rgba(255,0,0,0.15), inset 0 0 0.6em rgba(255,0,0,0.075); border: 2px solid rgba(255,0,0,0.4) !important; } -.token-counter div{ +.block.token-counter div{ display: inline; } -.token-counter span{ +.block.token-counter span{ padding: 0.1em 0.75em; } -#sh{ - min-width: 2em; - min-height: 2em; - max-width: 2em; - max-height: 2em; - flex-grow: 0; - padding-left: 0.25em; - padding-right: 0.25em; - margin: 0.1em 0; - opacity: 0%; - cursor: default; +[id$=_subseed_show]{ + min-width: auto !important; + flex-grow: 0 !important; + display: flex; } -.output-html p {margin: 0 0.5em;} - -.row > *, -.row > .gr-form > * { - min-width: min(120px, 100%); - flex: 1 1 0%; +[id$=_subseed_show] label{ + margin-bottom: 0.5em; + align-self: end; } .performance { @@ -75,196 +223,94 @@ object-fit: scale-down; } #txt2img_actions_column, #img2img_actions_column { - margin: 0.35rem 0.75rem 0.35rem 0; + gap: 0.5em; } -#script_list { - padding: .625rem .75rem 0 .625rem; -} -.justify-center.overflow-x-scroll { - justify-content: left; -} - -.justify-center.overflow-x-scroll button:first-of-type { - margin-left: auto; -} - -.justify-center.overflow-x-scroll button:last-of-type { - margin-right: auto; -} - -[id$=_random_seed], [id$=_random_subseed], [id$=_reuse_seed], [id$=_reuse_subseed], #open_folder{ - min-width: 2.3em; - height: 2.5em; - flex-grow: 0; - padding-left: 0.25em; - padding-right: 0.25em; -} - -#hidden_element{ - display: none; -} - -[id$=_seed_row], [id$=_subseed_row]{ - gap: 0.5rem; - padding: 0.6em; -} - -[id$=_subseed_show_box]{ - min-width: auto; - flex-grow: 0; -} - -[id$=_subseed_show_box] > div{ - border: 0; - height: 100%; -} - -[id$=_subseed_show]{ - min-width: auto; - flex-grow: 0; - padding: 0; -} - -[id$=_subseed_show] label{ - height: 100%; -} - -#txt2img_actions_column, #img2img_actions_column{ - gap: 0; - margin-right: .75rem; -} - #txt2img_tools, #img2img_tools{ gap: 0.4em; } -#interrogate_col{ +.interrogate-col{ min-width: 0 !important; - max-width: 8em !important; - margin-right: 1em; - gap: 0; + max-width: fit-content; + gap: 0.5em; } -#interrogate, #deepbooru{ - margin: 0em 0.25em 0.5em 0.25em; - min-width: 8em; - max-width: 8em; +.interrogate-col > button{ + flex: 1; } -#style_pos_col, #style_neg_col{ - min-width: 8em !important; +.generate-box{ + position: relative; } - -#txt2img_styles_row, #img2img_styles_row{ - gap: 0.25em; - margin-top: 0.3em; -} - -#txt2img_styles_row > button, #img2img_styles_row > button{ - margin: 0; -} - -#txt2img_styles, #img2img_styles{ - padding: 0; -} - -#txt2img_styles > label > div, #img2img_styles > label > div{ - min-height: 3.2em; -} - -ul.list-none{ - max-height: 35em; - z-index: 2000; -} - -.gr-form{ - background: transparent; -} - -.my-4{ - margin-top: 0; - margin-bottom: 0; -} - -#resize_mode{ - flex: 1.5; -} - -button{ - align-self: stretch !important; -} - -.overflow-hidden, .gr-panel{ - overflow: visible !important; -} - -#x_type, #y_type{ - max-width: 10em; -} - -#txt2img_preview, #img2img_preview, #ti_preview{ +.gradio-button.generate-box-skip, .gradio-button.generate-box-interrupt{ position: absolute; - width: 320px; - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - margin-top: 34px; - z-index: 100; - border: none; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -@media screen and (min-width: 768px) { - #txt2img_preview, #img2img_preview, #ti_preview { - position: absolute; - } -} - -@media screen and (max-width: 767px) { - #txt2img_preview, #img2img_preview, #ti_preview { - position: relative; - } -} - -#txt2img_preview div.left-0.top-0, #img2img_preview div.left-0.top-0, #ti_preview div.left-0.top-0{ + width: 50%; + height: 100%; display: none; + background: #b4c0cc; +} +.gradio-button.generate-box-skip:hover, .gradio-button.generate-box-interrupt:hover{ + background: #c2cfdb; +} +.gradio-button.generate-box-interrupt{ + left: 0; + border-radius: 0.5rem 0 0 0.5rem; +} +.gradio-button.generate-box-skip{ + right: 0; + border-radius: 0 0.5rem 0.5rem 0; } -fieldset span.text-gray-500, .gr-block.gr-box span.text-gray-500, label.block span{ - position: absolute; - top: -0.7em; - line-height: 1.2em; - padding: 0; - margin: 0 0.5em; - - background-color: white; - box-shadow: 6px 0 6px 0px white, -6px 0 6px 0px white; - - z-index: 300; +#txtimg_hr_finalres, #img2img_finalres { + min-height: 0 !important; + padding: .625rem .75rem; + margin-left: -0.75em } -.dark fieldset span.text-gray-500, .dark .gr-block.gr-box span.text-gray-500, .dark label.block span{ - background-color: rgb(31, 41, 55); - box-shadow: none; - border: 1px solid rgba(128, 128, 128, 0.1); - border-radius: 6px; - padding: 0.1em 0.5em; +#txtimg_hr_finalres .resolution, #img2img_finalres .resolution{ + font-weight: bold; } -#txt2img_column_batch, #img2img_column_batch{ +.inactive{ + opacity: 0.5; +} + +[id$=_column_batch]{ min-width: min(13.5em, 100%) !important; } -#settings fieldset span.text-gray-500, #settings .gr-block.gr-box span.text-gray-500, #settings label.block span{ - position: relative; - border: none; - margin-right: 8em; +div.dimensions-tools{ + min-width: 0 !important; + max-width: fit-content; + flex-direction: row; + align-content: center; } -#settings .gr-panel div.flex-col div.justify-between div{ - position: relative; - z-index: 200; +#mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{ + height: 480px !important; + max-height: 480px !important; + min-height: 480px !important; +} + +.image-buttons button{ + min-width: auto; +} + +.infotext { + overflow-wrap: break-word; +} + +/* settings */ +#quicksettings { + width: fit-content; +} + +#quicksettings > div, #quicksettings > fieldset{ + max-width: 24em; + min-width: 24em; + padding: 0; + border: none; + box-shadow: none; + background: none; } #settings{ @@ -276,17 +322,18 @@ fieldset span.text-gray-500, .gr-block.gr-box span.text-gray-500, label.block s margin-left: 10em; } -#settings > div.flex-wrap{ +#settings > div.tab-nav{ float: left; display: block; margin-left: 0; width: 10em; } -#settings > div.flex-wrap button{ +#settings > div.tab-nav button{ display: block; border: none; text-align: left; + white-space: initial; } #settings_result{ @@ -294,29 +341,8 @@ fieldset span.text-gray-500, .gr-block.gr-box span.text-gray-500, label.block s margin: 0 1.2em; } -input[type="range"]{ - margin: 0.5em 0 -0.3em 0; -} - -#mask_bug_info { - text-align: center; - display: block; - margin-top: -0.75em; - margin-bottom: -0.75em; -} - -#txt2img_negative_prompt, #img2img_negative_prompt{ -} - -/* gradio 3.8 adds opacity to progressbar which makes it blink; disable it here */ -.transition.opacity-20 { - opacity: 1 !important; -} - -/* more gradio's garbage cleanup */ -.min-h-\[4rem\] { min-height: unset !important; } -.min-h-\[6rem\] { min-height: unset !important; } +/* live preview */ .progressDiv{ position: relative; height: 20px; @@ -362,6 +388,8 @@ input[type="range"]{ height: 100%; } +/* fullscreen popup (ie in Lora's (i) button) */ + .popup-metadata{ color: black; background: white; @@ -402,87 +430,54 @@ input[type="range"]{ padding: 2em; } +/* fullpage image viewer */ + #lightboxModal{ - display: none; - position: fixed; - z-index: 1001; - padding-top: 100px; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(20, 20, 20, 0.95); - user-select: none; - -webkit-user-select: none; + display: none; + position: fixed; + z-index: 1001; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(20, 20, 20, 0.95); + user-select: none; + -webkit-user-select: none; + flex-direction: column; } .modalControls { - display: grid; - grid-template-columns: 32px 32px 32px 1fr 32px; - grid-template-areas: "zoom tile save space close"; - position: absolute; - top: 0; - left: 0; - right: 0; - padding: 16px; - gap: 16px; + display: flex; + gap: 1em; + padding: 1em; background-color: rgba(0,0,0,0.2); } - .modalClose { - grid-area: close; + margin-left: auto; } - -.modalZoom { - grid-area: zoom; -} - -.modalSave { - grid-area: save; -} - -.modalTileImage { - grid-area: tile; -} - -.modalClose, -.modalZoom, -.modalTileImage { - color: white; - font-size: 35px; - font-weight: bold; - cursor: pointer; -} - -.modalSave { +.modalControls span{ color: white; - font-size: 28px; - margin-top: 8px; + font-size: 35px; font-weight: bold; cursor: pointer; + width: 1em; } -.modalClose:hover, -.modalClose:focus, -.modalSave:hover, -.modalSave:focus, -.modalZoom:hover, -.modalZoom:focus { - color: #999; - text-decoration: none; - cursor: pointer; +.modalControls span:hover, .modalControls span:focus{ + color: #999; + text-decoration: none; } -#modalImage { +#lightboxModal > img { display: block; margin: auto; width: auto; } -.modalImageFullscreen { +#lightboxModal > img.modalImageFullscreen{ object-fit: contain; - height: 90%; + height: 100%; } .modalPrev, @@ -512,45 +507,7 @@ input[type="range"]{ background-color: rgba(0, 0, 0, 0.8); } -#imageARPreview{ - position:absolute; - top:0px; - left:0px; - border:2px solid red; - background:rgba(255, 0, 0, 0.3); - z-index: 900; - pointer-events:none; - display:none -} - -#txt2img_generate_box, #img2img_generate_box{ - position: relative; -} - -#txt2img_interrupt, #img2img_interrupt, #txt2img_skip, #img2img_skip{ - position: absolute; - width: 50%; - height: 100%; - background: #b4c0cc; - display: none; -} - -#txt2img_interrupt, #img2img_interrupt{ - left: 0; - border-radius: 0.5rem 0 0 0.5rem; -} -#txt2img_skip, #img2img_skip{ - right: 0; - border-radius: 0 0.5rem 0.5rem 0; -} - -.red { - color: red; -} - -.gallery-item { - --tw-bg-opacity: 0 !important; -} +/* context menu (ie for the generate button) */ #context-menu{ z-index:9999; @@ -579,61 +536,8 @@ input[type="range"]{ background: #a55000; } -#quicksettings { - width: fit-content; -} -#quicksettings > div, #quicksettings > fieldset{ - max-width: 24em; - min-width: 24em; - padding: 0; - border: none; - box-shadow: none; - background: none; - margin-right: 10px; -} - -#quicksettings > div > div > div > label > span { - position: relative; - margin-right: 9em; - margin-bottom: -1em; -} - -canvas[key="mask"] { - z-index: 12 !important; - filter: invert(); - mix-blend-mode: multiply; - pointer-events: none; -} - - -/* gradio 3.4.1 stuff for editable scrollbar values */ -.gr-box > div > div > input.gr-text-input{ - position: absolute; - right: 0.5em; - top: -0.6em; - z-index: 400; - width: 6em; -} -#quicksettings .gr-box > div > div > input.gr-text-input { - top: -1.12em; -} - -.row.gr-compact{ - overflow: visible; -} - -#img2img_image, #img2img_image > .h-60, #img2img_image > .h-60 > div, #img2img_image > .h-60 > div > img, -#img2img_sketch, #img2img_sketch > .h-60, #img2img_sketch > .h-60 > div, #img2img_sketch > .h-60 > div > img, -#img2maskimg, #img2maskimg > .h-60, #img2maskimg > .h-60 > div, #img2maskimg > .h-60 > div > img, -#inpaint_sketch, #inpaint_sketch > .h-60, #inpaint_sketch > .h-60 > div, #inpaint_sketch > .h-60 > div > img -{ - height: 480px !important; - max-height: 480px !important; - min-height: 480px !important; -} - -/* Extensions */ +/* extensions */ #tab_extensions table{ border-collapse: collapse; @@ -646,6 +550,7 @@ canvas[key="mask"] { #tab_extensions table input[type="checkbox"]{ margin-right: 0.5em; + appearance: checkbox; } #tab_extensions button{ @@ -670,74 +575,7 @@ canvas[key="mask"] { font-size: 90%; } -#image_buttons_txt2img button, #image_buttons_img2img button, #image_buttons_extras button{ - min-width: auto; - padding-left: 0.5em; - padding-right: 0.5em; -} - -.gr-form{ - background-color: white; -} - -.dark .gr-form{ - background-color: rgb(31 41 55 / var(--tw-bg-opacity)); -} - -.gr-button-tool, .gr-button-tool-top{ - max-width: 2.5em; - min-width: 2.5em !important; - height: 2.4em; -} - -.gr-button-tool{ - margin: 0.6em 0em 0.55em 0; -} - -.gr-button-tool-top, #settings .gr-button-tool{ - margin: 1.6em 0.7em 0.55em 0; -} - - -#modelmerger_results_container{ - margin-top: 1em; - overflow: visible; -} - -#modelmerger_models{ - gap: 0; -} - - -#quicksettings .gr-button-tool{ - margin: 0; - border-color: unset; - background-color: unset; -} - -#modelmerger_interp_description>p { - margin: 0!important; - text-align: center; -} -#modelmerger_interp_description { - margin: 0.35rem 0.75rem 1.23rem; -} -#img2img_settings > div.gr-form, #txt2img_settings > div.gr-form { - padding-top: 0.9em; - padding-bottom: 0.9em; -} -#txt2img_settings { - padding-top: 1.16em; - padding-bottom: 0.9em; -} -#img2img_settings { - padding-bottom: 0.9em; -} - -#img2img_settings div.gr-form .gr-form, #txt2img_settings div.gr-form .gr-form, #train_tabs div.gr-form .gr-form{ - border: none; - padding-bottom: 0.5em; -} +/* replace original footer with ours */ footer { display: none !important; @@ -756,99 +594,7 @@ footer { opacity: 0.85; } -#txtimg_hr_finalres{ - min-height: 0 !important; - padding: .625rem .75rem; - margin-left: -0.75em -} - -#txtimg_hr_finalres .resolution{ - font-weight: bold; -} - -#txt2img_checkboxes, #img2img_checkboxes{ - margin-bottom: 0.5em; - margin-left: 0em; -} -#txt2img_checkboxes > div, #img2img_checkboxes > div{ - flex: 0; - white-space: nowrap; - min-width: auto; -} - -#img2img_finalres{ - min-height: 0 !important; - padding: .625rem .75rem; - margin-left: 0.25em -} - -#img2img_finalres .resolution{ - font-weight: bold; -} - -#img2img_copy_to_img2img, #img2img_copy_to_sketch, #img2img_copy_to_inpaint, #img2img_copy_to_inpaint_sketch{ - margin-left: 0em; -} - -#axis_options { - margin-left: 0em; -} - -.inactive{ - opacity: 0.5; -} - -[id*='_prompt_container']{ - gap: 0; -} - -[id*='_prompt_container'] > div{ - margin: -0.4em 0 0 0; -} - -.gr-compact { - border: none; -} - -.dark .gr-compact{ - background-color: rgb(31 41 55 / var(--tw-bg-opacity)); - margin-left: 0; -} - -.gr-compact{ - overflow: visible; -} - -.gr-compact > *{ -} - -.gr-compact .gr-block, .gr-compact .gr-form{ - border: none; - box-shadow: none; -} - -.gr-compact .gr-box{ - border-radius: .5rem !important; - border-width: 1px !important; -} - -#mode_img2img > div > div{ - gap: 0 !important; -} - -[id*='img2img_copy_to_'] { - border: none; -} - -[id*='img2img_copy_to_'] > button { -} - -[id*='img2img_label_copy_to_'] { - font-size: 1.0em; - font-weight: bold; - text-align: center; - line-height: 2.4em; -} +/* extra networks UI */ .extra-networks > div > [id *= '_extra_']{ margin: 0.3em; @@ -861,12 +607,12 @@ footer { .extra-network-subdirs button{ margin: 0 0.15em; } - -#txt2img_extra_networks .search, #img2img_extra_networks .search{ +.extra-networks .tab-nav .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; + width: 16em; } #txt2img_extra_view, #img2img_extra_view { @@ -898,6 +644,7 @@ footer { text-shadow: 2px 2px 3px black; padding: 0.25em; font-size: 22pt; + width: 1.5em; } .extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; @@ -991,12 +738,15 @@ footer { left: 0; right: 0; padding: 0.5em; - color: white; background: rgba(0,0,0,0.5); box-shadow: 0 0 0.25em 0.25em rgba(0,0,0,0.5); text-shadow: 0 0 0.2em black; } +.extra-network-cards .card .actions *{ + color: white; +} + .extra-network-cards .card .actions:hover{ box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; } @@ -1034,7 +784,3 @@ footer { .extra-network-cards .card ul a:hover{ color: red; } - -[id*='_prompt_container'] > div { - margin: 0!important; -} From 945f6e5e99413ef74c386cdb7f029d1cac93a456 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:44:41 -0400 Subject: [PATCH 17/95] Fix img2img aspect ratio overlay in Gradio 3.23.0 --- javascript/aspectRatioOverlay.js | 47 +++++++++++++++++--------------- style.css | 11 ++++++++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/javascript/aspectRatioOverlay.js b/javascript/aspectRatioOverlay.js index 0f164b82..a8278cca 100644 --- a/javascript/aspectRatioOverlay.js +++ b/javascript/aspectRatioOverlay.js @@ -12,7 +12,7 @@ function dimensionChange(e, is_width, is_height){ currentHeight = e.target.value*1.0 } - var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) + var inImg2img = gradioApp().querySelector("#tab_img2img").style.display == "block"; if(!inImg2img){ return; @@ -22,7 +22,7 @@ function dimensionChange(e, is_width, is_height){ var tabIndex = get_tab_index('mode_img2img') if(tabIndex == 0){ // img2img - targetElement = gradioApp().querySelector('div[data-testid=image] img'); + targetElement = gradioApp().querySelector('#img2img_image div[data-testid=image] img'); } else if(tabIndex == 1){ //Sketch targetElement = gradioApp().querySelector('#img2img_sketch div[data-testid=image] img'); } else if(tabIndex == 2){ // Inpaint @@ -30,7 +30,7 @@ function dimensionChange(e, is_width, is_height){ } else if(tabIndex == 3){ // Inpaint sketch targetElement = gradioApp().querySelector('#inpaint_sketch div[data-testid=image] img'); } - + if(targetElement){ @@ -38,7 +38,7 @@ function dimensionChange(e, is_width, is_height){ if(!arPreviewRect){ arPreviewRect = document.createElement('div') arPreviewRect.id = "imageARPreview"; - gradioApp().getRootNode().appendChild(arPreviewRect) + gradioApp().appendChild(arPreviewRect) } @@ -91,23 +91,26 @@ onUiUpdate(function(){ if(arPreviewRect){ arPreviewRect.style.display = 'none'; } - var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) - if(inImg2img){ - let inputs = gradioApp().querySelectorAll('input'); - inputs.forEach(function(e){ - var is_width = e.parentElement.id == "img2img_width" - var is_height = e.parentElement.id == "img2img_height" + var tabImg2img = gradioApp().querySelector("#tab_img2img"); + if (tabImg2img) { + var inImg2img = tabImg2img.style.display == "block"; + if(inImg2img){ + let inputs = gradioApp().querySelectorAll('input'); + inputs.forEach(function(e){ + var is_width = e.parentElement.id == "img2img_width" + var is_height = e.parentElement.id == "img2img_height" - if((is_width || is_height) && !e.classList.contains('scrollwatch')){ - e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} ) - e.classList.add('scrollwatch') - } - if(is_width){ - currentWidth = e.value*1.0 - } - if(is_height){ - currentHeight = e.value*1.0 - } - }) - } + if((is_width || is_height) && !e.classList.contains('scrollwatch')){ + e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} ) + e.classList.add('scrollwatch') + } + if(is_width){ + currentWidth = e.value*1.0 + } + if(is_height){ + currentHeight = e.value*1.0 + } + }) + } + } }); diff --git a/style.css b/style.css index 0dcc3e25..b252e64d 100644 --- a/style.css +++ b/style.css @@ -507,6 +507,17 @@ div.dimensions-tools{ background-color: rgba(0, 0, 0, 0.8); } +#imageARPreview { + position: absolute; + top: 0px; + left: 0px; + border: 2px solid red; + background: rgba(255, 0, 0, 0.3); + z-index: 900; + pointer-events: none; + display: none; +} + /* context menu (ie for the generate button) */ #context-menu{ From 5eb7ff776878b38d8f15bc9c4a563f259908bee0 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:52:47 -0400 Subject: [PATCH 18/95] Fix Send to img2img buttons --- javascript/imageviewer.js | 24 +++--------------------- javascript/ui.js | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index 7547e771..d6483562 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -32,13 +32,7 @@ function negmod(n, m) { function updateOnBackgroundChange() { const modalImage = gradioApp().getElementById("modalImage") if (modalImage && modalImage.offsetParent) { - let allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2") - let currentButton = null - allcurrentButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - currentButton = elem; - } - }) + let currentButton = selected_gallery_button(); if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { modalImage.src = currentButton.children[0].src; @@ -50,22 +44,10 @@ function updateOnBackgroundChange() { } function modalImageSwitch(offset) { - var allgalleryButtons = gradioApp().querySelectorAll(".gradio-gallery .thumbnail-item") - var galleryButtons = [] - allgalleryButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - galleryButtons.push(elem); - } - }) + var galleryButtons = all_gallery_buttons(); if (galleryButtons.length > 1) { - var allcurrentButtons = gradioApp().querySelectorAll(".gradio-gallery .thumbnail-item.selected") - var currentButton = null - allcurrentButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - currentButton = elem; - } - }) + var currentButton = selected_gallery_button(); var result = -1 galleryButtons.forEach(function(v, i) { diff --git a/javascript/ui.js b/javascript/ui.js index fcaf5608..4a440193 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -7,9 +7,31 @@ function set_theme(theme){ } } +function all_gallery_buttons() { + var allGalleryButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnails > .thumbnail-item.thumbnail-small'); + var visibleGalleryButtons = []; + allGalleryButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + visibleGalleryButtons.push(elem); + } + }) + return visibleGalleryButtons; +} + +function selected_gallery_button() { + var allCurrentButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnail-item.thumbnail-small.selected'); + var visibleCurrentButton = null; + allCurrentButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + visibleCurrentButton = elem; + } + }) + return visibleCurrentButton; +} + function selected_gallery_index(){ - var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item') - var button = gradioApp().querySelector('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item.\\!ring-2') + var buttons = all_gallery_buttons(); + var button = selected_gallery_button(); var result = -1 buttons.forEach(function(v, i){ if(v==button) { result = i } }) @@ -18,14 +40,18 @@ function selected_gallery_index(){ } function extract_image_from_gallery(gallery){ - if(gallery.length == 1){ - return [gallery[0]] + if (gallery.length == 0){ + return [null]; + } + if (gallery.length == 1){ + return [gallery[0]]; } index = selected_gallery_index() if (index < 0 || index >= gallery.length){ - return [null] + // Use the first image in the gallery as the default + index = 0; } return [gallery[index]]; From d3b188c82d10e8af45357f258c1f65a04cfa4a18 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 15:52:06 -0400 Subject: [PATCH 19/95] Fix padding on accordion/dropdown list elements --- modules/scripts.py | 12 ++++++++++++ style.css | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/scripts.py b/modules/scripts.py index d661be4f..4d0bbd66 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -553,3 +553,15 @@ def IOComponent_init(self, *args, **kwargs): original_IOComponent_init = gr.components.IOComponent.__init__ gr.components.IOComponent.__init__ = IOComponent_init + + +def BlockContext_init(self, *args, **kwargs): + res = original_BlockContext_init(self, *args, **kwargs) + + add_classes_to_gradio_component(self) + + return res + + +original_BlockContext_init = gr.blocks.BlockContext.__init__ +gr.blocks.BlockContext.__init__ = BlockContext_init diff --git a/style.css b/style.css index 0dcc3e25..c7087210 100644 --- a/style.css +++ b/style.css @@ -7,7 +7,7 @@ --block-background-fill: transparent; } -.block.padded{ +.block.padded:not(.gradio-accordion) { padding: 0 !important; } @@ -65,6 +65,19 @@ div.compact{ margin-bottom: 0; } +.gradio-dropdown ul.options { + max-height: 35em; + z-index: 3000; +} + +.gradio-dropdown ul.options li.item { + padding: 0.05em 0; +} + +.gradio-dropdown ul.options li.item.selected { + background-color: var(--secondary-500); +} + .gradio-dropdown div.wrap.wrap.wrap.wrap{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } From abc4d3a6934f2024b7e8c217ae8d5c90bf7c20ed Mon Sep 17 00:00:00 2001 From: SirFrags <85357620+SirFrags@users.noreply.github.com> Date: Sat, 25 Mar 2023 15:59:50 -0400 Subject: [PATCH 20/95] preview replace save params --- modules/ui_extra_networks.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index daea03d6..aaa93969 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -2,8 +2,10 @@ import glob import os.path import urllib.parse from pathlib import Path +from PIL import PngImagePlugin from modules import shared +from modules.images import read_info_from_image import gradio as gr import json import html @@ -290,6 +292,7 @@ def setup_ui(ui, gallery): img_info = images[index if index >= 0 else 0] image = image_from_url_text(img_info) + geninfo, items = read_info_from_image(image) is_allowed = False for extra_page in ui.stored_extra_pages: @@ -299,7 +302,12 @@ def setup_ui(ui, gallery): assert is_allowed, f'writing to {filename} is not allowed' - image.save(filename) + if geninfo: + pnginfo_data = PngImagePlugin.PngInfo() + pnginfo_data.add_text('parameters', geninfo) + image.save(filename, pnginfo=pnginfo_data) + else: + image.save(filename) return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] From 80b26d2a69617b75d2d01c1e6b7d11445815ed4d Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 25 Mar 2023 23:06:33 +0300 Subject: [PATCH 21/95] apply Lora by altering layer's weights instead of adding more calculations in forward() --- extensions-builtin/Lora/lora.py | 72 ++++++++++++++----- .../Lora/scripts/lora_script.py | 12 +++- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index 7c371deb..a737fec3 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -131,7 +131,7 @@ def load_lora(name, filename): with torch.no_grad(): module.weight.copy_(weight) - module.to(device=devices.device, dtype=devices.dtype) + module.to(device=devices.cpu, dtype=devices.dtype) if lora_key == "lora_up.weight": lora_module.up = module @@ -177,29 +177,69 @@ def load_loras(names, multipliers=None): loaded_loras.append(lora) -def lora_forward(module, input, res): - input = devices.cond_cast_unet(input) - if len(loaded_loras) == 0: - return res +def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear): + """ + Applies the currently selected set of Loras to the weight of torch layer self. + If weights already have this particular set of loras applied, does nothing. + If not, restores orginal weights from backup and alters weights according to loras. + """ - lora_layer_name = getattr(module, 'lora_layer_name', None) - for lora in loaded_loras: - module = lora.modules.get(lora_layer_name, None) - if module is not None: - if shared.opts.lora_apply_to_outputs and res.shape == input.shape: - res = res + module.up(module.down(res)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) - else: - res = res + module.up(module.down(input)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + current_names = getattr(self, "lora_current_names", ()) + wanted_names = tuple((x.name, x.multiplier) for x in loaded_loras) - return res + weights_backup = getattr(self, "lora_weights_backup", None) + if weights_backup is None: + weights_backup = self.weight.to(devices.cpu, copy=True) + self.lora_weights_backup = weights_backup + + if current_names != wanted_names: + if weights_backup is not None: + self.weight.copy_(weights_backup) + + lora_layer_name = getattr(self, 'lora_layer_name', None) + for lora in loaded_loras: + module = lora.modules.get(lora_layer_name, None) + if module is None: + continue + + with torch.no_grad(): + up = module.up.weight.to(self.weight.device, dtype=self.weight.dtype) + down = module.down.weight.to(self.weight.device, dtype=self.weight.dtype) + + if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1): + updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3) + else: + updown = up @ down + + self.weight += updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + + setattr(self, "lora_current_names", wanted_names) def lora_Linear_forward(self, input): - return lora_forward(self, input, torch.nn.Linear_forward_before_lora(self, input)) + lora_apply_weights(self) + + return torch.nn.Linear_forward_before_lora(self, input) + + +def lora_Linear_load_state_dict(self: torch.nn.Linear, *args, **kwargs): + setattr(self, "lora_current_names", ()) + setattr(self, "lora_weights_backup", None) + + return torch.nn.Linear_load_state_dict_before_lora(self, *args, **kwargs) def lora_Conv2d_forward(self, input): - return lora_forward(self, input, torch.nn.Conv2d_forward_before_lora(self, input)) + lora_apply_weights(self) + + return torch.nn.Conv2d_forward_before_lora(self, input) + + +def lora_Conv2d_load_state_dict(self: torch.nn.Conv2d, *args, **kwargs): + setattr(self, "lora_current_names", ()) + setattr(self, "lora_weights_backup", None) + + return torch.nn.Conv2d_load_state_dict_before_lora(self, *args, **kwargs) def list_available_loras(): diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index 2e860160..dc329e81 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -9,7 +9,9 @@ from modules import script_callbacks, ui_extra_networks, extra_networks, shared def unload(): torch.nn.Linear.forward = torch.nn.Linear_forward_before_lora + torch.nn.Linear._load_from_state_dict = torch.nn.Linear_load_state_dict_before_lora torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora + torch.nn.Conv2d._load_from_state_dict = torch.nn.Conv2d_load_state_dict_before_lora def before_ui(): @@ -20,11 +22,19 @@ def before_ui(): if not hasattr(torch.nn, 'Linear_forward_before_lora'): torch.nn.Linear_forward_before_lora = torch.nn.Linear.forward +if not hasattr(torch.nn, 'Linear_load_state_dict_before_lora'): + torch.nn.Linear_load_state_dict_before_lora = torch.nn.Linear._load_from_state_dict + if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): torch.nn.Conv2d_forward_before_lora = torch.nn.Conv2d.forward +if not hasattr(torch.nn, 'Conv2d_load_state_dict_before_lora'): + torch.nn.Conv2d_load_state_dict_before_lora = torch.nn.Conv2d._load_from_state_dict + torch.nn.Linear.forward = lora.lora_Linear_forward +torch.nn.Linear._load_from_state_dict = lora.lora_Linear_load_state_dict torch.nn.Conv2d.forward = lora.lora_Conv2d_forward +torch.nn.Conv2d._load_from_state_dict = lora.lora_Conv2d_load_state_dict script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) @@ -33,6 +43,4 @@ script_callbacks.on_before_ui(before_ui) shared.options_templates.update(shared.options_section(('extra_networks', "Extra Networks"), { "sd_lora": shared.OptionInfo("None", "Add Lora to prompt", gr.Dropdown, lambda: {"choices": [""] + [x for x in lora.available_loras]}, refresh=lora.list_available_loras), - "lora_apply_to_outputs": shared.OptionInfo(False, "Apply Lora to outputs rather than inputs when possible (experimental)"), - })) From 254ad09ef3b356cbe945ad7ca476c5dcf8d842f8 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 25 Mar 2023 15:01:10 -0600 Subject: [PATCH 22/95] Update style.css --- style.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/style.css b/style.css index bef15875..7b93811b 100644 --- a/style.css +++ b/style.css @@ -786,8 +786,8 @@ footer { } button[id$='_extra_networks'] { - margin: 0.6em 0em 0.55em 0; - max-width: 2.5em; - min-width: 2.5em !important; + max-width: 2.2em; + min-width: 2.2em !important; height: 2.4em; + line-height: 1em !important; } From d64ff4248bab7fd4f5908f482e4ea2fa22f671ea Mon Sep 17 00:00:00 2001 From: bluelovers Date: Sun, 26 Mar 2023 06:15:09 +0800 Subject: [PATCH 23/95] remove changes in textual_inversion.py --- modules/textual_inversion/textual_inversion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 8b5bb6ce..c63c7d1d 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -129,7 +129,7 @@ class EmbeddingDatabase: if first_id not in self.ids_lookup: self.ids_lookup[first_id] = [] - self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: (len(x[0]), x[0].casefold()), reverse=True) + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True) return embedding @@ -196,7 +196,7 @@ class EmbeddingDatabase: return for root, dirs, fns in os.walk(embdir.path, followlinks=True): - for fn in sorted(fns, key=str.lower): + for fn in fns: try: fullfn = os.path.join(root, fn) From 8a34671fe91e142bce9e5556cca2258b3be9dd6e Mon Sep 17 00:00:00 2001 From: MrCheeze Date: Fri, 24 Mar 2023 22:48:16 -0400 Subject: [PATCH 24/95] Add support for the Variations models (unclip-h and unclip-l) --- launch.py | 2 +- models/karlo/ViT-L-14_stats.th | Bin 0 -> 7079 bytes modules/lowvram.py | 10 +++++--- modules/processing.py | 41 +++++++++++++++++++++--------- modules/sd_models.py | 5 ++++ modules/sd_models_config.py | 7 +++++ modules/sd_samplers_compvis.py | 31 +++++++++++++++++----- modules/sd_samplers_kdiffusion.py | 19 +++++++++----- 8 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 models/karlo/ViT-L-14_stats.th diff --git a/launch.py b/launch.py index b943fed2..e70df7ba 100644 --- a/launch.py +++ b/launch.py @@ -252,7 +252,7 @@ def prepare_environment(): codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git') blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git') - stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "47b6b607fdd31875c9279cd2f4f16b92e4ea958e") + stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf") taming_transformers_commit_hash = os.environ.get('TAMING_TRANSFORMERS_COMMIT_HASH', "24268930bf1dce879235a7fddd0b2355b84d7ea6") k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "5b3af030dd83e0297272d861c19477735d0317ec") codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af") diff --git a/models/karlo/ViT-L-14_stats.th b/models/karlo/ViT-L-14_stats.th new file mode 100644 index 0000000000000000000000000000000000000000..a6a06e94ecaa4f2977972ff991f75db6c90403ea GIT binary patch literal 7079 zcmb7p2UJu`vo0t}k|>HHK*5ZPnDap~9urJA z22=zI6PRIuA?KtdK``9$p8x*$oOADcZ@r$?t9Q??s$RQl*H=}$9PPWRC@E=ZDE*J2 zr_@u)eT&CB@2zV_d%6d>kJ`M!XXaF0CFj45kS?x%N@gAbn-r9z+yVoWVY`6_oB?Yy7(W$)Y*aXOnxt;y?e?^y})Rte_d= zr{dIAv3hI{^i!Ru)HT3QZK1uA;$okly1nu~KaB_vk4-*4YdiwHH~IQ&F4*F^X3HAS z>E0dzntt8P7x`%h_-QTj(_W`Ib6uBpf6BD-(^>bYEZzT-)%}+&y@++cSb7Bb^<3nq zzh;!9y}HJx9wu>Fit~R>$N!^#bd|>bO+TZ@%$z#rAKLkws{YrFx^ga>p0CFBn^zLU zn}=haz>G}~d`+r$|3-e=e@lEHeb6pd*NO?3yri5^%3=;JCufeGq=x#Lbe~ByjmT`V zd%T!Wn&Ngt?SX{f-R5^1zjP82)E=dSUZ233TOQ1)=oUYH#d#uW=t>=KpCjE$-|&xa zH)J&qK`8kc!KUR7XP!ahVEiEwozd;|+D8ZCPez?5t7O!kbI$zS|AdV)f zFq`%vZ2ljU5_;=;CMNf~&Ax2}EngZXcqw$uAxwD9wwCb7u8LQ36ikanq-R($ti`Q}X| z|BwR%amKt4v*?DHF(_zv#Pa$3sX1>Td475{8E{LTN$FNNTa4%3I6RL;|JH>iZz&?8 zI9K?EK4Er`8c1o@3o>l?SQ?Y4N!BJVWJ{SRwX!?Q|4L`DWt#QWq4OLssn;;7zjZJ> zLod8&qF<AG0uC1-%-zoVveCAyazV@&Yg-WK{1_~?=5wO(xcgfIM2%bt+)F}_Uw zY6$P-Pkr<>715s=!&zedOTPNhbpB$~wV2ueIYuumrG0&llU^U*@U`=25RVtX@uh(e ziCxK94403^qZv_TUG7*KvgQjhYV~4EZ(Spa3ks=i;A3*r)Qab1zZK(3HF0KNI5Tr9 zAcKQ*__JS2>34&%I6ujar?DrV8a`|zUyTaMJH2|EyK^Fq-Ek(#Zb}JH5Iu~Ed!OTM zZ31{R7dO&`cwKg=`48$UP-PiEhLNGR%FO9@2hYSzja6H{rIsT~NT^*lITkj7_Bg3d z#;+d?SL3sEw)T6hTtA65AHPSRSDmL>YSz>}SRY5%?&E0-o>MC|6P~|EFQ)p+n!Ogz z#V7q^ygR>-CQ5HraWu`F*R%a3buTd_%fdu->nJUDW@iKGw&Vp_X|>6&WX3jj;*tk_ zax<9(h&0hddxh=VG#4U@G^Upx3P{g6bI9vX8@s}5x5ybUQ~Gw93%S?EV@ZYmP+_4< zt{iEkRmwq#oa9VYBbMRKfai2Vw2&P9noMlCE|~bSfm&sHaqpKabYpLdwSu3#oEHaJ z*qqIYLo2`1*t_RQ#akhJwYewRK3Po1>qHTcb+3_V-%PJ*aY^xgQ>cfdH9PWRJTn#E zB+sX7v%{CeAgWCvy$tWtgK2t5G*(Kq{;Gll6_0tvSHk&UFNBgqTQAT6@|27Sdq-tE zLdarYJr>(EmbYqLUzYsxY?2Y*o4!=aVs~@jlUXV8^xnv^COGbgc**!eu0zy#`jC6KirokXIHbP&APik|8dOI=*2Kug7&Z|)dF zqsHDP!}`dmw0R+dueIB*n|zV@XbopsJauMj^pXXStfAIl)#yw$K3%Z?Sd!_zWAwzZ zc>Ww-6MsSC3R+NM&UoUkNW2-zx7aX{cAXN&HX7ciB!4|Mp4Lvotd6-hRq zZKh(Qcp`ZuCbf@xVAlh}rh4{dcHZIah5972YLtM?vhRsO=X)^6(J}0qu{Z0V6Uw_; zGlqP1HYO#(XQ^;y2HDc58$6tQV6ovKj?<+@;MR`(;3_gh~kY1;>XM;kg;cPg=+zl5GBNFp|a)!CLQ zx|p|BnJuzf%PyEc1U22rRO1F?!uGj{e>0ajYG+c%%OS+1d^X!KOqJIlo6Ub9TZ$#Q zKX`q*cB3o%9AvqNqL`9k1DVd#z_{3WDiB|#;X^KC?n`aNw;pB_0!v8=V!$1Dz^yNasTXX82mh@6)P?bH~it#o5KIrpS{v=4#Pd3EAlVrZ*N|?MpOf zpF(PnXT&$<0H*ZcPjm-W@p>F`L57wWuWX?WKD!KJrN%|{?VI`hx%V;>jW3I-x8*q2 zXW3_d>#{?5Sh9dfc293R3A<{JiM_tl)O>AByS@m`k4Lai zmYaDs=F@l4+uxD5nl|kI1!FkH=JBJh@8=C{`-AtgXgKmGtVeub z!thB-hjm_{J+E7`$pwd*pTSP@ZPN~p3vxH-#sbDc!Q7{RCL z2kpIV2X(5mB%j^2*}Dba?7FH7J3rQi##(FBa|Ro^=I8y{?aLQv?fZ}Ln{|_HeMsnD z)x+3t*vX5uo4|`RaYILqGa{3}(r&YQGZXOzYC9l~7hK(y(f+d_&#a_sE>V1k@Vj(r zi5BMkRz?m4TCi1G%5a(!NetXR^SfTl;k{Tt4uPv&sa)$eiHSYMTtx$^=iVD6RJk-E zOIJos2am)qizaekOb6WW}V$9}zbvlxkF+f|b)y5_j=ED}3;P>6f1* z$MOtV|MaEc9(||7YP+!zNey|sYcjm_W}@Gh5q#e}u5dkQ!E^-b@E28+;cjLm-|0Es zcJDNKz33VlfqlFSCswi`-DuWmsPNy-79{eDC)MfyFj1pp0{_lhcc$Dj3Tk%`(3}Go zXqD~_h?jf9{jvjER}7}+RvkoAEF$7+XWE(TN=bT`DQ@4Qh<=9~9JYke3x?y!R+IU3 za=I0{YJP=WIGju$k2%gJ^P_o3O|B4CwUO|+)R*bJyG-AWdxRQ)Cw|DP^+fgLE<6+6 z=dB*M149qYB_~_EvZ$UH=x@Ek$=UB)@hl~Tgy}71D|KeDYa!uuUhF|w&dukoo*Pg4 zD%-J7CvEw+T?}BGU`UhmMv+bRL2UZ#DePkQM>;7EJ}eo)2lSv}9&uEJ+m2e& z=Hf{l8v4-f-$Urbp@Vq|%^&E%S<92oJ0-G8xnYvQtr-6H`)zc?)dBpX1bw`Jk;a$w zuBE#r=SWan4bScJ32NMWo9=3{<^Qf>!EZTwieEnO0iDxUL7bMDl0CvX=(Ww2Smx|X zOj$Y}hKFr&Epj%qxV)K$<){&dXl3xG?8Si~6{33RS;Dl6-7qy6loY&g7B)u~+nID( zN%zmaPAu2t&>#8>$fT*aS)=6z=(V+y#U7b-iMN0}nK=c&>WxhE1@kluj3P;l79IaMk6h@R85trq8Xx`_6QuOGm9d4?Da|FgK?V^JkZ#k4qj@JH@C!QxC&^#jsl`hsAIa zhVY9J7nY2>QDv}+$cD*NA+~DCFs9-?mQ85F5t~Xp9wmd9O*YCl39(^sCUno{V!mG! zR!%R)$;wK6Ir0^g&*x&DeHGleT%=dOftjNm%FjeF_|T3EO%g1aAj0isP1q{WL3Mru zLY>;6w>AyR$~Djt<)O!nDwJF0AZnZ(+h>d6_ehL0WkMWlZbOdICw!@?hbkw;gSJm7 zUX%w>sxc=!mxhw*0!*mL!i(@WIG*~1%!~C{=9rEo-(vhM5n}ltGIVLrK;M8&)Hh2o z_E|Q}bqnxvSTSyO{|2Ld6}TqIK&5gSLT4&-tsPldZkvbQx3UmhE`k1%5=@PipkZ1y zBK&eNFW8AY)z*n_#<^&*6C%d42n*wckTwgjWTP>suO@^2=VIKvUxlx|v(c{l34(hy z-1Plo*!|IpX&W0c<0iO;2aAv(E5fS8Qlyg-gzc2z!^%`VT$PPZQ!%Pnf5PzG78tB6 zht{efoq1)8CvgbKyf7py`19I`a zt^$S?Ip{cD0HgLwc-o23RwaW{^=oKKDEy7naBheI!?zScwy6ZG(#2SRtPLt-a>2qX zQO*zJ{wQ$a)XvLLQ{lt~Ud(~$bT+no2%&Ucgj)e0Vc(&_8Ed2>#v~uqCI`*AX;^EN z2kG2;s42_Q7^KP_Od8Kc)_h0WT@fNTD4unO2#ueF@Uxr3-O?z+A5SwOF|9{8YcUo| zKA`k=8mi)Da9<@tpX5SR?@YyUYlSU-FGKjNYCL-@!IBFq+_171s6V9;Tk$xQKc(I zm&Za37}kgbV+x^nSPX7JDFRY5ut$)Bc_bGNsKu#xJMP)_0`z^U!rAl{;nXUHKk8(| zDWnBC$`UMn)0=bmsemu!s5QyP&?~xJ+W|3tEV1R1_esI+&&K*OVyLT&VRbwMnTvEd zFQWpiSu29}odV<>72$qf4(x3eH1%?X?frl?;W8{PmY^vk537hA!De~5xU>$DE%~q+ zx|6HZ%*Cm?!JLazA%HxYkM~?CA1#(I|aDnnhL|01-Lz5iW4G=cc~(5 zxLb#VeM&L8UVyBK0;Ff8BV&XFuZO0==RhgSd&yD3mtt4QM>KkrLSMfGE2=Z$_`!^e zDa^vxwpN_AFyMmw2+-mrgWuDWIJ36}yo6+Y`Cf_RZh2@{lVDqG}9(s=_T) zEx@ap5?HO4z*4&g9)eV8SQetTqXfQvz};UxoV%Z%jkxXwxD;-|sS+)2Xgsi> zGvbU)v(fEADw-0Nx%=lE(f7d+E;+aiUECBleOid=dkP?!CW5p+1%X|9aLI!Uuq`ed zx`GEJLX)?JLcKGhH$5g_724lJ(hM4J0sEWOi;1DEAc z$x&eW9tpZU&x2l4Hd-bX;q^B$vS&}_v>#U>@Pa+pPdyXg&C4*=R*r|3Vmz2#i1y41 z#O#$|*|}n5zRbm|vl$4HCU6xoHr$?QDR|Bb{hdF@J&MUgO|cwX&q%SYt`IX6esJcb z0Q1uXxZ%-=y$XG%mg#drExC}iXJJTJ5wu>XV%&Izzid~;6L&c_PcK4fqvHMAl7+EL zTJU^VS1#dYHq;-8@W`fGqA+utC2Ik((N9(q7tadWu_QvMn&azhYz9mCW=wNORNr%Z9EzTuFjLR$!_ImFy z(5xS4Yfykbu60;3QiD5MnT$K9YEf!Xh04$Qcq%`|{WQ&mYJMg*{*Yp>iZ3V7$iU=< z3cvU!MBmf^?rQJ%7^8@9Yr~TuO%TAvEE}==M2IfB{hdHo^6z5Jg`y5f)mAniVC8^K;{7nTv33S^?ZGWVm=y zkz3?_!9yPb9$Ob+U9}?jNRh+qRS^y>5W;A50nU7DKzq-R=qD6Fq`>g^Jx6iJFPCFh zj3S51P~ysWRKw6W87jv0SkWaL5kustFDXY*-gm5R65&Q)W6svU94-g4aQB1^Go~u+ z_NE@^U9#Y?WCVA8>mm+2yK#;e3lLW$g#1GRG|p$>&YXPA-I#$RZeK98R|U#@S93WI z0_gp|oO@v=f^FYobiMuoQ*;Uu_P7epkMhv{U=|m(w+-Ccbi9ogB6@u$G@MHjwKWT; zT!ko@pomq9oFZaZCA75Hx%ut>>5 zMqC#3Ph>%`rU+vlzT?}-Y}oz~AumjTGmpw)?5@ZGMd_&9nF}Y4NN&GtId0|2VLe=m zgzw)VCAAnM{{Sz38U_x`$H|avyvxi&nJ|c3eLDwpR^_7KZ;E%$IS1F5%kjZogzMuQ zpt`aE`}=1?yAIs36W`!lmx;GEA2HEFjAOlXAx`c@-oi}ahXQj_Yau$khqE?wku_vE1C=R#ITynW$Pz{9?IFeQL!|#%KRlpkZvVgQ zhwe(#{#`$8U9-hs(L0$rmHYp0Sp1`lVWau0f)QNwTG~ZX!2Fty_R7DOfA^IB+D*~> zb>na2pB<)ua{sk9()}B!k)l=fSMJec{>l4Sz5Jc`n9e_Vt^UdT*C*=!w{x@l{WtHw z^yp}>rZ(bVM1#AI{5QFxv-Y3kzt-k|?HBd`^FqP*t0$&3W5A#LU(GK^dsVexI~B$6 J7yX~K{{h(9ncn~a literal 0 HcmV?d00001 diff --git a/modules/lowvram.py b/modules/lowvram.py index 042a0254..e254cc13 100644 --- a/modules/lowvram.py +++ b/modules/lowvram.py @@ -55,12 +55,12 @@ def setup_for_low_vram(sd_model, use_medvram): if hasattr(sd_model.cond_stage_model, 'model'): sd_model.cond_stage_model.transformer = sd_model.cond_stage_model.model - # remove four big modules, cond, first_stage, depth (if applicable), and unet from the model and then + # remove several big modules: cond, first_stage, depth/embedder (if applicable), and unet from the model and then # send the model to GPU. Then put modules back. the modules will be in CPU. - stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, getattr(sd_model, 'depth_model', None), sd_model.model - sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.model = None, None, None, None + stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, getattr(sd_model, 'depth_model', None), getattr(sd_model, 'embedder', None), sd_model.model + sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.embedder, sd_model.model = None, None, None, None, None sd_model.to(devices.device) - sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.model = stored + sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.embedder, sd_model.model = stored # register hooks for those the first three models sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu) @@ -69,6 +69,8 @@ def setup_for_low_vram(sd_model, use_medvram): sd_model.first_stage_model.decode = first_stage_model_decode_wrap if sd_model.depth_model: sd_model.depth_model.register_forward_pre_hook(send_me_to_gpu) + if sd_model.embedder: + sd_model.embedder.register_forward_pre_hook(send_me_to_gpu) parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model if hasattr(sd_model.cond_stage_model, 'model'): diff --git a/modules/processing.py b/modules/processing.py index 59717b4c..1451811c 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -78,22 +78,28 @@ def apply_overlay(image, paste_loc, index, overlays): def txt2img_image_conditioning(sd_model, x, width, height): - if sd_model.model.conditioning_key not in {'hybrid', 'concat'}: - # Dummy zero conditioning if we're not using inpainting model. + if sd_model.model.conditioning_key in {'hybrid', 'concat'}: # Inpainting models + + # The "masked-image" in this case will just be all zeros since the entire image is masked. + image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device) + image_conditioning = sd_model.get_first_stage_encoding(sd_model.encode_first_stage(image_conditioning)) + + # Add the fake full 1s mask to the first dimension. + image_conditioning = torch.nn.functional.pad(image_conditioning, (0, 0, 0, 0, 1, 0), value=1.0) + image_conditioning = image_conditioning.to(x.dtype) + + return image_conditioning + + elif sd_model.model.conditioning_key == "crossattn-adm": # UnCLIP models + + return x.new_zeros(x.shape[0], 2*sd_model.noise_augmentor.time_embed.dim, dtype=x.dtype, device=x.device) + + else: + # Dummy zero conditioning if we're not using inpainting or unclip models. # Still takes up a bit of memory, but no encoder call. # Pretty sure we can just make this a 1x1 image since its not going to be used besides its batch size. return x.new_zeros(x.shape[0], 5, 1, 1, dtype=x.dtype, device=x.device) - # The "masked-image" in this case will just be all zeros since the entire image is masked. - image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device) - image_conditioning = sd_model.get_first_stage_encoding(sd_model.encode_first_stage(image_conditioning)) - - # Add the fake full 1s mask to the first dimension. - image_conditioning = torch.nn.functional.pad(image_conditioning, (0, 0, 0, 0, 1, 0), value=1.0) - image_conditioning = image_conditioning.to(x.dtype) - - return image_conditioning - class StableDiffusionProcessing: """ @@ -190,6 +196,14 @@ class StableDiffusionProcessing: return conditioning_image + def unclip_image_conditioning(self, source_image): + c_adm = self.sd_model.embedder(source_image) + if self.sd_model.noise_augmentor is not None: + noise_level = 0 # TODO: Allow other noise levels? + c_adm, noise_level_emb = self.sd_model.noise_augmentor(c_adm, noise_level=repeat(torch.tensor([noise_level]).to(c_adm.device), '1 -> b', b=c_adm.shape[0])) + c_adm = torch.cat((c_adm, noise_level_emb), 1) + return c_adm + def inpainting_image_conditioning(self, source_image, latent_image, image_mask=None): self.is_using_inpainting_conditioning = True @@ -241,6 +255,9 @@ class StableDiffusionProcessing: if self.sampler.conditioning_key in {'hybrid', 'concat'}: return self.inpainting_image_conditioning(source_image, latent_image, image_mask=image_mask) + if self.sampler.conditioning_key == "crossattn-adm": + return self.unclip_image_conditioning(source_image) + # Dummy zero conditioning if we're not using inpainting or depth model. return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1) diff --git a/modules/sd_models.py b/modules/sd_models.py index f0cb1240..c1a80d82 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -383,6 +383,11 @@ def repair_config(sd_config): elif shared.cmd_opts.upcast_sampling: sd_config.model.params.unet_config.params.use_fp16 = True + # For UnCLIP-L, override the hardcoded karlo directory + if hasattr(sd_config.model.params, "noise_aug_config") and hasattr(sd_config.model.params.noise_aug_config.params, "clip_stats_path"): + karlo_path = os.path.join(paths.models_path, 'karlo') + sd_config.model.params.noise_aug_config.params.clip_stats_path = sd_config.model.params.noise_aug_config.params.clip_stats_path.replace("checkpoints/karlo_models", karlo_path) + sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embedding.weight' sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight' diff --git a/modules/sd_models_config.py b/modules/sd_models_config.py index 91c21700..9398f528 100644 --- a/modules/sd_models_config.py +++ b/modules/sd_models_config.py @@ -14,6 +14,8 @@ config_sd2 = os.path.join(sd_repo_configs_path, "v2-inference.yaml") config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-v.yaml") config_sd2_inpainting = os.path.join(sd_repo_configs_path, "v2-inpainting-inference.yaml") config_depth_model = os.path.join(sd_repo_configs_path, "v2-midas-inference.yaml") +config_unclip = os.path.join(sd_repo_configs_path, "v2-1-stable-unclip-l-inference.yaml") +config_unopenclip = os.path.join(sd_repo_configs_path, "v2-1-stable-unclip-h-inference.yaml") config_inpainting = os.path.join(sd_configs_path, "v1-inpainting-inference.yaml") config_instruct_pix2pix = os.path.join(sd_configs_path, "instruct-pix2pix.yaml") config_alt_diffusion = os.path.join(sd_configs_path, "alt-diffusion-inference.yaml") @@ -65,9 +67,14 @@ def is_using_v_parameterization_for_sd2(state_dict): def guess_model_config_from_state_dict(sd, filename): sd2_cond_proj_weight = sd.get('cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight', None) diffusion_model_input = sd.get('model.diffusion_model.input_blocks.0.0.weight', None) + sd2_variations_weight = sd.get('embedder.model.ln_final.weight', None) if sd.get('depth_model.model.pretrained.act_postprocess3.0.project.0.bias', None) is not None: return config_depth_model + elif sd2_variations_weight is not None and sd2_variations_weight.shape[0] == 768: + return config_unclip + elif sd2_variations_weight is not None and sd2_variations_weight.shape[0] == 1024: + return config_unopenclip if sd2_cond_proj_weight is not None and sd2_cond_proj_weight.shape[1] == 1024: if diffusion_model_input.shape[1] == 9: diff --git a/modules/sd_samplers_compvis.py b/modules/sd_samplers_compvis.py index 083da18c..bfcc5574 100644 --- a/modules/sd_samplers_compvis.py +++ b/modules/sd_samplers_compvis.py @@ -70,8 +70,13 @@ class VanillaStableDiffusionSampler: # Have to unwrap the inpainting conditioning here to perform pre-processing image_conditioning = None + uc_image_conditioning = None if isinstance(cond, dict): - image_conditioning = cond["c_concat"][0] + if self.conditioning_key == "crossattn-adm": + image_conditioning = cond["c_adm"] + uc_image_conditioning = unconditional_conditioning["c_adm"] + else: + image_conditioning = cond["c_concat"][0] cond = cond["c_crossattn"][0] unconditional_conditioning = unconditional_conditioning["c_crossattn"][0] @@ -98,8 +103,12 @@ class VanillaStableDiffusionSampler: # Wrap the image conditioning back up since the DDIM code can accept the dict directly. # Note that they need to be lists because it just concatenates them later. if image_conditioning is not None: - cond = {"c_concat": [image_conditioning], "c_crossattn": [cond]} - unconditional_conditioning = {"c_concat": [image_conditioning], "c_crossattn": [unconditional_conditioning]} + if self.conditioning_key == "crossattn-adm": + cond = {"c_adm": image_conditioning, "c_crossattn": [cond]} + unconditional_conditioning = {"c_adm": uc_image_conditioning, "c_crossattn": [unconditional_conditioning]} + else: + cond = {"c_concat": [image_conditioning], "c_crossattn": [cond]} + unconditional_conditioning = {"c_concat": [image_conditioning], "c_crossattn": [unconditional_conditioning]} return x, ts, cond, unconditional_conditioning @@ -176,8 +185,12 @@ class VanillaStableDiffusionSampler: # Wrap the conditioning models with additional image conditioning for inpainting model if image_conditioning is not None: - conditioning = {"c_concat": [image_conditioning], "c_crossattn": [conditioning]} - unconditional_conditioning = {"c_concat": [image_conditioning], "c_crossattn": [unconditional_conditioning]} + if self.conditioning_key == "crossattn-adm": + conditioning = {"c_adm": image_conditioning, "c_crossattn": [conditioning]} + unconditional_conditioning = {"c_adm": torch.zeros_like(image_conditioning), "c_crossattn": [unconditional_conditioning]} + else: + conditioning = {"c_concat": [image_conditioning], "c_crossattn": [conditioning]} + unconditional_conditioning = {"c_concat": [image_conditioning], "c_crossattn": [unconditional_conditioning]} samples = self.launch_sampling(t_enc + 1, lambda: self.sampler.decode(x1, conditioning, t_enc, unconditional_guidance_scale=p.cfg_scale, unconditional_conditioning=unconditional_conditioning)) @@ -195,8 +208,12 @@ class VanillaStableDiffusionSampler: # Wrap the conditioning models with additional image conditioning for inpainting model # dummy_for_plms is needed because PLMS code checks the first item in the dict to have the right shape if image_conditioning is not None: - conditioning = {"dummy_for_plms": np.zeros((conditioning.shape[0],)), "c_crossattn": [conditioning], "c_concat": [image_conditioning]} - unconditional_conditioning = {"c_crossattn": [unconditional_conditioning], "c_concat": [image_conditioning]} + if self.conditioning_key == "crossattn-adm": + conditioning = {"dummy_for_plms": np.zeros((conditioning.shape[0],)), "c_crossattn": [conditioning], "c_adm": image_conditioning} + unconditional_conditioning = {"c_crossattn": [unconditional_conditioning], "c_adm": torch.zeros_like(image_conditioning)} + else: + conditioning = {"dummy_for_plms": np.zeros((conditioning.shape[0],)), "c_crossattn": [conditioning], "c_concat": [image_conditioning]} + unconditional_conditioning = {"c_crossattn": [unconditional_conditioning], "c_concat": [image_conditioning]} samples_ddim = self.launch_sampling(steps, lambda: self.sampler.sample(S=steps, conditioning=conditioning, batch_size=int(x.shape[0]), shape=x[0].shape, verbose=False, unconditional_guidance_scale=p.cfg_scale, unconditional_conditioning=unconditional_conditioning, x_T=x, eta=self.eta)[0]) diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 93f0e55a..e9f08518 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -92,14 +92,21 @@ class CFGDenoiser(torch.nn.Module): batch_size = len(conds_list) repeats = [len(conds_list[i]) for i in range(batch_size)] + if shared.sd_model.model.conditioning_key == "crossattn-adm": + image_uncond = torch.zeros_like(image_cond) + make_condition_dict = lambda c_crossattn, c_adm: {"c_crossattn": c_crossattn, "c_adm": c_adm} + else: + image_uncond = image_cond + make_condition_dict = lambda c_crossattn, c_concat: {"c_crossattn": c_crossattn, "c_concat": [c_concat]} + if not is_edit_model: x_in = torch.cat([torch.stack([x[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [x]) sigma_in = torch.cat([torch.stack([sigma[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [sigma]) - image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_cond]) + image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_uncond]) else: x_in = torch.cat([torch.stack([x[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [x] + [x]) sigma_in = torch.cat([torch.stack([sigma[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [sigma] + [sigma]) - image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_cond] + [torch.zeros_like(self.init_latent)]) + image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_uncond] + [torch.zeros_like(self.init_latent)]) denoiser_params = CFGDenoiserParams(x_in, image_cond_in, sigma_in, state.sampling_step, state.sampling_steps, tensor, uncond) cfg_denoiser_callback(denoiser_params) @@ -116,13 +123,13 @@ class CFGDenoiser(torch.nn.Module): cond_in = torch.cat([tensor, uncond, uncond]) if shared.batch_cond_uncond: - x_out = self.inner_model(x_in, sigma_in, cond={"c_crossattn": [cond_in], "c_concat": [image_cond_in]}) + x_out = self.inner_model(x_in, sigma_in, cond=make_condition_dict([cond_in], image_cond_in)) else: x_out = torch.zeros_like(x_in) for batch_offset in range(0, x_out.shape[0], batch_size): a = batch_offset b = a + batch_size - x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond={"c_crossattn": [cond_in[a:b]], "c_concat": [image_cond_in[a:b]]}) + x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond=make_condition_dict([cond_in[a:b]], image_cond_in[a:b])) else: x_out = torch.zeros_like(x_in) batch_size = batch_size*2 if shared.batch_cond_uncond else batch_size @@ -135,9 +142,9 @@ class CFGDenoiser(torch.nn.Module): else: c_crossattn = torch.cat([tensor[a:b]], uncond) - x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond={"c_crossattn": c_crossattn, "c_concat": [image_cond_in[a:b]]}) + x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond=make_condition_dict(c_crossattn, image_cond_in[a:b])) - x_out[-uncond.shape[0]:] = self.inner_model(x_in[-uncond.shape[0]:], sigma_in[-uncond.shape[0]:], cond={"c_crossattn": [uncond], "c_concat": [image_cond_in[-uncond.shape[0]:]]}) + x_out[-uncond.shape[0]:] = self.inner_model(x_in[-uncond.shape[0]:], sigma_in[-uncond.shape[0]:], cond=make_condition_dict([uncond], image_cond_in[-uncond.shape[0]:])) denoised_params = CFGDenoisedParams(x_out, state.sampling_step, state.sampling_steps) cfg_denoised_callback(denoised_params) From d286df0a7145ac578b40a5c5c8cc47e4268284a6 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 25 Mar 2023 21:00:02 -0600 Subject: [PATCH 25/95] Fix dropdown width --- style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/style.css b/style.css index 0dcc3e25..48973d5a 100644 --- a/style.css +++ b/style.css @@ -56,6 +56,9 @@ div.compact{ .gradio-dropdown ul.options{ z-index: 3000; + min-width: fit-content; + max-width: inherit; + white-space: nowrap; } .gradio-dropdown label span:not(.has-info), From 650ddc9dd3c1d126221682be8270f7fba1b5b6ce Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 26 Mar 2023 10:44:20 +0300 Subject: [PATCH 26/95] Lora support for SD2 --- extensions-builtin/Lora/lora.py | 157 +++++++++++++----- .../Lora/scripts/lora_script.py | 10 ++ 2 files changed, 127 insertions(+), 40 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index d4345ada..edd95f78 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -8,14 +8,27 @@ from modules import shared, devices, sd_models, errors metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20} re_digits = re.compile(r"\d+") -re_unet_down_blocks = re.compile(r"lora_unet_down_blocks_(\d+)_attentions_(\d+)_(.+)") -re_unet_mid_blocks = re.compile(r"lora_unet_mid_block_attentions_(\d+)_(.+)") -re_unet_up_blocks = re.compile(r"lora_unet_up_blocks_(\d+)_attentions_(\d+)_(.+)") -re_text_block = re.compile(r"lora_te_text_model_encoder_layers_(\d+)_(.+)") +re_x_proj = re.compile(r"(.*)_([qkv]_proj)$") +re_compiled = {} + +suffix_conversion = { + "attentions": {}, + "resnets": { + "conv1": "in_layers_2", + "conv2": "out_layers_3", + "time_emb_proj": "emb_layers_1", + "conv_shortcut": "skip_connection", + } +} def convert_diffusers_name_to_compvis(key, is_sd2): - def match(match_list, regex): + def match(match_list, regex_text): + regex = re_compiled.get(regex_text) + if regex is None: + regex = re.compile(regex_text) + re_compiled[regex_text] = regex + r = re.match(regex, key) if not r: return False @@ -26,16 +39,25 @@ def convert_diffusers_name_to_compvis(key, is_sd2): m = [] - if match(m, re_unet_down_blocks): - return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[1]}_1_{m[2]}" + if match(m, r"lora_unet_down_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"): + suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3]) + return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}" - if match(m, re_unet_mid_blocks): - return f"diffusion_model_middle_block_1_{m[1]}" + if match(m, r"lora_unet_mid_block_(attentions|resnets)_(\d+)_(.+)"): + suffix = suffix_conversion.get(m[0], {}).get(m[2], m[2]) + return f"diffusion_model_middle_block_{1 if m[0] == 'attentions' else m[1] * 2}_{suffix}" - if match(m, re_unet_up_blocks): - return f"diffusion_model_output_blocks_{m[0] * 3 + m[1]}_1_{m[2]}" + if match(m, r"lora_unet_up_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"): + suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3]) + return f"diffusion_model_output_blocks_{m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}" - if match(m, re_text_block): + if match(m, r"lora_unet_down_blocks_(\d+)_downsamplers_0_conv"): + return f"diffusion_model_input_blocks_{3 + m[0] * 3}_0_op" + + if match(m, r"lora_unet_up_blocks_(\d+)_upsamplers_0_conv"): + return f"diffusion_model_output_blocks_{2 + m[0] * 3}_{2 if m[0]>0 else 1}_conv" + + if match(m, r"lora_te_text_model_encoder_layers_(\d+)_(.+)"): if is_sd2: if 'mlp_fc1' in m[1]: return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}" @@ -109,16 +131,22 @@ def load_lora(name, filename): sd = sd_models.read_state_dict(filename) - keys_failed_to_match = [] + keys_failed_to_match = {} is_sd2 = 'model_transformer_resblocks' in shared.sd_model.lora_layer_mapping for key_diffusers, weight in sd.items(): - fullkey = convert_diffusers_name_to_compvis(key_diffusers, is_sd2) - key, lora_key = fullkey.split(".", 1) + key_diffusers_without_lora_parts, lora_key = key_diffusers.split(".", 1) + key = convert_diffusers_name_to_compvis(key_diffusers_without_lora_parts, is_sd2) sd_module = shared.sd_model.lora_layer_mapping.get(key, None) + if sd_module is None: - keys_failed_to_match.append(key_diffusers) + m = re_x_proj.match(key) + if m: + sd_module = shared.sd_model.lora_layer_mapping.get(m.group(1), None) + + if sd_module is None: + keys_failed_to_match[key_diffusers] = key continue lora_module = lora.modules.get(key, None) @@ -133,7 +161,9 @@ def load_lora(name, filename): if type(sd_module) == torch.nn.Linear: module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) elif type(sd_module) == torch.nn.modules.linear.NonDynamicallyQuantizableLinear: - module = torch.nn.modules.linear.NonDynamicallyQuantizableLinear(weight.shape[1], weight.shape[0], bias=False) + module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) + elif type(sd_module) == torch.nn.MultiheadAttention: + module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) elif type(sd_module) == torch.nn.Conv2d: module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False) else: @@ -190,54 +220,94 @@ def load_loras(names, multipliers=None): loaded_loras.append(lora) -def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear): +def lora_calc_updown(lora, module, target): + with torch.no_grad(): + up = module.up.weight.to(target.device, dtype=target.dtype) + down = module.down.weight.to(target.device, dtype=target.dtype) + + if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1): + updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3) + else: + updown = up @ down + + updown = updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + + return updown + + +def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear | torch.nn.MultiheadAttention): """ - Applies the currently selected set of Loras to the weight of torch layer self. + Applies the currently selected set of Loras to the weights of torch layer self. If weights already have this particular set of loras applied, does nothing. If not, restores orginal weights from backup and alters weights according to loras. """ + lora_layer_name = getattr(self, 'lora_layer_name', None) + if lora_layer_name is None: + return + current_names = getattr(self, "lora_current_names", ()) wanted_names = tuple((x.name, x.multiplier) for x in loaded_loras) weights_backup = getattr(self, "lora_weights_backup", None) if weights_backup is None: - weights_backup = self.weight.to(devices.cpu, copy=True) + if isinstance(self, torch.nn.MultiheadAttention): + weights_backup = (self.in_proj_weight.to(devices.cpu, copy=True), self.out_proj.weight.to(devices.cpu, copy=True)) + else: + weights_backup = self.weight.to(devices.cpu, copy=True) + self.lora_weights_backup = weights_backup if current_names != wanted_names: if weights_backup is not None: - self.weight.copy_(weights_backup) + if isinstance(self, torch.nn.MultiheadAttention): + self.in_proj_weight.copy_(weights_backup[0]) + self.out_proj.weight.copy_(weights_backup[1]) + else: + self.weight.copy_(weights_backup) - lora_layer_name = getattr(self, 'lora_layer_name', None) for lora in loaded_loras: module = lora.modules.get(lora_layer_name, None) + if module is not None and hasattr(self, 'weight'): + self.weight += lora_calc_updown(lora, module, self.weight) + continue + + module_q = lora.modules.get(lora_layer_name + "_q_proj", None) + module_k = lora.modules.get(lora_layer_name + "_k_proj", None) + module_v = lora.modules.get(lora_layer_name + "_v_proj", None) + module_out = lora.modules.get(lora_layer_name + "_out_proj", None) + + if isinstance(self, torch.nn.MultiheadAttention) and module_q and module_k and module_v and module_out: + updown_q = lora_calc_updown(lora, module_q, self.in_proj_weight) + updown_k = lora_calc_updown(lora, module_k, self.in_proj_weight) + updown_v = lora_calc_updown(lora, module_v, self.in_proj_weight) + updown_qkv = torch.vstack([updown_q, updown_k, updown_v]) + + self.in_proj_weight += updown_qkv + self.out_proj.weight += lora_calc_updown(lora, module_out, self.out_proj.weight) + continue + if module is None: continue - with torch.no_grad(): - up = module.up.weight.to(self.weight.device, dtype=self.weight.dtype) - down = module.down.weight.to(self.weight.device, dtype=self.weight.dtype) - - if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1): - updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3) - else: - updown = up @ down - - self.weight += updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + print(f'failed to calculate lora weights for layer {lora_layer_name}') setattr(self, "lora_current_names", wanted_names) +def lora_reset_cached_weight(self: torch.nn.Conv2d | torch.nn.Linear): + setattr(self, "lora_current_names", ()) + setattr(self, "lora_weights_backup", None) + + def lora_Linear_forward(self, input): lora_apply_weights(self) return torch.nn.Linear_forward_before_lora(self, input) -def lora_Linear_load_state_dict(self: torch.nn.Linear, *args, **kwargs): - setattr(self, "lora_current_names", ()) - setattr(self, "lora_weights_backup", None) +def lora_Linear_load_state_dict(self, *args, **kwargs): + lora_reset_cached_weight(self) return torch.nn.Linear_load_state_dict_before_lora(self, *args, **kwargs) @@ -248,15 +318,22 @@ def lora_Conv2d_forward(self, input): return torch.nn.Conv2d_forward_before_lora(self, input) -def lora_Conv2d_load_state_dict(self: torch.nn.Conv2d, *args, **kwargs): - setattr(self, "lora_current_names", ()) - setattr(self, "lora_weights_backup", None) +def lora_Conv2d_load_state_dict(self, *args, **kwargs): + lora_reset_cached_weight(self) return torch.nn.Conv2d_load_state_dict_before_lora(self, *args, **kwargs) -def lora_NonDynamicallyQuantizableLinear_forward(self, input): - return lora_forward(self, input, torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora(self, input)) +def lora_MultiheadAttention_forward(self, *args, **kwargs): + lora_apply_weights(self) + + return torch.nn.MultiheadAttention_forward_before_lora(self, *args, **kwargs) + + +def lora_MultiheadAttention_load_state_dict(self, *args, **kwargs): + lora_reset_cached_weight(self) + + return torch.nn.MultiheadAttention_load_state_dict_before_lora(self, *args, **kwargs) def list_available_loras(): diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index dc329e81..0adab225 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -12,6 +12,8 @@ def unload(): torch.nn.Linear._load_from_state_dict = torch.nn.Linear_load_state_dict_before_lora torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora torch.nn.Conv2d._load_from_state_dict = torch.nn.Conv2d_load_state_dict_before_lora + torch.nn.MultiheadAttention.forward = torch.nn.MultiheadAttention_forward_before_lora + torch.nn.MultiheadAttention._load_from_state_dict = torch.nn.MultiheadAttention_load_state_dict_before_lora def before_ui(): @@ -31,10 +33,18 @@ if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): if not hasattr(torch.nn, 'Conv2d_load_state_dict_before_lora'): torch.nn.Conv2d_load_state_dict_before_lora = torch.nn.Conv2d._load_from_state_dict +if not hasattr(torch.nn, 'MultiheadAttention_forward_before_lora'): + torch.nn.MultiheadAttention_forward_before_lora = torch.nn.MultiheadAttention.forward + +if not hasattr(torch.nn, 'MultiheadAttention_load_state_dict_before_lora'): + torch.nn.MultiheadAttention_load_state_dict_before_lora = torch.nn.MultiheadAttention._load_from_state_dict + torch.nn.Linear.forward = lora.lora_Linear_forward torch.nn.Linear._load_from_state_dict = lora.lora_Linear_load_state_dict torch.nn.Conv2d.forward = lora.lora_Conv2d_forward torch.nn.Conv2d._load_from_state_dict = lora.lora_Conv2d_load_state_dict +torch.nn.MultiheadAttention.forward = lora.lora_MultiheadAttention_forward +torch.nn.MultiheadAttention._load_from_state_dict = lora.lora_MultiheadAttention_load_state_dict script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) From 4c1ad743e3baf1246db0711aa0107debf036a12b Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 26 Mar 2023 11:01:32 +0300 Subject: [PATCH 27/95] for img2img, use None as upscaler instead of erroring out if the desired upscaler is not found --- modules/images.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/images.py b/modules/images.py index 7030aaaa..b3535070 100644 --- a/modules/images.py +++ b/modules/images.py @@ -261,9 +261,12 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None): if scale > 1.0: upscalers = [x for x in shared.sd_upscalers if x.name == upscaler_name] - assert len(upscalers) > 0, f"could not find upscaler named {upscaler_name}" + if len(upscalers) == 0: + upscaler = shared.sd_upscalers[0] + print(f"could not find upscaler named {upscaler_name or ''}, using {upscaler.name} as a fallback") + else: + upscaler = upscalers[0] - upscaler = upscalers[0] im = upscaler.scaler.upscale(im, scale, upscaler.data_path) if im.width != w or im.height != h: From 1f08600345298fac0bcb66cc215a81875a84d7b9 Mon Sep 17 00:00:00 2001 From: MrCheeze Date: Sun, 26 Mar 2023 16:55:29 -0400 Subject: [PATCH 28/95] overwrite xformers in the unclip model config if not available --- modules/sd_models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/sd_models.py b/modules/sd_models.py index c1a80d82..e741470a 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -383,6 +383,9 @@ def repair_config(sd_config): elif shared.cmd_opts.upcast_sampling: sd_config.model.params.unet_config.params.use_fp16 = True + if getattr(sd_config.model.params.first_stage_config.params.ddconfig, "attn_type", None) == "vanilla-xformers" and not shared.xformers_available: + sd_config.model.params.first_stage_config.params.ddconfig.attn_type = "vanilla" + # For UnCLIP-L, override the hardcoded karlo directory if hasattr(sd_config.model.params, "noise_aug_config") and hasattr(sd_config.model.params.noise_aug_config.params, "clip_stats_path"): karlo_path = os.path.join(paths.models_path, 'karlo') From 9d7390d2d19a8baf04ee4ebe598b96ac6ba7f97e Mon Sep 17 00:00:00 2001 From: camenduru <54370274+camenduru@users.noreply.github.com> Date: Mon, 27 Mar 2023 04:28:40 +0300 Subject: [PATCH 29/95] convert to python v3.9 --- extensions-builtin/Lora/lora.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index edd95f78..79d11e0e 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -2,6 +2,7 @@ import glob import os import re import torch +from typing import Union from modules import shared, devices, sd_models, errors @@ -235,7 +236,7 @@ def lora_calc_updown(lora, module, target): return updown -def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear | torch.nn.MultiheadAttention): +def lora_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.MultiheadAttention]): """ Applies the currently selected set of Loras to the weights of torch layer self. If weights already have this particular set of loras applied, does nothing. From 6a147db1287fe660e1bfb2ebf5b3fadc14835c69 Mon Sep 17 00:00:00 2001 From: camenduru <54370274+camenduru@users.noreply.github.com> Date: Mon, 27 Mar 2023 04:40:31 +0300 Subject: [PATCH 30/95] convert to python v3.9 --- extensions-builtin/Lora/lora.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index 79d11e0e..696be8ea 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -296,7 +296,7 @@ def lora_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.Mu setattr(self, "lora_current_names", wanted_names) -def lora_reset_cached_weight(self: torch.nn.Conv2d | torch.nn.Linear): +def lora_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]): setattr(self, "lora_current_names", ()) setattr(self, "lora_weights_backup", None) From 774c691df8b99f5e24d3f41e451fe65f5b447952 Mon Sep 17 00:00:00 2001 From: pieresimakp <69743585+pieresimakp@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:31:56 +0800 Subject: [PATCH 31/95] fixed style box wrapping --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 0dcc3e25..2218739a 100644 --- a/style.css +++ b/style.css @@ -69,7 +69,7 @@ div.compact{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } -.gradio-dropdown .wrap-inner.wrap-inner.wrap-inner{ +.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ flex-wrap: unset; } From ff0d97c1e3dafe9b15d37d157859a8b0fa934bff Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 07:11:39 +0300 Subject: [PATCH 32/95] bring back -y --- modules/cmd_args.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 0af87251..4614e82d 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -4,6 +4,7 @@ from modules.paths_internal import models_path, script_path, data_path, extensio parser = argparse.ArgumentParser() +parser.add_argument("-y", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui parser.add_argument("--update-all-extensions", action='store_true', help="launch.py argument: download updates for all extensions when starting the program") parser.add_argument("--skip-python-version-check", action='store_true', help="launch.py argument: do not check python version") parser.add_argument("--skip-torch-cuda-test", action='store_true', help="launch.py argument: do not check if CUDA is able to work properly") From 3d09b4e99fc77acddcb2a4562efe8d6444a87e3c Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 07:12:40 +0300 Subject: [PATCH 33/95] remove -y, bring back -f --- modules/cmd_args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 4614e82d..81c0b82a 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -4,7 +4,7 @@ from modules.paths_internal import models_path, script_path, data_path, extensio parser = argparse.ArgumentParser() -parser.add_argument("-y", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui +parser.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui parser.add_argument("--update-all-extensions", action='store_true', help="launch.py argument: download updates for all extensions when starting the program") parser.add_argument("--skip-python-version-check", action='store_true', help="launch.py argument: do not check python version") parser.add_argument("--skip-torch-cuda-test", action='store_true', help="launch.py argument: do not check if CUDA is able to work properly") From b40538a7fe2ee6914b3121f252f0d5dc9f7e8fab Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 07:30:38 +0300 Subject: [PATCH 34/95] reformat css from latest commits, change color for dropdown selection to more neutral one --- style.css | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/style.css b/style.css index 84fee3d3..5ecfce98 100644 --- a/style.css +++ b/style.css @@ -54,13 +54,6 @@ div.compact{ gap: 1em; } -.gradio-dropdown ul.options{ - z-index: 3000; - min-width: fit-content; - max-width: inherit; - white-space: nowrap; -} - .gradio-dropdown label span:not(.has-info), .gradio-textbox label span:not(.has-info), .gradio-number label span:not(.has-info) @@ -68,17 +61,23 @@ div.compact{ margin-bottom: 0; } -.gradio-dropdown ul.options { - max-height: 35em; - z-index: 3000; +.gradio-dropdown ul.options{ + z-index: 3000; + min-width: fit-content; + max-width: inherit; + white-space: nowrap; } .gradio-dropdown ul.options li.item { - padding: 0.05em 0; + padding: 0.05em 0; } .gradio-dropdown ul.options li.item.selected { - background-color: var(--secondary-500); + background-color: var(--neutral-100); +} + +.dark .gradio-dropdown ul.options li.item.selected { + background-color: var(--neutral-900); } .gradio-dropdown div.wrap.wrap.wrap.wrap{ From a70ae917ea86270f365f1347674980e4e7b0bfb2 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 08:05:55 +0300 Subject: [PATCH 35/95] update button pressed down style for #8569 --- modules/ui_extra_networks.py | 2 +- style.css | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 2a3b8553..7f73131e 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -252,7 +252,7 @@ def create_ui(container, button, tabname): def toggle_visibility(is_visible): is_visible = not is_visible - return is_visible, gr.update(visible=is_visible), gr.update(variant=("primary" if is_visible else "tool")) + return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary")) state_visible = gr.State(value=False) button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button]) diff --git a/style.css b/style.css index 6a6f73d7..7a979f22 100644 --- a/style.css +++ b/style.css @@ -138,6 +138,18 @@ div.gradio-html.min{ border-radius: 0.5em; } +.gradio-button.secondary-down{ + background: var(--button-secondary-background-fill); + color: var(--button-secondary-text-color); +} +.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ + box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; +} +.gradio-button.secondary-down:hover{ + background: var(--button-secondary-background-fill-hover); + color: var(--button-secondary-text-color-hover); +} + .checkboxes-row{ margin-bottom: 0.5em; margin-left: 0em; @@ -810,10 +822,3 @@ footer { .extra-network-cards .card ul a:hover{ color: red; } - -button[id$='_extra_networks'] { - max-width: 2.2em; - min-width: 2.2em !important; - height: 2.4em; - line-height: 1em !important; -} From 5cf3822e463393e6ba1eab0b5647174f754886b9 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 08:18:28 +0300 Subject: [PATCH 36/95] Revert "Merge pull request #8651 from vladmandic/flicker" This reverts commit 84026821188ccf81b91c9c854a18f9852b90b13f, reversing changes made to e8bbc344c3309c70f7f5abeb34b90f14bd0cfa51. --- webui.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/webui.py b/webui.py index 30f3e4a1..b570895f 100644 --- a/webui.py +++ b/webui.py @@ -265,9 +265,6 @@ def webui(): inbrowser=cmd_opts.autolaunch, prevent_thread_lock=True ) - for dep in shared.demo.dependencies: - dep['show_progress'] = False # disable gradio css animation on component update - # after initial launch, disable --autolaunch for subsequent restarts cmd_opts.autolaunch = False From 5fcd4bfa3d86c3ebf91ff993b6472a68a3a4f6ad Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 10:02:30 +0300 Subject: [PATCH 37/95] do not read extensions' git stuff at startup --- modules/extensions.py | 29 +++++++++++++++++++---------- modules/ui_extensions.py | 5 +++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/extensions.py b/modules/extensions.py index a14ffbf0..0d34b89a 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -5,13 +5,14 @@ import traceback import time import git -from modules import paths, shared +from modules import shared from modules.paths_internal import extensions_dir, extensions_builtin_dir extensions = [] -if not os.path.exists(paths.extensions_dir): - os.makedirs(paths.extensions_dir) +if not os.path.exists(extensions_dir): + os.makedirs(extensions_dir) + def active(): return [x for x in extensions if x.enabled] @@ -26,21 +27,29 @@ class Extension: self.can_update = False self.is_builtin = is_builtin self.version = '' + self.remote = None + self.have_info_from_repo = False + + def read_info_from_repo(self): + if self.have_info_from_repo: + return + + self.have_info_from_repo = True repo = None try: - if os.path.exists(os.path.join(path, ".git")): - repo = git.Repo(path) + if os.path.exists(os.path.join(self.path, ".git")): + repo = git.Repo(self.path) except Exception: - print(f"Error reading github repository info from {path}:", file=sys.stderr) + print(f"Error reading github repository info from {self.path}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) if repo is None or repo.bare: self.remote = None else: try: - self.remote = next(repo.remote().urls, None) self.status = 'unknown' + self.remote = next(repo.remote().urls, None) head = repo.head.commit ts = time.asctime(time.gmtime(repo.head.commit.committed_date)) self.version = f'{head.hexsha[:8]} ({ts})' @@ -85,11 +94,11 @@ class Extension: def list_extensions(): extensions.clear() - if not os.path.isdir(paths.extensions_dir): + if not os.path.isdir(extensions_dir): return extension_paths = [] - for dirname in [paths.extensions_dir, paths.extensions_builtin_dir]: + for dirname in [extensions_dir, extensions_builtin_dir]: if not os.path.isdir(dirname): return @@ -98,7 +107,7 @@ def list_extensions(): if not os.path.isdir(path): continue - extension_paths.append((extension_dirname, path, dirname == paths.extensions_builtin_dir)) + extension_paths.append((extension_dirname, path, dirname == extensions_builtin_dir)) for dirname, path, is_builtin in extension_paths: extension = Extension(name=dirname, path=path, enabled=dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index da7e79f0..b4a0d6ec 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -63,6 +63,9 @@ def check_updates(id_task, disable_list): try: ext.check_updates() + except FileNotFoundError as e: + if 'FETCH_HEAD' not in str(e): + raise except Exception: print(f"Error checking updates for {ext.name}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) @@ -87,6 +90,8 @@ def extension_table(): """ for ext in extensions.extensions: + ext.read_info_from_repo() + remote = f"""{html.escape("built-in" if ext.is_builtin else ext.remote or '')}""" if ext.can_update: From 9e82896d5f0365b25aacf755799e9fb08078b0ba Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 10:20:01 +0300 Subject: [PATCH 38/95] remove an extra unneeded row in outputs --- modules/ui_common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/ui_common.py b/modules/ui_common.py index 0f3427c8..3b11dcc8 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -145,8 +145,7 @@ Requested path was: {f} ) if tabname != "extras": - with gr.Row(): - download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}') + download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}') with gr.Group(): html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext") From 77f9db3b080fafbc39c1b188777a93b5a1ab0f9e Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 12:59:12 +0300 Subject: [PATCH 39/95] serve css as independent files --- modules/ui.py | 74 ++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index af8546c2..eb5fcd3f 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -70,17 +70,6 @@ def gr_show(visible=True): sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg" sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None -css_hide_progressbar = """ -.wrap .m-12 svg { display:none!important; } -.wrap .m-12::before { content:"Loading..." } -.wrap .z-20 svg { display:none!important; } -.wrap .z-20::before { content:"Loading..." } -.wrap.cover-bg .z-20::before { content:"" } -.progress-bar { display:none!important; } -.meta-text { display:none!important; } -.meta-text-center { display:none!important; } -""" - # Using constants for these since the variation selector isn't visible. # Important that they exactly match script.js for tooltip to work. random_symbol = '\U0001f3b2\ufe0f' # 🎲️ @@ -1566,22 +1555,6 @@ def create_ui(): (train_interface, "Train", "ti"), ] - css = "" - - for cssfile in modules.scripts.list_files_with_name("style.css"): - if not os.path.isfile(cssfile): - continue - - with open(cssfile, "r", encoding="utf8") as file: - css += file.read() + "\n" - - if os.path.exists(os.path.join(data_path, "user.css")): - with open(os.path.join(data_path, "user.css"), "r", encoding="utf8") as file: - css += file.read() + "\n" - - if not cmd_opts.no_progressbar_hiding: - css += css_hide_progressbar - interfaces += script_callbacks.ui_tabs_callback() interfaces += [(settings_interface, "Settings", "settings")] @@ -1592,7 +1565,7 @@ def create_ui(): for _interface, label, _ifid in interfaces: shared.tab_names.append(label) - with gr.Blocks(css=css, analytics_enabled=False, title="Stable Diffusion") as demo: + with gr.Blocks(analytics_enabled=False, title="Stable Diffusion") as demo: with gr.Row(elem_id="quicksettings", variant="compact"): for i, k, item in sorted(quicksettings_list, key=lambda x: quicksettings_names.get(x[1], x[0])): component = create_setting_component(k, is_quicksettings=True) @@ -1777,25 +1750,60 @@ def create_ui(): return demo -def reload_javascript(): +def webpath(fn): + if fn.startswith(script_path): + web_path = os.path.relpath(fn, script_path).replace('\\', '/') + else: + web_path = os.path.abspath(fn) + + return f'file={web_path}?{os.path.getmtime(fn)}' + + +def javascript_html(): script_js = os.path.join(script_path, "script.js") - head = f'\n' + head = f'\n' inline = f"{localization.localization_js(shared.opts.localization)};" if cmd_opts.theme is not None: inline += f"set_theme('{cmd_opts.theme}');" for script in modules.scripts.list_scripts("javascript", ".js"): - head += f'\n' + head += f'\n' for script in modules.scripts.list_scripts("javascript", ".mjs"): - head += f'\n' + head += f'\n' head += f'\n' + return head + + +def css_html(): + head = "" + + def stylesheet(fn): + return f'' + + for cssfile in modules.scripts.list_files_with_name("style.css"): + if not os.path.isfile(cssfile): + continue + + head += stylesheet(cssfile) + + if os.path.exists(os.path.join(data_path, "user.css")): + head += stylesheet(os.path.join(data_path, "user.css")) + + return head + + +def reload_javascript(): + js = javascript_html() + css = css_html() + def template_response(*args, **kwargs): res = shared.GradioTemplateResponseOriginal(*args, **kwargs) - res.body = res.body.replace(b'', f'{head}'.encode("utf8")) + res.body = res.body.replace(b'', f'{js}'.encode("utf8")) + res.body = res.body.replace(b'', f'{css}'.encode("utf8")) res.init_headers() return res From 2a4d3d21242dcc8b2b9cef85aa8f4227e855dc96 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:04:45 -0400 Subject: [PATCH 40/95] Add temporary "disable all extensions" option for debugging use --- launch.py | 4 ++++ modules/extensions.py | 4 ++++ modules/shared.py | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/launch.py b/launch.py index c41ae82d..1321b77a 100644 --- a/launch.py +++ b/launch.py @@ -206,6 +206,10 @@ def list_extensions(settings_file): print(e, file=sys.stderr) disabled_extensions = set(settings.get('disabled_extensions', [])) + disable_all_extensions = settings.get('disable_all_extensions', False) + + if disable_all_extensions: + return [] return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions] diff --git a/modules/extensions.py b/modules/extensions.py index 0d34b89a..1493e8c8 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -97,6 +97,10 @@ def list_extensions(): if not os.path.isdir(extensions_dir): return + if shared.opts.disable_all_extensions: + print("*** \"Disable all extensions\" option was set, will not load any extensions ***") + return + extension_paths = [] for dirname in [extensions_dir, extensions_builtin_dir]: if not os.path.isdir(dirname): diff --git a/modules/shared.py b/modules/shared.py index 3ad0862b..c79ec67b 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -422,7 +422,8 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), { })) options_templates.update(options_section((None, "Hidden options"), { - "disabled_extensions": OptionInfo([], "Disable those extensions"), + "disabled_extensions": OptionInfo([], "Disable these extensions"), + "disable_all_extensions": OptionInfo(False, "Disable all extensions (preserves the list of disabled extensions)"), "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"), })) From fc8e1008ea93f98554907f25aaf52f24ce661847 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:44:49 -0400 Subject: [PATCH 41/95] Make disable configurable between builtin/extra extensions --- javascript/extensions.js | 6 +++--- launch.py | 4 ---- modules/extensions.py | 13 +++++++++---- modules/shared.py | 2 +- modules/ui_extensions.py | 21 +++++++++++++++++---- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/javascript/extensions.js b/javascript/extensions.js index c593cd2e..72924a28 100644 --- a/javascript/extensions.js +++ b/javascript/extensions.js @@ -1,5 +1,5 @@ -function extensions_apply(_, _){ +function extensions_apply(_, _, disable_all){ var disable = [] var update = [] @@ -13,10 +13,10 @@ function extensions_apply(_, _){ restart_reload() - return [JSON.stringify(disable), JSON.stringify(update)] + return [JSON.stringify(disable), JSON.stringify(update), disable_all] } -function extensions_check(){ +function extensions_check(_, _){ var disable = [] gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach(function(x){ diff --git a/launch.py b/launch.py index 1321b77a..c41ae82d 100644 --- a/launch.py +++ b/launch.py @@ -206,10 +206,6 @@ def list_extensions(settings_file): print(e, file=sys.stderr) disabled_extensions = set(settings.get('disabled_extensions', [])) - disable_all_extensions = settings.get('disable_all_extensions', False) - - if disable_all_extensions: - return [] return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions] diff --git a/modules/extensions.py b/modules/extensions.py index 1493e8c8..3a7a0372 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -15,7 +15,12 @@ if not os.path.exists(extensions_dir): def active(): - return [x for x in extensions if x.enabled] + if shared.opts.disable_all_extensions == "all": + return [] + elif shared.opts.disable_all_extensions == "extra": + return [x for x in extensions if x.enabled and x.is_builtin] + else: + return [x for x in extensions if x.enabled] class Extension: @@ -97,9 +102,10 @@ def list_extensions(): if not os.path.isdir(extensions_dir): return - if shared.opts.disable_all_extensions: + if shared.opts.disable_all_extensions == "all": print("*** \"Disable all extensions\" option was set, will not load any extensions ***") - return + elif shared.opts.disable_all_extensions == "extra": + print("*** \"Disable all extensions\" option was set, will only load built-in extensions ***") extension_paths = [] for dirname in [extensions_dir, extensions_builtin_dir]: @@ -116,4 +122,3 @@ def list_extensions(): for dirname, path, is_builtin in extension_paths: extension = Extension(name=dirname, path=path, enabled=dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin) extensions.append(extension) - diff --git a/modules/shared.py b/modules/shared.py index c79ec67b..5fd0eecb 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -423,7 +423,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), { options_templates.update(options_section((None, "Hidden options"), { "disabled_extensions": OptionInfo([], "Disable these extensions"), - "disable_all_extensions": OptionInfo(False, "Disable all extensions (preserves the list of disabled extensions)"), + "disable_all_extensions": OptionInfo("none", "Disable all extensions (preserves the list of disabled extensions)", gr.Radio, {"choices": ["none", "extra", "all"]}), "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"), })) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index b4a0d6ec..efd6cda2 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -21,7 +21,7 @@ def check_access(): assert not shared.cmd_opts.disable_extension_access, "extension access disabled because of command line flags" -def apply_and_restart(disable_list, update_list): +def apply_and_restart(disable_list, update_list, disable_all): check_access() disabled = json.loads(disable_list) @@ -43,6 +43,7 @@ def apply_and_restart(disable_list, update_list): print(traceback.format_exc(), file=sys.stderr) shared.opts.disabled_extensions = disabled + shared.opts.disable_all_extensions = disable_all shared.opts.save(shared.config_filename) shared.state.interrupt() @@ -99,9 +100,13 @@ def extension_table(): else: ext_status = ext.status + style = "" + if shared.opts.disable_all_extensions == "extra" and not ext.is_builtin or shared.opts.disable_all_extensions == "all": + style = ' style="color: var(--primary-400)"' + code += f""" - + {html.escape(ext.name)} {remote} {ext.version} {ext_status} @@ -294,16 +299,24 @@ def create_ui(): with gr.Row(elem_id="extensions_installed_top"): apply = gr.Button(value="Apply and restart UI", variant="primary") check = gr.Button(value="Check for updates") + extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "extra", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all") extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False) extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False).style(container=False) - info = gr.HTML() + html = "" + if shared.opts.disable_all_extensions != "none": + html = """ + + "Disable all extensions" was set, change it to "none" to load all extensions again + + """ + info = gr.HTML(html) extensions_table = gr.HTML(lambda: extension_table()) apply.click( fn=apply_and_restart, _js="extensions_apply", - inputs=[extensions_disabled_list, extensions_update_list], + inputs=[extensions_disabled_list, extensions_update_list, extensions_disable_all], outputs=[], ) From 9ecf3471339d011983f2e3c878e920e49718ff90 Mon Sep 17 00:00:00 2001 From: AlUlkesh <99896447+AlUlkesh@users.noreply.github.com> Date: Mon, 27 Mar 2023 20:01:19 +0200 Subject: [PATCH 42/95] fix: lightboxModal, selectedTab --- javascript/generationParams.js | 2 +- javascript/imageviewer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/generationParams.js b/javascript/generationParams.js index 95f05093..06a771bc 100644 --- a/javascript/generationParams.js +++ b/javascript/generationParams.js @@ -16,7 +16,7 @@ onUiUpdate(function(){ let modalObserver = new MutationObserver(function(mutations) { mutations.forEach(function(mutationRecord) { - let selectedTab = gradioApp().querySelector('#tabs div button.bg-white')?.innerText + let selectedTab = gradioApp().querySelector('#tabs div button')?.innerText if (mutationRecord.target.style.display === 'none' && selectedTab === 'txt2img' || selectedTab === 'img2img') gradioApp().getElementById(selectedTab+"_generation_info_button").click() }); diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index d6483562..bb61ee24 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -251,7 +251,7 @@ document.addEventListener("DOMContentLoaded", function() { modal.appendChild(modalNext) - gradioApp().appendChild(modal) + gradioApp().body.appendChild(modal) document.body.appendChild(modal); From 4b4902050684dc4db1bbd007a4f2e8f7c37bf1a4 Mon Sep 17 00:00:00 2001 From: zetclansu Date: Mon, 27 Mar 2023 21:05:56 +0300 Subject: [PATCH 43/95] Update style.css Fix for wide width image in img2img_sketch, img2maskimg, inpaint_sketch --- style.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/style.css b/style.css index 5e8fb533..be6ceb1e 100644 --- a/style.css +++ b/style.css @@ -318,6 +318,13 @@ div.dimensions-tools{ min-height: 480px !important; } +#img2img_sketch, #img2maskimg, #inpaint_sketch { + overflow: overlay !important; + resize: auto; + background: var(--panel-background-fill); + z-index: 5; +} + .image-buttons button{ min-width: auto; } From 56f62d3851ff08dc1574a9ff2a05271f3730f3f7 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:23:20 -0400 Subject: [PATCH 44/95] Skip extension installers if all disabled --- launch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launch.py b/launch.py index c41ae82d..2e3cd4c4 100644 --- a/launch.py +++ b/launch.py @@ -206,6 +206,10 @@ def list_extensions(settings_file): print(e, file=sys.stderr) disabled_extensions = set(settings.get('disabled_extensions', [])) + disable_all_extensions = settings.get('disable_all_extensions', 'none') + + if disable_all_extensions != 'none': + return [] return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions] From b3e593edcb72c0431a50b423fe7aa88a0daf2a90 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Mon, 27 Mar 2023 16:38:42 -0600 Subject: [PATCH 45/95] Fix quicksettings alignment --- style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/style.css b/style.css index 5e8fb533..4827a9a1 100644 --- a/style.css +++ b/style.css @@ -329,6 +329,7 @@ div.dimensions-tools{ /* settings */ #quicksettings { width: fit-content; + align-items: end; } #quicksettings > div, #quicksettings > fieldset{ From 1b63afbedc7789c0eb9a4742b780ab304d7a9caf Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Tue, 28 Mar 2023 20:03:57 +0300 Subject: [PATCH 46/95] sort hypernetworks and checkpoints by name --- modules/hypernetworks/hypernetwork.py | 2 +- modules/sd_models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index f6ef42d5..1fc49537 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -312,7 +312,7 @@ class Hypernetwork: def list_hypernetworks(path): res = {} - for filename in sorted(glob.iglob(os.path.join(path, '**/*.pt'), recursive=True)): + for filename in sorted(glob.iglob(os.path.join(path, '**/*.pt'), recursive=True), key=str.lower): name = os.path.splitext(os.path.basename(filename))[0] # Prevent a hypothetical "None.pt" from being listed. if name != "None": diff --git a/modules/sd_models.py b/modules/sd_models.py index c2b3405c..6ea874df 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -122,7 +122,7 @@ def list_models(): elif cmd_ckpt is not None and cmd_ckpt != shared.default_sd_model_file: print(f"Checkpoint in --ckpt argument not found (Possible it was moved to {model_path}: {cmd_ckpt}", file=sys.stderr) - for filename in model_list: + for filename in sorted(model_list, key=str.lower): checkpoint_info = CheckpointInfo(filename) checkpoint_info.register() From 433b3ab7017556a19173a86d1215ed0a0b5b1396 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Tue, 28 Mar 2023 20:36:57 +0300 Subject: [PATCH 47/95] Revert "Merge pull request #7931 from space-nuko/img2img-enhance" This reverts commit 426875937048e21305ac24bea53df06523bdaa81, reversing changes made to 1b63afbedc7789c0eb9a4742b780ab304d7a9caf. --- javascript/ui.js | 22 +------ modules/generation_parameters_copypaste.py | 3 - modules/img2img.py | 4 +- modules/processing.py | 37 ++--------- modules/ui.py | 73 ++-------------------- scripts/xyz_grid.py | 1 - style.css | 4 +- 7 files changed, 13 insertions(+), 131 deletions(-) diff --git a/javascript/ui.js b/javascript/ui.js index a73eeaa2..4a440193 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -132,14 +132,7 @@ function create_tab_index_args(tabId, args){ function get_img2img_tab_index() { let res = args_to_array(arguments) - res.splice(-2) // gradio also sends outputs to the arguments, pop them off - res[0] = get_tab_index('mode_img2img') - return res -} - -function get_img2img_tab_index_for_res_preview() { - let res = args_to_array(arguments) - res.splice(-1) // gradio also sends outputs to the arguments, pop them off + res.splice(-2) res[0] = get_tab_index('mode_img2img') return res } @@ -368,16 +361,3 @@ function selectCheckpoint(name){ desiredCheckpointName = name; gradioApp().getElementById('change_checkpoint').click() } - - -function onCalcResolutionImg2Img(mode, scale, width, height, resize_mode, init_img, sketch, init_img_with_mask, inpaint_color_sketch, init_img_inpaint){ - i2iScale = gradioApp().getElementById('img2img_scale') - i2iWidth = gradioApp().getElementById('img2img_width') - i2iHeight = gradioApp().getElementById('img2img_height') - - setInactive(i2iScale, scale == 1) - setInactive(i2iWidth, scale > 1) - setInactive(i2iHeight, scale > 1) - - return []; -} diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 0ad2ad4f..6df76858 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -282,9 +282,6 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model res["Hires resize-1"] = 0 res["Hires resize-2"] = 0 - if "Img2Img upscale" not in res: - res["Img2Img upscale"] = 1 - restore_old_hires_fix_params(res) return res diff --git a/modules/img2img.py b/modules/img2img.py index 959dd96e..c973b770 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -78,7 +78,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args): processed_image.save(os.path.join(output_dir, filename)) -def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, scale: float, upscaler: str, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): +def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): override_settings = create_override_settings_dict(override_settings_texts) is_batch = mode == 5 @@ -149,8 +149,6 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s inpaint_full_res_padding=inpaint_full_res_padding, inpainting_mask_invert=inpainting_mask_invert, override_settings=override_settings, - scale=scale, - upscaler=upscaler, ) p.scripts = modules.scripts.scripts_txt2img diff --git a/modules/processing.py b/modules/processing.py index 509b80b9..6d9c6a8d 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -946,7 +946,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): sampler = None - def __init__(self, init_images: Optional[list] = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: Optional[float] = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: Optional[float] = None, scale: float = 0, upscaler: Optional[str] = None, **kwargs): + def __init__(self, init_images: list = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, **kwargs): super().__init__(**kwargs) self.init_images = init_images @@ -966,37 +966,11 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.mask = None self.nmask = None self.image_conditioning = None - self.scale = scale - self.upscaler = upscaler - - def get_final_size(self): - if self.scale > 1: - img = self.init_images[0] - width = int(img.width * self.scale) - height = int(img.height * self.scale) - return width, height - else: - return self.width, self.height - def init(self, all_prompts, all_seeds, all_subseeds): self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) crop_region = None - if self.scale > 1: - self.extra_generation_params["Img2Img upscale"] = self.scale - - # Non-latent upscalers are run before sampling - # Latent upscalers are run during sampling - init_upscaler = None - if self.upscaler is not None: - self.extra_generation_params["Img2Img upscaler"] = self.upscaler - if self.upscaler not in shared.latent_upscale_modes: - assert len([x for x in shared.sd_upscalers if x.name == self.upscaler]) > 0, f"could not find upscaler named {self.upscaler}" - init_upscaler = self.upscaler - - self.width, self.height = self.get_final_size() - image_mask = self.image_mask if image_mask is not None: @@ -1019,7 +993,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image_mask = images.resize_image(2, mask, self.width, self.height) self.paste_to = (x1, y1, x2-x1, y2-y1) else: - image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height, init_upscaler) + image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height) np_mask = np.array(image_mask) np_mask = np.clip((np_mask.astype(np.float32)) * 2, 0, 255).astype(np.uint8) self.mask_for_overlay = Image.fromarray(np_mask) @@ -1036,7 +1010,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image = images.flatten(img, opts.img2img_background_color) if crop_region is None and self.resize_mode != 3: - image = images.resize_image(self.resize_mode, image, self.width, self.height, init_upscaler) + image = images.resize_image(self.resize_mode, image, self.width, self.height) if image_mask is not None: image_masked = Image.new('RGBa', (image.width, image.height)) @@ -1081,9 +1055,8 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image)) - latent_scale_mode = shared.latent_upscale_modes.get(self.upscaler, None) if self.upscaler is not None else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "nearest") - if latent_scale_mode is not None: - self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // opt_f, self.width // opt_f), mode=latent_scale_mode["mode"], antialias=latent_scale_mode["antialias"]) + if self.resize_mode == 3: + self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") if image_mask is not None: init_mask = latent_mask diff --git a/modules/ui.py b/modules/ui.py index f22da16a..eb5fcd3f 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -15,7 +15,6 @@ import warnings import gradio as gr import gradio.routes import gradio.utils -from gradio.events import Releaseable import numpy as np from PIL import Image, PngImagePlugin from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call @@ -128,26 +127,6 @@ def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resiz return f"resize: from {p.width}x{p.height} to {p.hr_resize_x or p.hr_upscale_to_x}x{p.hr_resize_y or p.hr_upscale_to_y}" -def calc_resolution_img2img(mode, scale, resize_x, resize_y, resize_mode, *i2i_images): - init_img = None - if mode in {0, 1, 3, 4}: - init_img = i2i_images[mode] - elif mode == 2: - init_img = i2i_images[mode]["image"] - - if not init_img: - return "" - - if scale > 1: - width = int(init_img.width * scale) - height = int(init_img.height * scale) - else: - width = resize_x - height = resize_y - - return f"resize: from {init_img.width}x{init_img.height} to {width}x{height}" - - def apply_styles(prompt, prompt_neg, styles): prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles) prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, styles) @@ -756,7 +735,7 @@ def create_ui(): ) with FormRow(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize") + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize") for category in ordered_ui_categories(): if category == "sampler": @@ -765,13 +744,8 @@ def create_ui(): elif category == "dimensions": with FormRow(): with gr.Column(elem_id="img2img_column_size", scale=4): - with FormRow(variant="compact"): - final_resolution = FormHTML(value="", elem_id="img2img_finalres", label="Upscaled resolution", interactive=False) - with FormRow(variant="compact"): - scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=1.0, elem_id="img2img_scale") - with FormRow(variant="compact"): - width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") - height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") + width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") + height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") with gr.Column(elem_id="img2img_dimensions_row", scale=1, elem_classes="dimensions-tools"): res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") @@ -786,9 +760,7 @@ def create_ui(): with FormRow(): cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="img2img_cfg_scale") image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit") - with FormRow(): - upscaler = gr.Dropdown(label="Upscaler", elem_id="img2img_upscaler", choices=[*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]], value=shared.latent_upscale_default_mode) - denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") elif category == "seed": seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('img2img') @@ -841,39 +813,6 @@ def create_ui(): outputs=[inpaint_controls, mask_alpha], ) - img2img_resolution_preview_inputs = [dummy_component, # filled in by selected img2img tab index in _js - scale, width, height, resize_mode, - init_img, sketch, init_img_with_mask, inpaint_color_sketch, init_img_inpaint] - for input in img2img_resolution_preview_inputs[1:]: - if isinstance(input, Releaseable): - input.release( - fn=calc_resolution_img2img, - _js="get_img2img_tab_index_for_res_preview", - inputs=img2img_resolution_preview_inputs, - outputs=[final_resolution], - show_progress=False, - ).success( - None, - _js="onCalcResolutionImg2Img", - inputs=img2img_resolution_preview_inputs, - outputs=[], - show_progress=False, - ) - else: - input.change( - fn=calc_resolution_img2img, - _js="get_img2img_tab_index_for_res_preview", - inputs=img2img_resolution_preview_inputs, - outputs=[final_resolution], - show_progress=False, - ).success( - None, - _js="onCalcResolutionImg2Img", - inputs=img2img_resolution_preview_inputs, - outputs=[], - show_progress=False, - ) - img2img_gallery, generation_info, html_info, html_log = create_output_panel("img2img", opts.outdir_img2img_samples) connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) @@ -922,8 +861,6 @@ def create_ui(): subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox, height, width, - scale, - upscaler, resize_mode, inpaint_full_res, inpaint_full_res_padding, @@ -1009,8 +946,6 @@ def create_ui(): (seed, "Seed"), (width, "Size-1"), (height, "Size-2"), - (scale, "Img2Img upscale"), - (upscaler, "Img2Img upscaler"), (batch_size, "Batch size"), (subseed, "Variation seed"), (subseed_strength, "Variation seed strength"), diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 3f6c1997..3895a795 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -220,7 +220,6 @@ axis_options = [ AxisOption("Clip skip", int, apply_clip_skip), AxisOption("Denoising", float, apply_field("denoising_strength")), AxisOptionTxt2Img("Hires upscaler", str, apply_field("hr_upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]), - AxisOptionImg2Img("Upscaler", str, apply_field("upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]), AxisOptionImg2Img("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight")), AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: list(sd_vae.vae_dict)), AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)), diff --git a/style.css b/style.css index 379a89dc..de16a7f2 100644 --- a/style.css +++ b/style.css @@ -287,13 +287,13 @@ button.custom-button{ border-radius: 0 0.5rem 0.5rem 0; } -#txtimg_hr_finalres, #img2img_finalres { +#txtimg_hr_finalres{ min-height: 0 !important; padding: .625rem .75rem; margin-left: -0.75em } -#txtimg_hr_finalres .resolution, #img2img_finalres .resolution{ +#txtimg_hr_finalres .resolution{ font-weight: bold; } From 3856ada5cc9ac4124e20ff311ce7aa77330845d9 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Tue, 28 Mar 2023 22:20:31 +0300 Subject: [PATCH 48/95] do not add mask blur to infotext if there is no mask --- modules/img2img.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/img2img.py b/modules/img2img.py index c973b770..953ac5d2 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -157,7 +157,8 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s if shared.cmd_opts.enable_console_prompts: print(f"\nimg2img: {prompt}", file=shared.progress_print_out) - p.extra_generation_params["Mask blur"] = mask_blur + if mask: + p.extra_generation_params["Mask blur"] = mask_blur if is_batch: assert not shared.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled" From 5a25826d841a13574ab6afbeb9c50c81a491fa21 Mon Sep 17 00:00:00 2001 From: AlUlkesh <99896447+AlUlkesh@users.noreply.github.com> Date: Tue, 28 Mar 2023 23:28:46 +0200 Subject: [PATCH 49/95] try both versions of appendChild --- javascript/imageviewer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index bb61ee24..3deffa9b 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -251,8 +251,11 @@ document.addEventListener("DOMContentLoaded", function() { modal.appendChild(modalNext) - gradioApp().body.appendChild(modal) - + try { + gradioApp().appendChild(modal); + } catch (e) { + gradioApp().body.appendChild(modal); + } document.body.appendChild(modal); From 22bcc7be428c94e9408f589966c2040187245d81 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Wed, 29 Mar 2023 08:58:29 +0300 Subject: [PATCH 50/95] attempted fix for infinite loading for settings that some people experience --- modules/ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ui.py b/modules/ui.py index eb5fcd3f..627fbe0b 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1628,6 +1628,7 @@ def create_ui(): fn=get_settings_values, inputs=[], outputs=[component_dict[k] for k in component_keys], + queue=False, ) def modelmerger(*args): From 79d57d02f15aa406e38997d65ec8ed99374691b7 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Wed, 29 Mar 2023 01:52:34 -0500 Subject: [PATCH 51/95] Improve custom code extension - Uses `gr.Code` component - Includes example - Can return out of body --- scripts/custom_code.py | 63 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/scripts/custom_code.py b/scripts/custom_code.py index d29113e6..4071d86d 100644 --- a/scripts/custom_code.py +++ b/scripts/custom_code.py @@ -1,9 +1,40 @@ import modules.scripts as scripts import gradio as gr +import ast +import copy from modules.processing import Processed from modules.shared import opts, cmd_opts, state + +def convertExpr2Expression(expr): + expr.lineno = 0 + expr.col_offset = 0 + result = ast.Expression(expr.value, lineno=0, col_offset = 0) + + return result + + +def exec_with_return(code, module): + """ + like exec() but can return values + https://stackoverflow.com/a/52361938/5862977 + """ + code_ast = ast.parse(code) + + init_ast = copy.deepcopy(code_ast) + init_ast.body = code_ast.body[:-1] + + last_ast = copy.deepcopy(code_ast) + last_ast.body = code_ast.body[-1:] + + exec(compile(init_ast, "", "exec"), module.__dict__) + if type(last_ast.body[0]) == ast.Expr: + return eval(compile(convertExpr2Expression(last_ast.body[0]), "", "eval"), module.__dict__) + else: + exec(compile(last_ast, "", "exec"), module.__dict__) + + class Script(scripts.Script): def title(self): @@ -13,12 +44,23 @@ class Script(scripts.Script): return cmd_opts.allow_code def ui(self, is_img2img): - code = gr.Textbox(label="Python code", lines=1, elem_id=self.elem_id("code")) + example = """from modules.processing import process_images - return [code] +p.width = 768 +p.height = 768 +p.batch_size = 2 +p.steps = 10 + +return process_images(p) +""" - def run(self, p, code): + code = gr.Code(value=example, language="python", label="Python code", elem_id=self.elem_id("code")) + indent_level = gr.Number(label='Indent level', value=2, precision=0, elem_id=self.elem_id("indent_level")) + + return [code, indent_level] + + def run(self, p, code, indent_level): assert cmd_opts.allow_code, '--allow-code option must be enabled' display_result_data = [[], -1, ""] @@ -29,13 +71,20 @@ class Script(scripts.Script): display_result_data[2] = i from types import ModuleType - compiled = compile(code, '', 'exec') module = ModuleType("testmodule") module.__dict__.update(globals()) module.p = p module.display = display - exec(compiled, module.__dict__) + + indent = " " * indent_level + indented = code.replace('\n', '\n' + indent) + body = f"""def __webuitemp__(): +{indent}{indented} +__webuitemp__()""" + + result = exec_with_return(body, module) + + if isinstance(result, Processed): + return result return Processed(p, *display_result_data) - - \ No newline at end of file From d5063e07e8b4737621978feffd37b18077b9ea64 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 30 Mar 2023 10:57:54 -0400 Subject: [PATCH 52/95] update torch --- .gitignore | 2 +- environment-wsl2.yaml | 11 ++++++----- launch.py | 6 +++--- requirements.txt | 1 + requirements_versions.txt | 4 ++-- webui-macos-env.sh | 2 +- webui.py | 2 ++ 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 0b1d17ca..3b48ba9a 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,4 @@ notification.mp3 /extensions /test/stdout.txt /test/stderr.txt -/cache.json +/cache.json* diff --git a/environment-wsl2.yaml b/environment-wsl2.yaml index f8872750..06134565 100644 --- a/environment-wsl2.yaml +++ b/environment-wsl2.yaml @@ -4,8 +4,9 @@ channels: - defaults dependencies: - python=3.10 - - pip=22.2.2 - - cudatoolkit=11.3 - - pytorch=1.12.1 - - torchvision=0.13.1 - - numpy=1.23.1 \ No newline at end of file + - pip=23.0 + - cudatoolkit=11.8 + - pytorch=2.0 + - torchvision=0.15 + - numpy=1.23 + \ No newline at end of file diff --git a/launch.py b/launch.py index 68e08114..37c8b516 100644 --- a/launch.py +++ b/launch.py @@ -225,10 +225,10 @@ def run_extensions_installers(settings_file): def prepare_environment(): global skip_install - torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117") + torch_command = os.environ.get('TORCH_COMMAND', "pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") - xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.16rc425') + xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17') gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379") clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1") openclip_package = os.environ.get('OPENCLIP_PACKAGE', "git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b") @@ -296,7 +296,7 @@ def prepare_environment(): if not os.path.isfile(requirements_file): requirements_file = os.path.join(script_path, requirements_file) - run_pip(f"install -r \"{requirements_file}\"", "requirements for Web UI") + run_pip(f"install -r \"{requirements_file}\"", "requirements") run_extensions_installers(settings_file=args.ui_settings_file) diff --git a/requirements.txt b/requirements.txt index c72b2927..36cdae6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +astunparse blendmodes accelerate basicsr diff --git a/requirements_versions.txt b/requirements_versions.txt index df65431a..6487f1c3 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -1,10 +1,10 @@ blendmodes==2022 transformers==4.25.1 -accelerate==0.12.0 +accelerate==0.18.0 basicsr==1.4.2 gfpgan==1.3.8 gradio==3.23 -numpy==1.23.3 +numpy==1.23.5 Pillow==9.4.0 realesrgan==0.3.0 torch diff --git a/webui-macos-env.sh b/webui-macos-env.sh index 37cac4fb..65d80413 100644 --- a/webui-macos-env.sh +++ b/webui-macos-env.sh @@ -11,7 +11,7 @@ fi export install_dir="$HOME" export COMMANDLINE_ARGS="--skip-torch-cuda-test --upcast-sampling --no-half-vae --use-cpu interrogate" -export TORCH_COMMAND="pip install torch==1.12.1 torchvision==0.13.1" +export TORCH_COMMAND="pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118" export K_DIFFUSION_REPO="https://github.com/brkirch/k-diffusion.git" export K_DIFFUSION_COMMIT_HASH="51c9778f269cedb55a4d88c79c0246d35bdadb71" export PYTORCH_ENABLE_MPS_FALLBACK=1 diff --git a/webui.py b/webui.py index b570895f..54552cdd 100644 --- a/webui.py +++ b/webui.py @@ -20,6 +20,8 @@ startup_timer = timer.Timer() import torch import pytorch_lightning # pytorch_lightning should be imported after torch, but it re-enables warnings on import so import once to disable them warnings.filterwarnings(action="ignore", category=DeprecationWarning, module="pytorch_lightning") +warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvision") + startup_timer.record("import torch") import gradio From a73f3bf0cfc89cde294b42f5c566017daf4b2ccd Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 30 Mar 2023 23:19:40 -0600 Subject: [PATCH 53/95] Change extras "scale to" to sliders --- scripts/postprocessing_upscale.py | 14 ++++++++++---- style.css | 4 ++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index 11eab31a..bc43719b 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -4,8 +4,9 @@ import numpy as np from modules import scripts_postprocessing, shared import gradio as gr -from modules.ui_components import FormRow +from modules.ui_components import FormRow, ToolButton +switch_values_symbol = '\U000021C5' # ⇅ upscale_cache = {} @@ -25,9 +26,12 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: with FormRow(): - upscaling_resize_w = gr.Number(label="Width", value=512, precision=0, elem_id="extras_upscaling_resize_w") - upscaling_resize_h = gr.Number(label="Height", value=512, precision=0, elem_id="extras_upscaling_resize_h") - upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") + with gr.Column(elem_id="upscaling_column_size", scale=4): + upscaling_resize_w = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") + upscaling_resize_h = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_w") + with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"): + upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn") + upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") with FormRow(): extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) @@ -36,6 +40,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") + upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False) tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) @@ -45,6 +50,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): "upscale_to_width": upscaling_resize_w, "upscale_to_height": upscaling_resize_h, "upscale_crop": upscaling_crop, + "upscaling_res_switch_btn": upscaling_res_switch_btn, "upscaler_1_name": extras_upscaler_1, "upscaler_2_name": extras_upscaler_2, "upscaler_2_visibility": extras_upscaler_2_visibility, diff --git a/style.css b/style.css index de16a7f2..aafc2362 100644 --- a/style.css +++ b/style.css @@ -312,6 +312,10 @@ div.dimensions-tools{ align-content: center; } +div#extras_scale_to_tab div.form{ + flex-direction: row; +} + #mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{ height: 480px !important; max-height: 480px !important; From 69ad46b047678a7a97a152a20e702bac61e37b8b Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 30 Mar 2023 23:25:39 -0600 Subject: [PATCH 54/95] Import switch_values_symbol --- scripts/postprocessing_upscale.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index bc43719b..bf27b64d 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -5,8 +5,7 @@ from modules import scripts_postprocessing, shared import gradio as gr from modules.ui_components import FormRow, ToolButton - -switch_values_symbol = '\U000021C5' # ⇅ +from modules.ui import switch_values_symbol upscale_cache = {} From 3ebdd2afd3769046289880d44bbe1322a832073f Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 31 Mar 2023 00:56:38 -0600 Subject: [PATCH 55/95] Don't return upscaling_res_switch_btn --- scripts/postprocessing_upscale.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index bf27b64d..e60208ac 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -49,7 +49,6 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): "upscale_to_width": upscaling_resize_w, "upscale_to_height": upscaling_resize_h, "upscale_crop": upscaling_crop, - "upscaling_res_switch_btn": upscaling_res_switch_btn, "upscaler_1_name": extras_upscaler_1, "upscaler_2_name": extras_upscaler_2, "upscaler_2_visibility": extras_upscaler_2_visibility, From c938b172a49433291e246b04f9835f3383bad0c8 Mon Sep 17 00:00:00 2001 From: bbonvi <6573230@gmail.com> Date: Fri, 31 Mar 2023 19:29:34 +0600 Subject: [PATCH 56/95] fix missing live preview and progress during certain tasks Sometimes tasks take longer than 5 seconds to start, resulting in missing progress bar and livepreviews, so we have to keep pulling for progress a bit longer (5s -> 20s). --- javascript/progressbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/progressbar.js b/javascript/progressbar.js index 4ac9b8db..eb44aab9 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -138,7 +138,7 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre return } - if(elapsedFromStart > 5 && !res.queued && !res.active){ + if(elapsedFromStart > 20 && !res.queued && !res.active){ removeProgressBar() return } From aef42bfec09a9ca93d1222b7b47256f37e192a32 Mon Sep 17 00:00:00 2001 From: keith <1868690+wk5ovc@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:05:49 +0800 Subject: [PATCH 57/95] Fix #9046 /sdapi/v1/txt2img endpoint not working **Describe what this pull request is trying to achieve.** Fix https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/9046 **Environment this was tested in** * OS: Linux * Browser: chrome * Graphics card: RTX 3090 --- webui.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/webui.py b/webui.py index b570895f..8927aa33 100644 --- a/webui.py +++ b/webui.py @@ -5,6 +5,7 @@ import importlib import signal import re import warnings +import asyncio from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware @@ -66,6 +67,46 @@ if cmd_opts.server_name: else: server_name = "0.0.0.0" if cmd_opts.listen else None +if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"): + # "Any thread" and "selector" should be orthogonal, but there's not a clean + # interface for composing policies so pick the right base. + _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy # type: ignore +else: + _BasePolicy = asyncio.DefaultEventLoopPolicy + + +class AnyThreadEventLoopPolicy(_BasePolicy): # type: ignore + """Event loop policy that allows loop creation on any thread. + + The default `asyncio` event loop policy only automatically creates + event loops in the main threads. Other threads must create event + loops explicitly or `asyncio.get_event_loop` (and therefore + `.IOLoop.current`) will fail. Installing this policy allows event + loops to be created automatically on any thread, matching the + behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2). + + Usage:: + + asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) + + .. versionadded:: 5.0 + + """ + + def get_event_loop(self) -> asyncio.AbstractEventLoop: + try: + return super().get_event_loop() + except (RuntimeError, AssertionError): + # This was an AssertionError in python 3.4.2 (which ships with debian jessie) + # and changed to a RuntimeError in 3.4.3. + # "There is no current event loop in thread %r" + loop = self.new_event_loop() + self.set_event_loop(loop) + return loop + + +asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) + def check_versions(): if shared.cmd_opts.skip_version_check: From 4fa59b045add1d23350e884e201dc77bc34864e6 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 3 Apr 2023 15:23:35 -0400 Subject: [PATCH 58/95] update xformers --- environment-wsl2.yaml | 1 - launch.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/environment-wsl2.yaml b/environment-wsl2.yaml index 06134565..0c4ae680 100644 --- a/environment-wsl2.yaml +++ b/environment-wsl2.yaml @@ -9,4 +9,3 @@ dependencies: - pytorch=2.0 - torchvision=0.15 - numpy=1.23 - \ No newline at end of file diff --git a/launch.py b/launch.py index 37c8b516..c5ae9092 100644 --- a/launch.py +++ b/launch.py @@ -228,7 +228,7 @@ def prepare_environment(): torch_command = os.environ.get('TORCH_COMMAND', "pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") - xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17') + xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.18') gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379") clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1") openclip_package = os.environ.get('OPENCLIP_PACKAGE', "git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b") From 80752f43b22acd85bf6ab54b2e4788f144a0c813 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 4 Apr 2023 17:27:27 -0400 Subject: [PATCH 59/95] revert xformers --- launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launch.py b/launch.py index c5ae9092..37c8b516 100644 --- a/launch.py +++ b/launch.py @@ -228,7 +228,7 @@ def prepare_environment(): torch_command = os.environ.get('TORCH_COMMAND', "pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") - xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.18') + xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17') gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379") clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1") openclip_package = os.environ.get('OPENCLIP_PACKAGE', "git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b") From c01dc1cb30f7cd87e1df6458580da99c702ee513 Mon Sep 17 00:00:00 2001 From: pangbo13 <373108669@qq.com> Date: Wed, 5 Apr 2023 19:22:51 +0800 Subject: [PATCH 60/95] add dropdown for X/Y/Z plot --- scripts/xyz_grid.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 3895a795..774fa2c7 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -374,16 +374,19 @@ class Script(scripts.Script): with gr.Row(): x_type = gr.Dropdown(label="X type", choices=[x.label for x in self.current_axis_options], value=self.current_axis_options[1].label, type="index", elem_id=self.elem_id("x_type")) x_values = gr.Textbox(label="X values", lines=1, elem_id=self.elem_id("x_values")) + x_values_dropdown = gr.Dropdown(label="X values",visible=False,multiselect=True,interactive=True) fill_x_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_x_tool_button", visible=False) with gr.Row(): y_type = gr.Dropdown(label="Y type", choices=[x.label for x in self.current_axis_options], value=self.current_axis_options[0].label, type="index", elem_id=self.elem_id("y_type")) y_values = gr.Textbox(label="Y values", lines=1, elem_id=self.elem_id("y_values")) + y_values_dropdown = gr.Dropdown(label="Y values",visible=False,multiselect=True,interactive=True) fill_y_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_y_tool_button", visible=False) with gr.Row(): z_type = gr.Dropdown(label="Z type", choices=[x.label for x in self.current_axis_options], value=self.current_axis_options[0].label, type="index", elem_id=self.elem_id("z_type")) z_values = gr.Textbox(label="Z values", lines=1, elem_id=self.elem_id("z_values")) + z_values_dropdown = gr.Dropdown(label="Z values",visible=False,multiselect=True,interactive=True) fill_z_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_z_tool_button", visible=False) with gr.Row(variant="compact", elem_id="axis_options"): @@ -413,18 +416,20 @@ class Script(scripts.Script): def fill(x_type): axis = self.current_axis_options[x_type] - return ", ".join(axis.choices()) if axis.choices else gr.update() + return axis.choices() if axis.choices else gr.update() - fill_x_button.click(fn=fill, inputs=[x_type], outputs=[x_values]) - fill_y_button.click(fn=fill, inputs=[y_type], outputs=[y_values]) - fill_z_button.click(fn=fill, inputs=[z_type], outputs=[z_values]) + fill_x_button.click(fn=fill, inputs=[x_type], outputs=[x_values_dropdown]) + fill_y_button.click(fn=fill, inputs=[y_type], outputs=[y_values_dropdown]) + fill_z_button.click(fn=fill, inputs=[z_type], outputs=[z_values_dropdown]) def select_axis(x_type): - return gr.Button.update(visible=self.current_axis_options[x_type].choices is not None) + choices = self.current_axis_options[x_type].choices + has_choices = choices is not None + return gr.Button.update(visible=has_choices),gr.Textbox.update(visible=not has_choices),gr.update(choices=choices() if has_choices else None,visible=has_choices,value=[]) - x_type.change(fn=select_axis, inputs=[x_type], outputs=[fill_x_button]) - y_type.change(fn=select_axis, inputs=[y_type], outputs=[fill_y_button]) - z_type.change(fn=select_axis, inputs=[z_type], outputs=[fill_z_button]) + x_type.change(fn=select_axis, inputs=[x_type], outputs=[fill_x_button,x_values,x_values_dropdown]) + y_type.change(fn=select_axis, inputs=[y_type], outputs=[fill_y_button,y_values,y_values_dropdown]) + z_type.change(fn=select_axis, inputs=[z_type], outputs=[fill_z_button,z_values,z_values_dropdown]) self.infotext_fields = ( (x_type, "X Type"), @@ -435,20 +440,23 @@ class Script(scripts.Script): (z_values, "Z Values"), ) - return [x_type, x_values, y_type, y_values, z_type, z_values, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size] + return [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size] - def run(self, p, x_type, x_values, y_type, y_values, z_type, z_values, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size): + def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size): if not no_fixed_seeds: modules.processing.fix_seed(p) if not opts.return_grid: p.batch_size = 1 - def process_axis(opt, vals): + def process_axis(opt, vals, vals_dropdown): if opt.label == 'Nothing': return [0] - valslist = [x.strip() for x in chain.from_iterable(csv.reader(StringIO(vals))) if x] + if opt.choices is not None: + valslist = vals_dropdown + else: + valslist = [x.strip() for x in chain.from_iterable(csv.reader(StringIO(vals))) if x] if opt.type == int: valslist_ext = [] @@ -506,13 +514,13 @@ class Script(scripts.Script): return valslist x_opt = self.current_axis_options[x_type] - xs = process_axis(x_opt, x_values) + xs = process_axis(x_opt, x_values, x_values_dropdown) y_opt = self.current_axis_options[y_type] - ys = process_axis(y_opt, y_values) + ys = process_axis(y_opt, y_values, y_values_dropdown) z_opt = self.current_axis_options[z_type] - zs = process_axis(z_opt, z_values) + zs = process_axis(z_opt, z_values, z_values_dropdown) # this could be moved to common code, but unlikely to be ever triggered anywhere else Image.MAX_IMAGE_PIXELS = None # disable check in Pillow and rely on check below to allow large custom image sizes From 3ac5f9c471e4cfb5b664f9f0a7f7e7b171b1cee1 Mon Sep 17 00:00:00 2001 From: pangbo13 <373108669@qq.com> Date: Wed, 5 Apr 2023 21:43:27 +0800 Subject: [PATCH 61/95] fix axis swap and infotxt --- scripts/xyz_grid.py | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 774fa2c7..52ae1c6e 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -404,14 +404,14 @@ class Script(scripts.Script): swap_yz_axes_button = gr.Button(value="Swap Y/Z axes", elem_id="yz_grid_swap_axes_button") swap_xz_axes_button = gr.Button(value="Swap X/Z axes", elem_id="xz_grid_swap_axes_button") - def swap_axes(axis1_type, axis1_values, axis2_type, axis2_values): - return self.current_axis_options[axis2_type].label, axis2_values, self.current_axis_options[axis1_type].label, axis1_values + def swap_axes(axis1_type, axis1_values, axis1_values_dropdown, axis2_type, axis2_values, axis2_values_dropdown): + return self.current_axis_options[axis2_type].label, axis2_values, axis2_values_dropdown, self.current_axis_options[axis1_type].label, axis1_values, axis1_values_dropdown - xy_swap_args = [x_type, x_values, y_type, y_values] + xy_swap_args = [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown] swap_xy_axes_button.click(swap_axes, inputs=xy_swap_args, outputs=xy_swap_args) - yz_swap_args = [y_type, y_values, z_type, z_values] + yz_swap_args = [y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown] swap_yz_axes_button.click(swap_axes, inputs=yz_swap_args, outputs=yz_swap_args) - xz_swap_args = [x_type, x_values, z_type, z_values] + xz_swap_args = [x_type, x_values, x_values_dropdown, z_type, z_values, z_values_dropdown] swap_xz_axes_button.click(swap_axes, inputs=xz_swap_args, outputs=xz_swap_args) def fill(x_type): @@ -422,22 +422,37 @@ class Script(scripts.Script): fill_y_button.click(fn=fill, inputs=[y_type], outputs=[y_values_dropdown]) fill_z_button.click(fn=fill, inputs=[z_type], outputs=[z_values_dropdown]) - def select_axis(x_type): - choices = self.current_axis_options[x_type].choices + def select_axis(axis_type,axis_values_dropdown): + choices = self.current_axis_options[axis_type].choices has_choices = choices is not None - return gr.Button.update(visible=has_choices),gr.Textbox.update(visible=not has_choices),gr.update(choices=choices() if has_choices else None,visible=has_choices,value=[]) + current_values = axis_values_dropdown + if has_choices: + choices = choices() + if isinstance(current_values,str): + current_values = current_values.split(",") + current_values = list(filter(lambda x: x in choices, current_values)) + return gr.Button.update(visible=has_choices),gr.Textbox.update(visible=not has_choices),gr.update(choices=choices if has_choices else None,visible=has_choices,value=current_values) - x_type.change(fn=select_axis, inputs=[x_type], outputs=[fill_x_button,x_values,x_values_dropdown]) - y_type.change(fn=select_axis, inputs=[y_type], outputs=[fill_y_button,y_values,y_values_dropdown]) - z_type.change(fn=select_axis, inputs=[z_type], outputs=[fill_z_button,z_values,z_values_dropdown]) + x_type.change(fn=select_axis, inputs=[x_type,x_values_dropdown], outputs=[fill_x_button,x_values,x_values_dropdown]) + y_type.change(fn=select_axis, inputs=[y_type,y_values_dropdown], outputs=[fill_y_button,y_values,y_values_dropdown]) + z_type.change(fn=select_axis, inputs=[z_type,z_values_dropdown], outputs=[fill_z_button,z_values,z_values_dropdown]) + + def get_dropdown_update_from_params(axis,params): + val_key = axis + " Values" + vals = params.get(val_key,"") + valslist = [x.strip() for x in chain.from_iterable(csv.reader(StringIO(vals))) if x] + return gr.update(value = valslist) self.infotext_fields = ( (x_type, "X Type"), (x_values, "X Values"), + (x_values_dropdown, lambda params:get_dropdown_update_from_params("X",params)), (y_type, "Y Type"), (y_values, "Y Values"), + (y_values_dropdown, lambda params:get_dropdown_update_from_params("Y",params)), (z_type, "Z Type"), (z_values, "Z Values"), + (z_values_dropdown, lambda params:get_dropdown_update_from_params("Z",params)), ) return [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size] @@ -514,12 +529,18 @@ class Script(scripts.Script): return valslist x_opt = self.current_axis_options[x_type] + if x_opt.choices is not None: + x_values = ",".join(x_values_dropdown) xs = process_axis(x_opt, x_values, x_values_dropdown) y_opt = self.current_axis_options[y_type] + if y_opt.choices is not None: + y_values = ",".join(y_values_dropdown) ys = process_axis(y_opt, y_values, y_values_dropdown) z_opt = self.current_axis_options[z_type] + if z_opt.choices is not None: + z_values = ",".join(z_values_dropdown) zs = process_axis(z_opt, z_values, z_values_dropdown) # this could be moved to common code, but unlikely to be ever triggered anywhere else From b3593d0997bfdcca7f8aa01663e81720db50e494 Mon Sep 17 00:00:00 2001 From: For Sure Date: Thu, 6 Apr 2023 19:42:26 +0300 Subject: [PATCH 62/95] Add support for saving init images in img2img --- modules/processing.py | 8 ++++++++ modules/shared.py | 3 +++ 2 files changed, 11 insertions(+) diff --git a/modules/processing.py b/modules/processing.py index 6d9c6a8d..5556afc5 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -3,6 +3,7 @@ import math import os import sys import warnings +import hashlib import torch import numpy as np @@ -476,6 +477,7 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter "Conditional mask weight": getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None, "Clip skip": None if clip_skip <= 1 else clip_skip, "ENSD": None if opts.eta_noise_seed_delta == 0 else opts.eta_noise_seed_delta, + "Init image hash": getattr(p, 'init_img_hash', None) } generation_params.update(p.extra_generation_params) @@ -1007,6 +1009,12 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.color_corrections = [] imgs = [] for img in self.init_images: + + # Save init image + if opts.save_init_img: + self.init_img_hash = hashlib.md5(img.tobytes()).hexdigest() + images.save_image(img, path=opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False) + image = images.flatten(img, opts.img2img_background_color) if crop_region is None and self.resize_mode != 3: diff --git a/modules/shared.py b/modules/shared.py index 5fd0eecb..69c2b21e 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -39,6 +39,7 @@ restricted_opts = { "outdir_grids", "outdir_txt2img_grids", "outdir_save", + "outdir_init_images" } ui_reorder_categories = [ @@ -253,6 +254,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "use_upscaler_name_as_suffix": OptionInfo(False, "Use upscaler name as filename suffix in the extras tab"), "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), "do_not_add_watermark": OptionInfo(False, "Do not add watermark to images"), + "save_init_img": OptionInfo(True, "Save init images when using img2img"), "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), "clean_temp_dir_at_start": OptionInfo(False, "Cleanup non-default temporary directory when starting webui"), @@ -268,6 +270,7 @@ options_templates.update(options_section(('saving-paths', "Paths for saving"), { "outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output directory for txt2img grids', component_args=hide_dirs), "outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output directory for img2img grids', component_args=hide_dirs), "outdir_save": OptionInfo("log/images", "Directory for saving images using the Save button", component_args=hide_dirs), + "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs), })) options_templates.update(options_section(('saving-to-dirs', "Saving to a directory"), { From 63a6f9b4d98a192bb359910cb284cf00582baabf Mon Sep 17 00:00:00 2001 From: forsurefr <67145502+forsurefr@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:13:51 +0300 Subject: [PATCH 63/95] Do not save init image by default --- modules/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 69c2b21e..c5a1b5ad 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -254,7 +254,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "use_upscaler_name_as_suffix": OptionInfo(False, "Use upscaler name as filename suffix in the extras tab"), "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), "do_not_add_watermark": OptionInfo(False, "Do not add watermark to images"), - "save_init_img": OptionInfo(True, "Save init images when using img2img"), + "save_init_img": OptionInfo(False, "Save init images when using img2img"), "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), "clean_temp_dir_at_start": OptionInfo(False, "Cleanup non-default temporary directory when starting webui"), From 1aba8d82cbb816a755d012c5c729d8bafeb1b8ed Mon Sep 17 00:00:00 2001 From: yike5460 Date: Sun, 9 Apr 2023 22:22:43 +0800 Subject: [PATCH 64/95] feat: add branch support for extension installation --- modules/ui_extensions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index efd6cda2..d9487f83 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -129,7 +129,7 @@ def normalize_git_url(url): return url -def install_extension_from_url(dirname, url): +def install_extension_from_url(dirname, branch_name, url): check_access() assert url, 'No URL specified' @@ -150,7 +150,7 @@ def install_extension_from_url(dirname, url): try: shutil.rmtree(tmpdir, True) - with git.Repo.clone_from(url, tmpdir) as repo: + with git.Repo.clone_from(url, tmpdir, branch=branch_name if branch_name else '') as repo: repo.remote().fetch() for submodule in repo.submodules: submodule.update() @@ -376,13 +376,14 @@ def create_ui(): with gr.TabItem("Install from URL"): install_url = gr.Text(label="URL for extension's git repository") + install_branch = gr.Text(label="Branch name for extension's git repository", placeholder="Leave empty for default branch") install_dirname = gr.Text(label="Local directory name", placeholder="Leave empty for auto") install_button = gr.Button(value="Install", variant="primary") install_result = gr.HTML(elem_id="extension_install_result") install_button.click( fn=modules.ui.wrap_gradio_call(install_extension_from_url, extra_outputs=[gr.update()]), - inputs=[install_dirname, install_url], + inputs=[install_dirname, install_branch, install_url], outputs=[extensions_table, install_result], ) From c19618f37059b425b1e53429ad8def2caa78cdec Mon Sep 17 00:00:00 2001 From: Ilya Khadykin Date: Sun, 9 Apr 2023 21:33:09 +0200 Subject: [PATCH 65/95] fix(extras): fix batch image processing on 'Extras\Batch Process' tab This change fixes an issue where an incorrect type was passed to the PIL.Image.open() function that caused the whole process to fail. Scope of this change is limited to only batch image processing, and it shouldn't affect other functionality. --- modules/postprocessing.py | 6 ++++-- modules/ui_postprocessing.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/postprocessing.py b/modules/postprocessing.py index 09d8e605..c27ad8db 100644 --- a/modules/postprocessing.py +++ b/modules/postprocessing.py @@ -1,4 +1,6 @@ import os +import tempfile +from typing import List from PIL import Image @@ -6,7 +8,7 @@ from modules import shared, images, devices, scripts, scripts_postprocessing, ui from modules.shared import opts -def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, show_extras_results, *args, save_output: bool = True): +def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemporaryFile], input_dir, output_dir, show_extras_results, *args, save_output: bool = True): devices.torch_gc() shared.state.begin() @@ -18,7 +20,7 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, if extras_mode == 1: for img in image_folder: - image = Image.open(img) + image = Image.open(os.path.abspath(img.name)) image_data.append(image) image_names.append(os.path.splitext(img.orig_name)[0]) elif extras_mode == 2: diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py index b418d955..d278e1b6 100644 --- a/modules/ui_postprocessing.py +++ b/modules/ui_postprocessing.py @@ -13,7 +13,7 @@ def create_ui(): extras_image = gr.Image(label="Source", source="upload", interactive=True, type="pil", elem_id="extras_image") with gr.TabItem('Batch Process', elem_id="extras_batch_process_tab") as tab_batch: - image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file", elem_id="extras_image_batch") + image_batch = gr.Files(label="Batch Process", interactive=True, elem_id="extras_image_batch") with gr.TabItem('Batch from Directory', elem_id="extras_batch_directory_tab") as tab_batch_dir: extras_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs, placeholder="A directory on the same machine where the server is running.", elem_id="extras_batch_input_dir") From 7c62bb2788d9cec10bab9d0154bd24f3658f7a83 Mon Sep 17 00:00:00 2001 From: yike5460 Date: Mon, 10 Apr 2023 09:38:26 +0800 Subject: [PATCH 66/95] fix: support for default branch --- modules/ui_extensions.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index d9487f83..b402bc8b 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -150,10 +150,17 @@ def install_extension_from_url(dirname, branch_name, url): try: shutil.rmtree(tmpdir, True) - with git.Repo.clone_from(url, tmpdir, branch=branch_name if branch_name else '') as repo: - repo.remote().fetch() - for submodule in repo.submodules: - submodule.update() + if branch_name == '': + # if no branch is specified, use the default branch + with git.Repo.clone_from(url, tmpdir) as repo: + repo.remote().fetch() + for submodule in repo.submodules: + submodule.update() + else: + with git.Repo.clone_from(url, tmpdir, branch=branch_name) as repo: + repo.remote().fetch() + for submodule in repo.submodules: + submodule.update() try: os.rename(tmpdir, target_dir) except OSError as err: @@ -376,7 +383,7 @@ def create_ui(): with gr.TabItem("Install from URL"): install_url = gr.Text(label="URL for extension's git repository") - install_branch = gr.Text(label="Branch name for extension's git repository", placeholder="Leave empty for default branch") + install_branch = gr.Text(label="Specific branch name", placeholder="Leave empty for default main branch") install_dirname = gr.Text(label="Local directory name", placeholder="Leave empty for auto") install_button = gr.Button(value="Install", variant="primary") install_result = gr.HTML(elem_id="extension_install_result") From 7fb72edaffd3d4f2336d2478a424fc455f2376a6 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 13 Apr 2023 06:47:48 -0400 Subject: [PATCH 67/95] change index url --- launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launch.py b/launch.py index 37c8b516..4256ef25 100644 --- a/launch.py +++ b/launch.py @@ -225,7 +225,7 @@ def run_extensions_installers(settings_file): def prepare_environment(): global skip_install - torch_command = os.environ.get('TORCH_COMMAND', "pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118") + torch_command = os.environ.get('TORCH_COMMAND', "pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17') From 596556162efd66cca225dffa3765d77cd51f69fe Mon Sep 17 00:00:00 2001 From: File_xor Date: Sun, 16 Apr 2023 16:49:21 +0900 Subject: [PATCH 68/95] Add filename pattern for CLIP_stop_at_last_layers. --- modules/images.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/images.py b/modules/images.py index b3535070..6391c34c 100644 --- a/modules/images.py +++ b/modules/images.py @@ -352,6 +352,7 @@ class FilenameGenerator: 'prompt_no_styles': lambda self: self.prompt_no_style(), 'prompt_spaces': lambda self: sanitize_filename_part(self.prompt, replace_spaces=False), 'prompt_words': lambda self: self.prompt_words(), + 'clip_skip': lambda: opts.data["CLIP_stop_at_last_layers"], } default_time_format = '%Y%m%d%H%M%S' From acbec225549987297383e3a31290b3f80ed064fd Mon Sep 17 00:00:00 2001 From: File_xor Date: Sun, 16 Apr 2023 17:14:11 +0900 Subject: [PATCH 69/95] Add self argument that is mandatory to [clip_skip] filename pattern. --- modules/images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/images.py b/modules/images.py index 6391c34c..1a118a69 100644 --- a/modules/images.py +++ b/modules/images.py @@ -352,7 +352,7 @@ class FilenameGenerator: 'prompt_no_styles': lambda self: self.prompt_no_style(), 'prompt_spaces': lambda self: sanitize_filename_part(self.prompt, replace_spaces=False), 'prompt_words': lambda self: self.prompt_words(), - 'clip_skip': lambda: opts.data["CLIP_stop_at_last_layers"], + 'clip_skip': lambda self: opts.data["CLIP_stop_at_last_layers"], } default_time_format = '%Y%m%d%H%M%S' From eddcdb8061012113eb9a97ec6c1af538344970a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Ra=C4=8Dinsk=C3=BD?= Date: Mon, 17 Apr 2023 23:48:28 +0200 Subject: [PATCH 70/95] adds label to buttons to make them hide --- modules/ui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 627fbe0b..37fcb92c 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -171,8 +171,8 @@ def create_seed_inputs(target_interface): with FormRow(elem_id=target_interface + '_seed_row', variant="compact"): seed = (gr.Textbox if cmd_opts.use_textbox_seed else gr.Number)(label='Seed', value=-1, elem_id=target_interface + '_seed') seed.style(container=False) - random_seed = ToolButton(random_symbol, elem_id=target_interface + '_random_seed') - reuse_seed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_seed') + random_seed = ToolButton(random_symbol, elem_id=target_interface + '_random_seed', label='Random seed') + reuse_seed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_seed', label='Reuse seed') seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=False) @@ -468,7 +468,7 @@ def create_ui(): height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") with gr.Column(elem_id="txt2img_dimensions_row", scale=1, elem_classes="dimensions-tools"): - res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn") + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn", label="Switch dims") if opts.dimensions_and_batch_together: with gr.Column(elem_id="txt2img_column_batch"): @@ -1705,7 +1705,7 @@ def create_ui(): if init_field is not None: init_field(saved_value) - if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number, gr.Dropdown] and x.visible: + if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number, gr.Dropdown, ToolButton] and x.visible: apply_field(x, 'visible') if type(x) == gr.Slider: From f4b332f0419e09cec6983edcd07aae2ee0c14c24 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Tue, 18 Apr 2023 17:01:46 -0600 Subject: [PATCH 71/95] Add "None" option to extra networks dropdowns --- extensions-builtin/Lora/extra_networks_lora.py | 2 +- extensions-builtin/Lora/scripts/lora_script.py | 2 +- modules/extra_networks_hypernet.py | 2 +- modules/shared.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions-builtin/Lora/extra_networks_lora.py b/extensions-builtin/Lora/extra_networks_lora.py index 6be6ef73..45f899fc 100644 --- a/extensions-builtin/Lora/extra_networks_lora.py +++ b/extensions-builtin/Lora/extra_networks_lora.py @@ -8,7 +8,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): def activate(self, p, params_list): additional = shared.opts.sd_lora - if additional != "" and additional in lora.available_loras and len([x for x in params_list if x.items[0] == additional]) == 0: + if additional != "None" and additional in lora.available_loras and len([x for x in params_list if x.items[0] == additional]) == 0: p.all_prompts = [x + f"" for x in p.all_prompts] params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier])) diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index 0adab225..3fc38ab9 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -52,5 +52,5 @@ script_callbacks.on_before_ui(before_ui) shared.options_templates.update(shared.options_section(('extra_networks', "Extra Networks"), { - "sd_lora": shared.OptionInfo("None", "Add Lora to prompt", gr.Dropdown, lambda: {"choices": [""] + [x for x in lora.available_loras]}, refresh=lora.list_available_loras), + "sd_lora": shared.OptionInfo("None", "Add Lora to prompt", gr.Dropdown, lambda: {"choices": ["None"] + [x for x in lora.available_loras]}, refresh=lora.list_available_loras), })) diff --git a/modules/extra_networks_hypernet.py b/modules/extra_networks_hypernet.py index d3a4d7ad..33d100dd 100644 --- a/modules/extra_networks_hypernet.py +++ b/modules/extra_networks_hypernet.py @@ -9,7 +9,7 @@ class ExtraNetworkHypernet(extra_networks.ExtraNetwork): def activate(self, p, params_list): additional = shared.opts.sd_hypernetwork - if additional != "" and additional in shared.hypernetworks and len([x for x in params_list if x.items[0] == additional]) == 0: + if additional != "None" and additional in shared.hypernetworks and len([x for x in params_list if x.items[0] == additional]) == 0: p.all_prompts = [x + f"" for x in p.all_prompts] params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier])) diff --git a/modules/shared.py b/modules/shared.py index 5fd0eecb..5a5fbae6 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -361,7 +361,7 @@ options_templates.update(options_section(('extra_networks', "Extra Networks"), { "extra_networks_card_width": OptionInfo(0, "Card width for Extra Networks (px)"), "extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks (px)"), "extra_networks_add_text_separator": OptionInfo(" ", "Extra text to add before <...> when adding extra network to prompt"), - "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": [""] + [x for x in hypernetworks.keys()]}, refresh=reload_hypernetworks), + "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None"] + [x for x in hypernetworks.keys()]}, refresh=reload_hypernetworks), })) options_templates.update(options_section(('ui', "User interface"), { From d40e44ade479f7bba30d5317381cbc58c861775b Mon Sep 17 00:00:00 2001 From: Deciare <1689220+deciare@users.noreply.github.com> Date: Tue, 18 Apr 2023 23:18:58 -0400 Subject: [PATCH 72/95] Option to use CPU for random number generation. Makes a given manual seed generate the same images across different platforms, independently of the GPU architecture in use. Fixes #9613. --- modules/devices.py | 8 ++++++-- modules/sd_samplers_common.py | 9 +++++++++ modules/sd_samplers_kdiffusion.py | 2 +- modules/shared.py | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/devices.py b/modules/devices.py index 52c3e7cd..3bc86a6a 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -92,14 +92,18 @@ def cond_cast_float(input): def randn(seed, shape): + from modules.shared import opts + torch.manual_seed(seed) - if device.type == 'mps': + if opts.use_cpu_randn or device.type == 'mps': return torch.randn(shape, device=cpu).to(device) return torch.randn(shape, device=device) def randn_without_seed(shape): - if device.type == 'mps': + from modules.shared import opts + + if opts.use_cpu_randn or device.type == 'mps': return torch.randn(shape, device=cpu).to(device) return torch.randn(shape, device=device) diff --git a/modules/sd_samplers_common.py b/modules/sd_samplers_common.py index a1aac7cf..e6a372d5 100644 --- a/modules/sd_samplers_common.py +++ b/modules/sd_samplers_common.py @@ -60,3 +60,12 @@ def store_latent(decoded): class InterruptedException(BaseException): pass + +if opts.use_cpu_randn: + import torchsde._brownian.brownian_interval + + def torchsde_randn(size, dtype, device, seed): + generator = torch.Generator(devices.cpu).manual_seed(int(seed)) + return torch.randn(size, dtype=dtype, device=devices.cpu, generator=generator).to(device) + + torchsde._brownian.brownian_interval._randn = torchsde_randn diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index e9f08518..13f4567a 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -190,7 +190,7 @@ class TorchHijack: if noise.shape == x.shape: return noise - if x.device.type == 'mps': + if opts.use_cpu_randn or x.device.type == 'mps': return torch.randn_like(x, device=devices.cpu).to(x.device) else: return torch.randn_like(x) diff --git a/modules/shared.py b/modules/shared.py index 5fd0eecb..59b037d5 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -331,6 +331,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "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": 12, "step": 1}), "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"), + "use_cpu_randn": OptionInfo(False, "Use CPU for random number generation to make manual seeds generate the same image across platforms. This may change existing seeds."), })) options_templates.update(options_section(('compatibility', "Compatibility"), { From dec5cdd9b89dd683f04fb904ebd8a56dfce860ae Mon Sep 17 00:00:00 2001 From: AdjointOperator Date: Wed, 19 Apr 2023 15:35:50 +0800 Subject: [PATCH 73/95] add tiled inference support for ScuNET --- .../ScuNET/scripts/scunet_model.py | 81 +++++++++++++++---- modules/shared.py | 2 + 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/extensions-builtin/ScuNET/scripts/scunet_model.py b/extensions-builtin/ScuNET/scripts/scunet_model.py index e0fbf3a3..c7fd5739 100644 --- a/extensions-builtin/ScuNET/scripts/scunet_model.py +++ b/extensions-builtin/ScuNET/scripts/scunet_model.py @@ -5,11 +5,15 @@ import traceback import PIL.Image import numpy as np import torch +from tqdm import tqdm + from basicsr.utils.download_util import load_file_from_url import modules.upscaler from modules import devices, modelloader from scunet_model_arch import SCUNet as net +from modules.shared import opts +from modules import images class UpscalerScuNET(modules.upscaler.Upscaler): @@ -42,28 +46,78 @@ class UpscalerScuNET(modules.upscaler.Upscaler): scalers.append(scaler_data2) self.scalers = scalers - def do_upscale(self, img: PIL.Image, selected_file): + @staticmethod + @torch.no_grad() + def tiled_inference(img, model): + # test the image tile by tile + h, w = img.shape[2:] + tile = opts.SCUNET_tile + tile_overlap = opts.SCUNET_tile_overlap + if tile == 0: + return model(img) + + device = devices.get_device_for('scunet') + assert tile % 8 == 0, "tile size should be a multiple of window_size" + sf = 1 + + stride = tile - tile_overlap + h_idx_list = list(range(0, h - tile, stride)) + [h - tile] + w_idx_list = list(range(0, w - tile, stride)) + [w - tile] + E = torch.zeros(1, 3, h * sf, w * sf, dtype=img.dtype, device=device) + W = torch.zeros_like(E, dtype=devices.dtype, device=device) + + with tqdm(total=len(h_idx_list) * len(w_idx_list), desc="ScuNET tiles") as pbar: + for h_idx in h_idx_list: + + for w_idx in w_idx_list: + + in_patch = img[..., h_idx: h_idx + tile, w_idx: w_idx + tile] + + out_patch = model(in_patch) + out_patch_mask = torch.ones_like(out_patch) + + E[ + ..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf + ].add_(out_patch) + W[ + ..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf + ].add_(out_patch_mask) + pbar.update(1) + output = E.div_(W) + + return output + + def do_upscale(self, img: PIL.Image.Image, selected_file): + torch.cuda.empty_cache() model = self.load_model(selected_file) if model is None: + print(f"ScuNET: Unable to load model from {selected_file}", file=sys.stderr) return img device = devices.get_device_for('scunet') - img = np.array(img) - img = img[:, :, ::-1] - img = np.moveaxis(img, 2, 0) / 255 - img = torch.from_numpy(img).float() - img = img.unsqueeze(0).to(device) + tile = opts.SCUNET_tile + h, w = img.height, img.width + np_img = np.array(img) + np_img = np_img[:, :, ::-1] # RGB to BGR + np_img = np_img.transpose((2, 0, 1)) / 255 # HWC to CHW + torch_img = torch.from_numpy(np_img).float().unsqueeze(0).to(device) # type: ignore - with torch.no_grad(): - output = model(img) - output = output.squeeze().float().cpu().clamp_(0, 1).numpy() - output = 255. * np.moveaxis(output, 0, 2) - output = output.astype(np.uint8) - output = output[:, :, ::-1] + if tile > h or tile > w: + _img = torch.zeros(1, 3, max(h, tile), max(w, tile), dtype=torch_img.dtype, device=torch_img.device) + _img[:, :, :h, :w] = torch_img # pad image + torch_img = _img + + torch_output = self.tiled_inference(torch_img, model).squeeze(0) + torch_output = torch_output[:, :h * 1, :w * 1] # remove padding, if any + np_output: np.ndarray = torch_output.float().cpu().clamp_(0, 1).numpy() + del torch_img, torch_output torch.cuda.empty_cache() - return PIL.Image.fromarray(output, 'RGB') + + output = np_output.transpose((1, 2, 0)) # CHW to HWC + output = output[:, :, ::-1] # BGR to RGB + return PIL.Image.fromarray((output * 255).astype(np.uint8)) def load_model(self, path: str): device = devices.get_device_for('scunet') @@ -84,4 +138,3 @@ class UpscalerScuNET(modules.upscaler.Upscaler): model = model.to(device) return model - diff --git a/modules/shared.py b/modules/shared.py index 5fd0eecb..056f9fc6 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -283,6 +283,8 @@ options_templates.update(options_section(('upscaling', "Upscaling"), { "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Select which Real-ESRGAN models to show in the web UI. (Requires restart)", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}), "upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}), + "SCUNET_tile": OptionInfo(256, "Tile size for SCUNET upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), + "SCUNET_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SCUNET upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}), })) options_templates.update(options_section(('face-restoration', "Face restoration"), { From 33365f15bf667dc421bd844c372987cf58008daf Mon Sep 17 00:00:00 2001 From: TFWol <9045213+TFWol@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:23:46 -0700 Subject: [PATCH 74/95] Remove old code roll random artists Removed context menu entry that used to be for rolling artists from the now removed artists.csv. It was probably meant to be removed at commit 6d805b6. --- javascript/contextMenus.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js index 06f505b0..9468c107 100644 --- a/javascript/contextMenus.js +++ b/javascript/contextMenus.js @@ -161,14 +161,6 @@ addContextMenuEventListener = initResponse[2]; appendContextMenuOption('#img2img_interrupt','Cancel generate forever',cancelGenerateForever) appendContextMenuOption('#img2img_generate', 'Cancel generate forever',cancelGenerateForever) - appendContextMenuOption('#roll','Roll three', - function(){ - let rollbutton = get_uiCurrentTabContent().querySelector('#roll'); - setTimeout(function(){rollbutton.click()},100) - setTimeout(function(){rollbutton.click()},200) - setTimeout(function(){rollbutton.click()},300) - } - ) })(); //End example Context Menu Items From e735be8b5b455aa2bd02be0b3c6fe0596ad4ec52 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Wed, 19 Apr 2023 22:04:34 -0600 Subject: [PATCH 75/95] Automatically select current word --- javascript/edit-attention.js | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 20a5aadf..1c9f6737 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -17,7 +17,7 @@ function keyupEditAttention(event){ // Find opening parenthesis around current cursor const before = text.substring(0, selectionStart); let beforeParen = before.lastIndexOf(OPEN); - if (beforeParen == -1) return false; + if (beforeParen == -1) return false; let beforeParenClose = before.lastIndexOf(CLOSE); while (beforeParenClose !== -1 && beforeParenClose > beforeParen) { beforeParen = before.lastIndexOf(OPEN, beforeParen - 1); @@ -27,7 +27,7 @@ function keyupEditAttention(event){ // Find closing parenthesis around current cursor const after = text.substring(selectionStart); let afterParen = after.indexOf(CLOSE); - if (afterParen == -1) return false; + if (afterParen == -1) return false; let afterParenOpen = after.indexOf(OPEN); while (afterParenOpen !== -1 && afterParen > afterParenOpen) { afterParen = after.indexOf(CLOSE, afterParen + 1); @@ -43,10 +43,35 @@ function keyupEditAttention(event){ target.setSelectionRange(selectionStart, selectionEnd); return true; } + + function selectCurrentWord(){ + if (selectionStart !== selectionEnd) return false; + + // Select the current word, find the start and end of the word (first space before and after) + const wordStart = text.substring(0, selectionStart).lastIndexOf(" ") + 1; + const wordEnd = text.substring(selectionEnd).indexOf(" "); + // If there is no space after the word, select to the end of the string + if (wordEnd === -1) { + selectionEnd = text.length; + } else { + selectionEnd += wordEnd; + } + selectionStart = wordStart; - // If the user hasn't selected anything, let's select their current parenthesis block - if(! selectCurrentParenthesisBlock('<', '>')){ - selectCurrentParenthesisBlock('(', ')') + // Remove all punctuation at the end and beginning of the word + while (text[selectionStart].match(/[.,\/#!$%\^&\*;:{}=\-_`~()]/)) { + selectionStart++; + } + while (text[selectionEnd - 1].match(/[.,\/#!$%\^&\*;:{}=\-_`~()]/)) { + selectionEnd--; + } + target.setSelectionRange(selectionStart, selectionEnd); + return true; + } + + // If the user hasn't selected anything, let's select their current parenthesis block or word + if (!selectCurrentParenthesisBlock('<', '>') && !selectCurrentParenthesisBlock('(', ')')) { + selectCurrentWord(); } event.preventDefault(); From fbd34a68478caa528507fab830e3ac33a26fc6f4 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 20 Apr 2023 01:12:59 -0600 Subject: [PATCH 76/95] Use string.contains() instead of regex --- javascript/edit-attention.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 1c9f6737..01d37074 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,6 +46,7 @@ function keyupEditAttention(event){ function selectCurrentWord(){ if (selectionStart !== selectionEnd) return false; + const delimiters = ".,\/#!$%\^&\*;:{}=\-_`~()"; // Select the current word, find the start and end of the word (first space before and after) const wordStart = text.substring(0, selectionStart).lastIndexOf(" ") + 1; @@ -59,10 +60,10 @@ function keyupEditAttention(event){ selectionStart = wordStart; // Remove all punctuation at the end and beginning of the word - while (text[selectionStart].match(/[.,\/#!$%\^&\*;:{}=\-_`~()]/)) { + while (delimiters.includes(text[selectionStart])) { selectionStart++; } - while (text[selectionEnd - 1].match(/[.,\/#!$%\^&\*;:{}=\-_`~()]/)) { + while (delimiters.includes(text[selectionEnd - 1])) { selectionEnd--; } target.setSelectionRange(selectionStart, selectionEnd); From ee172c0fc13aeb3a8c1f4a8da63551dcb93fc5ec Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 20 Apr 2023 01:34:13 -0600 Subject: [PATCH 77/95] Simplify finding word boundaries This also makes it work with prompts without spaces between words --- javascript/edit-attention.js | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 01d37074..80d7c01c 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,26 +46,18 @@ function keyupEditAttention(event){ function selectCurrentWord(){ if (selectionStart !== selectionEnd) return false; - const delimiters = ".,\/#!$%\^&\*;:{}=\-_`~()"; + const delimiters = ".,\/#!$%\^&\*;:{}=\-_`~() "; - // Select the current word, find the start and end of the word (first space before and after) - const wordStart = text.substring(0, selectionStart).lastIndexOf(" ") + 1; - const wordEnd = text.substring(selectionEnd).indexOf(" "); - // If there is no space after the word, select to the end of the string - if (wordEnd === -1) { - selectionEnd = text.length; - } else { - selectionEnd += wordEnd; + // seek backward until to find beggining + while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { + selectionStart--; + } + + // seek forward to find end + while (!delimiters.includes(text[selectionEnd]) && selectionEnd < text.length) { + selectionEnd++; } - selectionStart = wordStart; - // Remove all punctuation at the end and beginning of the word - while (delimiters.includes(text[selectionStart])) { - selectionStart++; - } - while (delimiters.includes(text[selectionEnd - 1])) { - selectionEnd--; - } target.setSelectionRange(selectionStart, selectionEnd); return true; } From 7ef5551634f8d06301929cb43069f5bf37ee8f1a Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 20 Apr 2023 02:24:38 -0600 Subject: [PATCH 78/95] Update delimiters --- javascript/edit-attention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 80d7c01c..5fe1117c 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,7 +46,7 @@ function keyupEditAttention(event){ function selectCurrentWord(){ if (selectionStart !== selectionEnd) return false; - const delimiters = ".,\/#!$%\^&\*;:{}=\-_`~() "; + const delimiters = " .,\\/!?%^*;:{}=-_`~()\r\n\t"; // seek backward until to find beggining while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { From 27d02597c78a1b189c57feea12ba522d61a56c2e Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 21 Apr 2023 01:37:29 -0600 Subject: [PATCH 79/95] Remove parentheses if weight == 1 --- javascript/edit-attention.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 5fe1117c..c02d292f 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -99,7 +99,13 @@ function keyupEditAttention(event){ weight = parseFloat(weight.toPrecision(12)); if(String(weight).length == 1) weight += ".0" - text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + 1 + end - 1); + if (closeCharacter == ')' && weight == 1) { + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + 5); + selectionStart--; + selectionEnd--; + } else { + text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + 1 + end - 1); + } target.focus(); target.value = text; @@ -111,4 +117,4 @@ function keyupEditAttention(event){ addEventListener('keydown', (event) => { keyupEditAttention(event); -}); \ No newline at end of file +}); From c1fdba59045c2191433f9cdf82a1c86dc98e623b Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 21 Apr 2023 13:44:31 -0600 Subject: [PATCH 80/95] Remove hyphen, underscore delimiters --- javascript/edit-attention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index c02d292f..2307fd7a 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,7 +46,7 @@ function keyupEditAttention(event){ function selectCurrentWord(){ if (selectionStart !== selectionEnd) return false; - const delimiters = " .,\\/!?%^*;:{}=-_`~()\r\n\t"; + const delimiters = " .,\\/!?%^*;:{}=`~()\r\n\t"; // seek backward until to find beggining while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { From 05d7a63bbbe4a0369c842b75f99962aa8feaae3e Mon Sep 17 00:00:00 2001 From: arrix Date: Sun, 23 Apr 2023 12:44:12 +0800 Subject: [PATCH 81/95] fix: couldn't remove interrogate_tmp dir --- modules/interrogate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/interrogate.py b/modules/interrogate.py index cbb80683..0389cdec 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -41,7 +41,7 @@ def download_default_clip_interrogate_categories(content_dir): errors.display(e, "downloading default CLIP interrogate categories") finally: if os.path.exists(tmpdir): - os.remove(tmpdir) + os.removedirs(tmpdir) class InterrogateModels: From 8ae8aeca75debdf83d18416d16869b04fb320451 Mon Sep 17 00:00:00 2001 From: bbonvi <6573230@gmail.com> Date: Mon, 24 Apr 2023 13:12:49 +0600 Subject: [PATCH 82/95] pull progress for 40 seconds for some extreme network conditions 20 seconds may not be enough --- javascript/progressbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/progressbar.js b/javascript/progressbar.js index eb44aab9..8df3f569 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -138,7 +138,7 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre return } - if(elapsedFromStart > 20 && !res.queued && !res.active){ + if(elapsedFromStart > 40 && !res.queued && !res.active){ removeProgressBar() return } From bbc7a778d8a256e57c310bf27c6104a84beb5c78 Mon Sep 17 00:00:00 2001 From: dennissheng Date: Mon, 24 Apr 2023 17:36:16 +0800 Subject: [PATCH 83/95] fix ui img2img scripts --- modules/img2img.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/img2img.py b/modules/img2img.py index 953ac5d2..d22d9a49 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -151,7 +151,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s override_settings=override_settings, ) - p.scripts = modules.scripts.scripts_txt2img + p.scripts = modules.scripts.scripts_img2img p.script_args = args if shared.cmd_opts.enable_console_prompts: From 84c5b0801a25bbc8ae10b9151feb852ba48b0a73 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Mon, 24 Apr 2023 20:07:24 -0600 Subject: [PATCH 84/95] Update postprocessing_upscale.py --- scripts/postprocessing_upscale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index e60208ac..ef1186ac 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -27,7 +27,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): with FormRow(): with gr.Column(elem_id="upscaling_column_size", scale=4): upscaling_resize_w = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") - upscaling_resize_h = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_w") + upscaling_resize_h = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h") with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"): upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn") upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") From 0e071ae504a8219934b2f35a1d88dcb1f628d7c3 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 21 Apr 2023 20:19:58 -0600 Subject: [PATCH 85/95] Custom delimiters --- javascript/edit-attention.js | 2 +- modules/shared.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 2307fd7a..588c7b77 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,7 +46,7 @@ function keyupEditAttention(event){ function selectCurrentWord(){ if (selectionStart !== selectionEnd) return false; - const delimiters = " .,\\/!?%^*;:{}=`~()\r\n\t"; + const delimiters = opts.keyedit_delimiters + " \r\n\t"; // seek backward until to find beggining while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { diff --git a/modules/shared.py b/modules/shared.py index 5fd0eecb..3cc2f8ba 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -382,6 +382,7 @@ options_templates.update(options_section(('ui', "User interface"), { "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row"), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), + "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"), "hidden_tabs": OptionInfo([], "Hidden UI tabs (requires restart)", ui_components.DropdownMulti, lambda: {"choices": [x for x in tab_names]}), "ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"), From bb426de1cd1380844eb048f242bca6aa254edf58 Mon Sep 17 00:00:00 2001 From: darnell8 Date: Tue, 25 Apr 2023 22:53:06 +0800 Subject: [PATCH 86/95] Fix CLIP FileExistsError --- modules/interrogate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/interrogate.py b/modules/interrogate.py index cbb80683..ecdb11bb 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -32,7 +32,8 @@ def download_default_clip_interrogate_categories(content_dir): category_types = ["artists", "flavors", "mediums", "movements"] try: - os.makedirs(tmpdir) + if not os.path.exists(tmpdir): + os.makedirs(tmpdir) for category_type in category_types: torch.hub.download_url_to_file(f"https://raw.githubusercontent.com/pharmapsychotic/clip-interrogator/main/clip_interrogator/data/{category_type}.txt", os.path.join(tmpdir, f"{category_type}.txt")) os.rename(tmpdir, content_dir) @@ -41,7 +42,7 @@ def download_default_clip_interrogate_categories(content_dir): errors.display(e, "downloading default CLIP interrogate categories") finally: if os.path.exists(tmpdir): - os.remove(tmpdir) + os.removedirs(tmpdir) class InterrogateModels: From 7ea5be3e29dd79e2a7669e0b02f8ce66354c5416 Mon Sep 17 00:00:00 2001 From: aniaan Date: Wed, 26 Apr 2023 20:59:55 +0800 Subject: [PATCH 87/95] perf(webui): Remove duplicate code --- webui.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/webui.py b/webui.py index b570895f..bde4025b 100644 --- a/webui.py +++ b/webui.py @@ -126,9 +126,6 @@ def initialize(): modules.scripts.load_scripts() startup_timer.record("load scripts") - modelloader.load_upscalers() - startup_timer.record("load upscalers") - modules.sd_vae.refresh_vae_list() startup_timer.record("refresh VAE") From 101a18fc8466577501b57eac6a4b5d07351c9ec6 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 09:17:35 +0300 Subject: [PATCH 88/95] bump gradio to 3.27 --- modules/api/api.py | 12 +++--------- modules/postprocessing.py | 9 +++++++-- modules/ui.py | 2 +- modules/ui_common.py | 2 +- requirements.txt | 2 +- requirements_versions.txt | 2 +- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 518b2a61..5ed670e9 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -6,7 +6,6 @@ import uvicorn import gradio as gr from threading import Lock from io import BytesIO -from gradio.processing_utils import decode_base64_to_file from fastapi import APIRouter, Depends, FastAPI, Request, Response from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.exceptions import HTTPException @@ -395,16 +394,11 @@ class Api: def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): reqDict = setUpscalers(req) - def prepareFiles(file): - file = decode_base64_to_file(file.data, file_path=file.name) - file.orig_name = file.name - return file - - reqDict['image_folder'] = list(map(prepareFiles, reqDict['imageList'])) - reqDict.pop('imageList') + image_list = reqDict.pop('imageList', []) + image_folder = [decode_base64_to_image(x.data) for x in image_list] with self.queue_lock: - result = postprocessing.run_extras(extras_mode=1, image="", input_dir="", output_dir="", save_output=False, **reqDict) + result = postprocessing.run_extras(extras_mode=1, image_folder=image_folder, image="", input_dir="", output_dir="", save_output=False, **reqDict) return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) diff --git a/modules/postprocessing.py b/modules/postprocessing.py index 09d8e605..ba5745b9 100644 --- a/modules/postprocessing.py +++ b/modules/postprocessing.py @@ -18,9 +18,14 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, if extras_mode == 1: for img in image_folder: - image = Image.open(img) + if isinstance(img, Image.Image): + fn = '' + else: + image = Image.open(img) + fn = os.path.splitext(img.orig_name)[0] + image_data.append(image) - image_names.append(os.path.splitext(img.orig_name)[0]) + image_names.append(fn) 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' diff --git a/modules/ui.py b/modules/ui.py index 627fbe0b..dd28bdbb 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1204,7 +1204,7 @@ def create_ui(): with gr.Column(elem_id='ti_gallery_container'): ti_output = gr.Text(elem_id="ti_output", value="", show_label=False) - ti_gallery = gr.Gallery(label='Output', show_label=False, elem_id='ti_gallery').style(grid=4) + ti_gallery = gr.Gallery(label='Output', show_label=False, elem_id='ti_gallery').style(columns=4) ti_progress = gr.HTML(elem_id="ti_progress", value="") ti_outcome = gr.HTML(elem_id="ti_error", value="") diff --git a/modules/ui_common.py b/modules/ui_common.py index 3b11dcc8..27ab3ebb 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -125,7 +125,7 @@ Requested path was: {f} with gr.Column(variant='panel', elem_id=f"{tabname}_results"): with gr.Group(elem_id=f"{tabname}_gallery_container"): - result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery").style(grid=4) + result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery").style(columns=4) generation_info = None with gr.Column(): diff --git a/requirements.txt b/requirements.txt index c72b2927..77954b8d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ basicsr fonts font-roboto gfpgan -gradio==3.23 +gradio==3.27 invisible-watermark numpy omegaconf diff --git a/requirements_versions.txt b/requirements_versions.txt index df65431a..0a62c6de 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -3,7 +3,7 @@ transformers==4.25.1 accelerate==0.12.0 basicsr==1.4.2 gfpgan==1.3.8 -gradio==3.23 +gradio==3.27 numpy==1.23.3 Pillow==9.4.0 realesrgan==0.3.0 From 5a666f3904c39bb3d7d86818b2b6579325a8fa8a Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 09:17:35 +0300 Subject: [PATCH 89/95] bump gradio to 3.27 --- modules/postprocessing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/postprocessing.py b/modules/postprocessing.py index ba5745b9..ff055aae 100644 --- a/modules/postprocessing.py +++ b/modules/postprocessing.py @@ -19,6 +19,7 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, if extras_mode == 1: for img in image_folder: if isinstance(img, Image.Image): + image = img fn = '' else: image = Image.open(img) From 1514add5597d3dde0360110d158da7772b054cad Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 09:42:42 +0300 Subject: [PATCH 90/95] remove unneded imports and type signature --- modules/postprocessing.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/postprocessing.py b/modules/postprocessing.py index 9cb80957..4dc1a2ab 100644 --- a/modules/postprocessing.py +++ b/modules/postprocessing.py @@ -1,6 +1,4 @@ import os -import tempfile -from typing import List from PIL import Image @@ -8,7 +6,7 @@ from modules import shared, images, devices, scripts, scripts_postprocessing, ui from modules.shared import opts -def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemporaryFile], input_dir, output_dir, show_extras_results, *args, save_output: bool = True): +def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, show_extras_results, *args, save_output: bool = True): devices.torch_gc() shared.state.begin() From 642d96dcc83a66547899896c410bc27a34924c3f Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 10:04:01 +0300 Subject: [PATCH 91/95] use exist_ok=True instead of checking if directory exists --- modules/interrogate.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/interrogate.py b/modules/interrogate.py index ecdb11bb..e1665708 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -32,8 +32,7 @@ def download_default_clip_interrogate_categories(content_dir): category_types = ["artists", "flavors", "mediums", "movements"] try: - if not os.path.exists(tmpdir): - os.makedirs(tmpdir) + os.makedirs(tmpdir, exist_ok=True) for category_type in category_types: torch.hub.download_url_to_file(f"https://raw.githubusercontent.com/pharmapsychotic/clip-interrogator/main/clip_interrogator/data/{category_type}.txt", os.path.join(tmpdir, f"{category_type}.txt")) os.rename(tmpdir, content_dir) From 86bafb625ae2671722abe1f95a4c0fc5e38381e3 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 10:21:01 +0300 Subject: [PATCH 92/95] put asyncio fix into a function to make it more obvious where it starts and ends --- webui.py | 71 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/webui.py b/webui.py index fa8e21ae..0e2a3df0 100644 --- a/webui.py +++ b/webui.py @@ -5,7 +5,6 @@ import importlib import signal import re import warnings -import asyncio from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware @@ -67,45 +66,45 @@ if cmd_opts.server_name: else: server_name = "0.0.0.0" if cmd_opts.listen else None -if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"): - # "Any thread" and "selector" should be orthogonal, but there's not a clean - # interface for composing policies so pick the right base. - _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy # type: ignore -else: - _BasePolicy = asyncio.DefaultEventLoopPolicy - - -class AnyThreadEventLoopPolicy(_BasePolicy): # type: ignore - """Event loop policy that allows loop creation on any thread. - - The default `asyncio` event loop policy only automatically creates - event loops in the main threads. Other threads must create event - loops explicitly or `asyncio.get_event_loop` (and therefore - `.IOLoop.current`) will fail. Installing this policy allows event - loops to be created automatically on any thread, matching the - behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2). - - Usage:: - - asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) - - .. versionadded:: 5.0 +def fix_asyncio_event_loop_policy(): + """ + The default `asyncio` event loop policy only automatically creates + event loops in the main threads. Other threads must create event + loops explicitly or `asyncio.get_event_loop` (and therefore + `.IOLoop.current`) will fail. Installing this policy allows event + loops to be created automatically on any thread, matching the + behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2). """ - def get_event_loop(self) -> asyncio.AbstractEventLoop: - try: - return super().get_event_loop() - except (RuntimeError, AssertionError): - # This was an AssertionError in python 3.4.2 (which ships with debian jessie) - # and changed to a RuntimeError in 3.4.3. - # "There is no current event loop in thread %r" - loop = self.new_event_loop() - self.set_event_loop(loop) - return loop + import asyncio + if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"): + # "Any thread" and "selector" should be orthogonal, but there's not a clean + # interface for composing policies so pick the right base. + _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy # type: ignore + else: + _BasePolicy = asyncio.DefaultEventLoopPolicy -asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) + class AnyThreadEventLoopPolicy(_BasePolicy): # type: ignore + """Event loop policy that allows loop creation on any thread. + Usage:: + + asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) + """ + + def get_event_loop(self) -> asyncio.AbstractEventLoop: + try: + return super().get_event_loop() + except (RuntimeError, AssertionError): + # This was an AssertionError in python 3.4.2 (which ships with debian jessie) + # and changed to a RuntimeError in 3.4.3. + # "There is no current event loop in thread %r" + loop = self.new_event_loop() + self.set_event_loop(loop) + return loop + + asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) def check_versions(): @@ -140,6 +139,8 @@ Use --skip-version-check commandline argument to disable this check. def initialize(): + fix_asyncio_event_loop_policy() + check_versions() extensions.list_extensions() From 5fe0dd79beaa5ef737ff85254ee9870f60ae9464 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 11:29:37 +0300 Subject: [PATCH 93/95] rename CPU RNG to RNG source in settings, add infotext and parameters copypaste support to RNG source --- modules/devices.py | 4 ++-- modules/generation_parameters_copypaste.py | 5 +++++ modules/processing.py | 3 ++- modules/sd_samplers_common.py | 3 ++- modules/sd_samplers_kdiffusion.py | 2 +- modules/shared.py | 2 +- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/devices.py b/modules/devices.py index 3bc86a6a..c705a3cb 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -95,7 +95,7 @@ def randn(seed, shape): from modules.shared import opts torch.manual_seed(seed) - if opts.use_cpu_randn or device.type == 'mps': + if opts.randn_source == "CPU" or device.type == 'mps': return torch.randn(shape, device=cpu).to(device) return torch.randn(shape, device=device) @@ -103,7 +103,7 @@ def randn(seed, shape): def randn_without_seed(shape): from modules.shared import opts - if opts.use_cpu_randn or device.type == 'mps': + if opts.randn_source == "CPU" or device.type == 'mps': return torch.randn(shape, device=cpu).to(device) return torch.randn(shape, device=device) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 6df76858..e7269363 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -284,6 +284,10 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model restore_old_hires_fix_params(res) + # Missing RNG means the default was set, which is GPU RNG + if "RNG" not in res: + res["RNG"] = "GPU" + return res @@ -304,6 +308,7 @@ infotext_to_setting_name_mapping = [ ('UniPC skip type', 'uni_pc_skip_type'), ('UniPC order', 'uni_pc_order'), ('UniPC lower order final', 'uni_pc_lower_order_final'), + ('RNG', 'randn_source'), ] diff --git a/modules/processing.py b/modules/processing.py index 5556afc5..7bac154d 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -477,7 +477,8 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter "Conditional mask weight": getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None, "Clip skip": None if clip_skip <= 1 else clip_skip, "ENSD": None if opts.eta_noise_seed_delta == 0 else opts.eta_noise_seed_delta, - "Init image hash": getattr(p, 'init_img_hash', None) + "Init image hash": getattr(p, 'init_img_hash', None), + "RNG": (opts.randn_source if opts.randn_source != "GPU" else None) } generation_params.update(p.extra_generation_params) diff --git a/modules/sd_samplers_common.py b/modules/sd_samplers_common.py index e6a372d5..bc074238 100644 --- a/modules/sd_samplers_common.py +++ b/modules/sd_samplers_common.py @@ -61,7 +61,8 @@ def store_latent(decoded): class InterruptedException(BaseException): pass -if opts.use_cpu_randn: + +if opts.randn_source == "CPU": import torchsde._brownian.brownian_interval def torchsde_randn(size, dtype, device, seed): diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 13f4567a..a547d1b5 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -190,7 +190,7 @@ class TorchHijack: if noise.shape == x.shape: return noise - if opts.use_cpu_randn or x.device.type == 'mps': + if opts.randn_source == "CPU" or x.device.type == 'mps': return torch.randn_like(x, device=devices.cpu).to(x.device) else: return torch.randn_like(x) diff --git a/modules/shared.py b/modules/shared.py index b5b401fe..73704889 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -334,7 +334,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "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": 12, "step": 1}), "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"), - "use_cpu_randn": OptionInfo(False, "Use CPU for random number generation to make manual seeds generate the same image across platforms. This may change existing seeds."), + "randn_source": OptionInfo("GPU", "Random number generator source. Changes seeds drastically. Use CPU to produce the same picture across different vidocard vendors.", gr.Radio, {"choices": ["GPU", "CPU"]}), })) options_templates.update(options_section(('compatibility', "Compatibility"), { From ee71eee1818f6f6eba9895c93ba25e0cad27e069 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 12:36:50 +0300 Subject: [PATCH 94/95] stuff related to torch version change --- launch.py | 8 ++++---- modules/safe.py | 5 +---- requirements_versions.txt | 2 +- webui.py | 6 ++++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/launch.py b/launch.py index 4256ef25..af1c8309 100644 --- a/launch.py +++ b/launch.py @@ -121,12 +121,12 @@ def run_python(code, desc=None, errdesc=None): return run(f'"{python}" -c "{code}"', desc, errdesc) -def run_pip(args, desc=None): +def run_pip(args, desc=None, live=False): if skip_install: return index_url_line = f' --index-url {index_url}' if index_url != '' else '' - return run(f'"{python}" -m pip {args} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}") + return run(f'"{python}" -m pip {args} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live) def check_run_python(code): @@ -225,7 +225,7 @@ def run_extensions_installers(settings_file): def prepare_environment(): global skip_install - torch_command = os.environ.get('TORCH_COMMAND', "pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118") + torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==2.0.0 torchvision==0.15.1 --index-url https://download.pytorch.org/whl/cu118") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17') @@ -271,7 +271,7 @@ def prepare_environment(): if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers: if platform.system() == "Windows": if platform.python_version().startswith("3.10"): - run_pip(f"install -U -I --no-deps {xformers_package}", "xformers") + run_pip(f"install -U -I --no-deps {xformers_package}", "xformers", live=True) else: print("Installation of xformers is not supported in this version of Python.") print("You can also check this and build manually: https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers#building-xformers-on-windows-by-duckness") diff --git a/modules/safe.py b/modules/safe.py index 82d44be3..dadf319c 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -1,6 +1,5 @@ # this code is adapted from the script contributed by anon from /h/ -import io import pickle import collections import sys @@ -12,11 +11,9 @@ import _codecs import zipfile import re - # PyTorch 1.13 and later have _TypedStorage renamed to TypedStorage TypedStorage = torch.storage.TypedStorage if hasattr(torch.storage, 'TypedStorage') else torch.storage._TypedStorage - def encode(*args): out = _codecs.encode(*args) return out @@ -27,7 +24,7 @@ class RestrictedUnpickler(pickle.Unpickler): def persistent_load(self, saved_id): assert saved_id[0] == 'storage' - return TypedStorage() + return TypedStorage(_internal=True) def find_class(self, module, name): if self.extra_handler is not None: diff --git a/requirements_versions.txt b/requirements_versions.txt index 52bd9b4d..94d32d3d 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -25,6 +25,6 @@ lark==1.1.2 inflection==0.5.1 GitPython==3.1.30 torchsde==0.2.5 -safetensors==0.3.0 +safetensors==0.3.1 httpcore<=0.15 fastapi==0.94.0 diff --git a/webui.py b/webui.py index 1f97b475..3fd6e1e9 100644 --- a/webui.py +++ b/webui.py @@ -21,6 +21,8 @@ import torch import pytorch_lightning # pytorch_lightning should be imported after torch, but it re-enables warnings on import so import once to disable them warnings.filterwarnings(action="ignore", category=DeprecationWarning, module="pytorch_lightning") warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvision") +warnings.filterwarnings(action='ignore', category=UserWarning, message='TypedStorage is deprecated') + startup_timer.record("import torch") @@ -113,7 +115,7 @@ def check_versions(): if shared.cmd_opts.skip_version_check: return - expected_torch_version = "1.13.1" + expected_torch_version = "2.0.0" if version.parse(torch.__version__) < version.parse(expected_torch_version): errors.print_error_explanation(f""" @@ -126,7 +128,7 @@ there are reports of issues with training tab on the latest version. Use --skip-version-check commandline argument to disable this check. """.strip()) - expected_xformers_version = "0.0.16rc425" + expected_xformers_version = "0.0.17" if shared.xformers_available: import xformers From aee6d9bb74d9868d0782acc54b6cbefa32422a8f Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 29 Apr 2023 12:39:05 +0300 Subject: [PATCH 95/95] remove unneeded warning filter --- webui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/webui.py b/webui.py index 3fd6e1e9..759125e0 100644 --- a/webui.py +++ b/webui.py @@ -21,7 +21,6 @@ import torch import pytorch_lightning # pytorch_lightning should be imported after torch, but it re-enables warnings on import so import once to disable them warnings.filterwarnings(action="ignore", category=DeprecationWarning, module="pytorch_lightning") warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvision") -warnings.filterwarnings(action='ignore', category=UserWarning, message='TypedStorage is deprecated') startup_timer.record("import torch")