mirror of
https://github.com/sd-webui/stable-diffusion-webui.git
synced 2024-12-14 23:02:00 +03:00
Refactored the UI, added a web_playground.py to be run without any models.
This commit is contained in:
parent
8d15f865e9
commit
64ca78ca76
0
frontend/__init__.py
Normal file
0
frontend/__init__.py
Normal file
66
frontend/css_and_js.py
Normal file
66
frontend/css_and_js.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
def css(opt):
|
||||||
|
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; }
|
||||||
|
"""
|
||||||
|
styling = """
|
||||||
|
|
||||||
|
[data-testid="image"] {min-height: 512px !important}
|
||||||
|
* #body>.col:nth-child(2){width:250%;max-width:89vw}
|
||||||
|
|
||||||
|
#prompt_row input{
|
||||||
|
font-size:20px
|
||||||
|
}
|
||||||
|
#edit_mode_select{width:auto !important}
|
||||||
|
input[type=number]:disabled { -moz-appearance: textfield;+ }
|
||||||
|
#generate, #img2img_mask_btn,#img2img_edit_btn{
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
@media (max-width: 420px){
|
||||||
|
#generate, #img2img_mask_btn,#img2img_edit_btn{
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
return styling if opt.no_progressbar_hiding else styling + css_hide_progressbar
|
||||||
|
|
||||||
|
# This is the code that finds which selected item the user has in the gallery
|
||||||
|
js_part_getindex_txt2img="""
|
||||||
|
const root = document.querySelector('gradio-app').shadowRoot;
|
||||||
|
const getIndex = function(){
|
||||||
|
const selected = root.querySelector('#txt2img_gallery_output .\\\\!ring-2');
|
||||||
|
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;
|
||||||
|
};"""
|
||||||
|
js_part_getindex_img2img="""
|
||||||
|
const root = document.querySelector('gradio-app').shadowRoot;
|
||||||
|
const getIndex = function(){
|
||||||
|
const selected = root.querySelector('#img2img_gallery_output .\\\\!ring-2');
|
||||||
|
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;
|
||||||
|
};"""
|
||||||
|
js_part_clear_img2img="""
|
||||||
|
root.querySelector('#img2img_editor .modify-upload button:last-child')?.click();
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
js_return_selected_txt2img = "(x) => {" + js_part_getindex_txt2img + js_part_clear_img2img + """
|
||||||
|
return [x[getIndex()].replace('data:;','data:image/png;')];
|
||||||
|
}"""
|
||||||
|
js_return_selected_img2img = "(x) => {" + js_part_getindex_img2img + js_part_clear_img2img + """
|
||||||
|
return [x[getIndex()].replace('data:;','data:image/png;')];
|
||||||
|
}"""
|
||||||
|
|
||||||
|
js_part_copy_to_clipboard="""
|
||||||
|
const data = x[getIndex()];
|
||||||
|
const blob = await (await fetch(data.replace('data:;','data:image/png;'))).blob();
|
||||||
|
const item = new ClipboardItem({'image/png': blob});
|
||||||
|
navigator.clipboard.write([item]);
|
||||||
|
return x;
|
||||||
|
}"""
|
||||||
|
js_copy_selected_txt2img = "async (x) => {" + js_part_getindex_txt2img + js_part_copy_to_clipboard
|
||||||
|
js_copy_selected_img2img = "async (x) => {" + js_part_getindex_img2img + js_part_copy_to_clipboard
|
366
frontend/frontend.py
Normal file
366
frontend/frontend.py
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
import gradio as gr
|
||||||
|
from frontend.css_and_js import *
|
||||||
|
from frontend.css_and_js import css
|
||||||
|
import frontend.ui_functions as uifn
|
||||||
|
|
||||||
|
def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaults={}, RealESRGAN=True, GFPGAN=True,
|
||||||
|
txt2img_toggles={}, txt2img_toggle_defaults='k_euler', show_embeddings=False, img2img_defaults={},
|
||||||
|
img2img_toggles={}, img2img_toggle_defaults={}, sample_img2img=None, img2img_mask_modes=None,
|
||||||
|
img2img_resize_modes=None, user_defaults={}, run_GFPGAN=lambda x: x, run_RealESRGAN=lambda x: x):
|
||||||
|
|
||||||
|
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.Row(elem_id="prompt_row"):
|
||||||
|
txt2img_prompt = gr.Textbox(label="Prompt",
|
||||||
|
elem_id='prompt_input',
|
||||||
|
placeholder="A corgi wearing a top hat as an oil painting.",
|
||||||
|
lines=1,
|
||||||
|
max_lines=1 if txt2img_defaults['submit_on_enter'] == 'Yes' else 25,
|
||||||
|
value=txt2img_defaults['prompt'],
|
||||||
|
show_label=False)
|
||||||
|
txt2img_btn = gr.Button("Generate", elem_id="generate", variant="primary")
|
||||||
|
|
||||||
|
with gr.Row(elem_id='body').style(equal_height=False):
|
||||||
|
with gr.Column():
|
||||||
|
txt2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height",
|
||||||
|
value=txt2img_defaults["height"])
|
||||||
|
txt2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width",
|
||||||
|
value=txt2img_defaults["width"])
|
||||||
|
txt2img_cfg = gr.Slider(minimum=1.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'])
|
||||||
|
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)',
|
||||||
|
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'])
|
||||||
|
with gr.Column():
|
||||||
|
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,
|
||||||
|
inputs=output_txt2img_gallery,
|
||||||
|
outputs=[],
|
||||||
|
_js=js_copy_selected_txt2img)
|
||||||
|
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.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=[],
|
||||||
|
_js='(x) => navigator.clipboard.writeText(x)', 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",
|
||||||
|
value=txt2img_defaults['ddim_steps'])
|
||||||
|
txt2img_sampling = gr.Dropdown(label='Sampling method (k_lms is default k-diffusion sampler)',
|
||||||
|
choices=["DDIM", "PLMS", 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a',
|
||||||
|
'k_euler', 'k_heun', 'k_lms'],
|
||||||
|
value=txt2img_defaults['sampler_name'])
|
||||||
|
with gr.Tabs():
|
||||||
|
with gr.TabItem('Simple'):
|
||||||
|
txt2img_submit_on_enter = gr.Radio(['Yes', 'No'],
|
||||||
|
label="Submit on enter? (no means multiline)",
|
||||||
|
value=txt2img_defaults['submit_on_enter'],
|
||||||
|
interactive=True)
|
||||||
|
txt2img_submit_on_enter.change(
|
||||||
|
lambda x: gr.update(max_lines=1 if x == 'Single' 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_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model',
|
||||||
|
choices=['RealESRGAN_x4plus',
|
||||||
|
'RealESRGAN_x4plus_anime_6B'],
|
||||||
|
value='RealESRGAN_x4plus',
|
||||||
|
visible=RealESRGAN is not None) # TODO: Feels like I shouldnt slot it in here.
|
||||||
|
txt2img_ddim_eta = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="DDIM ETA",
|
||||||
|
value=txt2img_defaults['ddim_eta'], visible=False)
|
||||||
|
txt2img_embeddings = gr.File(label="Embeddings file for textual inversion",
|
||||||
|
visible=show_embeddings)
|
||||||
|
|
||||||
|
txt2img_btn.click(
|
||||||
|
txt2img,
|
||||||
|
[txt2img_prompt, txt2img_steps, txt2img_sampling, txt2img_toggles, txt2img_realesrgan_model_name,
|
||||||
|
txt2img_ddim_eta, txt2img_batch_count, txt2img_batch_size, txt2img_cfg, txt2img_seed,
|
||||||
|
txt2img_height, txt2img_width, txt2img_embeddings],
|
||||||
|
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
|
||||||
|
)
|
||||||
|
txt2img_prompt.submit(
|
||||||
|
txt2img,
|
||||||
|
[txt2img_prompt, txt2img_steps, txt2img_sampling, txt2img_toggles, txt2img_realesrgan_model_name,
|
||||||
|
txt2img_ddim_eta, txt2img_batch_count, txt2img_batch_size, txt2img_cfg, txt2img_seed,
|
||||||
|
txt2img_height, txt2img_width, txt2img_embeddings],
|
||||||
|
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
|
||||||
|
)
|
||||||
|
|
||||||
|
with gr.TabItem("Stable Diffusion 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',
|
||||||
|
placeholder="A fantasy landscape, trending on artstation.",
|
||||||
|
lines=1,
|
||||||
|
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")
|
||||||
|
with gr.Row().style(equal_height=False):
|
||||||
|
with gr.Column():
|
||||||
|
gr.Markdown('#### Img2Img input')
|
||||||
|
img2img_image_editor = gr.Image(value=sample_img2img, source="upload", interactive=True,
|
||||||
|
type="pil", tool="select", elem_id="img2img_editor")
|
||||||
|
img2img_image_mask = gr.Image(value=sample_img2img, source="upload", interactive=True,
|
||||||
|
type="pil", tool="sketch", visible=False,
|
||||||
|
elem_id="img2img_mask")
|
||||||
|
|
||||||
|
with gr.Row():
|
||||||
|
img2img_image_editor_mode = gr.Radio(choices=["Mask", "Crop"], label="Image Editor Mode",
|
||||||
|
value="Crop", elem_id='edit_mode_select')
|
||||||
|
|
||||||
|
img2img_painterro_btn = gr.Button("Advanced Editor")
|
||||||
|
img2img_copy_from_painterro_btn = gr.Button(value="Get Image from Advanced Editor")
|
||||||
|
img2img_show_help_btn = gr.Button("Show Hints")
|
||||||
|
img2img_hide_help_btn = gr.Button("Hide Hints", visible=False)
|
||||||
|
img2img_help = gr.Markdown(visible=False, value="")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
with gr.Column():
|
||||||
|
gr.Markdown('#### Img2Img Results')
|
||||||
|
output_img2img_gallery = gr.Gallery(label="Images", elem_id="img2img_gallery_output").style(grid=[4,4,4])
|
||||||
|
with gr.Tabs():
|
||||||
|
with gr.TabItem("Generated image actions", id="img2img_actions_tab"):
|
||||||
|
with gr.Group():
|
||||||
|
gr.Markdown("Select an image, then press one of the buttons below")
|
||||||
|
output_img2img_copy_to_clipboard_btn = gr.Button("Copy to clipboard")
|
||||||
|
output_img2img_copy_to_input_btn = gr.Button("Push to img2img input")
|
||||||
|
output_img2img_copy_to_mask_btn = gr.Button("Push to img2img input mask")
|
||||||
|
gr.Markdown("Warning: This will clear your current image and mask settings!")
|
||||||
|
with gr.TabItem("Output info", id="img2img_output_info_tab"):
|
||||||
|
output_img2img_params = gr.Textbox(label="Generation parameters")
|
||||||
|
with gr.Row():
|
||||||
|
output_img2img_copy_params = gr.Button("Copy full parameters").click(
|
||||||
|
inputs=output_img2img_params, outputs=[],
|
||||||
|
_js='(x) => navigator.clipboard.writeText(x)', 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=[],
|
||||||
|
_js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
|
||||||
|
output_img2img_stats = gr.HTML(label='Stats')
|
||||||
|
gr.Markdown('# img2img settings')
|
||||||
|
with gr.Row():
|
||||||
|
|
||||||
|
with gr.Column():
|
||||||
|
img2img_batch_size = gr.Slider(minimum=1, maximum=8, step=1,
|
||||||
|
label='Batch size (how many images are in a batch; memory-hungry)',
|
||||||
|
value=img2img_defaults['batch_size'])
|
||||||
|
img2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width",
|
||||||
|
value=img2img_defaults["width"])
|
||||||
|
img2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height",
|
||||||
|
value=img2img_defaults["height"])
|
||||||
|
img2img_seed = gr.Textbox(label="Seed (blank to randomize)", lines=1,
|
||||||
|
value=img2img_defaults["seed"])
|
||||||
|
img2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps",
|
||||||
|
value=img2img_defaults['ddim_steps'])
|
||||||
|
img2img_batch_count = gr.Slider(minimum=1, maximum=250, step=1,
|
||||||
|
label='Batch count (how many batches of images to generate)',
|
||||||
|
value=img2img_defaults['n_iter'])
|
||||||
|
with gr.Column():
|
||||||
|
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)
|
||||||
|
|
||||||
|
img2img_sampling = gr.Dropdown(label='Sampling method (k_lms is default k-diffusion sampler)',
|
||||||
|
choices=["DDIM", 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler',
|
||||||
|
'k_heun', 'k_lms'],
|
||||||
|
value=img2img_defaults['sampler_name'])
|
||||||
|
img2img_toggles = gr.CheckboxGroup(label='', choices=img2img_toggles,
|
||||||
|
value=img2img_toggle_defaults, type="index")
|
||||||
|
img2img_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model',
|
||||||
|
choices=['RealESRGAN_x4plus',
|
||||||
|
'RealESRGAN_x4plus_anime_6B'],
|
||||||
|
value='RealESRGAN_x4plus',
|
||||||
|
visible=RealESRGAN is not None) # TODO: Feels like I shouldnt slot it in here.
|
||||||
|
|
||||||
|
|
||||||
|
img2img_cfg = gr.Slider(minimum=1.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'])
|
||||||
|
img2img_denoising = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising Strength',
|
||||||
|
value=img2img_defaults['denoising_strength'])
|
||||||
|
|
||||||
|
img2img_resize = gr.Radio(label="Resize mode",
|
||||||
|
choices=["Just resize", "Crop and resize", "Resize and fill"],
|
||||||
|
type="index",
|
||||||
|
value=img2img_resize_modes[img2img_defaults['resize_mode']])
|
||||||
|
img2img_embeddings = gr.File(label="Embeddings file for textual inversion",
|
||||||
|
visible=show_embeddings)
|
||||||
|
|
||||||
|
img2img_image_editor_mode.change(
|
||||||
|
uifn.change_image_editor_mode,
|
||||||
|
[img2img_image_editor_mode, img2img_image_editor, img2img_resize, img2img_width, img2img_height],
|
||||||
|
[img2img_image_editor, img2img_image_mask, img2img_btn_editor, img2img_btn_mask,
|
||||||
|
img2img_painterro_btn, img2img_copy_from_painterro_btn, img2img_mask, img2img_mask_blur_strength]
|
||||||
|
)
|
||||||
|
|
||||||
|
img2img_image_editor.edit(
|
||||||
|
uifn.update_image_mask,
|
||||||
|
[img2img_image_editor, img2img_resize, img2img_width, img2img_height],
|
||||||
|
img2img_image_mask
|
||||||
|
)
|
||||||
|
|
||||||
|
img2img_show_help_btn.click(
|
||||||
|
uifn.show_help,
|
||||||
|
None,
|
||||||
|
[img2img_show_help_btn, img2img_hide_help_btn, img2img_help]
|
||||||
|
)
|
||||||
|
|
||||||
|
img2img_hide_help_btn.click(
|
||||||
|
uifn.hide_help,
|
||||||
|
None,
|
||||||
|
[img2img_show_help_btn, img2img_hide_help_btn, img2img_help]
|
||||||
|
)
|
||||||
|
|
||||||
|
output_txt2img_copy_to_input_btn.click(
|
||||||
|
uifn.copy_img_to_input,
|
||||||
|
[output_txt2img_gallery],
|
||||||
|
[img2img_image_editor, img2img_image_mask, tabs],
|
||||||
|
_js=js_return_selected_txt2img
|
||||||
|
)
|
||||||
|
|
||||||
|
output_img2img_copy_to_input_btn.click(
|
||||||
|
uifn.copy_img_to_edit,
|
||||||
|
[output_img2img_gallery],
|
||||||
|
[img2img_image_editor, tabs, img2img_image_editor_mode],
|
||||||
|
_js=js_return_selected_img2img
|
||||||
|
)
|
||||||
|
output_img2img_copy_to_mask_btn.click(
|
||||||
|
uifn.copy_img_to_mask,
|
||||||
|
[output_img2img_gallery],
|
||||||
|
[img2img_image_mask, tabs, img2img_image_editor_mode],
|
||||||
|
_js=js_return_selected_img2img
|
||||||
|
)
|
||||||
|
|
||||||
|
output_img2img_copy_to_clipboard_btn.click(fn=None, inputs=output_img2img_gallery, outputs=[],
|
||||||
|
_js=js_copy_selected_img2img)
|
||||||
|
|
||||||
|
img2img_btn_mask.click(
|
||||||
|
img2img,
|
||||||
|
[img2img_prompt, img2img_image_editor_mode, img2img_image_mask, img2img_mask,
|
||||||
|
img2img_mask_blur_strength, img2img_steps, img2img_sampling, img2img_toggles,
|
||||||
|
img2img_realesrgan_model_name, img2img_batch_count, img2img_batch_size, img2img_cfg,
|
||||||
|
img2img_denoising, img2img_seed, img2img_height, img2img_width, img2img_resize,
|
||||||
|
img2img_embeddings],
|
||||||
|
[output_img2img_gallery, output_img2img_seed, output_img2img_params, output_img2img_stats]
|
||||||
|
)
|
||||||
|
def img2img_submit_params():
|
||||||
|
return (img2img,
|
||||||
|
[img2img_prompt, img2img_image_editor_mode, img2img_image_editor, img2img_mask,
|
||||||
|
img2img_mask_blur_strength, img2img_steps, img2img_sampling, img2img_toggles,
|
||||||
|
img2img_realesrgan_model_name, img2img_batch_count, img2img_batch_size, img2img_cfg,
|
||||||
|
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())
|
||||||
|
img2img_prompt.submit(*img2img_submit_params())
|
||||||
|
|
||||||
|
img2img_painterro_btn.click(None, [img2img_image_editor], None, _js="""(img) => {
|
||||||
|
try {
|
||||||
|
Painterro({
|
||||||
|
hiddenTools: ['arrow'],
|
||||||
|
saveHandler: function (image, done) {
|
||||||
|
localStorage.setItem('painterro-image', image.asDataURL());
|
||||||
|
done(true);
|
||||||
|
},
|
||||||
|
}).show(Array.isArray(img) ? img[0] : img);
|
||||||
|
} catch(e) {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://unpkg.com/painterro@1.2.78/build/painterro.min.js';
|
||||||
|
document.head.appendChild(script);
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.appendChild(document.createTextNode('.ptro-holder-wrapper { z-index: 9999 !important; }'));
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}""")
|
||||||
|
|
||||||
|
img2img_copy_from_painterro_btn.click(None, None, [img2img_image_editor, img2img_image_mask], _js="""() => {
|
||||||
|
const image = localStorage.getItem('painterro-image')
|
||||||
|
return [image, image];
|
||||||
|
}""")
|
||||||
|
|
||||||
|
if GFPGAN is not None:
|
||||||
|
gfpgan_defaults = {
|
||||||
|
'strength': 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
if 'gfpgan' in user_defaults:
|
||||||
|
gfpgan_defaults.update(user_defaults['gfpgan'])
|
||||||
|
|
||||||
|
with gr.TabItem("GFPGAN", id='cfpgan_tab'):
|
||||||
|
gr.Markdown("Fix faces on images")
|
||||||
|
with gr.Row():
|
||||||
|
with gr.Column():
|
||||||
|
gfpgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
||||||
|
gfpgan_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Effect strength",
|
||||||
|
value=gfpgan_defaults['strength'])
|
||||||
|
gfpgan_btn = gr.Button("Generate", variant="primary")
|
||||||
|
with gr.Column():
|
||||||
|
gfpgan_output = gr.Image(label="Output")
|
||||||
|
gfpgan_btn.click(
|
||||||
|
run_GFPGAN,
|
||||||
|
[gfpgan_source, gfpgan_strength],
|
||||||
|
[gfpgan_output]
|
||||||
|
)
|
||||||
|
if RealESRGAN is not None:
|
||||||
|
with gr.TabItem("RealESRGAN", id='realesrgan_tab'):
|
||||||
|
gr.Markdown("Upscale images")
|
||||||
|
with gr.Row():
|
||||||
|
with gr.Column():
|
||||||
|
realesrgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
||||||
|
realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus',
|
||||||
|
'RealESRGAN_x4plus_anime_6B'],
|
||||||
|
value='RealESRGAN_x4plus')
|
||||||
|
realesrgan_btn = gr.Button("Generate")
|
||||||
|
with gr.Column():
|
||||||
|
realesrgan_output = gr.Image(label="Output")
|
||||||
|
realesrgan_btn.click(
|
||||||
|
run_RealESRGAN,
|
||||||
|
[realesrgan_source, realesrgan_model_name],
|
||||||
|
[realesrgan_output]
|
||||||
|
)
|
||||||
|
output_txt2img_to_upscale_esrgan.click(
|
||||||
|
uifn.copy_img_to_upscale_esrgan,
|
||||||
|
output_txt2img_gallery,
|
||||||
|
[realesrgan_source, tabs],
|
||||||
|
_js=js_return_selected_txt2img)
|
||||||
|
|
||||||
|
gr.HTML("""
|
||||||
|
<div id="90" style="max-width: 100%; font-size: 14px; text-align: center;" class="output-markdown gr-prose border-solid border border-gray-200 rounded gr-panel">
|
||||||
|
<p>For help and advanced usage guides, visit the <a href="https://github.com/hlky/stable-diffusion-webui/wiki" target="_blank">Project Wiki</a></p>
|
||||||
|
<p>Stable Diffusion WebUI is an open-source project. You can find the latest stable builds on the <a href="https://github.com/hlky/stable-diffusion" target="_blank">main repository</a>.
|
||||||
|
If you would like to contribute to developement or test bleeding edge builds, you can visit the <a href="https://github.com/hlky/stable-diffusion-webui" target="_blank">developement repository</a>.</p>
|
||||||
|
</div>
|
||||||
|
""")
|
||||||
|
return demo
|
115
frontend/ui_functions.py
Normal file
115
frontend/ui_functions.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import re
|
||||||
|
import gradio as gr
|
||||||
|
from PIL import Image, ImageFont, ImageDraw, ImageFilter, ImageOps
|
||||||
|
from io import BytesIO
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def change_image_editor_mode(choice, cropped_image, resize_mode, width, height):
|
||||||
|
if choice == "Mask":
|
||||||
|
return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True)]
|
||||||
|
return [gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]
|
||||||
|
|
||||||
|
def update_image_mask(cropped_image, resize_mode, width, height):
|
||||||
|
resized_cropped_image = resize_image(resize_mode, cropped_image, width, height) if cropped_image else None
|
||||||
|
return gr.update(value=resized_cropped_image)
|
||||||
|
|
||||||
|
def copy_img_to_input(img):
|
||||||
|
try:
|
||||||
|
image_data = re.sub('^data:image/.+;base64,', '', img)
|
||||||
|
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
||||||
|
tab_update = gr.update(selected='img2img_tab')
|
||||||
|
img_update = gr.update(value=processed_image)
|
||||||
|
return processed_image, processed_image , tab_update
|
||||||
|
except IndexError:
|
||||||
|
return [None, None]
|
||||||
|
|
||||||
|
def copy_img_to_edit(img):
|
||||||
|
try:
|
||||||
|
image_data = re.sub('^data:image/.+;base64,', '', img)
|
||||||
|
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
||||||
|
tab_update = gr.update(selected='img2img_tab')
|
||||||
|
img_update = gr.update(value=processed_image)
|
||||||
|
mode_update = gr.update(value='Crop')
|
||||||
|
return processed_image, tab_update, mode_update
|
||||||
|
except IndexError:
|
||||||
|
return [None, None]
|
||||||
|
|
||||||
|
def copy_img_to_mask(img):
|
||||||
|
try:
|
||||||
|
image_data = re.sub('^data:image/.+;base64,', '', img)
|
||||||
|
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
||||||
|
tab_update = gr.update(selected='img2img_tab')
|
||||||
|
img_update = gr.update(value=processed_image)
|
||||||
|
mode_update = gr.update(value='Mask')
|
||||||
|
return processed_image, tab_update, mode_update
|
||||||
|
except IndexError:
|
||||||
|
return [None, None]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def copy_img_to_upscale_esrgan(img):
|
||||||
|
tabs_update = gr.update(selected='realesrgan_tab')
|
||||||
|
image_data = re.sub('^data:image/.+;base64,', '', img)
|
||||||
|
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
||||||
|
return processed_image, tabs_update
|
||||||
|
|
||||||
|
|
||||||
|
help_text = """
|
||||||
|
## Mask/Crop
|
||||||
|
* The masking/cropping is very temperamental.
|
||||||
|
* It may take some time for the image to show when switching from Crop to Mask.
|
||||||
|
* If the image doesn't appear after switching to Mask, switch back to Crop and then back again to Mask
|
||||||
|
* If the mask appears distorted (the brush is weirdly shaped instead of round), switch back to Crop and then back again to Mask.
|
||||||
|
|
||||||
|
## Advanced Editor
|
||||||
|
* For now the button needs to be clicked twice the first time.
|
||||||
|
* Once you have edited your image, you _need_ to click the save button for the next step to work.
|
||||||
|
* Clear the image from the crop editor (click the x)
|
||||||
|
* Click "Get Image from Advanced Editor" to get the image you saved. If it doesn't work, try opening the editor and saving again.
|
||||||
|
|
||||||
|
If it keeps not working, try switching modes again, switch tabs, clear the image or reload.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def show_help():
|
||||||
|
return [gr.update(visible=False), gr.update(visible=True), gr.update(value=help_text)]
|
||||||
|
|
||||||
|
def hide_help():
|
||||||
|
return [gr.update(visible=True), gr.update(visible=False), gr.update(value="")]
|
||||||
|
|
||||||
|
def resize_image(resize_mode, im, width, height):
|
||||||
|
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS)
|
||||||
|
if resize_mode == 0:
|
||||||
|
res = im.resize((width, height), resample=LANCZOS)
|
||||||
|
elif resize_mode == 1:
|
||||||
|
ratio = width / height
|
||||||
|
src_ratio = im.width / im.height
|
||||||
|
|
||||||
|
src_w = width if ratio > src_ratio else im.width * height // im.height
|
||||||
|
src_h = height if ratio <= src_ratio else im.height * width // im.width
|
||||||
|
|
||||||
|
resized = im.resize((src_w, src_h), resample=LANCZOS)
|
||||||
|
res = Image.new("RGB", (width, height))
|
||||||
|
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
|
||||||
|
else:
|
||||||
|
ratio = width / height
|
||||||
|
src_ratio = im.width / im.height
|
||||||
|
|
||||||
|
src_w = width if ratio < src_ratio else im.width * height // im.height
|
||||||
|
src_h = height if ratio >= src_ratio else im.height * width // im.width
|
||||||
|
|
||||||
|
resized = im.resize((src_w, src_h), resample=LANCZOS)
|
||||||
|
res = Image.new("RGB", (width, height))
|
||||||
|
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
|
||||||
|
|
||||||
|
if ratio < src_ratio:
|
||||||
|
fill_height = height // 2 - src_h // 2
|
||||||
|
res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0))
|
||||||
|
res.paste(resized.resize((width, fill_height), box=(0, resized.height, width, resized.height)), box=(0, fill_height + src_h))
|
||||||
|
elif ratio > src_ratio:
|
||||||
|
fill_width = width // 2 - src_w // 2
|
||||||
|
res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0))
|
||||||
|
res.paste(resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)), box=(fill_width + src_w, 0))
|
||||||
|
|
||||||
|
return res
|
326
webui.py
326
webui.py
@ -1,5 +1,7 @@
|
|||||||
import argparse, os, sys, glob, re
|
import argparse, os, sys, glob, re
|
||||||
|
|
||||||
|
from frontend.frontend import draw_gradio_ui
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--outdir", type=str, nargs="?", help="dir to write results to", default=None)
|
parser.add_argument("--outdir", type=str, nargs="?", help="dir to write results to", default=None)
|
||||||
parser.add_argument("--outdir_txt2img", type=str, nargs="?", help="dir to write txt2img results to (overrides --outdir)", default=None)
|
parser.add_argument("--outdir_txt2img", type=str, nargs="?", help="dir to write txt2img results to (overrides --outdir)", default=None)
|
||||||
@ -471,40 +473,6 @@ def draw_prompt_matrix(im, width, height, all_prompts):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def resize_image(resize_mode, im, width, height):
|
|
||||||
if resize_mode == 0:
|
|
||||||
res = im.resize((width, height), resample=LANCZOS)
|
|
||||||
elif resize_mode == 1:
|
|
||||||
ratio = width / height
|
|
||||||
src_ratio = im.width / im.height
|
|
||||||
|
|
||||||
src_w = width if ratio > src_ratio else im.width * height // im.height
|
|
||||||
src_h = height if ratio <= src_ratio else im.height * width // im.width
|
|
||||||
|
|
||||||
resized = im.resize((src_w, src_h), resample=LANCZOS)
|
|
||||||
res = Image.new("RGB", (width, height))
|
|
||||||
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
|
|
||||||
else:
|
|
||||||
ratio = width / height
|
|
||||||
src_ratio = im.width / im.height
|
|
||||||
|
|
||||||
src_w = width if ratio < src_ratio else im.width * height // im.height
|
|
||||||
src_h = height if ratio >= src_ratio else im.height * width // im.width
|
|
||||||
|
|
||||||
resized = im.resize((src_w, src_h), resample=LANCZOS)
|
|
||||||
res = Image.new("RGB", (width, height))
|
|
||||||
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
|
|
||||||
|
|
||||||
if ratio < src_ratio:
|
|
||||||
fill_height = height // 2 - src_h // 2
|
|
||||||
res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0))
|
|
||||||
res.paste(resized.resize((width, fill_height), box=(0, resized.height, width, resized.height)), box=(0, fill_height + src_h))
|
|
||||||
elif ratio > src_ratio:
|
|
||||||
fill_width = width // 2 - src_w // 2
|
|
||||||
res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0))
|
|
||||||
res.paste(resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)), box=(fill_width + src_w, 0))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def check_prompt_length(prompt, comments):
|
def check_prompt_length(prompt, comments):
|
||||||
@ -1463,295 +1431,7 @@ def hide_help():
|
|||||||
return [gr.update(visible=True), gr.update(visible=False), gr.update(value="")]
|
return [gr.update(visible=True), gr.update(visible=False), gr.update(value="")]
|
||||||
|
|
||||||
|
|
||||||
css_hide_progressbar = """
|
demo = draw_gradio_ui(opt, txt2img_defaults=txt2img_defaults)
|
||||||
.wrap .m-12 svg { display:none!important; }
|
|
||||||
.wrap .m-12::before { content:"Loading..." }
|
|
||||||
.progress-bar { display:none!important; }
|
|
||||||
.meta-text { display:none!important; }
|
|
||||||
"""
|
|
||||||
|
|
||||||
styling = """
|
|
||||||
[data-testid="image"] {min-height: 512px !important}
|
|
||||||
* #body>.col:nth-child(2){width:250%;max-width:89vw}
|
|
||||||
#generate{width: 100%; }
|
|
||||||
#prompt_row input{
|
|
||||||
font-size:20px
|
|
||||||
}
|
|
||||||
input[type=number]:disabled { -moz-appearance: textfield;+ }
|
|
||||||
"""
|
|
||||||
|
|
||||||
css = styling if opt.no_progressbar_hiding else styling + css_hide_progressbar
|
|
||||||
# This is the code that finds which selected item the user has in the gallery
|
|
||||||
js_part_getindex_txt2img="""
|
|
||||||
const root = document.querySelector('gradio-app').shadowRoot;
|
|
||||||
const getIndex = function(){
|
|
||||||
const selected = root.querySelector('#txt2img_gallery_output .\\\\!ring-2');
|
|
||||||
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;
|
|
||||||
};"""
|
|
||||||
js_part_getindex_img2img="""
|
|
||||||
const root = document.querySelector('gradio-app').shadowRoot;
|
|
||||||
const getIndex = function(){
|
|
||||||
const selected = root.querySelector('#img2img_gallery_output .\\\\!ring-2');
|
|
||||||
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;
|
|
||||||
};"""
|
|
||||||
js_part_clear_img2img="""
|
|
||||||
root.querySelector('#img2img_editor .modify-upload button:last-child')?.click();
|
|
||||||
root.querySelector('#img2img_editor')?.parentNode.querySelector('input[type="radio"][value="Mask"]')?.click();
|
|
||||||
root.querySelector('#img2img_mask .modify-upload button:last-child')?.click();
|
|
||||||
"""
|
|
||||||
js_return_selected_txt2img = "(x) => {" + js_part_getindex_txt2img + js_part_clear_img2img + """
|
|
||||||
return [x[getIndex()].replace('data:;','data:image/png;')];
|
|
||||||
}"""
|
|
||||||
js_return_selected_img2img = "(x) => {" + js_part_getindex_img2img + js_part_clear_img2img + """
|
|
||||||
return [x[getIndex()].replace('data:;','data:image/png;')];
|
|
||||||
}"""
|
|
||||||
js_part_copy_to_clipboard="""
|
|
||||||
const data = x[getIndex()];
|
|
||||||
const blob = await (await fetch(data.replace('data:;','data:image/png;'))).blob();
|
|
||||||
const item = new ClipboardItem({'image/png': blob});
|
|
||||||
navigator.clipboard.write([item]);
|
|
||||||
return x;
|
|
||||||
}"""
|
|
||||||
js_copy_selected_txt2img = "async (x) => {" + js_part_getindex_txt2img + js_part_copy_to_clipboard
|
|
||||||
js_copy_selected_img2img = "async (x) => {" + js_part_getindex_img2img + js_part_copy_to_clipboard
|
|
||||||
|
|
||||||
with gr.Blocks(css=css, 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.Row(elem_id="prompt_row"):
|
|
||||||
txt2img_prompt = gr.Textbox(label="Prompt",
|
|
||||||
elem_id='prompt_input',
|
|
||||||
placeholder="A corgi wearing a top hat as an oil painting.",
|
|
||||||
lines=1,
|
|
||||||
max_lines=1 if txt2img_defaults['submit_on_enter'] == 'Yes' else 25,
|
|
||||||
value=txt2img_defaults['prompt'],
|
|
||||||
show_label=False)
|
|
||||||
|
|
||||||
with gr.Row(elem_id='body').style(equal_height=False):
|
|
||||||
with gr.Column():
|
|
||||||
txt2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=txt2img_defaults["height"])
|
|
||||||
txt2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=txt2img_defaults["width"])
|
|
||||||
txt2img_cfg = gr.Slider(minimum=1.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'])
|
|
||||||
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)', 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'])
|
|
||||||
with gr.Column():
|
|
||||||
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, inputs=output_txt2img_gallery, outputs=[], _js=js_copy_selected_txt2img)
|
|
||||||
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.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=[], _js='(x) => navigator.clipboard.writeText(x)', 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_btn = gr.Button("Generate", elem_id="generate", variant="primary")
|
|
||||||
txt2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=txt2img_defaults['ddim_steps'])
|
|
||||||
txt2img_sampling = gr.Dropdown(label='Sampling method (k_lms is default k-diffusion sampler)', choices=["DDIM", "PLMS", 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler', 'k_heun', 'k_lms'], value=txt2img_defaults['sampler_name'])
|
|
||||||
with gr.Tabs():
|
|
||||||
with gr.TabItem('Simple'):
|
|
||||||
txt2img_submit_on_enter = gr.Radio(['Yes', 'No'], label="Submit on enter? (no means multiline)", value=txt2img_defaults['submit_on_enter'], interactive=True)
|
|
||||||
txt2img_submit_on_enter.change(lambda x: gr.update(max_lines=1 if x == 'Single' 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_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus', visible=RealESRGAN is not None) # TODO: Feels like I shouldnt slot it in here.
|
|
||||||
txt2img_ddim_eta = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="DDIM ETA", value=txt2img_defaults['ddim_eta'], visible=False)
|
|
||||||
txt2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager"))
|
|
||||||
|
|
||||||
txt2img_btn.click(
|
|
||||||
txt2img,
|
|
||||||
[txt2img_prompt, txt2img_steps, txt2img_sampling, txt2img_toggles, txt2img_realesrgan_model_name, txt2img_ddim_eta, txt2img_batch_count, txt2img_batch_size, txt2img_cfg, txt2img_seed, txt2img_height, txt2img_width, txt2img_embeddings],
|
|
||||||
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
|
|
||||||
)
|
|
||||||
txt2img_prompt.submit(
|
|
||||||
txt2img,
|
|
||||||
[txt2img_prompt, txt2img_steps, txt2img_sampling, txt2img_toggles, txt2img_realesrgan_model_name, txt2img_ddim_eta, txt2img_batch_count, txt2img_batch_size, txt2img_cfg, txt2img_seed, txt2img_height, txt2img_width, txt2img_embeddings],
|
|
||||||
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
|
|
||||||
)
|
|
||||||
|
|
||||||
with gr.TabItem("Stable Diffusion 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',
|
|
||||||
placeholder="A fantasy landscape, trending on artstation.",
|
|
||||||
lines=1,
|
|
||||||
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_editot_btn")
|
|
||||||
with gr.Row().style(equal_height=False):
|
|
||||||
with gr.Column():
|
|
||||||
img2img_image_editor_mode = gr.Radio(choices=["Mask", "Crop"], label="Image Editor Mode", value="Crop")
|
|
||||||
img2img_show_help_btn = gr.Button("Show Hints")
|
|
||||||
img2img_hide_help_btn = gr.Button("Hide Hints", visible=False)
|
|
||||||
img2img_help = gr.Markdown(visible=False, value="")
|
|
||||||
with gr.Row():
|
|
||||||
img2img_painterro_btn = gr.Button("Advanced Editor")
|
|
||||||
img2img_copy_from_painterro_btn = gr.Button(value="Get Image from Advanced Editor")
|
|
||||||
img2img_image_editor = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="select", elem_id="img2img_editor")
|
|
||||||
img2img_image_mask = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="sketch", visible=False, elem_id="img2img_mask")
|
|
||||||
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)
|
|
||||||
img2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=img2img_defaults['ddim_steps'])
|
|
||||||
img2img_sampling = gr.Dropdown(label='Sampling method (k_lms is default k-diffusion sampler)', choices=["DDIM", 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler', 'k_heun', 'k_lms'], value=img2img_defaults['sampler_name'])
|
|
||||||
img2img_toggles = gr.CheckboxGroup(label='', choices=img2img_toggles, value=img2img_toggle_defaults, type="index")
|
|
||||||
img2img_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus', visible=RealESRGAN is not None) # TODO: Feels like I shouldnt slot it in here.
|
|
||||||
img2img_batch_count = gr.Slider(minimum=1, maximum=250, step=1, label='Batch count (how many batches of images to generate)', value=img2img_defaults['n_iter'])
|
|
||||||
img2img_batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size (how many images are in a batch; memory-hungry)', value=img2img_defaults['batch_size'])
|
|
||||||
img2img_cfg = gr.Slider(minimum=1.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'])
|
|
||||||
img2img_denoising = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising Strength', value=img2img_defaults['denoising_strength'])
|
|
||||||
img2img_seed = gr.Textbox(label="Seed (blank to randomize)", lines=1, value=img2img_defaults["seed"])
|
|
||||||
img2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=img2img_defaults["height"])
|
|
||||||
img2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=img2img_defaults["width"])
|
|
||||||
img2img_resize = gr.Radio(label="Resize mode", choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value=img2img_resize_modes[img2img_defaults['resize_mode']])
|
|
||||||
img2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager"))
|
|
||||||
|
|
||||||
with gr.Column():
|
|
||||||
output_img2img_gallery = gr.Gallery(label="Images", elem_id="img2img_gallery_output")
|
|
||||||
with gr.Tabs():
|
|
||||||
with gr.TabItem("Generated image actions", id="img2img_actions_tab"):
|
|
||||||
gr.Markdown("Select an image, then press one of the buttons below")
|
|
||||||
output_img2img_copy_to_clipboard_btn = gr.Button("Copy to clipboard")
|
|
||||||
output_img2img_copy_to_input_btn = gr.Button("Push to img2img input")
|
|
||||||
gr.Markdown("Warning: This will clear your current image and mask settings!")
|
|
||||||
with gr.TabItem("Output info", id="img2img_output_info_tab"):
|
|
||||||
output_img2img_params = gr.Textbox(label="Generation parameters")
|
|
||||||
with gr.Row():
|
|
||||||
output_img2img_copy_params = gr.Button("Copy full parameters").click(inputs=output_img2img_params, outputs=[], _js='(x) => navigator.clipboard.writeText(x)', 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=[], _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
|
|
||||||
output_img2img_stats = gr.HTML(label='Stats')
|
|
||||||
|
|
||||||
img2img_image_editor_mode.change(
|
|
||||||
change_image_editor_mode,
|
|
||||||
[img2img_image_editor_mode, img2img_image_editor, img2img_resize, img2img_width, img2img_height],
|
|
||||||
[img2img_image_editor, img2img_image_mask, img2img_btn_editor, img2img_btn_mask, img2img_painterro_btn, img2img_copy_from_painterro_btn, img2img_mask, img2img_mask_blur_strength]
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_image_editor.edit(
|
|
||||||
update_image_mask,
|
|
||||||
[img2img_image_editor, img2img_resize, img2img_width, img2img_height],
|
|
||||||
img2img_image_mask
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_show_help_btn.click(
|
|
||||||
show_help,
|
|
||||||
None,
|
|
||||||
[img2img_show_help_btn, img2img_hide_help_btn, img2img_help]
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_hide_help_btn.click(
|
|
||||||
hide_help,
|
|
||||||
None,
|
|
||||||
[img2img_show_help_btn, img2img_hide_help_btn, img2img_help]
|
|
||||||
)
|
|
||||||
|
|
||||||
output_txt2img_copy_to_input_btn.click(
|
|
||||||
copy_img_to_input,
|
|
||||||
[output_txt2img_gallery],
|
|
||||||
[img2img_image_editor, img2img_image_mask, tabs],
|
|
||||||
_js=js_return_selected_txt2img
|
|
||||||
)
|
|
||||||
|
|
||||||
output_img2img_copy_to_input_btn.click(
|
|
||||||
copy_img_to_input,
|
|
||||||
[output_img2img_gallery],
|
|
||||||
[img2img_image_editor, img2img_image_mask, tabs],
|
|
||||||
_js=js_return_selected_img2img
|
|
||||||
)
|
|
||||||
|
|
||||||
output_img2img_copy_to_clipboard_btn.click(fn=None, inputs=output_img2img_gallery, outputs=[], _js=js_copy_selected_img2img)
|
|
||||||
|
|
||||||
img2img_btn_mask.click(
|
|
||||||
img2img,
|
|
||||||
[img2img_prompt, img2img_image_editor_mode, img2img_image_mask, img2img_mask, img2img_mask_blur_strength, img2img_steps, img2img_sampling, img2img_toggles, img2img_realesrgan_model_name, img2img_batch_count, img2img_batch_size, img2img_cfg, 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,
|
|
||||||
[img2img_prompt, img2img_image_editor_mode, img2img_image_editor, img2img_mask, img2img_mask_blur_strength, img2img_steps, img2img_sampling, img2img_toggles, img2img_realesrgan_model_name, img2img_batch_count, img2img_batch_size, img2img_cfg, 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_painterro_btn.click(None, [img2img_image_editor], None, _js="""(img) => {
|
|
||||||
try {
|
|
||||||
Painterro({
|
|
||||||
hiddenTools: ['arrow'],
|
|
||||||
saveHandler: function (image, done) {
|
|
||||||
localStorage.setItem('painterro-image', image.asDataURL());
|
|
||||||
done(true);
|
|
||||||
},
|
|
||||||
}).show(Array.isArray(img) ? img[0] : img);
|
|
||||||
} catch(e) {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = 'https://unpkg.com/painterro@1.2.78/build/painterro.min.js';
|
|
||||||
document.head.appendChild(script);
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.appendChild(document.createTextNode('.ptro-holder-wrapper { z-index: 9999 !important; }'));
|
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}""")
|
|
||||||
|
|
||||||
img2img_copy_from_painterro_btn.click(None, None, [img2img_image_editor, img2img_image_mask], _js="""() => {
|
|
||||||
const image = localStorage.getItem('painterro-image')
|
|
||||||
return [image, image];
|
|
||||||
}""")
|
|
||||||
|
|
||||||
if GFPGAN is not None:
|
|
||||||
gfpgan_defaults = {
|
|
||||||
'strength': 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'gfpgan' in user_defaults:
|
|
||||||
gfpgan_defaults.update(user_defaults['gfpgan'])
|
|
||||||
|
|
||||||
with gr.TabItem("GFPGAN", id='cfpgan_tab'):
|
|
||||||
gr.Markdown("Fix faces on images")
|
|
||||||
with gr.Row():
|
|
||||||
with gr.Column():
|
|
||||||
gfpgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
|
||||||
gfpgan_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Effect strength", value=gfpgan_defaults['strength'])
|
|
||||||
gfpgan_btn = gr.Button("Generate", variant="primary")
|
|
||||||
with gr.Column():
|
|
||||||
gfpgan_output = gr.Image(label="Output")
|
|
||||||
gfpgan_btn.click(
|
|
||||||
run_GFPGAN,
|
|
||||||
[gfpgan_source, gfpgan_strength],
|
|
||||||
[gfpgan_output]
|
|
||||||
)
|
|
||||||
if RealESRGAN is not None:
|
|
||||||
with gr.TabItem("RealESRGAN", id='realesrgan_tab'):
|
|
||||||
gr.Markdown("Upscale images")
|
|
||||||
with gr.Row():
|
|
||||||
with gr.Column():
|
|
||||||
realesrgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
|
||||||
realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus')
|
|
||||||
realesrgan_btn = gr.Button("Generate")
|
|
||||||
with gr.Column():
|
|
||||||
realesrgan_output = gr.Image(label="Output")
|
|
||||||
realesrgan_btn.click(
|
|
||||||
run_RealESRGAN,
|
|
||||||
[realesrgan_source, realesrgan_model_name],
|
|
||||||
[realesrgan_output]
|
|
||||||
)
|
|
||||||
gr.HTML("""
|
|
||||||
<div id="90" style="max-width: 100%; font-size: 14px; text-align: center;" class="output-markdown gr-prose border-solid border border-gray-200 rounded gr-panel">
|
|
||||||
<p>For help and advanced usage guides, visit the <a href="https://github.com/hlky/stable-diffusion-webui/wiki" target="_blank">Project Wiki</a></p>
|
|
||||||
<p>Stable Diffusion WebUI is an open-source project. You can find the latest stable builds on the <a href="https://github.com/hlky/stable-diffusion" target="_blank">main repository</a>.
|
|
||||||
If you would like to contribute to developement or test bleeding edge builds, you can visit the <a href="https://github.com/hlky/stable-diffusion-webui" target="_blank">developement repository</a>.</p>
|
|
||||||
</div>
|
|
||||||
""")
|
|
||||||
|
|
||||||
class ServerLauncher(threading.Thread):
|
class ServerLauncher(threading.Thread):
|
||||||
def __init__(self, demo):
|
def __init__(self, demo):
|
||||||
|
@ -6,6 +6,8 @@ from io import BytesIO
|
|||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from frontend.frontend import draw_gradio_ui
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This file is here to play around with the interface without loading the whole model
|
This file is here to play around with the interface without loading the whole model
|
||||||
|
|
||||||
@ -161,60 +163,6 @@ if 'img2img' in user_defaults:
|
|||||||
img2img_toggle_defaults = [img2img_toggles[i] for i in img2img_defaults['toggles']]
|
img2img_toggle_defaults = [img2img_toggles[i] for i in img2img_defaults['toggles']]
|
||||||
img2img_image_mode = 'sketch'
|
img2img_image_mode = 'sketch'
|
||||||
|
|
||||||
def change_image_editor_mode(choice, cropped_image, resize_mode, width, height):
|
|
||||||
if choice == "Mask":
|
|
||||||
return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True)]
|
|
||||||
return [gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]
|
|
||||||
|
|
||||||
def update_image_mask(cropped_image, resize_mode, width, height):
|
|
||||||
resized_cropped_image = resize_image(resize_mode, cropped_image, width, height) if cropped_image else None
|
|
||||||
return gr.update(value=resized_cropped_image)
|
|
||||||
|
|
||||||
def copy_img_to_input(img):
|
|
||||||
try:
|
|
||||||
image_data = re.sub('^data:image/.+;base64,', '', img)
|
|
||||||
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
|
||||||
tab_update = gr.update(selected='img2img_tab')
|
|
||||||
img_update = gr.update(value=processed_image)
|
|
||||||
return {img2img_image_mask: processed_image, img2img_image_editor: img_update, tabs: tab_update}
|
|
||||||
except IndexError:
|
|
||||||
return [None, None]
|
|
||||||
|
|
||||||
|
|
||||||
def copy_img_to_upscale_esrgan(img):
|
|
||||||
update = gr.update(selected='realesrgan_tab')
|
|
||||||
image_data = re.sub('^data:image/.+;base64,', '', img)
|
|
||||||
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
|
||||||
return {realesrgan_source: processed_image, tabs: update}
|
|
||||||
|
|
||||||
def copy_img_to_upscale_gobig(img):
|
|
||||||
update = gr.update(selected='gobig_tab')
|
|
||||||
image_data = re.sub('^data:image/.+;base64,', '', img)
|
|
||||||
processed_image = Image.open(BytesIO(base64.b64decode(image_data)))
|
|
||||||
return {realesrganGoBig_source: processed_image, tabs: update}
|
|
||||||
|
|
||||||
help_text = """
|
|
||||||
## Mask/Crop
|
|
||||||
* The masking/cropping is very temperamental.
|
|
||||||
* It may take some time for the image to show when switching from Crop to Mask.
|
|
||||||
* If the image doesn't appear after switching to Mask, switch back to Crop and then back again to Mask
|
|
||||||
* If the mask appears distorted (the brush is weirdly shaped instead of round), switch back to Crop and then back again to Mask.
|
|
||||||
|
|
||||||
## Advanced Editor
|
|
||||||
* For now the button needs to be clicked twice the first time.
|
|
||||||
* Once you have edited your image, you _need_ to click the save button for the next step to work.
|
|
||||||
* Clear the image from the crop editor (click the x)
|
|
||||||
* Click "Get Image from Advanced Editor" to get the image you saved. If it doesn't work, try opening the editor and saving again.
|
|
||||||
|
|
||||||
If it keeps not working, try switching modes again, switch tabs, clear the image or reload.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def show_help():
|
|
||||||
return [gr.update(visible=False), gr.update(visible=True), gr.update(value=help_text)]
|
|
||||||
|
|
||||||
def hide_help():
|
|
||||||
return [gr.update(visible=True), gr.update(visible=False), gr.update(value="")]
|
|
||||||
|
|
||||||
|
|
||||||
css_hide_progressbar = """
|
css_hide_progressbar = """
|
||||||
.wrap .m-12 svg { display:none!important; }
|
.wrap .m-12 svg { display:none!important; }
|
||||||
@ -233,283 +181,25 @@ styling = """
|
|||||||
input[type=number]:disabled { -moz-appearance: textfield;+ }
|
input[type=number]:disabled { -moz-appearance: textfield;+ }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
css = styling if opt.no_progressbar_hiding else styling + css_hide_progressbar
|
demo = draw_gradio_ui(opt,
|
||||||
# This is the code that finds which selected item the user has in the gallery
|
user_defaults=user_defaults,
|
||||||
js_part="""let getIndex = function(){
|
txt2img=txt2img,
|
||||||
let selected = document.querySelector('gradio-app').shadowRoot.querySelector('#gallery_output .\\\\!ring-2');
|
img2img=img2img,
|
||||||
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;
|
txt2img_defaults=txt2img_defaults,
|
||||||
};"""
|
txt2img_toggles=txt2img_toggles,
|
||||||
return_selected_img_js = "(x) => {" + js_part+ " document.querySelector('gradio-app').shadowRoot.querySelector('#img2img_editor .modify-upload button:last-child')?.click();return [x[getIndex()].replace('data:;','data:image/png;')]}"
|
txt2img_toggle_defaults=txt2img_toggle_defaults,
|
||||||
copy_selected_img_js = "async (x) => {" + js_part+ """
|
show_embeddings=hasattr(model, "embedding_manager"),
|
||||||
let data = x[getIndex()];
|
img2img_defaults=img2img_defaults,
|
||||||
const blob = await (await fetch(data.replace('data:;','data:image/png;'))).blob();
|
img2img_toggles=img2img_toggles,
|
||||||
let item = new ClipboardItem({'image/png': blob})
|
img2img_toggle_defaults=img2img_toggle_defaults,
|
||||||
navigator.clipboard.write([item]);
|
img2img_mask_modes=img2img_mask_modes,
|
||||||
return x
|
img2img_resize_modes=img2img_resize_modes,
|
||||||
}"""
|
sample_img2img=sample_img2img,
|
||||||
|
RealESRGAN=RealESRGAN,
|
||||||
|
GFPGAN=GFPGAN,
|
||||||
|
run_GFPGAN=run_GFPGAN,
|
||||||
|
run_RealESRGAN=run_RealESRGAN
|
||||||
|
)
|
||||||
|
|
||||||
with gr.Blocks(css=css, analytics_enabled=False, title="Stable Diffusion WebUI") as demo:
|
# demo.queue()
|
||||||
with gr.Tabs(elem_id='tabss') as tabs:
|
|
||||||
with gr.TabItem("Stable Diffusion Text-to-Image Unified", id='txt2img_tab'):
|
|
||||||
with gr.Row(elem_id="prompt_row"):
|
|
||||||
txt2img_prompt = gr.Textbox(label="Prompt",
|
|
||||||
elem_id='prompt_input',
|
|
||||||
placeholder="A corgi wearing a top hat as an oil painting.",
|
|
||||||
lines=1,
|
|
||||||
max_lines=1 if txt2img_defaults['submit_on_enter'] == 'Yes' else 25,
|
|
||||||
value=txt2img_defaults['prompt'],
|
|
||||||
show_label=False)
|
|
||||||
|
|
||||||
with gr.Row(elem_id='body').style(equal_height=False):
|
|
||||||
with gr.Column():
|
|
||||||
txt2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=txt2img_defaults["height"])
|
|
||||||
txt2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=txt2img_defaults["width"])
|
|
||||||
txt2img_cfg = gr.Slider(minimum=1.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'])
|
|
||||||
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)', 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'])
|
|
||||||
with gr.Column():
|
|
||||||
with gr.Group():
|
|
||||||
output_txt2img_gallery = gr.Gallery(label="Images", elem_id="gallery_output").style(grid=[4,4])
|
|
||||||
gr.Markdown('Selected image actions:')
|
|
||||||
output_txt2img_copy_clipboard = gr.Button("Copy to clipboard").click(fn=None, inputs=output_txt2img_gallery, outputs=[], _js=copy_selected_img_js)
|
|
||||||
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/ ESRCan")
|
|
||||||
output_txt2img_to_upscale_gobig = gr.Button("Upscale w/ GoBig")
|
|
||||||
|
|
||||||
with gr.Row():
|
|
||||||
with gr.Group():
|
|
||||||
output_txt2img_seed = gr.Number(label='Seed', interactive=False)
|
|
||||||
output_txt2img_copy_seed = gr.Button("Copy").click(inputs=output_txt2img_seed, outputs=[], _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
|
|
||||||
with gr.Group():
|
|
||||||
output_txt2img_select_image = gr.Number(label='Image # and click Copy to copy to img2img', value=1, precision=None)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
with gr.Group():
|
|
||||||
output_txt2img_params = gr.Textbox(label="Copy-paste generation parameters", interactive=False)
|
|
||||||
output_txt2img_copy_params = gr.Button("Copy").click(inputs=output_txt2img_params, outputs=[], _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
|
|
||||||
output_txt2img_stats = gr.HTML(label='Stats')
|
|
||||||
with gr.Column():
|
|
||||||
txt2img_btn = gr.Button("Generate", elem_id="generate", variant="primary")
|
|
||||||
txt2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=txt2img_defaults['ddim_steps'])
|
|
||||||
txt2img_sampling = gr.Dropdown(label='Sampling method (k_lms is default k-diffusion sampler)', choices=["DDIM", "PLMS", 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler', 'k_heun', 'k_lms'], value=txt2img_defaults['sampler_name'])
|
|
||||||
with gr.Tabs():
|
|
||||||
with gr.TabItem('Simple'):
|
|
||||||
txt2img_submit_on_enter = gr.Radio(['Yes', 'No'], label="Submit on enter? (no means multiline)", value=txt2img_defaults['submit_on_enter'], interactive=True)
|
|
||||||
txt2img_submit_on_enter.change(lambda x: gr.update(max_lines=1 if x == 'Single' 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_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus', visible=RealESRGAN is not None) # TODO: Feels like I shouldnt slot it in here.
|
|
||||||
txt2img_ddim_eta = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="DDIM ETA", value=txt2img_defaults['ddim_eta'], visible=False)
|
|
||||||
txt2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager"))
|
|
||||||
|
|
||||||
txt2img_btn.click(
|
|
||||||
txt2img,
|
|
||||||
[txt2img_prompt, txt2img_steps, txt2img_sampling, txt2img_toggles, txt2img_realesrgan_model_name, txt2img_ddim_eta, txt2img_batch_count, txt2img_batch_size, txt2img_cfg, txt2img_seed, txt2img_height, txt2img_width, txt2img_embeddings],
|
|
||||||
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
|
|
||||||
)
|
|
||||||
txt2img_prompt.submit(
|
|
||||||
txt2img,
|
|
||||||
[txt2img_prompt, txt2img_steps, txt2img_sampling, txt2img_toggles, txt2img_realesrgan_model_name, txt2img_ddim_eta, txt2img_batch_count, txt2img_batch_size, txt2img_cfg, txt2img_seed, txt2img_height, txt2img_width, txt2img_embeddings],
|
|
||||||
[output_txt2img_gallery, output_txt2img_seed, output_txt2img_params, output_txt2img_stats]
|
|
||||||
)
|
|
||||||
|
|
||||||
with gr.TabItem("Stable Diffusion 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',
|
|
||||||
placeholder="A fantasy landscape, trending on artstation.",
|
|
||||||
lines=1,
|
|
||||||
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_editot_btn")
|
|
||||||
with gr.Row().style(equal_height=False):
|
|
||||||
with gr.Column():
|
|
||||||
img2img_image_editor_mode = gr.Radio(choices=["Mask", "Crop"], label="Image Editor Mode", value="Crop")
|
|
||||||
img2img_show_help_btn = gr.Button("Show Hints")
|
|
||||||
img2img_hide_help_btn = gr.Button("Hide Hints", visible=False)
|
|
||||||
img2img_help = gr.Markdown(visible=False, value="")
|
|
||||||
with gr.Row():
|
|
||||||
img2img_painterro_btn = gr.Button("Advanced Editor")
|
|
||||||
img2img_copy_from_painterro_btn = gr.Button(value="Get Image from Advanced Editor")
|
|
||||||
img2img_image_editor = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="select", elem_id="img2img_editor")
|
|
||||||
img2img_image_mask = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="sketch", visible=False, elem_id="img2img_mask")
|
|
||||||
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)
|
|
||||||
img2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=img2img_defaults['ddim_steps'])
|
|
||||||
img2img_sampling = gr.Dropdown(label='Sampling method (k_lms is default k-diffusion sampler)', choices=["DDIM", 'k_dpm_2_a', 'k_dpm_2', 'k_euler_a', 'k_euler', 'k_heun', 'k_lms'], value=img2img_defaults['sampler_name'])
|
|
||||||
img2img_toggles = gr.CheckboxGroup(label='', choices=img2img_toggles, value=img2img_toggle_defaults, type="index")
|
|
||||||
img2img_realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus', visible=RealESRGAN is not None) # TODO: Feels like I shouldnt slot it in here.
|
|
||||||
img2img_batch_count = gr.Slider(minimum=1, maximum=250, step=1, label='Batch count (how many batches of images to generate)', value=img2img_defaults['n_iter'])
|
|
||||||
img2img_batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size (how many images are in a batch; memory-hungry)', value=img2img_defaults['batch_size'])
|
|
||||||
img2img_cfg = gr.Slider(minimum=1.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'])
|
|
||||||
img2img_denoising = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising Strength', value=img2img_defaults['denoising_strength'])
|
|
||||||
img2img_seed = gr.Textbox(label="Seed (blank to randomize)", lines=1, value=img2img_defaults["seed"])
|
|
||||||
img2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=img2img_defaults["height"])
|
|
||||||
img2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=img2img_defaults["width"])
|
|
||||||
img2img_resize = gr.Radio(label="Resize mode", choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value=img2img_resize_modes[img2img_defaults['resize_mode']])
|
|
||||||
img2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager"))
|
|
||||||
|
|
||||||
with gr.Column():
|
|
||||||
with gr.Group():
|
|
||||||
output_img2img_gallery = gr.Gallery(label="Generated Images", elem_id="gallery_output").style(grid=[4,4])
|
|
||||||
output_img2img_copy_to_input_btn = gr.Button("⬅️ Copy selected image to input")
|
|
||||||
if RealESRGAN is not None:
|
|
||||||
output_txt2img_copy_to_gobig_input_btn = gr.Button("Upscale w/ goBig input")
|
|
||||||
gr.Markdown("Clear the input image before copying your output to your input. It may take some time to load the image.")
|
|
||||||
|
|
||||||
output_img2img_seed = gr.Number(label='Seed')
|
|
||||||
output_img2img_params = gr.Textbox(label="Copy-paste generation parameters")
|
|
||||||
output_img2img_stats = gr.HTML(label='Stats')
|
|
||||||
|
|
||||||
img2img_image_editor_mode.change(
|
|
||||||
change_image_editor_mode,
|
|
||||||
[img2img_image_editor_mode, img2img_image_editor, img2img_resize, img2img_width, img2img_height],
|
|
||||||
[img2img_image_editor, img2img_image_mask, img2img_btn_editor, img2img_btn_mask, img2img_painterro_btn, img2img_copy_from_painterro_btn, img2img_mask, img2img_mask_blur_strength]
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_image_editor.edit(
|
|
||||||
update_image_mask,
|
|
||||||
[img2img_image_editor, img2img_resize, img2img_width, img2img_height],
|
|
||||||
img2img_image_mask
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_show_help_btn.click(
|
|
||||||
show_help,
|
|
||||||
None,
|
|
||||||
[img2img_show_help_btn, img2img_hide_help_btn, img2img_help]
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_hide_help_btn.click(
|
|
||||||
hide_help,
|
|
||||||
None,
|
|
||||||
[img2img_show_help_btn, img2img_hide_help_btn, img2img_help]
|
|
||||||
)
|
|
||||||
|
|
||||||
output_img2img_copy_to_input_btn.click(
|
|
||||||
copy_img_to_input,
|
|
||||||
[output_img2img_gallery],
|
|
||||||
[img2img_image_editor, img2img_image_mask, tabs],
|
|
||||||
_js=return_selected_img_js
|
|
||||||
)
|
|
||||||
|
|
||||||
output_txt2img_copy_to_input_btn.click(
|
|
||||||
copy_img_to_input,
|
|
||||||
[output_txt2img_gallery],
|
|
||||||
[img2img_image_editor, img2img_image_mask, tabs],
|
|
||||||
_js=return_selected_img_js
|
|
||||||
)
|
|
||||||
|
|
||||||
img2img_btn_mask.click(
|
|
||||||
img2img,
|
|
||||||
[img2img_prompt, img2img_image_editor_mode, img2img_image_mask, img2img_mask, img2img_mask_blur_strength, img2img_steps, img2img_sampling, img2img_toggles, img2img_realesrgan_model_name, img2img_batch_count, img2img_batch_size, img2img_cfg, 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,
|
|
||||||
[img2img_prompt, img2img_image_editor_mode, img2img_image_editor, img2img_mask, img2img_mask_blur_strength, img2img_steps, img2img_sampling, img2img_toggles, img2img_realesrgan_model_name, img2img_batch_count, img2img_batch_size, img2img_cfg, 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_painterro_btn.click(None, [img2img_image_editor], None, _js="""(img) => {
|
|
||||||
try {
|
|
||||||
Painterro({
|
|
||||||
hiddenTools: ['arrow'],
|
|
||||||
saveHandler: function (image, done) {
|
|
||||||
localStorage.setItem('painterro-image', image.asDataURL());
|
|
||||||
done(true);
|
|
||||||
},
|
|
||||||
}).show(Array.isArray(img) ? img[0] : img);
|
|
||||||
} catch(e) {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = 'https://unpkg.com/painterro@1.2.78/build/painterro.min.js';
|
|
||||||
document.head.appendChild(script);
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.appendChild(document.createTextNode('.ptro-holder-wrapper { z-index: 9999 !important; }'));
|
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}""")
|
|
||||||
|
|
||||||
img2img_copy_from_painterro_btn.click(None, None, [img2img_image_editor, img2img_image_mask], _js="""() => {
|
|
||||||
const image = localStorage.getItem('painterro-image')
|
|
||||||
return [image, image];
|
|
||||||
}""")
|
|
||||||
|
|
||||||
if GFPGAN is not None:
|
|
||||||
gfpgan_defaults = {
|
|
||||||
'strength': 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'gfpgan' in user_defaults:
|
|
||||||
gfpgan_defaults.update(user_defaults['gfpgan'])
|
|
||||||
|
|
||||||
with gr.TabItem("GFPGAN", id='cfpgan_tab'):
|
|
||||||
gr.Markdown("Fix faces on images")
|
|
||||||
with gr.Row():
|
|
||||||
with gr.Column():
|
|
||||||
gfpgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
|
||||||
gfpgan_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Effect strength", value=gfpgan_defaults['strength'])
|
|
||||||
gfpgan_btn = gr.Button("Generate", variant="primary")
|
|
||||||
with gr.Column():
|
|
||||||
gfpgan_output = gr.Image(label="Output")
|
|
||||||
gfpgan_btn.click(
|
|
||||||
run_GFPGAN,
|
|
||||||
[gfpgan_source, gfpgan_strength],
|
|
||||||
[gfpgan_output]
|
|
||||||
)
|
|
||||||
if RealESRGAN is not None:
|
|
||||||
with gr.TabItem("RealESRGAN", id='realesrgan_tab'):
|
|
||||||
gr.Markdown("Upscale images")
|
|
||||||
with gr.Row():
|
|
||||||
with gr.Column():
|
|
||||||
realesrgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
|
||||||
realesrgan_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus')
|
|
||||||
realesrgan_btn = gr.Button("Generate")
|
|
||||||
with gr.Column():
|
|
||||||
realesrgan_output = gr.Image(label="Output")
|
|
||||||
realesrgan_btn.click(
|
|
||||||
run_RealESRGAN,
|
|
||||||
[realesrgan_source, realesrgan_model_name],
|
|
||||||
[realesrgan_output]
|
|
||||||
)
|
|
||||||
output_txt2img_to_upscale_esrgan.click(
|
|
||||||
copy_img_to_upscale_esrgan,
|
|
||||||
output_txt2img_gallery,
|
|
||||||
[realesrgan_source, tabs],
|
|
||||||
_js=return_selected_img_js)
|
|
||||||
with gr.TabItem("goBIG", id='gobig_tab'):
|
|
||||||
gr.Markdown("Upscale and detail images")
|
|
||||||
with gr.Row():
|
|
||||||
with gr.Column():
|
|
||||||
realesrganGoBig_source = gr.Image(source="upload", interactive=True, type="pil", tool="select")
|
|
||||||
realesrganGoBig_model_name = gr.Dropdown(label='RealESRGAN model', choices=['RealESRGAN_x4plus', 'RealESRGAN_x4plus_anime_6B'], value='RealESRGAN_x4plus')
|
|
||||||
realesrganGoBig_btn = gr.Button("Generate")
|
|
||||||
with gr.Column():
|
|
||||||
realesrganGoBig_output = gr.Image(label="Output")
|
|
||||||
realesrganGoBig_btn.click(
|
|
||||||
run_goBIG,
|
|
||||||
[realesrganGoBig_source, realesrganGoBig_model_name],
|
|
||||||
[realesrganGoBig_output]
|
|
||||||
)
|
|
||||||
output_txt2img_to_upscale_gobig.click(
|
|
||||||
copy_img_to_upscale_gobig,
|
|
||||||
output_txt2img_gallery,
|
|
||||||
[realesrganGoBig_source, tabs],
|
|
||||||
_js=return_selected_img_js)
|
|
||||||
|
|
||||||
output_txt2img_copy_to_gobig_input_btn.click(
|
|
||||||
copy_img_to_upscale_gobig,
|
|
||||||
output_txt2img_gallery,
|
|
||||||
[realesrganGoBig_source, tabs],
|
|
||||||
_js=return_selected_img_js
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
demo.queue()
|
|
||||||
demo.launch(share=False, debug=True)
|
demo.launch(share=False, debug=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user