From 39ce23f42d9b63eff39c50a0d069a442e6416cf5 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Fri, 23 Sep 2022 22:49:21 +0300 Subject: add the bitton to paste parameters into UI for txt2img, img2img, and pnginfo tabs fixed some [send to..] buttons to work properly with all tabs --- modules/extras.py | 6 +- modules/generation_parameters_copypaste.py | 88 +++++++++++++++++++++++ modules/ui.py | 111 +++++++++++++++++++++-------- 3 files changed, 173 insertions(+), 32 deletions(-) create mode 100644 modules/generation_parameters_copypaste.py (limited to 'modules') diff --git a/modules/extras.py b/modules/extras.py index 92569cff..382ffa7d 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -102,6 +102,7 @@ def run_pnginfo(image): return '', '', '' items = image.info + geninfo = '' if "exif" in image.info: exif = piexif.load(image.info["exif"]) @@ -111,13 +112,14 @@ def run_pnginfo(image): except ValueError: exif_comment = exif_comment.decode('utf8', errors="ignore") - items['exif comment'] = exif_comment + geninfo = exif_comment for field in ['jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif', 'loop', 'background', 'timestamp', 'duration']: items.pop(field, None) + geninfo = items.get('parameters', geninfo) info = '' for key, text in items.items(): @@ -132,4 +134,4 @@ def run_pnginfo(image): message = "Nothing found in the image." info = f"

{message}

" - return '', '', info + return '', geninfo, info diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py new file mode 100644 index 00000000..97f14ad1 --- /dev/null +++ b/modules/generation_parameters_copypaste.py @@ -0,0 +1,88 @@ +from collections import namedtuple +import re +import gradio as gr + +re_param = re.compile(r"\s*([\w ]+):\s*([^,]+)(?:,|$)") +re_imagesize = re.compile(r"^(\d+)x(\d+)$") + + +def parse_generation_parameters(x: str): + """parses generation parameters string, the one you see in text field under the picture in UI: +``` +girl with an artist's beret, determined, blue eyes, desert scene, computer monitors, heavy makeup, by Alphonse Mucha and Charlie Bowater, ((eyeshadow)), (coquettish), detailed, intricate +Negative prompt: ugly, fat, obese, chubby, (((deformed))), [blurry], bad anatomy, disfigured, poorly drawn face, mutation, mutated, (extra_limb), (ugly), (poorly drawn hands), messy drawing +Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model hash: 45dee52b +``` + + returns a dict with field values + """ + + res = {} + + prompt = "" + negative_prompt = "" + + done_with_prompt = False + + *lines, lastline = x.strip().split("\n") + 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 += line + else: + prompt += line + + if len(prompt) > 0: + res["Prompt"] = prompt + + if len(negative_prompt) > 0: + res["Negative prompt"] = negative_prompt + + for k, v in re_param.findall(lastline): + m = re_imagesize.match(v) + if m is not None: + res[k+"-1"] = m.group(1) + res[k+"-2"] = m.group(2) + else: + res[k] = v + + return res + + +def connect_paste(button, d, input_comp, js=None): + items = [] + outputs = [] + + def paste_func(prompt): + params = parse_generation_parameters(prompt) + res = [] + + for key, output in zip(items, outputs): + v = params.get(key, None) + + if v is None: + res.append(gr.update()) + else: + try: + valtype = type(output.value) + val = valtype(v) + res.append(gr.update(value=val)) + except Exception: + res.append(gr.update()) + + return res + + for k, v in d.items(): + items.append(k) + outputs.append(v) + + button.click( + fn=paste_func, + _js=js, + inputs=[input_comp], + outputs=outputs, + ) diff --git a/modules/ui.py b/modules/ui.py index 35d80d3d..1458efa3 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -27,6 +27,7 @@ import modules.scripts import modules.gfpgan_model import modules.codeformer_model import modules.styles +import modules.generation_parameters_copypaste # this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the bowser will not show any UI mimetypes.init() @@ -57,6 +58,8 @@ css_hide_progressbar = """ # Important that they exactly match script.js for tooltip to work. random_symbol = '\U0001f3b2\ufe0f' # 🎲️ reuse_symbol = '\u267b\ufe0f' # ♻️ +art_symbol = '\U0001f3a8' # 🎨 +paste_symbol = '\u2199\ufe0f' # ↙ def plaintext_to_html(text): @@ -336,8 +339,10 @@ def create_toprow(is_img2img): with gr.Column(scale=80): with gr.Row(): prompt = gr.Textbox(label="Prompt", elem_id="prompt", show_label=False, placeholder="Prompt", lines=2) - roll = gr.Button('Roll', elem_id="roll", visible=len(shared.artist_db.artists) > 0) + 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") 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) @@ -368,7 +373,7 @@ def create_toprow(is_img2img): prompt_style_apply = gr.Button('Apply style', elem_id="style_apply") save_style = gr.Button('Create style', elem_id="style_create") - return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style + return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style, paste def setup_progressbar(progressbar, preview, id_part): @@ -391,7 +396,7 @@ def setup_progressbar(progressbar, preview, id_part): def create_ui(txt2img, img2img, run_extras, run_pnginfo): 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 = create_toprow(is_img2img=False) + 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) with gr.Row(elem_id='txt2img_progress_row'): @@ -517,8 +522,27 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): ] ) + txt2img_paste_fields = { + "Prompt": txt2img_prompt, + "Negative prompt": txt2img_negative_prompt, + "Steps": steps, + "Sampler": sampler_index, + "Face restoration": restore_faces, + "CFG scale": cfg_scale, + "Seed": seed, + "Size-1": width, + "Size-2": height, + "Batch size": batch_size, + "Variation seed": subseed, + "Variation seed strength": subseed_strength, + "Seed resize from-1": seed_resize_from_w, + "Seed resize from-2": seed_resize_from_h, + "Denoising strength": denoising_strength, + } + modules.generation_parameters_copypaste.connect_paste(paste, txt2img_paste_fields, txt2img_prompt) + with gr.Blocks(analytics_enabled=False) as img2img_interface: - img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style = create_toprow(is_img2img=True) + img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style, paste = create_toprow(is_img2img=True) with gr.Row(elem_id='img2img_progress_row'): with gr.Column(scale=1): @@ -533,10 +557,10 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): with gr.Column(variant='panel'): with gr.Tabs(elem_id="mode_img2img") as tabs_img2img_mode: - with gr.TabItem('img2img'): + with gr.TabItem('img2img', id='img2img'): init_img = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil") - with gr.TabItem('Inpaint'): + 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) @@ -554,7 +578,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=False) inpaint_full_res_padding = gr.Slider(label='Inpaint at full resolution padding, pixels', minimum=0, maximum=256, step=4, value=32) - with gr.TabItem('Batch img2img'): + with gr.TabItem('Batch img2img', id='batch'): gr.HTML("

Process images in a directory on the same machine where the server is running.

") img2img_batch_input_dir = gr.Textbox(label="Input directory") img2img_batch_output_dir = gr.Textbox(label="Output directory") @@ -717,12 +741,31 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): outputs=[prompt, negative_prompt, style1, style2], ) + img2img_paste_fields = { + "Prompt": img2img_prompt, + "Negative prompt": img2img_negative_prompt, + "Steps": steps, + "Sampler": sampler_index, + "Face restoration": restore_faces, + "CFG scale": cfg_scale, + "Seed": seed, + "Size-1": width, + "Size-2": height, + "Batch size": batch_size, + "Variation seed": subseed, + "Variation seed strength": subseed_strength, + "Seed resize from-1": seed_resize_from_w, + "Seed resize from-2": seed_resize_from_h, + "Denoising strength": denoising_strength, + } + modules.generation_parameters_copypaste.connect_paste(paste, img2img_paste_fields, img2img_prompt) + with gr.Blocks(analytics_enabled=False) as extras_interface: with gr.Row().style(equal_height=False): with gr.Column(variant='panel'): with gr.Tabs(elem_id="mode_extras"): with gr.TabItem('Single Image'): - image = gr.Image(label="Source", source="upload", interactive=True, type="pil") + extras_image = gr.Image(label="Source", source="upload", interactive=True, type="pil") with gr.TabItem('Batch Process'): image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file") @@ -757,7 +800,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): _js="get_extras_tab_index", inputs=[ dummy_component, - image, + extras_image, image_batch, gfpgan_visibility, codeformer_visibility, @@ -788,20 +831,25 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): outputs=[init_img_with_mask], ) - pnginfo_interface = gr.Interface( - wrap_gradio_call(run_pnginfo), - inputs=[ - gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil"), - ], - outputs=[ - gr.HTML(), - gr.HTML(), - gr.HTML(), - ], - allow_flagging="never", - analytics_enabled=False, - live=True, - ) + 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) + html2 = gr.HTML() + + with gr.Row(): + pnginfo_send_to_txt2img = gr.Button('Send to txt2img') + pnginfo_send_to_img2img = gr.Button('Send to img2img') + + image.change( + fn=wrap_gradio_call(run_pnginfo), + inputs=[image], + outputs=[html, generation_info, html2], + ) def create_setting_component(key): def fun(): @@ -936,29 +984,29 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): ) send_to_img2img.click( - fn=lambda x: image_from_url_text(x), + fn=lambda x: (image_from_url_text(x)), _js="extract_image_from_gallery_img2img", inputs=[txt2img_gallery], outputs=[init_img], ) send_to_inpaint.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_img2img", + fn=lambda x: (image_from_url_text(x)), + _js="extract_image_from_gallery_inpaint", inputs=[txt2img_gallery], outputs=[init_img_with_mask], ) img2img_send_to_img2img.click( fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery", + _js="extract_image_from_gallery_img2img", inputs=[img2img_gallery], outputs=[init_img], ) img2img_send_to_inpaint.click( fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery", + _js="extract_image_from_gallery_inpaint", inputs=[img2img_gallery], outputs=[init_img_with_mask], ) @@ -967,16 +1015,19 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): fn=lambda x: image_from_url_text(x), _js="extract_image_from_gallery_extras", inputs=[txt2img_gallery], - outputs=[image], + outputs=[extras_image], ) img2img_send_to_extras.click( fn=lambda x: image_from_url_text(x), _js="extract_image_from_gallery_extras", inputs=[img2img_gallery], - outputs=[image], + outputs=[extras_image], ) + modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_txt2img, txt2img_paste_fields, generation_info, 'switch_to_txt2img') + modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_img2img, img2img_paste_fields, generation_info, 'switch_to_img2img_img2img') + ui_config_file = cmd_opts.ui_config_file ui_settings = {} settings_count = len(ui_settings) -- cgit v1.2.1