Add support for configuring defaults.

This commit is contained in:
Sam Green 2022-08-26 00:17:58 +01:00
parent 41dbcae10e
commit 844311fe7b
2 changed files with 157 additions and 38 deletions

137
webui.py
View File

@ -59,6 +59,7 @@ parser.add_argument("--gfpgan-dir", type=str, help="GFPGAN directory", default=(
parser.add_argument("--no-verify-input", action='store_true', help="do not verify input to check if it's too long") parser.add_argument("--no-verify-input", action='store_true', help="do not verify input to check if it's too long")
parser.add_argument("--no-half", action='store_true', help="do not switch the model to 16-bit floats") parser.add_argument("--no-half", action='store_true', help="do not switch the model to 16-bit floats")
parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware accleration in browser)") parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware accleration in browser)")
parser.add_argument("--defaults", type=str, help="path to configuration file providing UI defaults, uses same format as cli parameter", default='configs/webui/webui.yaml')
parser.add_argument("--cli", type=str, help="don't launch web server, take Python function kwargs from this file.", default=None) parser.add_argument("--cli", type=str, help="don't launch web server, take Python function kwargs from this file.", default=None)
opt = parser.parse_args() opt = parser.parse_args()
@ -684,7 +685,7 @@ class Flagging(gr.FlaggingCallback):
print("Logged:", filenames[0]) print("Logged:", filenames[0])
def img2img(prompt: str, image_editor_mode: str, cropped_image, image_with_mask, mask_mode: str, ddim_steps: int, sampler_name: str, def img2img(prompt: str, image_editor_mode: str, cropped_image, image_with_mask, mask_mode: int, ddim_steps: int, sampler_name: str,
toggles: List[int], n_iter: int, batch_size: int, cfg_scale: float, denoising_strength: float, toggles: List[int], n_iter: int, batch_size: int, cfg_scale: float, denoising_strength: float,
seed: int, height: int, width: int, resize_mode: int, fp): seed: int, height: int, width: int, resize_mode: int, fp):
outpath = opt.outdir_img2img or opt.outdir or "outputs/img2img-samples" outpath = opt.outdir_img2img or opt.outdir or "outputs/img2img-samples"
@ -723,7 +724,7 @@ def img2img(prompt: str, image_editor_mode: str, cropped_image, image_with_mask,
init_mask = image_with_mask["mask"] init_mask = image_with_mask["mask"]
init_mask = init_mask.convert("RGB") init_mask = init_mask.convert("RGB")
init_mask = resize_image(resize_mode, init_mask, width, height) init_mask = resize_image(resize_mode, init_mask, width, height)
keep_mask = mask_mode == "Keep masked area" keep_mask = mask_mode == 0
init_mask = init_mask if keep_mask else ImageOps.invert(init_mask) init_mask = init_mask if keep_mask else ImageOps.invert(init_mask)
else: else:
init_img = cropped_image init_img = cropped_image
@ -919,8 +920,16 @@ def run_GFPGAN(image, strength):
css = "" if opt.no_progressbar_hiding else css_hide_progressbar css = "" if opt.no_progressbar_hiding else css_hide_progressbar
css = css + '[data-testid="image"] {min-height: 512px !important}' css = css + '[data-testid="image"] {min-height: 512px !important}'
sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg" if opt.defaults is not None and os.path.isfile(opt.defaults):
sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None try:
with open(opt.defaults, "r", encoding="utf8") as f:
user_defaults = yaml.safe_load(f)
except (OSError, yaml.YAMLError) as e:
print(f"Error loading defaults file {opt.defaults}:", e)
print("Falling back to program defaults.")
user_defaults = {}
else:
user_defaults = {}
# make sure these indicies line up at the top of txt2img() # make sure these indicies line up at the top of txt2img()
txt2img_toggles = [ txt2img_toggles = [
@ -932,11 +941,28 @@ txt2img_toggles = [
if GFPGAN is not None: if GFPGAN is not None:
txt2img_toggles.append('Fix faces using GFPGAN') txt2img_toggles.append('Fix faces using GFPGAN')
txt2img_toggle_defaults = [ txt2img_defaults = {
'Normalize Prompt Weights (ensure sum of weights add up to 1.0)', 'prompt': '',
'Save individual images', 'ddim_steps': 50,
'Save grid' 'toggles': [1, 2, 3],
] 'sampler_name': 'k_lms',
'ddim_eta': 0.0,
'n_iter': 1,
'batch_size': 1,
'cfg_scale': 7.5,
'seed': '',
'height': 512,
'width': 512,
'fp': None,
}
if 'txt2img' in user_defaults:
txt2img_defaults.update(user_defaults['txt2img'])
txt2img_toggle_defaults = [txt2img_toggles[i] for i in txt2img_defaults['toggles']]
sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg"
sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None
# make sure these indicies line up at the top of img2img() # make sure these indicies line up at the top of img2img()
img2img_toggles = [ img2img_toggles = [
@ -950,11 +976,39 @@ img2img_toggles = [
if GFPGAN is not None: if GFPGAN is not None:
img2img_toggles.append('Fix faces using GFPGAN') img2img_toggles.append('Fix faces using GFPGAN')
img2img_toggle_defaults = [ img2img_mask_modes = [
'Normalize Prompt Weights (ensure sum of weights add up to 1.0)', "Keep masked area",
'Save individual images', "Regenerate only masked area",
'Save grid',
] ]
img2img_resize_modes = [
"Just resize",
"Crop and resize",
"Resize and fill",
]
img2img_defaults = {
'prompt': '',
'ddim_steps': 50,
'toggles': [1, 4, 5],
'sampler_name': 'k_lms',
'ddim_eta': 0.0,
'n_iter': 1,
'batch_size': 1,
'cfg_scale': 5.0,
'denoising_strength': 0.75,
'mask_mode': 0,
'resize_mode': 0,
'seed': '',
'height': 512,
'width': 512,
'fp': None,
}
if 'img2img' in user_defaults:
img2img_defaults.update(user_defaults['img2img'])
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): def change_image_editor_mode(choice, cropped_image, resize_mode, width, height):
@ -981,18 +1035,18 @@ with gr.Blocks(css=css) as demo:
with gr.Row().style(equal_height=False): with gr.Row().style(equal_height=False):
with gr.Column(): with gr.Column():
gr.Markdown("Generate images from text with Stable Diffusion") gr.Markdown("Generate images from text with Stable Diffusion")
txt2img_prompt = gr.Textbox(label="Prompt", placeholder="A corgi wearing a top hat as an oil painting.", lines=1) txt2img_prompt = gr.Textbox(label="Prompt", placeholder="A corgi wearing a top hat as an oil painting.", lines=1, value=txt2img_defaults['prompt'])
txt2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=50) txt2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=txt2img_defaults['ddim_steps'])
txt2img_sampling = gr.Radio(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="k_lms") txt2img_sampling = gr.Radio(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'])
txt2img_toggles = gr.CheckboxGroup(label='', choices=txt2img_toggles, value=txt2img_toggle_defaults, type="index") txt2img_toggles = gr.CheckboxGroup(label='', choices=txt2img_toggles, value=txt2img_toggle_defaults, type="index")
txt2img_ddim_eta = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="DDIM ETA", value=0.0, visible=False) 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_batch_count = gr.Slider(minimum=1, maximum=250, step=1, label='Batch count (how many batches of images to generate)', value=1) 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=1) txt2img_batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size (how many images are in a batch; memory-hungry)', value=txt2img_defaults['batch_size'])
txt2img_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=7.5) 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, value="") txt2img_seed = gr.Textbox(label="Seed (blank to randomize)", lines=1, value=txt2img_defaults['seed'])
txt2img_height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512) 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=512) txt2img_width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=txt2img_defaults['width'])
txt2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager")) txt2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager"), value=txt2img_defaults['fp'])
txt2img_btn = gr.Button("Generate") txt2img_btn = gr.Button("Generate")
with gr.Column(): with gr.Column():
output_txt2img_gallery = gr.Gallery(label="Images") output_txt2img_gallery = gr.Gallery(label="Images")
@ -1012,24 +1066,24 @@ with gr.Blocks(css=css) as demo:
with gr.Row().style(equal_height=False): with gr.Row().style(equal_height=False):
with gr.Column(): with gr.Column():
gr.Markdown("Generate images from images with Stable Diffusion") gr.Markdown("Generate images from images with Stable Diffusion")
img2img_prompt = gr.Textbox(label="Prompt", placeholder="A fantasy landscape, trending on artstation.", lines=1) img2img_prompt = gr.Textbox(label="Prompt", placeholder="A fantasy landscape, trending on artstation.", lines=1, value=img2img_defaults['prompt'])
img2img_image_editor_mode = gr.Radio(choices=["Mask", "Crop"], label="Image Editor Mode", value="Crop") img2img_image_editor_mode = gr.Radio(choices=["Mask", "Crop"], label="Image Editor Mode", value="Crop")
gr.Markdown("The masking/cropping is very temperamental. It may take some time for the image to show when switching from Crop to Mask. If it doesn't work try switching modes again, switch tabs, clear the image or reload.") gr.Markdown("The masking/cropping is very temperamental. It may take some time for the image to show when switching from Crop to Mask. If it doesn't work try switching modes again, switch tabs, clear the image or reload.")
img2img_image_editor = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="select") img2img_image_editor = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="select")
img2img_image_mask = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="sketch", visible=False) img2img_image_mask = gr.Image(value=sample_img2img, source="upload", interactive=True, type="pil", tool="sketch", visible=False)
img2img_mask = gr.Radio(choices=["Keep masked area", "Regenerate only masked area"], label="Mask Mode", value="Keep masked area") 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']])
img2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=50) img2img_steps = gr.Slider(minimum=1, maximum=250, step=1, label="Sampling Steps", value=img2img_defaults['ddim_steps'])
img2img_sampling = gr.Radio(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="k_lms") img2img_sampling = gr.Radio(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_toggles = gr.CheckboxGroup(label='', choices=img2img_toggles, value=img2img_toggle_defaults, type="index")
img2img_batch_count = gr.Slider(minimum=1, maximum=250, step=1, label='Batch count (how many batches of images to generate)', value=1) 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=1) 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=5.0) 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=0.75) 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_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=512) 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=512) 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="Just resize") 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")) img2img_embeddings = gr.File(label = "Embeddings file for textual inversion", visible=hasattr(model, "embedding_manager"), value=img2img_defaults['fp'])
img2img_btn = gr.Button("Generate") img2img_btn = gr.Button("Generate")
with gr.Column(): with gr.Column():
output_img2img_gallery = gr.Gallery(label="Images") output_img2img_gallery = gr.Gallery(label="Images")
@ -1064,12 +1118,19 @@ with gr.Blocks(css=css) as demo:
[output_img2img_gallery, output_img2img_seed, output_img2img_params, output_img2img_stats] [output_img2img_gallery, output_img2img_seed, output_img2img_params, output_img2img_stats]
) )
if GFPGAN is not None: if GFPGAN is not None:
gfpgan_defaults = {
'strength': 100,
}
if 'gfpgan' in user_defaults:
gfpgan_defaults.update(user_defaults['gfpgan'])
with gr.TabItem("GFPGAN"): with gr.TabItem("GFPGAN"):
gr.Markdown("Fix faces on images") gr.Markdown("Fix faces on images")
with gr.Row(): with gr.Row():
with gr.Column(): with gr.Column():
gfpgan_source = gr.Image(label="Source", source="upload", interactive=True, type="pil") 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=100) 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") gfpgan_btn = gr.Button("Generate")
with gr.Column(): with gr.Column():
gfpgan_output = gr.Image(label="Output") gfpgan_output = gr.Image(label="Output")

58
webui.yaml Normal file
View File

@ -0,0 +1,58 @@
# UI defaults configuration file. Is read automatically if located at configs/webui/webui.yaml, or specify path via --defaults.
txt2img:
prompt:
ddim_steps: 50
# Adding an int to toggles enables the corresponding feature.
# 0: Create prompt matrix (separate multiple prompts using |, and get all combinations of them)
# 1: Normalize Prompt Weights (ensure sum of weights add up to 1.0)
# 2: Save individual images
# 3: Save grid
# 4: Fix faces using GFPGAN
toggles: [1, 2, 3]
sampler_name: k_lms
ddim_eta: 0.0
n_iter: 1
batch_size: 1
cfg_scale: 7.5
# Leave blank for random seed
seed:
height: 512
width: 512
# Textual inversion embeddings file path
fp:
img2img:
prompt:
ddim_steps: 50
# Adding an int to toggles enables the corresponding feature.
# 0: Create prompt matrix (separate multiple prompts using |, and get all combinations of them)
# 1: Normalize Prompt Weights (ensure sum of weights add up to 1.0)
# 2: Loopback (use images from previous batch when creating next batch)
# 3: Random loopback seed
# 4: Save individual images
# 5: Save grid
# 6: Fix faces using GFPGAN
toggles: [1, 4, 5]
sampler_name: k_lms
ddim_eta: 0.0
n_iter: 1
batch_size: 1
cfg_scale: 5.0
denoising_strength: 0.75
# 0: Keep masked area
# 1: Regenerate only masked area
mask_mode: 0
# 0: Just resize
# 1: Crop and resize
# 2: Resize and fill
resize_mode: 0
# Leave blank for random seed
seed:
height: 512
width: 512
# Textual inversion embeddings file path
fp:
gfpgan:
strength: 100