mirror of
https://github.com/sd-webui/stable-diffusion-webui.git
synced 2024-12-15 07:12:58 +03:00
[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:
parent
a447938cde
commit
45fafec68a
@ -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 */
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ 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")
|
||||
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
|
||||
|
@ -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,53 +22,45 @@ 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():
|
||||
with gr.Box():
|
||||
output_txt2img_gallery = gr.Gallery(label="Images", elem_id="txt2img_gallery_output").style(grid=[4, 4])
|
||||
|
||||
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,
|
||||
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=call_JS("copyImageFromGalleryToClipboard",
|
||||
fromId="txt2img_gallery_output")
|
||||
#_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",visible=False)
|
||||
output_txt2img_to_upscale_esrgan = gr.Button("Upscale w/ ESRGAN")
|
||||
|
||||
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_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=[],
|
||||
fn=None, show_progress=False,
|
||||
_js=call_JS("gradioInputToClipboard")
|
||||
)
|
||||
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=call_JS("gradioInputToClipboard"), fn=None, show_progress=False)
|
||||
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():
|
||||
|
||||
@ -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")
|
||||
@ -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=[],
|
||||
@ -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
|
||||
|
18
webui.py
18
webui.py
@ -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
|
||||
args_and_names = {
|
||||
"seed": seed,
|
||||
"width": width,
|
||||
"height": height,
|
||||
"steps": steps,
|
||||
"cfg_scale": cfg_scale,
|
||||
"sampler": sampler_name,
|
||||
}
|
||||
|
||||
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()
|
||||
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) }%'''
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user