aboutsummaryrefslogtreecommitdiffstats
path: root/modules/ui.py
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ui.py')
-rw-r--r--modules/ui.py143
1 files changed, 126 insertions, 17 deletions
diff --git a/modules/ui.py b/modules/ui.py
index d2402e28..15572bb0 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -9,10 +9,13 @@ import random
import sys
import time
import traceback
+import platform
+import subprocess as sp
import numpy as np
import torch
-from PIL import Image
+from PIL import Image, PngImagePlugin
+import piexif
import gradio as gr
import gradio.utils
@@ -22,6 +25,7 @@ from modules.paths import script_path
from modules.shared import opts, cmd_opts
import modules.shared as shared
from modules.sd_samplers import samplers, samplers_for_img2img
+from modules.sd_hijack import model_hijack
import modules.ldsr_model
import modules.scripts
import modules.gfpgan_model
@@ -49,6 +53,7 @@ sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None
css_hide_progressbar = """
.wrap .m-12 svg { display:none!important; }
+.wrap .m-12::before { content:"Loading..." }
.progress-bar { display:none!important; }
.meta-text { display:none!important; }
"""
@@ -59,7 +64,7 @@ random_symbol = '\U0001f3b2\ufe0f' # 🎲️
reuse_symbol = '\u267b\ufe0f' # ♻️
art_symbol = '\U0001f3a8' # 🎨
paste_symbol = '\u2199\ufe0f' # ↙
-
+folder_symbol = '\uD83D\uDCC2'
def plaintext_to_html(text):
text = "<p>" + "<br>\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "</p>"
@@ -96,10 +101,11 @@ def save_files(js_data, images, index):
filenames = []
data = json.loads(js_data)
-
- if index > -1 and opts.save_selected_only and (index > 0 or not opts.return_grid): # ensures we are looking at a specific non-grid picture, and we have save_selected_only
+ if index > -1 and opts.save_selected_only and (index >= data["index_of_first_image"]): # ensures we are looking at a specific non-grid picture, and we have save_selected_only
images = [images[index]]
- data["seed"] += (index - 1 if opts.return_grid else index)
+ infotexts = [data["infotexts"][index]]
+ else:
+ infotexts = data["infotexts"]
with open(os.path.join(opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file:
at_start = file.tell() == 0
@@ -108,15 +114,26 @@ def save_files(js_data, images, index):
writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"])
filename_base = str(int(time.time() * 1000))
+ extension = opts.samples_format.lower()
for i, filedata in enumerate(images):
- filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + ".png"
+ filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + f".{extension}"
filepath = os.path.join(opts.outdir_save, filename)
if filedata.startswith("data:image/png;base64,"):
filedata = filedata[len("data:image/png;base64,"):]
- with open(filepath, "wb") as imgfile:
- imgfile.write(base64.decodebytes(filedata.encode('utf-8')))
+ image = Image.open(io.BytesIO(base64.decodebytes(filedata.encode('utf-8'))))
+ if opts.enable_pnginfo and extension == 'png':
+ pnginfo = PngImagePlugin.PngInfo()
+ pnginfo.add_text('parameters', infotexts[i])
+ image.save(filepath, pnginfo=pnginfo)
+ else:
+ image.save(filepath, quality=opts.jpeg_quality)
+
+ if opts.enable_pnginfo and extension in ("jpg", "jpeg", "webp"):
+ piexif.insert(piexif.dump({"Exif": {
+ piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(infotexts[i], encoding="unicode")
+ }}), filepath)
filenames.append(filename)
@@ -328,6 +345,10 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
outputs=[seed, dummy_component]
)
+def update_token_counter(text):
+ tokens, token_count, max_length = model_hijack.tokenize(text)
+ style_class = ' class="red"' if (token_count > max_length) else ""
+ return f"<span {style_class}>{token_count}/{max_length}</span>"
def create_toprow(is_img2img):
id_part = "img2img" if is_img2img else "txt2img"
@@ -337,11 +358,14 @@ def create_toprow(is_img2img):
with gr.Row():
with gr.Column(scale=80):
with gr.Row():
- prompt = gr.Textbox(label="Prompt", elem_id="prompt", show_label=False, placeholder="Prompt", lines=2)
+ prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, placeholder="Prompt", lines=2)
with gr.Column(scale=1, elem_id="roll_col"):
roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0)
paste = gr.Button(value=paste_symbol, elem_id="paste")
+ token_counter = gr.HTML(value="<span></span>", elem_id=f"{id_part}_token_counter")
+ hidden_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
+ hidden_button.click(fn=update_token_counter, inputs=[prompt], outputs=[token_counter])
with gr.Column(scale=10, elem_id="style_pos_col"):
prompt_style = gr.Dropdown(label="Style 1", elem_id=f"{id_part}_style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
@@ -356,7 +380,7 @@ def create_toprow(is_img2img):
with gr.Column(scale=1):
with gr.Row():
interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt")
- submit = gr.Button('Generate', elem_id="generate", variant='primary')
+ submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary')
interrupt.click(
fn=lambda: shared.state.interrupt(),
@@ -393,7 +417,7 @@ def setup_progressbar(progressbar, preview, id_part):
)
-def create_ui(txt2img, img2img, run_extras, run_pnginfo):
+def create_ui(txt2img, img2img, run_extras, run_pnginfo, run_modelmerger):
with gr.Blocks(analytics_enabled=False) as txt2img_interface:
txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style, paste = create_toprow(is_img2img=False)
dummy_component = gr.Label(visible=False)
@@ -448,6 +472,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
send_to_img2img = gr.Button('Send to img2img')
send_to_inpaint = gr.Button('Send to inpaint')
send_to_extras = gr.Button('Send to extras')
+ button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder'
+ open_txt2img_folder = gr.Button(folder_symbol, elem_id=button_id)
with gr.Group():
html_info = gr.HTML()
@@ -564,16 +590,16 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.TabItem('Inpaint', id='inpaint'):
init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA")
- init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False)
- init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False)
+ init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_base")
+ init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_mask")
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4)
with gr.Row():
- mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask")
+ mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask", elem_id="mask_mode")
inpainting_mask_invert = gr.Radio(label='Masking mode', show_label=False, choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index")
- inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index")
+ inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='original', type="index")
with gr.Row():
inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=False)
@@ -624,6 +650,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
img2img_send_to_img2img = gr.Button('Send to img2img')
img2img_send_to_inpaint = gr.Button('Send to inpaint')
img2img_send_to_extras = gr.Button('Send to extras')
+ button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder'
+ open_img2img_folder = gr.Button(folder_symbol, elem_id=button_id)
with gr.Group():
html_info = gr.HTML()
@@ -796,6 +824,8 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
html_info = gr.HTML()
extras_send_to_img2img = gr.Button('Send to img2img')
extras_send_to_inpaint = gr.Button('Send to inpaint')
+ button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else ''
+ open_extras_folder = gr.Button('Open output directory', elem_id=button_id)
submit.click(
fn=run_extras,
@@ -853,6 +883,23 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
outputs=[html, generation_info, html2],
)
+ with gr.Blocks() as modelmerger_interface:
+ with gr.Row().style(equal_height=False):
+ with gr.Column(variant='panel'):
+ gr.HTML(value="<p>A merger of the two checkpoints will be generated in your <b>checkpoint</b> directory.</p>")
+
+ with gr.Row():
+ primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary Model Name")
+ secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary Model Name")
+ custom_name = gr.Textbox(label="Custom Name (Optional)")
+ interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3)
+ interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method")
+ save_as_half = gr.Checkbox(value=False, label="Safe as float16")
+ modelmerger_merge = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary')
+
+ with gr.Column(variant='panel'):
+ submit_result = gr.Textbox(elem_id="modelmerger_result", show_label=False)
+
def create_setting_component(key):
def fun():
return opts.data[key] if key in opts.data else opts.data_labels[key].default
@@ -876,6 +923,17 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
return comp(label=info.label, value=fun, **(args or {}))
components = []
+ component_dict = {}
+
+ def open_folder(f):
+ if not shared.cmd_opts.hide_ui_dir_config:
+ path = os.path.normpath(f)
+ if platform.system() == "Windows":
+ os.startfile(path)
+ elif platform.system() == "Darwin":
+ sp.Popen(["open", path])
+ else:
+ sp.Popen(["xdg-open", path])
def run_settings(*args):
changed = 0
@@ -931,7 +989,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
gr.HTML(elem_id="settings_header_text_{}".format(item.section[0]), value='<h1 class="gr-button-lg">{}</h1>'.format(item.section[1]))
- components.append(create_setting_component(k))
+ component = create_setting_component(k)
+ component_dict[k] = component
+ components.append(component)
items_displayed += 1
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
@@ -950,6 +1010,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
(img2img_interface, "img2img", "img2img"),
(extras_interface, "Extras", "extras"),
(pnginfo_interface, "PNG Info", "pnginfo"),
+ (modelmerger_interface, "Checkpoint Merger", "modelmerger"),
(settings_interface, "Settings", "settings"),
]
@@ -970,6 +1031,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
for interface, label, ifid in interfaces:
with gr.TabItem(label, id=ifid):
interface.render()
+
+ if os.path.exists(os.path.join(script_path, "notification.mp3")):
+ audio_notification = gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False)
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
settings_submit.click(
@@ -977,7 +1041,34 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
inputs=components,
outputs=[result, text_settings],
)
-
+
+ def modelmerger(*args):
+ try:
+ results = run_modelmerger(*args)
+ except Exception as e:
+ print("Error loading/saving model file:", file=sys.stderr)
+ print(traceback.format_exc(), file=sys.stderr)
+ modules.sd_models.list_models() #To remove the potentially missing models from the list
+ return ["Error loading/saving model file. It doesn't exist or the name contains illegal characters"] + [gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(3)]
+ return results
+
+ modelmerger_merge.click(
+ fn=modelmerger,
+ inputs=[
+ primary_model_name,
+ secondary_model_name,
+ interp_method,
+ interp_amount,
+ save_as_half,
+ custom_name,
+ ],
+ outputs=[
+ submit_result,
+ primary_model_name,
+ secondary_model_name,
+ component_dict['sd_model_checkpoint'],
+ ]
+ )
paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Seed', 'Size-1', 'Size-2']
txt2img_fields = [field for field,name in txt2img_paste_fields if name in paste_field_names]
img2img_fields = [field for field,name in img2img_paste_fields if name in paste_field_names]
@@ -1016,6 +1107,24 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
outputs=[extras_image],
)
+ open_txt2img_folder.click(
+ fn=lambda: open_folder(opts.outdir_samples or opts.outdir_txt2img_samples),
+ inputs=[],
+ outputs=[],
+ )
+
+ open_img2img_folder.click(
+ fn=lambda: open_folder(opts.outdir_samples or opts.outdir_img2img_samples),
+ inputs=[],
+ outputs=[],
+ )
+
+ open_extras_folder.click(
+ fn=lambda: open_folder(opts.outdir_samples or opts.outdir_extras_samples),
+ inputs=[],
+ outputs=[],
+ )
+
img2img_send_to_extras.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery_extras",