From a320d157ec0221fa4e9c756327e31d881b9921ae Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 13 Feb 2023 20:26:47 -0500 Subject: [PATCH 01/41] all hiding of ui tabs --- modules/shared.py | 1 + modules/ui.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/modules/shared.py b/modules/shared.py index 79fbf7249..ded289252 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -455,6 +455,7 @@ options_templates.update(options_section(('ui', "User interface"), { "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}), "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"), + "hidden_tabs": OptionInfo("", "Hidden UI tabs"), "ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"), "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order"), "localization": OptionInfo("None", "Localization (requires restart)", gr.Dropdown, lambda: {"choices": ["None"] + list(localization.localizations.keys())}, refresh=lambda: localization.list_localizations(cmd_opts.localizations_dir)), diff --git a/modules/ui.py b/modules/ui.py index f5df1ffeb..c99e55aba 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1568,7 +1568,10 @@ def create_ui(): parameters_copypaste.connect_paste_params_buttons() with gr.Tabs(elem_id="tabs") as tabs: + hidden_tabs = [x.lower().strip() for x in shared.opts.hidden_tabs.split(",")] for interface, label, ifid in interfaces: + if label.lower() in hidden_tabs: + continue with gr.TabItem(label, id=ifid, elem_id='tab_' + ifid): interface.render() From 83829471decbde64d335eb510d4a5670baf68773 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sun, 19 Feb 2023 09:21:44 -0500 Subject: [PATCH 02/41] make ui as multiselect instead of string list --- modules/shared.py | 3 ++- modules/ui.py | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/shared.py b/modules/shared.py index 1a1abeb25..a7c5f58e1 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -305,6 +305,7 @@ def list_samplers(): hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config} +tab_names = [] options_templates = {} @@ -460,7 +461,7 @@ options_templates.update(options_section(('ui', "User interface"), { "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}), "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"), - "hidden_tabs": OptionInfo("", "Hidden UI tabs"), + "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"), "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order"), "localization": OptionInfo("None", "Localization (requires restart)", gr.Dropdown, lambda: {"choices": ["None"] + list(localization.localizations.keys())}, refresh=lambda: localization.list_localizations(cmd_opts.localizations_dir)), diff --git a/modules/ui.py b/modules/ui.py index a4ecd41b2..5ac249b2b 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1563,6 +1563,10 @@ def create_ui(): extensions_interface = ui_extensions.create_ui() interfaces += [(extensions_interface, "Extensions", "extensions")] + shared.tab_names = [] + 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.Row(elem_id="quicksettings", variant="compact"): for i, k, item in sorted(quicksettings_list, key=lambda x: quicksettings_names.get(x[1], x[0])): @@ -1572,9 +1576,8 @@ def create_ui(): parameters_copypaste.connect_paste_params_buttons() with gr.Tabs(elem_id="tabs") as tabs: - hidden_tabs = [x.lower().strip() for x in shared.opts.hidden_tabs.split(",")] for interface, label, ifid in interfaces: - if label.lower() in hidden_tabs: + if label in shared.opts.hidden_tabs: continue with gr.TabItem(label, id=ifid, elem_id='tab_' + ifid): interface.render() From a2d635ad135241a0a40f67f7e1638c9c8a4ded04 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Wed, 22 Feb 2023 01:52:53 -0800 Subject: [PATCH 03/41] Add before_process_batch script callback --- modules/processing.py | 3 +++ modules/scripts.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/modules/processing.py b/modules/processing.py index 2009d3bf8..187e98fde 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -597,6 +597,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: seeds = p.all_seeds[n * p.batch_size:(n + 1) * p.batch_size] subseeds = p.all_subseeds[n * p.batch_size:(n + 1) * p.batch_size] + if p.scripts is not None: + p.scripts.before_process_batch(p, batch_number=n, prompts=prompts, seeds=seeds, subseeds=subseeds) + if len(prompts) == 0: break diff --git a/modules/scripts.py b/modules/scripts.py index 24056a12f..e6a505b39 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -80,6 +80,20 @@ class Script: pass + def before_process_batch(self, p, *args, **kwargs): + """ + Called before extra networks are parsed from the prompt, so you can add + new extra network keywords to the prompt with this callback. + + **kwargs will have those items: + - batch_number - index of current batch, from 0 to number of batches-1 + - prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things + - seeds - list of seeds for current batch + - subseeds - list of subseeds for current batch + """ + + pass + def process_batch(self, p, *args, **kwargs): """ Same as process(), but called for every batch. @@ -388,6 +402,15 @@ class ScriptRunner: print(f"Error running process: {script.filename}", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) + def before_process_batch(self, p, **kwargs): + for script in self.alwayson_scripts: + try: + script_args = p.script_args[script.args_from:script.args_to] + script.before_process_batch(p, *script_args, **kwargs) + except Exception: + print(f"Error running before_process_batch: {script.filename}", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + def process_batch(self, p, **kwargs): for script in self.alwayson_scripts: try: From 2c58d373dd408153dc126f6eba1525d32fbf92bb Mon Sep 17 00:00:00 2001 From: 112292454 <92578848+112292454@users.noreply.github.com> Date: Wed, 22 Feb 2023 21:40:42 +0800 Subject: [PATCH 04/41] Update prompt_matrix.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this file last commit fixed common situation when using both prompts matrix and high-res。 but if we just open matrix option,but not use ‘|’,we will only get one pic,and `processed.images[0].width, processed.images[1].height` will cause a index out of bounds exception --- scripts/prompt_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prompt_matrix.py b/scripts/prompt_matrix.py index b1c486d44..7790ac38f 100644 --- a/scripts/prompt_matrix.py +++ b/scripts/prompt_matrix.py @@ -100,7 +100,7 @@ class Script(scripts.Script): processed = process_images(p) grid = images.image_grid(processed.images, p.batch_size, rows=1 << ((len(prompt_matrix_parts) - 1) // 2)) - grid = images.draw_prompt_matrix(grid, processed.images[0].width, processed.images[1].height, prompt_matrix_parts, margin_size) + grid = images.draw_prompt_matrix(grid, p.hr_upscale_to_x, p.hr_upscale_to_y,, prompt_matrix_parts, margin_size) processed.images.insert(0, grid) processed.index_of_first_image = 1 processed.infotexts.insert(0, processed.infotexts[0]) From 2fa91cbee65429e611861df1c32657c941f4acaf Mon Sep 17 00:00:00 2001 From: 112292454 <92578848+112292454@users.noreply.github.com> Date: Thu, 23 Feb 2023 01:55:07 +0800 Subject: [PATCH 05/41] Update prompt_matrix.py 1 --- scripts/prompt_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prompt_matrix.py b/scripts/prompt_matrix.py index 7790ac38f..e9b115170 100644 --- a/scripts/prompt_matrix.py +++ b/scripts/prompt_matrix.py @@ -100,7 +100,7 @@ class Script(scripts.Script): processed = process_images(p) grid = images.image_grid(processed.images, p.batch_size, rows=1 << ((len(prompt_matrix_parts) - 1) // 2)) - grid = images.draw_prompt_matrix(grid, p.hr_upscale_to_x, p.hr_upscale_to_y,, prompt_matrix_parts, margin_size) + grid = images.draw_prompt_matrix(grid, processed.images[0].width, processed.images[0].height, prompt_matrix_parts, margin_size) processed.images.insert(0, grid) processed.index_of_first_image = 1 processed.infotexts.insert(0, processed.infotexts[0]) From 6825de7bc811d777ff0d462e5668fa4fba73a889 Mon Sep 17 00:00:00 2001 From: Thomas Young <35073576+DrakeRichards@users.noreply.github.com> Date: Wed, 22 Feb 2023 15:31:49 -0600 Subject: [PATCH 06/41] Added results selector This causes the querySelectorAll function to only select images in a results div, ignoring images that might be in an extension's gallery. --- javascript/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/notification.js b/javascript/notification.js index 040a3afac..5ae6df24d 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -15,7 +15,7 @@ onUiUpdate(function(){ } } - const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] img.h-full.w-full.overflow-hidden'); + const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] img.h-full.w-full.overflow-hidden'); if (galleryPreviews == null) return; From b90cad7f3136bbe04efeee2a00e95d0cc6ce1a4a Mon Sep 17 00:00:00 2001 From: "fkunn1326@users.noreply.github.com" Date: Thu, 23 Feb 2023 03:29:22 +0000 Subject: [PATCH 07/41] Add .mjs support for extensions --- modules/ui.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ui.py b/modules/ui.py index 0516c6436..2509ce2d0 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1754,6 +1754,9 @@ def reload_javascript(): for script in modules.scripts.list_scripts("javascript", ".js"): head += f'\n' + for script in modules.scripts.list_scripts("javascript", ".mjs"): + head += f'\n' + head += f'\n' def template_response(*args, **kwargs): From ac4c7f05cd38dfa99cf64f7ddb9b1656e70a13c5 Mon Sep 17 00:00:00 2001 From: Tpinion Date: Fri, 24 Feb 2023 00:42:29 +0800 Subject: [PATCH 08/41] Filter out temporary files that will be generated if the download fails. --- modules/codeformer_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/codeformer_model.py b/modules/codeformer_model.py index 01fb7bd84..8d84bbc90 100644 --- a/modules/codeformer_model.py +++ b/modules/codeformer_model.py @@ -55,7 +55,7 @@ def setup_model(dirname): if self.net is not None and self.face_helper is not None: self.net.to(devices.device_codeformer) return self.net, self.face_helper - model_paths = modelloader.load_models(model_path, model_url, self.cmd_dir, download_name='codeformer-v0.1.0.pth') + model_paths = modelloader.load_models(model_path, model_url, self.cmd_dir, download_name='codeformer-v0.1.0.pth', ext_filter=['.pth']) if len(model_paths) != 0: ckpt_path = model_paths[0] else: From 327186b484c344598522a989a4b4859d6b90fb04 Mon Sep 17 00:00:00 2001 From: laksjdjf Date: Fri, 24 Feb 2023 14:03:46 +0900 Subject: [PATCH 09/41] Update script_callbacks.py --- modules/script_callbacks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index edd0e2a72..c5bb3e71e 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -29,7 +29,7 @@ class ImageSaveParams: class CFGDenoiserParams: - def __init__(self, x, image_cond, sigma, sampling_step, total_sampling_steps): + def __init__(self, x, image_cond, sigma, sampling_step, total_sampling_steps, tensor, uncond): self.x = x """Latent image representation in the process of being denoised""" @@ -44,6 +44,12 @@ class CFGDenoiserParams: self.total_sampling_steps = total_sampling_steps """Total number of sampling steps planned""" + + self.tensor = tensor + """ Encoder hidden states of condtioning""" + + self.uncond = uncond + """ Encoder hidden states of unconditioning""" class CFGDenoisedParams: From 9a1435946ce7cc7f2cdaa0a312e1c0d296b8c276 Mon Sep 17 00:00:00 2001 From: laksjdjf Date: Fri, 24 Feb 2023 14:04:23 +0900 Subject: [PATCH 10/41] Update sd_samplers_kdiffusion.py --- modules/sd_samplers_kdiffusion.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 528f513fe..ea974be04 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -101,11 +101,13 @@ class CFGDenoiser(torch.nn.Module): 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)]) - denoiser_params = CFGDenoiserParams(x_in, image_cond_in, sigma_in, state.sampling_step, state.sampling_steps) + denoiser_params = CFGDenoiserParams(x_in, image_cond_in, sigma_in, state.sampling_step, state.sampling_steps, tensor, uncond) cfg_denoiser_callback(denoiser_params) x_in = denoiser_params.x image_cond_in = denoiser_params.image_cond sigma_in = denoiser_params.sigma + tensor = denoiser_params.tensor + uncond = denoiser_params.uncond if tensor.shape[1] == uncond.shape[1]: if not is_edit_model: From 534cf60afb547de72891e1e87b59a1433aadeee3 Mon Sep 17 00:00:00 2001 From: laksjdjf Date: Fri, 24 Feb 2023 14:26:55 +0900 Subject: [PATCH 11/41] Update script_callbacks.py --- modules/script_callbacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index c5bb3e71e..d17031355 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -46,7 +46,7 @@ class CFGDenoiserParams: """Total number of sampling steps planned""" self.tensor = tensor - """ Encoder hidden states of condtioning""" + """ Encoder hidden states of conditioning""" self.uncond = uncond """ Encoder hidden states of unconditioning""" From b15bc73c99e6fbbeffdbdbeab39ba30276021d4b Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Fri, 24 Feb 2023 14:22:58 -0500 Subject: [PATCH 12/41] sort upscalers by name --- modules/modelloader.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/modelloader.py b/modules/modelloader.py index fc3f6249f..a7ac338c2 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -6,7 +6,7 @@ from urllib.parse import urlparse from basicsr.utils.download_util import load_file_from_url from modules import shared -from modules.upscaler import Upscaler +from modules.upscaler import Upscaler, UpscalerNone from modules.paths import script_path, models_path @@ -169,4 +169,8 @@ def load_upscalers(): scaler = cls(commandline_options.get(cmd_name, None)) datas += scaler.scalers - shared.sd_upscalers = datas + shared.sd_upscalers = sorted( + datas, + # Special case for UpscalerNone keeps it at the beginning of the list. + key=lambda x: x.name if not isinstance(x.scaler, UpscalerNone) else "" + ) From aa108bd02a8282e8213fa6c5967e3c47e49bb43f Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 24 Feb 2023 20:57:18 -0700 Subject: [PATCH 13/41] Add lossless webp option --- modules/images.py | 2 +- modules/shared.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/images.py b/modules/images.py index 5b80c23e1..7df2b08c7 100644 --- a/modules/images.py +++ b/modules/images.py @@ -556,7 +556,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i elif image_to_save.mode == 'I;16': image_to_save = image_to_save.point(lambda p: p * 0.0038910505836576).convert("RGB" if extension.lower() == ".webp" else "L") - image_to_save.save(temp_file_path, format=image_format, quality=opts.jpeg_quality) + image_to_save.save(temp_file_path, format=image_format, quality=opts.jpeg_quality, lossless=opts.webp_lossless) if opts.enable_pnginfo and info is not None: exif_bytes = piexif.dump({ diff --git a/modules/shared.py b/modules/shared.py index 805f9cc19..511019880 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -327,6 +327,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "save_images_before_highres_fix": OptionInfo(False, "Save a copy of image before applying highres fix."), "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"), "jpeg_quality": OptionInfo(80, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}), + "webp_lossless": OptionInfo(False, "Use lossless compression for webp images"), "export_for_4chan": OptionInfo(True, "If the saved image file size is above the limit, or its either width or height are above the limit, save a downscaled copy as JPG"), "img_downscale_threshold": OptionInfo(4.0, "File size limit for the above option, MB", gr.Number), "target_side_length": OptionInfo(4000, "Width/height limit for the above option, in pixels", gr.Number), From ed43a822b2df601fd1d6d2a3b97ae5924c06ca98 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 25 Feb 2023 12:56:03 -0500 Subject: [PATCH 14/41] fix progressbar --- javascript/progressbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/progressbar.js b/javascript/progressbar.js index ff6d757ba..9ccc9da46 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -139,7 +139,7 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre var divProgress = document.createElement('div') divProgress.className='progressDiv' - divProgress.style.display = opts.show_progressbar ? "" : "none" + divProgress.style.display = opts.show_progressbar ? "block" : "none" var divInner = document.createElement('div') divInner.className='progress' From 1e30e4d9ebd9c36ccee43ec0e61c6ab490171614 Mon Sep 17 00:00:00 2001 From: Ju1-js <40339350+Ju1-js@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:55:12 -0800 Subject: [PATCH 15/41] Gradio auth logic fix - Handle empty/newlines When the massive one-liner was split into multiple lines, it lost the ability to handle newlines. This removes empty strings & newline characters from the logins. It also closes the file so it's more robust if the garbage collection function is ever changed. --- webui.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webui.py b/webui.py index 9e8b486aa..5e925fa77 100644 --- a/webui.py +++ b/webui.py @@ -209,11 +209,12 @@ def webui(): gradio_auth_creds = [] if cmd_opts.gradio_auth: - gradio_auth_creds += cmd_opts.gradio_auth.strip('"').replace('\n', '').split(',') + gradio_auth_creds += [x.strip() for x in cmd_opts.gradio_auth.strip('"').replace('/n', '').split(',') if x.strip()] if cmd_opts.gradio_auth_path: with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file: for line in file.readlines(): - gradio_auth_creds += [x.strip() for x in line.split(',')] + gradio_auth_creds += [x.strip() for x in line.split(',') if x.strip()] + file.close() app, local_url, share_url = shared.demo.launch( share=cmd_opts.share, From 7990ed92be7f34e609b441252ff97ae1504b0a3f Mon Sep 17 00:00:00 2001 From: Ju1-js <40339350+Ju1-js@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:05:47 -0800 Subject: [PATCH 16/41] Slash was facing the wrong way --- webui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui.py b/webui.py index 5e925fa77..d60c4e5d1 100644 --- a/webui.py +++ b/webui.py @@ -209,7 +209,7 @@ def webui(): gradio_auth_creds = [] if cmd_opts.gradio_auth: - gradio_auth_creds += [x.strip() for x in cmd_opts.gradio_auth.strip('"').replace('/n', '').split(',') if x.strip()] + gradio_auth_creds += [x.strip() for x in cmd_opts.gradio_auth.strip('"').replace('\n', '').split(',') if x.strip()] if cmd_opts.gradio_auth_path: with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file: for line in file.readlines(): From fc3063d9b924c094b59229269f4afe722b120d88 Mon Sep 17 00:00:00 2001 From: Ju1-js <40339350+Ju1-js@users.noreply.github.com> Date: Wed, 1 Mar 2023 18:25:23 -0800 Subject: [PATCH 17/41] Remove unnecessary line --- webui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/webui.py b/webui.py index d60c4e5d1..be39fa8dc 100644 --- a/webui.py +++ b/webui.py @@ -214,7 +214,6 @@ def webui(): with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file: for line in file.readlines(): gradio_auth_creds += [x.strip() for x in line.split(',') if x.strip()] - file.close() app, local_url, share_url = shared.demo.launch( share=cmd_opts.share, From 23d4fb5bf2400622d00ca5fe489fadb160ee7c47 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 3 Mar 2023 08:29:10 -0500 Subject: [PATCH 18/41] allow saving of images via api --- modules/api/api.py | 8 ++++---- modules/api/models.py | 4 ++-- modules/images.py | 3 +++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 5a9ac5f1a..6b939daa9 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -180,8 +180,8 @@ class Api: populate = txt2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(txt2imgreq.sampler_name or txt2imgreq.sampler_index), - "do_not_save_samples": True, - "do_not_save_grid": True + "do_not_save_samples": True if not 'do_not_save_samples' in vars(txt2imgreq) else txt2imgreq.do_not_save_samples, + "do_not_save_grid": True if not 'do_not_save_grid' in vars(txt2imgreq) else txt2imgreq.do_not_save_grid, } ) if populate.sampler_name: @@ -220,8 +220,8 @@ class Api: populate = img2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index), - "do_not_save_samples": True, - "do_not_save_grid": True, + "do_not_save_samples": True if not 'do_not_save_samples' in img2imgreq else img2imgreq.do_not_save_samples, + "do_not_save_grid": True if not 'do_not_save_grid' in img2imgreq else img2imgreq.do_not_save_grid, "mask": mask } ) diff --git a/modules/api/models.py b/modules/api/models.py index cba43d3b1..a947e6ac3 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -14,8 +14,8 @@ API_NOT_ALLOWED = [ "outpath_samples", "outpath_grids", "sampler_index", - "do_not_save_samples", - "do_not_save_grid", + # "do_not_save_samples", + # "do_not_save_grid", "extra_generation_params", "overlay_images", "do_not_reload_embeddings", diff --git a/modules/images.py b/modules/images.py index 5b80c23e1..f8e62b718 100644 --- a/modules/images.py +++ b/modules/images.py @@ -489,6 +489,9 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i """ namegen = FilenameGenerator(p, seed, prompt, image) + if path is None: # set default path to avoid errors when functions are triggered manually or via api and param is not set + path = opts.outdir_save + if save_to_dirs is None: save_to_dirs = (grid and opts.grid_save_to_dirs) or (not grid and opts.save_to_dirs and not no_prompt) From f8e219bad9f33cde94cd31fff3edd70946612541 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 3 Mar 2023 09:00:52 -0500 Subject: [PATCH 19/41] allow api requests to specify do not send images in response --- modules/api/api.py | 10 ++++++++-- modules/api/models.py | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 6b939daa9..7da9081b9 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -190,6 +190,9 @@ class Api: args = vars(populate) args.pop('script_name', None) + send_images = True if not 'do_not_send_images' in args else not args['do_not_send_images'] + args.pop('do_not_send_images', None) + with self.queue_lock: p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) @@ -203,7 +206,7 @@ class Api: processed = process_images(p) shared.state.end() - b64images = list(map(encode_pil_to_base64, processed.images)) + b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else [] return TextToImageResponse(images=b64images, parameters=vars(txt2imgreq), info=processed.js()) @@ -232,6 +235,9 @@ class Api: args.pop('include_init_images', None) # this is meant to be done by "exclude": True in model, but it's for a reason that I cannot determine. args.pop('script_name', None) + send_images = True if not 'do_not_send_images' in args else not args['do_not_send_images'] + args.pop('do_not_send_images', None) + with self.queue_lock: p = StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args) p.init_images = [decode_base64_to_image(x) for x in init_images] @@ -246,7 +252,7 @@ class Api: processed = process_images(p) shared.state.end() - b64images = list(map(encode_pil_to_base64, processed.images)) + b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else [] if not img2imgreq.include_init_images: img2imgreq.init_images = None diff --git a/modules/api/models.py b/modules/api/models.py index a947e6ac3..aa4ea5d5f 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -100,13 +100,27 @@ class PydanticModelGenerator: StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingTxt2Img", StableDiffusionProcessingTxt2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}] + [ + {"key": "sampler_index", "type": str, "default": "Euler"}, + {"key": "script_name", "type": str, "default": None}, + {"key": "script_args", "type": list, "default": []}, + {"key": "do_not_send_images", "type": bool, "default": False} + ] ).generate_model() StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingImg2Img", StableDiffusionProcessingImg2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}, {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}] + [ + {"key": "sampler_index", "type": str, "default": "Euler"}, + {"key": "init_images", "type": list, "default": None}, + {"key": "denoising_strength", "type": float, "default": 0.75}, + {"key": "mask", "type": str, "default": None}, + {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, + {"key": "script_name", "type": str, "default": None}, + {"key": "script_args", "type": list, "default": []}, + {"key": "do_not_send_images", "type": bool, "default": False} + ] ).generate_model() class TextToImageResponse(BaseModel): From c48bbccf12f13cf309f532d70494ff04c27bcc2a Mon Sep 17 00:00:00 2001 From: Yea chen Date: Sat, 4 Mar 2023 11:46:07 +0800 Subject: [PATCH 20/41] add: /sdapi/v1/scripts in API API for get scripts list --- modules/api/api.py | 13 +++++++++++++ modules/api/models.py | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/modules/api/api.py b/modules/api/api.py index 5a9ac5f1a..46cb7c811 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -150,6 +150,7 @@ class Api: self.add_api_route("/sdapi/v1/train/embedding", self.train_embedding, methods=["POST"], response_model=TrainResponse) self.add_api_route("/sdapi/v1/train/hypernetwork", self.train_hypernetwork, methods=["POST"], response_model=TrainResponse) 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) def add_api_route(self, path: str, endpoint, **kwargs): if shared.cmd_opts.api_auth: @@ -174,6 +175,18 @@ class Api: script_idx = script_name_to_index(script_name, script_runner.selectable_scripts) script = script_runner.selectable_scripts[script_idx] return script, script_idx + + def get_scripts_list(self): + t2ilist = [] + i2ilist = [] + + for a in scripts.scripts_txt2img.titles: + t2ilist.append(str(a.lower())) + + for b in scripts.scripts_img2img.titles: + i2ilist.append(str(b.lower())) + + return ScriptsList(txt2img = t2ilist, img2img = i2ilist) def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): script, script_idx = self.get_script(txt2imgreq.script_name, scripts.scripts_txt2img) diff --git a/modules/api/models.py b/modules/api/models.py index cba43d3b1..db739f2b4 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -267,3 +267,7 @@ class EmbeddingsResponse(BaseModel): class MemoryResponse(BaseModel): ram: dict = Field(title="RAM", description="System memory stats") cuda: dict = Field(title="CUDA", description="nVidia CUDA memory stats") + +class ScriptsList(BaseModel): + txt2img: list = Field(default=None,title="Txt2img", description="Titles of scripts (txt2img)") + img2img: list = Field(default=None,title="Img2img", description="Titles of scripts (img2img)") \ No newline at end of file From b012d70f15641d6b85c9257b83cec892e941609c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 4 Mar 2023 17:51:37 -0500 Subject: [PATCH 21/41] update using original defaults --- modules/api/api.py | 17 +++++++++++------ modules/api/models.py | 6 ++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 7da9081b9..a6bb439c7 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -180,8 +180,8 @@ class Api: populate = txt2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(txt2imgreq.sampler_name or txt2imgreq.sampler_index), - "do_not_save_samples": True if not 'do_not_save_samples' in vars(txt2imgreq) else txt2imgreq.do_not_save_samples, - "do_not_save_grid": True if not 'do_not_save_grid' in vars(txt2imgreq) else txt2imgreq.do_not_save_grid, + "do_not_save_samples": txt2imgreq.do_not_save, + "do_not_save_grid": txt2imgreq.do_not_save, } ) if populate.sampler_name: @@ -190,8 +190,9 @@ class Api: args = vars(populate) args.pop('script_name', None) - send_images = True if not 'do_not_send_images' in args else not args['do_not_send_images'] - args.pop('do_not_send_images', None) + send_images = True if not 'do_not_send' in args else not args['do_not_send'] + args.pop('do_not_send', None) + args.pop('do_not_save', None) with self.queue_lock: p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) @@ -223,8 +224,8 @@ class Api: populate = img2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index), - "do_not_save_samples": True if not 'do_not_save_samples' in img2imgreq else img2imgreq.do_not_save_samples, - "do_not_save_grid": True if not 'do_not_save_grid' in img2imgreq else img2imgreq.do_not_save_grid, + "do_not_save_samples": img2imgreq.do_not_save, + "do_not_save_grid": img2imgreq.do_not_save, "mask": mask } ) @@ -235,6 +236,10 @@ class Api: args.pop('include_init_images', None) # this is meant to be done by "exclude": True in model, but it's for a reason that I cannot determine. args.pop('script_name', None) + send_images = True if not 'do_not_send' in args else not args['do_not_send'] + args.pop('do_not_send', None) + args.pop('do_not_save', None) + send_images = True if not 'do_not_send_images' in args else not args['do_not_send_images'] args.pop('do_not_send_images', None) diff --git a/modules/api/models.py b/modules/api/models.py index aa4ea5d5f..2b66e1f03 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -104,7 +104,8 @@ StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( {"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, - {"key": "do_not_send_images", "type": bool, "default": False} + {"key": "do_not_send", "type": bool, "default": False}, + {"key": "do_not_save", "type": bool, "default": True} ] ).generate_model() @@ -119,7 +120,8 @@ StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, - {"key": "do_not_send_images", "type": bool, "default": False} + {"key": "do_not_send", "type": bool, "default": False}, + {"key": "do_not_save", "type": bool, "default": True} ] ).generate_model() From d118cb6ea3f1a410b5e030519dc021eafc1d6b52 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Mon, 6 Mar 2023 13:18:35 -0500 Subject: [PATCH 22/41] use lowercase name for sorting; keep `UpscalerLanczos` and `UpscalerNearest` at the start of the list with `UpscalerNone` Co-authored-by: catboxanon <122327233+catboxanon@users.noreply.github.com> --- modules/modelloader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/modelloader.py b/modules/modelloader.py index a7ac338c2..e351d808a 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -6,7 +6,7 @@ from urllib.parse import urlparse from basicsr.utils.download_util import load_file_from_url from modules import shared -from modules.upscaler import Upscaler, UpscalerNone +from modules.upscaler import Upscaler, UpscalerLanczos, UpscalerNearest, UpscalerNone from modules.paths import script_path, models_path @@ -172,5 +172,5 @@ def load_upscalers(): shared.sd_upscalers = sorted( datas, # Special case for UpscalerNone keeps it at the beginning of the list. - key=lambda x: x.name if not isinstance(x.scaler, UpscalerNone) else "" + key=lambda x: x.name.lower() if not isinstance(x.scaler, (UpscalerNone, UpscalerLanczos, UpscalerNearest)) else "" ) From 49b1dc5e07825e76c85ac4ac078fd63aa835e8bd Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 6 Mar 2023 21:00:34 +0200 Subject: [PATCH 23/41] Deduplicate extra network preview-search code --- extensions-builtin/Lora/ui_extra_networks_lora.py | 10 +--------- modules/ui_extra_networks.py | 10 ++++++++++ modules/ui_extra_networks_checkpoints.py | 11 +---------- modules/ui_extra_networks_hypernets.py | 9 +-------- modules/ui_extra_networks_textual_inversion.py | 8 +------- 5 files changed, 14 insertions(+), 34 deletions(-) diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 22cabcb0f..4c1549d73 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -15,18 +15,10 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): def list_items(self): for name, lora_on_disk in lora.available_loras.items(): path, ext = os.path.splitext(lora_on_disk.filename) - previews = [path + ".png", path + ".preview.png"] - - preview = None - for file in previews: - if os.path.isfile(file): - preview = self.link_preview(file) - break - yield { "name": name, "filename": path, - "preview": preview, + "preview": self._find_preview(path), "search_term": self.search_terms_from_path(lora_on_disk.filename), "prompt": json.dumps(f""), "local_preview": path + ".png", diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 71f1d81f2..1a10a5df7 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -2,6 +2,7 @@ import glob import os.path import urllib.parse from pathlib import Path +from typing import Optional from modules import shared import gradio as gr @@ -137,6 +138,15 @@ class ExtraNetworksPage: return self.card_page.format(**args) + def _find_preview(self, path: str) -> Optional[str]: + """ + Find a preview PNG for a given path (without extension) and call link_preview on it. + """ + for file in [path + ".png", path + ".preview.png"]: + if os.path.isfile(file): + return self.link_preview(file) + return None + def intialize(): extra_pages.clear() diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 04097a794..b712d12b7 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -1,7 +1,6 @@ import html import json import os -import urllib.parse from modules import shared, ui_extra_networks, sd_models @@ -17,18 +16,10 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): checkpoint: sd_models.CheckpointInfo for name, checkpoint in sd_models.checkpoints_list.items(): path, ext = os.path.splitext(checkpoint.filename) - previews = [path + ".png", path + ".preview.png"] - - preview = None - for file in previews: - if os.path.isfile(file): - preview = self.link_preview(file) - break - yield { "name": checkpoint.name_for_extra, "filename": path, - "preview": preview, + "preview": self._find_preview(path), "search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""), "onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"', "local_preview": path + ".png", diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index 578510887..89f33242f 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -14,18 +14,11 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): def list_items(self): for name, path in shared.hypernetworks.items(): path, ext = os.path.splitext(path) - previews = [path + ".png", path + ".preview.png"] - - preview = None - for file in previews: - if os.path.isfile(file): - preview = self.link_preview(file) - break yield { "name": name, "filename": path, - "preview": preview, + "preview": self._find_preview(path), "search_term": self.search_terms_from_path(path), "prompt": json.dumps(f""), "local_preview": path + ".png", diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index bb64eb81e..f7057390d 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -15,16 +15,10 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): def list_items(self): for embedding in sd_hijack.model_hijack.embedding_db.word_embeddings.values(): path, ext = os.path.splitext(embedding.filename) - preview_file = path + ".preview.png" - - preview = None - if os.path.isfile(preview_file): - preview = self.link_preview(preview_file) - yield { "name": embedding.name, "filename": embedding.filename, - "preview": preview, + "preview": self._find_preview(path), "search_term": self.search_terms_from_path(embedding.filename), "prompt": json.dumps(embedding.name), "local_preview": path + ".preview.png", From 06f167da37cd00ea8241bd2a6a3c12d8c5fb9eaf Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 6 Mar 2023 21:14:06 +0200 Subject: [PATCH 24/41] Extra networks: support .txt description sidecar file --- extensions-builtin/Lora/ui_extra_networks_lora.py | 1 + html/extra-networks-card.html | 1 + modules/ui_extra_networks.py | 15 +++++++++++++++ modules/ui_extra_networks_checkpoints.py | 1 + modules/ui_extra_networks_hypernets.py | 1 + modules/ui_extra_networks_textual_inversion.py | 1 + style.css | 11 +++++++++++ 7 files changed, 31 insertions(+) diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 4c1549d73..9da13a090 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -19,6 +19,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): "name": name, "filename": path, "preview": self._find_preview(path), + "description": self._find_description(path), "search_term": self.search_terms_from_path(lora_on_disk.filename), "prompt": json.dumps(f""), "local_preview": path + ".png", diff --git a/html/extra-networks-card.html b/html/extra-networks-card.html index 8a5e2fbd2..8612396d2 100644 --- a/html/extra-networks-card.html +++ b/html/extra-networks-card.html @@ -7,6 +7,7 @@ {name} + {description} diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 1a10a5df7..cd61a5694 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -1,6 +1,7 @@ import glob import os.path import urllib.parse +from functools import lru_cache from pathlib import Path from typing import Optional @@ -131,6 +132,7 @@ class ExtraNetworksPage: "tabname": json.dumps(tabname), "local_preview": json.dumps(item["local_preview"]), "name": item["name"], + "description": (item.get("description") or ""), "card_clicked": onclick, "save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', "search_term": item.get("search_term", ""), @@ -147,6 +149,19 @@ class ExtraNetworksPage: return self.link_preview(file) return None + @lru_cache(maxsize=512) + def _find_description(self, path: str) -> Optional[str]: + """ + Find and read a description file for a given path (without extension). + """ + for file in [f"{path}.txt", f"{path}.description.txt"]: + try: + with open(file, "r", encoding="utf-8", errors="replace") as f: + return f.read() + except OSError: + pass + return None + def intialize(): extra_pages.clear() diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index b712d12b7..1deb785aa 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -20,6 +20,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): "name": checkpoint.name_for_extra, "filename": path, "preview": self._find_preview(path), + "description": self._find_description(path), "search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""), "onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"', "local_preview": path + ".png", diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index 89f33242f..80cc2a248 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -19,6 +19,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): "name": name, "filename": path, "preview": self._find_preview(path), + "description": self._find_description(path), "search_term": self.search_terms_from_path(path), "prompt": json.dumps(f""), "local_preview": path + ".png", diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index f7057390d..f3bae6665 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -19,6 +19,7 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): "name": embedding.name, "filename": embedding.filename, "preview": self._find_preview(path), + "description": self._find_description(path), "search_term": self.search_terms_from_path(embedding.filename), "prompt": json.dumps(embedding.name), "local_preview": path + ".preview.png", diff --git a/style.css b/style.css index 05572f662..9f2af2638 100644 --- a/style.css +++ b/style.css @@ -939,6 +939,17 @@ footer { line-break: anywhere; } +.extra-network-cards .card .actions .description { + display: block; + max-height: 3em; + white-space: pre-wrap; + line-height: 1.1; +} + +.extra-network-cards .card .actions .description:hover { + max-height: none; +} + .extra-network-cards .card .actions:hover .additional{ display: block; } From f85a192f998dcc17d0a59d8755c76100e8e31bde Mon Sep 17 00:00:00 2001 From: Yea Chen Date: Tue, 7 Mar 2023 04:04:35 +0800 Subject: [PATCH 25/41] Update modules/api/api.py Suggested change by @akx Co-authored-by: Aarni Koskela --- modules/api/api.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 46cb7c811..12b383861 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -177,14 +177,8 @@ class Api: return script, script_idx def get_scripts_list(self): - t2ilist = [] - i2ilist = [] - - for a in scripts.scripts_txt2img.titles: - t2ilist.append(str(a.lower())) - - for b in scripts.scripts_img2img.titles: - i2ilist.append(str(b.lower())) + t2ilist = [str(title.lower()) for title in scripts.scripts_txt2img.titles] + i2ilist = [str(title.lower()) for title in scripts.scripts_img2img.titles] return ScriptsList(txt2img = t2ilist, img2img = i2ilist) From 09c73710c9145afdd22bcdb6da68db8e346e35b6 Mon Sep 17 00:00:00 2001 From: vladlearns Date: Wed, 8 Mar 2023 23:00:55 +0200 Subject: [PATCH 26/41] chore: auto update all extensions using scripts --- extensions/update-all.bat | 1 + extensions/update-all.sh | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 extensions/update-all.bat create mode 100644 extensions/update-all.sh diff --git a/extensions/update-all.bat b/extensions/update-all.bat new file mode 100644 index 000000000..75a17cbe7 --- /dev/null +++ b/extensions/update-all.bat @@ -0,0 +1 @@ +for /d %%i in (*) do @if exist "%%i\.git" (echo Pulling updates for %%i... & git -C "%%i" pull) \ No newline at end of file diff --git a/extensions/update-all.sh b/extensions/update-all.sh new file mode 100644 index 000000000..b00de9278 --- /dev/null +++ b/extensions/update-all.sh @@ -0,0 +1,3 @@ +ls | while read dir; do if [ -d "$dir/.git" ]; +then echo "Pulling updates for $dir..."; +git -C "$dir" pull; fi; done \ No newline at end of file From b07b7057f0636aa142471ec27841a8001a85f98b Mon Sep 17 00:00:00 2001 From: vladlearns Date: Thu, 9 Mar 2023 16:25:18 +0200 Subject: [PATCH 27/41] chore: removed scripts and added a flag to launch.py --- extensions/update-all.bat | 1 - extensions/update-all.sh | 3 --- launch.py | 14 +++++++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) delete mode 100644 extensions/update-all.bat delete mode 100644 extensions/update-all.sh diff --git a/extensions/update-all.bat b/extensions/update-all.bat deleted file mode 100644 index 75a17cbe7..000000000 --- a/extensions/update-all.bat +++ /dev/null @@ -1 +0,0 @@ -for /d %%i in (*) do @if exist "%%i\.git" (echo Pulling updates for %%i... & git -C "%%i" pull) \ No newline at end of file diff --git a/extensions/update-all.sh b/extensions/update-all.sh deleted file mode 100644 index b00de9278..000000000 --- a/extensions/update-all.sh +++ /dev/null @@ -1,3 +0,0 @@ -ls | while read dir; do if [ -d "$dir/.git" ]; -then echo "Pulling updates for $dir..."; -git -C "$dir" pull; fi; done \ No newline at end of file diff --git a/launch.py b/launch.py index a68bb3a91..ba3067915 100644 --- a/launch.py +++ b/launch.py @@ -161,7 +161,15 @@ def git_clone(url, dir, name, commithash=None): if commithash is not None: run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}") - +def git_pull_recursive(dir): + for subdir, _, _ in os.walk(dir): + if os.path.exists(os.path.join(subdir, '.git')): + try: + output = subprocess.check_output(['git', '-C', subdir, 'pull']) + print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n") + except subprocess.CalledProcessError as e: + print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n") + def version_check(commit): try: import requests @@ -247,6 +255,7 @@ def prepare_environment(): args, _ = parser.parse_known_args(sys.argv) sys.argv, _ = extract_arg(sys.argv, '-f') + sys.argv, update_all_extensions = extract_arg(sys.argv, '--update-all-extensions') sys.argv, skip_torch_cuda_test = extract_arg(sys.argv, '--skip-torch-cuda-test') sys.argv, skip_python_version_check = extract_arg(sys.argv, '--skip-python-version-check') sys.argv, reinstall_xformers = extract_arg(sys.argv, '--reinstall-xformers') @@ -312,6 +321,9 @@ def prepare_environment(): if update_check: version_check(commit) + + if update_all_extensions: + git_pull_recursive(dir_extensions) if "--exit" in sys.argv: print("Exiting because of --exit argument") From 13081dd45ece33457f6cb2cad3a8e7840a0a6eaf Mon Sep 17 00:00:00 2001 From: vladlearns Date: Thu, 9 Mar 2023 16:56:06 +0200 Subject: [PATCH 28/41] chore: added autostash flag to pull --- launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launch.py b/launch.py index ba3067915..8cbf1ca5a 100644 --- a/launch.py +++ b/launch.py @@ -165,7 +165,7 @@ def git_pull_recursive(dir): for subdir, _, _ in os.walk(dir): if os.path.exists(os.path.join(subdir, '.git')): try: - output = subprocess.check_output(['git', '-C', subdir, 'pull']) + output = subprocess.check_output(['git', '-C', subdir, 'pull', '--autostash']) print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n") except subprocess.CalledProcessError as e: print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n") From 8d7fa2f67cb0554d8902d5d407166876020e067e Mon Sep 17 00:00:00 2001 From: Pam Date: Fri, 10 Mar 2023 22:48:41 +0500 Subject: [PATCH 29/41] sdp_attnblock_forward hijack --- modules/sd_hijack.py | 2 ++ modules/sd_hijack_optimizations.py | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index e98ae51a1..f4bb0266f 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -47,10 +47,12 @@ def apply_optimizations(): elif cmd_opts.opt_sdp_no_mem_attention and can_use_sdp: print("Applying scaled dot product cross attention optimization (without memory efficient attention).") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_no_mem_attention_forward + ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_no_mem_attnblock_forward optimization_method = 'sdp-no-mem' elif cmd_opts.opt_sdp_attention and can_use_sdp: print("Applying scaled dot product cross attention optimization.") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_attention_forward + ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_attnblock_forward optimization_method = 'sdp' elif cmd_opts.opt_sub_quad_attention: print("Applying sub-quadratic cross attention optimization.") diff --git a/modules/sd_hijack_optimizations.py b/modules/sd_hijack_optimizations.py index 68b1dd84f..2e307b5d0 100644 --- a/modules/sd_hijack_optimizations.py +++ b/modules/sd_hijack_optimizations.py @@ -473,6 +473,30 @@ def xformers_attnblock_forward(self, x): except NotImplementedError: return cross_attention_attnblock_forward(self, x) +def sdp_attnblock_forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + b, c, h, w = q.shape + q, k, v = map(lambda t: rearrange(t, 'b c h w -> b (h w) c'), (q, k, v)) + dtype = q.dtype + if shared.opts.upcast_attn: + q, k = q.float(), k.float() + q = q.contiguous() + k = k.contiguous() + v = v.contiguous() + out = torch.nn.functional.scaled_dot_product_attention(q, k, v, dropout_p=0.0, is_causal=False) + out = out.to(dtype) + out = rearrange(out, 'b (h w) c -> b c h w', h=h) + out = self.proj_out(out) + return x + out + +def sdp_no_mem_attnblock_forward(self, x): + with torch.backends.cuda.sdp_kernel(enable_flash=True, enable_math=True, enable_mem_efficient=False): + return sdp_attnblock_forward(self, x) + def sub_quad_attnblock_forward(self, x): h_ = x h_ = self.norm(h_) From 5fef67f6ee949a61826a3a043ea8610fd89fc371 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:56:14 -0500 Subject: [PATCH 30/41] Requested changes --- modules/models/diffusion/uni_pc/sampler.py | 2 +- modules/sd_samplers_compvis.py | 4 +++- modules/shared.py | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/models/diffusion/uni_pc/sampler.py b/modules/models/diffusion/uni_pc/sampler.py index 708a9b2ba..6bb3bb210 100644 --- a/modules/models/diffusion/uni_pc/sampler.py +++ b/modules/models/diffusion/uni_pc/sampler.py @@ -93,7 +93,7 @@ class UniPCSampler(object): guidance_scale=unconditional_guidance_scale, ) - uni_pc = UniPC(model_fn, ns, predict_x0=True, thresholding=shared.opts.uni_pc_thresholding, variant=shared.opts.uni_pc_variant, condition=conditioning, unconditional_condition=unconditional_conditioning, before_sample=self.before_sample, after_sample=self.after_sample, after_update=self.after_update) + uni_pc = UniPC(model_fn, ns, predict_x0=True, thresholding=False, variant=shared.opts.uni_pc_variant, condition=conditioning, unconditional_condition=unconditional_conditioning, before_sample=self.before_sample, after_sample=self.after_sample, after_update=self.after_update) x = uni_pc.sample(img, steps=S, skip_type=shared.opts.uni_pc_skip_type, method="multistep", order=shared.opts.uni_pc_order, lower_order_final=shared.opts.uni_pc_lower_order_final) return x.to(device), None diff --git a/modules/sd_samplers_compvis.py b/modules/sd_samplers_compvis.py index ad39ab2b3..7d07c4a5f 100644 --- a/modules/sd_samplers_compvis.py +++ b/modules/sd_samplers_compvis.py @@ -140,10 +140,12 @@ class VanillaStableDiffusionSampler: def adjust_steps_if_invalid(self, p, num_steps): if ((self.config.name == 'DDIM') and p.ddim_discretize == 'uniform') or (self.config.name == 'PLMS') or (self.config.name == 'UniPC'): + if self.config.name == 'UniPC' and num_steps < shared.opts.uni_pc_order: + num_steps = shared.opts.uni_pc_order valid_step = 999 / (1000 // num_steps) if valid_step == math.floor(valid_step): return int(valid_step) + 1 - + return num_steps def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): diff --git a/modules/shared.py b/modules/shared.py index 7c559fa42..29f8dccb1 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -485,10 +485,9 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters" 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}), 'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma"), - 'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "vary_coeff"]}), + 'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}), 'uni_pc_skip_type': OptionInfo("time_uniform", "UniPC skip type", gr.Radio, {"choices": ["time_uniform", "time_quadratic", "logSNR"]}), 'uni_pc_order': OptionInfo(3, "UniPC order (must be < sampling steps)", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}), - 'uni_pc_thresholding': OptionInfo(False, "UniPC thresholding"), 'uni_pc_lower_order_final': OptionInfo(True, "UniPC lower order final"), })) From f261a4a53c153c630a506bc5282e9955c36b3ef2 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 11:56:05 +0300 Subject: [PATCH 31/41] use selected device instead of always cuda for UniPC sampler --- modules/models/diffusion/uni_pc/sampler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/models/diffusion/uni_pc/sampler.py b/modules/models/diffusion/uni_pc/sampler.py index 6bb3bb210..bf346ff48 100644 --- a/modules/models/diffusion/uni_pc/sampler.py +++ b/modules/models/diffusion/uni_pc/sampler.py @@ -3,7 +3,8 @@ import torch from .uni_pc import NoiseScheduleVP, model_wrapper, UniPC -from modules import shared +from modules import shared, devices + class UniPCSampler(object): def __init__(self, model, **kwargs): @@ -16,8 +17,8 @@ class UniPCSampler(object): def register_buffer(self, name, attr): if type(attr) == torch.Tensor: - if attr.device != torch.device("cuda"): - attr = attr.to(torch.device("cuda")) + if attr.device != devices.device: + attr = attr.to(devices.device) setattr(self, name, attr) def set_hooks(self, before_sample, after_sample, after_update): From 58b5b7c2f1d3b843803c1fc7a0aae8b1d6be5763 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 12:09:36 +0300 Subject: [PATCH 32/41] add UniPC options to infotext --- modules/generation_parameters_copypaste.py | 8 +++++++- modules/sd_samplers_compvis.py | 14 ++++++++++++++ modules/shared.py | 9 +++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 89dc23bff..cb3676553 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -288,6 +288,8 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model settings_map = {} + + infotext_to_setting_name_mapping = [ ('Clip skip', 'CLIP_stop_at_last_layers', ), ('Conditional mask weight', 'inpainting_mask_weight'), @@ -296,7 +298,11 @@ infotext_to_setting_name_mapping = [ ('Noise multiplier', 'initial_noise_multiplier'), ('Eta', 'eta_ancestral'), ('Eta DDIM', 'eta_ddim'), - ('Discard penultimate sigma', 'always_discard_next_to_last_sigma') + ('Discard penultimate sigma', 'always_discard_next_to_last_sigma'), + ('UniPC variant', 'uni_pc_variant'), + ('UniPC skip type', 'uni_pc_skip_type'), + ('UniPC order', 'uni_pc_order'), + ('UniPC lower order final', 'uni_pc_lower_order_final'), ] diff --git a/modules/sd_samplers_compvis.py b/modules/sd_samplers_compvis.py index 7d07c4a5f..083da18ca 100644 --- a/modules/sd_samplers_compvis.py +++ b/modules/sd_samplers_compvis.py @@ -129,6 +129,19 @@ class VanillaStableDiffusionSampler: if self.eta != 0.0: p.extra_generation_params["Eta DDIM"] = self.eta + if self.is_unipc: + keys = [ + ('UniPC variant', 'uni_pc_variant'), + ('UniPC skip type', 'uni_pc_skip_type'), + ('UniPC order', 'uni_pc_order'), + ('UniPC lower order final', 'uni_pc_lower_order_final'), + ] + + for name, key in keys: + v = getattr(shared.opts, key) + if v != shared.opts.get_default(key): + p.extra_generation_params[name] = v + for fieldname in ['p_sample_ddim', 'p_sample_plms']: if hasattr(self.sampler, fieldname): setattr(self.sampler, fieldname, self.p_sample_ddim_hook) @@ -138,6 +151,7 @@ class VanillaStableDiffusionSampler: self.mask = p.mask if hasattr(p, 'mask') else None self.nmask = p.nmask if hasattr(p, 'nmask') else None + def adjust_steps_if_invalid(self, p, num_steps): if ((self.config.name == 'DDIM') and p.ddim_discretize == 'uniform') or (self.config.name == 'PLMS') or (self.config.name == 'UniPC'): if self.config.name == 'UniPC' and num_steps < shared.opts.uni_pc_order: diff --git a/modules/shared.py b/modules/shared.py index 29f8dccb1..d481c25ba 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -563,6 +563,15 @@ class Options: return True + def get_default(self, key): + """returns the default value for the key""" + + data_label = self.data_labels.get(key) + if data_label is None: + return None + + return data_label.default + def save(self, filename): assert not cmd_opts.freeze_settings, "saving settings is disabled" From 1ace16e799c1ff43a6f67947be2506c2f83857a1 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 12:21:53 +0300 Subject: [PATCH 33/41] use path to git from env variable for git_pull_recursive --- launch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launch.py b/launch.py index 8cbf1ca5a..0868f8a90 100644 --- a/launch.py +++ b/launch.py @@ -161,15 +161,17 @@ def git_clone(url, dir, name, commithash=None): if commithash is not None: run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}") + def git_pull_recursive(dir): for subdir, _, _ in os.walk(dir): if os.path.exists(os.path.join(subdir, '.git')): try: - output = subprocess.check_output(['git', '-C', subdir, 'pull', '--autostash']) + output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash']) print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n") except subprocess.CalledProcessError as e: print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n") + def version_check(commit): try: import requests From 3531a50080e63197752dd4d9b49f0ac34a758e12 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 13:22:59 +0300 Subject: [PATCH 34/41] rename fields for API for saving/sending images save images to correct directories --- modules/api/api.py | 41 +++++++++++++++++------------------------ modules/api/models.py | 8 ++++---- modules/images.py | 3 --- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index a6bb439c7..fbd50552b 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -178,29 +178,27 @@ class Api: def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): script, script_idx = self.get_script(txt2imgreq.script_name, scripts.scripts_txt2img) - populate = txt2imgreq.copy(update={ # Override __init__ params + populate = txt2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(txt2imgreq.sampler_name or txt2imgreq.sampler_index), - "do_not_save_samples": txt2imgreq.do_not_save, - "do_not_save_grid": txt2imgreq.do_not_save, - } - ) + "do_not_save_samples": not txt2imgreq.save_images, + "do_not_save_grid": not txt2imgreq.save_images, + }) if populate.sampler_name: populate.sampler_index = None # prevent a warning later on args = vars(populate) args.pop('script_name', None) - send_images = True if not 'do_not_send' in args else not args['do_not_send'] - args.pop('do_not_send', None) - args.pop('do_not_save', None) + send_images = args.pop('send_images', True) + args.pop('save_images', None) with self.queue_lock: p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) + p.outpath_grids = opts.outdir_txt2img_grids + p.outpath_samples = opts.outdir_txt2img_samples shared.state.begin() if script is not None: - p.outpath_grids = opts.outdir_txt2img_grids - p.outpath_samples = opts.outdir_txt2img_samples p.script_args = [script_idx + 1] + [None] * (script.args_from - 1) + p.script_args processed = scripts.scripts_txt2img.run(p, *p.script_args) else: @@ -222,13 +220,12 @@ class Api: if mask: mask = decode_base64_to_image(mask) - populate = img2imgreq.copy(update={ # Override __init__ params + populate = img2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index), - "do_not_save_samples": img2imgreq.do_not_save, - "do_not_save_grid": img2imgreq.do_not_save, - "mask": mask - } - ) + "do_not_save_samples": not img2imgreq.save_images, + "do_not_save_grid": not img2imgreq.save_images, + "mask": mask, + }) if populate.sampler_name: populate.sampler_index = None # prevent a warning later on @@ -236,21 +233,17 @@ class Api: args.pop('include_init_images', None) # this is meant to be done by "exclude": True in model, but it's for a reason that I cannot determine. args.pop('script_name', None) - send_images = True if not 'do_not_send' in args else not args['do_not_send'] - args.pop('do_not_send', None) - args.pop('do_not_save', None) - - send_images = True if not 'do_not_send_images' in args else not args['do_not_send_images'] - args.pop('do_not_send_images', None) + send_images = args.pop('send_images', True) + args.pop('save_images', None) with self.queue_lock: p = StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args) p.init_images = [decode_base64_to_image(x) for x in init_images] + p.outpath_grids = opts.outdir_img2img_grids + p.outpath_samples = opts.outdir_img2img_samples shared.state.begin() if script is not None: - p.outpath_grids = opts.outdir_img2img_grids - p.outpath_samples = opts.outdir_img2img_samples p.script_args = [script_idx + 1] + [None] * (script.args_from - 1) + p.script_args processed = scripts.scripts_img2img.run(p, *p.script_args) else: diff --git a/modules/api/models.py b/modules/api/models.py index 2b66e1f03..ff3fb3447 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -104,8 +104,8 @@ StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( {"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, - {"key": "do_not_send", "type": bool, "default": False}, - {"key": "do_not_save", "type": bool, "default": True} + {"key": "send_images", "type": bool, "default": True}, + {"key": "save_images", "type": bool, "default": False}, ] ).generate_model() @@ -120,8 +120,8 @@ StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, - {"key": "do_not_send", "type": bool, "default": False}, - {"key": "do_not_save", "type": bool, "default": True} + {"key": "send_images", "type": bool, "default": True}, + {"key": "save_images", "type": bool, "default": False}, ] ).generate_model() diff --git a/modules/images.py b/modules/images.py index f8e62b718..5b80c23e1 100644 --- a/modules/images.py +++ b/modules/images.py @@ -489,9 +489,6 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i """ namegen = FilenameGenerator(p, seed, prompt, image) - if path is None: # set default path to avoid errors when functions are triggered manually or via api and param is not set - path = opts.outdir_save - if save_to_dirs is None: save_to_dirs = (grid and opts.grid_save_to_dirs) or (not grid and opts.save_to_dirs and not no_prompt) From 946797b01de3671c9969f6f7d55e35ef1adaa6e6 Mon Sep 17 00:00:00 2001 From: butaixianran Date: Sat, 11 Mar 2023 18:42:14 +0800 Subject: [PATCH 35/41] update "replace preview" link button's css modify css `.extra-network-thumbs .card:hover .additional a` 's value from `block` to `inline-block`. So, extensions can add more buttons to extra network's thumbnail card. --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 05572f662..f3fb6069d 100644 --- a/style.css +++ b/style.css @@ -856,7 +856,7 @@ footer { } .extra-network-thumbs .card:hover .additional a { - display: block; + display: inline-block; } .extra-network-thumbs .actions .additional a { From aaa367e35ce4e823219c2954ca141ca1ed14800e Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 14:18:18 +0300 Subject: [PATCH 36/41] new setting: Extra text to add before <...> when adding extra network to prompt --- javascript/extraNetworks.js | 2 +- modules/shared.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 17bf20004..5781df4ff 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -78,7 +78,7 @@ function cardClicked(tabname, textToAdd, allowNegativePrompt){ var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea") if(! tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)){ - textarea.value = textarea.value + " " + textToAdd + textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd } updateInput(textarea) diff --git a/modules/shared.py b/modules/shared.py index dbab00185..28d952dd4 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -442,6 +442,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), options_templates.update(options_section(('extra_networks', "Extra Networks"), { "extra_networks_default_view": OptionInfo("cards", "Default view for Extra Networks", gr.Dropdown, {"choices": ["cards", "thumbs"]}), "extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "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), })) From 7f2005127ff20170e2d92f353416c7f0705c593b Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 14:52:29 +0300 Subject: [PATCH 37/41] rename CFGDenoiserParams fields for #8064 --- modules/script_callbacks.py | 10 +++++----- modules/sd_samplers_kdiffusion.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index d17031355..079118761 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -29,7 +29,7 @@ class ImageSaveParams: class CFGDenoiserParams: - def __init__(self, x, image_cond, sigma, sampling_step, total_sampling_steps, tensor, uncond): + def __init__(self, x, image_cond, sigma, sampling_step, total_sampling_steps, text_cond, text_uncond): self.x = x """Latent image representation in the process of being denoised""" @@ -45,11 +45,11 @@ class CFGDenoiserParams: self.total_sampling_steps = total_sampling_steps """Total number of sampling steps planned""" - self.tensor = tensor - """ Encoder hidden states of conditioning""" + self.text_cond = text_cond + """ Encoder hidden states of text conditioning from prompt""" - self.uncond = uncond - """ Encoder hidden states of unconditioning""" + self.text_uncond = text_uncond + """ Encoder hidden states of text conditioning from negative prompt""" class CFGDenoisedParams: diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index ea974be04..93f0e55a0 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -106,8 +106,8 @@ class CFGDenoiser(torch.nn.Module): x_in = denoiser_params.x image_cond_in = denoiser_params.image_cond sigma_in = denoiser_params.sigma - tensor = denoiser_params.tensor - uncond = denoiser_params.uncond + tensor = denoiser_params.text_cond + uncond = denoiser_params.text_uncond if tensor.shape[1] == uncond.shape[1]: if not is_edit_model: From f36ba9949a64fd35a81369e4ec7107b1b87ef7fb Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 15:02:50 +0300 Subject: [PATCH 38/41] add credit for UniPC sampler into the readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2ceb4d2db..24f8e7998 100644 --- a/README.md +++ b/README.md @@ -157,5 +157,6 @@ Licenses for borrowed code can be found in `Settings -> Licenses` screen, and al - Sampling in float32 precision from a float16 UNet - marunine for the idea, Birch-san for the example Diffusers implementation (https://github.com/Birch-san/diffusers-play/tree/92feee6) - Instruct pix2pix - Tim Brooks (star), Aleksander Holynski (star), Alexei A. Efros (no star) - https://github.com/timothybrooks/instruct-pix2pix - 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) From ce68ab8d0dfdd25e820c58dbc9d3b0148a2022a4 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 15:27:42 +0300 Subject: [PATCH 39/41] remove underscores from function names in #8366 remove LRU from #8366 because I don't know why it's there --- extensions-builtin/Lora/ui_extra_networks_lora.py | 4 ++-- modules/ui_extra_networks.py | 8 +++----- modules/ui_extra_networks_checkpoints.py | 4 ++-- modules/ui_extra_networks_hypernets.py | 4 ++-- modules/ui_extra_networks_textual_inversion.py | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 9da13a090..6815f6ef8 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -18,8 +18,8 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): yield { "name": name, "filename": path, - "preview": self._find_preview(path), - "description": self._find_description(path), + "preview": self.find_preview(path), + "description": self.find_description(path), "search_term": self.search_terms_from_path(lora_on_disk.filename), "prompt": json.dumps(f""), "local_preview": path + ".png", diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index cd61a5694..21c52287b 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -1,9 +1,7 @@ import glob import os.path import urllib.parse -from functools import lru_cache from pathlib import Path -from typing import Optional from modules import shared import gradio as gr @@ -140,17 +138,17 @@ class ExtraNetworksPage: return self.card_page.format(**args) - def _find_preview(self, path: str) -> Optional[str]: + def find_preview(self, path): """ Find a preview PNG for a given path (without extension) and call link_preview on it. """ for file in [path + ".png", path + ".preview.png"]: if os.path.isfile(file): return self.link_preview(file) + return None - @lru_cache(maxsize=512) - def _find_description(self, path: str) -> Optional[str]: + def find_description(self, path): """ Find and read a description file for a given path (without extension). """ diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 1deb785aa..7d1aa2030 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -19,8 +19,8 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): yield { "name": checkpoint.name_for_extra, "filename": path, - "preview": self._find_preview(path), - "description": self._find_description(path), + "preview": self.find_preview(path), + "description": self.find_description(path), "search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""), "onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"', "local_preview": path + ".png", diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index 80cc2a248..8e49e93b0 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -18,8 +18,8 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): yield { "name": name, "filename": path, - "preview": self._find_preview(path), - "description": self._find_description(path), + "preview": self.find_preview(path), + "description": self.find_description(path), "search_term": self.search_terms_from_path(path), "prompt": json.dumps(f""), "local_preview": path + ".png", diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index f3bae6665..1ad806ebf 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -18,8 +18,8 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): yield { "name": embedding.name, "filename": embedding.filename, - "preview": self._find_preview(path), - "description": self._find_description(path), + "preview": self.find_preview(path), + "description": self.find_description(path), "search_term": self.search_terms_from_path(embedding.filename), "prompt": json.dumps(embedding.name), "local_preview": path + ".preview.png", From 9320139bd8340e8b12c178fe80411c8e25f78d9e Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 15:33:24 +0300 Subject: [PATCH 40/41] support three extensions for preview instead of one: png, jpg, webp --- modules/ui_extra_networks.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 21c52287b..3b476f3a0 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -142,7 +142,11 @@ class ExtraNetworksPage: """ Find a preview PNG for a given path (without extension) and call link_preview on it. """ - for file in [path + ".png", path + ".preview.png"]: + + preview_extensions = ["png", "jpg", "webp"] + potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in preview_extensions], []) + + for file in potential_files: if os.path.isfile(file): return self.link_preview(file) From 6da2027213c3bf132c54489d34c48ec084f8dc11 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 15:46:20 +0300 Subject: [PATCH 41/41] save previews for extra networks in the selected format --- extensions-builtin/Lora/ui_extra_networks_lora.py | 2 +- modules/ui_extra_networks.py | 3 +++ modules/ui_extra_networks_checkpoints.py | 2 +- modules/ui_extra_networks_hypernets.py | 2 +- modules/ui_extra_networks_textual_inversion.py | 4 ++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 6815f6ef8..8d32052ec 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -22,7 +22,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): "description": self.find_description(path), "search_term": self.search_terms_from_path(lora_on_disk.filename), "prompt": json.dumps(f""), - "local_preview": path + ".png", + "local_preview": f"{path}.{shared.opts.samples_format}", } def allowed_directories_for_previews(self): diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 3b476f3a0..85f0af4c2 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -144,6 +144,9 @@ class ExtraNetworksPage: """ preview_extensions = ["png", "jpg", "webp"] + if shared.opts.samples_format not in preview_extensions: + preview_extensions.append(shared.opts.samples_format) + potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in preview_extensions], []) for file in potential_files: diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 7d1aa2030..a17aa9c9c 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -23,7 +23,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): "description": self.find_description(path), "search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""), "onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"', - "local_preview": path + ".png", + "local_preview": f"{path}.{shared.opts.samples_format}", } def allowed_directories_for_previews(self): diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index 8e49e93b0..6187e0007 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -22,7 +22,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): "description": self.find_description(path), "search_term": self.search_terms_from_path(path), "prompt": json.dumps(f""), - "local_preview": path + ".png", + "local_preview": f"{path}.preview.{shared.opts.samples_format}", } def allowed_directories_for_previews(self): diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index 1ad806ebf..6944d5593 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -1,7 +1,7 @@ import json import os -from modules import ui_extra_networks, sd_hijack +from modules import ui_extra_networks, sd_hijack, shared class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): @@ -22,7 +22,7 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): "description": self.find_description(path), "search_term": self.search_terms_from_path(embedding.filename), "prompt": json.dumps(embedding.name), - "local_preview": path + ".preview.png", + "local_preview": f"{path}.preview.{shared.opts.samples_format}", } def allowed_directories_for_previews(self):