From cd4fcd2f0c38acd97da9a00c492a3501d3d92de4 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sat, 25 Mar 2023 22:01:15 -0700 Subject: [PATCH 01/45] add a add decription button --- javascript/extraNetworks.js | 6 ++++++ style.css | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 253221389..9cde37e5b 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -118,6 +118,12 @@ function popup(contents){ close.title = "Close"; globalPopup.appendChild(close) + var addDescrip = document.createElement('div') + addDescrip.classList.add('global-popup-addDescrip'); + addDescrip.onclick = function(){ alert("add descript you idiot") }; + addDescrip.title = "add descript"; + globalPopup.appendChild(addDescrip) + globalPopupInner = document.createElement('div') globalPopupInner.onclick = function(event){ event.stopPropagation(); return false; }; globalPopupInner.classList.add('global-popup-inner'); diff --git a/style.css b/style.css index 0dcc3e25d..4c1099768 100644 --- a/style.css +++ b/style.css @@ -424,6 +424,20 @@ div.dimensions-tools{ font-size: 32pt; } +.global-popup-addDescrip:before { + content: "+"; +} + +.global-popup-addDescrip{ + position: fixed; + right: 2em; + top: 0; + cursor: pointer; + color: white; + font-size: 32pt; +} + + .global-popup-inner{ display: inline-block; margin: auto; From a6b30069f021391be49089bb5e642e6f2d602fbb Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sun, 26 Mar 2023 15:10:16 -0700 Subject: [PATCH 02/45] semi real solution --- html/extra-networks-card.html | 3 ++- javascript/extraNetworks.js | 23 +++++++++++++++++------ modules/ui_extra_networks.py | 27 +++++++++++++++++++++++++++ style.css | 14 -------------- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/html/extra-networks-card.html b/html/extra-networks-card.html index ef4b613af..df301eb5c 100644 --- a/html/extra-networks-card.html +++ b/html/extra-networks-card.html @@ -4,7 +4,8 @@
diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 9cde37e5b..20007a7ab 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -95,6 +95,23 @@ function saveCardPreview(event, tabname, filename){ event.preventDefault() } +function saveCardDescription(event, tabname, filename,descript){ + var textarea = gradioApp().querySelector("#" + tabname + '_description_filename > label > textarea') + var button = gradioApp().getElementById(tabname + '_save_description') + var description = gradioApp().getElementById(tabname+ '_description_input') + + textarea.value = filename + description.value=descript + updateInput(textarea) + + button.click() + + //alert("add description here!") + + event.stopPropagation() + event.preventDefault() +} + function extraNetworksSearchButton(tabs_id, event){ searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea') button = event.target @@ -118,12 +135,6 @@ function popup(contents){ close.title = "Close"; globalPopup.appendChild(close) - var addDescrip = document.createElement('div') - addDescrip.classList.add('global-popup-addDescrip'); - addDescrip.onclick = function(){ alert("add descript you idiot") }; - addDescrip.title = "add descript"; - globalPopup.appendChild(addDescrip) - globalPopupInner = document.createElement('div') globalPopupInner.onclick = function(event){ event.stopPropagation(); return false; }; globalPopupInner.classList.add('global-popup-inner'); diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index daea03d62..525cd9f3b 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -163,6 +163,7 @@ class ExtraNetworksPage: "name": item["name"], "description": (item.get("description") or ""), "card_clicked": onclick, + "save_card_description": '"' + html.escape(f"""return saveCardDescription(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', "save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', "search_term": item.get("search_term", ""), "metadata_button": metadata_button, @@ -212,6 +213,11 @@ class ExtraNetworksUi: self.button_save_preview = None self.preview_target_filename = None + self.button_save_description = None + self.description_target_filename = None + + self.description_input = None + self.tabname = None @@ -250,6 +256,10 @@ def create_ui(container, button, tabname): ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False) ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) + ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False) + ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False) + ui.description_input = gr.Text(elem_id=tabname+"_description_input") + def toggle_visibility(is_visible): is_visible = not is_visible return is_visible, gr.update(visible=is_visible) @@ -310,3 +320,20 @@ def setup_ui(ui, gallery): outputs=[*ui.pages] ) + def save_description(filenamex,images,filename,descrip): + filename = filename.split('.')[0]+".description.txt" + file1 = open(filename,'w') + print(file1) + file1.write(descrip) + file1.close() + return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] + + ui.button_save_description.click( + fn=save_description, + _js="function(x,y,z){return selected_gallery_index(), y, z]}", + inputs=[ui.description_target_filename, gallery, ui.description_target_filename, ui.description_input], + outputs=[*ui.pages] + ) + + + diff --git a/style.css b/style.css index 4c1099768..0dcc3e25d 100644 --- a/style.css +++ b/style.css @@ -424,20 +424,6 @@ div.dimensions-tools{ font-size: 32pt; } -.global-popup-addDescrip:before { - content: "+"; -} - -.global-popup-addDescrip{ - position: fixed; - right: 2em; - top: 0; - cursor: pointer; - color: white; - font-size: 32pt; -} - - .global-popup-inner{ display: inline-block; margin: auto; From a73f3bf0cfc89cde294b42f5c566017daf4b2ccd Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 30 Mar 2023 23:19:40 -0600 Subject: [PATCH 03/45] Change extras "scale to" to sliders --- scripts/postprocessing_upscale.py | 14 ++++++++++---- style.css | 4 ++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index 11eab31a5..bc43719bd 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -4,8 +4,9 @@ import numpy as np from modules import scripts_postprocessing, shared import gradio as gr -from modules.ui_components import FormRow +from modules.ui_components import FormRow, ToolButton +switch_values_symbol = '\U000021C5' # ⇅ upscale_cache = {} @@ -25,9 +26,12 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: with FormRow(): - upscaling_resize_w = gr.Number(label="Width", value=512, precision=0, elem_id="extras_upscaling_resize_w") - upscaling_resize_h = gr.Number(label="Height", value=512, precision=0, elem_id="extras_upscaling_resize_h") - upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") + with gr.Column(elem_id="upscaling_column_size", scale=4): + upscaling_resize_w = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") + upscaling_resize_h = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_w") + with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"): + upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn") + upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") with FormRow(): extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) @@ -36,6 +40,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") + upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False) tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) @@ -45,6 +50,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): "upscale_to_width": upscaling_resize_w, "upscale_to_height": upscaling_resize_h, "upscale_crop": upscaling_crop, + "upscaling_res_switch_btn": upscaling_res_switch_btn, "upscaler_1_name": extras_upscaler_1, "upscaler_2_name": extras_upscaler_2, "upscaler_2_visibility": extras_upscaler_2_visibility, diff --git a/style.css b/style.css index de16a7f2f..aafc23627 100644 --- a/style.css +++ b/style.css @@ -312,6 +312,10 @@ div.dimensions-tools{ align-content: center; } +div#extras_scale_to_tab div.form{ + flex-direction: row; +} + #mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{ height: 480px !important; max-height: 480px !important; From 69ad46b047678a7a97a152a20e702bac61e37b8b Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Thu, 30 Mar 2023 23:25:39 -0600 Subject: [PATCH 04/45] Import switch_values_symbol --- scripts/postprocessing_upscale.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index bc43719bd..bf27b64d0 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -5,8 +5,7 @@ from modules import scripts_postprocessing, shared import gradio as gr from modules.ui_components import FormRow, ToolButton - -switch_values_symbol = '\U000021C5' # ⇅ +from modules.ui import switch_values_symbol upscale_cache = {} From 3ebdd2afd3769046289880d44bbe1322a832073f Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 31 Mar 2023 00:56:38 -0600 Subject: [PATCH 05/45] Don't return upscaling_res_switch_btn --- scripts/postprocessing_upscale.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index bf27b64d0..e60208ac3 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -49,7 +49,6 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): "upscale_to_width": upscaling_resize_w, "upscale_to_height": upscaling_resize_h, "upscale_crop": upscaling_crop, - "upscaling_res_switch_btn": upscaling_res_switch_btn, "upscaler_1_name": extras_upscaler_1, "upscaler_2_name": extras_upscaler_2, "upscaler_2_visibility": extras_upscaler_2_visibility, From 0263a694c6fae541192a313d5bc7d20eee1e70d2 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Fri, 31 Mar 2023 17:36:32 -0700 Subject: [PATCH 06/45] make code cleaner --- javascript/extraNetworks.js | 4 +--- modules/ui_extra_networks.py | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 20007a7ab..bb2884ea0 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -95,7 +95,7 @@ function saveCardPreview(event, tabname, filename){ event.preventDefault() } -function saveCardDescription(event, tabname, filename,descript){ +function saveCardDescription(event, tabname, filename, descript){ var textarea = gradioApp().querySelector("#" + tabname + '_description_filename > label > textarea') var button = gradioApp().getElementById(tabname + '_save_description') var description = gradioApp().getElementById(tabname+ '_description_input') @@ -106,8 +106,6 @@ function saveCardDescription(event, tabname, filename,descript){ button.click() - //alert("add description here!") - event.stopPropagation() event.preventDefault() } diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 525cd9f3b..6a90281ec 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -215,7 +215,6 @@ class ExtraNetworksUi: self.button_save_description = None self.description_target_filename = None - self.description_input = None self.tabname = None @@ -320,18 +319,21 @@ def setup_ui(ui, gallery): outputs=[*ui.pages] ) - def save_description(filenamex,images,filename,descrip): + def save_description(filename,descrip): filename = filename.split('.')[0]+".description.txt" - file1 = open(filename,'w') - print(file1) - file1.write(descrip) - file1.close() + try: + f = open(filename,'w') + except OSError: + print ("Could not open file to write: " + filename) + with f: + f.write(descrip) + f.close() return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] ui.button_save_description.click( fn=save_description, - _js="function(x,y,z){return selected_gallery_index(), y, z]}", - inputs=[ui.description_target_filename, gallery, ui.description_target_filename, ui.description_input], + _js="function(x,y){return [x,y]}", + inputs=[ui.description_target_filename, ui.description_input], outputs=[*ui.pages] ) From 5d0a673df3fa4ecd1d179c26de9dbee264c04e5a Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Fri, 31 Mar 2023 20:04:17 -0700 Subject: [PATCH 07/45] ui changes --- javascript/extraNetworks.js | 4 +++- modules/ui_extra_networks.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index bb2884ea0..0984ae7ea 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -5,11 +5,13 @@ function setupExtraNetworksForTab(tabname){ var tabs = gradioApp().querySelector('#'+tabname+'_extra_tabs > div') var search = gradioApp().querySelector('#'+tabname+'_extra_search textarea') var refresh = gradioApp().getElementById(tabname+'_extra_refresh') + var descriptInput = gradioApp().getElementById(tabname+ '_description_input') search.classList.add('search') tabs.appendChild(search) tabs.appendChild(refresh) - + tabs.appendChild(descriptInput) + search.addEventListener("input", function(evt){ searchTerm = search.value.toLowerCase() diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 441b4bc05..22708e1e7 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -253,13 +253,14 @@ def create_ui(container, button, tabname): filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False) button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh") + ui.description_input = gr.TextArea('', show_label=False, elem_id=tabname+"_description_input", placeholder="Save/Replace Extra Network Description...", lines=2) ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False) ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False) ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False) - ui.description_input = gr.Text(elem_id=tabname+"_description_input") + def toggle_visibility(is_visible): is_visible = not is_visible From 62a9a9cbe9e0e734de836ed8dc9e873e74920d5a Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Fri, 31 Mar 2023 20:29:30 -0700 Subject: [PATCH 08/45] don't overwrite if description is empty --- modules/ui_extra_networks.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 22708e1e7..9cbd6d3df 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -327,16 +327,18 @@ def setup_ui(ui, gallery): inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename], outputs=[*ui.pages] ) - + + # write description to a file def save_description(filename,descrip): filename = filename.split('.')[0]+".description.txt" - try: - f = open(filename,'w') - except OSError: - print ("Could not open file to write: " + filename) - with f: - f.write(descrip) - f.close() + if descrip != "": + try: + f = open(filename,'w') + except OSError: + print ("Could not open file to write: " + filename) + with f: + f.write(descrip) + f.close() return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] ui.button_save_description.click( From bd8d3f169206003f7ab50118e2188c99644ec86a Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sat, 1 Apr 2023 18:16:23 -0700 Subject: [PATCH 09/45] load description to input box by clicking on card(except checkpoint) --- javascript/extraNetworks.js | 8 +++++++- modules/ui_extra_networks.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 0984ae7ea..dd99b6664 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -74,9 +74,15 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text){ return false } -function cardClicked(tabname, textToAdd, allowNegativePrompt){ +function cardClicked(tabname, textToAdd, allowNegativePrompt, descriptionText){ var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea") + var description_textarea = gradioApp().querySelector("#" + tabname+ '_description_input > label > textarea') + + description_textarea.value = descriptionText + + updateInput(description_textarea) + if(! tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)){ textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd } diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 9cbd6d3df..dc8c7d2a5 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -147,7 +147,7 @@ class ExtraNetworksPage: onclick = item.get("onclick", None) if onclick is None: - onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"' + onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"}, {json.dumps(item["description"])})""") + '"' height = f"height: {shared.opts.extra_networks_card_height}px;" if shared.opts.extra_networks_card_height else '' width = f"width: {shared.opts.extra_networks_card_width}px;" if shared.opts.extra_networks_card_width else '' From 26d0c124442ab750ef0d2ed6b962d79dd4e64eb3 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sat, 1 Apr 2023 18:54:36 -0700 Subject: [PATCH 10/45] Revert "load description to input box by clicking on card(except checkpoint)" This reverts commit bd8d3f169206003f7ab50118e2188c99644ec86a. --- javascript/extraNetworks.js | 8 +------- modules/ui_extra_networks.py | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index dd99b6664..0984ae7ea 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -74,15 +74,9 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text){ return false } -function cardClicked(tabname, textToAdd, allowNegativePrompt, descriptionText){ +function cardClicked(tabname, textToAdd, allowNegativePrompt){ var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea") - var description_textarea = gradioApp().querySelector("#" + tabname+ '_description_input > label > textarea') - - description_textarea.value = descriptionText - - updateInput(description_textarea) - if(! tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)){ textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd } diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index dc8c7d2a5..9cbd6d3df 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -147,7 +147,7 @@ class ExtraNetworksPage: onclick = item.get("onclick", None) if onclick is None: - onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"}, {json.dumps(item["description"])})""") + '"' + onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"' height = f"height: {shared.opts.extra_networks_card_height}px;" if shared.opts.extra_networks_card_height else '' width = f"width: {shared.opts.extra_networks_card_width}px;" if shared.opts.extra_networks_card_width else '' From 54e755528f93d50ebbe8c8a248a118835b7af1d0 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sat, 1 Apr 2023 19:47:45 -0700 Subject: [PATCH 11/45] add a button to read descript into decription input box --- html/extra-networks-card.html | 1 + javascript/extraNetworks.js | 16 ++++++++++++++++ modules/ui_extra_networks.py | 3 +++ 3 files changed, 20 insertions(+) diff --git a/html/extra-networks-card.html b/html/extra-networks-card.html index df301eb5c..cb4720f14 100644 --- a/html/extra-networks-card.html +++ b/html/extra-networks-card.html @@ -6,6 +6,7 @@
diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 0984ae7ea..3955c7f9f 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -112,6 +112,22 @@ function saveCardDescription(event, tabname, filename, descript){ event.preventDefault() } +function readCardDescription(event, tabname, filename, descript){ + var textarea = gradioApp().querySelector("#" + tabname + '_description_filename > label > textarea') + var description_textarea = gradioApp().querySelector("#" + tabname+ '_description_input > label > textarea') + var button = gradioApp().getElementById(tabname + '_read_description') + + textarea.value = filename + description_textarea.value = descript + + updateInput(textarea) + updateInput(description_textarea) + button.click() + + event.stopPropagation() + event.preventDefault() +} + function extraNetworksSearchButton(tabs_id, event){ searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea') button = event.target diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 9cbd6d3df..c6fb8f23a 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -167,6 +167,7 @@ class ExtraNetworksPage: "card_clicked": onclick, "save_card_description": '"' + html.escape(f"""return saveCardDescription(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', "save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', + "read_card_description": '"' + html.escape(f"""return readCardDescription(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])}, {json.dumps(item["description"])})""") + '"', "search_term": item.get("search_term", ""), "metadata_button": metadata_button, } @@ -216,6 +217,7 @@ class ExtraNetworksUi: self.preview_target_filename = None self.button_save_description = None + self.button_read_description = None self.description_target_filename = None self.description_input = None @@ -259,6 +261,7 @@ def create_ui(container, button, tabname): ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False) + ui.button_read_description = gr.Button('Save description', elem_id=tabname+"_read_description", visible=False) ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False) From 0587f6c5c686f57b88a6012ccfcafb5e555b1d94 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sun, 16 Apr 2023 14:10:35 -0700 Subject: [PATCH 12/45] fix bug when there are dots in the file name --- modules/ui_extra_networks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index c6fb8f23a..ef619937e 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -333,7 +333,8 @@ def setup_ui(ui, gallery): # write description to a file def save_description(filename,descrip): - filename = filename.split('.')[0]+".description.txt" + lastDotIndex = filename.rindex('.') + filename = filename[0:lastDotIndex]+".description.txt" if descrip != "": try: f = open(filename,'w') From b1050a3dcb0e993dcbfc67015593834a85c369e8 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Sun, 23 Apr 2023 21:00:22 -0700 Subject: [PATCH 13/45] fix typo --- modules/ui_extra_networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index ef619937e..f54a4ef70 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -261,7 +261,7 @@ def create_ui(container, button, tabname): ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False) - ui.button_read_description = gr.Button('Save description', elem_id=tabname+"_read_description", visible=False) + ui.button_read_description = gr.Button('Read description', elem_id=tabname+"_read_description", visible=False) ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False) From 93b0de7e599453027ad7cab6266b42920ebc1250 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 26 Apr 2023 09:02:32 -0400 Subject: [PATCH 14/45] update rollback vae --- extensions-builtin/sd-webui-controlnet | 2 +- modules/scripts.py | 41 +++++++++++++------------- modules/sd_vae.py | 2 +- modules/ui.py | 30 +++++++++---------- setup.py | 3 +- webui.py | 7 +++-- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index a07f6e8a1..93b0f9e1b 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit a07f6e8a1a4d4d6ced366aec30fd8244f1fde26d +Subproject commit 93b0f9e1b7cc246165666b7b307bc8243db2c3f4 diff --git a/modules/scripts.py b/modules/scripts.py index 2cc744568..5e844215d 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -2,7 +2,6 @@ import os import re import sys from collections import namedtuple -from rich import print # pylint: disable=redefined-builtin import gradio as gr from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors @@ -47,9 +46,9 @@ class Script: Values of those returned components will be passed to run() and process() functions. """ - pass + pass # pylint: disable=unnecessary-pass - def show(self, is_img2img): + def show(self, is_img2img): # pylint: disable=unused-argument """ is_img2img is True if this function is called for the img2img interface, and Fasle otherwise @@ -72,7 +71,7 @@ class Script: args contains all values returned by components from ui() """ - pass + pass # pylint: disable=unnecessary-pass def process(self, p, *args): """ @@ -81,7 +80,7 @@ class Script: args contains all values returned by components from ui() """ - pass + pass # pylint: disable=unnecessary-pass def before_process_batch(self, p, *args, **kwargs): """ @@ -95,7 +94,7 @@ class Script: - subseeds - list of subseeds for current batch """ - pass + pass # pylint: disable=unnecessary-pass def process_batch(self, p, *args, **kwargs): """ @@ -108,7 +107,7 @@ class Script: - subseeds - list of subseeds for current batch """ - pass + pass # pylint: disable=unnecessary-pass def postprocess_batch(self, p, *args, **kwargs): """ @@ -119,14 +118,14 @@ class Script: - images - torch tensor with all generated images, with values ranging from 0 to 1; """ - pass + pass # pylint: disable=unnecessary-pass def postprocess_image(self, p, pp: PostprocessImageArgs, *args): """ Called for every image after it has been generated. """ - pass + pass # pylint: disable=unnecessary-pass def postprocess(self, p, processed, *args): """ @@ -134,7 +133,7 @@ class Script: args contains all values returned by components from ui() """ - pass + pass # pylint: disable=unnecessary-pass def before_component(self, component, **kwargs): """ @@ -144,14 +143,14 @@ class Script: You can return created components in the ui() function to add them to the list of arguments for your processing functions """ - pass + pass # pylint: disable=unnecessary-pass def after_component(self, component, **kwargs): """ Called after a component is created. Same as above. """ - pass + pass # pylint: disable=unnecessary-pass def describe(self): """unused""" @@ -288,6 +287,7 @@ class ScriptRunner: self.titles = [] self.infotext_fields = [] self.paste_field_names = [] + self.script_load_ctr = 0 def initialize_scripts(self, is_img2img): from modules import scripts_auto_postprocessing @@ -298,7 +298,7 @@ class ScriptRunner: auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data() - for script_class, path, basedir, script_module in auto_processing_scripts + scripts_data: + for script_class, path, _basedir, _script_module in auto_processing_scripts + scripts_data: script = script_class() script.filename = path script.is_txt2img = not is_img2img @@ -380,7 +380,6 @@ class ScriptRunner: outputs=[script.group for script in self.selectable_scripts] ) - self.script_load_ctr = 0 def onload_script_visibility(params): title = params.get('Script', None) if title: @@ -416,12 +415,14 @@ class ScriptRunner: def process(self, p): for script in self.alwayson_scripts: + # from rich import print try: - if p.script_args[0] == 'enabled': - return + # print(f'HERE: {script.filename} from {script.args_from} to {script.args_to} args {p.script_args}') script_args = p.script_args[script.args_from:script.args_to] script.process(p, *script_args) except Exception as e: + # from modules.errors import console + # console.print_exception(show_locals=True, max_frames=10, extra_lines=2, theme="ansi_dark", word_wrap=False, width=min([console.width, 200])) errors.display(e, f'Running script process: {script.filename}') def before_process_batch(self, p, **kwargs): @@ -495,7 +496,7 @@ class ScriptRunner: module = script_loading.load_module(script.filename) cache[filename] = module - for key, script_class in module.__dict__.items(): + for _key, script_class in module.__dict__.items(): if type(script_class) == type and issubclass(script_class, Script): self.scripts[si] = script_class() self.scripts[si].filename = filename @@ -516,7 +517,7 @@ def reload_script_body_only(): def reload_scripts(): - global scripts_txt2img, scripts_img2img, scripts_postproc + global scripts_txt2img, scripts_img2img, scripts_postproc # pylint: disable=global-statement load_scripts() @@ -547,7 +548,7 @@ def IOComponent_init(self, *args, **kwargs): script_callbacks.before_component_callback(self, **kwargs) - res = original_IOComponent_init(self, *args, **kwargs) + res = original_IOComponent_init(self, *args, **kwargs) # pylint: disable=assignment-from-no-return add_classes_to_gradio_component(self) @@ -564,7 +565,7 @@ gr.components.IOComponent.__init__ = IOComponent_init def BlockContext_init(self, *args, **kwargs): - res = original_BlockContext_init(self, *args, **kwargs) + res = original_BlockContext_init(self, *args, **kwargs) # pylint: disable=assignment-from-no-return add_classes_to_gradio_component(self) diff --git a/modules/sd_vae.py b/modules/sd_vae.py index 237dafa4f..e5c544487 100644 --- a/modules/sd_vae.py +++ b/modules/sd_vae.py @@ -100,7 +100,7 @@ def resolve_vae(checkpoint_file): vae_near_checkpoint = find_vae_near_checkpoint(checkpoint_file) if vae_near_checkpoint is not None and (shared.opts.sd_vae_as_default): return vae_near_checkpoint, 'near checkpoint' - + if is_automatic: for named_vae_location in [os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.pt"), os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.ckpt"), os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.safetensors")]: if os.path.isfile(named_vae_location): diff --git a/modules/ui.py b/modules/ui.py index 83249d39c..0e8bd192a 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -302,7 +302,7 @@ def create_toprow(is_img2img): return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, prompt_style_apply, save_style, paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button -def setup_progressbar(*args, **kwargs): +def setup_progressbar(*args, **kwargs): # pylint: disable=unused-argument pass @@ -387,7 +387,7 @@ def get_value_for_setting(key): return gr.update(value=value, **args) -def create_override_settings_dropdown(tabname, row): +def create_override_settings_dropdown(tabname, row): # pylint: disable=unused-argument dropdown = gr.Dropdown([], label="Override settings", visible=False, elem_id=f"{tabname}_override_settings", multiselect=True) dropdown.change( @@ -400,8 +400,8 @@ def create_override_settings_dropdown(tabname, row): def create_ui(): - import modules.img2img - import modules.txt2img + import modules.img2img # pylint: disable=redefined-outer-name + import modules.txt2img # pylint: disable=redefined-outer-name reload_javascript() @@ -416,9 +416,9 @@ def create_ui(): dummy_component = gr.Label(visible=False) txt_prompt_img = gr.File(label="", elem_id="txt2img_prompt_image", file_count="single", type="binary", visible=False) - with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks: + with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks_ui: from modules import ui_extra_networks - extra_networks_ui = ui_extra_networks.create_ui(extra_networks, extra_networks_button, 'txt2img') + extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'txt2img') with gr.Row().style(equal_height=False): with gr.Column(variant='compact', elem_id="txt2img_settings"): @@ -478,14 +478,14 @@ def create_ui(): custom_inputs = modules.scripts.scripts_txt2img.setup_ui() hr_resolution_preview_inputs = [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y] - for input in hr_resolution_preview_inputs: - input.change( + for preview_input in hr_resolution_preview_inputs: + preview_input.change( fn=calc_resolution_hires, inputs=hr_resolution_preview_inputs, outputs=[hr_final_resolution], show_progress=False, ) - input.change( + preview_input.change( None, _js="onCalcResolutionHires", inputs=hr_resolution_preview_inputs, @@ -614,9 +614,9 @@ def create_ui(): img2img_prompt_img = gr.File(label="", elem_id="img2img_prompt_image", file_count="single", type="binary", visible=False) - with FormRow(variant='compact', elem_id="img2img_extra_networks", visible=False) as extra_networks: + with FormRow(variant='compact', elem_id="img2img_extra_networks", visible=False) as extra_networks_ui: from modules import ui_extra_networks - extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks, extra_networks_button, 'img2img') + extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'img2img') with FormRow().style(equal_height=False): with gr.Column(variant='compact', elem_id="img2img_settings"): @@ -767,7 +767,7 @@ def create_ui(): for i, elem in enumerate([tab_img2img, tab_sketch, tab_inpaint, tab_inpaint_color, tab_inpaint_upload, tab_batch]): elem.select( - fn=lambda tab=i: select_img2img_tab(tab), + fn=lambda tab=i: select_img2img_tab(tab), # pylint: disable=cell-var-from-loop inputs=[], outputs=[inpaint_controls, mask_alpha], ) @@ -1334,7 +1334,7 @@ def create_ui(): elif t == bool: comp = gr.Checkbox else: - raise Exception(f'bad options item type: {str(t)} for key {key}') + raise ValueError(f'bad options item type: {str(t)} for key {key}') elem_id = "setting_"+key @@ -1433,7 +1433,7 @@ def create_ui(): current_tab.__exit__() request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications", visible=False) - show_all_pages = gr.Button(value="Show all pages", variant='primary', elem_id="settings_show_all_pages") + _show_all_pages = gr.Button(value="Show all pages", variant='primary', elem_id="settings_show_all_pages") with gr.TabItem("Licenses"): gr.HTML(shared.html("licenses.html"), elem_id="licenses") @@ -1507,7 +1507,7 @@ def create_ui(): parameters_copypaste.connect_paste_params_buttons() - with gr.Tabs(elem_id="tabs") as tabs: + with gr.Tabs(elem_id="tabs") as _tabs: for interface, label, ifid in interfaces: if label in shared.opts.hidden_tabs: continue diff --git a/setup.py b/setup.py index 412def92c..8d4099912 100644 --- a/setup.py +++ b/setup.py @@ -377,7 +377,8 @@ def set_environment(): os.environ.setdefault('GRADIO_ANALYTICS_ENABLED', 'False') os.environ.setdefault('SAFETENSORS_FAST_GPU', '1') os.environ.setdefault('NUMEXPR_MAX_THREADS', '16') - os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1') + if sys.platform == 'darwin': + os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1') def check_extensions(): diff --git a/webui.py b/webui.py index ac097efc0..a0d8f81b8 100644 --- a/webui.py +++ b/webui.py @@ -66,10 +66,13 @@ else: def check_rollback_vae(): if shared.cmd_opts.rollback_vae: - if not torch.__version__.startswith('2.1'): + if not torch.cuda.is_available(): + print("Rollback VAE functionality requires CUDA support") + shared.cmd_opts.rollback_vae = False + elif not torch.__version__.startswith('2.1'): print("Rollback VAE functionality requires Torch 2.1 or higher") shared.cmd_opts.rollback_vae = False - if 0 < torch.cuda.get_device_capability()[0] < 8: + elif 0 < torch.cuda.get_device_capability()[0] < 8: print('Rollback VAE functionality device capabilities not met') shared.cmd_opts.rollback_vae = False From c79fce182ca27021ac744d77eea5950e8c9aa34d Mon Sep 17 00:00:00 2001 From: db <962888866@qq.com> Date: Wed, 26 Apr 2023 21:27:28 +0800 Subject: [PATCH 15/45] support for macOS. --- modules/shared.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/shared.py b/modules/shared.py index 6f362a7ca..b234d2d2a 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -320,12 +320,23 @@ options_templates.update(options_section(('saving-paths', "Image Paths"), { "outdir_save": OptionInfo("outputs/save", "Directory for saving images using the Save button", component_args=hide_dirs), })) +cuda_dtype = OptionInfo("FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}) +no_half = OptionInfo(False, "Use full precision for model (--no-half)") +no_half_vae = OptionInfo(False, "Use full precision for VAE (--no-half-vae)") +upcast_sampling = OptionInfo(False, + "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory") +# support for macOS. +if sys.platform == "darwin": + cuda_dtype.default = "FP32" + upcast_sampling.default = True + + options_templates.update(options_section(('cuda', "CUDA Settings"), { "precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}), - "cuda_dtype": OptionInfo("FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}), - "no_half": OptionInfo(False, "Use full precision for model (--no-half)"), - "no_half_vae": OptionInfo(False, "Use full precision for VAE (--no-half-vae)"), - "upcast_sampling": OptionInfo(False, "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory"), + "cuda_dtype": cuda_dtype, + "no_half": no_half, + "no_half_vae": no_half_vae, + "upcast_sampling": upcast_sampling, "disable_nan_check": OptionInfo(True, "Do not check if produced images/latent spaces have NaN values"), "rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"), "opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "), From 8ee0b47f518811caf06fb8ceb1ddf6d07f2ed2af Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 26 Apr 2023 13:33:08 -0400 Subject: [PATCH 16/45] cleanup scripts --- modules/lora | 2 +- modules/scripts.py | 95 ++-------------------------------------------- 2 files changed, 5 insertions(+), 92 deletions(-) diff --git a/modules/lora b/modules/lora index ac4935bf7..18f171d88 160000 --- a/modules/lora +++ b/modules/lora @@ -1 +1 @@ -Subproject commit ac4935bf79942f966d7b6578e8fbb9ee5f12d4ad +Subproject commit 18f171d885d4c870bd1c0656f7247e9649df62fd diff --git a/modules/scripts.py b/modules/scripts.py index 5e844215d..3427f4d9b 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -18,7 +18,6 @@ class Script: args_from = None args_to = None alwayson = False - is_txt2img = False is_img2img = False @@ -37,7 +36,6 @@ class Script: def title(self): """this function should return the title of the script. This is what will be displayed in the dropdown menu.""" - raise NotImplementedError() def ui(self, is_img2img): @@ -45,19 +43,16 @@ class Script: The return value should be an array of all components that are used in processing. Values of those returned components will be passed to run() and process() functions. """ - pass # pylint: disable=unnecessary-pass def show(self, is_img2img): # pylint: disable=unused-argument """ is_img2img is True if this function is called for the img2img interface, and Fasle otherwise - This function should return: - False if the script should not be shown in UI at all - True if the script should be shown in UI if it's selected in the scripts dropdown - script.AlwaysVisible if the script should be shown in UI at all times """ - return True def run(self, p, *args): @@ -65,12 +60,9 @@ class Script: This function is called if the script has been selected in the script dropdown. It must do all processing and return the Processed object with results, same as one returned by processing.process_images. - Usually the processing is done by calling the processing.process_images function. - args contains all values returned by components from ui() """ - pass # pylint: disable=unnecessary-pass def process(self, p, *args): @@ -79,52 +71,44 @@ class Script: You can modify the processing object (p) here, inject hooks, etc. args contains all values returned by components from ui() """ - pass # pylint: disable=unnecessary-pass def before_process_batch(self, p, *args, **kwargs): """ Called before extra networks are parsed from the prompt, so you can add new extra network keywords to the prompt with this callback. - **kwargs will have those items: - batch_number - index of current batch, from 0 to number of batches-1 - prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things - seeds - list of seeds for current batch - subseeds - list of subseeds for current batch """ - pass # pylint: disable=unnecessary-pass def process_batch(self, p, *args, **kwargs): """ Same as process(), but called for every batch. - **kwargs will have those items: - batch_number - index of current batch, from 0 to number of batches-1 - prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things - seeds - list of seeds for current batch - subseeds - list of subseeds for current batch """ - pass # pylint: disable=unnecessary-pass def postprocess_batch(self, p, *args, **kwargs): """ Same as process_batch(), but called for every batch after it has been generated. - **kwargs will have same items as process_batch, and also: - batch_number - index of current batch, from 0 to number of batches-1 - images - torch tensor with all generated images, with values ranging from 0 to 1; """ - pass # pylint: disable=unnecessary-pass def postprocess_image(self, p, pp: PostprocessImageArgs, *args): """ Called for every image after it has been generated. """ - pass # pylint: disable=unnecessary-pass def postprocess(self, p, processed, *args): @@ -132,7 +116,6 @@ class Script: This function is called after processing ends for AlwaysVisible scripts. args contains all values returned by components from ui() """ - pass # pylint: disable=unnecessary-pass def before_component(self, component, **kwargs): @@ -142,14 +125,12 @@ class Script: This can be useful to inject your own components somewhere in the middle of vanilla UI. You can return created components in the ui() function to add them to the list of arguments for your processing functions """ - pass # pylint: disable=unnecessary-pass def after_component(self, component, **kwargs): """ Called after a component is created. Same as above. """ - pass # pylint: disable=unnecessary-pass def describe(self): @@ -158,11 +139,9 @@ class Script: def elem_id(self, item_id): """helper function to generate id for a HTML element, constructs final id out of script name, tab and user-supplied item_id""" - need_tabname = self.show(True) == self.show(False) tabname = ('img2img' if self.is_img2img else 'txt2txt') + "_" if need_tabname else "" title = re.sub(r'[^a-z_0-9]', '', re.sub(r'\s', '_', self.title().lower())) - return f'script_{tabname}{title}_{item_id}' @@ -178,7 +157,6 @@ def basedir(): ScriptFile = namedtuple("ScriptFile", ["basedir", "filename", "path", "priority"]) - scripts_data = [] postprocessing_scripts_data = [] ScriptClassData = namedtuple("ScriptClassData", ["script_class", "path", "basedir", "module"]) @@ -186,16 +164,13 @@ ScriptClassData = namedtuple("ScriptClassData", ["script_class", "path", "basedi def list_scripts(scriptdirname, extension): tmp_list = [] - base = os.path.join(paths.script_path, scriptdirname) if os.path.exists(base): for filename in sorted(os.listdir(base)): tmp_list.append(ScriptFile(paths.script_path, filename, os.path.join(base, filename), '50')) - for ext in extensions.active(): tmp_list += ext.list_files(scriptdirname, extension) - - scripts_list = [] + priority_list = [] for script in tmp_list: if os.path.splitext(script.path)[1].lower() == extension and os.path.isfile(script.path): if script.basedir == paths.script_path: @@ -213,25 +188,20 @@ def list_scripts(scriptdirname, extension): priority = priority + str(f.read().strip()) else: priority = priority + script.priority - scripts_list.append(ScriptFile(script.basedir, script.filename, script.path, priority)) - - priority_sort = sorted(scripts_list, key=lambda item: item.priority + item.path.lower(), reverse=False) + priority_list.append(ScriptFile(script.basedir, script.filename, script.path, priority)) + priority_sort = sorted(priority_list, key=lambda item: item.priority + item.path.lower(), reverse=False) return priority_sort def list_files_with_name(filename): res = [] - dirs = [paths.script_path] + [ext.path for ext in extensions.active()] - for dirpath in dirs: if not os.path.isdir(dirpath): continue - path = os.path.join(dirpath, filename) if os.path.isfile(path): res.append(path) - return res @@ -240,16 +210,13 @@ def load_scripts(): scripts_data.clear() postprocessing_scripts_data.clear() script_callbacks.clear_callbacks() - scripts_list = list_scripts("scripts", ".py") - syspath = sys.path def register_scripts_from_module(module): for _key, script_class in module.__dict__.items(): if type(script_class) != type: continue - if issubclass(script_class, Script): scripts_data.append(ScriptClassData(script_class, scriptfile.path, scriptfile.basedir, module)) elif issubclass(script_class, scripts_postprocessing.ScriptPostprocessing): @@ -275,7 +242,6 @@ def wrap_call(func, filename, funcname, *args, default=None, **kwargs): return res except Exception as e: errors.display(e, f'Calling script: {filename}/{funcname}') - return default @@ -295,7 +261,6 @@ class ScriptRunner: self.scripts.clear() self.alwayson_scripts.clear() self.selectable_scripts.clear() - auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data() for script_class, path, _basedir, _script_module in auto_processing_scripts + scripts_data: @@ -303,42 +268,32 @@ class ScriptRunner: script.filename = path script.is_txt2img = not is_img2img script.is_img2img = is_img2img - visibility = script.show(script.is_img2img) - if visibility == AlwaysVisible: self.scripts.append(script) self.alwayson_scripts.append(script) script.alwayson = True - elif visibility: self.scripts.append(script) self.selectable_scripts.append(script) def setup_ui(self): self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts] - inputs = [None] inputs_alwayson = [True] def create_script_ui(script, inputs, inputs_alwayson): script.args_from = len(inputs) script.args_to = len(inputs) - controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img) - if controls is None: return - for control in controls: control.custom_script_source = os.path.basename(script.filename) - if script.infotext_fields is not None: self.infotext_fields += script.infotext_fields - if script.paste_field_names is not None: self.paste_field_names += script.paste_field_names - inputs += controls inputs_alwayson += [script.alwayson for _ in controls] script.args_to = len(inputs) @@ -348,37 +303,26 @@ class ScriptRunner: create_script_ui(script, inputs, inputs_alwayson) script.group = group - dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index") inputs[0] = dropdown - for script in self.selectable_scripts: with gr.Group(visible=False) as group: create_script_ui(script, inputs, inputs_alwayson) - script.group = group def select_script(script_index): selected_script = self.selectable_scripts[script_index - 1] if script_index>0 else None - return [gr.update(visible=selected_script == s) for s in self.selectable_scripts] def init_field(title): """called when an initial value is set from ui-config.json to show script's UI components""" - if title == 'None': return - script_index = self.titles.index(title) self.selectable_scripts[script_index].group.visible = True dropdown.init_field = init_field - - dropdown.change( - fn=select_script, - inputs=[dropdown], - outputs=[script.group for script in self.selectable_scripts] - ) + dropdown.change(fn=select_script, inputs=[dropdown], outputs=[script.group for script in self.selectable_scripts]) def onload_script_visibility(params): title = params.get('Script', None) @@ -392,37 +336,26 @@ class ScriptRunner: self.infotext_fields.append( (dropdown, lambda x: gr.update(value=x.get('Script', 'None'))) ) self.infotext_fields.extend( [(script.group, onload_script_visibility) for script in self.selectable_scripts] ) - return inputs def run(self, p, *args): script_index = args[0] - if script_index == 0: return None - script = self.selectable_scripts[script_index-1] - if script is None: return None - script_args = args[script.args_from:script.args_to] processed = script.run(p, *script_args) - shared.total_tqdm.clear() - return processed def process(self, p): for script in self.alwayson_scripts: - # from rich import print try: - # print(f'HERE: {script.filename} from {script.args_from} to {script.args_to} args {p.script_args}') script_args = p.script_args[script.args_from:script.args_to] script.process(p, *script_args) except Exception as e: - # from modules.errors import console - # console.print_exception(show_locals=True, max_frames=10, extra_lines=2, theme="ansi_dark", word_wrap=False, width=min([console.width, 200])) errors.display(e, f'Running script process: {script.filename}') def before_process_batch(self, p, **kwargs): @@ -436,8 +369,6 @@ class ScriptRunner: def process_batch(self, p, **kwargs): for script in self.alwayson_scripts: try: - if p.script_args[0] == 'enabled': - return script_args = p.script_args[script.args_from:script.args_to] script.process_batch(p, *script_args, **kwargs) except Exception as e: @@ -446,8 +377,6 @@ class ScriptRunner: def postprocess(self, p, processed): for script in self.alwayson_scripts: try: - if p.script_args[0] == 'enabled': - return script_args = p.script_args[script.args_from:script.args_to] script.postprocess(p, processed, *script_args) except Exception as e: @@ -456,8 +385,6 @@ class ScriptRunner: def postprocess_batch(self, p, images, **kwargs): for script in self.alwayson_scripts: try: - if p.script_args[0] == 'enabled': - return script_args = p.script_args[script.args_from:script.args_to] script.postprocess_batch(p, *script_args, images=images, **kwargs) except Exception as e: @@ -490,12 +417,10 @@ class ScriptRunner: args_from = script.args_from args_to = script.args_to filename = script.filename - module = cache.get(filename, None) if module is None: module = script_loading.load_module(script.filename) cache[filename] = module - for _key, script_class in module.__dict__.items(): if type(script_class) == type and issubclass(script_class, Script): self.scripts[si] = script_class() @@ -518,9 +443,7 @@ def reload_script_body_only(): def reload_scripts(): global scripts_txt2img, scripts_img2img, scripts_postproc # pylint: disable=global-statement - load_scripts() - scripts_txt2img = ScriptRunner() scripts_img2img = ScriptRunner() scripts_postproc = scripts_postprocessing.ScriptPostprocessingRunner() @@ -536,27 +459,19 @@ def add_classes_to_gradio_component(comp): if elem_classes is None: elem_classes = [] comp.elem_classes = ["gradio-" + comp.get_block_name(), *(elem_classes)] - if getattr(comp, 'multiselect', False): comp.elem_classes.append('multiselect') - def IOComponent_init(self, *args, **kwargs): if scripts_current is not None: scripts_current.before_component(self, **kwargs) - script_callbacks.before_component_callback(self, **kwargs) - res = original_IOComponent_init(self, *args, **kwargs) # pylint: disable=assignment-from-no-return - add_classes_to_gradio_component(self) - script_callbacks.after_component_callback(self, **kwargs) - if scripts_current is not None: scripts_current.after_component(self, **kwargs) - return res @@ -566,9 +481,7 @@ gr.components.IOComponent.__init__ = IOComponent_init def BlockContext_init(self, *args, **kwargs): res = original_BlockContext_init(self, *args, **kwargs) # pylint: disable=assignment-from-no-return - add_classes_to_gradio_component(self) - return res From 5ce8ef68a7bea173ef6caa7313d025c7b9cfd90b Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 26 Apr 2023 14:48:12 -0400 Subject: [PATCH 17/45] increase thumnails --- javascript/black-orange.css | 2 ++ modules/scripts.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/javascript/black-orange.css b/javascript/black-orange.css index 1bcf4c7fa..5ecdedac1 100644 --- a/javascript/black-orange.css +++ b/javascript/black-orange.css @@ -317,4 +317,6 @@ svg.feather.feather-image, .feather .feather-image { display: none } --button-small-text-size: var(--text-md); --button-small-text-weight: 400; --button-transition: none; + --size-9: 64px; + --size-14: 64px; } diff --git a/modules/scripts.py b/modules/scripts.py index 3427f4d9b..55418dc5b 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -350,11 +350,11 @@ class ScriptRunner: shared.total_tqdm.clear() return processed - def process(self, p): + def process(self, p, **kwargs): for script in self.alwayson_scripts: try: script_args = p.script_args[script.args_from:script.args_to] - script.process(p, *script_args) + script.process(p, *script_args, **kwargs) except Exception as e: errors.display(e, f'Running script process: {script.filename}') From 1bdfeb3114b87bc317933f17b73c24a07a90e9d2 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 26 Apr 2023 15:09:30 -0400 Subject: [PATCH 18/45] fix geninfo pretty print --- modules/ui_postprocessing.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/ui_postprocessing.py b/modules/ui_postprocessing.py index 243631d88..c5ddbfae5 100644 --- a/modules/ui_postprocessing.py +++ b/modules/ui_postprocessing.py @@ -41,8 +41,14 @@ def create_ui(): for tabname, button in buttons.items(): parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(paste_button=button, tabname=tabname, source_text_component=generation_info, source_image_component=extras_image)) - def pretty_geninfo(generation_info): - return generation_info.replace(', ', '\n') + def pretty_geninfo(generation_info: str): + if generation_info is None: + return '' + sections = generation_info.split('Steps:') + if len(sections) > 1: + param = sections[0].strip() + '\nSteps:' + sections[1].strip().replace(', ', '\n') + return param + return generation_info tab_single.select(fn=lambda: 0, inputs=[], outputs=[tab_index]) tab_batch.select(fn=lambda: 1, inputs=[], outputs=[tab_index]) From e83708284a6103c15532e46578a996b5a3f35869 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 26 Apr 2023 15:54:32 -0400 Subject: [PATCH 19/45] add shared.url --- TODO.md | 1 + javascript/dragdrop.js | 51 +++++++------------------- modules/scripts_auto_postprocessing.py | 4 -- modules/shared.py | 1 + modules/ui.py | 25 ------------- 5 files changed, 16 insertions(+), 66 deletions(-) diff --git a/TODO.md b/TODO.md index 2a6b118d3..ed0b7c875 100644 --- a/TODO.md +++ b/TODO.md @@ -21,6 +21,7 @@ Stuff to be added... - Stream-load models as option for slow storage - AMD optimizations - Apple optimizations +- Support multiple models locations ## Investigate diff --git a/javascript/dragdrop.js b/javascript/dragdrop.js index e4cd41ec4..9015e4bd5 100644 --- a/javascript/dragdrop.js +++ b/javascript/dragdrop.js @@ -5,17 +5,13 @@ function isValidImageList( files ) { } function dropReplaceImage( imgWrap, files ) { - if ( ! isValidImageList( files ) ) { - return; - } - + if (!isValidImageList(files)) return; const tmpFile = files[0]; - imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click(); const callback = () => { const fileInput = imgWrap.querySelector('input[type="file"]'); - if ( fileInput ) { - if ( files.length === 0 ) { + if (fileInput) { + if (files.length === 0) { files = new DataTransfer(); files.items.add(tmpFile); fileInput.files = files.files; @@ -26,7 +22,7 @@ function dropReplaceImage( imgWrap, files ) { } }; - if ( imgWrap.closest('#pnginfo_image') ) { + if (imgWrap.closest('#pnginfo_image')) { // special treatment for PNG Info tab, wait for fetch request to finish const oldFetch = window.fetch; window.fetch = async (input, options) => { @@ -44,16 +40,14 @@ function dropReplaceImage( imgWrap, files ) { return response; }; } else { - window.requestAnimationFrame( () => callback() ); + window.requestAnimationFrame(() => callback()); } } window.document.addEventListener('dragover', e => { const target = e.composedPath()[0]; const imgWrap = target.closest('[data-testid="image"]'); - if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) { - return; - } + if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) return; e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; @@ -62,37 +56,20 @@ window.document.addEventListener('dragover', e => { window.document.addEventListener('drop', e => { const target = e.composedPath()[0]; if (!target.placeholder) return; - if (target.placeholder.indexOf("Prompt") == -1) { - return; - } + if (target.placeholder.indexOf("Prompt") == -1) return; const imgWrap = target.closest('[data-testid="image"]'); - if ( !imgWrap ) { - return; - } + if (!imgWrap) return; e.stopPropagation(); e.preventDefault(); const files = e.dataTransfer.files; - dropReplaceImage( imgWrap, files ); + dropReplaceImage(imgWrap, files); }); window.addEventListener('paste', e => { const files = e.clipboardData.files; - if ( ! isValidImageList( files ) ) { - return; - } - - 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 ); + if ( ! isValidImageList( files ) ) return; + 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/modules/scripts_auto_postprocessing.py b/modules/scripts_auto_postprocessing.py index 30d6d6586..e37c977fc 100644 --- a/modules/scripts_auto_postprocessing.py +++ b/modules/scripts_auto_postprocessing.py @@ -27,15 +27,11 @@ class ScriptPostprocessingForMainUI(scripts.Script): def create_auto_preprocessing_script_data(): - from modules import scripts - res = [] - for name in shared.opts.postprocessing_enable_in_main_ui: script = next(iter([x for x in scripts.postprocessing_scripts_data if x.script_class.name == name]), None) if script is None: continue - constructor = lambda s=script: ScriptPostprocessingForMainUI(s.script_class()) res.append(scripts.ScriptClassData(script_class=constructor, path=script.path, basedir=script.basedir, module=script.module)) diff --git a/modules/shared.py b/modules/shared.py index b234d2d2a..e7d504a3b 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -20,6 +20,7 @@ errors.install(gr) demo: gr.Blocks = None log = setup_log parser = cmd_args.parser +url = 'https://github.com/vladmandic/automatic' if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None: cmd_opts = parser.parse_args() diff --git a/modules/ui.py b/modules/ui.py index 0e8bd192a..811e9261a 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -926,31 +926,6 @@ def create_ui(): with gr.Blocks(analytics_enabled=False) as extras_interface: ui_postprocessing.create_ui() - """ - with gr.Blocks(analytics_enabled=False) as pnginfo_interface: - with gr.Row().style(equal_height=False): - with gr.Column(variant='panel'): - image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil") - - with gr.Column(variant='panel'): - html = gr.HTML() - generation_info = gr.Textbox(visible=False, elem_id="pnginfo_generation_info") - html2 = gr.HTML() - with gr.Row(): - buttons = parameters_copypaste.create_buttons(["txt2img", "img2img", "inpaint", "extras"]) - - for tabname, button in buttons.items(): - parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( - paste_button=button, tabname=tabname, source_text_component=generation_info, source_image_component=image, - )) - - image.change( - fn=wrap_gradio_call(modules.extras.run_pnginfo), - inputs=[image], - outputs=[html, generation_info, html2], - ) - """ - def update_interp_description(value): interp_description_css = "

{}

" interp_descriptions = { From def7a02d82f4e7456177f3dbdf697dc7d02a3214 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 26 Apr 2023 16:08:56 -0400 Subject: [PATCH 20/45] add asyncio handler --- modules/shared.py | 23 ++++++----------------- webui.py | 20 +++++++++++++++++++- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/modules/shared.py b/modules/shared.py index e7d504a3b..d1f605053 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -1,8 +1,8 @@ -import datetime -import json import os import sys import time +import json +import datetime import gradio as gr import tqdm @@ -321,23 +321,12 @@ options_templates.update(options_section(('saving-paths', "Image Paths"), { "outdir_save": OptionInfo("outputs/save", "Directory for saving images using the Save button", component_args=hide_dirs), })) -cuda_dtype = OptionInfo("FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}) -no_half = OptionInfo(False, "Use full precision for model (--no-half)") -no_half_vae = OptionInfo(False, "Use full precision for VAE (--no-half-vae)") -upcast_sampling = OptionInfo(False, - "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory") -# support for macOS. -if sys.platform == "darwin": - cuda_dtype.default = "FP32" - upcast_sampling.default = True - - options_templates.update(options_section(('cuda', "CUDA Settings"), { "precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}), - "cuda_dtype": cuda_dtype, - "no_half": no_half, - "no_half_vae": no_half_vae, - "upcast_sampling": upcast_sampling, + "cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}), + "no_half": OptionInfo(False, "Use full precision for model (--no-half)"), + "no_half_vae": OptionInfo(False, "Use full precision for VAE (--no-half-vae)"), + "upcast_sampling": OptionInfo(True if sys.platform == "darwin" else False, "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory"), "disable_nan_check": OptionInfo(True, "Do not check if produced images/latent spaces have NaN values"), "rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"), "opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "), diff --git a/webui.py b/webui.py index a0d8f81b8..20c621818 100644 --- a/webui.py +++ b/webui.py @@ -1,9 +1,11 @@ import os import re +import sys import time import signal -import warnings +import asyncio import logging +import warnings from rich import print # pylint: disable=W0622 from modules import timer, errors @@ -166,9 +168,25 @@ def create_api(app): return api +def async_policy(): + _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy") else asyncio.DefaultEventLoopPolicy + + class AnyThreadEventLoopPolicy(_BasePolicy): + def get_event_loop(self) -> asyncio.AbstractEventLoop: + try: + return super().get_event_loop() + except (RuntimeError, AssertionError): + loop = self.new_event_loop() + self.set_event_loop(loop) + return loop + + asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) + + def start_ui(): logging.disable(logging.INFO) create_paths(opts) + async_policy() initialize() if shared.opts.clean_temp_dir_at_start: ui_tempdir.cleanup_tmpdr() From bab615e0bf73d4c76dcabbe06e6d9ff308199074 Mon Sep 17 00:00:00 2001 From: Miao Xiang Date: Wed, 26 Apr 2023 16:02:34 -0700 Subject: [PATCH 21/45] remove test output --- test/stderr.txt | 49 ------------------------------------------------- test/stdout.txt | 10 ---------- 2 files changed, 59 deletions(-) delete mode 100644 test/stderr.txt delete mode 100644 test/stdout.txt diff --git a/test/stderr.txt b/test/stderr.txt deleted file mode 100644 index 5fed8ec5f..000000000 --- a/test/stderr.txt +++ /dev/null @@ -1,49 +0,0 @@ - Downloading pytorch_model.bin: 0%| | 0.00/1.71G [00:00 - start() - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\launch.py", line 351, in start - webui.webui() - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\webui.py", line 233, in webui - initialize() - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\webui.py", line 139, in initialize - modules.sd_models.load_model() - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\modules\sd_models.py", line 432, in load_model - sd_model = instantiate_from_config(sd_config.model) - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\repositories\stable-diffusion-stability-ai\ldm\util.py", line 89, in instantiate_from_config - return get_obj_from_str(config["target"])(**config.get("params", dict())) - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\repositories\stable-diffusion-stability-ai\ldm\models\diffusion\ddpm.py", line 563, in __init__ - self.instantiate_cond_stage(cond_stage_config) - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\repositories\stable-diffusion-stability-ai\ldm\models\diffusion\ddpm.py", line 630, in instantiate_cond_stage - model = instantiate_from_config(config) - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\repositories\stable-diffusion-stability-ai\ldm\util.py", line 89, in instantiate_from_config - return get_obj_from_str(config["target"])(**config.get("params", dict())) - File "C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\repositories\stable-diffusion-stability-ai\ldm\modules\encoders\modules.py", line 104, in __init__ - self.transformer = CLIPTextModel.from_pretrained(version) - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\transformers\modeling_utils.py", line 2151, in from_pretrained - resolved_archive_file = cached_file( - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\transformers\utils\hub.py", line 409, in cached_file - resolved_file = hf_hub_download( - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\huggingface_hub\utils\_validators.py", line 120, in _inner_fn - return fn(*args, **kwargs) - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\huggingface_hub\file_download.py", line 1326, in hf_hub_download - http_get( - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\huggingface_hub\file_download.py", line 538, in http_get - for chunk in r.iter_content(chunk_size=10 * 1024 * 1024): - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\models.py", line 753, in generate - for chunk in self.raw.stream(chunk_size, decode_content=True): - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\response.py", line 628, in stream - data = self.read(amt=amt, decode_content=decode_content) - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\response.py", line 567, in read - data = self._fp_read(amt) if not fp_closed else b"" - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\response.py", line 533, in _fp_read - return self._fp.read(amt) if amt is not None else self._fp.read() - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 465, in read - s = self.fp.read(amt) - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\socket.py", line 705, in readinto - return self._sock.recv_into(b) - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\ssl.py", line 1274, in recv_into - return self.read(nbytes, buffer) - File "C:\Users\MiaoPC-1\AppData\Local\Programs\Python\Python310\lib\ssl.py", line 1130, in read - return self._sslobj.read(len, buffer) -KeyboardInterrupt - Downloading pytorch_model.bin: 51%| | 870M/1.71G [01:14<01:12, 11.6MB/s] diff --git a/test/stdout.txt b/test/stdout.txt deleted file mode 100644 index 15a0c9753..000000000 --- a/test/stdout.txt +++ /dev/null @@ -1,10 +0,0 @@ -Python 3.10.10 (tags/v3.10.10:aad5f6a, Feb 7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)] -Commit hash: f3eee04b8312e26a1778a1f7dcb55d18c749ad71 -Installing requirements for Web UI -Launching Web UI with arguments: --skip-torch-cuda-test --deepdanbooru --no-half-vae --tests TESTS --api --ckpt C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\test/test_files/empty.pt --disable-nan-check --no-tests -No module 'xformers'. Proceeding without it. -Calculating sha256 for C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\test/test_files/empty.pt: d030ad8db708280fcae77d87e973102039acd23a11bdecc3db8eb6c0ac940ee1 -Loading weights [d030ad8db7] from C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\test/test_files/empty.pt -Creating model from config: C:\Users\MiaoPC-1\Documents\stable-diffusion-webui\configs\v1-inference.yaml -LatentDiffusion: Running in eps-prediction mode -DiffusionWrapper has 859.52 M params. From ba02d0c2e8d61a9fd8dc3deed67600909c129b04 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 27 Apr 2023 09:18:10 -0400 Subject: [PATCH 22/45] fix realesrgan path, secondary sampler --- TODO.md | 2 ++ modules/cmd_args.py | 1 + modules/processing.py | 2 +- modules/realesrgan_model.py | 11 +++++------ modules/shared.py | 1 + modules/upscaler.py | 10 +++++----- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/TODO.md b/TODO.md index ed0b7c875..f67eeebfa 100644 --- a/TODO.md +++ b/TODO.md @@ -61,3 +61,5 @@ Tech that can be integrated as part of the core workflow... ### Pending Code Updates +- add optional models description shown in extra networks cards +- Add option to specify fallback sampler if primary sampler is not compatible with desired operation diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 87b47d6c7..a1afcd2bd 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -74,6 +74,7 @@ def compatibility_args(opts, args): parser.add_argument("--sub-quad-q-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_q_chunk_size) parser.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size) parser.add_argument("--sub-quad-chunk-threshold", help=argparse.SUPPRESS, default=opts.sub_quad_chunk_threshold) + parser.add_argument("--dimensions-and-batch-together", help=argparse.SUPPRESS, default=True) opts.use_old_emphasis_implementation = False opts.use_old_karras_scheduler_sigmas = False diff --git a/modules/processing.py b/modules/processing.py index 0ce6d4c91..1d49da237 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -973,7 +973,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): img2img_sampler_name = self.sampler_name if self.sampler_name in ['PLMS', 'UniPC']: # PLMS/UniPC do not support img2img so we just silently switch to DDIM - img2img_sampler_name = 'DDIM' + img2img_sampler_name = shared.opts.fallback_sampler self.sampler = sd_samplers.create_sampler(img2img_sampler_name, self.sd_model) samples = samples[:, :, self.truncate_y//2:samples.shape[2]-(self.truncate_y+1)//2, self.truncate_x//2:samples.shape[3]-(self.truncate_x+1)//2] diff --git a/modules/realesrgan_model.py b/modules/realesrgan_model.py index 86007f906..11b76ca3d 100644 --- a/modules/realesrgan_model.py +++ b/modules/realesrgan_model.py @@ -13,7 +13,7 @@ import modules.errors as errors class UpscalerRealESRGAN(Upscaler): def __init__(self, path): self.name = "RealESRGAN" - self.user_path = path + self.model_path = path super().__init__() try: from basicsr.archs.rrdbnet_arch import RRDBNet @@ -31,7 +31,7 @@ class UpscalerRealESRGAN(Upscaler): self.enable = False self.scalers = [] - def do_upscale(self, img, path): + def do_upscale(self, img, selected_model): if not self.enable: return img @@ -41,9 +41,9 @@ class UpscalerRealESRGAN(Upscaler): print("Error importing Real-ESRGAN:", file=sys.stderr) return img - info = self.load_model(path) + info = self.load_model(selected_model) if not os.path.exists(info.local_data_path): - print("Unable to load RealESRGAN model: %s" % info.name) + print(f"Unable to load RealESRGAN model: {info.name}") return img upsampler = RealESRGANer( @@ -67,7 +67,6 @@ class UpscalerRealESRGAN(Upscaler): if info is None: print(f"Unable to find model info: {path}") return None - info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_path, progress=True) return info except Exception as e: @@ -127,6 +126,6 @@ def get_realesrgan_models(scaler): ), ] return models - except Exception as e: + except Exception: print("Error creating Real-ESRGAN models list", file=sys.stderr) return [] diff --git a/modules/shared.py b/modules/shared.py index d1f605053..466ec6605 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -431,6 +431,7 @@ options_templates.update(options_section(('ui', "Live previews"), { options_templates.update(options_section(('sampler-params', "Sampler parameters"), { "show_samplers": OptionInfo(["Euler a", "UniPC", "DDIM", "DPM++ SDE", "DPM++ SDE", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers()]}), + "fallback_sampler": OptionInfo("Euler a", "Fallback sampler if primary sampler is not compatible", gr.Dropdown, lambda: {"choices": [x.name for x in list_samplers()]}), "eta_ancestral": OptionInfo(1.0, "Noise multiplier for ancestral samplers (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), "eta_ddim": OptionInfo(0.0, "Noise multiplier for DDIM (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), "ddim_discretize": OptionInfo('uniform', "DDIM discretize img2img", gr.Radio, {"choices": ['uniform', 'quad']}), diff --git a/modules/upscaler.py b/modules/upscaler.py index 0376d256c..9ca6e4596 100644 --- a/modules/upscaler.py +++ b/modules/upscaler.py @@ -54,7 +54,7 @@ class Upscaler: dest_w = int(img.width * scale) dest_h = int(img.height * scale) - for i in range(3): + for _i in range(3): shape = (img.width, img.height) img = self.do_upscale(img, selected_model) @@ -74,7 +74,7 @@ class Upscaler: def load_model(self, path: str): pass - def find_models(self, ext_filter=None) -> list: + def find_models(self, ext_filter=None) -> list: # pylint: disable=unused-argument return modelloader.load_models(model_path=self.model_path, model_url=self.model_url, command_path=self.user_path) def update_status(self, prompt): @@ -107,7 +107,7 @@ class UpscalerNone(Upscaler): def do_upscale(self, img, selected_model=None): return img - def __init__(self, dirname=None): + def __init__(self, dirname=None): # pylint: disable=unused-argument super().__init__(False) self.scalers = [UpscalerData("None", None, self)] @@ -121,7 +121,7 @@ class UpscalerLanczos(Upscaler): def load_model(self, _): pass - def __init__(self, dirname=None): + def __init__(self, dirname=None): # pylint: disable=unused-argument super().__init__(False) self.name = "Lanczos" self.scalers = [UpscalerData("Lanczos", None, self)] @@ -136,7 +136,7 @@ class UpscalerNearest(Upscaler): def load_model(self, _): pass - def __init__(self, dirname=None): + def __init__(self, dirname=None): # pylint: disable=unused-argument super().__init__(False) self.name = "Nearest" self.scalers = [UpscalerData("Nearest", None, self)] From 5dcaaba614cf76005f66c2e9269361750440caf9 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 27 Apr 2023 09:42:41 -0400 Subject: [PATCH 23/45] reduce extra network exceptions --- TODO.md | 4 +- extensions-builtin/sd-webui-controlnet | 2 +- modules/lora | 2 +- modules/ui_extra_networks.py | 83 ++++---------------------- scripts/postprocessing_upscale.py | 7 +-- 5 files changed, 19 insertions(+), 79 deletions(-) diff --git a/TODO.md b/TODO.md index f67eeebfa..174c4b337 100644 --- a/TODO.md +++ b/TODO.md @@ -61,5 +61,5 @@ Tech that can be integrated as part of the core workflow... ### Pending Code Updates -- add optional models description shown in extra networks cards -- Add option to specify fallback sampler if primary sampler is not compatible with desired operation +- ability to view/add/edit model description shown in extra networks cards +- add option to specify fallback sampler if primary sampler is not compatible with desired operation diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 93b0f9e1b..2bc440001 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 93b0f9e1b7cc246165666b7b307bc8243db2c3f4 +Subproject commit 2bc4400011b38ab7f1d3f27a95897a6cb0c28c2a diff --git a/modules/lora b/modules/lora index 18f171d88..d52c524fc 160000 --- a/modules/lora +++ b/modules/lora @@ -1 +1 @@ -Subproject commit 18f171d885d4c870bd1c0656f7247e9649df62fd +Subproject commit d52c524fc2942c053cf37c648188502a3a26df1b diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index a719bde33..a8e3851b1 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -20,37 +20,28 @@ close_symbol = '\U0000274C' # ❌ def register_page(page): """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions""" - extra_pages.append(page) allowed_dirs.clear() allowed_dirs.update(set(sum([x.allowed_directories_for_previews() for x in extra_pages], []))) def fetch_file(filename: str = ""): - from starlette.responses import FileResponse - + from starlette.responses import FileResponse, JSONResponse if not any([Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs]): - raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.") - - ext = os.path.splitext(filename)[1].lower() - if ext not in (".png", ".jpg", ".webp"): - raise ValueError(f"File cannot be fetched: {filename}. Only png and jpg and webp.") - - # would profit from returning 304 + return JSONResponse({"error": f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages."}) + if os.path.splitext(filename)[1].lower() not in (".png", ".jpg", ".webp"): + return JSONResponse({"error": f"File cannot be fetched: {filename}. Only png and jpg and webp."}) return FileResponse(filename, headers={"Accept-Ranges": "bytes"}) def get_metadata(page: str = "", item: str = ""): from starlette.responses import JSONResponse - page = next(iter([x for x in extra_pages if x.name == page]), None) if page is None: return JSONResponse({}) - metadata = page.metadata.get(item) if metadata is None: return JSONResponse({}) - return JSONResponse({"metadata": metadata}) @@ -75,58 +66,44 @@ class ExtraNetworksPage: def search_terms_from_path(self, filename, possible_directories=None): abspath = os.path.abspath(filename) - for parentdir in (possible_directories if possible_directories is not None else self.allowed_directories_for_previews()): parentdir = os.path.abspath(parentdir) if abspath.startswith(parentdir): return abspath[len(parentdir):].replace('\\', '/') - return "" def create_html(self, tabname): view = shared.opts.extra_networks_default_view items_html = '' - self.metadata = {} - subdirs = {} for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]: for x in glob.glob(os.path.join(parentdir, '**/*'), recursive=True): if not os.path.isdir(x): continue - subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/") while subdir.startswith("/"): subdir = subdir[1:] - is_empty = len(os.listdir(x)) == 0 if not is_empty and not subdir.endswith("/"): subdir = subdir + "/" - subdirs[subdir] = 1 - if subdirs: subdirs = {"": 1, **subdirs} - subdirs_html = "".join([f""" """ for subdir in subdirs]) - for item in self.list_items(): metadata = item.get("metadata") if metadata: self.metadata[item["name"]] = metadata - items_html += self.create_html_for_item(item, tabname) - if items_html == '': dirs = "".join([f"
  • {x}
  • " for x in self.allowed_directories_for_previews()]) items_html = shared.html("extra-networks-no-cards.html").format(dirs=dirs) - self_name_id = self.name.replace(" ", "_") - res = f"""
    {subdirs_html} @@ -135,7 +112,6 @@ class ExtraNetworksPage: {items_html}
    """ - return res def list_items(self): @@ -146,11 +122,9 @@ class ExtraNetworksPage: def create_html_for_item(self, item, tabname): preview = item.get("preview", None) - onclick = item.get("onclick", None) if onclick is None: onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"' - height = f"height: {shared.opts.extra_networks_card_height}px;" if shared.opts.extra_networks_card_height else '' width = f"width: {shared.opts.extra_networks_card_width}px;" if shared.opts.extra_networks_card_width else '' background_image = f"background-image: url(\"{html.escape(preview)}\");" if preview else '' @@ -158,7 +132,6 @@ class ExtraNetworksPage: metadata = item.get("metadata") if metadata: metadata_button = f"" - args = { "style": f"'{height}{width}{background_image}'", "prompt": item.get("prompt", None), @@ -173,24 +146,19 @@ class ExtraNetworksPage: "search_term": item.get("search_term", ""), "metadata_button": metadata_button, } - return self.card_page.format(**args) def find_preview(self, path): """ Find a preview PNG for a given path (without extension) and call link_preview on it. """ - preview_extensions = ["png", "jpg", "webp"] if shared.opts.samples_format not in preview_extensions: preview_extensions.append(shared.opts.samples_format) - potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in preview_extensions], []) - for file in potential_files: if os.path.isfile(file): return self.link_preview(file) - return None def find_description(self, path): @@ -214,31 +182,24 @@ class ExtraNetworksUi: def __init__(self): self.pages = None self.stored_extra_pages = None - self.button_save_preview = None self.preview_target_filename = None - self.button_save_description = None self.button_read_description = None self.description_target_filename = None self.description_input = None - self.tabname = None def pages_in_preferred_order(pages): tab_order = [x.lower().strip() for x in shared.opts.ui_extra_networks_tab_reorder.split(",")] - def tab_name_score(name): name = name.lower() for i, possible_match in enumerate(tab_order): if possible_match in name: return i - return len(pages) - tab_scores = {page.name: (tab_name_score(page.name), original_index) for original_index, page in enumerate(pages)} - return sorted(pages, key=lambda x: tab_scores[x.name]) @@ -247,54 +208,43 @@ def create_ui(container, button, tabname): ui.pages = [] ui.stored_extra_pages = pages_in_preferred_order(extra_pages.copy()) ui.tabname = tabname - - with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs: + with gr.Tabs(elem_id=tabname+"_extra_tabs"): for page in ui.stored_extra_pages: with gr.Tab(page.title): - page_elem = gr.HTML(page.create_html(ui.tabname)) ui.pages.append(page_elem) - - filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False) - + _filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False) ui.description_input = gr.TextArea('', show_label=False, elem_id=tabname+"_description_input", placeholder="Save/Replace Extra Network Description...", lines=2) button_refresh = ToolButton(refresh_symbol, elem_id=tabname+"_extra_refresh") button_close = ToolButton(close_symbol, elem_id=tabname+"_extra_close") - ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False) ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) - ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False) ui.button_read_description = gr.Button('Read description', elem_id=tabname+"_read_description", visible=False) ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False) - def toggle_visibility(is_visible): is_visible = not is_visible return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary")) - state_visible = gr.State(value=False) + state_visible = gr.State(value=False) # pylint: disable=abstract-class-instantiated button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button]) button_close.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) def refresh(): res = [] - for pg in ui.stored_extra_pages: pg.refresh() res.append(pg.create_html(ui.tabname)) - return res button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages) - return ui def path_is_parent(parent_path, child_path): parent_path = os.path.abspath(parent_path) child_path = os.path.abspath(child_path) - return child_path.startswith(parent_path) @@ -303,30 +253,24 @@ def setup_ui(ui, gallery): if len(images) == 0: print("There is no image in gallery to save as a preview.") return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] - index = int(index) index = 0 if index < 0 else index index = len(images) - 1 if index >= len(images) else index - img_info = images[index if index >= 0 else 0] image = image_from_url_text(img_info) - geninfo, items = read_info_from_image(image) - + geninfo, _items = read_info_from_image(image) is_allowed = False for extra_page in ui.stored_extra_pages: if any([path_is_parent(x, filename) for x in extra_page.allowed_directories_for_previews()]): is_allowed = True break - assert is_allowed, f'writing to {filename} is not allowed' - if geninfo: pnginfo_data = PngImagePlugin.PngInfo() pnginfo_data.add_text('parameters', geninfo) image.save(filename, pnginfo=pnginfo_data) else: image.save(filename) - return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] ui.button_save_preview.click( @@ -335,27 +279,24 @@ def setup_ui(ui, gallery): inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename], outputs=[*ui.pages] ) - + # write description to a file def save_description(filename,descrip): lastDotIndex = filename.rindex('.') filename = filename[0:lastDotIndex]+".description.txt" if descrip != "": - try: - f = open(filename,'w') + try: + f = open(filename,'w', encoding='utf-8') except OSError: print ("Could not open file to write: " + filename) with f: f.write(descrip) f.close() return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] - + ui.button_save_description.click( fn=save_description, _js="function(x,y){return [x,y]}", inputs=[ui.description_target_filename, ui.description_input], outputs=[*ui.pages] ) - - - diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index ef1186ac6..b2f9c7408 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -25,10 +25,9 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: with FormRow(): - with gr.Column(elem_id="upscaling_column_size", scale=4): - upscaling_resize_w = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") - upscaling_resize_h = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h") - with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"): + with gr.Row(elem_id="upscaling_column_size", scale=4): + upscaling_resize_w = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") + upscaling_resize_h = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h") upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn") upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") From 1f6261be80b20995da8d086b6d3bf559f7e9447f Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 27 Apr 2023 13:12:20 -0400 Subject: [PATCH 24/45] jumbo patch --- TODO.md | 2 + javascript/ui.js | 24 ++++- modules/generation_parameters_copypaste.py | 52 +-------- modules/processing.py | 12 +-- modules/sd_hijack_clip.py | 4 +- modules/shared.py | 93 +++++----------- modules/txt2img.py | 4 +- modules/ui.py | 120 ++------------------- script.js | 29 ++--- webui.bat | 9 +- webui.sh | 106 +++--------------- 11 files changed, 96 insertions(+), 359 deletions(-) diff --git a/TODO.md b/TODO.md index 174c4b337..d2939f9fa 100644 --- a/TODO.md +++ b/TODO.md @@ -63,3 +63,5 @@ Tech that can be integrated as part of the core workflow... - ability to view/add/edit model description shown in extra networks cards - add option to specify fallback sampler if primary sampler is not compatible with desired operation +- make clip skip a local parameter +- remove obsolete items from settings diff --git a/javascript/ui.js b/javascript/ui.js index 774422b93..52a9341ce 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -338,6 +338,28 @@ function reconnect_ui() { const atEnd = () => showSubmitButtons('txt2img', true) requestProgress(task_id, el1, el2, atEnd, null, true) } + + sd_model = gradioApp().getElementById("setting_sd_model_checkpoint") + let loadingStarted = 0; + let loadingMonitor = 0; + const sd_model_callback = () => { + loading = sd_model.querySelector(".eta-bar") + if (!loading) { + loadingStarted = 0 + clearInterval(loadingMonitor) + } else { + if (loadingStarted === 0) { + loadingStarted = Date.now(); + loadingMonitor = setInterval(() => { + elapsed = Date.now() - loadingStarted; + console.log('Loading', elapsed) + if (elapsed > 3000 && loading) loading.style.display = 'none'; + }, 5000); + } + } + }; + const sd_model_observer = new MutationObserver(sd_model_callback); + sd_model_observer.observe(sd_model, { attributes: true, childList: true, subtree: true }); } -var start_check = setInterval(reconnect_ui, 50) +var start_check = setInterval(reconnect_ui, 50); diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index bc58011f2..964432d72 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -11,7 +11,7 @@ from modules import shared, ui_tempdir, script_callbacks re_param_code = r'\s*([\w ]+):\s*("(?:\\"[^,]|\\"|\\|[^\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_imagesize = re.compile(r"^(\d+)x(\d+)$") -re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") +re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") # pylint: disable=anomalous-backslash-in-string type_of_gr_update = type(gr.update()) paste_fields = {} @@ -102,7 +102,6 @@ def bind_buttons(buttons, send_image, send_generate_info): for tabname, button in buttons.items(): source_text_component = send_generate_info if isinstance(send_generate_info, gr.components.Component) else None source_tabname = send_generate_info if isinstance(send_generate_info, str) else None - register_paste_params_button(ParamBinding(paste_button=button, tabname=tabname, source_text_component=source_text_component, source_image_component=send_image, source_tabname=source_tabname)) @@ -116,7 +115,6 @@ def connect_paste_params_buttons(): destination_image_component = paste_fields[binding.tabname]["init_img"] fields = paste_fields[binding.tabname]["fields"] override_settings_component = binding.override_settings_component or paste_fields[binding.tabname]["override_settings_component"] - destination_width_component = next(iter([field for field, name in fields if name == "Size-1"] if fields else []), None) destination_height_component = next(iter([field for field, name in fields if name == "Size-2"] if fields else []), None) @@ -127,17 +125,14 @@ def connect_paste_params_buttons(): else: func = send_image_and_dimensions if destination_width_component else lambda x: x jsfunc = None - binding.paste_button.click( fn=func, _js=jsfunc, inputs=[binding.source_image_component], outputs=[destination_image_component, destination_width_component, destination_height_component] if destination_width_component else [destination_image_component], ) - if binding.source_text_component is not None and fields is not None: connect_paste(binding.paste_button, fields, binding.source_text_component, override_settings_component, binding.tabname) - if binding.source_tabname is not None and fields is not None: paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration'] + (["Seed"] if shared.opts.send_seed else []) + binding.paste_field_names binding.paste_button.click( @@ -145,7 +140,6 @@ def connect_paste_params_buttons(): inputs=[field for field, name in paste_fields[binding.source_tabname]["fields"] if name in paste_field_names], outputs=[field for field, name in fields if name in paste_field_names], ) - binding.paste_button.click( fn=None, _js=f"switch_to_{binding.tabname}", @@ -159,14 +153,12 @@ def send_image_and_dimensions(x): img = x else: img = image_from_url_text(x) - if shared.opts.send_size and isinstance(img, Image.Image): w = img.width h = img.height else: w = gr.update() h = gr.update() - return img, w, h @@ -238,33 +230,25 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model returns a dict with field values """ - res = {} - prompt = "" negative_prompt = "" - done_with_prompt = False - *lines, lastline = x.strip().split("\n") if len(re_param.findall(lastline)) < 3: lines.append(lastline) lastline = '' - for _i, line in enumerate(lines): line = line.strip() if line.startswith("Negative prompt:"): done_with_prompt = True line = line[16:].strip() - if done_with_prompt: negative_prompt += ("" if negative_prompt == "" else "\n") + line else: prompt += ("" if prompt == "" else "\n") + line - res["Prompt"] = prompt res["Negative prompt"] = negative_prompt - for k, v in re_param.findall(lastline): v = v[1:-1] if v[0] == '"' and v[-1] == '"' else v m = re_imagesize.match(v) @@ -273,31 +257,24 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model res[k+"-2"] = m.group(2) else: res[k] = v - # Missing CLIP skip means it was set to 1 (the default) if "Clip skip" not in res: res["Clip skip"] = "1" - hypernet = res.get("Hypernet", None) if hypernet is not None: res["Prompt"] += f"""""" - if "Hires resize-1" not in res: res["Hires resize-1"] = 0 res["Hires resize-2"] = 0 - # Infer additional override settings for token merging token_merging_ratio = res.get("Token merging ratio", None) token_merging_ratio_hr = res.get("Token merging ratio hr", None) - if token_merging_ratio is not None or token_merging_ratio_hr is not None: res["Token merging"] = 'True' - if token_merging_ratio is None: res["Token merging hr only"] = 'True' else: res["Token merging hr only"] = 'False' - if res.get("Token merging random", None) is None: res["Token merging random"] = 'False' if res.get("Token merging merge attention", None) is None: @@ -312,14 +289,12 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model res["Token merging stride y"] = '2' restore_old_hires_fix_params(res) - return res settings_map = {} - infotext_to_setting_name_mapping = [ ('Clip skip', 'CLIP_stop_at_last_layers', ), ('Conditional mask weight', 'inpainting_mask_weight'), @@ -349,34 +324,29 @@ infotext_to_setting_name_mapping = [ def create_override_settings_dict(text_pairs): """creates processing's override_settings parameters from gradio's multiselect - Example input: ['Clip skip: 2', 'Model hash: e6e99610c4', 'ENSD: 31337'] Example output: {'CLIP_stop_at_last_layers': 2, 'sd_model_checkpoint': 'e6e99610c4', 'eta_noise_seed_delta': 31337} """ - res = {} params = {} for pair in text_pairs: k, v = pair.split(":", maxsplit=1) - params[k] = v.strip() - for param_name, setting_name in infotext_to_setting_name_mapping: value = params.get(param_name, None) - if value is None: continue - res[setting_name] = shared.opts.cast_value(setting_name, value) - return res -def connect_paste(button, paste_fields, input_comp, override_settings_component, tabname): +def connect_paste(button, paste_fields, input_comp, override_settings_component, tabname): # pylint: disable=redefined-outer-name def paste_func(prompt): + if 'Negative prompt' not in prompt and 'Steps' not in prompt: + prompt = None if not prompt and not shared.cmd_opts.hide_ui_dir_config: filename = os.path.join(data_path, "params.txt") if os.path.exists(filename): @@ -384,17 +354,14 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component, prompt = file.read() else: prompt = '' - params = parse_generation_parameters(prompt) script_callbacks.infotext_pasted_callback(prompt, params) res = [] - for output, key in paste_fields: if callable(key): v = key(params) else: v = params.get(key, None) - if v is None: res.append(gr.update()) elif isinstance(v, type_of_gr_update): @@ -402,42 +369,31 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component, else: try: valtype = type(output.value) - if valtype == bool and v == "False": val = False else: val = valtype(v) - res.append(gr.update(value=val)) except Exception: res.append(gr.update()) - return res if override_settings_component is not None: def paste_settings(params): vals = {} - for param_name, setting_name in infotext_to_setting_name_mapping: v = params.get(param_name, None) if v is None: continue - if setting_name == "sd_model_checkpoint" and shared.opts.disable_weights_auto_swap: continue - v = shared.opts.cast_value(setting_name, v) current_value = getattr(shared.opts, setting_name, None) - if v == current_value: continue - vals[param_name] = v - vals_pairs = [f"{k}: {v}" for k, v in vals.items()] - return gr.Dropdown.update(value=vals_pairs, choices=vals_pairs, visible=len(vals_pairs) > 0) - paste_fields = paste_fields + [(override_settings_component, paste_settings)] button.click( diff --git a/modules/processing.py b/modules/processing.py index 1d49da237..597ea5bee 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -111,7 +111,7 @@ class StableDiffusionProcessing: """ The first set of paramaters: sd_models -> do_not_reload_embeddings represent the minimum required to create a StableDiffusionProcessing """ - def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None): # pylint: disable=unused-argument + def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 20, cfg_scale: float = 6.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None): # pylint: disable=unused-argument if sampler_index is not None: print("sampler_index argument for StableDiffusionProcessing does not do anything; use sampler_name", file=sys.stderr) @@ -165,6 +165,7 @@ class StableDiffusionProcessing: self.all_negative_prompts = None self.all_seeds = None self.all_subseeds = None + self.clip_skip = opts.CLIP_stop_at_last_layers self.iteration = 0 @property @@ -302,8 +303,7 @@ class Processed: self.index_of_first_image = index_of_first_image self.styles = p.styles self.job_timestamp = state.job_timestamp - self.clip_skip = opts.CLIP_stop_at_last_layers - + self.clip_skip = p.clip_skip self.eta = p.eta self.ddim_discretize = p.ddim_discretize self.s_churn = p.s_churn @@ -457,11 +457,9 @@ def fix_seed(p): p.subseed = get_fixed_seed(p.subseed) -def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument +def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument index = position_in_batch + iteration * p.batch_size - clip_skip = getattr(p, 'clip_skip', opts.CLIP_stop_at_last_layers) - generation_params = { "Steps": p.steps, "Sampler": p.sampler_name, @@ -478,7 +476,7 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter "Seed resize from": (None if p.seed_resize_from_w == 0 or p.seed_resize_from_h == 0 else f"{p.seed_resize_from_w}x{p.seed_resize_from_h}"), "Denoising strength": getattr(p, 'denoising_strength', None), "Conditional mask weight": getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None, - "Clip skip": None if clip_skip <= 1 else clip_skip, + "Clip skip": p.clip_skip, "ENSD": None if opts.eta_noise_seed_delta == 0 else opts.eta_noise_seed_delta, "Token merging ratio": None if not (opts.token_merging or cmd_opts.token_merging) or opts.token_merging_hr_only else opts.token_merging_ratio, "Token merging ratio hr": None if not (opts.token_merging or cmd_opts.token_merging) else opts.token_merging_ratio_hr, diff --git a/modules/sd_hijack_clip.py b/modules/sd_hijack_clip.py index c994a0b67..b979ed6f9 100644 --- a/modules/sd_hijack_clip.py +++ b/modules/sd_hijack_clip.py @@ -205,7 +205,7 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module): is when you do prompt editing: "a picture of a [cat:dog:0.4] eating ice cream" """ - batch_chunks, token_count = self.process_texts(texts) + batch_chunks, _token_count = self.process_texts(texts) used_embeddings = {} chunk_count = max([len(x) for x in batch_chunks]) @@ -219,7 +219,7 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module): self.hijack.fixes = [x.fixes for x in batch_chunk] for fixes in self.hijack.fixes: - for position, embedding in fixes: + for _position, embedding in fixes: used_embeddings[embedding.name] = embedding z = self.process_tokens(tokens, multipliers) diff --git a/modules/shared.py b/modules/shared.py index 466ec6605..adccb4812 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -3,10 +3,8 @@ import sys import time import json import datetime - import gradio as gr import tqdm - import modules.interrogate import modules.memmon import modules.styles @@ -21,7 +19,6 @@ demo: gr.Blocks = None log = setup_log parser = cmd_args.parser url = 'https://github.com/vladmandic/automatic' - if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None: cmd_opts = parser.parse_args() else: @@ -53,10 +50,7 @@ ui_reorder_categories = [ ] cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure - -devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = \ - (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer']) - +devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer']) device = devices.device sd_upscalers = [] sd_model = None @@ -97,7 +91,6 @@ class State: def nextjob(self): if opts.live_previews_enable and opts.show_progress_every_n_steps == -1: self.do_set_current_image() - self.job_no += 1 self.sampling_step = 0 self.current_image_sampling_step = 0 @@ -129,13 +122,11 @@ class State: self.interrupted = False self.textinfo = None self.time_start = time.time() - devices.torch_gc() def end(self): self.job = "" self.job_count = 0 - devices.torch_gc() def set_current_image(self): @@ -162,9 +153,7 @@ class State: state = State() state.server_start = time.time() - interrogator = modules.interrogate.InterrogateModels("interrogate") - face_restorers = [] class OptionInfo: @@ -181,7 +170,6 @@ class OptionInfo: def options_section(section_identifier, options_dict): for _k, v in options_dict.items(): v.section = section_identifier - return options_dict @@ -227,26 +215,25 @@ def refresh_themes(): hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config} tab_names = [] - options_templates = {} default_checkpoint = list_checkpoint_tiles()[0] if len(list_checkpoint_tiles()) > 0 else "model.ckpt" options_templates.update(options_section(('sd', "Stable Diffusion"), { "sd_model_checkpoint": OptionInfo(default_checkpoint, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints), - "sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), - "sd_vae_checkpoint_cache": OptionInfo(0, "VAE Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), - "sd_vae": OptionInfo("Automatic", "SD VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list), - "sd_vae_as_default": OptionInfo(True, "Ignore selected VAE for stable diffusion checkpoints that have their own .vae.pt next to them"), + "sd_checkpoint_cache": OptionInfo(0, "Model checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), + "sd_vae_checkpoint_cache": OptionInfo(0, "VAE checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), + "sd_vae": OptionInfo("Automatic", "Select VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list), + "sd_vae_as_default": OptionInfo(True, "Ignore selected VAE for stable diffusion checkpoints that have their own .vae.pt next to them", gr.Checkbox, {"visible": False}), "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.5, "maximum": 1.5, "step": 0.01}), "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."), - "img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."), + "img2img_fix_steps": OptionInfo(False, "For image processing do exactly the amount of steps as specified."), "img2img_background_color": OptionInfo("#ffffff", "With img2img, fill image's transparent parts with this color.", ui_components.FormColorPicker, {}), "enable_quantization": OptionInfo(True, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds."), - "enable_emphasis": OptionInfo(True, "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention"), - "enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"), + "enable_emphasis": OptionInfo(True, "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention", gr.Checkbox, {"visible": False}), + "enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image", gr.Checkbox, {"visible": False}), "comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }), - "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}), + "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1, "visible": False}), "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"), "cross_attention_optimization": OptionInfo("Scaled-Dot-Product", "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }), "cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}), @@ -254,6 +241,8 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attentionkv chunk size for the sub-quadratic cross-attention layer optimization to use", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}), "sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention percentage of VRAM chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}), "always_batch_cond_uncond": OptionInfo(False, "Disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram"), + "multiple_tqdm": OptionInfo(False, "Add a second progress bar to the console that shows progress for an entire job.", gr.Checkbox, {"visible": False}), + "print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console.", gr.Checkbox, {"visible": False}), })) options_templates.update(options_section(('system-paths', "System Paths"), { @@ -322,6 +311,7 @@ options_templates.update(options_section(('saving-paths', "Image Paths"), { })) options_templates.update(options_section(('cuda', "CUDA Settings"), { + "memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}), "precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}), "cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}), "no_half": OptionInfo(False, "Use full precision for model (--no-half)"), @@ -341,7 +331,7 @@ options_templates.update(options_section(('upscaling', "Upscaling"), { "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Select which Real-ESRGAN models to show in the web UI.", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}), - "upscaler_for_img2img": OptionInfo("SwinIR_4x", "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}), + "upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}), "use_old_hires_fix_width_height": OptionInfo(False, "For hires fix, use width/height sliders to set final resolution rather than first pass (disables Upscale by, Resize width/height to)."), "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers."), })) @@ -352,12 +342,6 @@ options_templates.update(options_section(('face-restoration', "Face restoration" "face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"), })) -options_templates.update(options_section(('system', "System"), { - "memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}), - "multiple_tqdm": OptionInfo(False, "Add a second progress bar to the console that shows progress for an entire job."), - "print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console."), -})) - options_templates.update(options_section(('training', "Training"), { "unload_models_when_training": OptionInfo(False, "Move VAE and CLIP to RAM when training if possible. Saves VRAM."), "pin_memory": OptionInfo(True, "Turn on pin_memory for DataLoader. Makes training slightly faster but can increase memory usage."), @@ -404,13 +388,13 @@ options_templates.update(options_section(('ui', "User interface"), { "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), "add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"), "add_model_name_to_info": OptionInfo(True, "Add model name to generation information"), - "disable_weights_auto_swap": OptionInfo(True, "When reading generation parameters from text into UI (from PNG info or pasted text), do not change the selected model/checkpoint."), + "disable_weights_auto_swap": OptionInfo(True, "Do not change the selected model when reading generation parameters."), "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"), "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"), "font": OptionInfo("", "Font for image grids that have text"), - "js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"), - "js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"), - "show_progress_in_title": OptionInfo(False, "Show generation progress in window title."), + "js_modal_lightbox": OptionInfo(True, "Enable full page image viewer", gr.Checkbox, {"visible": False}), + "js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer", gr.Checkbox, {"visible": False}), + "show_progress_in_title": OptionInfo(False, "Show generation progress in window title.", gr.Checkbox, {"visible": False}), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"), @@ -486,17 +470,14 @@ class Options: def __setattr__(self, key, value): if self.data is not None: if key in self.data or key in self.data_labels: - assert not cmd_opts.freeze_settings, "changing settings is disabled" - - info = opts.data_labels.get(key, None) - comp_args = info.component_args if info else None - if isinstance(comp_args, dict) and comp_args.get('visible', True) is False: - raise RuntimeError(f"not possible to set {key} because it is restricted") - + if cmd_opts.freeze_settings: + print(f'Settings are frozen: {key}') + return if cmd_opts.hide_ui_dir_config and key in restricted_opts: - raise RuntimeError(f"not possible to set {key} because it is restricted") - - self.data[key] = value + print(f'Settings key is restricted: {key}') + return + else: + self.data[key] = value return return super(Options, self).__setattr__(key, value) @@ -505,24 +486,19 @@ class Options: if self.data is not None: if item in self.data: return self.data[item] - if item in self.data_labels: return self.data_labels[item].default - return super(Options, self).__getattribute__(item) def set(self, key, value): """sets an option and calls its onchange callback, returning True if the option changed and False otherwise""" - oldval = self.data.get(key, None) if oldval == value: return False - try: setattr(self, key, value) except RuntimeError: return False - if self.data_labels[key].onchange is not None: try: self.data_labels[key].onchange() @@ -530,37 +506,30 @@ class Options: errors.display(e, f"changing setting {key} to {value}") setattr(self, key, oldval) return False - return True def get_default(self, key): """returns the default value for the key""" - data_label = self.data_labels.get(key) if data_label is None: return None - return data_label.default def save(self, filename): assert not cmd_opts.freeze_settings, "saving settings is disabled" - with open(filename, "w", encoding="utf8") as file: json.dump(self.data, file, indent=4) def same_type(self, x, y): if x is None or y is None: return True - type_x = self.typemap.get(type(x), type(x)) type_y = self.typemap.get(type(y), type(y)) - return type_x == type_y def load(self, filename): with open(filename, "r", encoding="utf8") as file: self.data = json.load(file) - bad_settings = 0 for k, v in self.data.items(): info = self.data_labels.get(k, None) @@ -574,7 +543,6 @@ class Options: def onchange(self, key, func, call=True): item = self.data_labels.get(key) item.onchange = func - if call: func() @@ -587,13 +555,11 @@ class Options: def reorder(self): """reorder settings so that all items related to section always go together""" - section_ids = {} settings_items = self.data_labels.items() for k, item in settings_items: if item.section not in section_ids: section_ids[item.section] = len(section_ids) - self.data_labels = {k: v for k, v in sorted(settings_items, key=lambda x: section_ids[x[1].section])} def cast_value(self, key, value): @@ -619,27 +585,20 @@ class Options: return value - opts = Options() - batch_cond_uncond = opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram) parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram xformers_available = False config_filename = cmd_opts.ui_settings_file - os.makedirs(opts.hypernetwork_dir, exist_ok=True) hypernetworks = {} loaded_hypernetworks = [] - if os.path.exists(config_filename): opts.load(config_filename) - cmd_opts = cmd_args.compatibility_args(opts, cmd_opts) prompt_styles = modules.styles.StyleDatabase(opts.styles_dir) - settings_components = None """assinged from ui.py, a mapping on setting names to gradio components repsponsible for those settings""" - latent_upscale_default_mode = "Latent" latent_upscale_modes = { "Latent": {"mode": "bilinear", "antialias": False}, @@ -649,11 +608,10 @@ latent_upscale_modes = { "Latent (nearest)": {"mode": "nearest", "antialias": False}, "Latent (nearest-exact)": {"mode": "nearest-exact", "antialias": False}, } - progress_print_out = sys.stdout - gradio_theme = gr.themes.Base() + def reload_gradio_theme(theme_name=None): global gradio_theme # pylint: disable=global-statement if not theme_name: @@ -714,7 +672,6 @@ class TotalTQDM: total_tqdm = TotalTQDM() - mem_mon = modules.memmon.MemUsageMonitor("MemMon", device, opts) mem_mon.start() diff --git a/modules/txt2img.py b/modules/txt2img.py index 70204293b..2fcb4c49d 100644 --- a/modules/txt2img.py +++ b/modules/txt2img.py @@ -7,8 +7,8 @@ import modules.shared as shared from modules.ui import plaintext_to_html -def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, override_settings_texts, *args): - override_settings = create_override_settings_dict(override_settings_texts) # pylint: disable=unused-argument +def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, override_settings_texts, *args): # pylint: disable=unused-argument + override_settings = create_override_settings_dict(override_settings_texts) p = StableDiffusionProcessingTxt2Img( sd_model=shared.sd_model, outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples, diff --git a/modules/ui.py b/modules/ui.py index 811e9261a..c811cd3eb 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -81,34 +81,25 @@ def visit(x, func, path=""): def add_style(name: str, prompt: str, negative_prompt: str): if name is None: return [gr_show() for x in range(4)] - style = modules.styles.PromptStyle(name, prompt, negative_prompt) shared.prompt_styles.styles[style.name] = style - # Save all loaded prompt styles: this allows us to update the storage format in the future more easily, because we - # reserialize all styles every time we save them shared.prompt_styles.save_styles(shared.opts.styles_dir) - return [gr.Dropdown.update(visible=True, choices=list(shared.prompt_styles.styles)) for _ in range(2)] def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y): from modules import processing, devices - if not enable: return "" - p = processing.StableDiffusionProcessingTxt2Img(width=width, height=height, enable_hr=True, hr_scale=hr_scale, hr_resize_x=hr_resize_x, hr_resize_y=hr_resize_y) - with devices.autocast(): p.init([""], [0], [0]) - return f"resize: from {p.width}x{p.height} to {p.hr_resize_x or p.hr_upscale_to_x}x{p.hr_resize_y or p.hr_upscale_to_y}" def apply_styles(prompt, prompt_neg, styles): prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles) prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, styles) - return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value=[])] @@ -125,7 +116,6 @@ def process_interrogate(interrogation_function, mode, ii_input_dir, ii_output_di os.makedirs(ii_output_dir, exist_ok=True) else: ii_output_dir = ii_input_dir - for image in images: img = Image.open(image) filename = os.path.basename(image) @@ -144,44 +134,35 @@ def interrogate_deepbooru(image): prompt = deepbooru.model.tag(image) return gr.update() if prompt is None else prompt + def change_clip_skip(val): shared.opts.CLIP_stop_at_last_layers = val + def create_seed_inputs(target_interface): with FormRow(elem_id=target_interface + '_seed_row', variant="compact"): seed = gr.Number(label='Seed', value=-1, elem_id=target_interface + '_seed') seed.style(container=False) random_seed = ToolButton(random_symbol, elem_id=target_interface + '_random_seed') reuse_seed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_seed') - seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=False, visible=False) # Ghost checkbox, so it still gets sent. For compatibility with extensions that call txt2img or img2img manually - with FormRow(visible=True, elem_id=target_interface + '_subseed_row'): subseed = gr.Number(label='Variation seed', value=-1, elem_id=target_interface + '_subseed') subseed.style(container=False) random_subseed = ToolButton(random_symbol, elem_id=target_interface + '_random_subseed') reuse_subseed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_subseed') subseed_strength = gr.Slider(label='Strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=target_interface + '_subseed_strength') - with FormRow(visible=False): seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=target_interface + '_seed_resize_from_w') seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from height", value=0, elem_id=target_interface + '_seed_resize_from_h') - random_seed.click(fn=lambda: [-1, -1], show_progress=False, inputs=[], outputs=[seed, subseed]) random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed]) - return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox - def connect_clear_prompt(button): """Given clear button, prompt, and token_counter objects, setup clear prompt button click event""" - button.click( - _js="clear_prompt", - fn=None, - inputs=[], - outputs=[], - ) + button.click(_js="clear_prompt", fn=None, inputs=[], outputs=[]) def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed): @@ -190,7 +171,6 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: was 0, i.e. no variation seed was used, it copies the normal seed value instead.""" def copy_seed(gen_info_string: str, index): res = -1 - try: gen_info = json.loads(gen_info_string) index -= gen_info.get('index_of_first_image', 0) @@ -201,35 +181,24 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: else: all_seeds = gen_info.get('all_seeds', [-1]) res = all_seeds[index if 0 <= index < len(all_seeds) else 0] - except json.decoder.JSONDecodeError: if gen_info_string != '': print("Error parsing JSON generation info:", file=sys.stderr) print(gen_info_string, file=sys.stderr) - return [res, gr_show(False)] - reuse_seed.click( - fn=copy_seed, - _js="(x, y) => [x, selected_gallery_index()]", - show_progress=False, - inputs=[generation_info, dummy_component], - outputs=[seed, dummy_component] - ) + reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component]) def update_token_counter(text, steps): try: text, _ = extra_networks.parse_prompt(text) - _, prompt_flat_list, _ = prompt_parser.get_multicond_prompt_list([text]) prompt_schedules = prompt_parser.get_learned_conditioning_prompt_schedules(prompt_flat_list, steps) - except Exception: # a parsing error can happen here during typing, and we don't want to bother the user with # messages related to it in console prompt_schedules = [[[steps, text]]] - flat_prompts = reduce(lambda list1, list2: list1+list2, prompt_schedules) prompts = [prompt_text for step, prompt_text in flat_prompts] token_count, max_length = max([model_hijack.get_prompt_lengths(prompt) for prompt in prompts], key=lambda args: args[0]) @@ -238,67 +207,43 @@ def update_token_counter(text, steps): def create_toprow(is_img2img): id_part = "img2img" if is_img2img else "txt2img" - with gr.Row(elem_id=f"{id_part}_toprow", variant="compact"): with gr.Column(elem_id=f"{id_part}_prompt_container", scale=6): with gr.Row(): with gr.Column(scale=80): with gr.Row(): prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)") - with gr.Row(): with gr.Column(scale=80): with gr.Row(): negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)") - button_interrogate = None button_deepbooru = None if is_img2img: with gr.Column(scale=1, elem_classes="interrogate-col"): button_interrogate = gr.Button('Interrogate\nCLIP', elem_id="interrogate") button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id="deepbooru") - with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"): with gr.Row(elem_id=f"{id_part}_generate_box", elem_classes="generate-box"): interrupt = gr.Button('Stop', elem_id=f"{id_part}_interrupt", elem_classes="generate-box-interrupt") skip = gr.Button('Skip', elem_id=f"{id_part}_skip", elem_classes="generate-box-skip") submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary') - - skip.click( - fn=lambda: shared.state.skip(), - inputs=[], - outputs=[], - ) - - interrupt.click( - fn=lambda: shared.state.interrupt(), - inputs=[], - outputs=[], - ) - + skip.click(fn=lambda: shared.state.skip(), inputs=[], outputs=[]) + interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) with gr.Row(elem_id=f"{id_part}_tools"): paste = ToolButton(value=paste_symbol, elem_id="paste") clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt") extra_networks_button = ToolButton(value=extra_networks_symbol, elem_id=f"{id_part}_extra_networks") prompt_style_apply = ToolButton(value=apply_style_symbol, elem_id=f"{id_part}_style_apply") save_style = ToolButton(value=save_style_symbol, elem_id=f"{id_part}_style_create") - token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"]) token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button") negative_token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_negative_token_counter", elem_classes=["token-counter"]) negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button") - - clear_prompt_button.click( - fn=lambda *x: x, - _js="confirm_clear_prompt", - inputs=[prompt, negative_prompt], - outputs=[prompt, negative_prompt], - ) - + clear_prompt_button.click(fn=lambda *x: x, _js="confirm_clear_prompt", inputs=[prompt, negative_prompt], outputs=[prompt, negative_prompt]) with gr.Row(elem_id=f"{id_part}_styles_row"): prompt_styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[k for k, v in shared.prompt_styles.styles.items()], value=[], multiselect=True) create_refresh_button(prompt_styles, shared.prompt_styles.reload, lambda: {"choices": [k for k, v in shared.prompt_styles.styles.items()]}, f"refresh_{id_part}_styles") - return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, prompt_style_apply, save_style, paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button @@ -309,32 +254,25 @@ def setup_progressbar(*args, **kwargs): # pylint: disable=unused-argument def apply_setting(key, value): if value is None: return gr.update() - if shared.cmd_opts.freeze_settings: return gr.update() - # dont allow model to be swapped when model hash exists in prompt if key == "sd_model_checkpoint" and opts.disable_weights_auto_swap: return gr.update() - if key == "sd_model_checkpoint": ckpt_info = sd_models.get_closet_checkpoint_match(value) - if ckpt_info is not None: value = ckpt_info.title else: return gr.update() - comp_args = opts.data_labels[key].component_args if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False: return - valtype = type(opts.data_labels[key].default) oldval = opts.data.get(key, None) opts.data[key] = valtype(value) if valtype != type(None) else value if oldval != value and opts.data_labels[key].onchange is not None: opts.data_labels[key].onchange() - opts.save(shared.config_filename) return getattr(opts, key) @@ -343,18 +281,12 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args, ele def refresh(): refresh_method() args = refreshed_args() if callable(refreshed_args) else refreshed_args - for k, v in args.items(): setattr(refresh_component, k, v) - return gr.update(**(args or {})) refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id) - refresh_button.click( - fn=refresh, - inputs=[], - outputs=[refresh_component] - ) + refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component]) return refresh_button @@ -366,117 +298,89 @@ def create_sampler_and_steps_selection(choices, tabname): with FormRow(elem_id=f"sampler_selection_{tabname}"): sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value="UniPC" if tabname == 'txt2img' else "Euler a", type="index") steps = gr.Slider(minimum=1, maximum=150, step=1, elem_id=f"{tabname}_steps", label="Sampling steps", value=10 if tabname == 'txt2img' else 20) - return steps, sampler_index def ordered_ui_categories(): user_order = {x.strip(): i * 2 + 1 for i, x in enumerate(shared.opts.ui_reorder.split(","))} - for i, category in sorted(enumerate(shared.ui_reorder_categories), key=lambda x: user_order.get(x[1], x[0] * 2 + 0)): yield category def get_value_for_setting(key): value = getattr(opts, key) - info = opts.data_labels[key] args = info.component_args() if callable(info.component_args) else info.component_args or {} args = {k: v for k, v in args.items() if k not in {'precision'}} - return gr.update(value=value, **args) def create_override_settings_dropdown(tabname, row): # pylint: disable=unused-argument dropdown = gr.Dropdown([], label="Override settings", visible=False, elem_id=f"{tabname}_override_settings", multiselect=True) - - dropdown.change( - fn=lambda x: gr.Dropdown.update(visible=len(x) > 0), - inputs=[dropdown], - outputs=[dropdown], - ) - + dropdown.change(fn=lambda x: gr.Dropdown.update(visible=len(x) > 0), inputs=[dropdown], outputs=[dropdown]) return dropdown def create_ui(): import modules.img2img # pylint: disable=redefined-outer-name import modules.txt2img # pylint: disable=redefined-outer-name - reload_javascript() - parameters_copypaste.reset() - modules.scripts.scripts_current = modules.scripts.scripts_txt2img modules.scripts.scripts_txt2img.initialize_scripts(is_img2img=False) - with gr.Blocks(analytics_enabled=False) as txt2img_interface: txt2img_prompt, txt2img_prompt_styles, txt2img_negative_prompt, submit, _, _, txt2img_prompt_style_apply, txt2img_save_style, txt2img_paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button = create_toprow(is_img2img=False) - dummy_component = gr.Label(visible=False) txt_prompt_img = gr.File(label="", elem_id="txt2img_prompt_image", file_count="single", type="binary", visible=False) - with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks_ui: from modules import ui_extra_networks extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'txt2img') - with gr.Row().style(equal_height=False): with gr.Column(variant='compact', elem_id="txt2img_settings"): for category in ordered_ui_categories(): if category == "sampler": steps, sampler_index = create_sampler_and_steps_selection(samplers, "txt2img") - elif category == "dimensions": with FormRow(): with gr.Column(elem_id="txt2img_column_size", scale=4): with FormRow(elem_id="txt2img_row_dimension"): width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="txt2img_width") height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") - with gr.Column(elem_id="txt2img_dimensions_row", scale=1, elem_classes="dimensions-tools"): res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn") - with gr.Column(elem_id="txt2img_column_batch"): with FormRow(elem_id="txt2img_row_batch"): batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") - elif category == "cfg": with FormRow(): cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="txt2img_cfg_scale") clip_skip = gr.Slider(label='CLIP Skip', value=1, minimum=1, maximum=4, step=1, elem_id='txt2img_clip_skip', interactive=True) clip_skip.change(fn=change_clip_skip, show_progress=False, inputs=clip_skip) - elif category == "seed": seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('txt2img') - elif category == "checkboxes": with FormRow(elem_classes="checkboxes-row", variant="compact"): restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1, elem_id="txt2img_restore_faces") tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling") enable_hr = gr.Checkbox(label='Hires fix', value=False, elem_id="txt2img_enable_hr") hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) - elif category == "hires_fix": with FormGroup(visible=False, elem_id="txt2img_hires_fix") as hr_options: with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): hr_upscaler = gr.Dropdown(label="Upscaler", elem_id="txt2img_hr_upscaler", choices=[*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]], value=shared.latent_upscale_default_mode) hr_second_pass_steps = gr.Slider(minimum=0, maximum=150, step=1, label='Hires steps', value=0, elem_id="txt2img_hires_steps") denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7, elem_id="txt2img_denoising_strength") - with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"): hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale") hr_resize_x = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x") hr_resize_y = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y") - elif category == "override_settings": with FormRow(elem_id="txt2img_override_settings_row") as row: override_settings = create_override_settings_dropdown('txt2img', row) - elif category == "scripts": with FormGroup(elem_id="txt2img_script_container"): custom_inputs = modules.scripts.scripts_txt2img.setup_ui() - hr_resolution_preview_inputs = [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y] for preview_input in hr_resolution_preview_inputs: preview_input.change( @@ -494,7 +398,6 @@ def create_ui(): ) txt2img_gallery, generation_info, html_info, html_log = create_output_panel("txt2img", opts.outdir_txt2img_samples) - connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) @@ -1297,9 +1200,7 @@ def create_ui(): info = opts.data_labels[key] t = type(info.default) - args = info.component_args() if callable(info.component_args) else info.component_args - if info.component is not None: comp = info.component elif t == str: @@ -1310,9 +1211,7 @@ def create_ui(): comp = gr.Checkbox else: raise ValueError(f'bad options item type: {str(t)} for key {key}') - elem_id = "setting_"+key - if info.refresh is not None: if is_quicksettings: res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {})) @@ -1323,7 +1222,6 @@ def create_ui(): create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key) else: res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {})) - return res components = [] diff --git a/script.js b/script.js index 03afe8445..9b0eebe03 100644 --- a/script.js +++ b/script.js @@ -1,7 +1,6 @@ function gradioApp() { const elems = document.getElementsByTagName('gradio-app') const elem = elems.length == 0 ? document : elems[0] - if (elem !== document) elem.getElementById = function(id){ return document.getElementById(id) } return elem.shadowRoot ? elem.shadowRoot : elem } @@ -34,12 +33,10 @@ function onOptionsChanged(callback){ } function runCallback(x, m){ - try { - x(m) - } catch (e) { - (console.error || console.log).call(console, e.message, e); - } + try { x(m) + } catch (e) { (console.error || console.log).call(console, e.message, e); } } + function executeCallbacks(queue, m) { queue.forEach(function(x){runCallback(x, m)}) } @@ -52,7 +49,6 @@ document.addEventListener("DOMContentLoaded", function() { executedOnLoaded = true; executeCallbacks(uiLoadedCallbacks); } - executeCallbacks(uiUpdateCallbacks, m); const newTab = get_uiCurrentTab(); if ( newTab && ( newTab !== uiCurrentTab ) ) { @@ -75,9 +71,7 @@ document.addEventListener('keydown', function(e) { } if (handled) { button = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); - if (button) { - button.click(); - } + if (button) button.click(); e.preventDefault(); } }) @@ -87,18 +81,11 @@ document.addEventListener('keydown', function(e) { */ function uiElementIsVisible(el) { let isVisible = !el.closest('.\\!hidden'); - if ( ! isVisible ) { - return false; - } - + 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; - } + if ( ! isVisible ) return false; + else if ( el.parentElement ) el = el.parentElement + else break; } return isVisible; } diff --git a/webui.bat b/webui.bat index 209d972bd..6a6edcf10 100755 --- a/webui.bat +++ b/webui.bat @@ -2,10 +2,7 @@ if not defined PYTHON (set PYTHON=python) if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv") - - set ERROR_REPORTING=FALSE - mkdir tmp 2>NUL %PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt @@ -38,14 +35,14 @@ goto :show_stdout_stderr :activate_venv set PYTHON="%VENV_DIR%\Scripts\Python.exe" -echo venv %PYTHON% +echo Using VENV: %VENV_DIR% :skip_venv if [%ACCELERATE%] == ["True"] goto :accelerate goto :launch :accelerate -echo Checking for accelerate +echo Checking for accelerate: %ACCELERATE% set ACCELERATE="%VENV_DIR%\Scripts\accelerate.exe" if EXIST %ACCELERATE% goto :accelerate_launch @@ -56,7 +53,7 @@ exit /b :accelerate_launch echo Accelerating -%ACCELERATE% launch --num_cpu_threads_per_process=6 launch.py +%ACCELERATE% launch --num_cpu_threads_per_process=6 launch.py %* pause exit /b diff --git a/webui.sh b/webui.sh index ebbe586fb..75725a712 100755 --- a/webui.sh +++ b/webui.sh @@ -4,34 +4,16 @@ # change the variables in webui-user.sh instead # ################################################# -# If run from macOS, load defaults from webui-macos-env.sh -if [[ "$OSTYPE" == "darwin"* ]]; then - if [[ -f webui-macos-env.sh ]] - then - source ./webui-macos-env.sh - fi -fi +can_run_as_root=0 +export ERROR_REPORTING=FALSE +export PIP_IGNORE_INSTALLED=0 # Read variables from webui-user.sh -# shellcheck source=/dev/null if [[ -f webui-user.sh ]] then source ./webui-user.sh fi -# Set defaults -# Install directory without trailing slash -if [[ -z "${install_dir}" ]] -then - install_dir="${HOME}" -fi - -# Name of the subdirectory (defaults to stable-diffusion-webui) -if [[ -z "${clone_dir}" ]] -then - clone_dir="stable-diffusion-webui" -fi - # python3 executable if [[ -z "${python_cmd}" ]] then @@ -44,19 +26,11 @@ then export GIT="git" fi -# python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv) if [[ -z "${venv_dir}" ]] then venv_dir="venv" fi -if [[ -z "${LAUNCH_SCRIPT}" ]] -then - LAUNCH_SCRIPT="launch.py" -fi - -# this script cannot be run as root by default -can_run_as_root=0 # read any command line flags to the webui.sh script while getopts "f" flag > /dev/null 2>&1 @@ -67,102 +41,48 @@ do esac done -# Disable sentry logging -export ERROR_REPORTING=FALSE - -# Do not reinstall existing pip packages on Debian/Ubuntu -export PIP_IGNORE_INSTALLED=0 - -# Pretty print -delimiter="################################################################" - -printf "\n%s\n" "${delimiter}" -printf "\e[1m\e[32mInstall script for stable-diffusion + Web UI\n" -printf "\e[1m\e[34mTested on Debian 11 (Bullseye)\e[0m" -printf "\n%s\n" "${delimiter}" - # Do not run as root if [[ $(id -u) -eq 0 && can_run_as_root -eq 0 ]] then - printf "\n%s\n" "${delimiter}" - printf "\e[1m\e[31mERROR: This script must not be launched as root, aborting...\e[0m" - printf "\n%s\n" "${delimiter}" + echo "Cannot run as root" exit 1 -else - printf "\n%s\n" "${delimiter}" - printf "Running on \e[1m\e[32m%s\e[0m user" "$(whoami)" - printf "\n%s\n" "${delimiter}" -fi - -if [[ -d .git ]] -then - printf "\n%s\n" "${delimiter}" - printf "Repo already cloned, using it as install directory" - printf "\n%s\n" "${delimiter}" - install_dir="${PWD}/../" - clone_dir="${PWD##*/}" fi for preq in "${GIT}" "${python_cmd}" do if ! hash "${preq}" &>/dev/null then - printf "\n%s\n" "${delimiter}" - printf "\e[1m\e[31mERROR: %s is not installed, aborting...\e[0m" "${preq}" - printf "\n%s\n" "${delimiter}" + printf "Error: %s is not installed, aborting...\n" "${preq}" exit 1 fi done if ! "${python_cmd}" -c "import venv" &>/dev/null then - printf "\n%s\n" "${delimiter}" - printf "\e[1m\e[31mERROR: python3-venv is not installed, aborting...\e[0m" - printf "\n%s\n" "${delimiter}" + echo "Error: python3-venv is not installed" exit 1 fi -cd "${install_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/, aborting...\e[0m" "${install_dir}"; exit 1; } -if [[ -d "${clone_dir}" ]] -then - cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } -else - printf "\n%s\n" "${delimiter}" - printf "Clone stable-diffusion-webui" - printf "\n%s\n" "${delimiter}" - "${GIT}" clone https://github.com/vladmandic/automatic.git "${clone_dir}" - cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } -fi - -printf "\n%s\n" "${delimiter}" -printf "Create and activate python venv" -printf "\n%s\n" "${delimiter}" -cd "${install_dir}"/"${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } +echo "Create and activate python venv" if [[ ! -d "${venv_dir}" ]] then "${python_cmd}" -m venv "${venv_dir}" first_launch=1 fi -# shellcheck source=/dev/null + if [[ -f "${venv_dir}"/bin/activate ]] then source "${venv_dir}"/bin/activate else - printf "\n%s\n" "${delimiter}" - printf "\e[1m\e[31mERROR: Cannot activate python venv, aborting...\e[0m" - printf "\n%s\n" "${delimiter}" + echo "Error: Cannot activate python venv" exit 1 fi if [[ ! -z "${ACCELERATE}" ]] && [ ${ACCELERATE}="True" ] && [ -x "$(command -v accelerate)" ] then - printf "\n%s\n" "${delimiter}" - printf "Accelerating launch.py..." - printf "\n%s\n" "${delimiter}" - exec accelerate launch --num_cpu_threads_per_process=6 "${LAUNCH_SCRIPT}" "$@" + echo "Accelerating launch.py..." + exec accelerate launch --num_cpu_threads_per_process=6 launch.py "$@" else - printf "\n%s\n" "${delimiter}" - printf "Launching launch.py..." - printf "\n%s\n" "${delimiter}" - exec "${python_cmd}" "${LAUNCH_SCRIPT}" "$@" + echo "Launching launch.py..." + exec "${python_cmd}" launch.py "$@" fi From bfe62127e9d90015ba4c24df55333163bf53c73a Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 27 Apr 2023 15:47:13 -0400 Subject: [PATCH 25/45] update --- TODO.md | 4 ---- modules/cmd_args.py | 1 - modules/shared.py | 1 + 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index d2939f9fa..ed0b7c875 100644 --- a/TODO.md +++ b/TODO.md @@ -61,7 +61,3 @@ Tech that can be integrated as part of the core workflow... ### Pending Code Updates -- ability to view/add/edit model description shown in extra networks cards -- add option to specify fallback sampler if primary sampler is not compatible with desired operation -- make clip skip a local parameter -- remove obsolete items from settings diff --git a/modules/cmd_args.py b/modules/cmd_args.py index a1afcd2bd..87b47d6c7 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -74,7 +74,6 @@ def compatibility_args(opts, args): parser.add_argument("--sub-quad-q-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_q_chunk_size) parser.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size) parser.add_argument("--sub-quad-chunk-threshold", help=argparse.SUPPRESS, default=opts.sub_quad_chunk_threshold) - parser.add_argument("--dimensions-and-batch-together", help=argparse.SUPPRESS, default=True) opts.use_old_emphasis_implementation = False opts.use_old_karras_scheduler_sigmas = False diff --git a/modules/shared.py b/modules/shared.py index adccb4812..4d456a6be 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -243,6 +243,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "always_batch_cond_uncond": OptionInfo(False, "Disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram"), "multiple_tqdm": OptionInfo(False, "Add a second progress bar to the console that shows progress for an entire job.", gr.Checkbox, {"visible": False}), "print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console.", gr.Checkbox, {"visible": False}), + "dimensions_and_batch_together": OptionInfo(False, "", gr.Checkbox, {"visible": False}), })) options_templates.update(options_section(('system-paths', "System Paths"), { From 1a978be2710b65693c7abb01d98283fed06362ff Mon Sep 17 00:00:00 2001 From: derVedro Date: Thu, 27 Apr 2023 23:58:33 +0200 Subject: [PATCH 26/45] Update webui.sh webui can launch from any directory --- webui.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webui.sh b/webui.sh index 75725a712..ff9c58b4d 100755 --- a/webui.sh +++ b/webui.sh @@ -4,6 +4,9 @@ # change the variables in webui-user.sh instead # ################################################# +# change to local directory +cd -- "$(dirname -- "$0")" + can_run_as_root=0 export ERROR_REPORTING=FALSE export PIP_IGNORE_INSTALLED=0 From e75a19c09017bc28d4afec2024c2023d0cea5723 Mon Sep 17 00:00:00 2001 From: Ryan Harden Date: Thu, 27 Apr 2023 20:47:47 -0500 Subject: [PATCH 27/45] Renable Zip button in black-orange.css --- javascript/black-orange.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/black-orange.css b/javascript/black-orange.css index 5ecdedac1..52e601529 100644 --- a/javascript/black-orange.css +++ b/javascript/black-orange.css @@ -81,7 +81,7 @@ svg.feather.feather-image, .feather .feather-image { display: none } #quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; } #quicksettings > div, #quicksettings > fieldset { min-width: 26em; max-width: 26em; line-height: 2em; } #refresh_sd_model_checkpoint { height: 40px; margin-left: -14px; background: #333333; box-shadow: none; } -#refresh_txt2img_styles, #refresh_img2img_styles, #open_folder_txt2img, #open_folder_img2img, #open_folder_extras, #footer, #style_pos_col, #style_neg_col, #roll_col, #save_zip_txt2img, #save_zip_img2img, #extras_upscaler_2, #extras_upscaler_2_visibility, #txt2img_res_switch_btn, #img2img_res_switch_btn, #txt2img_seed_resize_from_w, #txt2img_seed_resize_from_h, #txt2img_tiling { display: none; } +#refresh_txt2img_styles, #refresh_img2img_styles, #open_folder_txt2img, #open_folder_img2img, #open_folder_extras, #footer, #style_pos_col, #style_neg_col, #roll_col, #extras_upscaler_2, #extras_upscaler_2_visibility, #txt2img_res_switch_btn, #img2img_res_switch_btn, #txt2img_seed_resize_from_w, #txt2img_seed_resize_from_h, #txt2img_tiling { display: none; } #save-animation { border-radius: 0 !important; margin-bottom: 16px; background-color: #111111; } #script_list { padding: 4px; margin-top: 20px; margin-bottom: 20px; } #settings > div.flex-wrap { width: 15em; } From f9d636e427b4ae636d94979930a4dcfcbd2cb4d1 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 09:13:14 -0400 Subject: [PATCH 28/45] update models_dir --- extensions-builtin/seed_travel | 2 +- extensions-builtin/stable-diffusion-webui-images-browser | 2 +- modules/paths_internal.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions-builtin/seed_travel b/extensions-builtin/seed_travel index ffe0553c5..4bc8b2f10 160000 --- a/extensions-builtin/seed_travel +++ b/extensions-builtin/seed_travel @@ -1 +1 @@ -Subproject commit ffe0553c59e91067ebf1e4fc7ad85ca9c870bf57 +Subproject commit 4bc8b2f10d5c12958f48b67ad23fb445aff074df diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser index 704e42c10..2c988c08c 160000 --- a/extensions-builtin/stable-diffusion-webui-images-browser +++ b/extensions-builtin/stable-diffusion-webui-images-browser @@ -1 +1 @@ -Subproject commit 704e42c10d01e6c6965493ec956a82bb8fc2da51 +Subproject commit 2c988c08c7fc2f1c0f572bc4209f0baa1fac4fee diff --git a/modules/paths_internal.py b/modules/paths_internal.py index b5d81df51..2601e4709 100644 --- a/modules/paths_internal.py +++ b/modules/paths_internal.py @@ -15,6 +15,6 @@ parser_pre.add_argument("--data-dir", type=str, default=os.path.dirname(os.path. parser_pre.add_argument("--models-dir", type=str, default="models", help="base path where all models are stored",) cmd_opts_pre = parser_pre.parse_known_args()[0] data_path = cmd_opts_pre.data_dir -models_path = os.path.join(data_path, cmd_opts_pre.models_dir) +models_path = cmd_opts_pre.models_dir if os.path.isabs(cmd_opts_pre.models_dir) else os.path.join(data_path, cmd_opts_pre.models_dir) extensions_dir = os.path.join(data_path, "extensions") extensions_builtin_dir = os.path.join(script_path, "extensions-builtin") From 57241e256aa379bd74955383f857177cf09cc0cb Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 09:28:59 -0400 Subject: [PATCH 29/45] handle clip_skip is none --- modules/sd_hijack_clip.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/sd_hijack_clip.py b/modules/sd_hijack_clip.py index b979ed6f9..945f7732d 100644 --- a/modules/sd_hijack_clip.py +++ b/modules/sd_hijack_clip.py @@ -295,6 +295,8 @@ class FrozenCLIPEmbedderWithCustomWords(FrozenCLIPEmbedderWithCustomWordsBase): return tokenized def encode_with_transformers(self, tokens): + if opts.CLIP_stop_at_last_layers is None: + opts.CLIP_stop_at_last_layers = 1 outputs = self.wrapped.transformer(input_ids=tokens, output_hidden_states=-opts.CLIP_stop_at_last_layers) if opts.CLIP_stop_at_last_layers > 1: From 21ff7bad67e65294c14b6d50d8a0794f08cfd017 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 09:42:19 -0400 Subject: [PATCH 30/45] configurable train log --- modules/shared.py | 1 + modules/textual_inversion/textual_inversion.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 4d456a6be..f44ab2f2a 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -253,6 +253,7 @@ options_templates.update(options_section(('system-paths', "System Paths"), { "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Path to directory with VAE files"), "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Embeddings directory for textual inversion"), "embeddings_templates_dir": OptionInfo(os.path.join(paths.script_path, 'train/templates'), "Embeddings train templates directory"), + "embeddings_train_log": OptionInfo(os.path.join(paths.script_path, 'train.csv'), "Embeddings train log file"), "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Hypernetwork directory"), "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Path to directory with codeformer model file(s)."), "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Path to directory with GFPGAN model file(s)"), diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 24ccf743f..75bd1cd88 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -526,7 +526,7 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st save_embedding(embedding, optimizer, checkpoint, embedding_name_every, last_saved_file, remove_cached_checksum=True) embedding_yet_to_be_embedded = True - write_loss(log_directory, "train.csv", embedding.step, steps_per_epoch, { + write_loss(log_directory, shared.ops.embeddings_train_log, embedding.step, steps_per_epoch, { "loss": f"{loss_step:.7f}", "learn_rate": scheduler.learn_rate }) From 07a589b1b3b1a391db41d07ee2c4476e5ac98c92 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 09:52:29 -0400 Subject: [PATCH 31/45] update settings --- modules/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index f44ab2f2a..d3720db6b 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -243,7 +243,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "always_batch_cond_uncond": OptionInfo(False, "Disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram"), "multiple_tqdm": OptionInfo(False, "Add a second progress bar to the console that shows progress for an entire job.", gr.Checkbox, {"visible": False}), "print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console.", gr.Checkbox, {"visible": False}), - "dimensions_and_batch_together": OptionInfo(False, "", gr.Checkbox, {"visible": False}), + "dimensions_and_batch_together": OptionInfo(True, "", gr.Checkbox, {"visible": False}), })) options_templates.update(options_section(('system-paths', "System Paths"), { From 99e3fceedb2b3ecc34951d3a86148e7e39b53745 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 13:09:34 -0400 Subject: [PATCH 32/45] cleanup scripts --- .gitignore | 4 +++ TODO.md | 4 --- modules/processing.py | 2 ++ scripts/img2imgalt.py | 54 ++++---------------------------- scripts/outpainting_mk_2.py | 2 +- scripts/poor_mans_outpainting.py | 2 +- scripts/prompts_from_file.py | 2 +- setup.py | 1 + webui.py | 5 +-- 9 files changed, 17 insertions(+), 59 deletions(-) diff --git a/.gitignore b/.gitignore index 13e8eab0c..7d839b593 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,10 @@ venv *.zip *.rar *.pyc +/*.bat +/*.sh +!webui.bat +!webui.sh # all dynamic stuff /repositories/**/* diff --git a/TODO.md b/TODO.md index ed0b7c875..a4e6f6d66 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,6 @@ Stuff to be fixed... -- ClipSkip not updated on read gen info - Run VAE with hires at 1280 - Transformers version - Move Restart Server from WebUI to Launch and reload modules @@ -19,9 +18,6 @@ Stuff to be added... - Create new GitHub hooks/actions for CI/CD - Redo Extensions tab: see - Stream-load models as option for slow storage -- AMD optimizations -- Apple optimizations -- Support multiple models locations ## Investigate diff --git a/modules/processing.py b/modules/processing.py index 597ea5bee..04379fabe 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -1024,6 +1024,8 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.image_conditioning = None def init(self, all_prompts, all_seeds, all_subseeds): + if self.sampler_name in ['PLMS', 'UniPC']: # PLMS/UniPC do not support img2img so we just silently switch to DDIM + self.sampler_name = shared.opts.fallback_sampler self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) crop_region = None diff --git a/scripts/img2imgalt.py b/scripts/img2imgalt.py index bb00fb3f1..3066100a2 100644 --- a/scripts/img2imgalt.py +++ b/scripts/img2imgalt.py @@ -1,19 +1,15 @@ from collections import namedtuple - import numpy as np from tqdm import trange - -import modules.scripts as scripts -import gradio as gr - -from modules import processing, shared, sd_samplers, sd_samplers_common - import torch import k_diffusion as K +import gradio as gr +import modules.scripts as scripts +from modules import processing, shared, sd_samplers, sd_samplers_common + def find_noise_for_image(p, cond, uncond, cfg_scale, steps): x = p.init_latent - s_in = x.new_ones([x.shape[0]]) if shared.sd_model.parameterization == "v": dnw = K.external.CompVisVDenoiser(shared.sd_model) @@ -22,40 +18,29 @@ def find_noise_for_image(p, cond, uncond, cfg_scale, steps): dnw = K.external.CompVisDenoiser(shared.sd_model) skip = 0 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] * s_in] * 2) cond_in = torch.cat([uncond, cond]) - image_conditioning = torch.cat([p.image_conditioning] * 2) cond_in = {"c_concat": [image_conditioning], "c_crossattn": [cond_in]} - c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)[skip:]] 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 - d = (x - denoised) / sigmas[i] dt = sigmas[i] - sigmas[i - 1] - x = x + d * dt - sd_samplers_common.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 / x.std() @@ -65,7 +50,6 @@ Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "origina # 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]]) if shared.sd_model.parameterization == "v": dnw = K.external.CompVisVDenoiser(shared.sd_model) @@ -79,42 +63,31 @@ def find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg_scale, 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]) - image_conditioning = torch.cat([p.image_conditioning] * 2) cond_in = {"c_concat": [image_conditioning], "c_crossattn": [cond_in]} - c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)[skip:]] - 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_common.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] @@ -123,7 +96,7 @@ class Script(scripts.Script): self.cache = None def title(self): - return "img2img alternative test" + return "Alternative" def show(self, is_img2img): return is_img2img @@ -132,24 +105,19 @@ class Script(scripts.Script): info = gr.Markdown(''' * `CFG Scale` should be 2 or lower. ''') - override_sampler = gr.Checkbox(label="Override `Sampling method` to Euler?(this method is built for it)", value=True, elem_id=self.elem_id("override_sampler")) - override_prompt = gr.Checkbox(label="Override `prompt` to the same value as `original prompt`?(and `negative prompt`)", value=True, elem_id=self.elem_id("override_prompt")) original_prompt = gr.Textbox(label="Original prompt", lines=1, elem_id=self.elem_id("original_prompt")) original_negative_prompt = gr.Textbox(label="Original negative prompt", lines=1, elem_id=self.elem_id("original_negative_prompt")) - override_steps = gr.Checkbox(label="Override `Sampling Steps` to the same value as `Decode steps`?", value=True, elem_id=self.elem_id("override_steps")) st = gr.Slider(label="Decode steps", minimum=1, maximum=150, step=1, value=50, elem_id=self.elem_id("st")) - override_strength = gr.Checkbox(label="Override `Denoising strength` to 1?", value=True, elem_id=self.elem_id("override_strength")) - cfg = gr.Slider(label="Decode CFG scale", minimum=0.0, maximum=15.0, step=0.1, value=1.0, elem_id=self.elem_id("cfg")) randomness = gr.Slider(label="Randomness", minimum=0.0, maximum=1.0, step=0.01, value=0.0, elem_id=self.elem_id("randomness")) sigma_adjustment = gr.Checkbox(label="Sigma adjustment for finding noise for image", value=False, elem_id=self.elem_id("sigma_adjustment")) return [ - info, + info, override_sampler, override_prompt, original_prompt, original_negative_prompt, override_steps, st, @@ -171,13 +139,11 @@ class Script(scripts.Script): def sample_extra(conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts): 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 \ 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: rec_noise = self.cache.noise else: @@ -191,28 +157,20 @@ class Script(scripts.Script): 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:], seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w, p=p) - combined_noise = ((1 - randomness) * rec_noise + randomness * rand_noise) / ((randomness**2 + (1-randomness)**2) ** 0.5) - sampler = sd_samplers.create_sampler(p.sampler_name, p.sd_model) - sigmas = sampler.model_wrap.get_sigmas(p.steps) - noise_dt = combined_noise - (p.init_latent / sigmas[0]) - p.seed = p.seed + 1 - return sampler.sample_img2img(p, p.init_latent, noise_dt, conditioning, unconditional_conditioning, image_conditioning=p.image_conditioning) p.sample = sample_extra - p.extra_generation_params["Decode prompt"] = original_prompt p.extra_generation_params["Decode negative prompt"] = original_negative_prompt 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) return processed diff --git a/scripts/outpainting_mk_2.py b/scripts/outpainting_mk_2.py index 670bb8ace..4e764fee4 100644 --- a/scripts/outpainting_mk_2.py +++ b/scripts/outpainting_mk_2.py @@ -120,7 +120,7 @@ def get_matched_noise(_np_src_image, np_mask_rgb, noise_q=1, color_variation=0.0 class Script(scripts.Script): def title(self): - return "Outpainting mk2" + return "Outpainting" def show(self, is_img2img): return is_img2img diff --git a/scripts/poor_mans_outpainting.py b/scripts/poor_mans_outpainting.py index ddcbd2d3a..e80478b7c 100644 --- a/scripts/poor_mans_outpainting.py +++ b/scripts/poor_mans_outpainting.py @@ -11,7 +11,7 @@ from modules.shared import opts, cmd_opts, state class Script(scripts.Script): def title(self): - return "Poor man's outpainting" + return "Outpainting alternative" def show(self, is_img2img): return is_img2img diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index e6d5c5a39..027e6539f 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -109,7 +109,7 @@ def load_prompt_file(file): class Script(scripts.Script): def title(self): - return "Prompts from file or textbox" + return "Prompts from file" def ui(self, is_img2img): checkbox_iterate = gr.Checkbox(label="Iterate seed every line", value=False, elem_id=self.elem_id("checkbox_iterate")) diff --git a/setup.py b/setup.py index 8d4099912..72cb12c90 100644 --- a/setup.py +++ b/setup.py @@ -51,6 +51,7 @@ def setup_logging(clean=False): rh.set_name(logging.DEBUG if args.debug else logging.INFO) log.addHandler(rh) + # check if package is installed def installed(package, friendly: str = None): import pkg_resources diff --git a/webui.py b/webui.py index 20c621818..54d159d24 100644 --- a/webui.py +++ b/webui.py @@ -69,7 +69,7 @@ else: def check_rollback_vae(): if shared.cmd_opts.rollback_vae: if not torch.cuda.is_available(): - print("Rollback VAE functionality requires CUDA support") + print("Rollback VAE functionality requires compatible GPU") shared.cmd_opts.rollback_vae = False elif not torch.__version__.startswith('2.1'): print("Rollback VAE functionality requires Torch 2.1 or higher") @@ -95,9 +95,6 @@ def initialize(): gfpgan.setup_model(opts.gfpgan_models_path) startup_timer.record("gfpgan") - modelloader.list_builtin_upscalers() - startup_timer.record("upscalers") - modules.scripts.load_scripts() startup_timer.record("scripts") From 1f8f1c60c84d7f0e6718813643a86ac718716b31 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 13:19:23 -0400 Subject: [PATCH 33/45] update cli requirements --- .gitignore | 1 + cli/requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7d839b593..06c8f11c5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ venv *.pyc /*.bat /*.sh +/*.txt !webui.bat !webui.sh diff --git a/cli/requirements.txt b/cli/requirements.txt index 77e0c427a..b8f807b41 100644 --- a/cli/requirements.txt +++ b/cli/requirements.txt @@ -1,3 +1,4 @@ mediapipe colormap invisible-watermark +filetype From eeea06b9e1ef2c1ba7799be88dc89208a5098680 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 13:51:27 -0400 Subject: [PATCH 34/45] update todo --- TODO.md | 3 ++- setup.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index a4e6f6d66..167a823ec 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,8 @@ Stuff to be fixed... - Run VAE with hires at 1280 - Transformers version - Move Restart Server from WebUI to Launch and reload modules -- follow-up on `p.script_args` +- Follow-up on `p.script_args` +- Mdularize `cli` scripts ## Features diff --git a/setup.py b/setup.py index 72cb12c90..d368b44f3 100644 --- a/setup.py +++ b/setup.py @@ -392,6 +392,9 @@ def check_extensions(): for ext in extensions: newest = 0 extension_dir = os.path.join(folder, ext) + if not os.path.isdir(extension_dir): + log.debug(f'Extension listed as installed but folder missing: {extension_dir}') + continue for f in os.listdir(extension_dir): if '.json' in f or '.csv' in f or '__pycache__' in f: continue From 21ea7d2c89ba73052c3047f2d36d683ea284b85d Mon Sep 17 00:00:00 2001 From: Thomas Young <35073576+DrakeRichards@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:06:57 -0500 Subject: [PATCH 35/45] Moved exra network close button --- javascript/extraNetworks.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index fc8319e91..19b69ec8b 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -11,9 +11,8 @@ function setupExtraNetworksForTab(tabname){ search.classList.add('search') tabs.appendChild(search) tabs.appendChild(refresh) - tabs.appendChild(descriptInput) - tabs.appendChild(close) + tabs.appendChild(descriptInput) search.addEventListener("input", function(evt){ searchTerm = search.value.toLowerCase() From 9a09b2eef30264490a0b6f155f8044a47cfe2a04 Mon Sep 17 00:00:00 2001 From: cool-bigdogs-tshirt <131823432+cool-bigdogs-tshirt@users.noreply.github.com> Date: Thu, 27 Apr 2023 11:24:26 -0500 Subject: [PATCH 36/45] attempt at unipc latent upscaling i should have taken linear algebra before i dropped out... --- modules/models/diffusion/uni_pc/sampler.py | 74 ++++++++++++++++++++++ modules/processing.py | 3 +- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/modules/models/diffusion/uni_pc/sampler.py b/modules/models/diffusion/uni_pc/sampler.py index a241c8a7c..41b8c9a5b 100644 --- a/modules/models/diffusion/uni_pc/sampler.py +++ b/modules/models/diffusion/uni_pc/sampler.py @@ -4,6 +4,7 @@ import torch from .uni_pc import NoiseScheduleVP, model_wrapper, UniPC from modules import shared, devices +from ldm.modules.diffusionmodules.util import extract_into_tensor class UniPCSampler(object): @@ -15,6 +16,79 @@ class UniPCSampler(object): self.after_sample = None self.register_buffer('alphas_cumprod', to_torch(model.alphas_cumprod)) + def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True): + # persist steps so we can eventually find denoising strength + self.inflated_steps = ddim_num_steps + + @torch.no_grad() + def stochastic_encode(self, x0, t, use_original_steps=False, noise=None): + if noise is None: + noise = torch.randn_like(x0) + + # first time we have all the info to get the real parameters from the ui + hires_steps = t[0] + 1 + inflated_steps = self.inflated_steps + self.denoising_strength = hires_steps/inflated_steps + + adjusted_steps = int(hires_steps * self.denoising_strength) + self.steps = max(adjusted_steps, shared.opts.uni_pc_order+1) + + t = torch.full(t.shape, self.steps).to(t.device) + + timesteps = torch.asarray(list(range( + t, + self.model.num_timesteps, + self.model.num_timesteps // hires_steps, + ))) + 1 + alphas = self.model.alphas_cumprod[timesteps] + sqrt_one_minus_alphas = torch.sqrt(1. - alphas) + a = extract_into_tensor(torch.sqrt(alphas), t, x0.shape) * x0 + b = extract_into_tensor(sqrt_one_minus_alphas, t, x0.shape) * noise + + return (a+b) + + def decode(self, x_latent, conditioning, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None, + use_original_steps=False, callback=None): + #print(f'steps {self.steps} denoising {self.denoising_strength}') + + noise_schedule = NoiseScheduleVP("discrete", alphas_cumprod=self.alphas_cumprod) + + # same as in .sample(), i guess + model_type = "v" if self.model.parameterization == "v" else "noise" + + model_fn = model_wrapper( + lambda x, t, c: self.model.apply_model(x, t, c), + noise_schedule, + model_type=model_type, + guidance_type="classifier-free", + #condition=conditioning, + #unconditional_condition=unconditional_conditioning, + guidance_scale=unconditional_guidance_scale, + ) + + self.uni_pc = UniPC( + model_fn, + noise_schedule, + predict_x0=True, + thresholding=False, + variant=shared.opts.uni_pc_variant, + condition=conditioning, + unconditional_condition=unconditional_conditioning, + before_sample=self.before_sample, + after_sample=self.after_sample, + after_update=self.after_update, + ) + + return self.uni_pc.sample( + x_latent, + steps=self.steps, + skip_type=shared.opts.uni_pc_skip_type, + method="multistep", + order=shared.opts.uni_pc_order, + lower_order_final=shared.opts.uni_pc_lower_order_final, + t_start=self.denoising_strength, + ) + def register_buffer(self, name, attr): if type(attr) == torch.Tensor: if attr.device != devices.device: diff --git a/modules/processing.py b/modules/processing.py index 04379fabe..c3ea4b2b8 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -970,7 +970,8 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): shared.state.nextjob() img2img_sampler_name = self.sampler_name - if self.sampler_name in ['PLMS', 'UniPC']: # PLMS/UniPC do not support img2img so we just silently switch to DDIM + if self.sampler_name in ['PLMS']: + # PLMS does not support img2img, use fallback instead img2img_sampler_name = shared.opts.fallback_sampler self.sampler = sd_samplers.create_sampler(img2img_sampler_name, self.sd_model) From d3f0294bde458778d51010bf990848ee8a993881 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 28 Apr 2023 22:01:29 -0400 Subject: [PATCH 37/45] update options --- javascript/black-orange.css | 1 + javascript/notification.js | 28 ++++------------------------ modules/cmd_args.py | 1 - modules/shared.py | 8 ++++---- 4 files changed, 9 insertions(+), 29 deletions(-) diff --git a/javascript/black-orange.css b/javascript/black-orange.css index 52e601529..a055e2e51 100644 --- a/javascript/black-orange.css +++ b/javascript/black-orange.css @@ -103,6 +103,7 @@ svg.feather.feather-image, .feather .feather-image { display: none } #txt2img_tools, #img2img_tools { margin-top: 54px; scale: 120%; margin-left: 26px; } #txtimg_hr_finalres { max-width: 200px; } #pnginfo_html2_info { margin-top: -18px; background-color: var(--input-background-fill); padding: var(--input-padding) } +#txt2img_extra_refresh, #txt2img_extra_close { height: 1.7em } /* custom elements overrides */ #steps-animation, #controlnet { border-width: 0; } diff --git a/javascript/notification.js b/javascript/notification.js index 3b68ffe53..9f7c2e439 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -1,52 +1,32 @@ // Monitors the gallery and sends a browser notification when the leading image is new. let lastHeadImg = null; - let notificationButton = null; - const regExpTempImage = /(?<=\/|\\)tmp[\w\d]{8}\.png$/gm; onUiUpdate(function(){ if(notificationButton == null){ notificationButton = gradioApp().getElementById('request_notifications') - - if(notificationButton != null){ - notificationButton.addEventListener('click', function (evt) { - Notification.requestPermission(); - },true); - } + if (notificationButton != null) notificationButton.addEventListener('click', (evt) => Notification.requestPermission(), true); } - const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); - if (galleryPreviews == null) return; - const headImg = galleryPreviews[0]?.src; - if (headImg == null || headImg == lastHeadImg) return; - if (headImg.search(regExpTempImage) != -1) return; - 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. const imgs = new Set(Array.from(galleryPreviews).map(img => img.src)); - const notification = new Notification( - 'Stable Diffusion', - { + 'Stable Diffusion', { body: `Generated ${imgs.size > 1 ? imgs.size - opts.return_grid : 1} image${imgs.size > 1 ? 's' : ''}`, icon: headImg, - image: headImg, - } + image: headImg } ); - - notification.onclick = function(_){ + notification.onclick = function(_) { parent.focus(); this.close(); }; diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 87b47d6c7..65d4d4cfe 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -78,7 +78,6 @@ def compatibility_args(opts, args): opts.use_old_emphasis_implementation = False opts.use_old_karras_scheduler_sigmas = False opts.no_dpmpp_sde_batch_determinism = False - opts.use_old_hires_fix_width_height = False parser.add_argument("--lora-dir", help=argparse.SUPPRESS, default=opts.lora_dir) args = parser.parse_args() diff --git a/modules/shared.py b/modules/shared.py index d3720db6b..5c94bc4ff 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -330,11 +330,11 @@ options_templates.update(options_section(('cuda', "CUDA Settings"), { })) options_templates.update(options_section(('upscaling', "Upscaling"), { - "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), - "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), - "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Select which Real-ESRGAN models to show in the web UI.", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}), + "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), + "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), + "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}), "upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}), - "use_old_hires_fix_width_height": OptionInfo(False, "For hires fix, use width/height sliders to set final resolution rather than first pass (disables Upscale by, Resize width/height to)."), + "use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution rather than first pass"), "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers."), })) From 42e30bfc3cf6b10664fbd66cabcd3c65f69e9cd4 Mon Sep 17 00:00:00 2001 From: cool-bigdogs-tshirt <131823432+cool-bigdogs-tshirt@users.noreply.github.com> Date: Fri, 28 Apr 2023 22:31:53 -0500 Subject: [PATCH 38/45] unipc img2img - add a bunch of code to get a single value that maybe performs slightly better? --- modules/models/diffusion/uni_pc/sampler.py | 51 ++++++++++++++++------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/modules/models/diffusion/uni_pc/sampler.py b/modules/models/diffusion/uni_pc/sampler.py index 41b8c9a5b..3b468cf3f 100644 --- a/modules/models/diffusion/uni_pc/sampler.py +++ b/modules/models/diffusion/uni_pc/sampler.py @@ -1,5 +1,6 @@ """SAMPLING ONLY.""" +import numpy as np import torch from .uni_pc import NoiseScheduleVP, model_wrapper, UniPC @@ -26,26 +27,48 @@ class UniPCSampler(object): noise = torch.randn_like(x0) # first time we have all the info to get the real parameters from the ui - hires_steps = t[0] + 1 + # value from the hires steps slider: + num_inference_steps = t[0] + 1 + # (num_inference_steps // denoising_strength): inflated_steps = self.inflated_steps - self.denoising_strength = hires_steps/inflated_steps + # not exact: + self.denoising_strength = num_inference_steps/inflated_steps - adjusted_steps = int(hires_steps * self.denoising_strength) - self.steps = max(adjusted_steps, shared.opts.uni_pc_order+1) + # values used for timesteps that generate noise in diffusers repo + init_timestep = min( + int(num_inference_steps * self.denoising_strength), + num_inference_steps, + ) + t_start = max(num_inference_steps - init_timestep, 0) + + # actual number of steps we'll run + self.steps = max( + num_inference_steps - init_timestep, + shared.opts.uni_pc_order+1, + ) t = torch.full(t.shape, self.steps).to(t.device) - timesteps = torch.asarray(list(range( - t, - self.model.num_timesteps, - self.model.num_timesteps // hires_steps, - ))) + 1 - alphas = self.model.alphas_cumprod[timesteps] - sqrt_one_minus_alphas = torch.sqrt(1. - alphas) - a = extract_into_tensor(torch.sqrt(alphas), t, x0.shape) * x0 - b = extract_into_tensor(sqrt_one_minus_alphas, t, x0.shape) * noise + scheduler_timesteps = np.linspace( + 0, + self.model.num_timesteps-1, + num_inference_steps + 1, + ).round()[::-1][:-1].copy().astype(np.int64) + _, unique_indices = np.unique(scheduler_timesteps, return_index=True) + scheduler_timesteps = scheduler_timesteps[np.sort(unique_indices)] + scheduler_timesteps = torch.from_numpy(scheduler_timesteps).to(t.device) - return (a+b) + sample_timesteps = scheduler_timesteps[t_start:] + latent_timestep = sample_timesteps[:1].repeat(x0.shape[0]) + + alphas_cumprod = self.alphas_cumprod + sqrt_alphas_prod = alphas_cumprod[latent_timestep] ** 0.5 + sqrt_alphas_prod = sqrt_alphas_prod.flatten() + + sqrt_one_minus_alpha_prod = (1 - alphas_cumprod[latent_timestep]) ** 0.5 + sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten() + + return (sqrt_alphas_prod * x0 + sqrt_one_minus_alpha_prod * noise) def decode(self, x_latent, conditioning, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None, use_original_steps=False, callback=None): From a78ce0a3ca4521a5661042c47e3dd56eee7a1eeb Mon Sep 17 00:00:00 2001 From: cool-bigdogs-tshirt <131823432+cool-bigdogs-tshirt@users.noreply.github.com> Date: Thu, 27 Apr 2023 12:04:14 -0500 Subject: [PATCH 39/45] xyz override for latent upscaler fallback --- modules/processing.py | 5 +++-- scripts/xyz_grid.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index c3ea4b2b8..a83d9fa12 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -970,9 +970,10 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): shared.state.nextjob() img2img_sampler_name = self.sampler_name - if self.sampler_name in ['PLMS']: + force_latent_upscaler = shared.opts.data.get('xyz_fallback_sampler') + if self.sampler_name in ['PLMS'] or force_latent_upscaler is not None: # PLMS does not support img2img, use fallback instead - img2img_sampler_name = shared.opts.fallback_sampler + img2img_sampler_name = force_latent_upscaler or shared.opts.fallback_sampler self.sampler = sd_samplers.create_sampler(img2img_sampler_name, self.sd_model) samples = samples[:, :, self.truncate_y//2:samples.shape[2]-(self.truncate_y+1)//2, self.truncate_x//2:samples.shape[3]-(self.truncate_x+1)//2] diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 52ae1c6e1..9a5a67241 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -128,6 +128,14 @@ def apply_styles(p: StableDiffusionProcessingTxt2Img, x: str, _): p.styles.extend(x.split(',')) +def apply_fallback(p, x, xs): + sampler_name = sd_samplers.samplers_map.get(x.lower(), None) + if sampler_name is None: + raise RuntimeError(f"Unknown sampler: {x}") + + opts.data["xyz_fallback_sampler"] = sampler_name + + def apply_uni_pc_order(p, x, xs): opts.data["uni_pc_order"] = min(x, p.steps - 1) @@ -220,6 +228,7 @@ axis_options = [ AxisOption("Clip skip", int, apply_clip_skip), AxisOption("Denoising", float, apply_field("denoising_strength")), AxisOptionTxt2Img("Hires upscaler", str, apply_field("hr_upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]), + AxisOptionTxt2Img("Fallback latent upscaler sampler", str, apply_fallback, format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]), AxisOptionImg2Img("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight")), AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: list(sd_vae.vae_dict)), AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)), From 5148c5b0ad6a5a9522e57952b428f89d29f64375 Mon Sep 17 00:00:00 2001 From: cool-bigdogs-tshirt <131823432+cool-bigdogs-tshirt@users.noreply.github.com> Date: Fri, 28 Apr 2023 22:50:56 -0500 Subject: [PATCH 40/45] fix batching issue --- modules/models/diffusion/uni_pc/sampler.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/models/diffusion/uni_pc/sampler.py b/modules/models/diffusion/uni_pc/sampler.py index 3b468cf3f..3100522ab 100644 --- a/modules/models/diffusion/uni_pc/sampler.py +++ b/modules/models/diffusion/uni_pc/sampler.py @@ -47,8 +47,6 @@ class UniPCSampler(object): shared.opts.uni_pc_order+1, ) - t = torch.full(t.shape, self.steps).to(t.device) - scheduler_timesteps = np.linspace( 0, self.model.num_timesteps-1, @@ -62,13 +60,17 @@ class UniPCSampler(object): latent_timestep = sample_timesteps[:1].repeat(x0.shape[0]) alphas_cumprod = self.alphas_cumprod - sqrt_alphas_prod = alphas_cumprod[latent_timestep] ** 0.5 - sqrt_alphas_prod = sqrt_alphas_prod.flatten() + sqrt_alpha_prod = alphas_cumprod[latent_timestep] ** 0.5 + sqrt_alpha_prod = sqrt_alpha_prod.flatten() + while len(sqrt_alpha_prod.shape) < len(x0.shape): + sqrt_alpha_prod = sqrt_alpha_prod.unsqueeze(-1) sqrt_one_minus_alpha_prod = (1 - alphas_cumprod[latent_timestep]) ** 0.5 sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten() + while len(sqrt_one_minus_alpha_prod.shape) < len(x0.shape): + sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.unsqueeze(-1) - return (sqrt_alphas_prod * x0 + sqrt_one_minus_alpha_prod * noise) + return (sqrt_alpha_prod * x0 + sqrt_one_minus_alpha_prod * noise) def decode(self, x_latent, conditioning, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None, use_original_steps=False, callback=None): From d51918c68210f9cdecd156932559d8e2773711f9 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 29 Apr 2023 07:37:53 -0400 Subject: [PATCH 41/45] change order of argparse --- .../multidiffusion-upscaler-for-automatic1111 | 2 +- extensions-builtin/sd-webui-controlnet | 2 +- launch.py | 1 + modules/cmd_args.py | 1 + setup.py | 13 +++++++------ 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 index f3d79a474..5da750b9d 160000 --- a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 +++ b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 @@ -1 +1 @@ -Subproject commit f3d79a474b9795f07143eaf8104737a403b5fb52 +Subproject commit 5da750b9de930b0e28883423f697c5ea82457c24 diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 2bc440001..940d4edfb 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 2bc4400011b38ab7f1d3f27a95897a6cb0c28c2a +Subproject commit 940d4edfbab1525615b1827a9cb7b7ea21af8a6c diff --git a/launch.py b/launch.py index ec9fd514b..bf34ee507 100644 --- a/launch.py +++ b/launch.py @@ -14,6 +14,7 @@ from rich import print # pylint: disable=redefined-builtin,wrong-import-order commandline_args = os.environ.get('COMMANDLINE_ARGS', "") sys.argv += shlex.split(commandline_args) +setup.add_args() setup.extensions_preload(force=False) setup.parse_args() args, _ = modules.cmd_args.parser.parse_known_args() diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 65d4d4cfe..692f6451f 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -78,6 +78,7 @@ def compatibility_args(opts, args): opts.use_old_emphasis_implementation = False opts.use_old_karras_scheduler_sigmas = False opts.no_dpmpp_sde_batch_determinism = False + opts.lora_apply_to_outputs = False parser.add_argument("--lora-dir", help=argparse.SUPPRESS, default=opts.lora_dir) args = parser.parse_args() diff --git a/setup.py b/setup.py index d368b44f3..3963832dd 100644 --- a/setup.py +++ b/setup.py @@ -486,12 +486,9 @@ def check_timestamp(): return ok -def parse_args(): - # command line args - # parser = argparse.ArgumentParser(description = 'Setup for SD WebUI') - if vars(parser)['_option_string_actions'].get('--debug', None) is not None: - return - parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s") +def add_args(): + if vars(parser)['_option_string_actions'].get('--debug', None) is None: + parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s") parser.add_argument('--reset', default = False, action='store_true', help = "Reset main repository to latest version, default: %(default)s") parser.add_argument('--upgrade', default = False, action='store_true', help = "Upgrade main repository to latest version, default: %(default)s") parser.add_argument('--noupdate', default = False, action='store_true', help = "Skip update of extensions and submodules, default: %(default)s") @@ -499,6 +496,10 @@ def parse_args(): parser.add_argument('--skip-extensions', default = False, action='store_true', help = "Skips running individual extension installers, default: %(default)s") parser.add_argument('--skip-git', default = False, action='store_true', help = "Skips running all GIT operations, default: %(default)s") parser.add_argument('--experimental', default = False, action='store_true', help = "Allow unsupported versions of libraries, default: %(default)s") + + +def parse_args(): + # command line args global args # pylint: disable=global-statement args = parser.parse_args() From 4e05d95ee0994260dc50e33219f5808c227d30a1 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 29 Apr 2023 08:58:32 -0400 Subject: [PATCH 42/45] fix prompts from file --- .../multidiffusion-upscaler-for-automatic1111 | 2 +- html/extra-networks-card.html | 7 +-- modules/processing.py | 27 ++------- modules/shared.py | 2 +- modules/ui_common.py | 6 +- scripts/xyz_grid.py | 56 ++++++++----------- style.css | 33 +++-------- 7 files changed, 46 insertions(+), 87 deletions(-) diff --git a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 index 5da750b9d..7253cb449 160000 --- a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 +++ b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 @@ -1 +1 @@ -Subproject commit 5da750b9de930b0e28883423f697c5ea82457c24 +Subproject commit 7253cb449c85e2d2317ba541bf770ef372a807a4 diff --git a/html/extra-networks-card.html b/html/extra-networks-card.html index cb4720f14..6825a2752 100644 --- a/html/extra-networks-card.html +++ b/html/extra-networks-card.html @@ -4,9 +4,9 @@ - diff --git a/modules/processing.py b/modules/processing.py index a83d9fa12..1c6c36d32 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -971,9 +971,8 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): img2img_sampler_name = self.sampler_name force_latent_upscaler = shared.opts.data.get('xyz_fallback_sampler') - if self.sampler_name in ['PLMS'] or force_latent_upscaler is not None: - # PLMS does not support img2img, use fallback instead - img2img_sampler_name = force_latent_upscaler or shared.opts.fallback_sampler + if self.sampler_name in ['PLMS'] or (force_latent_upscaler is not None and force_latent_upscaler != 'None'): + img2img_sampler_name = force_latent_upscaler or shared.opts.fallback_sampler # PLMS does not support img2img, use fallback instead self.sampler = sd_samplers.create_sampler(img2img_sampler_name, self.sd_model) samples = samples[:, :, self.truncate_y//2:samples.shape[2]-(self.truncate_y+1)//2, self.truncate_x//2:samples.shape[3]-(self.truncate_x+1)//2] @@ -1026,29 +1025,24 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.image_conditioning = None def init(self, all_prompts, all_seeds, all_subseeds): - if self.sampler_name in ['PLMS', 'UniPC']: # PLMS/UniPC do not support img2img so we just silently switch to DDIM - self.sampler_name = shared.opts.fallback_sampler + force_latent_upscaler = shared.opts.data.get('xyz_fallback_sampler') + if self.sampler_name in ['PLMS'] or (force_latent_upscaler is not None and force_latent_upscaler != 'None'): + self.sampler_name = force_latent_upscaler or shared.opts.fallback_sampler # PLMS does not support img2img, use fallback instead self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) crop_region = None - image_mask = self.image_mask - if image_mask is not None: image_mask = image_mask.convert('L') - if self.inpainting_mask_invert: image_mask = ImageOps.invert(image_mask) - if self.mask_blur > 0: image_mask = image_mask.filter(ImageFilter.GaussianBlur(self.mask_blur)) - if self.inpaint_full_res: self.mask_for_overlay = image_mask mask = image_mask.convert('L') crop_region = masking.get_crop_region(np.array(mask), self.inpaint_full_res_padding) crop_region = masking.expand_crop_region(crop_region, self.width, self.height, mask.width, mask.height) x1, y1, x2, y2 = crop_region - mask = mask.crop(crop_region) image_mask = images.resize_image(2, mask, self.width, self.height) self.paste_to = (x1, y1, x2-x1, y2-y1) @@ -1057,42 +1051,31 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): np_mask = np.array(image_mask) np_mask = np.clip((np_mask.astype(np.float32)) * 2, 0, 255).astype(np.uint8) self.mask_for_overlay = Image.fromarray(np_mask) - self.overlay_images = [] - latent_mask = self.latent_mask if self.latent_mask is not None else image_mask - add_color_corrections = opts.img2img_color_correction and self.color_corrections is None if add_color_corrections: self.color_corrections = [] imgs = [] for img in self.init_images: image = images.flatten(img, opts.img2img_background_color) - if crop_region is None and self.resize_mode != 3: image = images.resize_image(self.resize_mode, image, self.width, self.height) - if image_mask is not None: image_masked = Image.new('RGBa', (image.width, image.height)) image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L'))) - self.overlay_images.append(image_masked.convert('RGBA')) - # crop_region is not None if we are doing inpaint full res if crop_region is not None: image = image.crop(crop_region) image = images.resize_image(2, image, self.width, self.height) - if image_mask is not None: if self.inpainting_fill != 1: image = masking.fill(image, latent_mask) - if add_color_corrections: self.color_corrections.append(setup_color_correction(image)) - image = np.array(image).astype(np.float32) / 255.0 image = np.moveaxis(image, 2, 0) - imgs.append(image) if len(imgs) == 1: diff --git a/modules/shared.py b/modules/shared.py index 5c94bc4ff..c18e31021 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -417,7 +417,7 @@ options_templates.update(options_section(('ui', "Live previews"), { options_templates.update(options_section(('sampler-params', "Sampler parameters"), { "show_samplers": OptionInfo(["Euler a", "UniPC", "DDIM", "DPM++ SDE", "DPM++ SDE", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers()]}), - "fallback_sampler": OptionInfo("Euler a", "Fallback sampler if primary sampler is not compatible", gr.Dropdown, lambda: {"choices": [x.name for x in list_samplers()]}), + "fallback_sampler": OptionInfo("Euler a", "Fallback sampler if primary sampler is not compatible", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}), "eta_ancestral": OptionInfo(1.0, "Noise multiplier for ancestral samplers (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), "eta_ddim": OptionInfo(0.0, "Noise multiplier for DDIM (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), "ddim_discretize": OptionInfo('uniform', "DDIM discretize img2img", gr.Radio, {"choices": ['uniform', 'quad']}), diff --git a/modules/ui_common.py b/modules/ui_common.py index 12908005e..cb9870196 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -66,10 +66,12 @@ def save_files(js_data, images, do_make_zip, index): for image_index, filedata in enumerate(images, start_index): image = image_from_url_text(filedata) - is_grid = image_index < p.index_of_first_image i = 0 if is_grid else (image_index - p.index_of_first_image) - + if len(p.all_seeds) <= i: + p.all_seeds.append(p.seed) + if len(p.all_prompts) <= i: + p.all_prompts.append(p.prompt) fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs) filename = os.path.relpath(fullfn, path) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 9a5a67241..95fa9ac95 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -1,26 +1,18 @@ +import re +import csv +import random from collections import namedtuple from copy import copy from itertools import permutations, chain -import random -import csv from io import StringIO from PIL import Image import numpy as np - -import modules.scripts as scripts import gradio as gr - -from modules import images, paths, sd_samplers, processing, sd_models, sd_vae -from modules.processing import process_images, Processed, StableDiffusionProcessingTxt2Img -from modules.shared import opts, cmd_opts, state +import modules.scripts as scripts import modules.shared as shared -import modules.sd_samplers -import modules.sd_models -import modules.sd_vae -import glob -import os -import re - +from modules import images, sd_samplers, processing, sd_models, sd_vae +from modules.processing import process_images, Processed, StableDiffusionProcessingTxt2Img +from modules.shared import opts, state from modules.ui_components import ToolButton fill_values_symbol = "\U0001f4d2" # 📒 @@ -83,15 +75,15 @@ def confirm_samplers(p, xs): def apply_checkpoint(p, x, xs): - info = modules.sd_models.get_closet_checkpoint_match(x) + info = sd_models.get_closet_checkpoint_match(x) if info is None: raise RuntimeError(f"Unknown checkpoint: {x}") - modules.sd_models.reload_model_weights(shared.sd_model, info) + sd_models.reload_model_weights(shared.sd_model, info) def confirm_checkpoints(p, xs): for x in xs: - if modules.sd_models.get_closet_checkpoint_match(x) is None: + if sd_models.get_closet_checkpoint_match(x) is None: raise RuntimeError(f"Unknown checkpoint: {x}") @@ -108,20 +100,20 @@ def apply_upscale_latent_space(p, x, xs): def find_vae(name: str): if name.lower() in ['auto', 'automatic']: - return modules.sd_vae.unspecified + return sd_vae.unspecified if name.lower() == 'none': return None else: - choices = [x for x in sorted(modules.sd_vae.vae_dict, key=lambda x: len(x)) if name.lower().strip() in x.lower()] + choices = [x for x in sorted(sd_vae.vae_dict, key=lambda x: len(x)) if name.lower().strip() in x.lower()] if len(choices) == 0: print(f"No VAE found for {name}; using automatic") - return modules.sd_vae.unspecified + return sd_vae.unspecified else: - return modules.sd_vae.vae_dict[choices[0]] + return sd_vae.vae_dict[choices[0]] def apply_vae(p, x, xs): - modules.sd_vae.reload_vae_weights(shared.sd_model, vae_file=find_vae(x)) + sd_vae.reload_vae_weights(shared.sd_model, vae_file=find_vae(x)) def apply_styles(p: StableDiffusionProcessingTxt2Img, x: str, _): @@ -341,7 +333,6 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend if draw_legend: z_grid = images.draw_grid_annotations(z_grid, sub_grid_size[0], sub_grid_size[1], title_texts, [[images.GridAnnotation()]]) processed_result.images.insert(0, z_grid) - #TODO: Deeper aspects of the program rely on grid info being misaligned between metadata arrays, which is not ideal. #processed_result.all_prompts.insert(0, processed_result.all_prompts[0]) #processed_result.all_seeds.insert(0, processed_result.all_seeds[0]) processed_result.infotexts.insert(0, processed_result.infotexts[0]) @@ -354,12 +345,12 @@ class SharedSettingsStackHelper(object): self.CLIP_stop_at_last_layers = opts.CLIP_stop_at_last_layers self.vae = opts.sd_vae self.uni_pc_order = opts.uni_pc_order - + def __exit__(self, exc_type, exc_value, tb): opts.data["sd_vae"] = self.vae opts.data["uni_pc_order"] = self.uni_pc_order - modules.sd_models.reload_model_weights() - modules.sd_vae.reload_vae_weights() + sd_models.reload_model_weights() + sd_vae.reload_vae_weights() opts.data["CLIP_stop_at_last_layers"] = self.CLIP_stop_at_last_layers @@ -407,7 +398,7 @@ class Script(scripts.Script): include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids")) with gr.Column(): margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) - + with gr.Row(variant="compact", elem_id="swap_axes"): swap_xy_axes_button = gr.Button(value="Swap X/Y axes", elem_id="xy_grid_swap_axes_button") swap_yz_axes_button = gr.Button(value="Swap Y/Z axes", elem_id="yz_grid_swap_axes_button") @@ -468,7 +459,7 @@ class Script(scripts.Script): def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size): if not no_fixed_seeds: - modules.processing.fix_seed(p) + processing.fix_seed(p) if not opts.return_grid: p.batch_size = 1 @@ -498,7 +489,7 @@ class Script(scripts.Script): start = int(mc.group(1)) end = int(mc.group(2)) num = int(mc.group(3)) if mc.group(3) is not None else 1 - + valslist_ext += [int(x) for x in np.linspace(start=start, stop=end, num=num).tolist()] else: valslist_ext.append(val) @@ -520,7 +511,7 @@ class Script(scripts.Script): start = float(mc.group(1)) end = float(mc.group(2)) num = int(mc.group(3)) if mc.group(3) is not None else 1 - + valslist_ext += np.linspace(start=start, stop=end, num=num).tolist() else: valslist_ext.append(val) @@ -708,13 +699,12 @@ class Script(scripts.Script): # Auto-save main and sub-grids: grid_count = z_count + 1 if z_count > 1 else 1 for g in range(grid_count): - #TODO: See previous comment about intentional data misalignment. adj_g = g-1 if g > 0 else g images.save_image(processed.images[g], p.outpath_grids, "xyz_grid", info=processed.infotexts[g], extension=opts.grid_format, prompt=processed.all_prompts[adj_g], seed=processed.all_seeds[adj_g], grid=True, p=processed) if not include_sub_grids: # Done with sub-grids, drop all related information: - for sg in range(z_count): + for _sg in range(z_count): del processed.images[1] del processed.all_prompts[1] del processed.all_seeds[1] diff --git a/style.css b/style.css index 55c571d6f..4f5449a61 100644 --- a/style.css +++ b/style.css @@ -679,10 +679,6 @@ footer { margin-left: 0.5em; } - -.extra-network-cards .card .metadata-button:before, .extra-network-thumbs .card .metadata-button:before{ - content: "🛈"; -} .extra-network-cards .card .metadata-button, .extra-network-thumbs .card .metadata-button{ display: none; position: absolute; @@ -696,11 +692,11 @@ footer { .extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; } + .extra-network-cards .card .metadata-button:hover, .extra-network-thumbs .card .metadata-button:hover{ color: red; } - .extra-network-thumbs { display: flex; flex-flow: row wrap; @@ -708,8 +704,9 @@ footer { } .extra-network-thumbs .card { - height: 6em; - width: 6em; + display: inline-block; + height: 9em; + width: 9em; cursor: pointer; background-image: url('./file=html/card-no-preview.png'); background-size: cover; @@ -717,23 +714,13 @@ footer { position: relative; } -.extra-network-thumbs .card:hover .additional a { - display: inline-block; +.extra-network-thumbs .card .additional, .extra-network-thumbs .card .additional { + white-space: nowrap; + overflow: hidden; } -.extra-network-thumbs .actions .additional a { - background-image: url('./file=html/image-update.svg'); - background-repeat: no-repeat; - background-size: cover; - background-position: center center; - position: absolute; - top: 0; - left: 0; - width: 24px; - height: 24px; - display: none; - font-size: 0; - text-align: -9999; +.extra-network-thumbs .card:hover .additional a { + display: inline-block; } .extra-network-thumbs .actions .name { @@ -762,12 +749,10 @@ footer { box-shadow: 0 0 5px rgba(128, 128, 128, 0.5); border-radius: 0.2em; position: relative; - background-size: auto 100%; background-position: center; overflow: hidden; cursor: pointer; - background-image: url('./file=html/card-no-preview.png') } From 6831b033808b6b4b7f341be638bafcf05e1ce845 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 29 Apr 2023 09:32:06 -0400 Subject: [PATCH 43/45] fix cards previews --- .../multidiffusion-upscaler-for-automatic1111 | 2 +- html/extra-networks-card.html | 4 +- style.css | 353 +++--------------- 3 files changed, 62 insertions(+), 297 deletions(-) diff --git a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 index 7253cb449..6931b89cb 160000 --- a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 +++ b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 @@ -1 +1 @@ -Subproject commit 7253cb449c85e2d2317ba541bf770ef372a807a4 +Subproject commit 6931b89cb4507c7dc8fa81ac36c2c19d0691c44e diff --git a/html/extra-networks-card.html b/html/extra-networks-card.html index 6825a2752..3cf6e2836 100644 --- a/html/extra-networks-card.html +++ b/html/extra-networks-card.html @@ -4,8 +4,8 @@
    diff --git a/style.css b/style.css index 4f5449a61..996ad15d2 100644 --- a/style.css +++ b/style.css @@ -1,165 +1,36 @@ - -/* general gradio fixes */ - -:root, .dark{ - --checkbox-label-gap: 0.25em 0.1em; - --section-header-text-size: 12pt; - --block-background-fill: transparent; -} - -.block.padded:not(.gradio-accordion) { - padding: 0 !important; -} - -div.gradio-container{ - max-width: unset !important; -} - -.hidden{ - display: none; -} - -.compact{ - background: transparent !important; - padding: 0 !important; -} - -div.form{ - border-width: 0; - box-shadow: none; - background: transparent; - overflow: visible; - gap: 0.5em; -} - -.block.gradio-dropdown, -.block.gradio-slider, -.block.gradio-checkbox, -.block.gradio-textbox, -.block.gradio-radio, -.block.gradio-checkboxgroup, -.block.gradio-number, -.block.gradio-colorpicker -{ - border-width: 0 !important; - box-shadow: none !important; -} - -.gap.compact{ - padding: 0; - gap: 0.2em 0; -} - -div.compact{ - gap: 1em; -} - -.gradio-dropdown label span:not(.has-info), -.gradio-textbox label span:not(.has-info), -.gradio-number label span:not(.has-info) -{ - margin-bottom: 0; -} - -.gradio-dropdown ul.options{ - z-index: 3000; - min-width: fit-content; - max-width: inherit; - white-space: nowrap; -} - -.gradio-dropdown ul.options li.item { - padding: 0.05em 0; -} - -.gradio-dropdown ul.options li.item:not(:has(.hide)) { - background-color: var(--neutral-100); -} - -.dark .gradio-dropdown ul.options li.item:not(:has(.hide)) { - background-color: var(--neutral-900); -} - -.gradio-dropdown div.wrap.wrap.wrap.wrap{ - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); -} - -.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ - flex-wrap: unset; -} - -.gradio-dropdown .single-select{ - white-space: nowrap; - overflow: hidden; -} - -.gradio-dropdown .token-remove.remove-all.remove-all{ - display: none; -} - -.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{ - display: flex; -} - -.gradio-slider input[type="number"]{ - width: 6em; -} - -.block.gradio-checkbox { - margin: 0.75em 1.5em 0 0; -} - -.gradio-html div.wrap{ - height: 100%; -} -div.gradio-html.min{ - min-height: 0; -} - -.block.gradio-gallery{ - background: var(--input-background-fill); -} - -.gradio-container .prose a, .gradio-container .prose a:visited{ - color: unset; - text-decoration: none; -} - - +:root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;} +.block.padded:not(.gradio-accordion) { padding: 0 !important; } +div.gradio-container{ max-width: unset !important; } +.hidden{ display: none; } +.compact{ background: transparent !important; padding: 0 !important; } +div.form{ border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em; } +.block.gradio-dropdown, .block.gradio-slider, .block.gradio-checkbox, .block.gradio-textbox, .block.gradio-radio, .block.gradio-checkboxgroup, .block.gradio-number, .block.gradio-colorpicker { border-width: 0 !important; box-shadow: none !important;} +.gap.compact{ padding: 0; gap: 0.2em 0; } +div.compact{ gap: 1em; } +.gradio-dropdown label span:not(.has-info), .gradio-textbox label span:not(.has-info), .gradio-number label span:not(.has-info) { margin-bottom: 0; } +.gradio-dropdown ul.options{ z-index: 3000; min-width: fit-content; max-width: inherit; white-space: nowrap; } +.gradio-dropdown ul.options li.item { padding: 0.05em 0; } +.gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-100); } +.dark .gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-900); } +.gradio-dropdown div.wrap.wrap.wrap.wrap{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } +.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ flex-wrap: unset; } +.gradio-dropdown .single-select{ white-space: nowrap; overflow: hidden; } +.gradio-dropdown .token-remove.remove-all.remove-all{ display: none; } +.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{ display: flex; } +.gradio-slider input[type="number"]{ width: 6em; } +.block.gradio-checkbox { margin: 0.75em 1.5em 0 0; } +.gradio-html div.wrap{ height: 100%; } +div.gradio-html.min{ min-height: 0; } +.block.gradio-gallery{ background: var(--input-background-fill); } +.gradio-container .prose a, .gradio-container .prose a:visited{ color: unset; text-decoration: none; } /* general styled components */ - -.gradio-button.tool{ - max-width: 2.2em; - min-width: 2.2em !important; - height: 2.4em; - align-self: end; - line-height: 1em; - border-radius: 0.5em; -} - -.gradio-button.secondary-down{ - background: var(--button-secondary-background-fill); - color: var(--button-secondary-text-color); -} -.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ - box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; -} -.gradio-button.secondary-down:hover{ - background: var(--button-secondary-background-fill-hover); - color: var(--button-secondary-text-color-hover); -} - -.checkboxes-row{ - margin-bottom: 0.5em; - margin-left: 0em; -} -.checkboxes-row > div{ - flex: 0; - white-space: nowrap; - min-width: auto; -} - +.gradio-button.tool{ max-width: 2.2em; min-width: 2.2em !important; height: 2.4em; align-self: end; line-height: 1em; border-radius: 0.5em; } +.gradio-button.secondary-down{ background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); } +.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; } +.gradio-button.secondary-down:hover{ background: var(--button-secondary-background-fill-hover); color: var(--button-secondary-text-color-hover); } +.checkboxes-row{ margin-bottom: 0.5em; margin-left: 0em; } +.checkboxes-row > div{ flex: 0; white-space: nowrap; min-width: auto; } button.custom-button{ border-radius: var(--button-large-radius); padding: var(--button-large-padding); @@ -176,9 +47,7 @@ button.custom-button{ text-align: center; } - /* txt2img/img2img specific */ - .block.token-counter{ position: absolute; display: inline-block; @@ -201,13 +70,8 @@ button.custom-button{ border: 2px solid rgba(255,0,0,0.4) !important; } -.block.token-counter div{ - display: inline; -} - -.block.token-counter span{ - padding: 0.1em 0.75em; -} +.block.token-counter div{ display: inline; } +.block.token-counter span{ padding: 0.1em 0.75em; } [id$=_subseed_show]{ min-width: auto !important; @@ -642,42 +506,14 @@ footer { } /* extra networks UI */ - -.extra-networks > div > [id *= '_extra_']{ - margin: 0.3em; -} - -.extra-network-subdirs{ - padding: 0.2em 0.35em; -} - -.extra-network-subdirs button{ - margin: 0 0.15em; -} -.extra-networks .tab-nav .search{ - display: inline-block; - max-width: 16em; - margin: 0.3em; - align-self: center; - width: 16em; -} - -#txt2img_extra_view, #img2img_extra_view { - width: auto; -} - -.extra-network-cards .nocards, .extra-network-thumbs .nocards{ - margin: 1.25em 0.5em 0.5em 0.5em; -} - -.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ - font-size: 1.5em; - margin-bottom: 1em; -} - -.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{ - margin-left: 0.5em; -} +.extra-networks > div > [id *= '_extra_']{ margin: 0.3em; } +.extra-network-subdirs{ padding: 0.2em 0.35em; } +.extra-network-subdirs button{ margin: 0 0.15em; } +.extra-networks .tab-nav .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; width: 16em; } +#txt2img_extra_view, #img2img_extra_view { width: auto; } +.extra-network-cards .nocards, .extra-network-thumbs .nocards{ margin: 1.25em 0.5em 0.5em 0.5em; } +.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ font-size: 1.5em; margin-bottom: 1em; } +.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{ margin-left: 0.5em; } .extra-network-cards .card .metadata-button, .extra-network-thumbs .card .metadata-button{ display: none; @@ -689,19 +525,9 @@ footer { font-size: 22pt; width: 1.5em; } -.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ - display: inline-block; -} - -.extra-network-cards .card .metadata-button:hover, .extra-network-thumbs .card .metadata-button:hover{ - color: red; -} - -.extra-network-thumbs { - display: flex; - flex-flow: row wrap; - gap: 10px; -} +.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; } +.extra-network-thumbs { display: flex; flex-flow: row wrap; gap: 10px; } +.extra-network-cards .card .additional a:hover, .extra-network-thumbs .card .additional a:hover { color: darkorange } .extra-network-thumbs .card { display: inline-block; @@ -714,15 +540,8 @@ footer { position: relative; } -.extra-network-thumbs .card .additional, .extra-network-thumbs .card .additional { - white-space: nowrap; - overflow: hidden; -} - -.extra-network-thumbs .card:hover .additional a { - display: inline-block; -} - +.extra-network-cards .card .additional, .extra-network-thumbs .card .additional { white-space: nowrap; overflow: hidden; } +.extra-network-thumbs .card:hover .additional a { display: inline-block; } .extra-network-thumbs .actions .name { position: absolute; bottom: 0; @@ -736,11 +555,7 @@ footer { color: white; } -.extra-network-thumbs .card:hover .actions .name { - white-space: normal; - word-break: break-all; -} - +.extra-network-thumbs .card:hover .actions .name { white-space: normal; word-break: break-all; } .extra-network-cards .card{ display: inline-block; margin: 0.5em; @@ -756,13 +571,8 @@ footer { background-image: url('./file=html/card-no-preview.png') } -.extra-network-cards .card:hover{ - box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); -} - -.extra-network-cards .card .actions .additional{ - display: none; -} +.extra-network-cards .card:hover { box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); } +.extra-network-cards .card .actions .additional, .extra-network-thumbs .card .actions .additional{ display: none; } .extra-network-cards .card .actions{ position: absolute; @@ -775,58 +585,13 @@ footer { text-shadow: 0 0 0.2em black; } -.extra-network-cards .card .actions *{ - color: white; -} - -.extra-network-cards .card .actions:hover{ - box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; -} - -.extra-network-cards .card .actions .name{ - font-size: 1.7em; - font-weight: bold; - line-break: anywhere; -} - -.extra-network-cards .card .actions .description { - display: block; - max-height: 3em; - white-space: pre-wrap; - line-height: 1.1; -} - -.extra-network-cards .card .actions .description:hover { - max-height: none; -} - -.extra-network-cards .card .actions:hover .additional{ - display: block; -} - -.extra-network-cards .card ul{ - margin: 0.25em 0 0.75em 0.25em; - cursor: unset; -} - -.extra-network-cards .card ul a{ - cursor: pointer; -} - -.extra-network-cards .card ul a:hover{ - color: red; -} - -.theme-preview { - display: none; - position: fixed; - border: 4px solid var(--neutral-600); - box-shadow: 2px 2px 2px 2px var(--neutral-700); - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; - max-width: 75vw; - z-index: 999; -} +.extra-network-cards .card .actions *{ color: white; } +.extra-network-cards .card .actions:hover { box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; } +.extra-network-cards .card .actions .name { font-size: 1.7em; font-weight: bold; line-break: anywhere; } +.extra-network-cards .card .actions .description { display: block; max-height: 3em; white-space: pre-wrap; line-height: 1.1; } +.extra-network-cards .card .actions .description:hover { max-height: none; } +.extra-network-cards .card .actions:hover .additional, .extra-network-thumbs .card:hover .additional{ display: block; } +.extra-network-cards .card ul{ margin: 0.25em 0 0.75em 0.25em; cursor: unset; } +.extra-network-cards .card ul a{ cursor: pointer; } +.extra-network-cards .card ul a:hover{ color: red; } +.theme-preview { display: none; position: fixed; border: 4px solid var(--neutral-600); box-shadow: 2px 2px 2px 2px var(--neutral-700); top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-width: 75vw; z-index: 999; } From 9975f819a0e6de41a26b007ab1320c94e179cccd Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 29 Apr 2023 10:07:09 -0400 Subject: [PATCH 44/45] xyz improvements --- scripts/xyz_grid.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 95fa9ac95..748fe7bbc 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -390,14 +390,12 @@ class Script(scripts.Script): fill_z_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_z_tool_button", visible=False) with gr.Row(variant="compact", elem_id="axis_options"): - with gr.Column(): - draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend")) - no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds")) - with gr.Column(): - include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images")) - include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids")) - with gr.Column(): - margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) + draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend")) + no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds")) + include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images")) + include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids")) + with gr.Row(variant="compact", elem_id="axis_options"): + margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) with gr.Row(variant="compact", elem_id="swap_axes"): swap_xy_axes_button = gr.Button(value="Swap X/Y axes", elem_id="xy_grid_swap_axes_button") @@ -428,6 +426,9 @@ class Script(scripts.Script): current_values = axis_values_dropdown if has_choices: choices = choices() + if len(choices) > 12: + has_choices = False + if has_choices: if isinstance(current_values,str): current_values = current_values.split(",") current_values = list(filter(lambda x: x in choices, current_values)) From d793afbd03f3ecadedac5156850c18bc22630b82 Mon Sep 17 00:00:00 2001 From: Aurora <46530683+AuwowaUwU@users.noreply.github.com> Date: Sat, 29 Apr 2023 16:53:41 +0200 Subject: [PATCH 45/45] Remove hardcoded disabling of grids in file script --- scripts/prompts_from_file.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 027e6539f..fe30d4b07 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -130,8 +130,6 @@ class Script(scripts.Script): lines = [x.strip() for x in prompt_txt.splitlines()] lines = [x for x in lines if len(x) > 0] - p.do_not_save_grid = True - job_count = 0 jobs = []