[Big update] - Dynamic params, Ui rehaul, mobile fixes, and simplification of the txt2img tab (#421)

* Merge with master

* params in prompt WIP

Signed-off-by: Alex Volkov <alex.volkov@fundbox.com>

* Added highligted text output, added copy and pastable dynamic params

Signed-off-by: Alex Volkov <alex.volkov@fundbox.com>

* Fixes with working repo

Signed-off-by: altryne <altryne@gmail.com>

* some fixes

Signed-off-by: Alex Volkov <alex.volkov@fundbox.com>
Signed-off-by: altryne <altryne@gmail.com>
Co-authored-by: hlky <106811348+hlky@users.noreply.github.com>
This commit is contained in:
altryne 2022-09-01 07:18:17 -06:00 committed by GitHub
parent a447938cde
commit 45fafec68a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 206 additions and 63 deletions

View File

@ -1,5 +1,9 @@
[data-testid="image"] {min-height: 512px !important}
* #body>.col:nth-child(2){width:250%;max-width:89vw}
* #body>.col:nth-child(2){
width:250%;
max-width:89vw
}
#prompt_input, #img2img_prompt_input {
padding: 0px;
@ -22,3 +26,39 @@ input[type=number]:disabled { -moz-appearance: textfield; }
width: 512px;
height: auto;
}
#highlight{
font-size: 1.2rem
}
#highlight .uppercase{
text-transform: initial;
}
#highlight .textfield .textspan:nth-child(1){
font-size: 1.2rem
}
/* Mobile fixes and styles */
@media only screen and (max-width: 768px) {
/* rearrange the columns on mobile */
#body>.col:nth-child(2){
width:100%;
max-width:100vw;
order: 1;
}
#body>.col:nth-child(1){
order: 2;
}
#body>.col:nth-child(3){
order: 3;
}
#txt2img_actions_row,
#txt2img_output_row{
flex-direction: row;
flex-wrap: wrap-reverse;
}
/* fix buttons layouts */
}

View File

@ -12,8 +12,9 @@ def readTextFile(*args):
def css(opt):
styling = readTextFile("css", "styles.css")
# TODO: @altryne restore this before merge
if not opt.no_progressbar_hiding:
styling += readTextFile("css", "no_progress_bar.css")
styling += readTextFile("css", "no_progress_bar.css")
return styling
@ -23,6 +24,76 @@ def js(opt):
return data
def js_painterro_launch(to_id):
return w(f"Painterro.init('{to_id}')")
def js_move_image(from_id, to_id):
return w(f"moveImageFromGallery('{from_id}', '{to_id}')")
def js_copy_to_clipboard(from_id):
return w(f"copyImageFromGalleryToClipboard('{from_id}')")
def js_img2img_submit(prompt_row_id):
return w(f"clickFirstVisibleButton('{prompt_row_id}')")
# TODO : @altryne fix this to the new JS format
js_copy_txt2img_output = "(x) => {navigator.clipboard.writeText(document.querySelector('gradio-app').shadowRoot.querySelector('#highlight .textfield').textContent.replace(/\s+/g,' ').replace(/: /g,':'))}"
js_parse_prompt ="""
(txt2img_prompt, txt2img_width, txt2img_height, txt2img_steps, txt2img_seed, txt2img_batch_count, txt2img_cfg) => {
const prompt_input = document.querySelector('gradio-app').shadowRoot.querySelector('#prompt_input [data-testid="textbox"]');
const multiline = document.querySelector('gradio-app').shadowRoot.querySelector('#submit_on_enter label:nth-child(2)')
if (prompt_input.scrollWidth > prompt_input.clientWidth + 10 ) {
multiline.click();
}
let height_match = /(?:-h|-H|--height|height)[ :]?(?<height>\d+) /.exec(txt2img_prompt);
if (height_match) {
txt2img_height = Math.round(height_match.groups.height / 64) * 64;
txt2img_prompt = txt2img_prompt.replace(height_match[0], '');
}
let width_match = /(?:-w|-W|--width|width)[ :]?(?<width>\d+) /.exec(txt2img_prompt);
if (width_match) {
txt2img_width = Math.round(width_match.groups.width / 64) * 64;
txt2img_prompt = txt2img_prompt.replace(width_match[0], '');
}
let steps_match = /(?:-s|--steps|steps)[ :]?(?<steps>\d+) /.exec(txt2img_prompt);
if (steps_match) {
txt2img_steps = steps_match.groups.steps.trim();
txt2img_prompt = txt2img_prompt.replace(steps_match[0], '');
}
let seed_match = /(?:-S|--seed|seed)[ :]?(?<seed>\d+) /.exec(txt2img_prompt);
if (seed_match) {
txt2img_seed = seed_match.groups.seed;
txt2img_prompt = txt2img_prompt.replace(seed_match[0], '');
}
let batch_count_match = /(?:-n|-N|--number|number)[ :]?(?<batch_count>\d+) /.exec(txt2img_prompt);
if (batch_count_match) {
txt2img_batch_count = batch_count_match.groups.batch_count;
txt2img_prompt = txt2img_prompt.replace(batch_count_match[0], '');
}
let cfg_scale_match = /(?:-c|-C|--cfg-scale|cfg_scale|cfg)[ :]?(?<cfgscale>\d\.?\d+?) /.exec(txt2img_prompt);
if (cfg_scale_match) {
txt2img_cfg = parseFloat(cfg_scale_match.groups.cfgscale).toFixed(1);
txt2img_prompt = txt2img_prompt.replace(cfg_scale_match[0], '');
}
let sampler_match = /(?:-A|--sampler|sampler)[ :]?(?<sampler>\w+) /.exec(txt2img_prompt);
if (sampler_match) {
txt2img_prompt = txt2img_prompt.replace(sampler_match[0], '');
}
return [txt2img_prompt, parseInt(txt2img_width), parseInt(txt2img_height), parseInt(txt2img_steps), txt2img_seed, parseInt(txt2img_batch_count), parseFloat(txt2img_cfg)];
}
"""
# @altryne this came up as conflict, still needed or no?
# Wrap the typical SD method call into async closure for ease of use
# Supplies the js function with a params object
# That includes all the passed arguments and input from Gradio: x

View File

@ -1,5 +1,5 @@
import gradio as gr
from frontend.css_and_js import css, js, call_JS
from frontend.css_and_js import css, js, call_JS, js_parse_prompt, js_copy_txt2img_output
import frontend.ui_functions as uifn
def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda x: x, txt2img_defaults={}, RealESRGAN=True, GFPGAN=True,LDSR=True,
@ -9,7 +9,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
with gr.Blocks(css=css(opt), analytics_enabled=False, title="Stable Diffusion WebUI") as demo:
with gr.Tabs(elem_id='tabss') as tabs:
with gr.TabItem("Stable Diffusion Text-to-Image Unified", id='txt2img_tab'):
with gr.TabItem("Text-to-Image", id='txt2img_tab'):
with gr.Row(elem_id="prompt_row"):
txt2img_prompt = gr.Textbox(label="Prompt",
elem_id='prompt_input',
@ -22,54 +22,46 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
with gr.Row(elem_id='body').style(equal_height=False):
with gr.Column():
txt2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width",
txt2img_width = gr.Slider(minimum=64, maximum=1024, step=64, label="Width",
value=txt2img_defaults["width"])
txt2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height",
txt2img_height = gr.Slider(minimum=64, maximum=1024, step=64, label="Height",
value=txt2img_defaults["height"])
txt2img_cfg = gr.Slider(minimum=-40.0, maximum=30.0, step=0.5,
label='Classifier Free Guidance Scale (how strongly the image should follow the prompt)',
value=txt2img_defaults['cfg_scale'], elem_id='cfg_slider')
txt2img_seed = gr.Textbox(label="Seed (blank to randomize)", lines=1, max_lines=1,
value=txt2img_defaults["seed"])
txt2img_batch_count = gr.Slider(minimum=1, maximum=250, step=1,
label='Batch count (how many batches of images to generate)',
txt2img_batch_count = gr.Slider(minimum=1, maximum=10, step=1,
label='Number of images to generate',
value=txt2img_defaults['n_iter'])
txt2img_batch_size = gr.Slider(minimum=1, maximum=8, step=1,
label='Batch size (how many images are in a batch; memory-hungry)',
value=txt2img_defaults['batch_size'])
txt2img_dimensions_info_text_box = gr.Textbox(label="Aspect ratio (4:3 = 1.333 | 16:9 = 1.777 | 21:9 = 2.333)")
with gr.Column():
output_txt2img_gallery = gr.Gallery(label="Images", elem_id="txt2img_gallery_output").style(grid=[4, 4])
with gr.Box():
output_txt2img_gallery = gr.Gallery(label="Images", elem_id="txt2img_gallery_output").style(grid=[4, 4])
gr.Markdown("Select an image from the gallery, then click one of the buttons below to perform an action.")
with gr.Row(elem_id='txt2img_actions_row'):
gr.Button("Copy to clipboard").click(fn=None,
inputs=output_txt2img_gallery,
outputs=[],
#_js=js_copy_to_clipboard( 'txt2img_gallery_output')
)
output_txt2img_copy_to_input_btn = gr.Button("Push to img2img")
if RealESRGAN is not None:
output_txt2img_to_upscale_esrgan = gr.Button("Upscale w/ ESRGAN")
with gr.Tabs():
with gr.TabItem("Generated image actions", id="text2img_actions_tab"):
gr.Markdown(
'Select an image from the gallery, then click one of the buttons below to perform an action.')
with gr.Row():
output_txt2img_copy_clipboard = gr.Button("Copy to clipboard")\
.click(fn=None,
inputs=output_txt2img_gallery,
outputs=[],
_js=call_JS("copyImageFromGalleryToClipboard",
fromId="txt2img_gallery_output")
)
output_txt2img_copy_to_input_btn = gr.Button("Push to img2img")
if RealESRGAN is not None:
output_txt2img_to_upscale_esrgan = gr.Button("Upscale w/ ESRGAN",visible=False)
with gr.TabItem("Output Info", id="text2img_output_info_tab"):
output_txt2img_params = gr.Textbox(label="Generation parameters", interactive=False)
with gr.Row():
output_txt2img_copy_params = gr.Button("Copy full parameters").click(
inputs=output_txt2img_params, outputs=[],
fn=None, show_progress=False,
_js=call_JS("gradioInputToClipboard")
)
output_txt2img_seed = gr.Number(label='Seed', interactive=False, visible=False)
output_txt2img_copy_seed = gr.Button("Copy only seed").click(
inputs=output_txt2img_seed, outputs=[],
_js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False)
output_txt2img_stats = gr.HTML(label='Stats')
output_txt2img_params = gr.Highlightedtext(label="Generation parameters", interactive=False, elem_id='highlight')
with gr.Group():
with gr.Row(elem_id='txt2img_output_row'):
output_txt2img_copy_params = gr.Button("Copy full parameters").click(
inputs=[output_txt2img_params], outputs=[],
_js=js_copy_txt2img_output,
fn=None, show_progress=False)
output_txt2img_seed = gr.Number(label='Seed', interactive=False, visible=False)
output_txt2img_copy_seed = gr.Button("Copy only seed").click(
inputs=[output_txt2img_seed], outputs=[],
_js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
output_txt2img_stats = gr.HTML(label='Stats')
with gr.Column():
txt2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps",
@ -83,13 +75,16 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
txt2img_submit_on_enter = gr.Radio(['Yes', 'No'],
label="Submit on enter? (no means multiline)",
value=txt2img_defaults['submit_on_enter'],
interactive=True)
interactive=True, elem_id='submit_on_enter')
txt2img_submit_on_enter.change(
lambda x: gr.update(max_lines=1 if x == 'Yes' else 25), txt2img_submit_on_enter,
txt2img_prompt)
with gr.TabItem('Advanced'):
txt2img_toggles = gr.CheckboxGroup(label='', choices=txt2img_toggles,
value=txt2img_toggle_defaults, type="index")
txt2img_batch_size = gr.Slider(minimum=1, maximum=8, step=1,
label='Batch size (how many images are in a batch; memory-hungry)',
value=txt2img_defaults['batch_size'])
txt2img_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model',
choices=['RealESRGAN_x4plus',
'RealESRGAN_x4plus_anime_6B'],
@ -118,10 +113,19 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
txt2img_height, txt2img_width, txt2img_embeddings, txt2img_variant_amount, txt2img_variant_seed],
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
)
txt2img_width.change(fn=uifn.update_dimensions_info, inputs=[txt2img_width, txt2img_height], outputs=txt2img_dimensions_info_text_box)
txt2img_height.change(fn=uifn.update_dimensions_info, inputs=[txt2img_width, txt2img_height], outputs=txt2img_dimensions_info_text_box)
# txt2img_width.change(fn=uifn.update_dimensions_info, inputs=[txt2img_width, txt2img_height], outputs=txt2img_dimensions_info_text_box)
# txt2img_height.change(fn=uifn.update_dimensions_info, inputs=[txt2img_width, txt2img_height], outputs=txt2img_dimensions_info_text_box)
with gr.TabItem("Stable Diffusion Image-to-Image Unified", id="img2img_tab"):
live_prompt_params = [txt2img_prompt, txt2img_width, txt2img_height, txt2img_steps, txt2img_seed, txt2img_batch_count, txt2img_cfg]
txt2img_prompt.change(
fn=None,
inputs=live_prompt_params,
outputs=live_prompt_params,
_js=js_parse_prompt
)
with gr.TabItem("Image-to-Image Unified", id="img2img_tab"):
with gr.Row(elem_id="prompt_row"):
img2img_prompt = gr.Textbox(label="Prompt",
elem_id='img2img_prompt_input',
@ -130,6 +134,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
max_lines=1 if txt2img_defaults['submit_on_enter'] == 'Yes' else 25,
value=img2img_defaults['prompt'],
show_label=False).style()
img2img_btn_mask = gr.Button("Generate", variant="primary", visible=False,
elem_id="img2img_mask_btn")
img2img_btn_editor = gr.Button("Generate", variant="primary", elem_id="img2img_edit_btn")
@ -144,14 +149,14 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
elem_id="img2img_mask")
with gr.Tabs():
with gr.TabItem("Editor Options"):
with gr.TabItem("Editor Options"):
with gr.Column():
img2img_image_editor_mode = gr.Radio(choices=["Mask", "Crop", "Uncrop"], label="Image Editor Mode",
value="Crop", elem_id='edit_mode_select')
img2img_mask = gr.Radio(choices=["Keep masked area", "Regenerate only masked area"],
label="Mask Mode", type="index",
value=img2img_mask_modes[img2img_defaults['mask_mode']], visible=False)
img2img_mask_blur_strength = gr.Slider(minimum=1, maximum=10, step=1,
label="How much blurry should the mask be? (to avoid hard edges)",
value=3, visible=False)
@ -160,7 +165,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
choices=["Just resize"],
type="index",
value=img2img_resize_modes[img2img_defaults['resize_mode']])
img2img_painterro_btn = gr.Button("Advanced Editor")
with gr.TabItem("Hints"):
img2img_help = gr.Markdown(visible=False, value=uifn.help_text)
@ -183,7 +188,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
with gr.Row():
output_img2img_copy_params = gr.Button("Copy full parameters").click(
inputs=output_img2img_params, outputs=[],
_js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False)
_js='(x) => {navigator.clipboard.writeText(x.replace(": ",":"))}', fn=None, show_progress=False)
output_img2img_seed = gr.Number(label='Seed', interactive=False, visible=False)
output_img2img_copy_seed = gr.Button("Copy only seed").click(
inputs=output_img2img_seed, outputs=[],
@ -197,7 +202,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
value=img2img_defaults["width"])
img2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height",
value=img2img_defaults["height"])
img2img_cfg = gr.Slider(minimum=-40.0, maximum=30.0, step=0.5,
label='Classifier Free Guidance Scale (how strongly the image should follow the prompt)',
value=img2img_defaults['cfg_scale'], elem_id='cfg_slider')
@ -296,6 +301,8 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x,imgproc=lambda
img2img_denoising, img2img_seed, img2img_height, img2img_width, img2img_resize,
img2img_embeddings],
[output_img2img_gallery, output_img2img_seed, output_img2img_params, output_img2img_stats])
img2img_btn_editor.click(*img2img_submit_params())
# GENERATE ON ENTER

View File

@ -976,10 +976,22 @@ skip_grid, sort_samples, sampler_name, ddim_eta, n_iter, batch_size, i, denoisin
mem_max_used, mem_total = mem_mon.read_and_stop()
time_diff = time.time()-start_time
info = f"""
{prompt}
Steps: {steps}, Sampler: {sampler_name}, CFG scale: {cfg_scale}, Seed: {seed}{', Denoising strength: '+str(denoising_strength) if init_img is not None else ''}{', GFPGAN' if use_GFPGAN and GFPGAN is not None else ''}{', '+realesrgan_model_name if use_RealESRGAN and RealESRGAN is not None else ''}{', Prompt Matrix Mode.' if prompt_matrix else ''}""".strip()
args_and_names = {
"seed": seed,
"width": width,
"height": height,
"steps": steps,
"cfg_scale": cfg_scale,
"sampler": sampler_name,
}
full_string = f"{prompt}\n"+ " ".join([f"{k}:" for k,v in args_and_names.items()])
info = {
'text': full_string,
'entities': [{'entity':str(v), 'start': full_string.find(f"{k}:"),'end': full_string.find(f"{k}:") + len(f"{k} ")} for k,v in args_and_names.items()]
}
# info = f"""
# {prompt} --seed {seed} --W {width} --H {height} -s {steps} -C {cfg_scale} --sampler {sampler_name} {', Denoising strength: '+str(denoising_strength) if init_img is not None else ''}{', GFPGAN' if use_GFPGAN and GFPGAN is not None else ''}{', '+realesrgan_model_name if use_RealESRGAN and RealESRGAN is not None else ''}{', Prompt Matrix Mode.' if prompt_matrix else ''}""".strip()
stats = f'''
Took { round(time_diff, 2) }s total ({ round(time_diff/(len(all_prompts)),2) }s per image)
Peak memory usage: { -(mem_max_used // -1_048_576) } MiB / { -(mem_total // -1_048_576) } MiB / { round(mem_max_used/mem_total*100, 3) }%'''

View File

@ -20,14 +20,27 @@ RealESRGAN = True
def run_goBIG():
pass
def txt2img(*args, **kwargs):
images = [
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80",
"https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=386&q=80",
"https://images.unsplash.com/photo-1542909168-82c3e7fdca5c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8aHVtYW4lMjBmYWNlfGVufDB8fDB8fA%3D%3D&w=1000&q=80",
"https://images.unsplash.com/photo-1546456073-92b9f0a8d413?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80",
"https://images.unsplash.com/photo-1601412436009-d964bd02edbc?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=464&q=80",
]
return images, 1234, 'random', 'random output'
#Output should match output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats
# info = f"""{args[0]} --seed {args[9]} --W {args[11]} --H {args[10]} -s {args[1]} -C {float(args[8])} --sampler {args[2]} """.strip()
args_and_names = {
"seed": args[9],
"width": args[11],
"height": args[10],
"steps": args[1],
"cfg_scale": str(args[8]),
"sampler": args[2],
}
full_string = f"{args[0]}\n"+ " ".join([f"{k}:" for k,v in args_and_names.items()])
info = {
'text': full_string,
'entities': [{'entity':str(v), 'start': full_string.find(f"{k}:"),'end': full_string.find(f"{k}:") + len(f"{k} ")} for k,v in args_and_names.items()]
}
images = []
for i in range(args[6]):
images.append(f"http://placeimg.com/{args[11]}/{args[10]}/any")
return images, int(time.time()) , info, 'random output'
def img2img(*args, **kwargs):
images = [
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80",
@ -204,4 +217,4 @@ demo = draw_gradio_ui(opt,
)
# demo.queue()
demo.launch(share=False, debug=True)
demo.launch(share=True, debug=True)