028fc25f28
add a setting for lightbox max size images use setting for lightbox max size images late to let user change it and see results
1020 lines
42 KiB
Python
1020 lines
42 KiB
Python
import base64
|
|
import html
|
|
import io
|
|
import json
|
|
import math
|
|
import mimetypes
|
|
import os
|
|
import random
|
|
import sys
|
|
import time
|
|
import traceback
|
|
|
|
import numpy as np
|
|
import torch
|
|
from PIL import Image
|
|
|
|
import gradio as gr
|
|
import gradio.utils
|
|
import gradio.routes
|
|
|
|
from modules.paths import script_path
|
|
from modules.shared import opts, cmd_opts
|
|
import modules.shared as shared
|
|
from modules.sd_samplers import samplers, samplers_for_img2img
|
|
import modules.realesrgan_model as realesrgan
|
|
import modules.scripts
|
|
import modules.gfpgan_model
|
|
import modules.codeformer_model
|
|
import modules.styles
|
|
|
|
# this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the bowser will not show any UI
|
|
mimetypes.init()
|
|
mimetypes.add_type('application/javascript', '.js')
|
|
|
|
|
|
if not cmd_opts.share and not cmd_opts.listen:
|
|
# fix gradio phoning home
|
|
gradio.utils.version_check = lambda: None
|
|
gradio.utils.get_local_ip_address = lambda: '127.0.0.1'
|
|
|
|
|
|
def gr_show(visible=True):
|
|
return {"visible": visible, "__type__": "update"}
|
|
|
|
|
|
sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg"
|
|
sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None
|
|
|
|
css_hide_progressbar = """
|
|
.wrap .m-12 svg { display:none!important; }
|
|
.wrap .m-12::before { content:"Loading..." }
|
|
.progress-bar { display:none!important; }
|
|
.meta-text { display:none!important; }
|
|
"""
|
|
|
|
# Using constants for these since the variation selector isn't visible.
|
|
# Important that they exactly match script.js for tooltip to work.
|
|
random_symbol = '\U0001f3b2\ufe0f' # 🎲️
|
|
reuse_symbol = '\u267b\ufe0f' # ♻️
|
|
|
|
|
|
def plaintext_to_html(text):
|
|
text = "<p>" + "<br>\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "</p>"
|
|
return text
|
|
|
|
|
|
def image_from_url_text(filedata):
|
|
if type(filedata) == list:
|
|
if len(filedata) == 0:
|
|
return None
|
|
|
|
filedata = filedata[0]
|
|
|
|
if filedata.startswith("data:image/png;base64,"):
|
|
filedata = filedata[len("data:image/png;base64,"):]
|
|
|
|
filedata = base64.decodebytes(filedata.encode('utf-8'))
|
|
image = Image.open(io.BytesIO(filedata))
|
|
return image
|
|
|
|
|
|
def send_gradio_gallery_to_image(x):
|
|
if len(x) == 0:
|
|
return None
|
|
|
|
return image_from_url_text(x[0])
|
|
|
|
|
|
def save_files(js_data, images, index):
|
|
import csv
|
|
|
|
os.makedirs(opts.outdir_save, exist_ok=True)
|
|
|
|
filenames = []
|
|
|
|
data = json.loads(js_data)
|
|
|
|
if index > -1 and opts.save_selected_only and (index > 0 or not opts.return_grid): # ensures we are looking at a specific non-grid picture, and we have save_selected_only
|
|
images = [images[index]]
|
|
data["seed"] += (index - 1 if opts.return_grid else index)
|
|
|
|
with open(os.path.join(opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file:
|
|
at_start = file.tell() == 0
|
|
writer = csv.writer(file)
|
|
if at_start:
|
|
writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"])
|
|
|
|
filename_base = str(int(time.time() * 1000))
|
|
for i, filedata in enumerate(images):
|
|
filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + ".png"
|
|
filepath = os.path.join(opts.outdir_save, filename)
|
|
|
|
if filedata.startswith("data:image/png;base64,"):
|
|
filedata = filedata[len("data:image/png;base64,"):]
|
|
|
|
with open(filepath, "wb") as imgfile:
|
|
imgfile.write(base64.decodebytes(filedata.encode('utf-8')))
|
|
|
|
filenames.append(filename)
|
|
|
|
writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]])
|
|
|
|
return '', '', plaintext_to_html(f"Saved: {filenames[0]}")
|
|
|
|
|
|
def wrap_gradio_call(func):
|
|
def f(*args, **kwargs):
|
|
run_memmon = opts.memmon_poll_rate > 0 and not shared.mem_mon.disabled
|
|
if run_memmon:
|
|
shared.mem_mon.monitor()
|
|
t = time.perf_counter()
|
|
|
|
try:
|
|
res = list(func(*args, **kwargs))
|
|
except Exception as e:
|
|
print("Error completing request", file=sys.stderr)
|
|
print("Arguments:", args, kwargs, file=sys.stderr)
|
|
print(traceback.format_exc(), file=sys.stderr)
|
|
|
|
shared.state.job = ""
|
|
shared.state.job_count = 0
|
|
|
|
res = [None, '', f"<div class='error'>{plaintext_to_html(type(e).__name__+': '+str(e))}</div>"]
|
|
|
|
elapsed = time.perf_counter() - t
|
|
|
|
if run_memmon:
|
|
mem_stats = {k: -(v//-(1024*1024)) for k, v in shared.mem_mon.stop().items()}
|
|
active_peak = mem_stats['active_peak']
|
|
reserved_peak = mem_stats['reserved_peak']
|
|
sys_peak = mem_stats['system_peak']
|
|
sys_total = mem_stats['total']
|
|
sys_pct = round(sys_peak/max(sys_total, 1) * 100, 2)
|
|
|
|
vram_html = f"<p class='vram'>Torch active/reserved: {active_peak}/{reserved_peak} MiB, <wbr>Sys VRAM: {sys_peak}/{sys_total} MiB ({sys_pct}%)</p>"
|
|
else:
|
|
vram_html = ''
|
|
|
|
# last item is always HTML
|
|
res[-1] += f"<div class='performance'><p class='time'>Time taken: <wbr>{elapsed:.2f}s</p>{vram_html}</div>"
|
|
|
|
shared.state.interrupted = False
|
|
|
|
return tuple(res)
|
|
|
|
return f
|
|
|
|
|
|
def check_progress_call():
|
|
if shared.state.job_count == 0:
|
|
return "", gr_show(False), gr_show(False)
|
|
|
|
progress = 0
|
|
|
|
if shared.state.job_count > 0:
|
|
progress += shared.state.job_no / shared.state.job_count
|
|
if shared.state.sampling_steps > 0:
|
|
progress += 1 / shared.state.job_count * shared.state.sampling_step / shared.state.sampling_steps
|
|
|
|
progress = min(progress, 1)
|
|
|
|
progressbar = ""
|
|
if opts.show_progressbar:
|
|
progressbar = f"""<div class='progressDiv'><div class='progress' style="width:{progress * 100}%">{str(int(progress*100))+"%" if progress > 0.01 else ""}</div></div>"""
|
|
|
|
image = gr_show(False)
|
|
preview_visibility = gr_show(False)
|
|
|
|
if opts.show_progress_every_n_steps > 0:
|
|
if shared.parallel_processing_allowed:
|
|
|
|
if shared.state.sampling_step - shared.state.current_image_sampling_step >= opts.show_progress_every_n_steps and shared.state.current_latent is not None:
|
|
shared.state.current_image = modules.sd_samplers.sample_to_image(shared.state.current_latent)
|
|
shared.state.current_image_sampling_step = shared.state.sampling_step
|
|
|
|
image = shared.state.current_image
|
|
|
|
if image is None or progress >= 1:
|
|
image = gr.update(value=None)
|
|
else:
|
|
preview_visibility = gr_show(True)
|
|
|
|
return f"<span style='display: none'>{time.time()}</span><p>{progressbar}</p>", preview_visibility, image
|
|
|
|
|
|
def check_progress_call_initial():
|
|
shared.state.job_count = -1
|
|
shared.state.current_latent = None
|
|
shared.state.current_image = None
|
|
|
|
return check_progress_call()
|
|
|
|
|
|
def roll_artist(prompt):
|
|
allowed_cats = set([x for x in shared.artist_db.categories() if len(opts.random_artist_categories)==0 or x in opts.random_artist_categories])
|
|
artist = random.choice([x for x in shared.artist_db.artists if x.category in allowed_cats])
|
|
|
|
return prompt + ", " + artist.name if prompt != '' else artist.name
|
|
|
|
|
|
def visit(x, func, path=""):
|
|
if hasattr(x, 'children'):
|
|
for c in x.children:
|
|
visit(c, func, path)
|
|
elif x.label is not None:
|
|
func(path + "/" + str(x.label), x)
|
|
|
|
|
|
def add_style(name: str, prompt: str, negative_prompt: str):
|
|
if name is None:
|
|
return [gr_show(), gr_show()]
|
|
|
|
style = modules.styles.PromptStyle(name, prompt, negative_prompt)
|
|
shared.prompt_styles.styles[style.name] = style
|
|
# Save all loaded prompt styles: this allows us to update the storage format in the future more easily, because we
|
|
# reserialize all styles every time we save them
|
|
shared.prompt_styles.save_styles(shared.styles_filename)
|
|
|
|
update = {"visible": True, "choices": list(shared.prompt_styles.styles), "__type__": "update"}
|
|
return [update, update, update, update]
|
|
|
|
|
|
def apply_styles(prompt, prompt_neg, style1_name, style2_name):
|
|
prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, [style1_name, style2_name])
|
|
prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, [style1_name, style2_name])
|
|
|
|
return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value="None"), gr.Dropdown.update(value="None")]
|
|
|
|
|
|
def interrogate(image):
|
|
prompt = shared.interrogator.interrogate(image)
|
|
|
|
return gr_show(True) if prompt is None else prompt
|
|
|
|
|
|
def create_seed_inputs():
|
|
with gr.Row():
|
|
with gr.Box():
|
|
with gr.Row(elem_id='seed_row'):
|
|
seed = gr.Number(label='Seed', value=-1)
|
|
seed.style(container=False)
|
|
random_seed = gr.Button(random_symbol, elem_id='random_seed')
|
|
reuse_seed = gr.Button(reuse_symbol, elem_id='reuse_seed')
|
|
|
|
with gr.Box(elem_id='subseed_show_box'):
|
|
seed_checkbox = gr.Checkbox(label='Extra', elem_id='subseed_show', value=False)
|
|
|
|
# Components to show/hide based on the 'Extra' checkbox
|
|
seed_extras = []
|
|
|
|
with gr.Row(visible=False) as seed_extra_row_1:
|
|
seed_extras.append(seed_extra_row_1)
|
|
with gr.Box():
|
|
with gr.Row(elem_id='subseed_row'):
|
|
subseed = gr.Number(label='Variation seed', value=-1)
|
|
subseed.style(container=False)
|
|
random_subseed = gr.Button(random_symbol, elem_id='random_subseed')
|
|
reuse_subseed = gr.Button(reuse_symbol, elem_id='reuse_subseed')
|
|
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01)
|
|
|
|
with gr.Row(visible=False) as seed_extra_row_2:
|
|
seed_extras.append(seed_extra_row_2)
|
|
seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from width", value=0)
|
|
seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from height", value=0)
|
|
|
|
random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed])
|
|
random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed])
|
|
|
|
def change_visibility(show):
|
|
return {comp: gr_show(show) for comp in seed_extras}
|
|
|
|
seed_checkbox.change(change_visibility, show_progress=False, inputs=[seed_checkbox], outputs=seed_extras)
|
|
|
|
return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w
|
|
|
|
|
|
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed):
|
|
""" Connects a 'reuse (sub)seed' button's click event so that it copies last used
|
|
(sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
|
|
was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
|
|
def copy_seed(gen_info_string: str, index):
|
|
res = -1
|
|
|
|
try:
|
|
gen_info = json.loads(gen_info_string)
|
|
index -= gen_info.get('index_of_first_image', 0)
|
|
|
|
if is_subseed and gen_info.get('subseed_strength', 0) > 0:
|
|
all_subseeds = gen_info.get('all_subseeds', [-1])
|
|
res = all_subseeds[index if 0 <= index < len(all_subseeds) else 0]
|
|
else:
|
|
all_seeds = gen_info.get('all_seeds', [-1])
|
|
res = all_seeds[index if 0 <= index < len(all_seeds) else 0]
|
|
|
|
except json.decoder.JSONDecodeError as e:
|
|
if gen_info_string != '':
|
|
print("Error parsing JSON generation info:", file=sys.stderr)
|
|
print(gen_info_string, file=sys.stderr)
|
|
|
|
return [res, gr_show(False)]
|
|
|
|
reuse_seed.click(
|
|
fn=copy_seed,
|
|
_js="(x, y) => [x, selected_gallery_index()]",
|
|
show_progress=False,
|
|
inputs=[generation_info, dummy_component],
|
|
outputs=[seed, dummy_component]
|
|
)
|
|
|
|
|
|
def create_toprow(is_img2img):
|
|
with gr.Row(elem_id="toprow"):
|
|
with gr.Column(scale=4):
|
|
with gr.Row():
|
|
with gr.Column(scale=8):
|
|
with gr.Row():
|
|
prompt = gr.Textbox(label="Prompt", elem_id="prompt", show_label=False, placeholder="Prompt", lines=2)
|
|
roll = gr.Button('Roll', elem_id="roll", visible=len(shared.artist_db.artists) > 0)
|
|
|
|
with gr.Column(scale=1, elem_id="style_pos_col"):
|
|
prompt_style = gr.Dropdown(label="Style 1", elem_id="style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
|
|
|
|
with gr.Row():
|
|
with gr.Column(scale=8):
|
|
negative_prompt = gr.Textbox(label="Negative prompt", elem_id="negative_prompt", show_label=False, placeholder="Negative prompt", lines=2)
|
|
|
|
with gr.Column(scale=1, elem_id="style_neg_col"):
|
|
prompt_style2 = gr.Dropdown(label="Style 2", elem_id="style2_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
|
|
|
|
with gr.Column(scale=1):
|
|
with gr.Row():
|
|
submit = gr.Button('Generate', elem_id="generate", variant='primary')
|
|
|
|
with gr.Row():
|
|
if is_img2img:
|
|
interrogate = gr.Button('Interrogate', elem_id="interrogate")
|
|
else:
|
|
interrogate = None
|
|
prompt_style_apply = gr.Button('Apply style', elem_id="style_apply")
|
|
save_style = gr.Button('Create style', elem_id="style_create")
|
|
|
|
return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style
|
|
|
|
|
|
def setup_progressbar(progressbar, preview):
|
|
check_progress = gr.Button('Check progress', elem_id="check_progress", visible=False)
|
|
check_progress.click(
|
|
fn=check_progress_call,
|
|
show_progress=False,
|
|
inputs=[],
|
|
outputs=[progressbar, preview, preview],
|
|
)
|
|
|
|
check_progress_initial = gr.Button('Check progress (first)', elem_id="check_progress_initial", visible=False)
|
|
check_progress_initial.click(
|
|
fn=check_progress_call_initial,
|
|
show_progress=False,
|
|
inputs=[],
|
|
outputs=[progressbar, preview, preview],
|
|
)
|
|
|
|
|
|
def create_ui(txt2img, img2img, run_extras, run_pnginfo):
|
|
with gr.Blocks(analytics_enabled=False) as txt2img_interface:
|
|
txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style = create_toprow(is_img2img=False)
|
|
dummy_component = gr.Label(visible=False)
|
|
|
|
with gr.Row().style(equal_height=False):
|
|
with gr.Column(variant='panel'):
|
|
steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
|
|
sampler_index = gr.Radio(label='Sampling method', elem_id="txt2img_sampling", choices=[x.name for x in samplers], value=samplers[0].name, type="index")
|
|
|
|
with gr.Row():
|
|
restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1)
|
|
tiling = gr.Checkbox(label='Tiling', value=False)
|
|
enable_hr = gr.Checkbox(label='Highres. fix', value=False)
|
|
|
|
with gr.Row(visible=False) as hr_options:
|
|
scale_latent = gr.Checkbox(label='Scale latent', value=True)
|
|
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7)
|
|
|
|
with gr.Row():
|
|
batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1)
|
|
batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1)
|
|
|
|
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0)
|
|
|
|
with gr.Group():
|
|
width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
|
|
height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)
|
|
|
|
seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs()
|
|
|
|
with gr.Group():
|
|
custom_inputs = modules.scripts.scripts_txt2img.setup_ui(is_img2img=False)
|
|
|
|
with gr.Column(variant='panel'):
|
|
progressbar = gr.HTML(elem_id="progressbar")
|
|
|
|
with gr.Group():
|
|
txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
|
|
txt2img_gallery = gr.Gallery(label='Output', elem_id='txt2img_gallery').style(grid=4)
|
|
|
|
setup_progressbar(progressbar, txt2img_preview)
|
|
|
|
with gr.Group():
|
|
with gr.Row():
|
|
save = gr.Button('Save')
|
|
send_to_img2img = gr.Button('Send to img2img')
|
|
send_to_inpaint = gr.Button('Send to inpaint')
|
|
send_to_extras = gr.Button('Send to extras')
|
|
interrupt = gr.Button('Interrupt')
|
|
|
|
with gr.Group():
|
|
html_info = gr.HTML()
|
|
generation_info = gr.Textbox(visible=False)
|
|
|
|
connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False)
|
|
connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True)
|
|
|
|
txt2img_args = dict(
|
|
fn=txt2img,
|
|
_js="submit",
|
|
inputs=[
|
|
txt2img_prompt,
|
|
txt2img_negative_prompt,
|
|
txt2img_prompt_style,
|
|
txt2img_prompt_style2,
|
|
steps,
|
|
sampler_index,
|
|
restore_faces,
|
|
tiling,
|
|
batch_count,
|
|
batch_size,
|
|
cfg_scale,
|
|
seed,
|
|
subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w,
|
|
height,
|
|
width,
|
|
enable_hr,
|
|
scale_latent,
|
|
denoising_strength,
|
|
] + custom_inputs,
|
|
outputs=[
|
|
txt2img_gallery,
|
|
generation_info,
|
|
html_info
|
|
],
|
|
show_progress=False,
|
|
)
|
|
|
|
txt2img_prompt.submit(**txt2img_args)
|
|
submit.click(**txt2img_args)
|
|
|
|
enable_hr.change(
|
|
fn=lambda x: gr_show(x),
|
|
inputs=[enable_hr],
|
|
outputs=[hr_options],
|
|
)
|
|
|
|
interrupt.click(
|
|
fn=lambda: shared.state.interrupt(),
|
|
inputs=[],
|
|
outputs=[],
|
|
)
|
|
|
|
save.click(
|
|
fn=wrap_gradio_call(save_files),
|
|
_js="(x, y, z) => [x, y, selected_gallery_index()]",
|
|
inputs=[
|
|
generation_info,
|
|
txt2img_gallery,
|
|
html_info,
|
|
],
|
|
outputs=[
|
|
html_info,
|
|
html_info,
|
|
html_info,
|
|
]
|
|
)
|
|
|
|
roll.click(
|
|
fn=roll_artist,
|
|
inputs=[
|
|
txt2img_prompt,
|
|
],
|
|
outputs=[
|
|
txt2img_prompt,
|
|
]
|
|
)
|
|
|
|
with gr.Blocks(analytics_enabled=False) as img2img_interface:
|
|
img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style = create_toprow(is_img2img=True)
|
|
|
|
with gr.Row().style(equal_height=False):
|
|
with gr.Column(variant='panel'):
|
|
with gr.Group():
|
|
switch_mode = gr.Radio(label='Mode', elem_id="img2img_mode", choices=['Redraw whole image', 'Inpaint a part of image', 'SD upscale'], value='Redraw whole image', type="index", show_label=False)
|
|
init_img = gr.Image(label="Image for img2img", source="upload", interactive=True, type="pil")
|
|
init_img_with_mask = gr.Image(label="Image for inpainting with mask", elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", visible=False, image_mode="RGBA")
|
|
init_mask = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False)
|
|
init_img_with_mask_comment = gr.HTML(elem_id="mask_bug_info", value="<small>if the editor shows ERROR, switch to another tab and back, then to another img2img mode above and back</small>", visible=False)
|
|
|
|
with gr.Row():
|
|
resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize")
|
|
mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask")
|
|
|
|
steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
|
|
sampler_index = gr.Radio(label='Sampling method', choices=[x.name for x in samplers_for_img2img], value=samplers_for_img2img[0].name, type="index")
|
|
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, visible=False)
|
|
inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", visible=False)
|
|
|
|
with gr.Row():
|
|
inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=False, visible=False)
|
|
inpainting_mask_invert = gr.Radio(label='Masking mode', choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index", visible=False)
|
|
|
|
with gr.Row():
|
|
restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1)
|
|
tiling = gr.Checkbox(label='Tiling', value=False)
|
|
sd_upscale_overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64, visible=False)
|
|
|
|
with gr.Row():
|
|
sd_upscale_upscaler_name = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index", visible=False)
|
|
|
|
with gr.Row():
|
|
batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1)
|
|
batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1)
|
|
|
|
with gr.Group():
|
|
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0)
|
|
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75)
|
|
|
|
with gr.Group():
|
|
width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
|
|
height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)
|
|
|
|
seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs()
|
|
|
|
with gr.Group():
|
|
custom_inputs = modules.scripts.scripts_img2img.setup_ui(is_img2img=True)
|
|
|
|
with gr.Column(variant='panel'):
|
|
progressbar = gr.HTML(elem_id="progressbar")
|
|
|
|
with gr.Group():
|
|
img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
|
|
img2img_gallery = gr.Gallery(label='Output', elem_id='img2img_gallery').style(grid=4)
|
|
|
|
setup_progressbar(progressbar, img2img_preview)
|
|
|
|
with gr.Group():
|
|
with gr.Row():
|
|
save = gr.Button('Save')
|
|
img2img_send_to_img2img = gr.Button('Send to img2img')
|
|
img2img_send_to_inpaint = gr.Button('Send to inpaint')
|
|
img2img_send_to_extras = gr.Button('Send to extras')
|
|
interrupt = gr.Button('Interrupt')
|
|
img2img_save_style = gr.Button('Save prompt as style')
|
|
|
|
|
|
with gr.Group():
|
|
html_info = gr.HTML()
|
|
generation_info = gr.Textbox(visible=False)
|
|
|
|
connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False)
|
|
connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True)
|
|
|
|
def apply_mode(mode, uploadmask):
|
|
is_classic = mode == 0
|
|
is_inpaint = mode == 1
|
|
is_upscale = mode == 2
|
|
|
|
return {
|
|
init_img: gr_show(not is_inpaint or (is_inpaint and uploadmask == 1)),
|
|
init_img_with_mask: gr_show(is_inpaint and uploadmask == 0),
|
|
init_img_with_mask_comment: gr_show(is_inpaint and uploadmask == 0),
|
|
init_mask: gr_show(is_inpaint and uploadmask == 1),
|
|
mask_mode: gr_show(is_inpaint),
|
|
mask_blur: gr_show(is_inpaint),
|
|
inpainting_fill: gr_show(is_inpaint),
|
|
sd_upscale_upscaler_name: gr_show(is_upscale),
|
|
sd_upscale_overlap: gr_show(is_upscale),
|
|
inpaint_full_res: gr_show(is_inpaint),
|
|
inpainting_mask_invert: gr_show(is_inpaint),
|
|
img2img_interrogate: gr_show(not is_inpaint),
|
|
}
|
|
|
|
switch_mode.change(
|
|
apply_mode,
|
|
inputs=[switch_mode, mask_mode],
|
|
outputs=[
|
|
init_img,
|
|
init_img_with_mask,
|
|
init_img_with_mask_comment,
|
|
init_mask,
|
|
mask_mode,
|
|
mask_blur,
|
|
inpainting_fill,
|
|
sd_upscale_upscaler_name,
|
|
sd_upscale_overlap,
|
|
inpaint_full_res,
|
|
inpainting_mask_invert,
|
|
img2img_interrogate,
|
|
]
|
|
)
|
|
|
|
mask_mode.change(
|
|
lambda mode: {
|
|
init_img: gr_show(mode == 1),
|
|
init_img_with_mask: gr_show(mode == 0),
|
|
init_mask: gr_show(mode == 1),
|
|
},
|
|
inputs=[mask_mode],
|
|
outputs=[
|
|
init_img,
|
|
init_img_with_mask,
|
|
init_mask,
|
|
],
|
|
)
|
|
|
|
img2img_args = dict(
|
|
fn=img2img,
|
|
_js="submit",
|
|
inputs=[
|
|
img2img_prompt,
|
|
img2img_negative_prompt,
|
|
img2img_prompt_style,
|
|
img2img_prompt_style2,
|
|
init_img,
|
|
init_img_with_mask,
|
|
init_mask,
|
|
mask_mode,
|
|
steps,
|
|
sampler_index,
|
|
mask_blur,
|
|
inpainting_fill,
|
|
restore_faces,
|
|
tiling,
|
|
switch_mode,
|
|
batch_count,
|
|
batch_size,
|
|
cfg_scale,
|
|
denoising_strength,
|
|
seed,
|
|
subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w,
|
|
height,
|
|
width,
|
|
resize_mode,
|
|
sd_upscale_upscaler_name,
|
|
sd_upscale_overlap,
|
|
inpaint_full_res,
|
|
inpainting_mask_invert,
|
|
] + custom_inputs,
|
|
outputs=[
|
|
img2img_gallery,
|
|
generation_info,
|
|
html_info
|
|
],
|
|
show_progress=False,
|
|
)
|
|
|
|
img2img_prompt.submit(**img2img_args)
|
|
submit.click(**img2img_args)
|
|
|
|
img2img_interrogate.click(
|
|
fn=interrogate,
|
|
inputs=[init_img],
|
|
outputs=[img2img_prompt],
|
|
)
|
|
|
|
interrupt.click(
|
|
fn=lambda: shared.state.interrupt(),
|
|
inputs=[],
|
|
outputs=[],
|
|
)
|
|
|
|
save.click(
|
|
fn=wrap_gradio_call(save_files),
|
|
_js="(x, y, z) => [x, y, selected_gallery_index()]",
|
|
inputs=[
|
|
generation_info,
|
|
img2img_gallery,
|
|
html_info
|
|
],
|
|
outputs=[
|
|
html_info,
|
|
html_info,
|
|
html_info,
|
|
]
|
|
)
|
|
|
|
roll.click(
|
|
fn=roll_artist,
|
|
inputs=[
|
|
img2img_prompt,
|
|
],
|
|
outputs=[
|
|
img2img_prompt,
|
|
]
|
|
)
|
|
|
|
prompts = [(txt2img_prompt, txt2img_negative_prompt), (img2img_prompt, img2img_negative_prompt)]
|
|
style_dropdowns = [(txt2img_prompt_style, txt2img_prompt_style2), (img2img_prompt_style, img2img_prompt_style2)]
|
|
|
|
for button, (prompt, negative_prompt) in zip([txt2img_save_style, img2img_save_style], prompts):
|
|
button.click(
|
|
fn=add_style,
|
|
_js="ask_for_style_name",
|
|
# Have to pass empty dummy component here, because the JavaScript and Python function have to accept
|
|
# the same number of parameters, but we only know the style-name after the JavaScript prompt
|
|
inputs=[dummy_component, prompt, negative_prompt],
|
|
outputs=[txt2img_prompt_style, img2img_prompt_style, txt2img_prompt_style2, img2img_prompt_style2],
|
|
)
|
|
|
|
for button, (prompt, negative_prompt), (style1, style2) in zip([txt2img_prompt_style_apply, img2img_prompt_style_apply], prompts, style_dropdowns):
|
|
button.click(
|
|
fn=apply_styles,
|
|
inputs=[prompt, negative_prompt, style1, style2],
|
|
outputs=[prompt, negative_prompt, style1, style2],
|
|
)
|
|
|
|
with gr.Blocks(analytics_enabled=False) as extras_interface:
|
|
with gr.Row().style(equal_height=False):
|
|
with gr.Column(variant='panel'):
|
|
with gr.Tabs():
|
|
with gr.TabItem('Single Image'):
|
|
image = gr.Image(label="Source", source="upload", interactive=True, type="pil")
|
|
|
|
with gr.TabItem('Batch Process'):
|
|
image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file")
|
|
|
|
upscaling_resize = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Resize", value=2)
|
|
|
|
with gr.Group():
|
|
extras_upscaler_1 = gr.Radio(label='Upscaler 1', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index")
|
|
|
|
with gr.Group():
|
|
extras_upscaler_2 = gr.Radio(label='Upscaler 2', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index")
|
|
extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=1)
|
|
|
|
with gr.Group():
|
|
gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="GFPGAN visibility", value=0, interactive=modules.gfpgan_model.have_gfpgan)
|
|
|
|
with gr.Group():
|
|
codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer visibility", value=0, interactive=modules.codeformer_model.have_codeformer)
|
|
codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer weight (0 = maximum effect, 1 = minimum effect)", value=0, interactive=modules.codeformer_model.have_codeformer)
|
|
|
|
submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
|
|
|
|
with gr.Column(variant='panel'):
|
|
result_images = gr.Gallery(label="Result")
|
|
html_info_x = gr.HTML()
|
|
html_info = gr.HTML()
|
|
|
|
extras_args = dict(
|
|
fn=run_extras,
|
|
inputs=[
|
|
image,
|
|
image_batch,
|
|
gfpgan_visibility,
|
|
codeformer_visibility,
|
|
codeformer_weight,
|
|
upscaling_resize,
|
|
extras_upscaler_1,
|
|
extras_upscaler_2,
|
|
extras_upscaler_2_visibility,
|
|
],
|
|
outputs=[
|
|
result_images,
|
|
html_info_x,
|
|
html_info,
|
|
]
|
|
)
|
|
|
|
submit.click(**extras_args)
|
|
|
|
pnginfo_interface = gr.Interface(
|
|
wrap_gradio_call(run_pnginfo),
|
|
inputs=[
|
|
gr.Image(label="Source", source="upload", interactive=True, type="pil"),
|
|
],
|
|
outputs=[
|
|
gr.HTML(),
|
|
gr.HTML(),
|
|
gr.HTML(),
|
|
],
|
|
allow_flagging="never",
|
|
analytics_enabled=False,
|
|
)
|
|
|
|
def create_setting_component(key):
|
|
def fun():
|
|
return opts.data[key] if key in opts.data else opts.data_labels[key].default
|
|
|
|
info = opts.data_labels[key]
|
|
t = type(info.default)
|
|
|
|
args = info.component_args() if callable(info.component_args) else info.component_args
|
|
|
|
if info.component is not None:
|
|
comp = info.component
|
|
elif t == str:
|
|
comp = gr.Textbox
|
|
elif t == int:
|
|
comp = gr.Number
|
|
elif t == bool:
|
|
comp = gr.Checkbox
|
|
else:
|
|
raise Exception(f'bad options item type: {str(t)} for key {key}')
|
|
|
|
return comp(label=info.label, value=fun, **(args or {}))
|
|
|
|
components = []
|
|
keys = list(opts.data_labels.keys())
|
|
settings_cols = 3
|
|
items_per_col = math.ceil(len(keys) / settings_cols)
|
|
|
|
def run_settings(*args):
|
|
up = []
|
|
|
|
for key, value, comp in zip(opts.data_labels.keys(), args, components):
|
|
comp_args = opts.data_labels[key].component_args
|
|
if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
|
|
continue
|
|
|
|
oldval = opts.data.get(key, None)
|
|
opts.data[key] = value
|
|
|
|
if oldval != value and opts.data_labels[key].onchange is not None:
|
|
opts.data_labels[key].onchange()
|
|
|
|
up.append(comp.update(value=value))
|
|
|
|
opts.save(shared.config_filename)
|
|
|
|
return 'Settings applied.'
|
|
|
|
with gr.Blocks(analytics_enabled=False) as settings_interface:
|
|
settings_submit = gr.Button(value="Apply settings", variant='primary')
|
|
result = gr.HTML()
|
|
|
|
with gr.Row(elem_id="settings").style(equal_height=False):
|
|
for colno in range(settings_cols):
|
|
with gr.Column(variant='panel'):
|
|
for rowno in range(items_per_col):
|
|
index = rowno + colno * items_per_col
|
|
|
|
if index < len(keys):
|
|
components.append(create_setting_component(keys[index]))
|
|
|
|
settings_submit.click(
|
|
fn=run_settings,
|
|
inputs=components,
|
|
outputs=[result]
|
|
)
|
|
|
|
interfaces = [
|
|
(txt2img_interface, "txt2img", "txt2img"),
|
|
(img2img_interface, "img2img", "img2img"),
|
|
(extras_interface, "Extras", "extras"),
|
|
(pnginfo_interface, "PNG Info", "pnginfo"),
|
|
(settings_interface, "Settings", "settings"),
|
|
]
|
|
|
|
with open(os.path.join(script_path, "style.css"), "r", encoding="utf8") as file:
|
|
css = file.read()
|
|
|
|
if os.path.exists(os.path.join(script_path, "user.css")):
|
|
with open(os.path.join(script_path, "user.css"), "r", encoding="utf8") as file:
|
|
usercss = file.read()
|
|
css += usercss
|
|
|
|
if not cmd_opts.no_progressbar_hiding:
|
|
css += css_hide_progressbar
|
|
|
|
with gr.Blocks(css=css, analytics_enabled=False, title="Stable Diffusion") as demo:
|
|
|
|
with gr.Tabs() as tabs:
|
|
for interface, label, ifid in interfaces:
|
|
with gr.TabItem(label, id=ifid):
|
|
interface.render()
|
|
|
|
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
|
|
|
|
settings_submit.click(
|
|
fn=lambda: opts.dumpjson(),
|
|
inputs=[],
|
|
outputs=[text_settings],
|
|
)
|
|
|
|
tabs.change(
|
|
fn=lambda x: x,
|
|
inputs=[init_img_with_mask],
|
|
outputs=[init_img_with_mask],
|
|
)
|
|
|
|
send_to_img2img.click(
|
|
fn=lambda x: image_from_url_text(x),
|
|
_js="extract_image_from_gallery_img2img",
|
|
inputs=[txt2img_gallery],
|
|
outputs=[init_img],
|
|
)
|
|
|
|
send_to_inpaint.click(
|
|
fn=lambda x: image_from_url_text(x),
|
|
_js="extract_image_from_gallery_img2img",
|
|
inputs=[txt2img_gallery],
|
|
outputs=[init_img_with_mask],
|
|
)
|
|
|
|
img2img_send_to_img2img.click(
|
|
fn=lambda x: image_from_url_text(x),
|
|
_js="extract_image_from_gallery",
|
|
inputs=[img2img_gallery],
|
|
outputs=[init_img],
|
|
)
|
|
|
|
img2img_send_to_inpaint.click(
|
|
fn=lambda x: image_from_url_text(x),
|
|
_js="extract_image_from_gallery",
|
|
inputs=[img2img_gallery],
|
|
outputs=[init_img_with_mask],
|
|
)
|
|
|
|
send_to_extras.click(
|
|
fn=lambda x: image_from_url_text(x),
|
|
_js="extract_image_from_gallery_extras",
|
|
inputs=[txt2img_gallery],
|
|
outputs=[image],
|
|
)
|
|
|
|
img2img_send_to_extras.click(
|
|
fn=lambda x: image_from_url_text(x),
|
|
_js="extract_image_from_gallery_extras",
|
|
inputs=[img2img_gallery],
|
|
outputs=[image],
|
|
)
|
|
|
|
ui_config_file = cmd_opts.ui_config_file
|
|
ui_settings = {}
|
|
settings_count = len(ui_settings)
|
|
error_loading = False
|
|
|
|
try:
|
|
if os.path.exists(ui_config_file):
|
|
with open(ui_config_file, "r", encoding="utf8") as file:
|
|
ui_settings = json.load(file)
|
|
except Exception:
|
|
error_loading = True
|
|
print("Error loading settings:", file=sys.stderr)
|
|
print(traceback.format_exc(), file=sys.stderr)
|
|
|
|
def loadsave(path, x):
|
|
def apply_field(obj, field, condition=None):
|
|
key = path + "/" + field
|
|
|
|
saved_value = ui_settings.get(key, None)
|
|
if saved_value is None:
|
|
ui_settings[key] = getattr(obj, field)
|
|
elif condition is None or condition(saved_value):
|
|
setattr(obj, field, saved_value)
|
|
|
|
if type(x) == gr.Slider:
|
|
apply_field(x, 'value')
|
|
apply_field(x, 'minimum')
|
|
apply_field(x, 'maximum')
|
|
apply_field(x, 'step')
|
|
|
|
if type(x) == gr.Radio:
|
|
apply_field(x, 'value', lambda val: val in x.choices)
|
|
|
|
visit(txt2img_interface, loadsave, "txt2img")
|
|
visit(img2img_interface, loadsave, "img2img")
|
|
visit(extras_interface, loadsave, "extras")
|
|
|
|
if not error_loading and (not os.path.exists(ui_config_file) or settings_count != len(ui_settings)):
|
|
with open(ui_config_file, "w", encoding="utf8") as file:
|
|
json.dump(ui_settings, file, indent=4)
|
|
|
|
return demo
|
|
|
|
|
|
with open(os.path.join(script_path, "script.js"), "r", encoding="utf8") as jsfile:
|
|
javascript = f'<script>{jsfile.read()}</script>'
|
|
|
|
jsdir = os.path.join(script_path, "javascript")
|
|
for filename in os.listdir(jsdir):
|
|
with open(os.path.join(jsdir, filename), "r", encoding="utf8") as jsfile:
|
|
javascript += f"\n<script>{jsfile.read()}</script>"
|
|
|
|
|
|
def template_response(*args, **kwargs):
|
|
res = gradio_routes_templates_response(*args, **kwargs)
|
|
res.body = res.body.replace(b'</head>', f'{javascript}</head>'.encode("utf8"))
|
|
res.init_headers()
|
|
return res
|
|
|
|
|
|
gradio_routes_templates_response = gradio.routes.templates.TemplateResponse
|
|
gradio.routes.templates.TemplateResponse = template_response
|