From 20b41b7be708a501fbc7e10dceebababfd48ffda Mon Sep 17 00:00:00 2001 From: Bernard Maltais Date: Tue, 27 Sep 2022 15:21:25 -0400 Subject: (feat): Rework Checkpoint Merger UI for better clarity - Rename variables to better align with field input - Improve merged checkpoint output file name --- modules/ui.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/ui.py b/modules/ui.py index e96109c9..e7382ca8 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -859,10 +859,11 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): 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)") - interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid"], value="Weighted Sum", label="Interpolation Method") + with gr.Row(): + from_model_name = gr.Textbox(elem_id="modelmerger_from_model_name", label="Model Name (from)") + to_model_name = gr.Textbox(elem_id="modelmerger_to_model_name", label="Model Name (to)") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) + interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid"], value="Weighted Sum", label="Interpolation Method") submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') with gr.Column(variant='panel'): @@ -871,8 +872,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): submit.click( fn=run_modelmerger, inputs=[ - modelname_0, - modelname_1, + from_model_name, + to_model_name, interp_method, interp_amount ], -- cgit v1.2.3 From 5034f7d7597685aaa4779296983be0f49f4f991f Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 27 Sep 2022 15:56:18 -0400 Subject: added token counter next to txt2img and img2img prompts --- javascript/helpers.js | 13 +++++++++++++ javascript/ui.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ modules/sd_hijack.py | 30 ++++++++++++++++++++++-------- modules/ui.py | 7 ++++++- style.css | 4 ++++ 5 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 javascript/helpers.js (limited to 'modules/ui.py') diff --git a/javascript/helpers.js b/javascript/helpers.js new file mode 100644 index 00000000..1b26931f --- /dev/null +++ b/javascript/helpers.js @@ -0,0 +1,13 @@ +// helper functions + +function debounce(func, wait_time) { + let timeout; + return function wrapped(...args) { + let call_function = () => { + clearTimeout(timeout); + func(...args) + } + clearTimeout(timeout); + timeout = setTimeout(call_function, wait_time); + }; +} \ No newline at end of file diff --git a/javascript/ui.js b/javascript/ui.js index 076e9436..77e0f4c1 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -183,4 +183,51 @@ onUiUpdate(function(){ }); json_elem.parentElement.style.display="none" + + let debounce_time = 800 + if (!txt2img_textarea) { + txt2img_textarea = gradioApp().querySelector("#txt2img_prompt > label > textarea") + txt2img_textarea?.addEventListener("input", debounce(submit_prompt_text.bind(null, "txt2img"), debounce_time)) + } + if (!img2img_textarea) { + img2img_textarea = gradioApp().querySelector("#img2img_prompt > label > textarea") + img2img_textarea?.addEventListener("input", debounce(submit_prompt_text.bind(null, "img2img"), debounce_time)) + } }) + + +let txt2img_textarea, img2img_textarea = undefined; +function submit_prompt_text(source, e) { + let prompt_text; + if (source == "txt2img") + prompt_text = txt2img_textarea.value; + else if (source == "img2img") + prompt_text = img2img_textarea.value; + if (!prompt_text) + return; + params = { + method: "POST", + headers: { + "Accept": "application/json", + "Content-type": "application/json" + }, + body: JSON.stringify({data:[prompt_text]}) + } + fetch('http://127.0.0.1:7860/api/tokenize/', params) + .then((response) => response.json()) + .then((data) => { + if (data?.data.length) { + let response_json = data.data[0] + if (elem = gradioApp().getElementById(source+"_token_counter")) { + if (response_json.token_count > response_json.max_length) + elem.classList.add("red"); + else + elem.classList.remove("red"); + elem.innerText = response_json.token_count + "/" + response_json.max_length; + } + } + }) + .catch((error) => { + console.error('Error:', error); + }); +} \ No newline at end of file diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 7b2030d4..4d799ac0 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -180,6 +180,7 @@ class StableDiffusionModelHijack: dir_mtime = None layers = None circular_enabled = False + clip = None def load_textual_inversion_embeddings(self, dirname, model): mt = os.path.getmtime(dirname) @@ -242,6 +243,7 @@ class StableDiffusionModelHijack: model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.token_embedding, self) m.cond_stage_model = FrozenCLIPEmbedderWithCustomWords(m.cond_stage_model, self) + self.clip = m.cond_stage_model if cmd_opts.opt_split_attention_v1: ldm.modules.attention.CrossAttention.forward = split_cross_attention_forward_v1 @@ -268,6 +270,11 @@ class StableDiffusionModelHijack: for layer in [layer for layer in self.layers if type(layer) == torch.nn.Conv2d]: layer.padding_mode = 'circular' if enable else 'zeros' + def tokenize(self, text): + max_length = self.clip.max_length - 2 + _, remade_batch_tokens, _, _, _, token_count = self.clip.process_text([text]) + return {"tokens": remade_batch_tokens[0], "token_count":token_count, "max_length":max_length} + class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module): def __init__(self, wrapped, hijack): @@ -294,14 +301,16 @@ class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module): if mult != 1.0: self.token_mults[ident] = mult - def forward(self, text): - self.hijack.fixes = [] - self.hijack.comments = [] - remade_batch_tokens = [] + def process_text(self, text): id_start = self.wrapped.tokenizer.bos_token_id id_end = self.wrapped.tokenizer.eos_token_id maxlen = self.wrapped.max_length used_custom_terms = [] + remade_batch_tokens = [] + overflowing_words = [] + hijack_comments = [] + hijack_fixes = [] + token_count = 0 cache = {} batch_tokens = self.wrapped.tokenizer(text, truncation=False, add_special_tokens=False)["input_ids"] @@ -353,9 +362,8 @@ class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module): ovf = remade_tokens[maxlen - 2:] overflowing_words = [vocab.get(int(x), "") for x in ovf] overflowing_text = self.wrapped.tokenizer.convert_tokens_to_string(''.join(overflowing_words)) - - self.hijack.comments.append(f"Warning: too many input tokens; some ({len(overflowing_words)}) have been truncated:\n{overflowing_text}\n") - + hijack_comments.append(f"Warning: too many input tokens; some ({len(overflowing_words)}) have been truncated:\n{overflowing_text}\n") + token_count = len(remade_tokens) remade_tokens = remade_tokens + [id_end] * (maxlen - 2 - len(remade_tokens)) remade_tokens = [id_start] + remade_tokens[0:maxlen-2] + [id_end] cache[tuple_tokens] = (remade_tokens, fixes, multipliers) @@ -364,8 +372,14 @@ class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module): multipliers = [1.0] + multipliers[0:maxlen - 2] + [1.0] remade_batch_tokens.append(remade_tokens) - self.hijack.fixes.append(fixes) + hijack_fixes.append(fixes) batch_multipliers.append(multipliers) + return batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, token_count + + def forward(self, text): + batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, token_count = self.process_text(text) + self.hijack.fixes = hijack_fixes + self.hijack.comments = hijack_comments if len(used_custom_terms) > 0: self.hijack.comments.append("Used custom terms: " + ", ".join([f'{word} [{checksum}]' for word, checksum in used_custom_terms])) diff --git a/modules/ui.py b/modules/ui.py index f7ca5588..3b9c8525 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -22,6 +22,7 @@ from modules.paths import script_path from modules.shared import opts, cmd_opts import modules.shared as shared from modules.sd_samplers import samplers, samplers_for_img2img +from modules.sd_hijack import model_hijack import modules.ldsr_model import modules.scripts import modules.gfpgan_model @@ -337,11 +338,15 @@ def create_toprow(is_img2img): with gr.Row(): with gr.Column(scale=80): with gr.Row(): - prompt = gr.Textbox(label="Prompt", elem_id="prompt", show_label=False, placeholder="Prompt", lines=2) + prompt = gr.Textbox(label="Prompt", elem_id=id_part+"_prompt", show_label=False, placeholder="Prompt", lines=2) with gr.Column(scale=1, elem_id="roll_col"): roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0) paste = gr.Button(value=paste_symbol, elem_id="paste") + token_counter = gr.HTML(value="", elem_id=f"{id_part}_token_counter") + token_output = gr.JSON(visible=False) + if is_img2img: # only define the api function ONCE + token_counter.change(fn=model_hijack.tokenize, api_name="tokenize", inputs=[token_counter], outputs=[token_output]) with gr.Column(scale=10, elem_id="style_pos_col"): prompt_style = gr.Dropdown(label="Style 1", elem_id=f"{id_part}_style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1) diff --git a/style.css b/style.css index 4054e2df..877f2f7f 100644 --- a/style.css +++ b/style.css @@ -389,3 +389,7 @@ input[type="range"]{ border-radius: 8px; display: none; } + +.red { + color: red; +} -- cgit v1.2.3 From e5707b66d6db2c019bfccf66f9ba53e3daaea40b Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 27 Sep 2022 19:29:53 -0400 Subject: switched the token counter to use hidden buttons instead of api call --- javascript/helpers.js | 13 ----------- javascript/ui.js | 60 +++++++++++++-------------------------------------- modules/sd_hijack.py | 3 +-- modules/ui.py | 13 +++++++---- 4 files changed, 25 insertions(+), 64 deletions(-) delete mode 100644 javascript/helpers.js (limited to 'modules/ui.py') diff --git a/javascript/helpers.js b/javascript/helpers.js deleted file mode 100644 index 1b26931f..00000000 --- a/javascript/helpers.js +++ /dev/null @@ -1,13 +0,0 @@ -// helper functions - -function debounce(func, wait_time) { - let timeout; - return function wrapped(...args) { - let call_function = () => { - clearTimeout(timeout); - func(...args) - } - clearTimeout(timeout); - timeout = setTimeout(call_function, wait_time); - }; -} \ No newline at end of file diff --git a/javascript/ui.js b/javascript/ui.js index fbe5a11d..6cfa5c08 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -182,51 +182,21 @@ onUiUpdate(function(){ }); json_elem.parentElement.style.display="none" - - let debounce_time = 800 - if (!txt2img_textarea) { - txt2img_textarea = gradioApp().querySelector("#txt2img_prompt > label > textarea") - txt2img_textarea?.addEventListener("input", debounce(submit_prompt_text.bind(null, "txt2img"), debounce_time)) - } - if (!img2img_textarea) { - img2img_textarea = gradioApp().querySelector("#img2img_prompt > label > textarea") - img2img_textarea?.addEventListener("input", debounce(submit_prompt_text.bind(null, "img2img"), debounce_time)) - } }) +let wait_time = 800 +let token_timeout; +function txt2img_token_counter(text) { + return update_token_counter("txt2img_token_button", text); +} + +function img2img_token_counter(text) { + return update_token_counter("img2img_token_button", text); +} -let txt2img_textarea, img2img_textarea = undefined; -function submit_prompt_text(source, e) { - let prompt_text; - if (source == "txt2img") - prompt_text = txt2img_textarea.value; - else if (source == "img2img") - prompt_text = img2img_textarea.value; - if (!prompt_text) - return; - params = { - method: "POST", - headers: { - "Accept": "application/json", - "Content-type": "application/json" - }, - body: JSON.stringify({data:[prompt_text]}) - } - fetch('http://127.0.0.1:7860/api/tokenize/', params) - .then((response) => response.json()) - .then((data) => { - if (data?.data.length) { - let response_json = data.data[0] - if (elem = gradioApp().getElementById(source+"_token_counter")) { - if (response_json.token_count > response_json.max_length) - elem.classList.add("red"); - else - elem.classList.remove("red"); - elem.innerText = response_json.token_count + "/" + response_json.max_length; - } - } - }) - .catch((error) => { - console.error('Error:', error); - }); -} \ No newline at end of file +function update_token_counter(button_id, text) { + if (token_timeout) + clearTimeout(token_timeout); + token_timeout = setTimeout(() => gradioApp().getElementById(button_id)?.click(), wait_time); + return []; +} diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 4d799ac0..bfbd07f9 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -273,8 +273,7 @@ class StableDiffusionModelHijack: def tokenize(self, text): max_length = self.clip.max_length - 2 _, remade_batch_tokens, _, _, _, token_count = self.clip.process_text([text]) - return {"tokens": remade_batch_tokens[0], "token_count":token_count, "max_length":max_length} - + return remade_batch_tokens[0], token_count, max_length class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module): def __init__(self, wrapped, hijack): diff --git a/modules/ui.py b/modules/ui.py index 9a3d69c8..15bfd697 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -23,6 +23,7 @@ from modules.shared import opts, cmd_opts import modules.shared as shared from modules.sd_samplers import samplers, samplers_for_img2img from modules.sd_hijack import model_hijack +from modules.helpers import debounce import modules.ldsr_model import modules.scripts import modules.gfpgan_model @@ -330,6 +331,10 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: outputs=[seed, dummy_component] ) +def update_token_counter(text): + tokens, token_count, max_length = model_hijack.tokenize(text) + style_class = ' class="red"' if (token_count > max_length) else "" + return f"{token_count}/{max_length}" def create_toprow(is_img2img): id_part = "img2img" if is_img2img else "txt2img" @@ -339,15 +344,15 @@ def create_toprow(is_img2img): with gr.Row(): with gr.Column(scale=80): with gr.Row(): - prompt = gr.Textbox(label="Prompt", elem_id=id_part+"_prompt", show_label=False, placeholder="Prompt", lines=2) + prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, placeholder="Prompt", lines=2) + prompt.change(fn=lambda *args: [], _js=f"{id_part}_token_counter", inputs=[prompt], outputs=[], preprocess=False) with gr.Column(scale=1, elem_id="roll_col"): roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0) paste = gr.Button(value=paste_symbol, elem_id="paste") token_counter = gr.HTML(value="", elem_id=f"{id_part}_token_counter") - token_output = gr.JSON(visible=False) - if is_img2img: # only define the api function ONCE - token_counter.change(fn=model_hijack.tokenize, api_name="tokenize", inputs=[token_counter], outputs=[token_output]) + hidden_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button") + hidden_button.click(fn=update_token_counter, inputs=[prompt], outputs=[token_counter]) with gr.Column(scale=10, elem_id="style_pos_col"): prompt_style = gr.Dropdown(label="Style 1", elem_id=f"{id_part}_style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1) -- cgit v1.2.3 From d641af6a7d6f1edb7dbc584f72f95e036d830c85 Mon Sep 17 00:00:00 2001 From: Bernard Maltais Date: Tue, 27 Sep 2022 21:09:28 -0400 Subject: - Add gradio dropdown list to select checkpoints - Update checkpoint model fields labels --- modules/extras.py | 28 ++++++++++++++-------------- modules/ui.py | 9 +++++---- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/extras.py b/modules/extras.py index 9e1efeda..90968352 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -140,7 +140,7 @@ def run_pnginfo(image): return '', geninfo, info -def run_modelmerger(from_model_name, to_model_name, interp_method, interp_amount): +def run_modelmerger(primary_model_name, secondary_model_name, 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) @@ -150,23 +150,23 @@ def run_modelmerger(from_model_name, to_model_name, interp_method, interp_amount alpha = alpha * alpha * (3 - (2 * alpha)) return theta0 + ((theta1 - theta0) * alpha) - if os.path.exists(to_model_name): - to_model_filename = to_model_name - to_model_name = os.path.splitext(os.path.basename(to_model_name))[0] + if os.path.exists(secondary_model_name): + secondary_model_filename = secondary_model_name + secondary_model_name = os.path.splitext(os.path.basename(secondary_model_name))[0] else: - to_model_filename = 'models/' + to_model_name + '.ckpt' + secondary_model_filename = 'models/' + secondary_model_name + '.ckpt' - if os.path.exists(from_model_name): - from_model_filename = from_model_name - from_model_name = os.path.splitext(os.path.basename(from_model_name))[0] + if os.path.exists(primary_model_name): + primary_model_filename = primary_model_name + primary_model_name = os.path.splitext(os.path.basename(primary_model_name))[0] else: - from_model_filename = 'models/' + from_model_name + '.ckpt' + primary_model_filename = 'models/' + primary_model_name + '.ckpt' - print(f"Loading {to_model_filename}...") - model_0 = torch.load(to_model_filename, map_location='cpu') + print(f"Loading {secondary_model_filename}...") + model_0 = torch.load(secondary_model_filename, map_location='cpu') - print(f"Loading {from_model_filename}...") - model_1 = torch.load(from_model_filename, map_location='cpu') + print(f"Loading {primary_model_filename}...") + model_1 = torch.load(primary_model_filename, map_location='cpu') theta_0 = model_0['state_dict'] theta_1 = model_1['state_dict'] @@ -186,7 +186,7 @@ def run_modelmerger(from_model_name, to_model_name, interp_method, interp_amount if 'model' in key and key not in theta_0: theta_0[key] = theta_1[key] - output_modelname = 'models/' + from_model_name + '_' + str(interp_amount) + '-' + to_model_name + '_' + str(float(1.0) - interp_amount) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' + output_modelname = 'models/' + primary_model_name + '_' + str(interp_amount) + '-' + secondary_model_name + '_' + str(float(1.0) - interp_amount) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' print(f"Saving to {output_modelname}...") torch.save(model_0, output_modelname) diff --git a/modules/ui.py b/modules/ui.py index e7382ca8..4a5326f7 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -860,8 +860,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): gr.HTML(value="

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

") with gr.Row(): - from_model_name = gr.Textbox(elem_id="modelmerger_from_model_name", label="Model Name (from)") - to_model_name = gr.Textbox(elem_id="modelmerger_to_model_name", label="Model Name (to)") + ckpt_name_list = [x.model_name for x in modules.sd_models.checkpoints_list.values()] + primary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_primary_model_name", label="Primary Model Name") + secondary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid"], value="Weighted Sum", label="Interpolation Method") submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') @@ -872,8 +873,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): submit.click( fn=run_modelmerger, inputs=[ - from_model_name, - to_model_name, + primary_model_name, + secondary_model_name, interp_method, interp_amount ], -- cgit v1.2.3 From 4c9c2a67028097dd41a46d6b6fa52d7831e359bf Mon Sep 17 00:00:00 2001 From: Bernard Maltais Date: Tue, 27 Sep 2022 22:16:25 -0400 Subject: -Fix interp_amount rounding in filename -Sort list of checkpoints in list --- modules/extras.py | 4 ++-- modules/ui.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/extras.py b/modules/extras.py index f6704382..b8ebc619 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -186,9 +186,9 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int if 'model' in key and key not in theta_0: theta_0[key] = theta_1[key] - output_modelname = 'models/' + primary_model_name + '_' + str(interp_amount) + '-' + secondary_model_name + '_' + str(float(1.0) - interp_amount) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' + output_modelname = 'models/' + primary_model_name + '_' + str(round(interp_amount,2)) + '-' + secondary_model_name + '_' + str(round((float(1.0) - interp_amount),2)) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' print(f"Saving to {output_modelname}...") torch.save(primary_model, output_modelname) print(f"Checkpoint saved.") - return "Checkpoint saved to " + output_modelname + return "Checkpoint saved to " + output_modelname \ No newline at end of file diff --git a/modules/ui.py b/modules/ui.py index 4a5326f7..7db8edbd 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -860,7 +860,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): gr.HTML(value="

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

") with gr.Row(): - ckpt_name_list = [x.model_name for x in modules.sd_models.checkpoints_list.values()] + ckpt_name_list = sorted([x.model_name for x in modules.sd_models.checkpoints_list.values()]) primary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_primary_model_name", label="Primary Model Name") secondary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) -- cgit v1.2.3 From fe2f0e172923d4714cfef137400841f9ff7541fc Mon Sep 17 00:00:00 2001 From: Bernard Maltais Date: Wed, 28 Sep 2022 08:52:46 -0400 Subject: Adding support for inverse sigmoid interpolation --- modules/extras.py | 7 +++++++ modules/ui.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'modules/ui.py') diff --git a/modules/extras.py b/modules/extras.py index b8ebc619..15de033a 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -150,6 +150,12 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int alpha = alpha * alpha * (3 - (2 * alpha)) return theta0 + ((theta1 - theta0) * alpha) + # Inverse Smoothstep (https://en.wikipedia.org/wiki/Smoothstep) + def inv_sigmoid(theta0, theta1, alpha): + import math + alpha = 0.5 - math.sin(math.asin(1.0 - 2.0 * alpha) / 3.0) + return theta0 + ((theta1 - theta0) * alpha) + if os.path.exists(primary_model_name): primary_model_filename = primary_model_name primary_model_name = os.path.splitext(os.path.basename(primary_model_name))[0] @@ -174,6 +180,7 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int theta_funcs = { "Weighted Sum": weighted_sum, "Sigmoid": sigmoid, + "Inverse Sigmoid": inv_sigmoid } theta_func = theta_funcs[interp_method] diff --git a/modules/ui.py b/modules/ui.py index 7db8edbd..f5d76613 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -864,7 +864,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): primary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_primary_model_name", label="Primary Model Name") secondary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) - interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid"], value="Weighted Sum", label="Interpolation Method") + interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method") submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') with gr.Column(variant='panel'): -- cgit v1.2.3 From 7ca9858c4c05b67089b095142ff792e07b5962a9 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 28 Sep 2022 09:43:54 -0400 Subject: removed unused import; now using javascript to watch prompt textarea --- javascript/ui.js | 20 +++++++++++--------- modules/ui.py | 2 -- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'modules/ui.py') diff --git a/javascript/ui.js b/javascript/ui.js index 6cfa5c08..562d2552 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -182,21 +182,23 @@ onUiUpdate(function(){ }); json_elem.parentElement.style.display="none" + + if (!txt2img_textarea) { + txt2img_textarea = gradioApp().querySelector("#txt2img_prompt > label > textarea"); + txt2img_textarea?.addEventListener("input", () => update_token_counter("txt2img_token_button")); + } + if (!img2img_textarea) { + img2img_textarea = gradioApp().querySelector("#img2img_prompt > label > textarea"); + img2img_textarea?.addEventListener("input", () => update_token_counter("img2img_token_button")); + } }) +let txt2img_textarea, img2img_textarea = undefined; let wait_time = 800 let token_timeout; -function txt2img_token_counter(text) { - return update_token_counter("txt2img_token_button", text); -} - -function img2img_token_counter(text) { - return update_token_counter("img2img_token_button", text); -} -function update_token_counter(button_id, text) { +function update_token_counter(button_id) { if (token_timeout) clearTimeout(token_timeout); token_timeout = setTimeout(() => gradioApp().getElementById(button_id)?.click(), wait_time); - return []; } diff --git a/modules/ui.py b/modules/ui.py index 15bfd697..4e24eb55 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -23,7 +23,6 @@ from modules.shared import opts, cmd_opts import modules.shared as shared from modules.sd_samplers import samplers, samplers_for_img2img from modules.sd_hijack import model_hijack -from modules.helpers import debounce import modules.ldsr_model import modules.scripts import modules.gfpgan_model @@ -345,7 +344,6 @@ def create_toprow(is_img2img): with gr.Column(scale=80): with gr.Row(): prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, placeholder="Prompt", lines=2) - prompt.change(fn=lambda *args: [], _js=f"{id_part}_token_counter", inputs=[prompt], outputs=[], preprocess=False) with gr.Column(scale=1, elem_id="roll_col"): roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0) -- cgit v1.2.3 From aea5b2510ed4bd9150cea67b6036c837f7df2750 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Wed, 28 Sep 2022 17:05:23 +0300 Subject: save parameters for images when using the Save button. --- modules/processing.py | 9 +++++++-- modules/shared.py | 2 +- modules/ui.py | 16 ++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/processing.py b/modules/processing.py index 90e00bf8..e6b84684 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -100,7 +100,7 @@ class StableDiffusionProcessing: class Processed: - def __init__(self, p: StableDiffusionProcessing, images_list, seed=-1, info="", subseed=None, all_prompts=None, all_seeds=None, all_subseeds=None, index_of_first_image=0): + def __init__(self, p: StableDiffusionProcessing, images_list, seed=-1, info="", subseed=None, all_prompts=None, all_seeds=None, all_subseeds=None, index_of_first_image=0, infotexts=None): self.images = images_list self.prompt = p.prompt self.negative_prompt = p.negative_prompt @@ -139,6 +139,7 @@ class Processed: self.all_prompts = all_prompts or [self.prompt] self.all_seeds = all_seeds or [self.seed] self.all_subseeds = all_subseeds or [self.subseed] + self.infotexts = infotexts or [info] def js(self): obj = { @@ -165,6 +166,7 @@ class Processed: "denoising_strength": self.denoising_strength, "extra_generation_params": self.extra_generation_params, "index_of_first_image": self.index_of_first_image, + "infotexts": self.infotexts, } return json.dumps(obj) @@ -322,6 +324,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if os.path.exists(cmd_opts.embeddings_dir): model_hijack.load_textual_inversion_embeddings(cmd_opts.embeddings_dir, p.sd_model) + infotexts = [] output_images = [] precision_scope = torch.autocast if cmd_opts.precision == "autocast" else contextlib.nullcontext ema_scope = (contextlib.nullcontext if cmd_opts.lowvram else p.sd_model.ema_scope) @@ -404,6 +407,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if opts.samples_save and not p.do_not_save_samples: images.save_image(image, p.outpath_samples, "", seeds[i], prompts[i], opts.samples_format, info=infotext(n, i), p=p) + infotexts.append(infotext(n, i)) output_images.append(image) state.nextjob() @@ -416,6 +420,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: grid = images.image_grid(output_images, p.batch_size) if opts.return_grid: + infotexts.insert(0, infotext()) output_images.insert(0, grid) index_of_first_image = 1 @@ -423,7 +428,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: 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) + 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, infotexts=infotexts) class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): diff --git a/modules/shared.py b/modules/shared.py index 2502fe2d..ae459e14 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -143,6 +143,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "export_for_4chan": OptionInfo(True, "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG"), "use_original_name_batch": OptionInfo(False, "Use original name for output filename during batch process in extras tab"), + "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), })) options_templates.update(options_section(('saving-paths', "Paths for saving"), { @@ -180,7 +181,6 @@ options_templates.update(options_section(('face-restoration', "Face restoration" "face_restoration_model": OptionInfo(None, "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}), "code_former_weight": OptionInfo(0.5, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}), "face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"), - "save_selected_only": OptionInfo(False, "When using 'Save' button, only save a single selected image"), })) options_templates.update(options_section(('system', "System"), { diff --git a/modules/ui.py b/modules/ui.py index 7db8edbd..484be762 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -12,7 +12,7 @@ import traceback import numpy as np import torch -from PIL import Image +from PIL import Image, PngImagePlugin import gradio as gr import gradio.utils @@ -97,10 +97,11 @@ def save_files(js_data, images, index): filenames = [] data = json.loads(js_data) - - if index > -1 and opts.save_selected_only and (index > 0 or not opts.return_grid): # ensures we are looking at a specific non-grid picture, and we have save_selected_only + if index > -1 and opts.save_selected_only and (index >= data["index_of_first_image"]): # ensures we are looking at a specific non-grid picture, and we have save_selected_only images = [images[index]] - data["seed"] += (index - 1 if opts.return_grid else index) + infotexts = [data["infotexts"][index]] + else: + infotexts = data["infotexts"] with open(os.path.join(opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file: at_start = file.tell() == 0 @@ -116,8 +117,11 @@ def save_files(js_data, images, index): if filedata.startswith("data:image/png;base64,"): filedata = filedata[len("data:image/png;base64,"):] - with open(filepath, "wb") as imgfile: - imgfile.write(base64.decodebytes(filedata.encode('utf-8'))) + pnginfo = PngImagePlugin.PngInfo() + pnginfo.add_text('parameters', infotexts[i]) + + image = Image.open(io.BytesIO(base64.decodebytes(filedata.encode('utf-8')))) + image.save(filepath, quality=opts.jpeg_quality, pnginfo=pnginfo) filenames.append(filename) -- cgit v1.2.3 From 791808c890fc2fc3417f827f8744765970b23f13 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Thu, 29 Sep 2022 00:21:54 +0300 Subject: correctly list and display model names for #1261 --- modules/extras.py | 23 ++++++++--------------- modules/ui.py | 4 ++-- 2 files changed, 10 insertions(+), 17 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/extras.py b/modules/extras.py index 15de033a..dcc0148c 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -6,7 +6,7 @@ from PIL import Image import torch import tqdm -from modules import processing, shared, images, devices +from modules import processing, shared, images, devices, sd_models from modules.shared import opts import modules.gfpgan_model from modules.ui import plaintext_to_html @@ -156,17 +156,8 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int alpha = 0.5 - math.sin(math.asin(1.0 - 2.0 * alpha) / 3.0) return theta0 + ((theta1 - theta0) * alpha) - if os.path.exists(primary_model_name): - primary_model_filename = primary_model_name - primary_model_name = os.path.splitext(os.path.basename(primary_model_name))[0] - else: - primary_model_filename = 'models/' + primary_model_name + '.ckpt' - - if os.path.exists(secondary_model_name): - secondary_model_filename = secondary_model_name - secondary_model_name = os.path.splitext(os.path.basename(secondary_model_name))[0] - else: - secondary_model_filename = 'models/' + secondary_model_name + '.ckpt' + primary_model_filename = sd_models.checkpoints_list[primary_model_name].filename + secondary_model_filename = sd_models.checkpoints_list[secondary_model_name].filename print(f"Loading {primary_model_filename}...") primary_model = torch.load(primary_model_filename, map_location='cpu') @@ -180,7 +171,7 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int theta_funcs = { "Weighted Sum": weighted_sum, "Sigmoid": sigmoid, - "Inverse Sigmoid": inv_sigmoid + "Inverse Sigmoid": inv_sigmoid, } theta_func = theta_funcs[interp_method] @@ -193,9 +184,11 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int if 'model' in key and key not in theta_0: theta_0[key] = theta_1[key] - output_modelname = 'models/' + primary_model_name + '_' + str(round(interp_amount,2)) + '-' + secondary_model_name + '_' + str(round((float(1.0) - interp_amount),2)) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' + filename = primary_model_name + '_' + str(round(interp_amount,2)) + '-' + secondary_model_name + '_' + str(round((float(1.0) - interp_amount),2)) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' + output_modelname = os.path.join(shared.cmd_opts.ckpt_dir, filename) + print(f"Saving to {output_modelname}...") torch.save(primary_model, output_modelname) print(f"Checkpoint saved.") - return "Checkpoint saved to " + output_modelname \ No newline at end of file + return "Checkpoint saved to " + output_modelname diff --git a/modules/ui.py b/modules/ui.py index bf736b27..d51f7a08 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -872,8 +872,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): gr.HTML(value="

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

") with gr.Row(): - ckpt_name_list = sorted([x.model_name for x in modules.sd_models.checkpoints_list.values()]) - primary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_primary_model_name", label="Primary Model Name") + ckpt_name_list = sorted([x.title for x in modules.sd_models.checkpoints_list.values()]) + primary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_primary_model_name", label="Primary Model Name") secondary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method") -- cgit v1.2.3 From 7acfaca05a13352a7d86d281db6ad64dfd9350e0 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Thu, 29 Sep 2022 00:59:44 +0300 Subject: update lists of models after merging them in checkpoints tab support saving as half --- modules/extras.py | 27 +++++++++++++++++---------- modules/sd_models.py | 15 ++++++++++----- modules/shared.py | 2 +- modules/ui.py | 42 ++++++++++++++++++++++++------------------ 4 files changed, 52 insertions(+), 34 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/extras.py b/modules/extras.py index dcc0148c..9a825530 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -13,6 +13,7 @@ from modules.ui import plaintext_to_html import modules.codeformer_model import piexif import piexif.helper +import gradio as gr cached_images = {} @@ -140,7 +141,7 @@ def run_pnginfo(image): return '', geninfo, info -def run_modelmerger(primary_model_name, secondary_model_name, interp_method, interp_amount): +def run_modelmerger(primary_model_name, secondary_model_name, interp_method, interp_amount, save_as_half): # Linear interpolation (https://en.wikipedia.org/wiki/Linear_interpolation) def weighted_sum(theta0, theta1, alpha): return ((1 - alpha) * theta0) + (alpha * theta1) @@ -156,14 +157,14 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int alpha = 0.5 - math.sin(math.asin(1.0 - 2.0 * alpha) / 3.0) return theta0 + ((theta1 - theta0) * alpha) - primary_model_filename = sd_models.checkpoints_list[primary_model_name].filename - secondary_model_filename = sd_models.checkpoints_list[secondary_model_name].filename + primary_model_info = sd_models.checkpoints_list[primary_model_name] + secondary_model_info = sd_models.checkpoints_list[secondary_model_name] - print(f"Loading {primary_model_filename}...") - primary_model = torch.load(primary_model_filename, map_location='cpu') + print(f"Loading {primary_model_info.filename}...") + primary_model = torch.load(primary_model_info.filename, map_location='cpu') - print(f"Loading {secondary_model_filename}...") - secondary_model = torch.load(secondary_model_filename, map_location='cpu') + print(f"Loading {secondary_model_info.filename}...") + secondary_model = torch.load(secondary_model_info.filename, map_location='cpu') theta_0 = primary_model['state_dict'] theta_1 = secondary_model['state_dict'] @@ -178,17 +179,23 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int 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], (float(1.0) - interp_amount)) # Need to reverse the interp_amount to match the desired mix ration in the merged checkpoint + theta_0[key] = theta_func(theta_0[key], theta_1[key], (float(1.0) - interp_amount)) # Need to reverse the interp_amount to match the desired mix ration in the merged checkpoint + if save_as_half: + theta_0[key] = theta_0[key].half() for key in theta_1.keys(): if 'model' in key and key not in theta_0: theta_0[key] = theta_1[key] + if save_as_half: + theta_0[key] = theta_0[key].half() - filename = primary_model_name + '_' + str(round(interp_amount,2)) + '-' + secondary_model_name + '_' + str(round((float(1.0) - interp_amount),2)) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' + filename = primary_model_info.model_name + '_' + str(round(interp_amount, 2)) + '-' + secondary_model_info.model_name + '_' + str(round((float(1.0) - interp_amount), 2)) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' output_modelname = os.path.join(shared.cmd_opts.ckpt_dir, filename) print(f"Saving to {output_modelname}...") torch.save(primary_model, output_modelname) + sd_models.list_models() + print(f"Checkpoint saved.") - return "Checkpoint saved to " + output_modelname + return ["Checkpoint saved to " + output_modelname] + [gr.Dropdown.update(choices=sd_models.checkpoint_tiles()) for _ in range(3)] diff --git a/modules/sd_models.py b/modules/sd_models.py index 9decc911..dd47dffb 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -23,6 +23,11 @@ except Exception: pass +def checkpoint_tiles(): + print(sorted([x.title for x in checkpoints_list.values()])) + return sorted([x.title for x in checkpoints_list.values()]) + + def list_models(): checkpoints_list.clear() @@ -39,13 +44,14 @@ def list_models(): if name.startswith("\\") or name.startswith("/"): name = name[1:] - return f'{name} [{h}]' + shortname = os.path.splitext(name.replace("/", "_").replace("\\", "_"))[0] + + return f'{name} [{h}]', shortname cmd_ckpt = shared.cmd_opts.ckpt if os.path.exists(cmd_ckpt): h = model_hash(cmd_ckpt) - title = modeltitle(cmd_ckpt, h) - model_name = title.rsplit(".",1)[0] # remove extension if present + title, model_name = modeltitle(cmd_ckpt, h) checkpoints_list[title] = CheckpointInfo(cmd_ckpt, title, h, model_name) elif cmd_ckpt is not None and cmd_ckpt != shared.default_sd_model_file: print(f"Checkpoint in --ckpt argument not found: {cmd_ckpt}", file=sys.stderr) @@ -53,8 +59,7 @@ def list_models(): if os.path.exists(model_dir): for filename in glob.glob(model_dir + '/**/*.ckpt', recursive=True): h = model_hash(filename) - title = modeltitle(filename, h) - model_name = title.rsplit(".",1)[0] # remove extension if present + title, model_name = modeltitle(filename, h) checkpoints_list[title] = CheckpointInfo(filename, title, h, model_name) diff --git a/modules/shared.py b/modules/shared.py index 39cf89bc..ec1e569b 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -190,7 +190,7 @@ options_templates.update(options_section(('system', "System"), { })) options_templates.update(options_section(('sd', "Stable Diffusion"), { - "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Radio, lambda: {"choices": [x.title for x in modules.sd_models.checkpoints_list.values()]}), + "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Radio, lambda: {"choices": modules.sd_models.checkpoint_tiles()}), "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."), "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"), "img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."), diff --git a/modules/ui.py b/modules/ui.py index d51f7a08..4958036a 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -872,29 +872,16 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): gr.HTML(value="

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

") with gr.Row(): - ckpt_name_list = sorted([x.title for x in modules.sd_models.checkpoints_list.values()]) - primary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_primary_model_name", label="Primary Model Name") - secondary_model_name = gr.Dropdown(ckpt_name_list, elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") + primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary Model Name") + secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method") - submit = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') + save_as_half = gr.Checkbox(value=False, label="Safe as float16") + modelmerger_merge = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary') with gr.Column(variant='panel'): submit_result = gr.Textbox(elem_id="modelmerger_result", show_label=False) - submit.click( - fn=run_modelmerger, - inputs=[ - primary_model_name, - secondary_model_name, - interp_method, - interp_amount - ], - 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 @@ -918,6 +905,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): return comp(label=info.label, value=fun, **(args or {})) components = [] + component_dict = {} def run_settings(*args): changed = 0 @@ -973,7 +961,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): gr.HTML(elem_id="settings_header_text_{}".format(item.section[0]), value='

{}

'.format(item.section[1])) - components.append(create_setting_component(k)) + component = create_setting_component(k) + component_dict[k] = component + components.append(component) items_displayed += 1 request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications") @@ -1024,6 +1014,22 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): outputs=[result, text_settings], ) + modelmerger_merge.click( + fn=run_modelmerger, + inputs=[ + primary_model_name, + secondary_model_name, + interp_method, + interp_amount, + save_as_half, + ], + outputs=[ + submit_result, + primary_model_name, + secondary_model_name, + component_dict['sd_model_checkpoint'], + ] + ) 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] -- cgit v1.2.3 From 041d2aefc082c2883aa7e28ee3e4a990b3be9758 Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Wed, 28 Sep 2022 18:00:48 -0400 Subject: Respect --ckpt-dir command-line argument when merging models (#1261) * Update .gitignore to ignore *all* checkpoint files in /models * Fixed typo in UI --- .gitignore | 2 +- modules/ui.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'modules/ui.py') diff --git a/.gitignore b/.gitignore index 69ea78c5..b71e1875 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ __pycache__ /venv /tmp /model.ckpt -/models/*.ckpt +/models/**/*.ckpt /GFPGANv1.3.pth /gfpgan/weights/*.pth /ui-config.json diff --git a/modules/ui.py b/modules/ui.py index 4958036a..87024238 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -869,7 +869,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): 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.

") + gr.HTML(value="

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

") with gr.Row(): primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary Model Name") -- cgit v1.2.3 From f8acbb8f880815facb5037efcd676f2f0d2b5bf4 Mon Sep 17 00:00:00 2001 From: Michoko Date: Tue, 27 Sep 2022 23:02:11 +0200 Subject: Add output folder icons Adds icons on the first 3 tabs to directly open the corresponding images output directory --- javascript/hints.js | 1 + modules/ui.py | 26 +++++++++++++++++++++++++- style.css | 8 +++++++- 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'modules/ui.py') diff --git a/javascript/hints.js b/javascript/hints.js index 59dd770c..96cd24d5 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -15,6 +15,7 @@ titles = { "\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed", "\u{1f3a8}": "Add a random artist to the prompt.", "\u2199\ufe0f": "Read generation parameters from prompt into user interface.", + "\uD83D\uDCC2": "Open images output directory", "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back", diff --git a/modules/ui.py b/modules/ui.py index 87024238..4c93fca9 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -61,7 +61,7 @@ random_symbol = '\U0001f3b2\ufe0f' # 🎲️ reuse_symbol = '\u267b\ufe0f' # ♻️ art_symbol = '\U0001f3a8' # 🎨 paste_symbol = '\u2199\ufe0f' # ↙ - +folder_symbol = '\uD83D\uDCC2' def plaintext_to_html(text): text = "

" + "
\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "

" @@ -461,6 +461,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): send_to_img2img = gr.Button('Send to img2img') send_to_inpaint = gr.Button('Send to inpaint') send_to_extras = gr.Button('Send to extras') + open_txt2img_folder = gr.Button(folder_symbol, elem_id="open_folder") with gr.Group(): html_info = gr.HTML() @@ -637,6 +638,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): img2img_send_to_img2img = gr.Button('Send to img2img') img2img_send_to_inpaint = gr.Button('Send to inpaint') img2img_send_to_extras = gr.Button('Send to extras') + open_img2img_folder = gr.Button(folder_symbol, elem_id="open_folder") with gr.Group(): html_info = gr.HTML() @@ -809,6 +811,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): html_info = gr.HTML() extras_send_to_img2img = gr.Button('Send to img2img') extras_send_to_inpaint = gr.Button('Send to inpaint') + open_extras_folder = gr.Button('Open output directory') submit.click( fn=run_extras, @@ -907,6 +910,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): components = [] component_dict = {} + def open_folder(f): + os.startfile(os.path.normpath(f)) + def run_settings(*args): changed = 0 @@ -1068,6 +1074,24 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): outputs=[extras_image], ) + open_txt2img_folder.click( + fn=lambda: open_folder(opts.outdir_samples or opts.outdir_txt2img_samples), + inputs=[], + outputs=[], + ) + + open_img2img_folder.click( + fn=lambda: open_folder(opts.outdir_samples or opts.outdir_img2img_samples), + inputs=[], + outputs=[], + ) + + open_extras_folder.click( + fn=lambda: open_folder(opts.outdir_samples or opts.outdir_extras_samples), + inputs=[], + outputs=[], + ) + img2img_send_to_extras.click( fn=lambda x: image_from_url_text(x), _js="extract_image_from_gallery_extras", diff --git a/style.css b/style.css index 877f2f7f..a98c46ec 100644 --- a/style.css +++ b/style.css @@ -1,5 +1,11 @@ .output-html p {margin: 0 0.5em;} +.row > *, +.row > .gr-form > * { + min-width: min(120px, 100%); + flex: 1 1 0%; +} + .performance { font-size: 0.85em; color: #444; @@ -43,7 +49,7 @@ margin-right: auto; } -#random_seed, #random_subseed, #reuse_seed, #reuse_subseed{ +#random_seed, #random_subseed, #reuse_seed, #reuse_subseed, #open_folder{ min-width: auto; flex-grow: 0; padding-left: 0.25em; -- cgit v1.2.3 From 02c4b757b6e4e0894c5a988b49cb8f50c0d99cc1 Mon Sep 17 00:00:00 2001 From: Michoko Date: Wed, 28 Sep 2022 10:31:53 +0200 Subject: Add output folder icons Handling of the --hide-ui-dir-config flag and added multi-platform code for opening a folder --- modules/ui.py | 20 ++++++++++++++++---- style.css | 4 ++++ 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/ui.py b/modules/ui.py index 4c93fca9..2dbcd50c 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -9,6 +9,8 @@ import random import sys import time import traceback +import platform +import subprocess as sp import numpy as np import torch @@ -461,7 +463,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): send_to_img2img = gr.Button('Send to img2img') send_to_inpaint = gr.Button('Send to inpaint') send_to_extras = gr.Button('Send to extras') - open_txt2img_folder = gr.Button(folder_symbol, elem_id="open_folder") + button_id = "open_folder_hidden" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' + open_txt2img_folder = gr.Button(folder_symbol, elem_id=button_id) with gr.Group(): html_info = gr.HTML() @@ -638,7 +641,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): img2img_send_to_img2img = gr.Button('Send to img2img') img2img_send_to_inpaint = gr.Button('Send to inpaint') img2img_send_to_extras = gr.Button('Send to extras') - open_img2img_folder = gr.Button(folder_symbol, elem_id="open_folder") + button_id = "open_folder_hidden" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' + open_img2img_folder = gr.Button(folder_symbol, elem_id=button_id) with gr.Group(): html_info = gr.HTML() @@ -811,7 +815,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): html_info = gr.HTML() extras_send_to_img2img = gr.Button('Send to img2img') extras_send_to_inpaint = gr.Button('Send to inpaint') - open_extras_folder = gr.Button('Open output directory') + button_id = "open_folder_hidden" if shared.cmd_opts.hide_ui_dir_config else '' + open_extras_folder = gr.Button('Open output directory', elem_id=button_id) submit.click( fn=run_extras, @@ -911,7 +916,14 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): component_dict = {} def open_folder(f): - os.startfile(os.path.normpath(f)) + if not shared.cmd_opts.hide_ui_dir_config: + path = os.path.normpath(f) + if platform.system() == "Windows": + os.startfile(path) + elif platform.system() == "Darwin": + sp.Popen(["open", path]) + else: + sp.Popen(["xdg-open", path]) def run_settings(*args): changed = 0 diff --git a/style.css b/style.css index a98c46ec..1b53f05f 100644 --- a/style.css +++ b/style.css @@ -56,6 +56,10 @@ padding-right: 0.25em; } +#open_folder_hidden{ + display: none; +} + #seed_row, #subseed_row{ gap: 0.5rem; } -- cgit v1.2.3 From 819fd3af40d7cb5bac9a496f0e08c062fedf100b Mon Sep 17 00:00:00 2001 From: Michoko Date: Wed, 28 Sep 2022 10:40:05 +0200 Subject: Add output folder icons Changed the hidden element class name to a more generic one, so people can reuse it if they want to hide further elements in the future --- modules/ui.py | 6 +++--- style.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/ui.py b/modules/ui.py index 2dbcd50c..f704749a 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -463,7 +463,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): send_to_img2img = gr.Button('Send to img2img') send_to_inpaint = gr.Button('Send to inpaint') send_to_extras = gr.Button('Send to extras') - button_id = "open_folder_hidden" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' + button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' open_txt2img_folder = gr.Button(folder_symbol, elem_id=button_id) with gr.Group(): @@ -641,7 +641,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): img2img_send_to_img2img = gr.Button('Send to img2img') img2img_send_to_inpaint = gr.Button('Send to inpaint') img2img_send_to_extras = gr.Button('Send to extras') - button_id = "open_folder_hidden" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' + button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' open_img2img_folder = gr.Button(folder_symbol, elem_id=button_id) with gr.Group(): @@ -815,7 +815,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): html_info = gr.HTML() extras_send_to_img2img = gr.Button('Send to img2img') extras_send_to_inpaint = gr.Button('Send to inpaint') - button_id = "open_folder_hidden" if shared.cmd_opts.hide_ui_dir_config else '' + button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else '' open_extras_folder = gr.Button('Open output directory', elem_id=button_id) submit.click( diff --git a/style.css b/style.css index 1b53f05f..9709c4ee 100644 --- a/style.css +++ b/style.css @@ -56,7 +56,7 @@ padding-right: 0.25em; } -#open_folder_hidden{ +#hidden_element{ display: none; } -- cgit v1.2.3 From 66fed8ffb8e75bbed4e36ae39c30df686b477677 Mon Sep 17 00:00:00 2001 From: safentisAuth Date: Thu, 29 Sep 2022 02:50:34 +0300 Subject: Add custom name and try-except --- modules/extras.py | 3 ++- modules/ui.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/extras.py b/modules/extras.py index 9a825530..c2543fcf 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -141,7 +141,7 @@ def run_pnginfo(image): return '', geninfo, info -def run_modelmerger(primary_model_name, secondary_model_name, interp_method, interp_amount, save_as_half): +def run_modelmerger(primary_model_name, secondary_model_name, interp_method, interp_amount, save_as_half, custom_name): # Linear interpolation (https://en.wikipedia.org/wiki/Linear_interpolation) def weighted_sum(theta0, theta1, alpha): return ((1 - alpha) * theta0) + (alpha * theta1) @@ -190,6 +190,7 @@ def run_modelmerger(primary_model_name, secondary_model_name, interp_method, int theta_0[key] = theta_0[key].half() filename = primary_model_info.model_name + '_' + str(round(interp_amount, 2)) + '-' + secondary_model_info.model_name + '_' + str(round((float(1.0) - interp_amount), 2)) + '-' + interp_method.replace(" ", "_") + '-merged.ckpt' + filename = filename if custom_name == '' else (custom_name + '.ckpt') output_modelname = os.path.join(shared.cmd_opts.ckpt_dir, filename) print(f"Saving to {output_modelname}...") diff --git a/modules/ui.py b/modules/ui.py index f704749a..9b5e4e92 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -882,6 +882,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): with gr.Row(): primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary Model Name") secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary Model Name") + custom_name = gr.Textbox(label="Custom Name (Optional)") interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3) interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method") save_as_half = gr.Checkbox(value=False, label="Safe as float16") @@ -1031,15 +1032,26 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger): inputs=components, outputs=[result, text_settings], ) + + def modelmerger(*args): + try: + results = run_modelmerger(*args) + except Exception as e: + print("Error loading/saving model file:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) + modules.sd_models.list_models() #To remove the potentially missing models from the list + return ["Error loading/saving model file. It doesn't exist or the name contains illegal characters"] + [gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(3)] + return results modelmerger_merge.click( - fn=run_modelmerger, + fn=modelmerger, inputs=[ primary_model_name, secondary_model_name, interp_method, interp_amount, save_as_half, + custom_name, ], outputs=[ submit_result, -- cgit v1.2.3 From ca5901b5c8b5851d2556844d64760bac7caab354 Mon Sep 17 00:00:00 2001 From: Justin Maier Date: Wed, 28 Sep 2022 20:51:11 -0600 Subject: Save should use desired sample format fixes #1028 --- modules/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/ui.py') diff --git a/modules/ui.py b/modules/ui.py index 9b5e4e92..e1e88396 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -114,7 +114,7 @@ def save_files(js_data, images, index): filename_base = str(int(time.time() * 1000)) for i, filedata in enumerate(images): - filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + ".png" + filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + f".{opts.samples_format}" filepath = os.path.join(opts.outdir_save, filename) if filedata.startswith("data:image/png;base64,"): -- cgit v1.2.3 From a112168d2814d691936a7fb1d4cde5acb8440679 Mon Sep 17 00:00:00 2001 From: Justin Maier Date: Thu, 29 Sep 2022 08:54:45 -0600 Subject: Save parameters as comments when saving as jpg --- modules/ui.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'modules/ui.py') diff --git a/modules/ui.py b/modules/ui.py index e1e88396..ada9a38e 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -15,6 +15,7 @@ import subprocess as sp import numpy as np import torch from PIL import Image, PngImagePlugin +import piexif import gradio as gr import gradio.utils @@ -113,18 +114,26 @@ def save_files(js_data, images, index): writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"]) filename_base = str(int(time.time() * 1000)) + extension = opts.samples_format.lower() for i, filedata in enumerate(images): - filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + f".{opts.samples_format}" + filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + f".{extension}" filepath = os.path.join(opts.outdir_save, filename) if filedata.startswith("data:image/png;base64,"): filedata = filedata[len("data:image/png;base64,"):] - pnginfo = PngImagePlugin.PngInfo() - pnginfo.add_text('parameters', infotexts[i]) - image = Image.open(io.BytesIO(base64.decodebytes(filedata.encode('utf-8')))) - image.save(filepath, quality=opts.jpeg_quality, pnginfo=pnginfo) + if opts.enable_pnginfo and extension == 'png': + pnginfo = PngImagePlugin.PngInfo() + pnginfo.add_text('parameters', infotexts[i]) + image.save(filepath, pnginfo=pnginfo) + else: + image.save(filepath, quality=opts.jpeg_quality) + + if opts.enable_pnginfo and extension in ("jpg", "jpeg", "webp"): + piexif.insert(piexif.dump({"Exif": { + piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(infotexts[i], encoding="unicode") + }}), filepath) filenames.append(filename) -- cgit v1.2.3