Compare commits

...
Author SHA1 Message Date
48DESIGNandGitHub e4145c8453 Merge branch 'master' into notification-sound 2022-09-27 08:05:19 +02:00
ConnumandAUTOMATIC1111 c0b1177a32 refactored image paste handling to fill unset images successively, then replace last existing image (fixes #981) 2022-09-27 08:59:40 +03:00
DanielandAUTOMATIC1111 8dcac9ac81 Grids saving to subdirectory when setting unchecked
Fixed grids saving to subdirectory when the setting is unchecked but "save images to subdirectory" was checked.
2022-09-27 08:58:47 +03:00
Martin CairnsandAUTOMATIC1111 258a2d4f06 Add option to img2imgalt.py to use sigma adjustment instead of original method for #736 2022-09-27 08:53:35 +03:00
Alexandre SimardandAUTOMATIC1111 c74becca23 Solve issue #962
Fix by @MrAcademy
2022-09-27 08:52:43 +03:00
DepFAandAUTOMATIC1111 16e7ac09df job_timestamp initialization change 2022-09-27 08:51:59 +03:00
Connum 2846ca5702 adds support for a notification.mp3 in the root directory that will play upon completion (fixes #1013) 2022-09-26 22:57:31 +02:00
tateisuandAUTOMATIC1111 78d6aef302 use strftime. update hints.js 2022-09-26 16:24:21 +03:00
tateisuandAUTOMATIC1111 b39f06cea7 add [datetime] to image file name pattern 2022-09-26 16:24:21 +03:00
DepFAandAUTOMATIC1111 e61da308ba Disable sentry logging 2022-09-26 16:19:47 +03:00
DepFAandAUTOMATIC1111 b8a8f749ef Update webui.bat 2022-09-26 16:19:47 +03:00
LiamandAUTOMATIC1111 db4ab47f09 copy over fields when sending to img2img: https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/1029 2022-09-26 08:33:02 +03:00
AUTOMATIC ca3e5519e8 Merge remote-tracking branch 'origin/master' 2022-09-25 19:45:22 +03:00
AUTOMATIC ff107845b4 added hiding items to ui-config.json 2022-09-25 19:43:42 +03:00
EyrieandAUTOMATIC1111 1877a3767e Changed job_id to timestamp 2022-09-25 17:11:07 +03:00
EyrieandAUTOMATIC1111 4c3d4aad60 fix formatting 2022-09-25 17:11:07 +03:00
EyrieandAUTOMATIC1111 e13912dfde fix formatting 2022-09-25 17:11:07 +03:00
EyrieandAUTOMATIC1111 ba34b790d3 Added hints for file & dir patterns 2022-09-25 17:11:07 +03:00
EyrieandAUTOMATIC1111 40166dbf08 Added job_id pattern for directories 2022-09-25 17:11:07 +03:00
AUTOMATIC 073f6eac22 potential fix for embeddings no loading on AMD cards 2022-09-25 15:04:39 +03:00
DepFAandAUTOMATIC1111 50ae19acf6 add custom script source to config path keys 2022-09-25 09:59:31 +03:00
DepFAandAUTOMATIC1111 31a0fbf1ad add attribute to custom script controls 2022-09-25 09:59:31 +03:00
guaneecandAUTOMATIC1111 615b2fc9ce Fix token max length 2022-09-25 09:30:02 +03:00
AUTOMATIC b8eae5de93 Merge remote-tracking branch 'origin/master' 2022-09-25 09:25:47 +03:00
AUTOMATIC 600cc0342d added support for setting hires fix from pasted prompts
added more robust detection of last line with parameters for pasted prompts
2022-09-25 09:25:28 +03:00
DepFAandAUTOMATIC1111 72e2a96284 Correct indent 2022-09-25 08:46:09 +03:00
DepFAandAUTOMATIC1111 ab5a472556 Persist default Number values 2022-09-25 08:46:09 +03:00
DepFAandAUTOMATIC1111 ca13264609 Add attr to skip 2022-09-25 08:46:09 +03:00
DepFAandAUTOMATIC1111 b1320d79b5 Apply to all checkboxes and textboxes 2022-09-25 08:46:09 +03:00
DepFAandAUTOMATIC1111 714dcda068 Add attributes to checkboxes for persisting 2022-09-25 08:46:09 +03:00
achaiahandAUTOMATIC1111 5194a58278 removing duplicates
removing duplicated requirements (transformers, diffusers) and reorganizing alphabetically to be able to spot duplicates in the future.
2022-09-25 08:44:54 +03:00
AUTOMATIC 53be15c204 by popular request, moves width/height sliders higher 2022-09-25 08:40:29 +03:00
EyeDeckandAUTOMATIC1111 b5d705e263 Disable 'batch img2img' when launched with --hide-ui-dir-config
Prevent writing to arbitrary directories, as usual
2022-09-24 19:06:36 +03:00
RnDMonkeyandAUTOMATIC1111 a2bea2f97a added [styles] filename pattern 2022-09-24 11:12:58 +03:00
AUTOMATIC 410bef0853 do not hide preview image too early 2022-09-24 09:16:54 +03:00
AUTOMATIC 31ac0baba0 Reformat some changes from the previous commit. 2022-09-24 08:23:01 +03:00
RnDMonkeyandGitHub ed8b8b3b8c Fix to XY_Grid script console progress bar and other progress bar improvements (#890)
Fix to XY_Grid script console progress bar and other progress bar improvements #890
2022-09-24 08:09:59 +03:00
jciancaandAUTOMATIC1111 7aec029423 Update README.md 2022-09-24 07:58:30 +03:00
jciancaandAUTOMATIC1111 e94ec98102 Update README.md 2022-09-24 07:58:30 +03:00
jciancaandAUTOMATIC1111 db341bd0d8 Add files via upload 2022-09-24 07:58:30 +03:00
innovacionesandAUTOMATIC1111 3e4b7fb869 Remove double loading message 2022-09-24 07:58:07 +03:00
ConnumandAUTOMATIC1111 910a3d9427 make callback queue handling reusable and implement onUiTabChange() 2022-09-24 07:44:05 +03:00
Jacob TyndallandAUTOMATIC1111 0f77e1852f Clear no longer needed VRAM during a 'highres fix' generation created during the first sampling before doing the second (img2img) sampling. Makes it possible to use the feature on an 8GB card. 2022-09-24 07:41:59 +03:00
22 changed files with 310 additions and 130 deletions
+3 -1
View File
@@ -19,4 +19,6 @@ __pycache__
/webui-user.sh
/interrogate
/user.css
/.idea
/.idea
notification.mp3
/SwinIR
+1 -1
View File
@@ -1,7 +1,7 @@
# Stable Diffusion web UI
A browser interface based on Gradio library for Stable Diffusion.
![](screenshot.png)
![](txt2img_Screenshot.png)
## Features
[Detailed feature showcase with images](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features):
+15 -9
View File
@@ -68,13 +68,19 @@ window.addEventListener('paste', e => {
if ( ! isValidImageList( files ) ) {
return;
}
[...gradioApp().querySelectorAll('input[type=file][accept="image/x-png,image/gif,image/jpeg"]')]
.filter(input => !input.matches('.\\!hidden input[type=file]'))
.forEach(input => {
input.files = files;
input.dispatchEvent(new Event('change'))
});
[...gradioApp().querySelectorAll('[data-testid="image"]')]
.filter(imgWrap => !imgWrap.closest('.\\!hidden'))
.forEach(imgWrap => dropReplaceImage( imgWrap, files ));
const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')]
.filter(el => uiElementIsVisible(el));
if ( ! visibleImageFields.length ) {
return;
}
const firstFreeImageField = visibleImageFields
.filter(el => el.querySelector('input[type=file]'))?.[0];
dropReplaceImage(
firstFreeImageField ?
firstFreeImageField :
visibleImageFields[visibleImageFields.length - 1]
, files );
});
+2 -3
View File
@@ -57,14 +57,13 @@ titles = {
"Interrogate": "Reconstruct prompt from existing image and put it into the prompt field.",
"Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [sampler], [seed], [model_hash], [prompt_words], [date]; leave empty for default.",
"Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [sampler], [seed], [model_hash], [prompt_words], [date]; leave empty for default.",
"Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [job_timestamp]; leave empty for default.",
"Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [job_timestamp]; leave empty for default.",
"Max prompt words": "Set the maximum number of words to be used in the [prompt_words] option; ATTENTION: If the words are too long, they may exceed the maximum length of the file path that the system can handle",
"Loopback": "Process an image, use it as an input, repeat.",
"Loops": "How many times to repeat processing an image and using it as input for the next iteration",
"Style 1": "Style to apply; styles have components for both positive and negative prompts and apply to both",
"Style 2": "Style to apply; styles have components for both positive and negative prompts and apply to both",
"Apply style": "Insert selected styles into prompt fields",
+3
View File
@@ -25,6 +25,9 @@ onUiUpdate(function(){
lastHeadImg = headImg;
// play notification sound if available
gradioApp().querySelector('#audio_notification audio')?.play();
if (document.hasFocus()) return;
// Multiple copies of the images are in the DOM when one is selected. Dedup with a Set to get the real number generated.
+2 -3
View File
@@ -1,9 +1,8 @@
// various functions for interation with ui.py not large enough to warrant putting them in separate files
function selected_gallery_index(){
var gr = gradioApp()
var buttons = gradioApp().querySelectorAll(".gallery-item")
var button = gr.querySelector(".gallery-item.\\!ring-2")
var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem .gallery-item')
var button = gradioApp().querySelector('[style="display: block;"].tabitem .gallery-item.\\!ring-2')
var result = -1
buttons.forEach(function(v, i){ if(v==button) { result = i } })
+19 -15
View File
@@ -1,9 +1,11 @@
from collections import namedtuple
import re
import gradio as gr
re_param = re.compile(r"\s*([\w ]+):\s*([^,]+)(?:,|$)")
re_param_code = r"\s*([\w ]+):\s*([^,]+)(?:,|$)"
re_param = re.compile(re_param_code)
re_params = re.compile(r"^(?:" + re_param_code + "){3,}$")
re_imagesize = re.compile(r"^(\d+)x(\d+)$")
type_of_gr_update = type(gr.update())
def parse_generation_parameters(x: str):
@@ -25,6 +27,10 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
done_with_prompt = False
*lines, lastline = x.strip().split("\n")
if not re_params.match(lastline):
lines.append(lastline)
lastline = ''
for i, line in enumerate(lines):
line = line.strip()
if line.startswith("Negative prompt:"):
@@ -32,9 +38,9 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
line = line[16:].strip()
if done_with_prompt:
negative_prompt += line
negative_prompt += ("" if negative_prompt == "" else "\n") + line
else:
prompt += line
prompt += ("" if prompt == "" else "\n") + line
if len(prompt) > 0:
res["Prompt"] = prompt
@@ -53,19 +59,21 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
return res
def connect_paste(button, d, input_comp, js=None):
items = []
outputs = []
def connect_paste(button, paste_fields, input_comp, js=None):
def paste_func(prompt):
params = parse_generation_parameters(prompt)
res = []
for key, output in zip(items, outputs):
v = params.get(key, None)
for output, key in paste_fields:
if callable(key):
v = key(params)
else:
v = params.get(key, None)
if v is None:
res.append(gr.update())
elif isinstance(v, type_of_gr_update):
res.append(v)
else:
try:
valtype = type(output.value)
@@ -76,13 +84,9 @@ def connect_paste(button, d, input_comp, js=None):
return res
for k, v in d.items():
items.append(k)
outputs.append(v)
button.click(
fn=paste_func,
_js=js,
inputs=[input_comp],
outputs=outputs,
outputs=[x[0] for x in paste_fields],
)
+3
View File
@@ -290,10 +290,13 @@ def apply_filename_pattern(x, p, seed, prompt):
x = x.replace("[cfg]", str(p.cfg_scale))
x = x.replace("[width]", str(p.width))
x = x.replace("[height]", str(p.height))
x = x.replace("[styles]", sanitize_filename_part(", ".join(p.styles), replace_spaces=False))
x = x.replace("[sampler]", sanitize_filename_part(sd_samplers.samplers[p.sampler_index].name, replace_spaces=False))
x = x.replace("[model_hash]", shared.sd_model.sd_model_hash)
x = x.replace("[date]", datetime.date.today().isoformat())
x = x.replace("[datetime]", datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
x = x.replace("[job_timestamp]", shared.state.job_timestamp)
if cmd_opts.hide_ui_dir_config:
x = re.sub(r'^[\\/]+|\.{2,}[\\/]+|[\\/]+\.{2,}', '', x)
+2
View File
@@ -108,6 +108,8 @@ def img2img(mode: int, prompt: str, negative_prompt: str, prompt_style: str, pro
p.extra_generation_params["Mask blur"] = mask_blur
if is_batch:
assert not shared.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled"
process_batch(p, img2img_batch_input_dir, img2img_batch_output_dir, args)
processed = Processed(p, [], p.seed, "")
+6 -1
View File
@@ -406,7 +406,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
index_of_first_image = 1
if opts.grid_save:
images.save_image(grid, p.outpath_grids, "grid", all_seeds[0], all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename, p=p)
images.save_image(grid, p.outpath_grids, "grid", all_seeds[0], all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename, p=p, grid=True)
devices.torch_gc()
return Processed(p, output_images, all_seeds[0], infotext(), subseed=all_subseeds[0], all_prompts=all_prompts, all_seeds=all_seeds, all_subseeds=all_subseeds, index_of_first_image=index_of_first_image)
@@ -488,6 +488,11 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
self.sampler = samplers[self.sampler_index].constructor(self.sd_model)
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self)
# GC now before running the next img2img to prevent running out of memory
x = None
devices.torch_gc()
samples = self.sampler.sample_img2img(self, samples, noise, conditioning, unconditional_conditioning, steps=self.steps)
return samples
+1
View File
@@ -119,6 +119,7 @@ class ScriptRunner:
continue
for control in controls:
control.custom_script_source = os.path.basename(script.filename)
control.visible = False
inputs += controls
+3 -3
View File
@@ -201,7 +201,7 @@ class StableDiffusionModelHijack:
def process_file(path, filename):
name = os.path.splitext(filename)[0]
data = torch.load(path)
data = torch.load(path, map_location="cpu")
# textual inversion embeddings
if 'string_to_param' in data:
@@ -217,7 +217,7 @@ class StableDiffusionModelHijack:
if len(emb.shape) == 1:
emb = emb.unsqueeze(0)
self.word_embeddings[name] = emb.detach()
self.word_embeddings[name] = emb.detach().to(device)
self.word_embeddings_checksums[name] = f'{const_hash(emb.reshape(-1)*100)&0xffff:04x}'
ids = tokenizer([name], add_special_tokens=False)['input_ids'][0]
@@ -300,7 +300,7 @@ class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module):
remade_batch_tokens = []
id_start = self.wrapped.tokenizer.bos_token_id
id_end = self.wrapped.tokenizer.eos_token_id
maxlen = self.wrapped.max_length - 2
maxlen = self.wrapped.max_length
used_custom_terms = []
cache = {}
+13 -1
View File
@@ -4,6 +4,7 @@ import json
import os
import gradio as gr
import tqdm
import datetime
import modules.artists
from modules.paths import script_path, sd_path
@@ -65,6 +66,7 @@ class State:
job = ""
job_no = 0
job_count = 0
job_timestamp = '0'
sampling_step = 0
sampling_steps = 0
current_latent = None
@@ -78,6 +80,9 @@ class State:
self.job_no += 1
self.sampling_step = 0
self.current_image_sampling_step = 0
def get_job_timestamp(self):
return datetime.datetime.now().strftime("%Y%m%d%H%M%S")
state = State()
@@ -116,7 +121,7 @@ def options_section(section_identifer, options_dict):
return options_dict
hide_dirs = {"visible": False} if cmd_opts.hide_ui_dir_config else None
hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
options_templates = {}
@@ -307,6 +312,13 @@ class TotalTQDM:
self.reset()
self._tqdm.update()
def updateTotal(self, new_total):
if not opts.multiple_tqdm:
return
if self._tqdm is None:
self.reset()
self._tqdm.total=new_total
def clear(self):
if self._tqdm is not None:
self._tqdm.close()
+84 -58
View File
@@ -49,7 +49,6 @@ 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; }
"""
@@ -198,7 +197,7 @@ def check_progress_call(id_part):
image = shared.state.current_image
if image is None or progress >= 1:
if image is None:
image = gr.update(value=None)
else:
preview_visibility = gr_show(True)
@@ -413,6 +412,10 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
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.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)
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)
@@ -428,10 +431,6 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
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, seed_checkbox = create_seed_inputs()
with gr.Group():
@@ -522,23 +521,25 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
]
)
txt2img_paste_fields = {
"Prompt": txt2img_prompt,
"Negative prompt": txt2img_negative_prompt,
"Steps": steps,
"Sampler": sampler_index,
"Face restoration": restore_faces,
"CFG scale": cfg_scale,
"Seed": seed,
"Size-1": width,
"Size-2": height,
"Batch size": batch_size,
"Variation seed": subseed,
"Variation seed strength": subseed_strength,
"Seed resize from-1": seed_resize_from_w,
"Seed resize from-2": seed_resize_from_h,
"Denoising strength": denoising_strength,
}
txt2img_paste_fields = [
(txt2img_prompt, "Prompt"),
(txt2img_negative_prompt, "Negative prompt"),
(steps, "Steps"),
(sampler_index, "Sampler"),
(restore_faces, "Face restoration"),
(cfg_scale, "CFG scale"),
(seed, "Seed"),
(width, "Size-1"),
(height, "Size-2"),
(batch_size, "Batch size"),
(subseed, "Variation seed"),
(subseed_strength, "Variation seed strength"),
(seed_resize_from_w, "Seed resize from-1"),
(seed_resize_from_h, "Seed resize from-2"),
(denoising_strength, "Denoising strength"),
(enable_hr, lambda d: "Denoising strength" in d),
(hr_options, lambda d: gr.Row.update(visible="Denoising strength" in d)),
]
modules.generation_parameters_copypaste.connect_paste(paste, txt2img_paste_fields, txt2img_prompt)
with gr.Blocks(analytics_enabled=False) as img2img_interface:
@@ -563,13 +564,13 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.TabItem('Inpaint', id='inpaint'):
init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA")
init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False)
init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False)
init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_base")
init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_mask")
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4)
with gr.Row():
mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask")
mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask", elem_id="mask_mode")
inpainting_mask_invert = gr.Radio(label='Masking mode', show_label=False, choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index")
inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index")
@@ -579,9 +580,10 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
inpaint_full_res_padding = gr.Slider(label='Inpaint at full resolution padding, pixels', minimum=0, maximum=256, step=4, value=32)
with gr.TabItem('Batch img2img', id='batch'):
gr.HTML("<p class=\"text-gray-500\">Process images in a directory on the same machine where the server is running.</p>")
img2img_batch_input_dir = gr.Textbox(label="Input directory")
img2img_batch_output_dir = gr.Textbox(label="Output directory")
hidden = '<br>Disabled when launched with --hide-ui-dir-config.' if shared.cmd_opts.hide_ui_dir_config else ''
gr.HTML(f"<p class=\"text-gray-500\">Process images in a directory on the same machine where the server is running.{hidden}</p>")
img2img_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs)
img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs)
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")
@@ -589,6 +591,10 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
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")
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)
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)
@@ -601,10 +607,6 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
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, seed_checkbox = create_seed_inputs()
with gr.Group():
@@ -741,23 +743,23 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
outputs=[prompt, negative_prompt, style1, style2],
)
img2img_paste_fields = {
"Prompt": img2img_prompt,
"Negative prompt": img2img_negative_prompt,
"Steps": steps,
"Sampler": sampler_index,
"Face restoration": restore_faces,
"CFG scale": cfg_scale,
"Seed": seed,
"Size-1": width,
"Size-2": height,
"Batch size": batch_size,
"Variation seed": subseed,
"Variation seed strength": subseed_strength,
"Seed resize from-1": seed_resize_from_w,
"Seed resize from-2": seed_resize_from_h,
"Denoising strength": denoising_strength,
}
img2img_paste_fields = [
(img2img_prompt, "Prompt"),
(img2img_negative_prompt, "Negative prompt"),
(steps, "Steps"),
(sampler_index, "Sampler"),
(restore_faces, "Face restoration"),
(cfg_scale, "CFG scale"),
(seed, "Seed"),
(width, "Size-1"),
(height, "Size-2"),
(batch_size, "Batch size"),
(subseed, "Variation seed"),
(subseed_strength, "Variation seed strength"),
(seed_resize_from_w, "Seed resize from-1"),
(seed_resize_from_h, "Seed resize from-2"),
(denoising_strength, "Denoising strength"),
]
modules.generation_parameters_copypaste.connect_paste(paste, img2img_paste_fields, img2img_prompt)
with gr.Blocks(analytics_enabled=False) as extras_interface:
@@ -968,6 +970,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
for interface, label, ifid in interfaces:
with gr.TabItem(label, id=ifid):
interface.render()
if os.path.exists(os.path.join(script_path, "notification.mp3")):
audio_notification = gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False)
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
settings_submit.click(
@@ -976,18 +981,21 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
outputs=[result, text_settings],
)
paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Seed', 'Size-1', 'Size-2']
txt2img_fields = [field for field,name in txt2img_paste_fields if name in paste_field_names]
img2img_fields = [field for field,name in img2img_paste_fields if name in paste_field_names]
send_to_img2img.click(
fn=lambda x: (image_from_url_text(x)),
_js="extract_image_from_gallery_img2img",
inputs=[txt2img_gallery],
outputs=[init_img],
fn=lambda img, *args: (image_from_url_text(img),*args),
_js="(gallery, ...args) => [extract_image_from_gallery_img2img(gallery), ...args]",
inputs=[txt2img_gallery] + txt2img_fields,
outputs=[init_img] + img2img_fields,
)
send_to_inpaint.click(
fn=lambda x: (image_from_url_text(x)),
_js="extract_image_from_gallery_inpaint",
inputs=[txt2img_gallery],
outputs=[init_img_with_mask],
fn=lambda x, *args: (image_from_url_text(x), *args),
_js="(gallery, ...args) => [extract_image_from_gallery_inpaint(gallery), ...args]",
inputs=[txt2img_gallery] + txt2img_fields,
outputs=[init_img_with_mask] + img2img_fields,
)
img2img_send_to_img2img.click(
@@ -1039,12 +1047,21 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
def apply_field(obj, field, condition=None):
key = path + "/" + field
if getattr(obj,'custom_script_source',None) is not None:
key = 'customscript/' + obj.custom_script_source + '/' + key
if getattr(obj, 'do_not_save_to_config', False):
return
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) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number] and x.visible:
apply_field(x, 'visible')
if type(x) == gr.Slider:
apply_field(x, 'value')
apply_field(x, 'minimum')
@@ -1054,6 +1071,15 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
if type(x) == gr.Radio:
apply_field(x, 'value', lambda val: val in x.choices)
if type(x) == gr.Checkbox:
apply_field(x, 'value')
if type(x) == gr.Textbox:
apply_field(x, 'value')
if type(x) == gr.Number:
apply_field(x, 'value')
visit(txt2img_interface, loadsave, "txt2img")
visit(img2img_interface, loadsave, "img2img")
visit(extras_interface, loadsave, "extras")
+13 -15
View File
@@ -1,22 +1,20 @@
transformers
diffusers
basicsr
gfpgan
gradio
numpy
Pillow
realesrgan
torch
transformers
omegaconf
pytorch_lightning
diffusers
invisible-watermark
scikit-image>=0.19
fairscale==0.4.4
fonts
font-roboto
gfpgan
gradio
invisible-watermark
git+https://github.com/crowsonkb/k-diffusion.git
numpy
omegaconf
piexif
Pillow
pytorch_lightning
realesrgan
scikit-image>=0.19
git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379
timm==0.4.12
fairscale==0.4.4
piexif
transformers
torch
+46 -9
View File
@@ -2,24 +2,61 @@ function gradioApp(){
return document.getElementsByTagName('gradio-app')[0].shadowRoot;
}
function get_uiCurrentTab() {
return gradioApp().querySelector('.tabs button:not(.border-transparent)')
}
uiUpdateCallbacks = []
uiTabChangeCallbacks = []
let uiCurrentTab = null
function onUiUpdate(callback){
uiUpdateCallbacks.push(callback)
}
function onUiTabChange(callback){
uiTabChangeCallbacks.push(callback)
}
function uiUpdate(root){
uiUpdateCallbacks.forEach(function(x){
try {
x()
} catch (e) {
(console.error || console.log).call(console, e.message, e);
}
})
function runCallback(x){
try {
x()
} catch (e) {
(console.error || console.log).call(console, e.message, e);
}
}
function executeCallbacks(queue) {
queue.forEach(runCallback)
}
document.addEventListener("DOMContentLoaded", function() {
var mutationObserver = new MutationObserver(function(m){
uiUpdate(gradioApp());
executeCallbacks(uiUpdateCallbacks);
const newTab = get_uiCurrentTab();
if ( newTab && ( newTab !== uiCurrentTab ) ) {
uiCurrentTab = newTab;
executeCallbacks(uiTabChangeCallbacks);
}
});
mutationObserver.observe( gradioApp(), { childList:true, subtree:true })
});
/**
* checks that a UI element is not in another hidden element or tab content
*/
function uiElementIsVisible(el) {
let isVisible = !el.closest('.\\!hidden');
if ( ! isVisible ) {
return false;
}
while( isVisible = el.closest('.tabitem')?.style.display !== 'none' ) {
if ( ! isVisible ) {
return false;
} else if ( el.parentElement ) {
el = el.parentElement
} else {
break;
}
}
return isVisible;
}
+62 -6
View File
@@ -59,7 +59,55 @@ def find_noise_for_image(p, cond, uncond, cfg_scale, steps):
return x / x.std()
Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "original_prompt", "original_negative_prompt"])
Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "original_prompt", "original_negative_prompt", "sigma_adjustment"])
# Based on changes suggested by briansemrau in https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/736
def find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg_scale, steps):
x = p.init_latent
s_in = x.new_ones([x.shape[0]])
dnw = K.external.CompVisDenoiser(shared.sd_model)
sigmas = dnw.get_sigmas(steps).flip(0)
shared.state.sampling_steps = steps
for i in trange(1, len(sigmas)):
shared.state.sampling_step += 1
x_in = torch.cat([x] * 2)
sigma_in = torch.cat([sigmas[i - 1] * s_in] * 2)
cond_in = torch.cat([uncond, cond])
c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)]
if i == 1:
t = dnw.sigma_to_t(torch.cat([sigmas[i] * s_in] * 2))
else:
t = dnw.sigma_to_t(sigma_in)
eps = shared.sd_model.apply_model(x_in * c_in, t, cond=cond_in)
denoised_uncond, denoised_cond = (x_in + eps * c_out).chunk(2)
denoised = denoised_uncond + (denoised_cond - denoised_uncond) * cfg_scale
if i == 1:
d = (x - denoised) / (2 * sigmas[i])
else:
d = (x - denoised) / sigmas[i - 1]
dt = sigmas[i] - sigmas[i - 1]
x = x + d * dt
sd_samplers.store_latent(x)
# This shouldn't be necessary, but solved some VRAM issues
del x_in, sigma_in, cond_in, c_out, c_in, t,
del eps, denoised_uncond, denoised_cond, denoised, d, dt
shared.state.nextjob()
return x / sigmas[-1]
class Script(scripts.Script):
@@ -78,9 +126,10 @@ class Script(scripts.Script):
cfg = gr.Slider(label="Decode CFG scale", minimum=0.0, maximum=15.0, step=0.1, value=1.0)
st = gr.Slider(label="Decode steps", minimum=1, maximum=150, step=1, value=50)
randomness = gr.Slider(label="Randomness", minimum=0.0, maximum=1.0, step=0.01, value=0.0)
return [original_prompt, original_negative_prompt, cfg, st, randomness]
sigma_adjustment = gr.Checkbox(label="Sigma adjustment for finding noise for image", value=False)
return [original_prompt, original_negative_prompt, cfg, st, randomness, sigma_adjustment]
def run(self, p, original_prompt, original_negative_prompt, cfg, st, randomness):
def run(self, p, original_prompt, original_negative_prompt, cfg, st, randomness, sigma_adjustment):
p.batch_size = 1
p.batch_count = 1
@@ -88,7 +137,10 @@ class Script(scripts.Script):
def sample_extra(conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength):
lat = (p.init_latent.cpu().numpy() * 10).astype(int)
same_params = self.cache is not None and self.cache.cfg_scale == cfg and self.cache.steps == st and self.cache.original_prompt == original_prompt and self.cache.original_negative_prompt == original_negative_prompt
same_params = self.cache is not None and self.cache.cfg_scale == cfg and self.cache.steps == st \
and self.cache.original_prompt == original_prompt \
and self.cache.original_negative_prompt == original_negative_prompt \
and self.cache.sigma_adjustment == sigma_adjustment
same_everything = same_params and self.cache.latent.shape == lat.shape and np.abs(self.cache.latent-lat).sum() < 100
if same_everything:
@@ -97,8 +149,11 @@ class Script(scripts.Script):
shared.state.job_count += 1
cond = p.sd_model.get_learned_conditioning(p.batch_size * [original_prompt])
uncond = p.sd_model.get_learned_conditioning(p.batch_size * [original_negative_prompt])
rec_noise = find_noise_for_image(p, cond, uncond, cfg, st)
self.cache = Cached(rec_noise, cfg, st, lat, original_prompt, original_negative_prompt)
if sigma_adjustment:
rec_noise = find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg, st)
else:
rec_noise = find_noise_for_image(p, cond, uncond, cfg, st)
self.cache = Cached(rec_noise, cfg, st, lat, original_prompt, original_negative_prompt, sigma_adjustment)
rand_noise = processing.create_random_tensors(p.init_latent.shape[1:], [p.seed + x + 1 for x in range(p.init_latent.shape[0])])
@@ -121,6 +176,7 @@ class Script(scripts.Script):
p.extra_generation_params["Decode CFG scale"] = cfg
p.extra_generation_params["Decode steps"] = st
p.extra_generation_params["Randomness"] = randomness
p.extra_generation_params["Sigma Adjustment"] = sigma_adjustment
processed = processing.process_images(p)
+26 -5
View File
@@ -141,10 +141,11 @@ class Script(scripts.Script):
y_values = gr.Textbox(label="Y values", visible=False, lines=1)
draw_legend = gr.Checkbox(label='Draw legend', value=True)
return [x_type, x_values, y_type, y_values, draw_legend]
no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False)
def run(self, p, x_type, x_values, y_type, y_values, draw_legend):
return [x_type, x_values, y_type, y_values, draw_legend, no_fixed_seeds]
def run(self, p, x_type, x_values, y_type, y_values, draw_legend, no_fixed_seeds):
modules.processing.fix_seed(p)
p.batch_size = 1
@@ -169,7 +170,7 @@ class Script(scripts.Script):
end = int(mc.group(2))
num = int(mc.group(3)) if mc.group(3) is not None else 1
valslist_ext += [int(x) for x in np.linspace(start = start, stop = end, num = num).tolist()]
valslist_ext += [int(x) for x in np.linspace(start=start, stop=end, num=num).tolist()]
else:
valslist_ext.append(val)
@@ -191,7 +192,7 @@ class Script(scripts.Script):
end = float(mc.group(2))
num = int(mc.group(3)) if mc.group(3) is not None else 1
valslist_ext += np.linspace(start = start, stop = end, num = num).tolist()
valslist_ext += np.linspace(start=start, stop=end, num=num).tolist()
else:
valslist_ext.append(val)
@@ -207,6 +208,26 @@ class Script(scripts.Script):
y_opt = axis_options[y_type]
ys = process_axis(y_opt, y_values)
def fix_axis_seeds(axis_opt, axis_list):
if axis_opt.label == 'Seed':
return [int(random.randrange(4294967294)) if val is None or val == '' or val == -1 else val for val in axis_list]
else:
return axis_list
if not no_fixed_seeds:
xs = fix_axis_seeds(x_opt, xs)
ys = fix_axis_seeds(y_opt, ys)
if x_opt.label == 'Steps':
total_steps = sum(xs) * len(ys)
elif y_opt.label == 'Steps':
total_steps = sum(ys) * len(xs)
else:
total_steps = p.steps * len(xs) * len(ys)
print(f"X/Y plot will create {len(xs) * len(ys) * p.n_iter} images on a {len(xs)}x{len(ys)} grid. (Total steps to process: {total_steps * p.n_iter})")
shared.total_tqdm.updateTotal(total_steps * p.n_iter)
def cell(x, y):
pc = copy(p)
x_opt.apply(pc, x, xs)
Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

+2
View File
@@ -3,6 +3,8 @@
if not defined PYTHON (set PYTHON=python)
if not defined VENV_DIR (set VENV_DIR=venv)
set ERROR_REPORTING=FALSE
mkdir tmp 2>NUL
%PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt
+1
View File
@@ -50,6 +50,7 @@ def wrap_gradio_gpu_call(func):
shared.state.sampling_step = 0
shared.state.job_count = -1
shared.state.job_no = 0
shared.state.job_timestamp = shared.state.get_job_timestamp()
shared.state.current_latent = None
shared.state.current_image = None
shared.state.current_image_sampling_step = 0
+3
View File
@@ -41,6 +41,9 @@ then
venv_dir="venv"
fi
# Disable sentry logging
export ERROR_REPORTING=FALSE
# Do not reinstall existing pip packages on Debian/Ubuntu
export PIP_IGNORE_INSTALLED=0