From 91643f651d2794349876b12abbf2449cdc4f30b6 Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Sun, 25 Sep 2022 19:22:12 -0400 Subject: [PATCH 01/29] Add support for checkpoint merging --- modules/extras.py | 24 ++++++++++++++++++++++++ modules/ui.py | 28 +++++++++++++++++++++++++++- webui.py | 3 ++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/modules/extras.py b/modules/extras.py index 382ffa7d..2c5b1fd6 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -3,6 +3,8 @@ import os import numpy as np from PIL import Image +import torch + from modules import processing, shared, images, devices from modules.shared import opts import modules.gfpgan_model @@ -135,3 +137,25 @@ def run_pnginfo(image): info = f"

{message}

" return '', geninfo, info + + +def run_modelmerger(modelname_0, modelname_1, alpha): + model_0 = torch.load('models/' + modelname_0 + '.ckpt') + model_1 = torch.load('models/' + modelname_1 + '.ckpt') + + theta_0 = model_0['state_dict'] + theta_1 = model_1['state_dict'] + + for key in theta_0.keys(): + if 'model' in key and key in theta_1: + theta_0[key] = (1 - alpha) * theta_0[key] + alpha * theta_1[key] + + for key in theta_1.keys(): + if 'model' in key and key not in theta_0: + theta_0[key] = theta_1[key] + + output_modelname = 'models/' + modelname_0 + '-' + modelname_1 + '-merged.ckpt'; + + torch.save(model_0, output_modelname) + + return "

Model saved to " + output_modelname + "

" diff --git a/modules/ui.py b/modules/ui.py index f7ca5588..6b3ba2f7 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -393,7 +393,7 @@ def setup_progressbar(progressbar, preview, id_part): ) -def create_ui(txt2img, img2img, run_extras, run_pnginfo): +def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): with gr.Blocks(analytics_enabled=False) as txt2img_interface: txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style, paste = create_toprow(is_img2img=False) dummy_component = gr.Label(visible=False) @@ -853,6 +853,31 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): outputs=[html, generation_info, html2], ) + with gr.Blocks() as modelmerger_interface: + with gr.Row().style(equal_height=False): + with gr.Column(variant='panel'): + gr.HTML(value="

A merger of the two checkpoints will be generated in your /models directory.

") + + modelname_0 = gr.Textbox(elem_id="modelmerger_modelname_0", label="Model Name (to)") + modelname_1 = gr.Textbox(elem_id="modelmerger_modelname_1", label="Model Name (from)") + alpha = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Alpha', value=0.3) + submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') + + with gr.Column(variant='panel'): + submit_result = gr.HTML(elem_id="modelmerger_result") + + submit.click( + fn=run_modelmerger, + inputs=[ + modelname_0, + modelname_1, + alpha + ], + outputs=[ + submit_result, + ] + ) + def create_setting_component(key): def fun(): return opts.data[key] if key in opts.data else opts.data_labels[key].default @@ -950,6 +975,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): (img2img_interface, "img2img", "img2img"), (extras_interface, "Extras", "extras"), (pnginfo_interface, "PNG Info", "pnginfo"), + (modelmerger_interface, "Checkpoint Merger", "modelmerger"), (settings_interface, "Settings", "settings"), ] diff --git a/webui.py b/webui.py index 9ea5f5a3..c70a11c7 100644 --- a/webui.py +++ b/webui.py @@ -85,7 +85,8 @@ def webui(): txt2img=wrap_gradio_gpu_call(modules.txt2img.txt2img), img2img=wrap_gradio_gpu_call(modules.img2img.img2img), run_extras=wrap_gradio_gpu_call(modules.extras.run_extras), - run_pnginfo=modules.extras.run_pnginfo + run_pnginfo=modules.extras.run_pnginfo, + run_modelmerger=modules.extras.run_modelmerger ) demo.launch( From db4ab47f09450cbeb454a6e02698cb636a456878 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 25 Sep 2022 16:09:43 -0400 Subject: [PATCH 02/29] copy over fields when sending to img2img: https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/1029 --- modules/ui.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index f7ca5588..d2402e28 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -978,18 +978,21 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): outputs=[result, text_settings], ) + paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Seed', 'Size-1', 'Size-2'] + txt2img_fields = [field for field,name in txt2img_paste_fields if name in paste_field_names] + img2img_fields = [field for field,name in img2img_paste_fields if name in paste_field_names] send_to_img2img.click( - fn=lambda x: (image_from_url_text(x)), - _js="extract_image_from_gallery_img2img", - inputs=[txt2img_gallery], - outputs=[init_img], + fn=lambda img, *args: (image_from_url_text(img),*args), + _js="(gallery, ...args) => [extract_image_from_gallery_img2img(gallery), ...args]", + inputs=[txt2img_gallery] + txt2img_fields, + outputs=[init_img] + img2img_fields, ) send_to_inpaint.click( - fn=lambda x: (image_from_url_text(x)), - _js="extract_image_from_gallery_inpaint", - inputs=[txt2img_gallery], - outputs=[init_img_with_mask], + fn=lambda x, *args: (image_from_url_text(x), *args), + _js="(gallery, ...args) => [extract_image_from_gallery_inpaint(gallery), ...args]", + inputs=[txt2img_gallery] + txt2img_fields, + outputs=[init_img_with_mask] + img2img_fields, ) img2img_send_to_img2img.click( From b8a8f749ef312a33769cc9df9491d6fab145e4c5 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 11:52:44 +0100 Subject: [PATCH 03/29] Update webui.bat --- webui.bat | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webui.bat b/webui.bat index bbe8f6c9..3f1d03f6 100644 --- a/webui.bat +++ b/webui.bat @@ -3,6 +3,8 @@ if not defined PYTHON (set PYTHON=python) if not defined VENV_DIR (set VENV_DIR=venv) +set ERROR_REPORTING=FALSE + mkdir tmp 2>NUL %PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt From e61da308bace62442833e1d97c897f56d824b68b Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 11:55:10 +0100 Subject: [PATCH 04/29] Disable sentry logging --- webui.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webui.sh b/webui.sh index 4534f149..8355c9df 100755 --- a/webui.sh +++ b/webui.sh @@ -41,6 +41,9 @@ then venv_dir="venv" fi +# Disable sentry logging +export ERROR_REPORTING=FALSE + # Do not reinstall existing pip packages on Debian/Ubuntu export PIP_IGNORE_INSTALLED=0 From b39f06cea7082183c0091119635239f10b3fe180 Mon Sep 17 00:00:00 2001 From: tateisu Date: Mon, 26 Sep 2022 11:50:11 +0900 Subject: [PATCH 05/29] add [datetime] to image file name pattern --- modules/images.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/images.py b/modules/images.py index ae0e6304..76278339 100644 --- a/modules/images.py +++ b/modules/images.py @@ -295,6 +295,7 @@ def apply_filename_pattern(x, p, seed, prompt): x = x.replace("[model_hash]", shared.sd_model.sd_model_hash) x = x.replace("[date]", datetime.date.today().isoformat()) + x = x.replace("[datetime]", re.sub(r'(\.\d+|\D+)', "", datetime.datetime.now().isoformat())) x = x.replace("[job_timestamp]", shared.state.job_timestamp) if cmd_opts.hide_ui_dir_config: From 78d6aef30249530d8cc0b5023b2f8bf8c86f8446 Mon Sep 17 00:00:00 2001 From: tateisu Date: Mon, 26 Sep 2022 15:44:52 +0900 Subject: [PATCH 06/29] use strftime. update hints.js --- javascript/hints.js | 4 ++-- modules/images.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/hints.js b/javascript/hints.js index ed79796f..59dd770c 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -57,8 +57,8 @@ titles = { "Interrogate": "Reconstruct prompt from existing image and put it into the prompt field.", - "Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [job_timestamp]; leave empty for default.", - "Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [job_timestamp]; leave empty for default.", + "Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [job_timestamp]; leave empty for default.", + "Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [job_timestamp]; leave empty for default.", "Max prompt words": "Set the maximum number of words to be used in the [prompt_words] option; ATTENTION: If the words are too long, they may exceed the maximum length of the file path that the system can handle", "Loopback": "Process an image, use it as an input, repeat.", diff --git a/modules/images.py b/modules/images.py index 76278339..9458bf8d 100644 --- a/modules/images.py +++ b/modules/images.py @@ -295,7 +295,7 @@ def apply_filename_pattern(x, p, seed, prompt): x = x.replace("[model_hash]", shared.sd_model.sd_model_hash) x = x.replace("[date]", datetime.date.today().isoformat()) - x = x.replace("[datetime]", re.sub(r'(\.\d+|\D+)', "", datetime.datetime.now().isoformat())) + x = x.replace("[datetime]", datetime.datetime.now().strftime("%Y%m%d%H%M%S")) x = x.replace("[job_timestamp]", shared.state.job_timestamp) if cmd_opts.hide_ui_dir_config: From dc11748dea36e7618a7cdad55526fa9d6faaa6cf Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Mon, 26 Sep 2022 10:50:21 -0400 Subject: [PATCH 07/29] Added smoothstep interpolation to checkpoint merging --- modules/extras.py | 19 +++++++++++++++++-- modules/ui.py | 6 ++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/modules/extras.py b/modules/extras.py index 2c5b1fd6..a9788e7d 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -139,16 +139,31 @@ def run_pnginfo(image): return '', geninfo, info -def run_modelmerger(modelname_0, modelname_1, alpha): +def run_modelmerger(modelname_0, modelname_1, interp_method, interp_amount): + # Linear interpolation (https://en.wikipedia.org/wiki/Linear_interpolation) + def weighted_sum(theta0, theta1, alpha): + return ((1 - alpha) * theta0) + (alpha * theta1) + + # Smoothstep (https://en.wikipedia.org/wiki/Smoothstep) + def sigmoid(theta0, theta1, alpha): + alpha = alpha * alpha * (3 - (2 * alpha)) + return theta0 + ((theta1 - theta0) * alpha) + model_0 = torch.load('models/' + modelname_0 + '.ckpt') model_1 = torch.load('models/' + modelname_1 + '.ckpt') theta_0 = model_0['state_dict'] theta_1 = model_1['state_dict'] + theta_func = weighted_sum + + if interp_method == "Weighted Sum": + theta_func = weighted_sum + if interp_method == "Sigmoid": + theta_func = sigmoid for key in theta_0.keys(): if 'model' in key and key in theta_1: - theta_0[key] = (1 - alpha) * theta_0[key] + alpha * theta_1[key] + theta_0[key] = theta_func(theta_0[key], theta_1[key], interp_amount) for key in theta_1.keys(): if 'model' in key and key not in theta_0: diff --git a/modules/ui.py b/modules/ui.py index 6b3ba2f7..6525676c 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -860,7 +860,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): modelname_0 = gr.Textbox(elem_id="modelmerger_modelname_0", label="Model Name (to)") modelname_1 = gr.Textbox(elem_id="modelmerger_modelname_1", label="Model Name (from)") - alpha = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Alpha', value=0.3) + interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid"], value="Weighted Sum", label="Interpolation Method") + interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') with gr.Column(variant='panel'): @@ -871,7 +872,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): inputs=[ modelname_0, modelname_1, - alpha + interp_method, + interp_amount ], outputs=[ submit_result, From 2846ca57028cca1a9ce9cee66d2500b4ac38a9c6 Mon Sep 17 00:00:00 2001 From: Connum Date: Mon, 26 Sep 2022 22:57:31 +0200 Subject: [PATCH 08/29] adds support for a notification.mp3 in the root directory that will play upon completion (fixes #1013) --- .gitignore | 3 ++- javascript/notification.js | 3 +++ modules/ui.py | 9 ++++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9d78853a..3e266baf 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ __pycache__ /webui-user.sh /interrogate /user.css -/.idea \ No newline at end of file +/.idea +notification.mp3 diff --git a/javascript/notification.js b/javascript/notification.js index e8159a7e..bdf614ad 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -25,6 +25,9 @@ onUiUpdate(function(){ lastHeadImg = headImg; + // play notification sound if available + gradioApp().querySelector('#audio_notification audio')?.play(); + if (document.hasFocus()) return; // Multiple copies of the images are in the DOM when one is selected. Dedup with a Set to get the real number generated. diff --git a/modules/ui.py b/modules/ui.py index d2402e28..efd46708 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -564,13 +564,13 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): with gr.TabItem('Inpaint', id='inpaint'): init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA") - init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False) - init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False) + init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_base") + init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_mask") mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4) with gr.Row(): - mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask") + mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask", elem_id="mask_mode") inpainting_mask_invert = gr.Radio(label='Masking mode', show_label=False, choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index") inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index") @@ -970,6 +970,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): for interface, label, ifid in interfaces: with gr.TabItem(label, id=ifid): interface.render() + + if os.path.exists(os.path.join(script_path, "notification.mp3")): + audio_notification = gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False) text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False) settings_submit.click( From 16e7ac09df1cef85781494a1234a7aa3c51fb4a9 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Tue, 27 Sep 2022 02:26:13 +0100 Subject: [PATCH 09/29] job_timestamp initialization change --- modules/shared.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index c32da110..bd030fe8 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -66,7 +66,7 @@ class State: job = "" job_no = 0 job_count = 0 - job_timestamp = 0 + job_timestamp = '0' sampling_step = 0 sampling_steps = 0 current_latent = None @@ -80,6 +80,7 @@ class State: self.job_no += 1 self.sampling_step = 0 self.current_image_sampling_step = 0 + def get_job_timestamp(self): return datetime.datetime.now().strftime("%Y%m%d%H%M%S") From c74becca23d17eeeee3774b6629674a010ae91bd Mon Sep 17 00:00:00 2001 From: Alexandre Simard Date: Mon, 26 Sep 2022 17:18:57 -0400 Subject: [PATCH 10/29] Solve issue #962 Fix by @MrAcademy --- .gitignore | 3 ++- javascript/ui.js | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9d78853a..fa1ab43e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ __pycache__ /webui-user.sh /interrogate /user.css -/.idea \ No newline at end of file +/.idea +/SwinIR diff --git a/javascript/ui.js b/javascript/ui.js index 076e9436..7db4db48 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -1,9 +1,8 @@ // various functions for interation with ui.py not large enough to warrant putting them in separate files function selected_gallery_index(){ - var gr = gradioApp() - var buttons = gradioApp().querySelectorAll(".gallery-item") - var button = gr.querySelector(".gallery-item.\\!ring-2") + var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem .gallery-item') + var button = gradioApp().querySelector('[style="display: block;"].tabitem .gallery-item.\\!ring-2') var result = -1 buttons.forEach(function(v, i){ if(v==button) { result = i } }) From 258a2d4f064c2c3c0d63c7cf8966d2260fea3f33 Mon Sep 17 00:00:00 2001 From: Martin Cairns Date: Mon, 26 Sep 2022 21:13:23 +0100 Subject: [PATCH 11/29] Add option to img2imgalt.py to use sigma adjustment instead of original method for #736 --- scripts/img2imgalt.py | 68 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/scripts/img2imgalt.py b/scripts/img2imgalt.py index 7b4ba244..0ef137f7 100644 --- a/scripts/img2imgalt.py +++ b/scripts/img2imgalt.py @@ -59,7 +59,55 @@ def find_noise_for_image(p, cond, uncond, cfg_scale, steps): return x / x.std() -Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "original_prompt", "original_negative_prompt"]) +Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "original_prompt", "original_negative_prompt", "sigma_adjustment"]) + + +# Based on changes suggested by briansemrau in https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/736 +def find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg_scale, steps): + x = p.init_latent + + s_in = x.new_ones([x.shape[0]]) + dnw = K.external.CompVisDenoiser(shared.sd_model) + sigmas = dnw.get_sigmas(steps).flip(0) + + shared.state.sampling_steps = steps + + for i in trange(1, len(sigmas)): + shared.state.sampling_step += 1 + + x_in = torch.cat([x] * 2) + sigma_in = torch.cat([sigmas[i - 1] * s_in] * 2) + cond_in = torch.cat([uncond, cond]) + + c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)] + + if i == 1: + t = dnw.sigma_to_t(torch.cat([sigmas[i] * s_in] * 2)) + else: + t = dnw.sigma_to_t(sigma_in) + + eps = shared.sd_model.apply_model(x_in * c_in, t, cond=cond_in) + denoised_uncond, denoised_cond = (x_in + eps * c_out).chunk(2) + + denoised = denoised_uncond + (denoised_cond - denoised_uncond) * cfg_scale + + if i == 1: + d = (x - denoised) / (2 * sigmas[i]) + else: + d = (x - denoised) / sigmas[i - 1] + + dt = sigmas[i] - sigmas[i - 1] + x = x + d * dt + + sd_samplers.store_latent(x) + + # This shouldn't be necessary, but solved some VRAM issues + del x_in, sigma_in, cond_in, c_out, c_in, t, + del eps, denoised_uncond, denoised_cond, denoised, d, dt + + shared.state.nextjob() + + return x / sigmas[-1] class Script(scripts.Script): @@ -78,9 +126,10 @@ class Script(scripts.Script): cfg = gr.Slider(label="Decode CFG scale", minimum=0.0, maximum=15.0, step=0.1, value=1.0) st = gr.Slider(label="Decode steps", minimum=1, maximum=150, step=1, value=50) randomness = gr.Slider(label="Randomness", minimum=0.0, maximum=1.0, step=0.01, value=0.0) - return [original_prompt, original_negative_prompt, cfg, st, randomness] + sigma_adjustment = gr.Checkbox(label="Sigma adjustment for finding noise for image", value=False) + return [original_prompt, original_negative_prompt, cfg, st, randomness, sigma_adjustment] - def run(self, p, original_prompt, original_negative_prompt, cfg, st, randomness): + def run(self, p, original_prompt, original_negative_prompt, cfg, st, randomness, sigma_adjustment): p.batch_size = 1 p.batch_count = 1 @@ -88,7 +137,10 @@ class Script(scripts.Script): def sample_extra(conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength): lat = (p.init_latent.cpu().numpy() * 10).astype(int) - same_params = self.cache is not None and self.cache.cfg_scale == cfg and self.cache.steps == st and self.cache.original_prompt == original_prompt and self.cache.original_negative_prompt == original_negative_prompt + same_params = self.cache is not None and self.cache.cfg_scale == cfg and self.cache.steps == st \ + and self.cache.original_prompt == original_prompt \ + and self.cache.original_negative_prompt == original_negative_prompt \ + and self.cache.sigma_adjustment == sigma_adjustment same_everything = same_params and self.cache.latent.shape == lat.shape and np.abs(self.cache.latent-lat).sum() < 100 if same_everything: @@ -97,8 +149,11 @@ class Script(scripts.Script): shared.state.job_count += 1 cond = p.sd_model.get_learned_conditioning(p.batch_size * [original_prompt]) uncond = p.sd_model.get_learned_conditioning(p.batch_size * [original_negative_prompt]) - rec_noise = find_noise_for_image(p, cond, uncond, cfg, st) - self.cache = Cached(rec_noise, cfg, st, lat, original_prompt, original_negative_prompt) + if sigma_adjustment: + rec_noise = find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg, st) + else: + rec_noise = find_noise_for_image(p, cond, uncond, cfg, st) + self.cache = Cached(rec_noise, cfg, st, lat, original_prompt, original_negative_prompt, sigma_adjustment) rand_noise = processing.create_random_tensors(p.init_latent.shape[1:], [p.seed + x + 1 for x in range(p.init_latent.shape[0])]) @@ -121,6 +176,7 @@ class Script(scripts.Script): p.extra_generation_params["Decode CFG scale"] = cfg p.extra_generation_params["Decode steps"] = st p.extra_generation_params["Randomness"] = randomness + p.extra_generation_params["Sigma Adjustment"] = sigma_adjustment processed = processing.process_images(p) From 8dcac9ac81db587cdac59d01db85c01579156392 Mon Sep 17 00:00:00 2001 From: Daniel <113887727+mortstraze@users.noreply.github.com> Date: Mon, 26 Sep 2022 19:30:18 +1000 Subject: [PATCH 12/29] Grids saving to subdirectory when setting unchecked Fixed grids saving to subdirectory when the setting is unchecked but "save images to subdirectory" was checked. --- modules/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index 0246e094..3abf3181 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -406,7 +406,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: index_of_first_image = 1 if opts.grid_save: - images.save_image(grid, p.outpath_grids, "grid", all_seeds[0], all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename, p=p) + images.save_image(grid, p.outpath_grids, "grid", all_seeds[0], all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename, p=p, grid=True) devices.torch_gc() return Processed(p, output_images, all_seeds[0], infotext(), subseed=all_subseeds[0], all_prompts=all_prompts, all_seeds=all_seeds, all_subseeds=all_subseeds, index_of_first_image=index_of_first_image) From c0b1177a3203091ca43f2d08f24dd821f1237612 Mon Sep 17 00:00:00 2001 From: Connum Date: Mon, 26 Sep 2022 18:12:55 +0200 Subject: [PATCH 13/29] refactored image paste handling to fill unset images successively, then replace last existing image (fixes #981) --- javascript/dragdrop.js | 24 +++++++++++++++--------- script.js | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/javascript/dragdrop.js b/javascript/dragdrop.js index c01f66e2..5aac57f7 100644 --- a/javascript/dragdrop.js +++ b/javascript/dragdrop.js @@ -68,13 +68,19 @@ window.addEventListener('paste', e => { if ( ! isValidImageList( files ) ) { return; } - [...gradioApp().querySelectorAll('input[type=file][accept="image/x-png,image/gif,image/jpeg"]')] - .filter(input => !input.matches('.\\!hidden input[type=file]')) - .forEach(input => { - input.files = files; - input.dispatchEvent(new Event('change')) - }); - [...gradioApp().querySelectorAll('[data-testid="image"]')] - .filter(imgWrap => !imgWrap.closest('.\\!hidden')) - .forEach(imgWrap => dropReplaceImage( imgWrap, files )); + + const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')] + .filter(el => uiElementIsVisible(el)); + if ( ! visibleImageFields.length ) { + return; + } + + const firstFreeImageField = visibleImageFields + .filter(el => el.querySelector('input[type=file]'))?.[0]; + + dropReplaceImage( + firstFreeImageField ? + firstFreeImageField : + visibleImageFields[visibleImageFields.length - 1] + , files ); }); diff --git a/script.js b/script.js index 7f26e23b..cf989605 100644 --- a/script.js +++ b/script.js @@ -39,3 +39,24 @@ document.addEventListener("DOMContentLoaded", function() { }); mutationObserver.observe( gradioApp(), { childList:true, subtree:true }) }); + +/** + * checks that a UI element is not in another hidden element or tab content + */ +function uiElementIsVisible(el) { + let isVisible = !el.closest('.\\!hidden'); + if ( ! isVisible ) { + return false; + } + + while( isVisible = el.closest('.tabitem')?.style.display !== 'none' ) { + if ( ! isVisible ) { + return false; + } else if ( el.parentElement ) { + el = el.parentElement + } else { + break; + } + } + return isVisible; +} \ No newline at end of file From 175baaaca23d3e58bbeaef183b1ee3e6344badec Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:46:18 +0100 Subject: [PATCH 14/29] xy_grid - Show blank cells when grid is incomplete --- scripts/xy_grid.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/xy_grid.py b/scripts/xy_grid.py index 3a2e103f..279af6b7 100644 --- a/scripts/xy_grid.py +++ b/scripts/xy_grid.py @@ -2,6 +2,7 @@ from collections import namedtuple from copy import copy import random +from PIL import Image import numpy as np import modules.scripts as scripts @@ -108,7 +109,10 @@ def draw_xy_grid(p, xs, ys, x_labels, y_labels, cell, draw_legend): if first_pocessed is None: first_pocessed = processed - res.append(processed.images[0]) + try: + res.append(processed.images[0]) + except: + res.append(Image.new(res[0].mode, res[0].size)) grid = images.image_grid(res, rows=len(ys)) if draw_legend: From 619668ccfeb10554ea5fc862787582d85f4bb623 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 08:00:11 +0100 Subject: [PATCH 15/29] Add options for exposed ddim sampler params ddim_eta & ddim_discretize --- modules/shared.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/shared.py b/modules/shared.py index bd030fe8..ce780e6e 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -220,6 +220,10 @@ options_templates.update(options_section(('ui', "User interface"), { "js_modal_lightbox_initialy_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"), })) +options_templates.update(options_section(('sampler-params', "Sampler parameters"), { + "ddim_eta": OptionInfo(0.0, "img2img ddim eta", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "ddim_discretize": OptionInfo('uniform', "img2img ddim discretize", gr.Radio, {"choices": ['uniform','quad']}), +})) class Options: data = None From ed5736139c2b3485f6c2fd3b35d900a042e9d54f Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 08:02:15 +0100 Subject: [PATCH 16/29] pass ddim_eta and ddim_discretize --- modules/sd_samplers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 1fc9d18c..39fdca70 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -120,9 +120,9 @@ class VanillaStableDiffusionSampler: # existing code fails with cetain step counts, like 9 try: - self.sampler.make_schedule(ddim_num_steps=steps, verbose=False) + self.sampler.make_schedule(ddim_num_steps=steps, ddim_eta=opts.ddim_eta, ddim_discretize=opts.ddim_discretize, verbose=False) except Exception: - self.sampler.make_schedule(ddim_num_steps=steps+1, verbose=False) + self.sampler.make_schedule(ddim_num_steps=steps+1,ddim_eta=opts.ddim_eta, ddim_discretize=opts.ddim_discretize, verbose=False) x1 = self.sampler.stochastic_encode(x, torch.tensor([t_enc] * int(x.shape[0])).to(shared.device), noise=noise) From 6b78833e3331ce9f9cbd85e2c75a1b11aefecf1c Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 09:54:24 +0100 Subject: [PATCH 17/29] Add extra kdiffusion parameters --- modules/shared.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/shared.py b/modules/shared.py index ce780e6e..d23a9008 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -223,6 +223,9 @@ options_templates.update(options_section(('ui', "User interface"), { options_templates.update(options_section(('sampler-params', "Sampler parameters"), { "ddim_eta": OptionInfo(0.0, "img2img ddim eta", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), "ddim_discretize": OptionInfo('uniform', "img2img ddim discretize", gr.Radio, {"choices": ['uniform','quad']}), + 's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), })) class Options: From 2ab3d593f9091689cdef07442df0213ef3242603 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 09:56:47 +0100 Subject: [PATCH 18/29] pass extra KDiffusionSampler function parameters --- modules/sd_samplers.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 39fdca70..2ac44f6c 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -37,6 +37,11 @@ samplers = [ ] samplers_for_img2img = [x for x in samplers if x.name != 'PLMS'] +sampler_extra_params = { + 'sample_euler':['s_churn','s_tmin','s_noise'], + 'sample_heun' :['s_churn','s_tmin','s_noise'], + 'sample_dpm_2':['s_churn','s_tmin','s_noise'], +} def setup_img2img_steps(p, steps=None): if opts.img2img_fix_steps or steps is not None: @@ -224,6 +229,7 @@ class KDiffusionSampler: self.model_wrap = k_diffusion.external.CompVisDenoiser(sd_model, quantize=shared.opts.enable_quantization) self.funcname = funcname self.func = getattr(k_diffusion.sampling, self.funcname) + self.extra_params = sampler_extra_params.get(funcname,[]) self.model_wrap_cfg = CFGDenoiser(self.model_wrap) self.sampler_noises = None self.sampler_noise_index = 0 @@ -269,7 +275,12 @@ class KDiffusionSampler: if self.sampler_noises is not None: k_diffusion.sampling.torch = TorchHijack(self) - return self.func(self.model_wrap_cfg, xi, sigma_sched, extra_args={'cond': conditioning, 'uncond': unconditional_conditioning, 'cond_scale': p.cfg_scale}, disable=False, callback=self.callback_state) + extra_params_kwargs = {} + for val in self.extra_params: + if hasattr(opts,val): + extra_params_kwargs[val] = getattr(opts,val) + + return self.func(self.model_wrap_cfg, xi, sigma_sched, extra_args={'cond': conditioning, 'uncond': unconditional_conditioning, 'cond_scale': p.cfg_scale}, disable=False, callback=self.callback_state, **extra_params_kwargs) def sample(self, p, x, conditioning, unconditional_conditioning, steps=None): steps = steps or p.steps @@ -286,7 +297,12 @@ class KDiffusionSampler: if self.sampler_noises is not None: k_diffusion.sampling.torch = TorchHijack(self) - samples = self.func(self.model_wrap_cfg, x, sigmas, extra_args={'cond': conditioning, 'uncond': unconditional_conditioning, 'cond_scale': p.cfg_scale}, disable=False, callback=self.callback_state) + extra_params_kwargs = {} + for val in self.extra_params: + if hasattr(opts,val): + extra_params_kwargs[val] = getattr(opts,val) + + samples = self.func(self.model_wrap_cfg, x, sigmas, extra_args={'cond': conditioning, 'uncond': unconditional_conditioning, 'cond_scale': p.cfg_scale}, disable=False, callback=self.callback_state, **extra_params_kwargs) return samples From 4ea36a37d69fafcb60cf3f250b6c0e1bcfe82bb1 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 15:40:47 +0100 Subject: [PATCH 19/29] add sampler properties to StableDiffusionProcessing and Processed --- modules/processing.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index 3abf3181..8d043f4d 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -78,7 +78,14 @@ class StableDiffusionProcessing: self.paste_to = None self.color_corrections = None self.denoising_strength: float = 0 - + + self.ddim_eta = opts.ddim_eta + self.ddim_discretize = opts.ddim_discretize + self.s_churn = opts.s_churn + self.s_tmin = opts.s_tmin + self.s_tmax = float('inf') # not representable as a standard ui option + self.s_noise = opts.s_noise + if not seed_enable_extras: self.subseed = -1 self.subseed_strength = 0 @@ -117,6 +124,13 @@ class Processed: self.extra_generation_params = p.extra_generation_params self.index_of_first_image = index_of_first_image + self.ddim_eta = p.ddim_eta + self.ddim_discretize = p.ddim_discretize + self.s_churn = p.s_churn + self.s_tmin = p.s_tmin + self.s_tmax = p.s_tmax + self.s_noise = p.s_noise + self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0] self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0] self.seed = int(self.seed if type(self.seed) != list else self.seed[0]) From a860839f1ffa827cd0a9356d6d5e9125585a66e0 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 15:43:16 +0100 Subject: [PATCH 20/29] take extra sampler properties from StableDiffusionProcessing --- modules/sd_samplers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 2ac44f6c..b18beb21 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -125,9 +125,9 @@ class VanillaStableDiffusionSampler: # existing code fails with cetain step counts, like 9 try: - self.sampler.make_schedule(ddim_num_steps=steps, ddim_eta=opts.ddim_eta, ddim_discretize=opts.ddim_discretize, verbose=False) + self.sampler.make_schedule(ddim_num_steps=steps, ddim_eta=p.ddim_eta, ddim_discretize=p.ddim_discretize, verbose=False) except Exception: - self.sampler.make_schedule(ddim_num_steps=steps+1,ddim_eta=opts.ddim_eta, ddim_discretize=opts.ddim_discretize, verbose=False) + self.sampler.make_schedule(ddim_num_steps=steps+1,ddim_eta=p.ddim_eta, ddim_discretize=p.ddim_discretize, verbose=False) x1 = self.sampler.stochastic_encode(x, torch.tensor([t_enc] * int(x.shape[0])).to(shared.device), noise=noise) @@ -277,8 +277,8 @@ class KDiffusionSampler: extra_params_kwargs = {} for val in self.extra_params: - if hasattr(opts,val): - extra_params_kwargs[val] = getattr(opts,val) + if hasattr(p,val): + extra_params_kwargs[val] = getattr(p,val) return self.func(self.model_wrap_cfg, xi, sigma_sched, extra_args={'cond': conditioning, 'uncond': unconditional_conditioning, 'cond_scale': p.cfg_scale}, disable=False, callback=self.callback_state, **extra_params_kwargs) @@ -299,8 +299,8 @@ class KDiffusionSampler: extra_params_kwargs = {} for val in self.extra_params: - if hasattr(opts,val): - extra_params_kwargs[val] = getattr(opts,val) + if hasattr(p,val): + extra_params_kwargs[val] = getattr(p,val) samples = self.func(self.model_wrap_cfg, x, sigmas, extra_args={'cond': conditioning, 'uncond': unconditional_conditioning, 'cond_scale': p.cfg_scale}, disable=False, callback=self.callback_state, **extra_params_kwargs) From fc2aa6b8f379c341bed9c7bc3457b5be9790f6ce Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 15:46:01 +0100 Subject: [PATCH 21/29] Add extra sampler params to xy_grid --- scripts/xy_grid.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/xy_grid.py b/scripts/xy_grid.py index 279af6b7..3721c717 100644 --- a/scripts/xy_grid.py +++ b/scripts/xy_grid.py @@ -87,7 +87,12 @@ axis_options = [ AxisOption("Prompt S/R", str, apply_prompt, format_value), AxisOption("Sampler", str, apply_sampler, format_value), AxisOption("Checkpoint name", str, apply_checkpoint, format_value), - AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label), # as it is now all AxisOptionImg2Img items must go after AxisOption ones + AxisOption("Sigma Churn", float, apply_field("s_churn"), format_value_add_label), + AxisOption("Sigma min", float, apply_field("s_tmin"), format_value_add_label), + AxisOption("Sigma max", float, apply_field("s_tmax"), format_value_add_label), + AxisOption("Sigma noise", float, apply_field("s_noise"), format_value_add_label), + AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label), + AxisOptionImg2Img("DDIM Eta", float, apply_field("ddim_eta"), format_value_add_label) # as it is now all AxisOptionImg2Img items must go after AxisOption ones ] From 61585e57789f9ae9733d76f66af508881b1e8090 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 17:47:28 +0100 Subject: [PATCH 22/29] rename DDIM eta label --- modules/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/shared.py b/modules/shared.py index d23a9008..870fb3b9 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -221,8 +221,8 @@ options_templates.update(options_section(('ui', "User interface"), { })) options_templates.update(options_section(('sampler-params', "Sampler parameters"), { - "ddim_eta": OptionInfo(0.0, "img2img ddim eta", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), - "ddim_discretize": OptionInfo('uniform', "img2img ddim discretize", gr.Radio, {"choices": ['uniform','quad']}), + "ddim_eta": OptionInfo(0.0, "DDIM eta", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), + "ddim_discretize": OptionInfo('uniform', "img2img DDIM discretize", gr.Radio, {"choices": ['uniform','quad']}), 's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), From 76d6f7b40728abf1cc6268f20b3ff446fb99123f Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 17:48:25 +0100 Subject: [PATCH 23/29] Add DDIM Eta to txt2img in xy_grid --- scripts/xy_grid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/xy_grid.py b/scripts/xy_grid.py index 3721c717..7c01231f 100644 --- a/scripts/xy_grid.py +++ b/scripts/xy_grid.py @@ -91,8 +91,8 @@ axis_options = [ AxisOption("Sigma min", float, apply_field("s_tmin"), format_value_add_label), AxisOption("Sigma max", float, apply_field("s_tmax"), format_value_add_label), AxisOption("Sigma noise", float, apply_field("s_noise"), format_value_add_label), - AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label), - AxisOptionImg2Img("DDIM Eta", float, apply_field("ddim_eta"), format_value_add_label) # as it is now all AxisOptionImg2Img items must go after AxisOption ones + AxisOption("DDIM Eta", float, apply_field("ddim_eta"), format_value_add_label), + AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label),# as it is now all AxisOptionImg2Img items must go after AxisOption ones ] From 48d5796f0dc6a60deccb591c9a2c47d2b132d3ac Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Mon, 26 Sep 2022 17:49:18 +0100 Subject: [PATCH 24/29] pass eta to tex2img sampler --- modules/sd_samplers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index b18beb21..fe2d0fb2 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -154,9 +154,9 @@ class VanillaStableDiffusionSampler: # existing code fails with cetin step counts, like 9 try: - samples_ddim, _ = 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) + samples_ddim, _ = 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=p.ddim_eta) except Exception: - samples_ddim, _ = self.sampler.sample(S=steps+1, 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) + samples_ddim, _ = self.sampler.sample(S=steps+1, 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=p.ddim_eta) return samples_ddim From adbd26a68ff3e4fdfd410859048a3b12f2d80ff6 Mon Sep 17 00:00:00 2001 From: DepFA <35278260+dfaker@users.noreply.github.com> Date: Tue, 27 Sep 2022 04:18:10 +0100 Subject: [PATCH 25/29] add missing s_tmax --- modules/sd_samplers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index fe2d0fb2..666ee1ee 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -38,9 +38,9 @@ samplers = [ samplers_for_img2img = [x for x in samplers if x.name != 'PLMS'] sampler_extra_params = { - 'sample_euler':['s_churn','s_tmin','s_noise'], - 'sample_heun' :['s_churn','s_tmin','s_noise'], - 'sample_dpm_2':['s_churn','s_tmin','s_noise'], + 'sample_euler':['s_churn','s_tmin','s_tmax','s_noise'], + 'sample_heun' :['s_churn','s_tmin','s_tmax','s_noise'], + 'sample_dpm_2':['s_churn','s_tmin','s_tmax','s_noise'], } def setup_img2img_steps(p, steps=None): From ada901ed661a717c44281d640b8fc0a275d4cb48 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Tue, 27 Sep 2022 10:44:00 +0300 Subject: [PATCH 26/29] added console outputs, more clear indication of progress, and ability to specify full filename to checkpoint merger restore "Loading..." text --- modules/extras.py | 48 ++++++++++++++++++++++++++++++++--------------- modules/ui.py | 3 ++- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/modules/extras.py b/modules/extras.py index a9788e7d..15873204 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -4,6 +4,7 @@ import numpy as np from PIL import Image import torch +import tqdm from modules import processing, shared, images, devices from modules.shared import opts @@ -149,28 +150,45 @@ def run_modelmerger(modelname_0, modelname_1, interp_method, interp_amount): alpha = alpha * alpha * (3 - (2 * alpha)) return theta0 + ((theta1 - theta0) * alpha) - model_0 = torch.load('models/' + modelname_0 + '.ckpt') - model_1 = torch.load('models/' + modelname_1 + '.ckpt') + if os.path.exists(modelname_0): + model0_filename = modelname_0 + modelname_0 = os.path.splitext(os.path.basename(modelname_0))[0] + else: + model0_filename = 'models/' + modelname_0 + '.ckpt' + + if os.path.exists(modelname_1): + model1_filename = modelname_1 + modelname_1 = os.path.splitext(os.path.basename(modelname_1))[0] + else: + model1_filename = 'models/' + modelname_1 + '.ckpt' + + print(f"Loading {model0_filename}...") + model_0 = torch.load(model0_filename, map_location='cpu') + + print(f"Loading {model1_filename}...") + model_1 = torch.load(model1_filename, map_location='cpu') theta_0 = model_0['state_dict'] theta_1 = model_1['state_dict'] - theta_func = weighted_sum - - if interp_method == "Weighted Sum": - theta_func = weighted_sum - if interp_method == "Sigmoid": - theta_func = sigmoid - - for key in theta_0.keys(): + + theta_funcs = { + "Weighted Sum": weighted_sum, + "Sigmoid": sigmoid, + } + theta_func = theta_funcs[interp_method] + + print(f"Merging...") + for key in tqdm.tqdm(theta_0.keys()): if 'model' in key and key in theta_1: theta_0[key] = theta_func(theta_0[key], theta_1[key], interp_amount) for key in theta_1.keys(): if 'model' in key and key not in theta_0: theta_0[key] = theta_1[key] - - output_modelname = 'models/' + modelname_0 + '-' + modelname_1 + '-merged.ckpt'; - + + output_modelname = 'models/' + modelname_0 + '-' + modelname_1 + '-merged.ckpt' + print(f"Saving to {output_modelname}...") torch.save(model_0, output_modelname) - - return "

Model saved to " + output_modelname + "

" + + print(f"Checkpoint saved.") + return "Checkpoint saved to " + output_modelname diff --git a/modules/ui.py b/modules/ui.py index 5476c32f..e96109c9 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -49,6 +49,7 @@ 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..." } .progress-bar { display:none!important; } .meta-text { display:none!important; } """ @@ -865,7 +866,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') with gr.Column(variant='panel'): - submit_result = gr.HTML(elem_id="modelmerger_result") + submit_result = gr.Textbox(elem_id="modelmerger_result", show_label=False) submit.click( fn=run_modelmerger, From c055132606e084fc69887357da5dd7c1bac1f590 Mon Sep 17 00:00:00 2001 From: Dave Bauman Date: Tue, 27 Sep 2022 09:50:06 -0400 Subject: [PATCH 27/29] Fix downssample typo in options Updated `downssample` to `down-sample`, matching spelling in other places. --- modules/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 870fb3b9..84302438 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -170,7 +170,7 @@ options_templates.update(options_section(('upscaling', "Upscaling"), { "SWIN_tile": OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}), "SWIN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), "ldsr_steps": OptionInfo(100, "LDSR processing steps. Lower = faster", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}), - "ldsr_pre_down": OptionInfo(1, "LDSR Pre-process downssample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), + "ldsr_pre_down": OptionInfo(1, "LDSR Pre-process down-sample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), "ldsr_post_down": OptionInfo(1, "LDSR Post-process down-sample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), "upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Radio, lambda: {"choices": [x.name for x in sd_upscalers]}), From 0bd8aa937f9172b44de30d158258c51b2174f136 Mon Sep 17 00:00:00 2001 From: Bernard Maltais Date: Tue, 27 Sep 2022 10:29:52 -0400 Subject: [PATCH 28/29] Add interpolation method and weight to output filename --- modules/extras.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/extras.py b/modules/extras.py index 15873204..c4ee2b62 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -186,7 +186,7 @@ def run_modelmerger(modelname_0, modelname_1, interp_method, interp_amount): if 'model' in key and key not in theta_0: theta_0[key] = theta_1[key] - output_modelname = 'models/' + modelname_0 + '-' + modelname_1 + '-merged.ckpt' + output_modelname = 'models/' + modelname_0 + '-' + modelname_1 + '-' + interp_method.replace(" ", "_") + '-' + str(interp_amount) + '-merged.ckpt' print(f"Saving to {output_modelname}...") torch.save(model_0, output_modelname) From b8bb14960a55e08a2c69f2d17b129b33e7ae3632 Mon Sep 17 00:00:00 2001 From: Alex Offshore Date: Tue, 27 Sep 2022 17:47:07 +0300 Subject: [PATCH 29/29] Fix variable name typo in error message for launch.py COMMANDINE_ARGS -> COMMANDLINE_ARGS --- launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launch.py b/launch.py index 4462631c..58e28f94 100644 --- a/launch.py +++ b/launch.py @@ -108,7 +108,7 @@ if not is_installed("torch") or not is_installed("torchvision"): run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch") if not skip_torch_cuda_test: - run_python("import torch; assert torch.cuda.is_available(), 'Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDINE_ARGS variable to disable this check'") + run_python("import torch; assert torch.cuda.is_available(), 'Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'") if not is_installed("k_diffusion.sampling"): run_pip(f"install {k_diffusion_package}", "k-diffusion")