Merge branch 'master' into directml

This commit is contained in:
Seunghoon Lee
2023-04-30 00:45:33 +09:00
40 changed files with 560 additions and 1120 deletions
+5
View File
@@ -24,6 +24,11 @@ venv
*.zip
*.rar
*.pyc
/*.bat
/*.sh
/*.txt
!webui.bat
!webui.sh
# all dynamic stuff
/repositories/**/*
+2 -4
View File
@@ -4,11 +4,11 @@
Stuff to be fixed...
- ClipSkip not updated on read gen info
- Run VAE with hires at 1280
- Transformers version
- Move Restart Server from WebUI to Launch and reload modules
- follow-up on `p.script_args`
- Follow-up on `p.script_args`
- Mdularize `cli` scripts
## Features
@@ -19,8 +19,6 @@ Stuff to be added...
- Create new GitHub hooks/actions for CI/CD
- Redo Extensions tab: see <https://vladmandic.github.io/sd-extension-manager/pages/extensions.html>
- Stream-load models as option for slow storage
- AMD optimizations
- Apple optimizations
## Investigate
+1
View File
@@ -1,3 +1,4 @@
mediapipe
colormap
invisible-watermark
filetype
+3 -2
View File
@@ -4,7 +4,9 @@
<div class='actions'>
<div class='additional'>
<ul>
<a style="font-size:0.5rem" href="#" title="replace preview image with currently selected in gallery" onclick={save_card_preview}>replace preview</a>
<li><a href="#" title="replace preview image with current selection" onclick={save_card_preview}>Replace preview</a></li>
<li><a href="#" title="replace preview description with current selection" onclick={save_card_description}>Replace description</a></li>
<li><a href="#" title="read description" onclick={read_card_description}>Read description</a></li>
</ul>
<span style="display:none" class='search_term'>{search_term}</span>
</div>
@@ -12,4 +14,3 @@
<span class='description'>{description}</span>
</div>
</div>
+4 -1
View File
@@ -81,7 +81,7 @@ svg.feather.feather-image, .feather .feather-image { display: none }
#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; }
#quicksettings > div, #quicksettings > fieldset { min-width: 26em; max-width: 26em; line-height: 2em; }
#refresh_sd_model_checkpoint { height: 40px; margin-left: -14px; background: #333333; box-shadow: none; }
#refresh_txt2img_styles, #refresh_img2img_styles, #open_folder_txt2img, #open_folder_img2img, #open_folder_extras, #footer, #style_pos_col, #style_neg_col, #roll_col, #save_zip_txt2img, #save_zip_img2img, #extras_upscaler_2, #extras_upscaler_2_visibility, #txt2img_res_switch_btn, #img2img_res_switch_btn, #txt2img_seed_resize_from_w, #txt2img_seed_resize_from_h, #txt2img_tiling { display: none; }
#refresh_txt2img_styles, #refresh_img2img_styles, #open_folder_txt2img, #open_folder_img2img, #open_folder_extras, #footer, #style_pos_col, #style_neg_col, #roll_col, #extras_upscaler_2, #extras_upscaler_2_visibility, #txt2img_res_switch_btn, #img2img_res_switch_btn, #txt2img_seed_resize_from_w, #txt2img_seed_resize_from_h, #txt2img_tiling { display: none; }
#save-animation { border-radius: 0 !important; margin-bottom: 16px; background-color: #111111; }
#script_list { padding: 4px; margin-top: 20px; margin-bottom: 20px; }
#settings > div.flex-wrap { width: 15em; }
@@ -103,6 +103,7 @@ svg.feather.feather-image, .feather .feather-image { display: none }
#txt2img_tools, #img2img_tools { margin-top: 54px; scale: 120%; margin-left: 26px; }
#txtimg_hr_finalres { max-width: 200px; }
#pnginfo_html2_info { margin-top: -18px; background-color: var(--input-background-fill); padding: var(--input-padding) }
#txt2img_extra_refresh, #txt2img_extra_close { height: 1.7em }
/* custom elements overrides */
#steps-animation, #controlnet { border-width: 0; }
@@ -317,4 +318,6 @@ svg.feather.feather-image, .feather .feather-image { display: none }
--button-small-text-size: var(--text-md);
--button-small-text-weight: 400;
--button-transition: none;
--size-9: 64px;
--size-14: 64px;
}
+14 -37
View File
@@ -5,17 +5,13 @@ function isValidImageList( files ) {
}
function dropReplaceImage( imgWrap, files ) {
if ( ! isValidImageList( files ) ) {
return;
}
if (!isValidImageList(files)) return;
const tmpFile = files[0];
imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click();
const callback = () => {
const fileInput = imgWrap.querySelector('input[type="file"]');
if ( fileInput ) {
if ( files.length === 0 ) {
if (fileInput) {
if (files.length === 0) {
files = new DataTransfer();
files.items.add(tmpFile);
fileInput.files = files.files;
@@ -26,7 +22,7 @@ function dropReplaceImage( imgWrap, files ) {
}
};
if ( imgWrap.closest('#pnginfo_image') ) {
if (imgWrap.closest('#pnginfo_image')) {
// special treatment for PNG Info tab, wait for fetch request to finish
const oldFetch = window.fetch;
window.fetch = async (input, options) => {
@@ -44,16 +40,14 @@ function dropReplaceImage( imgWrap, files ) {
return response;
};
} else {
window.requestAnimationFrame( () => callback() );
window.requestAnimationFrame(() => callback());
}
}
window.document.addEventListener('dragover', e => {
const target = e.composedPath()[0];
const imgWrap = target.closest('[data-testid="image"]');
if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) {
return;
}
if ( !imgWrap && target.placeholder && target.placeholder.indexOf("Prompt") == -1) return;
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
@@ -62,37 +56,20 @@ window.document.addEventListener('dragover', e => {
window.document.addEventListener('drop', e => {
const target = e.composedPath()[0];
if (!target.placeholder) return;
if (target.placeholder.indexOf("Prompt") == -1) {
return;
}
if (target.placeholder.indexOf("Prompt") == -1) return;
const imgWrap = target.closest('[data-testid="image"]');
if ( !imgWrap ) {
return;
}
if (!imgWrap) return;
e.stopPropagation();
e.preventDefault();
const files = e.dataTransfer.files;
dropReplaceImage( imgWrap, files );
dropReplaceImage(imgWrap, files);
});
window.addEventListener('paste', e => {
const files = e.clipboardData.files;
if ( ! isValidImageList( files ) ) {
return;
}
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 );
if ( ! isValidImageList( files ) ) return;
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);
});
+33
View File
@@ -5,12 +5,14 @@ function setupExtraNetworksForTab(tabname){
var tabs = gradioApp().querySelector('#'+tabname+'_extra_tabs > div')
var search = gradioApp().querySelector('#'+tabname+'_extra_search textarea')
var refresh = gradioApp().getElementById(tabname+'_extra_refresh')
var descriptInput = gradioApp().getElementById(tabname+ '_description_input')
var close = gradioApp().getElementById(tabname+'_extra_close')
search.classList.add('search')
tabs.appendChild(search)
tabs.appendChild(refresh)
tabs.appendChild(close)
tabs.appendChild(descriptInput)
search.addEventListener("input", function(evt){
searchTerm = search.value.toLowerCase()
@@ -97,6 +99,37 @@ function saveCardPreview(event, tabname, filename){
event.preventDefault()
}
function saveCardDescription(event, tabname, filename, descript){
var textarea = gradioApp().querySelector("#" + tabname + '_description_filename > label > textarea')
var button = gradioApp().getElementById(tabname + '_save_description')
var description = gradioApp().getElementById(tabname+ '_description_input')
textarea.value = filename
description.value=descript
updateInput(textarea)
button.click()
event.stopPropagation()
event.preventDefault()
}
function readCardDescription(event, tabname, filename, descript){
var textarea = gradioApp().querySelector("#" + tabname + '_description_filename > label > textarea')
var description_textarea = gradioApp().querySelector("#" + tabname+ '_description_input > label > textarea')
var button = gradioApp().getElementById(tabname + '_read_description')
textarea.value = filename
description_textarea.value = descript
updateInput(textarea)
updateInput(description_textarea)
button.click()
event.stopPropagation()
event.preventDefault()
}
function extraNetworksSearchButton(tabs_id, event){
searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea')
button = event.target
+4 -24
View File
@@ -1,52 +1,32 @@
// Monitors the gallery and sends a browser notification when the leading image is new.
let lastHeadImg = null;
let notificationButton = null;
const regExpTempImage = /(?<=\/|\\)tmp[\w\d]{8}\.png$/gm;
onUiUpdate(function(){
if(notificationButton == null){
notificationButton = gradioApp().getElementById('request_notifications')
if(notificationButton != null){
notificationButton.addEventListener('click', function (evt) {
Notification.requestPermission();
},true);
}
if (notificationButton != null) notificationButton.addEventListener('click', (evt) => Notification.requestPermission(), true);
}
const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img');
if (galleryPreviews == null) return;
const headImg = galleryPreviews[0]?.src;
if (headImg == null || headImg == lastHeadImg) return;
if (headImg.search(regExpTempImage) != -1) return;
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.
const imgs = new Set(Array.from(galleryPreviews).map(img => img.src));
const notification = new Notification(
'Stable Diffusion',
{
'Stable Diffusion', {
body: `Generated ${imgs.size > 1 ? imgs.size - opts.return_grid : 1} image${imgs.size > 1 ? 's' : ''}`,
icon: headImg,
image: headImg,
}
image: headImg }
);
notification.onclick = function(_){
notification.onclick = function(_) {
parent.focus();
this.close();
};
+23 -1
View File
@@ -338,6 +338,28 @@ function reconnect_ui() {
const atEnd = () => showSubmitButtons('txt2img', true)
requestProgress(task_id, el1, el2, atEnd, null, true)
}
sd_model = gradioApp().getElementById("setting_sd_model_checkpoint")
let loadingStarted = 0;
let loadingMonitor = 0;
const sd_model_callback = () => {
loading = sd_model.querySelector(".eta-bar")
if (!loading) {
loadingStarted = 0
clearInterval(loadingMonitor)
} else {
if (loadingStarted === 0) {
loadingStarted = Date.now();
loadingMonitor = setInterval(() => {
elapsed = Date.now() - loadingStarted;
console.log('Loading', elapsed)
if (elapsed > 3000 && loading) loading.style.display = 'none';
}, 5000);
}
}
};
const sd_model_observer = new MutationObserver(sd_model_callback);
sd_model_observer.observe(sd_model, { attributes: true, childList: true, subtree: true });
}
var start_check = setInterval(reconnect_ui, 50)
var start_check = setInterval(reconnect_ui, 50);
+1
View File
@@ -14,6 +14,7 @@ from rich import print # pylint: disable=redefined-builtin,wrong-import-order
commandline_args = os.environ.get('COMMANDLINE_ARGS', "")
sys.argv += shlex.split(commandline_args)
setup.add_args()
setup.extensions_preload(force=False)
setup.parse_args()
args, _ = modules.cmd_args.parser.parse_known_args()
+1 -1
View File
@@ -78,7 +78,7 @@ def compatibility_args(opts, args):
opts.use_old_emphasis_implementation = False
opts.use_old_karras_scheduler_sigmas = False
opts.no_dpmpp_sde_batch_determinism = False
opts.use_old_hires_fix_width_height = False
opts.lora_apply_to_outputs = False
parser.add_argument("--lora-dir", help=argparse.SUPPRESS, default=opts.lora_dir)
args = parser.parse_args()
+4 -48
View File
@@ -11,7 +11,7 @@ from modules import shared, ui_tempdir, script_callbacks
re_param_code = r'\s*([\w ]+):\s*("(?:\\"[^,]|\\"|\\|[^\"])+"|[^,]*)(?:,|$)'
re_param = re.compile(re_param_code)
re_imagesize = re.compile(r"^(\d+)x(\d+)$")
re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$")
re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") # pylint: disable=anomalous-backslash-in-string
type_of_gr_update = type(gr.update())
paste_fields = {}
@@ -102,7 +102,6 @@ def bind_buttons(buttons, send_image, send_generate_info):
for tabname, button in buttons.items():
source_text_component = send_generate_info if isinstance(send_generate_info, gr.components.Component) else None
source_tabname = send_generate_info if isinstance(send_generate_info, str) else None
register_paste_params_button(ParamBinding(paste_button=button, tabname=tabname, source_text_component=source_text_component, source_image_component=send_image, source_tabname=source_tabname))
@@ -116,7 +115,6 @@ def connect_paste_params_buttons():
destination_image_component = paste_fields[binding.tabname]["init_img"]
fields = paste_fields[binding.tabname]["fields"]
override_settings_component = binding.override_settings_component or paste_fields[binding.tabname]["override_settings_component"]
destination_width_component = next(iter([field for field, name in fields if name == "Size-1"] if fields else []), None)
destination_height_component = next(iter([field for field, name in fields if name == "Size-2"] if fields else []), None)
@@ -127,17 +125,14 @@ def connect_paste_params_buttons():
else:
func = send_image_and_dimensions if destination_width_component else lambda x: x
jsfunc = None
binding.paste_button.click(
fn=func,
_js=jsfunc,
inputs=[binding.source_image_component],
outputs=[destination_image_component, destination_width_component, destination_height_component] if destination_width_component else [destination_image_component],
)
if binding.source_text_component is not None and fields is not None:
connect_paste(binding.paste_button, fields, binding.source_text_component, override_settings_component, binding.tabname)
if binding.source_tabname is not None and fields is not None:
paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration'] + (["Seed"] if shared.opts.send_seed else []) + binding.paste_field_names
binding.paste_button.click(
@@ -145,7 +140,6 @@ def connect_paste_params_buttons():
inputs=[field for field, name in paste_fields[binding.source_tabname]["fields"] if name in paste_field_names],
outputs=[field for field, name in fields if name in paste_field_names],
)
binding.paste_button.click(
fn=None,
_js=f"switch_to_{binding.tabname}",
@@ -159,14 +153,12 @@ def send_image_and_dimensions(x):
img = x
else:
img = image_from_url_text(x)
if shared.opts.send_size and isinstance(img, Image.Image):
w = img.width
h = img.height
else:
w = gr.update()
h = gr.update()
return img, w, h
@@ -238,33 +230,25 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
returns a dict with field values
"""
res = {}
prompt = ""
negative_prompt = ""
done_with_prompt = False
*lines, lastline = x.strip().split("\n")
if len(re_param.findall(lastline)) < 3:
lines.append(lastline)
lastline = ''
for _i, line in enumerate(lines):
line = line.strip()
if line.startswith("Negative prompt:"):
done_with_prompt = True
line = line[16:].strip()
if done_with_prompt:
negative_prompt += ("" if negative_prompt == "" else "\n") + line
else:
prompt += ("" if prompt == "" else "\n") + line
res["Prompt"] = prompt
res["Negative prompt"] = negative_prompt
for k, v in re_param.findall(lastline):
v = v[1:-1] if v[0] == '"' and v[-1] == '"' else v
m = re_imagesize.match(v)
@@ -273,31 +257,24 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
res[k+"-2"] = m.group(2)
else:
res[k] = v
# Missing CLIP skip means it was set to 1 (the default)
if "Clip skip" not in res:
res["Clip skip"] = "1"
hypernet = res.get("Hypernet", None)
if hypernet is not None:
res["Prompt"] += f"""<hypernet:{hypernet}:{res.get("Hypernet strength", "1.0")}>"""
if "Hires resize-1" not in res:
res["Hires resize-1"] = 0
res["Hires resize-2"] = 0
# Infer additional override settings for token merging
token_merging_ratio = res.get("Token merging ratio", None)
token_merging_ratio_hr = res.get("Token merging ratio hr", None)
if token_merging_ratio is not None or token_merging_ratio_hr is not None:
res["Token merging"] = 'True'
if token_merging_ratio is None:
res["Token merging hr only"] = 'True'
else:
res["Token merging hr only"] = 'False'
if res.get("Token merging random", None) is None:
res["Token merging random"] = 'False'
if res.get("Token merging merge attention", None) is None:
@@ -312,14 +289,12 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
res["Token merging stride y"] = '2'
restore_old_hires_fix_params(res)
return res
settings_map = {}
infotext_to_setting_name_mapping = [
('Clip skip', 'CLIP_stop_at_last_layers', ),
('Conditional mask weight', 'inpainting_mask_weight'),
@@ -349,34 +324,29 @@ infotext_to_setting_name_mapping = [
def create_override_settings_dict(text_pairs):
"""creates processing's override_settings parameters from gradio's multiselect
Example input:
['Clip skip: 2', 'Model hash: e6e99610c4', 'ENSD: 31337']
Example output:
{'CLIP_stop_at_last_layers': 2, 'sd_model_checkpoint': 'e6e99610c4', 'eta_noise_seed_delta': 31337}
"""
res = {}
params = {}
for pair in text_pairs:
k, v = pair.split(":", maxsplit=1)
params[k] = v.strip()
for param_name, setting_name in infotext_to_setting_name_mapping:
value = params.get(param_name, None)
if value is None:
continue
res[setting_name] = shared.opts.cast_value(setting_name, value)
return res
def connect_paste(button, paste_fields, input_comp, override_settings_component, tabname):
def connect_paste(button, paste_fields, input_comp, override_settings_component, tabname): # pylint: disable=redefined-outer-name
def paste_func(prompt):
if 'Negative prompt' not in prompt and 'Steps' not in prompt:
prompt = None
if not prompt and not shared.cmd_opts.hide_ui_dir_config:
filename = os.path.join(data_path, "params.txt")
if os.path.exists(filename):
@@ -384,17 +354,14 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
prompt = file.read()
else:
prompt = ''
params = parse_generation_parameters(prompt)
script_callbacks.infotext_pasted_callback(prompt, params)
res = []
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):
@@ -402,42 +369,31 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
else:
try:
valtype = type(output.value)
if valtype == bool and v == "False":
val = False
else:
val = valtype(v)
res.append(gr.update(value=val))
except Exception:
res.append(gr.update())
return res
if override_settings_component is not None:
def paste_settings(params):
vals = {}
for param_name, setting_name in infotext_to_setting_name_mapping:
v = params.get(param_name, None)
if v is None:
continue
if setting_name == "sd_model_checkpoint" and shared.opts.disable_weights_auto_swap:
continue
v = shared.opts.cast_value(setting_name, v)
current_value = getattr(shared.opts, setting_name, None)
if v == current_value:
continue
vals[param_name] = v
vals_pairs = [f"{k}: {v}" for k, v in vals.items()]
return gr.Dropdown.update(value=vals_pairs, choices=vals_pairs, visible=len(vals_pairs) > 0)
paste_fields = paste_fields + [(override_settings_component, paste_settings)]
button.click(
@@ -1,9 +1,11 @@
"""SAMPLING ONLY."""
import numpy as np
import torch
from .uni_pc import NoiseScheduleVP, model_wrapper, UniPC
from modules import shared, devices
from ldm.modules.diffusionmodules.util import extract_into_tensor
class UniPCSampler(object):
@@ -15,6 +17,103 @@ class UniPCSampler(object):
self.after_sample = None
self.register_buffer('alphas_cumprod', to_torch(model.alphas_cumprod))
def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True):
# persist steps so we can eventually find denoising strength
self.inflated_steps = ddim_num_steps
@torch.no_grad()
def stochastic_encode(self, x0, t, use_original_steps=False, noise=None):
if noise is None:
noise = torch.randn_like(x0)
# first time we have all the info to get the real parameters from the ui
# value from the hires steps slider:
num_inference_steps = t[0] + 1
# (num_inference_steps // denoising_strength):
inflated_steps = self.inflated_steps
# not exact:
self.denoising_strength = num_inference_steps/inflated_steps
# values used for timesteps that generate noise in diffusers repo
init_timestep = min(
int(num_inference_steps * self.denoising_strength),
num_inference_steps,
)
t_start = max(num_inference_steps - init_timestep, 0)
# actual number of steps we'll run
self.steps = max(
num_inference_steps - init_timestep,
shared.opts.uni_pc_order+1,
)
scheduler_timesteps = np.linspace(
0,
self.model.num_timesteps-1,
num_inference_steps + 1,
).round()[::-1][:-1].copy().astype(np.int64)
_, unique_indices = np.unique(scheduler_timesteps, return_index=True)
scheduler_timesteps = scheduler_timesteps[np.sort(unique_indices)]
scheduler_timesteps = torch.from_numpy(scheduler_timesteps).to(t.device)
sample_timesteps = scheduler_timesteps[t_start:]
latent_timestep = sample_timesteps[:1].repeat(x0.shape[0])
alphas_cumprod = self.alphas_cumprod
sqrt_alpha_prod = alphas_cumprod[latent_timestep] ** 0.5
sqrt_alpha_prod = sqrt_alpha_prod.flatten()
while len(sqrt_alpha_prod.shape) < len(x0.shape):
sqrt_alpha_prod = sqrt_alpha_prod.unsqueeze(-1)
sqrt_one_minus_alpha_prod = (1 - alphas_cumprod[latent_timestep]) ** 0.5
sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten()
while len(sqrt_one_minus_alpha_prod.shape) < len(x0.shape):
sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.unsqueeze(-1)
return (sqrt_alpha_prod * x0 + sqrt_one_minus_alpha_prod * noise)
def decode(self, x_latent, conditioning, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None,
use_original_steps=False, callback=None):
#print(f'steps {self.steps} denoising {self.denoising_strength}')
noise_schedule = NoiseScheduleVP("discrete", alphas_cumprod=self.alphas_cumprod)
# same as in .sample(), i guess
model_type = "v" if self.model.parameterization == "v" else "noise"
model_fn = model_wrapper(
lambda x, t, c: self.model.apply_model(x, t, c),
noise_schedule,
model_type=model_type,
guidance_type="classifier-free",
#condition=conditioning,
#unconditional_condition=unconditional_conditioning,
guidance_scale=unconditional_guidance_scale,
)
self.uni_pc = UniPC(
model_fn,
noise_schedule,
predict_x0=True,
thresholding=False,
variant=shared.opts.uni_pc_variant,
condition=conditioning,
unconditional_condition=unconditional_conditioning,
before_sample=self.before_sample,
after_sample=self.after_sample,
after_update=self.after_update,
)
return self.uni_pc.sample(
x_latent,
steps=self.steps,
skip_type=shared.opts.uni_pc_skip_type,
method="multistep",
order=shared.opts.uni_pc_order,
lower_order_final=shared.opts.uni_pc_lower_order_final,
t_start=self.denoising_strength,
)
def register_buffer(self, name, attr):
if type(attr) == torch.Tensor:
if attr.device != devices.device:
+1 -1
View File
@@ -15,6 +15,6 @@ parser_pre.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.
parser_pre.add_argument("--models-dir", type=str, default="models", help="base path where all models are stored",)
cmd_opts_pre = parser_pre.parse_known_args()[0]
data_path = cmd_opts_pre.data_dir
models_path = os.path.join(data_path, cmd_opts_pre.models_dir)
models_path = cmd_opts_pre.models_dir if os.path.isabs(cmd_opts_pre.models_dir) else os.path.join(data_path, cmd_opts_pre.models_dir)
extensions_dir = os.path.join(data_path, "extensions")
extensions_builtin_dir = os.path.join(script_path, "extensions-builtin")
+11 -26
View File
@@ -111,7 +111,7 @@ class StableDiffusionProcessing:
"""
The first set of paramaters: sd_models -> do_not_reload_embeddings represent the minimum required to create a StableDiffusionProcessing
"""
def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None): # pylint: disable=unused-argument
def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 20, cfg_scale: float = 6.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None): # pylint: disable=unused-argument
if sampler_index is not None:
print("sampler_index argument for StableDiffusionProcessing does not do anything; use sampler_name", file=sys.stderr)
@@ -165,6 +165,7 @@ class StableDiffusionProcessing:
self.all_negative_prompts = None
self.all_seeds = None
self.all_subseeds = None
self.clip_skip = opts.CLIP_stop_at_last_layers
self.iteration = 0
@property
@@ -302,8 +303,7 @@ class Processed:
self.index_of_first_image = index_of_first_image
self.styles = p.styles
self.job_timestamp = state.job_timestamp
self.clip_skip = opts.CLIP_stop_at_last_layers
self.clip_skip = p.clip_skip
self.eta = p.eta
self.ddim_discretize = p.ddim_discretize
self.s_churn = p.s_churn
@@ -457,11 +457,9 @@ def fix_seed(p):
p.subseed = get_fixed_seed(p.subseed)
def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument
def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument
index = position_in_batch + iteration * p.batch_size
clip_skip = getattr(p, 'clip_skip', opts.CLIP_stop_at_last_layers)
generation_params = {
"Steps": p.steps,
"Sampler": p.sampler_name,
@@ -478,7 +476,7 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter
"Seed resize from": (None if p.seed_resize_from_w == 0 or p.seed_resize_from_h == 0 else f"{p.seed_resize_from_w}x{p.seed_resize_from_h}"),
"Denoising strength": getattr(p, 'denoising_strength', None),
"Conditional mask weight": getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None,
"Clip skip": None if clip_skip <= 1 else clip_skip,
"Clip skip": p.clip_skip,
"ENSD": None if opts.eta_noise_seed_delta == 0 else opts.eta_noise_seed_delta,
"Token merging ratio": None if not (opts.token_merging or cmd_opts.token_merging) or opts.token_merging_hr_only else opts.token_merging_ratio,
"Token merging ratio hr": None if not (opts.token_merging or cmd_opts.token_merging) else opts.token_merging_ratio_hr,
@@ -972,8 +970,9 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
shared.state.nextjob()
img2img_sampler_name = self.sampler_name
if self.sampler_name in ['PLMS', 'UniPC']: # PLMS/UniPC do not support img2img so we just silently switch to DDIM
img2img_sampler_name = 'DDIM'
force_latent_upscaler = shared.opts.data.get('xyz_fallback_sampler')
if self.sampler_name in ['PLMS'] or (force_latent_upscaler is not None and force_latent_upscaler != 'None'):
img2img_sampler_name = force_latent_upscaler or shared.opts.fallback_sampler # PLMS does not support img2img, use fallback instead
self.sampler = sd_samplers.create_sampler(img2img_sampler_name, self.sd_model)
samples = samples[:, :, self.truncate_y//2:samples.shape[2]-(self.truncate_y+1)//2, self.truncate_x//2:samples.shape[3]-(self.truncate_x+1)//2]
@@ -1026,27 +1025,24 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
self.image_conditioning = None
def init(self, all_prompts, all_seeds, all_subseeds):
force_latent_upscaler = shared.opts.data.get('xyz_fallback_sampler')
if self.sampler_name in ['PLMS'] or (force_latent_upscaler is not None and force_latent_upscaler != 'None'):
self.sampler_name = force_latent_upscaler or shared.opts.fallback_sampler # PLMS does not support img2img, use fallback instead
self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model)
crop_region = None
image_mask = self.image_mask
if image_mask is not None:
image_mask = image_mask.convert('L')
if self.inpainting_mask_invert:
image_mask = ImageOps.invert(image_mask)
if self.mask_blur > 0:
image_mask = image_mask.filter(ImageFilter.GaussianBlur(self.mask_blur))
if self.inpaint_full_res:
self.mask_for_overlay = image_mask
mask = image_mask.convert('L')
crop_region = masking.get_crop_region(np.array(mask), self.inpaint_full_res_padding)
crop_region = masking.expand_crop_region(crop_region, self.width, self.height, mask.width, mask.height)
x1, y1, x2, y2 = crop_region
mask = mask.crop(crop_region)
image_mask = images.resize_image(2, mask, self.width, self.height)
self.paste_to = (x1, y1, x2-x1, y2-y1)
@@ -1055,42 +1051,31 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
np_mask = np.array(image_mask)
np_mask = np.clip((np_mask.astype(np.float32)) * 2, 0, 255).astype(np.uint8)
self.mask_for_overlay = Image.fromarray(np_mask)
self.overlay_images = []
latent_mask = self.latent_mask if self.latent_mask is not None else image_mask
add_color_corrections = opts.img2img_color_correction and self.color_corrections is None
if add_color_corrections:
self.color_corrections = []
imgs = []
for img in self.init_images:
image = images.flatten(img, opts.img2img_background_color)
if crop_region is None and self.resize_mode != 3:
image = images.resize_image(self.resize_mode, image, self.width, self.height)
if image_mask is not None:
image_masked = Image.new('RGBa', (image.width, image.height))
image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L')))
self.overlay_images.append(image_masked.convert('RGBA'))
# crop_region is not None if we are doing inpaint full res
if crop_region is not None:
image = image.crop(crop_region)
image = images.resize_image(2, image, self.width, self.height)
if image_mask is not None:
if self.inpainting_fill != 1:
image = masking.fill(image, latent_mask)
if add_color_corrections:
self.color_corrections.append(setup_color_correction(image))
image = np.array(image).astype(np.float32) / 255.0
image = np.moveaxis(image, 2, 0)
imgs.append(image)
if len(imgs) == 1:
+5 -6
View File
@@ -13,7 +13,7 @@ import modules.errors as errors
class UpscalerRealESRGAN(Upscaler):
def __init__(self, path):
self.name = "RealESRGAN"
self.user_path = path
self.model_path = path
super().__init__()
try:
from basicsr.archs.rrdbnet_arch import RRDBNet
@@ -31,7 +31,7 @@ class UpscalerRealESRGAN(Upscaler):
self.enable = False
self.scalers = []
def do_upscale(self, img, path):
def do_upscale(self, img, selected_model):
if not self.enable:
return img
@@ -41,9 +41,9 @@ class UpscalerRealESRGAN(Upscaler):
print("Error importing Real-ESRGAN:", file=sys.stderr)
return img
info = self.load_model(path)
info = self.load_model(selected_model)
if not os.path.exists(info.local_data_path):
print("Unable to load RealESRGAN model: %s" % info.name)
print(f"Unable to load RealESRGAN model: {info.name}")
return img
upsampler = RealESRGANer(
@@ -68,7 +68,6 @@ class UpscalerRealESRGAN(Upscaler):
if info is None:
print(f"Unable to find model info: {path}")
return None
info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_path, progress=True)
return info
except Exception as e:
@@ -128,6 +127,6 @@ def get_realesrgan_models(scaler):
),
]
return models
except Exception as e:
except Exception:
print("Error creating Real-ESRGAN models list", file=sys.stderr)
return []
+23 -109
View File
@@ -2,7 +2,6 @@ import os
import re
import sys
from collections import namedtuple
from rich import print # pylint: disable=redefined-builtin
import gradio as gr
from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors
@@ -19,7 +18,6 @@ class Script:
args_from = None
args_to = None
alwayson = False
is_txt2img = False
is_img2img = False
@@ -38,7 +36,6 @@ class Script:
def title(self):
"""this function should return the title of the script. This is what will be displayed in the dropdown menu."""
raise NotImplementedError()
def ui(self, is_img2img):
@@ -46,19 +43,16 @@ class Script:
The return value should be an array of all components that are used in processing.
Values of those returned components will be passed to run() and process() functions.
"""
pass # pylint: disable=unnecessary-pass
pass
def show(self, is_img2img):
def show(self, is_img2img): # pylint: disable=unused-argument
"""
is_img2img is True if this function is called for the img2img interface, and Fasle otherwise
This function should return:
- False if the script should not be shown in UI at all
- True if the script should be shown in UI if it's selected in the scripts dropdown
- script.AlwaysVisible if the script should be shown in UI at all times
"""
return True
def run(self, p, *args):
@@ -66,13 +60,10 @@ class Script:
This function is called if the script has been selected in the script dropdown.
It must do all processing and return the Processed object with results, same as
one returned by processing.process_images.
Usually the processing is done by calling the processing.process_images function.
args contains all values returned by components from ui()
"""
pass
pass # pylint: disable=unnecessary-pass
def process(self, p, *args):
"""
@@ -80,61 +71,52 @@ class Script:
You can modify the processing object (p) here, inject hooks, etc.
args contains all values returned by components from ui()
"""
pass
pass # pylint: disable=unnecessary-pass
def before_process_batch(self, p, *args, **kwargs):
"""
Called before extra networks are parsed from the prompt, so you can add
new extra network keywords to the prompt with this callback.
**kwargs will have those items:
- batch_number - index of current batch, from 0 to number of batches-1
- prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things
- seeds - list of seeds for current batch
- subseeds - list of subseeds for current batch
"""
pass
pass # pylint: disable=unnecessary-pass
def process_batch(self, p, *args, **kwargs):
"""
Same as process(), but called for every batch.
**kwargs will have those items:
- batch_number - index of current batch, from 0 to number of batches-1
- prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things
- seeds - list of seeds for current batch
- subseeds - list of subseeds for current batch
"""
pass
pass # pylint: disable=unnecessary-pass
def postprocess_batch(self, p, *args, **kwargs):
"""
Same as process_batch(), but called for every batch after it has been generated.
**kwargs will have same items as process_batch, and also:
- batch_number - index of current batch, from 0 to number of batches-1
- images - torch tensor with all generated images, with values ranging from 0 to 1;
"""
pass
pass # pylint: disable=unnecessary-pass
def postprocess_image(self, p, pp: PostprocessImageArgs, *args):
"""
Called for every image after it has been generated.
"""
pass
pass # pylint: disable=unnecessary-pass
def postprocess(self, p, processed, *args):
"""
This function is called after processing ends for AlwaysVisible scripts.
args contains all values returned by components from ui()
"""
pass
pass # pylint: disable=unnecessary-pass
def before_component(self, component, **kwargs):
"""
@@ -143,15 +125,13 @@ class Script:
This can be useful to inject your own components somewhere in the middle of vanilla UI.
You can return created components in the ui() function to add them to the list of arguments for your processing functions
"""
pass
pass # pylint: disable=unnecessary-pass
def after_component(self, component, **kwargs):
"""
Called after a component is created. Same as above.
"""
pass
pass # pylint: disable=unnecessary-pass
def describe(self):
"""unused"""
@@ -159,11 +139,9 @@ class Script:
def elem_id(self, item_id):
"""helper function to generate id for a HTML element, constructs final id out of script name, tab and user-supplied item_id"""
need_tabname = self.show(True) == self.show(False)
tabname = ('img2img' if self.is_img2img else 'txt2txt') + "_" if need_tabname else ""
title = re.sub(r'[^a-z_0-9]', '', re.sub(r'\s', '_', self.title().lower()))
return f'script_{tabname}{title}_{item_id}'
@@ -179,7 +157,6 @@ def basedir():
ScriptFile = namedtuple("ScriptFile", ["basedir", "filename", "path", "priority"])
scripts_data = []
postprocessing_scripts_data = []
ScriptClassData = namedtuple("ScriptClassData", ["script_class", "path", "basedir", "module"])
@@ -187,16 +164,13 @@ ScriptClassData = namedtuple("ScriptClassData", ["script_class", "path", "basedi
def list_scripts(scriptdirname, extension):
tmp_list = []
base = os.path.join(paths.script_path, scriptdirname)
if os.path.exists(base):
for filename in sorted(os.listdir(base)):
tmp_list.append(ScriptFile(paths.script_path, filename, os.path.join(base, filename), '50'))
for ext in extensions.active():
tmp_list += ext.list_files(scriptdirname, extension)
scripts_list = []
priority_list = []
for script in tmp_list:
if os.path.splitext(script.path)[1].lower() == extension and os.path.isfile(script.path):
if script.basedir == paths.script_path:
@@ -214,25 +188,20 @@ def list_scripts(scriptdirname, extension):
priority = priority + str(f.read().strip())
else:
priority = priority + script.priority
scripts_list.append(ScriptFile(script.basedir, script.filename, script.path, priority))
priority_sort = sorted(scripts_list, key=lambda item: item.priority + item.path.lower(), reverse=False)
priority_list.append(ScriptFile(script.basedir, script.filename, script.path, priority))
priority_sort = sorted(priority_list, key=lambda item: item.priority + item.path.lower(), reverse=False)
return priority_sort
def list_files_with_name(filename):
res = []
dirs = [paths.script_path] + [ext.path for ext in extensions.active()]
for dirpath in dirs:
if not os.path.isdir(dirpath):
continue
path = os.path.join(dirpath, filename)
if os.path.isfile(path):
res.append(path)
return res
@@ -241,16 +210,13 @@ def load_scripts():
scripts_data.clear()
postprocessing_scripts_data.clear()
script_callbacks.clear_callbacks()
scripts_list = list_scripts("scripts", ".py")
syspath = sys.path
def register_scripts_from_module(module):
for _key, script_class in module.__dict__.items():
if type(script_class) != type:
continue
if issubclass(script_class, Script):
scripts_data.append(ScriptClassData(script_class, scriptfile.path, scriptfile.basedir, module))
elif issubclass(script_class, scripts_postprocessing.ScriptPostprocessing):
@@ -276,7 +242,6 @@ def wrap_call(func, filename, funcname, *args, default=None, **kwargs):
return res
except Exception as e:
errors.display(e, f'Calling script: {filename}/{funcname}')
return default
@@ -288,6 +253,7 @@ class ScriptRunner:
self.titles = []
self.infotext_fields = []
self.paste_field_names = []
self.script_load_ctr = 0
def initialize_scripts(self, is_img2img):
from modules import scripts_auto_postprocessing
@@ -295,50 +261,39 @@ class ScriptRunner:
self.scripts.clear()
self.alwayson_scripts.clear()
self.selectable_scripts.clear()
auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data()
for script_class, path, basedir, script_module in auto_processing_scripts + scripts_data:
for script_class, path, _basedir, _script_module in auto_processing_scripts + scripts_data:
script = script_class()
script.filename = path
script.is_txt2img = not is_img2img
script.is_img2img = is_img2img
visibility = script.show(script.is_img2img)
if visibility == AlwaysVisible:
self.scripts.append(script)
self.alwayson_scripts.append(script)
script.alwayson = True
elif visibility:
self.scripts.append(script)
self.selectable_scripts.append(script)
def setup_ui(self):
self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts]
inputs = [None]
inputs_alwayson = [True]
def create_script_ui(script, inputs, inputs_alwayson):
script.args_from = len(inputs)
script.args_to = len(inputs)
controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img)
if controls is None:
return
for control in controls:
control.custom_script_source = os.path.basename(script.filename)
if script.infotext_fields is not None:
self.infotext_fields += script.infotext_fields
if script.paste_field_names is not None:
self.paste_field_names += script.paste_field_names
inputs += controls
inputs_alwayson += [script.alwayson for _ in controls]
script.args_to = len(inputs)
@@ -348,39 +303,27 @@ class ScriptRunner:
create_script_ui(script, inputs, inputs_alwayson)
script.group = group
dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index")
inputs[0] = dropdown
for script in self.selectable_scripts:
with gr.Group(visible=False) as group:
create_script_ui(script, inputs, inputs_alwayson)
script.group = group
def select_script(script_index):
selected_script = self.selectable_scripts[script_index - 1] if script_index>0 else None
return [gr.update(visible=selected_script == s) for s in self.selectable_scripts]
def init_field(title):
"""called when an initial value is set from ui-config.json to show script's UI components"""
if title == 'None':
return
script_index = self.titles.index(title)
self.selectable_scripts[script_index].group.visible = True
dropdown.init_field = init_field
dropdown.change(fn=select_script, inputs=[dropdown], outputs=[script.group for script in self.selectable_scripts])
dropdown.change(
fn=select_script,
inputs=[dropdown],
outputs=[script.group for script in self.selectable_scripts]
)
self.script_load_ctr = 0
def onload_script_visibility(params):
title = params.get('Script', None)
if title:
@@ -393,34 +336,25 @@ class ScriptRunner:
self.infotext_fields.append( (dropdown, lambda x: gr.update(value=x.get('Script', 'None'))) )
self.infotext_fields.extend( [(script.group, onload_script_visibility) for script in self.selectable_scripts] )
return inputs
def run(self, p, *args):
script_index = args[0]
if script_index == 0:
return None
script = self.selectable_scripts[script_index-1]
if script is None:
return None
script_args = args[script.args_from:script.args_to]
processed = script.run(p, *script_args)
shared.total_tqdm.clear()
return processed
def process(self, p):
def process(self, p, **kwargs):
for script in self.alwayson_scripts:
try:
if p.script_args[0] == 'enabled':
return
script_args = p.script_args[script.args_from:script.args_to]
script.process(p, *script_args)
script.process(p, *script_args, **kwargs)
except Exception as e:
errors.display(e, f'Running script process: {script.filename}')
@@ -435,8 +369,6 @@ class ScriptRunner:
def process_batch(self, p, **kwargs):
for script in self.alwayson_scripts:
try:
if p.script_args[0] == 'enabled':
return
script_args = p.script_args[script.args_from:script.args_to]
script.process_batch(p, *script_args, **kwargs)
except Exception as e:
@@ -445,8 +377,6 @@ class ScriptRunner:
def postprocess(self, p, processed):
for script in self.alwayson_scripts:
try:
if p.script_args[0] == 'enabled':
return
script_args = p.script_args[script.args_from:script.args_to]
script.postprocess(p, processed, *script_args)
except Exception as e:
@@ -455,8 +385,6 @@ class ScriptRunner:
def postprocess_batch(self, p, images, **kwargs):
for script in self.alwayson_scripts:
try:
if p.script_args[0] == 'enabled':
return
script_args = p.script_args[script.args_from:script.args_to]
script.postprocess_batch(p, *script_args, images=images, **kwargs)
except Exception as e:
@@ -489,13 +417,11 @@ class ScriptRunner:
args_from = script.args_from
args_to = script.args_to
filename = script.filename
module = cache.get(filename, None)
if module is None:
module = script_loading.load_module(script.filename)
cache[filename] = module
for key, script_class in module.__dict__.items():
for _key, script_class in module.__dict__.items():
if type(script_class) == type and issubclass(script_class, Script):
self.scripts[si] = script_class()
self.scripts[si].filename = filename
@@ -516,10 +442,8 @@ def reload_script_body_only():
def reload_scripts():
global scripts_txt2img, scripts_img2img, scripts_postproc
global scripts_txt2img, scripts_img2img, scripts_postproc # pylint: disable=global-statement
load_scripts()
scripts_txt2img = ScriptRunner()
scripts_img2img = ScriptRunner()
scripts_postproc = scripts_postprocessing.ScriptPostprocessingRunner()
@@ -535,27 +459,19 @@ def add_classes_to_gradio_component(comp):
if elem_classes is None:
elem_classes = []
comp.elem_classes = ["gradio-" + comp.get_block_name(), *(elem_classes)]
if getattr(comp, 'multiselect', False):
comp.elem_classes.append('multiselect')
def IOComponent_init(self, *args, **kwargs):
if scripts_current is not None:
scripts_current.before_component(self, **kwargs)
script_callbacks.before_component_callback(self, **kwargs)
res = original_IOComponent_init(self, *args, **kwargs)
res = original_IOComponent_init(self, *args, **kwargs) # pylint: disable=assignment-from-no-return
add_classes_to_gradio_component(self)
script_callbacks.after_component_callback(self, **kwargs)
if scripts_current is not None:
scripts_current.after_component(self, **kwargs)
return res
@@ -564,10 +480,8 @@ gr.components.IOComponent.__init__ = IOComponent_init
def BlockContext_init(self, *args, **kwargs):
res = original_BlockContext_init(self, *args, **kwargs)
res = original_BlockContext_init(self, *args, **kwargs) # pylint: disable=assignment-from-no-return
add_classes_to_gradio_component(self)
return res
-4
View File
@@ -27,15 +27,11 @@ class ScriptPostprocessingForMainUI(scripts.Script):
def create_auto_preprocessing_script_data():
from modules import scripts
res = []
for name in shared.opts.postprocessing_enable_in_main_ui:
script = next(iter([x for x in scripts.postprocessing_scripts_data if x.script_class.name == name]), None)
if script is None:
continue
constructor = lambda s=script: ScriptPostprocessingForMainUI(s.script_class())
res.append(scripts.ScriptClassData(script_class=constructor, path=script.path, basedir=script.basedir, module=script.module))
+4 -2
View File
@@ -205,7 +205,7 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
is when you do prompt editing: "a picture of a [cat:dog:0.4] eating ice cream"
"""
batch_chunks, token_count = self.process_texts(texts)
batch_chunks, _token_count = self.process_texts(texts)
used_embeddings = {}
chunk_count = max([len(x) for x in batch_chunks])
@@ -219,7 +219,7 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
self.hijack.fixes = [x.fixes for x in batch_chunk]
for fixes in self.hijack.fixes:
for position, embedding in fixes:
for _position, embedding in fixes:
used_embeddings[embedding.name] = embedding
z = self.process_tokens(tokens, multipliers)
@@ -295,6 +295,8 @@ class FrozenCLIPEmbedderWithCustomWords(FrozenCLIPEmbedderWithCustomWordsBase):
return tokenized
def encode_with_transformers(self, tokens):
if opts.CLIP_stop_at_last_layers is None:
opts.CLIP_stop_at_last_layers = 1
outputs = self.wrapped.transformer(input_ids=tokens, output_hidden_states=-opts.CLIP_stop_at_last_layers)
if opts.CLIP_stop_at_last_layers > 1:
+1 -1
View File
@@ -100,7 +100,7 @@ def resolve_vae(checkpoint_file):
vae_near_checkpoint = find_vae_near_checkpoint(checkpoint_file)
if vae_near_checkpoint is not None and (shared.opts.sd_vae_as_default):
return vae_near_checkpoint, 'near checkpoint'
if is_automatic:
for named_vae_location in [os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.pt"), os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.ckpt"), os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.safetensors")]:
if os.path.isfile(named_vae_location):
+37 -76
View File
@@ -1,12 +1,10 @@
import datetime
import json
import os
import sys
import time
import json
import datetime
import gradio as gr
import tqdm
import modules.interrogate
import modules.memmon
import modules.styles
@@ -20,7 +18,7 @@ errors.install(gr)
demo: gr.Blocks = None
log = setup_log
parser = cmd_args.parser
url = 'https://github.com/vladmandic/automatic'
if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None:
cmd_opts = parser.parse_args()
else:
@@ -52,10 +50,7 @@ ui_reorder_categories = [
]
cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = \
(devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
device = devices.device
is_device_dml = False
sd_upscalers = []
@@ -102,7 +97,6 @@ class State:
def nextjob(self):
if opts.live_previews_enable and opts.show_progress_every_n_steps == -1:
self.do_set_current_image()
self.job_no += 1
self.sampling_step = 0
self.current_image_sampling_step = 0
@@ -134,13 +128,11 @@ class State:
self.interrupted = False
self.textinfo = None
self.time_start = time.time()
devices.torch_gc()
def end(self):
self.job = ""
self.job_count = 0
devices.torch_gc()
def set_current_image(self):
@@ -167,9 +159,7 @@ class State:
state = State()
state.server_start = time.time()
interrogator = modules.interrogate.InterrogateModels("interrogate")
face_restorers = []
class OptionInfo:
@@ -186,7 +176,6 @@ class OptionInfo:
def options_section(section_identifier, options_dict):
for _k, v in options_dict.items():
v.section = section_identifier
return options_dict
@@ -232,26 +221,25 @@ def refresh_themes():
hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
tab_names = []
options_templates = {}
default_checkpoint = list_checkpoint_tiles()[0] if len(list_checkpoint_tiles()) > 0 else "model.ckpt"
options_templates.update(options_section(('sd', "Stable Diffusion"), {
"sd_model_checkpoint": OptionInfo(default_checkpoint, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints),
"sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_vae_checkpoint_cache": OptionInfo(0, "VAE Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_vae": OptionInfo("Automatic", "SD VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list),
"sd_vae_as_default": OptionInfo(True, "Ignore selected VAE for stable diffusion checkpoints that have their own .vae.pt next to them"),
"sd_checkpoint_cache": OptionInfo(0, "Model checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_vae_checkpoint_cache": OptionInfo(0, "VAE checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_vae": OptionInfo("Automatic", "Select VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list),
"sd_vae_as_default": OptionInfo(True, "Ignore selected VAE for stable diffusion checkpoints that have their own .vae.pt next to them", gr.Checkbox, {"visible": False}),
"inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
"initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.5, "maximum": 1.5, "step": 0.01}),
"img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."),
"img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."),
"img2img_fix_steps": OptionInfo(False, "For image processing do exactly the amount of steps as specified."),
"img2img_background_color": OptionInfo("#ffffff", "With img2img, fill image's transparent parts with this color.", ui_components.FormColorPicker, {}),
"enable_quantization": OptionInfo(True, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds."),
"enable_emphasis": OptionInfo(True, "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention"),
"enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"),
"enable_emphasis": OptionInfo(True, "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention", gr.Checkbox, {"visible": False}),
"enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image", gr.Checkbox, {"visible": False}),
"comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
"CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}),
"CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1, "visible": False}),
"upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"),
"cross_attention_optimization": OptionInfo("Sub-quadratic" if is_device_dml else "Scaled-Dot-Product", "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
"cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}),
@@ -259,6 +247,9 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), {
"sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attentionkv chunk size for the sub-quadratic cross-attention layer optimization to use", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
"sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention percentage of VRAM chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
"always_batch_cond_uncond": OptionInfo(False, "Disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram"),
"multiple_tqdm": OptionInfo(False, "Add a second progress bar to the console that shows progress for an entire job.", gr.Checkbox, {"visible": False}),
"print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console.", gr.Checkbox, {"visible": False}),
"dimensions_and_batch_together": OptionInfo(True, "", gr.Checkbox, {"visible": False}),
}))
options_templates.update(options_section(('system-paths', "System Paths"), {
@@ -268,6 +259,7 @@ options_templates.update(options_section(('system-paths', "System Paths"), {
"vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Path to directory with VAE files"),
"embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Embeddings directory for textual inversion"),
"embeddings_templates_dir": OptionInfo(os.path.join(paths.script_path, 'train/templates'), "Embeddings train templates directory"),
"embeddings_train_log": OptionInfo(os.path.join(paths.script_path, 'train.csv'), "Embeddings train log file"),
"hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Hypernetwork directory"),
"codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Path to directory with codeformer model file(s)."),
"gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Path to directory with GFPGAN model file(s)"),
@@ -327,11 +319,12 @@ options_templates.update(options_section(('saving-paths', "Image Paths"), {
}))
options_templates.update(options_section(('cuda', "CUDA Settings"), {
"memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}),
"precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}),
"cuda_dtype": OptionInfo("FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}),
"cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}),
"no_half": OptionInfo(True if is_device_dml else False, "Use full precision for model (--no-half)"),
"no_half_vae": OptionInfo(True if is_device_dml else False, "Use full precision for VAE (--no-half-vae)"),
"upcast_sampling": OptionInfo(False, "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory"),
"upcast_sampling": OptionInfo(True if sys.platform == "darwin" else False, "Enable upcast sampling. Usually produces similar results to --no-half with better performance while using less memory"),
"disable_nan_check": OptionInfo(True, "Do not check if produced images/latent spaces have NaN values"),
"rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"),
"opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "),
@@ -343,11 +336,11 @@ options_templates.update(options_section(('cuda', "CUDA Settings"), {
}))
options_templates.update(options_section(('upscaling', "Upscaling"), {
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Select which Real-ESRGAN models to show in the web UI.", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
"upscaler_for_img2img": OptionInfo("SwinIR_4x", "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
"use_old_hires_fix_width_height": OptionInfo(False, "For hires fix, use width/height sliders to set final resolution rather than first pass (disables Upscale by, Resize width/height to)."),
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
"upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
"use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution rather than first pass"),
"dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers."),
}))
@@ -357,12 +350,6 @@ options_templates.update(options_section(('face-restoration', "Face restoration"
"face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
}))
options_templates.update(options_section(('system', "System"), {
"memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}),
"multiple_tqdm": OptionInfo(False, "Add a second progress bar to the console that shows progress for an entire job."),
"print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console."),
}))
options_templates.update(options_section(('training', "Training"), {
"unload_models_when_training": OptionInfo(False, "Move VAE and CLIP to RAM when training if possible. Saves VRAM."),
"pin_memory": OptionInfo(True, "Turn on pin_memory for DataLoader. Makes training slightly faster but can increase memory usage."),
@@ -409,13 +396,13 @@ options_templates.update(options_section(('ui', "User interface"), {
"do_not_show_images": OptionInfo(False, "Do not show any images in results for web"),
"add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"),
"add_model_name_to_info": OptionInfo(True, "Add model name to generation information"),
"disable_weights_auto_swap": OptionInfo(True, "When reading generation parameters from text into UI (from PNG info or pasted text), do not change the selected model/checkpoint."),
"disable_weights_auto_swap": OptionInfo(True, "Do not change the selected model when reading generation parameters."),
"send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"),
"send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"),
"font": OptionInfo("", "Font for image grids that have text"),
"js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"),
"js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"),
"show_progress_in_title": OptionInfo(False, "Show generation progress in window title."),
"js_modal_lightbox": OptionInfo(True, "Enable full page image viewer", gr.Checkbox, {"visible": False}),
"js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer", gr.Checkbox, {"visible": False}),
"show_progress_in_title": OptionInfo(False, "Show generation progress in window title.", gr.Checkbox, {"visible": False}),
"keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
"keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing <extra networks:0.9>", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
"quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"),
@@ -436,6 +423,7 @@ options_templates.update(options_section(('ui', "Live previews"), {
options_templates.update(options_section(('sampler-params', "Sampler parameters"), {
"show_samplers": OptionInfo(["Euler a", "UniPC", "DDIM", "DPM++ SDE", "DPM++ SDE", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers()]}),
"fallback_sampler": OptionInfo("Euler a", "Fallback sampler if primary sampler is not compatible", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
"eta_ancestral": OptionInfo(1.0, "Noise multiplier for ancestral samplers (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
"eta_ddim": OptionInfo(0.0, "Noise multiplier for DDIM (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
"ddim_discretize": OptionInfo('uniform', "DDIM discretize img2img", gr.Radio, {"choices": ['uniform', 'quad']}),
@@ -490,17 +478,14 @@ class Options:
def __setattr__(self, key, value):
if self.data is not None:
if key in self.data or key in self.data_labels:
assert not cmd_opts.freeze_settings, "changing settings is disabled"
info = opts.data_labels.get(key, None)
comp_args = info.component_args if info else None
if isinstance(comp_args, dict) and comp_args.get('visible', True) is False:
raise RuntimeError(f"not possible to set {key} because it is restricted")
if cmd_opts.freeze_settings:
print(f'Settings are frozen: {key}')
return
if cmd_opts.hide_ui_dir_config and key in restricted_opts:
raise RuntimeError(f"not possible to set {key} because it is restricted")
self.data[key] = value
print(f'Settings key is restricted: {key}')
return
else:
self.data[key] = value
return
return super(Options, self).__setattr__(key, value)
@@ -509,24 +494,19 @@ class Options:
if self.data is not None:
if item in self.data:
return self.data[item]
if item in self.data_labels:
return self.data_labels[item].default
return super(Options, self).__getattribute__(item)
def set(self, key, value):
"""sets an option and calls its onchange callback, returning True if the option changed and False otherwise"""
oldval = self.data.get(key, None)
if oldval == value:
return False
try:
setattr(self, key, value)
except RuntimeError:
return False
if self.data_labels[key].onchange is not None:
try:
self.data_labels[key].onchange()
@@ -534,37 +514,30 @@ class Options:
errors.display(e, f"changing setting {key} to {value}")
setattr(self, key, oldval)
return False
return True
def get_default(self, key):
"""returns the default value for the key"""
data_label = self.data_labels.get(key)
if data_label is None:
return None
return data_label.default
def save(self, filename):
assert not cmd_opts.freeze_settings, "saving settings is disabled"
with open(filename, "w", encoding="utf8") as file:
json.dump(self.data, file, indent=4)
def same_type(self, x, y):
if x is None or y is None:
return True
type_x = self.typemap.get(type(x), type(x))
type_y = self.typemap.get(type(y), type(y))
return type_x == type_y
def load(self, filename):
with open(filename, "r", encoding="utf8") as file:
self.data = json.load(file)
bad_settings = 0
for k, v in self.data.items():
info = self.data_labels.get(k, None)
@@ -578,7 +551,6 @@ class Options:
def onchange(self, key, func, call=True):
item = self.data_labels.get(key)
item.onchange = func
if call:
func()
@@ -591,13 +563,11 @@ class Options:
def reorder(self):
"""reorder settings so that all items related to section always go together"""
section_ids = {}
settings_items = self.data_labels.items()
for k, item in settings_items:
if item.section not in section_ids:
section_ids[item.section] = len(section_ids)
self.data_labels = {k: v for k, v in sorted(settings_items, key=lambda x: section_ids[x[1].section])}
def cast_value(self, key, value):
@@ -623,27 +593,20 @@ class Options:
return value
opts = Options()
batch_cond_uncond = opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram)
parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram
xformers_available = False
config_filename = cmd_opts.ui_settings_file
os.makedirs(opts.hypernetwork_dir, exist_ok=True)
hypernetworks = {}
loaded_hypernetworks = []
if os.path.exists(config_filename):
opts.load(config_filename)
cmd_opts = cmd_args.compatibility_args(opts, cmd_opts)
prompt_styles = modules.styles.StyleDatabase(opts.styles_dir)
settings_components = None
"""assinged from ui.py, a mapping on setting names to gradio components repsponsible for those settings"""
latent_upscale_default_mode = "Latent"
latent_upscale_modes = {
"Latent": {"mode": "bilinear", "antialias": False},
@@ -653,11 +616,10 @@ latent_upscale_modes = {
"Latent (nearest)": {"mode": "nearest", "antialias": False},
"Latent (nearest-exact)": {"mode": "nearest-exact", "antialias": False},
}
progress_print_out = sys.stdout
gradio_theme = gr.themes.Base()
def reload_gradio_theme(theme_name=None):
global gradio_theme # pylint: disable=global-statement
if not theme_name:
@@ -718,7 +680,6 @@ class TotalTQDM:
total_tqdm = TotalTQDM()
mem_mon = modules.memmon.MemUsageMonitor("MemMon", device, opts)
mem_mon.start()
@@ -526,7 +526,7 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st
save_embedding(embedding, optimizer, checkpoint, embedding_name_every, last_saved_file, remove_cached_checksum=True)
embedding_yet_to_be_embedded = True
write_loss(log_directory, "train.csv", embedding.step, steps_per_epoch, {
write_loss(log_directory, shared.ops.embeddings_train_log, embedding.step, steps_per_epoch, {
"loss": f"{loss_step:.7f}",
"learn_rate": scheduler.learn_rate
})
+2 -2
View File
@@ -7,8 +7,8 @@ import modules.shared as shared
from modules.ui import plaintext_to_html
def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, override_settings_texts, *args):
override_settings = create_override_settings_dict(override_settings_texts) # pylint: disable=unused-argument
def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, override_settings_texts, *args): # pylint: disable=unused-argument
override_settings = create_override_settings_dict(override_settings_texts)
p = StableDiffusionProcessingTxt2Img(
sd_model=shared.sd_model,
outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples,
+24 -151
View File
@@ -81,34 +81,25 @@ def visit(x, func, path=""):
def add_style(name: str, prompt: str, negative_prompt: str):
if name is None:
return [gr_show() for x in range(4)]
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.opts.styles_dir)
return [gr.Dropdown.update(visible=True, choices=list(shared.prompt_styles.styles)) for _ in range(2)]
def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y):
from modules import processing, devices
if not enable:
return ""
p = processing.StableDiffusionProcessingTxt2Img(width=width, height=height, enable_hr=True, hr_scale=hr_scale, hr_resize_x=hr_resize_x, hr_resize_y=hr_resize_y)
with devices.autocast():
p.init([""], [0], [0])
return f"resize: from <span class='resolution'>{p.width}x{p.height}</span> to <span class='resolution'>{p.hr_resize_x or p.hr_upscale_to_x}x{p.hr_resize_y or p.hr_upscale_to_y}</span>"
def apply_styles(prompt, prompt_neg, styles):
prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles)
prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, styles)
return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value=[])]
@@ -125,7 +116,6 @@ def process_interrogate(interrogation_function, mode, ii_input_dir, ii_output_di
os.makedirs(ii_output_dir, exist_ok=True)
else:
ii_output_dir = ii_input_dir
for image in images:
img = Image.open(image)
filename = os.path.basename(image)
@@ -144,44 +134,35 @@ def interrogate_deepbooru(image):
prompt = deepbooru.model.tag(image)
return gr.update() if prompt is None else prompt
def change_clip_skip(val):
shared.opts.CLIP_stop_at_last_layers = val
def create_seed_inputs(target_interface):
with FormRow(elem_id=target_interface + '_seed_row', variant="compact"):
seed = gr.Number(label='Seed', value=-1, elem_id=target_interface + '_seed')
seed.style(container=False)
random_seed = ToolButton(random_symbol, elem_id=target_interface + '_random_seed')
reuse_seed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_seed')
seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=False, visible=False) # Ghost checkbox, so it still gets sent. For compatibility with extensions that call txt2img or img2img manually
with FormRow(visible=True, elem_id=target_interface + '_subseed_row'):
subseed = gr.Number(label='Variation seed', value=-1, elem_id=target_interface + '_subseed')
subseed.style(container=False)
random_subseed = ToolButton(random_symbol, elem_id=target_interface + '_random_subseed')
reuse_subseed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_subseed')
subseed_strength = gr.Slider(label='Strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=target_interface + '_subseed_strength')
with FormRow(visible=False):
seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=target_interface + '_seed_resize_from_w')
seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from height", value=0, elem_id=target_interface + '_seed_resize_from_h')
random_seed.click(fn=lambda: [-1, -1], show_progress=False, inputs=[], outputs=[seed, subseed])
random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed])
return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox
def connect_clear_prompt(button):
"""Given clear button, prompt, and token_counter objects, setup clear prompt button click event"""
button.click(
_js="clear_prompt",
fn=None,
inputs=[],
outputs=[],
)
button.click(_js="clear_prompt", fn=None, inputs=[], outputs=[])
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed):
@@ -190,7 +171,6 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
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)
@@ -201,35 +181,24 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
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:
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]
)
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 update_token_counter(text, steps):
try:
text, _ = extra_networks.parse_prompt(text)
_, prompt_flat_list, _ = prompt_parser.get_multicond_prompt_list([text])
prompt_schedules = prompt_parser.get_learned_conditioning_prompt_schedules(prompt_flat_list, steps)
except Exception:
# a parsing error can happen here during typing, and we don't want to bother the user with
# messages related to it in console
prompt_schedules = [[[steps, text]]]
flat_prompts = reduce(lambda list1, list2: list1+list2, prompt_schedules)
prompts = [prompt_text for step, prompt_text in flat_prompts]
token_count, max_length = max([model_hijack.get_prompt_lengths(prompt) for prompt in prompts], key=lambda args: args[0])
@@ -238,103 +207,72 @@ def update_token_counter(text, steps):
def create_toprow(is_img2img):
id_part = "img2img" if is_img2img else "txt2img"
with gr.Row(elem_id=f"{id_part}_toprow", variant="compact"):
with gr.Column(elem_id=f"{id_part}_prompt_container", scale=6):
with gr.Row():
with gr.Column(scale=80):
with gr.Row():
prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)")
with gr.Row():
with gr.Column(scale=80):
with gr.Row():
negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)")
button_interrogate = None
button_deepbooru = None
if is_img2img:
with gr.Column(scale=1, elem_classes="interrogate-col"):
button_interrogate = gr.Button('Interrogate\nCLIP', elem_id="interrogate")
button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id="deepbooru")
with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"):
with gr.Row(elem_id=f"{id_part}_generate_box", elem_classes="generate-box"):
interrupt = gr.Button('Stop', elem_id=f"{id_part}_interrupt", elem_classes="generate-box-interrupt")
skip = gr.Button('Skip', elem_id=f"{id_part}_skip", elem_classes="generate-box-skip")
submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary')
skip.click(
fn=lambda: shared.state.skip(),
inputs=[],
outputs=[],
)
interrupt.click(
fn=lambda: shared.state.interrupt(),
inputs=[],
outputs=[],
)
skip.click(fn=lambda: shared.state.skip(), inputs=[], outputs=[])
interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[])
with gr.Row(elem_id=f"{id_part}_tools"):
paste = ToolButton(value=paste_symbol, elem_id="paste")
clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt")
extra_networks_button = ToolButton(value=extra_networks_symbol, elem_id=f"{id_part}_extra_networks")
prompt_style_apply = ToolButton(value=apply_style_symbol, elem_id=f"{id_part}_style_apply")
save_style = ToolButton(value=save_style_symbol, elem_id=f"{id_part}_style_create")
token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"])
token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
negative_token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{id_part}_negative_token_counter", elem_classes=["token-counter"])
negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button")
clear_prompt_button.click(
fn=lambda *x: x,
_js="confirm_clear_prompt",
inputs=[prompt, negative_prompt],
outputs=[prompt, negative_prompt],
)
clear_prompt_button.click(fn=lambda *x: x, _js="confirm_clear_prompt", inputs=[prompt, negative_prompt], outputs=[prompt, negative_prompt])
with gr.Row(elem_id=f"{id_part}_styles_row"):
prompt_styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[k for k, v in shared.prompt_styles.styles.items()], value=[], multiselect=True)
create_refresh_button(prompt_styles, shared.prompt_styles.reload, lambda: {"choices": [k for k, v in shared.prompt_styles.styles.items()]}, f"refresh_{id_part}_styles")
return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, prompt_style_apply, save_style, paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button
def setup_progressbar(*args, **kwargs):
def setup_progressbar(*args, **kwargs): # pylint: disable=unused-argument
pass
def apply_setting(key, value):
if value is None:
return gr.update()
if shared.cmd_opts.freeze_settings:
return gr.update()
# dont allow model to be swapped when model hash exists in prompt
if key == "sd_model_checkpoint" and opts.disable_weights_auto_swap:
return gr.update()
if key == "sd_model_checkpoint":
ckpt_info = sd_models.get_closet_checkpoint_match(value)
if ckpt_info is not None:
value = ckpt_info.title
else:
return gr.update()
comp_args = opts.data_labels[key].component_args
if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
return
valtype = type(opts.data_labels[key].default)
oldval = opts.data.get(key, None)
opts.data[key] = valtype(value) if valtype != type(None) else value
if oldval != value and opts.data_labels[key].onchange is not None:
opts.data_labels[key].onchange()
opts.save(shared.config_filename)
return getattr(opts, key)
@@ -343,18 +281,12 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args, ele
def refresh():
refresh_method()
args = refreshed_args() if callable(refreshed_args) else refreshed_args
for k, v in args.items():
setattr(refresh_component, k, v)
return gr.update(**(args or {}))
refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id)
refresh_button.click(
fn=refresh,
inputs=[],
outputs=[refresh_component]
)
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component])
return refresh_button
@@ -366,126 +298,98 @@ def create_sampler_and_steps_selection(choices, tabname):
with FormRow(elem_id=f"sampler_selection_{tabname}"):
sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value="UniPC" if tabname == 'txt2img' else "Euler a", type="index")
steps = gr.Slider(minimum=1, maximum=150, step=1, elem_id=f"{tabname}_steps", label="Sampling steps", value=10 if tabname == 'txt2img' else 20)
return steps, sampler_index
def ordered_ui_categories():
user_order = {x.strip(): i * 2 + 1 for i, x in enumerate(shared.opts.ui_reorder.split(","))}
for i, category in sorted(enumerate(shared.ui_reorder_categories), key=lambda x: user_order.get(x[1], x[0] * 2 + 0)):
yield category
def get_value_for_setting(key):
value = getattr(opts, key)
info = opts.data_labels[key]
args = info.component_args() if callable(info.component_args) else info.component_args or {}
args = {k: v for k, v in args.items() if k not in {'precision'}}
return gr.update(value=value, **args)
def create_override_settings_dropdown(tabname, row):
def create_override_settings_dropdown(tabname, row): # pylint: disable=unused-argument
dropdown = gr.Dropdown([], label="Override settings", visible=False, elem_id=f"{tabname}_override_settings", multiselect=True)
dropdown.change(
fn=lambda x: gr.Dropdown.update(visible=len(x) > 0),
inputs=[dropdown],
outputs=[dropdown],
)
dropdown.change(fn=lambda x: gr.Dropdown.update(visible=len(x) > 0), inputs=[dropdown], outputs=[dropdown])
return dropdown
def create_ui():
import modules.img2img
import modules.txt2img
import modules.img2img # pylint: disable=redefined-outer-name
import modules.txt2img # pylint: disable=redefined-outer-name
reload_javascript()
parameters_copypaste.reset()
modules.scripts.scripts_current = modules.scripts.scripts_txt2img
modules.scripts.scripts_txt2img.initialize_scripts(is_img2img=False)
with gr.Blocks(analytics_enabled=False) as txt2img_interface:
txt2img_prompt, txt2img_prompt_styles, txt2img_negative_prompt, submit, _, _, txt2img_prompt_style_apply, txt2img_save_style, txt2img_paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button = create_toprow(is_img2img=False)
dummy_component = gr.Label(visible=False)
txt_prompt_img = gr.File(label="", elem_id="txt2img_prompt_image", file_count="single", type="binary", visible=False)
with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks:
with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks_ui:
from modules import ui_extra_networks
extra_networks_ui = ui_extra_networks.create_ui(extra_networks, extra_networks_button, 'txt2img')
extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'txt2img')
with gr.Row().style(equal_height=False):
with gr.Column(variant='compact', elem_id="txt2img_settings"):
for category in ordered_ui_categories():
if category == "sampler":
steps, sampler_index = create_sampler_and_steps_selection(samplers, "txt2img")
elif category == "dimensions":
with FormRow():
with gr.Column(elem_id="txt2img_column_size", scale=4):
with FormRow(elem_id="txt2img_row_dimension"):
width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="txt2img_width")
height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height")
with gr.Column(elem_id="txt2img_dimensions_row", scale=1, elem_classes="dimensions-tools"):
res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn")
with gr.Column(elem_id="txt2img_column_batch"):
with FormRow(elem_id="txt2img_row_batch"):
batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count")
batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size")
elif category == "cfg":
with FormRow():
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="txt2img_cfg_scale")
clip_skip = gr.Slider(label='CLIP Skip', value=1, minimum=1, maximum=4, step=1, elem_id='txt2img_clip_skip', interactive=True)
clip_skip.change(fn=change_clip_skip, show_progress=False, inputs=clip_skip)
elif category == "seed":
seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('txt2img')
elif category == "checkboxes":
with FormRow(elem_classes="checkboxes-row", variant="compact"):
restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1, elem_id="txt2img_restore_faces")
tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling")
enable_hr = gr.Checkbox(label='Hires fix', value=False, elem_id="txt2img_enable_hr")
hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False)
elif category == "hires_fix":
with FormGroup(visible=False, elem_id="txt2img_hires_fix") as hr_options:
with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"):
hr_upscaler = gr.Dropdown(label="Upscaler", elem_id="txt2img_hr_upscaler", choices=[*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]], value=shared.latent_upscale_default_mode)
hr_second_pass_steps = gr.Slider(minimum=0, maximum=150, step=1, label='Hires steps', value=0, elem_id="txt2img_hires_steps")
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7, elem_id="txt2img_denoising_strength")
with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"):
hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale")
hr_resize_x = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x")
hr_resize_y = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y")
elif category == "override_settings":
with FormRow(elem_id="txt2img_override_settings_row") as row:
override_settings = create_override_settings_dropdown('txt2img', row)
elif category == "scripts":
with FormGroup(elem_id="txt2img_script_container"):
custom_inputs = modules.scripts.scripts_txt2img.setup_ui()
hr_resolution_preview_inputs = [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y]
for input in hr_resolution_preview_inputs:
input.change(
for preview_input in hr_resolution_preview_inputs:
preview_input.change(
fn=calc_resolution_hires,
inputs=hr_resolution_preview_inputs,
outputs=[hr_final_resolution],
show_progress=False,
)
input.change(
preview_input.change(
None,
_js="onCalcResolutionHires",
inputs=hr_resolution_preview_inputs,
@@ -494,7 +398,6 @@ def create_ui():
)
txt2img_gallery, generation_info, html_info, html_log = create_output_panel("txt2img", opts.outdir_txt2img_samples)
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)
@@ -614,9 +517,9 @@ def create_ui():
img2img_prompt_img = gr.File(label="", elem_id="img2img_prompt_image", file_count="single", type="binary", visible=False)
with FormRow(variant='compact', elem_id="img2img_extra_networks", visible=False) as extra_networks:
with FormRow(variant='compact', elem_id="img2img_extra_networks", visible=False) as extra_networks_ui:
from modules import ui_extra_networks
extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks, extra_networks_button, 'img2img')
extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'img2img')
with FormRow().style(equal_height=False):
with gr.Column(variant='compact', elem_id="img2img_settings"):
@@ -767,7 +670,7 @@ def create_ui():
for i, elem in enumerate([tab_img2img, tab_sketch, tab_inpaint, tab_inpaint_color, tab_inpaint_upload, tab_batch]):
elem.select(
fn=lambda tab=i: select_img2img_tab(tab),
fn=lambda tab=i: select_img2img_tab(tab), # pylint: disable=cell-var-from-loop
inputs=[],
outputs=[inpaint_controls, mask_alpha],
)
@@ -926,31 +829,6 @@ def create_ui():
with gr.Blocks(analytics_enabled=False) as extras_interface:
ui_postprocessing.create_ui()
"""
with gr.Blocks(analytics_enabled=False) as pnginfo_interface:
with gr.Row().style(equal_height=False):
with gr.Column(variant='panel'):
image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil")
with gr.Column(variant='panel'):
html = gr.HTML()
generation_info = gr.Textbox(visible=False, elem_id="pnginfo_generation_info")
html2 = gr.HTML()
with gr.Row():
buttons = parameters_copypaste.create_buttons(["txt2img", "img2img", "inpaint", "extras"])
for tabname, button in buttons.items():
parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(
paste_button=button, tabname=tabname, source_text_component=generation_info, source_image_component=image,
))
image.change(
fn=wrap_gradio_call(modules.extras.run_pnginfo),
inputs=[image],
outputs=[html, generation_info, html2],
)
"""
def update_interp_description(value):
interp_description_css = "<p style='margin-bottom: 2.5em'>{}</p>"
interp_descriptions = {
@@ -1322,9 +1200,7 @@ def create_ui():
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:
@@ -1334,10 +1210,8 @@ def create_ui():
elif t == bool:
comp = gr.Checkbox
else:
raise Exception(f'bad options item type: {str(t)} for key {key}')
raise ValueError(f'bad options item type: {str(t)} for key {key}')
elem_id = "setting_"+key
if info.refresh is not None:
if is_quicksettings:
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
@@ -1348,7 +1222,6 @@ def create_ui():
create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key)
else:
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
return res
components = []
@@ -1433,7 +1306,7 @@ def create_ui():
current_tab.__exit__()
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications", visible=False)
show_all_pages = gr.Button(value="Show all pages", variant='primary', elem_id="settings_show_all_pages")
_show_all_pages = gr.Button(value="Show all pages", variant='primary', elem_id="settings_show_all_pages")
with gr.TabItem("Licenses"):
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
@@ -1507,7 +1380,7 @@ def create_ui():
parameters_copypaste.connect_paste_params_buttons()
with gr.Tabs(elem_id="tabs") as tabs:
with gr.Tabs(elem_id="tabs") as _tabs:
for interface, label, ifid in interfaces:
if label in shared.opts.hidden_tabs:
continue
+4 -2
View File
@@ -66,10 +66,12 @@ def save_files(js_data, images, do_make_zip, index):
for image_index, filedata in enumerate(images, start_index):
image = image_from_url_text(filedata)
is_grid = image_index < p.index_of_first_image
i = 0 if is_grid else (image_index - p.index_of_first_image)
if len(p.all_seeds) <= i:
p.all_seeds.append(p.seed)
if len(p.all_prompts) <= i:
p.all_prompts.append(p.prompt)
fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs)
filename = os.path.relpath(fullfn, path)
+39 -60
View File
@@ -20,37 +20,28 @@ close_symbol = '\U0000274C' # ❌
def register_page(page):
"""registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions"""
extra_pages.append(page)
allowed_dirs.clear()
allowed_dirs.update(set(sum([x.allowed_directories_for_previews() for x in extra_pages], [])))
def fetch_file(filename: str = ""):
from starlette.responses import FileResponse
from starlette.responses import FileResponse, JSONResponse
if not any([Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs]):
raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.")
ext = os.path.splitext(filename)[1].lower()
if ext not in (".png", ".jpg", ".webp"):
raise ValueError(f"File cannot be fetched: {filename}. Only png and jpg and webp.")
# would profit from returning 304
return JSONResponse({"error": f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages."})
if os.path.splitext(filename)[1].lower() not in (".png", ".jpg", ".webp"):
return JSONResponse({"error": f"File cannot be fetched: {filename}. Only png and jpg and webp."})
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
def get_metadata(page: str = "", item: str = ""):
from starlette.responses import JSONResponse
page = next(iter([x for x in extra_pages if x.name == page]), None)
if page is None:
return JSONResponse({})
metadata = page.metadata.get(item)
if metadata is None:
return JSONResponse({})
return JSONResponse({"metadata": metadata})
@@ -75,58 +66,44 @@ class ExtraNetworksPage:
def search_terms_from_path(self, filename, possible_directories=None):
abspath = os.path.abspath(filename)
for parentdir in (possible_directories if possible_directories is not None else self.allowed_directories_for_previews()):
parentdir = os.path.abspath(parentdir)
if abspath.startswith(parentdir):
return abspath[len(parentdir):].replace('\\', '/')
return ""
def create_html(self, tabname):
view = shared.opts.extra_networks_default_view
items_html = ''
self.metadata = {}
subdirs = {}
for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]:
for x in glob.glob(os.path.join(parentdir, '**/*'), recursive=True):
if not os.path.isdir(x):
continue
subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/")
while subdir.startswith("/"):
subdir = subdir[1:]
is_empty = len(os.listdir(x)) == 0
if not is_empty and not subdir.endswith("/"):
subdir = subdir + "/"
subdirs[subdir] = 1
if subdirs:
subdirs = {"": 1, **subdirs}
subdirs_html = "".join([f"""
<button class='lg secondary gradio-button custom-button{" search-all" if subdir=="" else ""}' onclick='extraNetworksSearchButton("{tabname}_extra_tabs", event)'>
{html.escape(subdir if subdir!="" else "all")}
</button>
""" for subdir in subdirs])
for item in self.list_items():
metadata = item.get("metadata")
if metadata:
self.metadata[item["name"]] = metadata
items_html += self.create_html_for_item(item, tabname)
if items_html == '':
dirs = "".join([f"<li>{x}</li>" for x in self.allowed_directories_for_previews()])
items_html = shared.html("extra-networks-no-cards.html").format(dirs=dirs)
self_name_id = self.name.replace(" ", "_")
res = f"""
<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs extra-network-subdirs-{view}'>
{subdirs_html}
@@ -135,7 +112,6 @@ class ExtraNetworksPage:
{items_html}
</div>
"""
return res
def list_items(self):
@@ -146,11 +122,9 @@ class ExtraNetworksPage:
def create_html_for_item(self, item, tabname):
preview = item.get("preview", None)
onclick = item.get("onclick", None)
if onclick is None:
onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"'
height = f"height: {shared.opts.extra_networks_card_height}px;" if shared.opts.extra_networks_card_height else ''
width = f"width: {shared.opts.extra_networks_card_width}px;" if shared.opts.extra_networks_card_width else ''
background_image = f"background-image: url(\"{html.escape(preview)}\");" if preview else ''
@@ -158,7 +132,6 @@ class ExtraNetworksPage:
metadata = item.get("metadata")
if metadata:
metadata_button = f"<div class='metadata-button' title='Show metadata' onclick='extraNetworksRequestMetadata(event, {json.dumps(self.name)}, {json.dumps(item['name'])})'></div>"
args = {
"style": f"'{height}{width}{background_image}'",
"prompt": item.get("prompt", None),
@@ -167,28 +140,25 @@ class ExtraNetworksPage:
"name": item["name"],
"description": (item.get("description") or ""),
"card_clicked": onclick,
"save_card_description": '"' + html.escape(f"""return saveCardDescription(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"',
"save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"',
"read_card_description": '"' + html.escape(f"""return readCardDescription(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])}, {json.dumps(item["description"])})""") + '"',
"search_term": item.get("search_term", ""),
"metadata_button": metadata_button,
}
return self.card_page.format(**args)
def find_preview(self, path):
"""
Find a preview PNG for a given path (without extension) and call link_preview on it.
"""
preview_extensions = ["png", "jpg", "webp"]
if shared.opts.samples_format not in preview_extensions:
preview_extensions.append(shared.opts.samples_format)
potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in preview_extensions], [])
for file in potential_files:
if os.path.isfile(file):
return self.link_preview(file)
return None
def find_description(self, path):
@@ -212,26 +182,24 @@ class ExtraNetworksUi:
def __init__(self):
self.pages = None
self.stored_extra_pages = None
self.button_save_preview = None
self.preview_target_filename = None
self.button_save_description = None
self.button_read_description = None
self.description_target_filename = None
self.description_input = None
self.tabname = None
def pages_in_preferred_order(pages):
tab_order = [x.lower().strip() for x in shared.opts.ui_extra_networks_tab_reorder.split(",")]
def tab_name_score(name):
name = name.lower()
for i, possible_match in enumerate(tab_order):
if possible_match in name:
return i
return len(pages)
tab_scores = {page.name: (tab_name_score(page.name), original_index) for original_index, page in enumerate(pages)}
return sorted(pages, key=lambda x: tab_scores[x.name])
@@ -240,47 +208,43 @@ def create_ui(container, button, tabname):
ui.pages = []
ui.stored_extra_pages = pages_in_preferred_order(extra_pages.copy())
ui.tabname = tabname
with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs:
with gr.Tabs(elem_id=tabname+"_extra_tabs"):
for page in ui.stored_extra_pages:
with gr.Tab(page.title):
page_elem = gr.HTML(page.create_html(ui.tabname))
ui.pages.append(page_elem)
filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False)
_filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False)
ui.description_input = gr.TextArea('', show_label=False, elem_id=tabname+"_description_input", placeholder="Save/Replace Extra Network Description...", lines=2)
button_refresh = ToolButton(refresh_symbol, elem_id=tabname+"_extra_refresh")
button_close = ToolButton(close_symbol, elem_id=tabname+"_extra_close")
ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False)
ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False)
ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False)
ui.button_read_description = gr.Button('Read description', elem_id=tabname+"_read_description", visible=False)
ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False)
def toggle_visibility(is_visible):
is_visible = not is_visible
return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary"))
state_visible = gr.State(value=False)
state_visible = gr.State(value=False) # pylint: disable=abstract-class-instantiated
button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button])
button_close.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container])
def refresh():
res = []
for pg in ui.stored_extra_pages:
pg.refresh()
res.append(pg.create_html(ui.tabname))
return res
button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages)
return ui
def path_is_parent(parent_path, child_path):
parent_path = os.path.abspath(parent_path)
child_path = os.path.abspath(child_path)
return child_path.startswith(parent_path)
@@ -289,30 +253,24 @@ def setup_ui(ui, gallery):
if len(images) == 0:
print("There is no image in gallery to save as a preview.")
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
index = int(index)
index = 0 if index < 0 else index
index = len(images) - 1 if index >= len(images) else index
img_info = images[index if index >= 0 else 0]
image = image_from_url_text(img_info)
geninfo, items = read_info_from_image(image)
geninfo, _items = read_info_from_image(image)
is_allowed = False
for extra_page in ui.stored_extra_pages:
if any([path_is_parent(x, filename) for x in extra_page.allowed_directories_for_previews()]):
is_allowed = True
break
assert is_allowed, f'writing to {filename} is not allowed'
if geninfo:
pnginfo_data = PngImagePlugin.PngInfo()
pnginfo_data.add_text('parameters', geninfo)
image.save(filename, pnginfo=pnginfo_data)
else:
image.save(filename)
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
ui.button_save_preview.click(
@@ -321,3 +279,24 @@ def setup_ui(ui, gallery):
inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename],
outputs=[*ui.pages]
)
# write description to a file
def save_description(filename,descrip):
lastDotIndex = filename.rindex('.')
filename = filename[0:lastDotIndex]+".description.txt"
if descrip != "":
try:
f = open(filename,'w', encoding='utf-8')
except OSError:
print ("Could not open file to write: " + filename)
with f:
f.write(descrip)
f.close()
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
ui.button_save_description.click(
fn=save_description,
_js="function(x,y){return [x,y]}",
inputs=[ui.description_target_filename, ui.description_input],
outputs=[*ui.pages]
)
+8 -2
View File
@@ -41,8 +41,14 @@ def create_ui():
for tabname, button in buttons.items():
parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(paste_button=button, tabname=tabname, source_text_component=generation_info, source_image_component=extras_image))
def pretty_geninfo(generation_info):
return generation_info.replace(', ', '\n')
def pretty_geninfo(generation_info: str):
if generation_info is None:
return ''
sections = generation_info.split('Steps:')
if len(sections) > 1:
param = sections[0].strip() + '\nSteps:' + sections[1].strip().replace(', ', '\n')
return param
return generation_info
tab_single.select(fn=lambda: 0, inputs=[], outputs=[tab_index])
tab_batch.select(fn=lambda: 1, inputs=[], outputs=[tab_index])
+5 -5
View File
@@ -54,7 +54,7 @@ class Upscaler:
dest_w = int(img.width * scale)
dest_h = int(img.height * scale)
for i in range(3):
for _i in range(3):
shape = (img.width, img.height)
img = self.do_upscale(img, selected_model)
@@ -74,7 +74,7 @@ class Upscaler:
def load_model(self, path: str):
pass
def find_models(self, ext_filter=None) -> list:
def find_models(self, ext_filter=None) -> list: # pylint: disable=unused-argument
return modelloader.load_models(model_path=self.model_path, model_url=self.model_url, command_path=self.user_path)
def update_status(self, prompt):
@@ -107,7 +107,7 @@ class UpscalerNone(Upscaler):
def do_upscale(self, img, selected_model=None):
return img
def __init__(self, dirname=None):
def __init__(self, dirname=None): # pylint: disable=unused-argument
super().__init__(False)
self.scalers = [UpscalerData("None", None, self)]
@@ -121,7 +121,7 @@ class UpscalerLanczos(Upscaler):
def load_model(self, _):
pass
def __init__(self, dirname=None):
def __init__(self, dirname=None): # pylint: disable=unused-argument
super().__init__(False)
self.name = "Lanczos"
self.scalers = [UpscalerData("Lanczos", None, self)]
@@ -136,7 +136,7 @@ class UpscalerNearest(Upscaler):
def load_model(self, _):
pass
def __init__(self, dirname=None):
def __init__(self, dirname=None): # pylint: disable=unused-argument
super().__init__(False)
self.name = "Nearest"
self.scalers = [UpscalerData("Nearest", None, self)]
+8 -21
View File
@@ -1,7 +1,6 @@
function gradioApp() {
const elems = document.getElementsByTagName('gradio-app')
const elem = elems.length == 0 ? document : elems[0]
if (elem !== document) elem.getElementById = function(id){ return document.getElementById(id) }
return elem.shadowRoot ? elem.shadowRoot : elem
}
@@ -34,12 +33,10 @@ function onOptionsChanged(callback){
}
function runCallback(x, m){
try {
x(m)
} catch (e) {
(console.error || console.log).call(console, e.message, e);
}
try { x(m)
} catch (e) { (console.error || console.log).call(console, e.message, e); }
}
function executeCallbacks(queue, m) {
queue.forEach(function(x){runCallback(x, m)})
}
@@ -52,7 +49,6 @@ document.addEventListener("DOMContentLoaded", function() {
executedOnLoaded = true;
executeCallbacks(uiLoadedCallbacks);
}
executeCallbacks(uiUpdateCallbacks, m);
const newTab = get_uiCurrentTab();
if ( newTab && ( newTab !== uiCurrentTab ) ) {
@@ -75,9 +71,7 @@ document.addEventListener('keydown', function(e) {
}
if (handled) {
button = get_uiCurrentTabContent().querySelector('button[id$=_generate]');
if (button) {
button.click();
}
if (button) button.click();
e.preventDefault();
}
})
@@ -87,18 +81,11 @@ document.addEventListener('keydown', function(e) {
*/
function uiElementIsVisible(el) {
let isVisible = !el.closest('.\\!hidden');
if ( ! isVisible ) {
return false;
}
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;
}
if ( ! isVisible ) return false;
else if ( el.parentElement ) el = el.parentElement
else break;
}
return isVisible;
}
+6 -48
View File
@@ -1,19 +1,15 @@
from collections import namedtuple
import numpy as np
from tqdm import trange
import modules.scripts as scripts
import gradio as gr
from modules import processing, shared, sd_samplers, sd_samplers_common
import torch
import k_diffusion as K
import gradio as gr
import modules.scripts as scripts
from modules import processing, shared, sd_samplers, sd_samplers_common
def find_noise_for_image(p, cond, uncond, cfg_scale, steps):
x = p.init_latent
s_in = x.new_ones([x.shape[0]])
if shared.sd_model.parameterization == "v":
dnw = K.external.CompVisVDenoiser(shared.sd_model)
@@ -22,40 +18,29 @@ def find_noise_for_image(p, cond, uncond, cfg_scale, steps):
dnw = K.external.CompVisDenoiser(shared.sd_model)
skip = 0
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] * s_in] * 2)
cond_in = torch.cat([uncond, cond])
image_conditioning = torch.cat([p.image_conditioning] * 2)
cond_in = {"c_concat": [image_conditioning], "c_crossattn": [cond_in]}
c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)[skip:]]
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
d = (x - denoised) / sigmas[i]
dt = sigmas[i] - sigmas[i - 1]
x = x + d * dt
sd_samplers_common.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 / x.std()
@@ -65,7 +50,6 @@ Cached = namedtuple("Cached", ["noise", "cfg_scale", "steps", "latent", "origina
# 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]])
if shared.sd_model.parameterization == "v":
dnw = K.external.CompVisVDenoiser(shared.sd_model)
@@ -79,42 +63,31 @@ def find_noise_for_image_sigma_adjustment(p, cond, uncond, cfg_scale, 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])
image_conditioning = torch.cat([p.image_conditioning] * 2)
cond_in = {"c_concat": [image_conditioning], "c_crossattn": [cond_in]}
c_out, c_in = [K.utils.append_dims(k, x_in.ndim) for k in dnw.get_scalings(sigma_in)[skip:]]
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_common.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]
@@ -123,7 +96,7 @@ class Script(scripts.Script):
self.cache = None
def title(self):
return "img2img alternative test"
return "Alternative"
def show(self, is_img2img):
return is_img2img
@@ -132,24 +105,19 @@ class Script(scripts.Script):
info = gr.Markdown('''
* `CFG Scale` should be 2 or lower.
''')
override_sampler = gr.Checkbox(label="Override `Sampling method` to Euler?(this method is built for it)", value=True, elem_id=self.elem_id("override_sampler"))
override_prompt = gr.Checkbox(label="Override `prompt` to the same value as `original prompt`?(and `negative prompt`)", value=True, elem_id=self.elem_id("override_prompt"))
original_prompt = gr.Textbox(label="Original prompt", lines=1, elem_id=self.elem_id("original_prompt"))
original_negative_prompt = gr.Textbox(label="Original negative prompt", lines=1, elem_id=self.elem_id("original_negative_prompt"))
override_steps = gr.Checkbox(label="Override `Sampling Steps` to the same value as `Decode steps`?", value=True, elem_id=self.elem_id("override_steps"))
st = gr.Slider(label="Decode steps", minimum=1, maximum=150, step=1, value=50, elem_id=self.elem_id("st"))
override_strength = gr.Checkbox(label="Override `Denoising strength` to 1?", value=True, elem_id=self.elem_id("override_strength"))
cfg = gr.Slider(label="Decode CFG scale", minimum=0.0, maximum=15.0, step=0.1, value=1.0, elem_id=self.elem_id("cfg"))
randomness = gr.Slider(label="Randomness", minimum=0.0, maximum=1.0, step=0.01, value=0.0, elem_id=self.elem_id("randomness"))
sigma_adjustment = gr.Checkbox(label="Sigma adjustment for finding noise for image", value=False, elem_id=self.elem_id("sigma_adjustment"))
return [
info,
info,
override_sampler,
override_prompt, original_prompt, original_negative_prompt,
override_steps, st,
@@ -171,13 +139,11 @@ class Script(scripts.Script):
def sample_extra(conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
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 \
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:
rec_noise = self.cache.noise
else:
@@ -191,28 +157,20 @@ class Script(scripts.Script):
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:], seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w, p=p)
combined_noise = ((1 - randomness) * rec_noise + randomness * rand_noise) / ((randomness**2 + (1-randomness)**2) ** 0.5)
sampler = sd_samplers.create_sampler(p.sampler_name, p.sd_model)
sigmas = sampler.model_wrap.get_sigmas(p.steps)
noise_dt = combined_noise - (p.init_latent / sigmas[0])
p.seed = p.seed + 1
return sampler.sample_img2img(p, p.init_latent, noise_dt, conditioning, unconditional_conditioning, image_conditioning=p.image_conditioning)
p.sample = sample_extra
p.extra_generation_params["Decode prompt"] = original_prompt
p.extra_generation_params["Decode negative prompt"] = original_negative_prompt
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)
return processed
+1 -1
View File
@@ -120,7 +120,7 @@ def get_matched_noise(_np_src_image, np_mask_rgb, noise_q=1, color_variation=0.0
class Script(scripts.Script):
def title(self):
return "Outpainting mk2"
return "Outpainting"
def show(self, is_img2img):
return is_img2img
+1 -1
View File
@@ -11,7 +11,7 @@ from modules.shared import opts, cmd_opts, state
class Script(scripts.Script):
def title(self):
return "Poor man's outpainting"
return "Outpainting alternative"
def show(self, is_img2img):
return is_img2img
+21 -15
View File
@@ -4,8 +4,8 @@ import numpy as np
from modules import scripts_postprocessing, shared
import gradio as gr
from modules.ui_components import FormRow
from modules.ui_components import FormRow, ToolButton
from modules.ui import switch_values_symbol
upscale_cache = {}
@@ -17,22 +17,28 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing):
def ui(self):
selected_tab = gr.State(value=0)
with gr.Tabs(elem_id="extras_resize_mode"):
with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by:
with FormRow():
upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id="extras_upscaling_resize")
extras_upscaler_1 = gr.Dropdown(label='Upscaler', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value="SwinIR_4x")
with gr.Column():
with FormRow():
with gr.Tabs(elem_id="extras_resize_mode"):
with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by:
upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id="extras_upscaling_resize")
with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to:
with FormRow():
upscaling_resize_w = gr.Number(label="Width", value=512, precision=0, elem_id="extras_upscaling_resize_w")
upscaling_resize_h = gr.Number(label="Height", value=512, precision=0, elem_id="extras_upscaling_resize_h")
upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop")
with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to:
with FormRow():
with gr.Row(elem_id="upscaling_column_size", scale=4):
upscaling_resize_w = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w")
upscaling_resize_h = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h")
upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn")
upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop")
with FormRow():
extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, visible=False)
extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility")
with FormRow():
extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
with FormRow():
extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility")
upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False)
tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab])
tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab])
+1 -3
View File
@@ -109,7 +109,7 @@ def load_prompt_file(file):
class Script(scripts.Script):
def title(self):
return "Prompts from file or textbox"
return "Prompts from file"
def ui(self, is_img2img):
checkbox_iterate = gr.Checkbox(label="Iterate seed every line", value=False, elem_id=self.elem_id("checkbox_iterate"))
@@ -130,8 +130,6 @@ class Script(scripts.Script):
lines = [x.strip() for x in prompt_txt.splitlines()]
lines = [x for x in lines if len(x) > 0]
p.do_not_save_grid = True
job_count = 0
jobs = []
+41 -41
View File
@@ -1,26 +1,18 @@
import re
import csv
import random
from collections import namedtuple
from copy import copy
from itertools import permutations, chain
import random
import csv
from io import StringIO
from PIL import Image
import numpy as np
import modules.scripts as scripts
import gradio as gr
from modules import images, paths, sd_samplers, processing, sd_models, sd_vae
from modules.processing import process_images, Processed, StableDiffusionProcessingTxt2Img
from modules.shared import opts, cmd_opts, state
import modules.scripts as scripts
import modules.shared as shared
import modules.sd_samplers
import modules.sd_models
import modules.sd_vae
import glob
import os
import re
from modules import images, sd_samplers, processing, sd_models, sd_vae
from modules.processing import process_images, Processed, StableDiffusionProcessingTxt2Img
from modules.shared import opts, state
from modules.ui_components import ToolButton
fill_values_symbol = "\U0001f4d2" # 📒
@@ -83,15 +75,15 @@ def confirm_samplers(p, xs):
def apply_checkpoint(p, x, xs):
info = modules.sd_models.get_closet_checkpoint_match(x)
info = sd_models.get_closet_checkpoint_match(x)
if info is None:
raise RuntimeError(f"Unknown checkpoint: {x}")
modules.sd_models.reload_model_weights(shared.sd_model, info)
sd_models.reload_model_weights(shared.sd_model, info)
def confirm_checkpoints(p, xs):
for x in xs:
if modules.sd_models.get_closet_checkpoint_match(x) is None:
if sd_models.get_closet_checkpoint_match(x) is None:
raise RuntimeError(f"Unknown checkpoint: {x}")
@@ -108,26 +100,34 @@ def apply_upscale_latent_space(p, x, xs):
def find_vae(name: str):
if name.lower() in ['auto', 'automatic']:
return modules.sd_vae.unspecified
return sd_vae.unspecified
if name.lower() == 'none':
return None
else:
choices = [x for x in sorted(modules.sd_vae.vae_dict, key=lambda x: len(x)) if name.lower().strip() in x.lower()]
choices = [x for x in sorted(sd_vae.vae_dict, key=lambda x: len(x)) if name.lower().strip() in x.lower()]
if len(choices) == 0:
print(f"No VAE found for {name}; using automatic")
return modules.sd_vae.unspecified
return sd_vae.unspecified
else:
return modules.sd_vae.vae_dict[choices[0]]
return sd_vae.vae_dict[choices[0]]
def apply_vae(p, x, xs):
modules.sd_vae.reload_vae_weights(shared.sd_model, vae_file=find_vae(x))
sd_vae.reload_vae_weights(shared.sd_model, vae_file=find_vae(x))
def apply_styles(p: StableDiffusionProcessingTxt2Img, x: str, _):
p.styles.extend(x.split(','))
def apply_fallback(p, x, xs):
sampler_name = sd_samplers.samplers_map.get(x.lower(), None)
if sampler_name is None:
raise RuntimeError(f"Unknown sampler: {x}")
opts.data["xyz_fallback_sampler"] = sampler_name
def apply_uni_pc_order(p, x, xs):
opts.data["uni_pc_order"] = min(x, p.steps - 1)
@@ -220,6 +220,7 @@ axis_options = [
AxisOption("Clip skip", int, apply_clip_skip),
AxisOption("Denoising", float, apply_field("denoising_strength")),
AxisOptionTxt2Img("Hires upscaler", str, apply_field("hr_upscaler"), choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]),
AxisOptionTxt2Img("Fallback latent upscaler sampler", str, apply_fallback, format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]),
AxisOptionImg2Img("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight")),
AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: list(sd_vae.vae_dict)),
AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)),
@@ -332,7 +333,6 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend
if draw_legend:
z_grid = images.draw_grid_annotations(z_grid, sub_grid_size[0], sub_grid_size[1], title_texts, [[images.GridAnnotation()]])
processed_result.images.insert(0, z_grid)
#TODO: Deeper aspects of the program rely on grid info being misaligned between metadata arrays, which is not ideal.
#processed_result.all_prompts.insert(0, processed_result.all_prompts[0])
#processed_result.all_seeds.insert(0, processed_result.all_seeds[0])
processed_result.infotexts.insert(0, processed_result.infotexts[0])
@@ -345,12 +345,12 @@ class SharedSettingsStackHelper(object):
self.CLIP_stop_at_last_layers = opts.CLIP_stop_at_last_layers
self.vae = opts.sd_vae
self.uni_pc_order = opts.uni_pc_order
def __exit__(self, exc_type, exc_value, tb):
opts.data["sd_vae"] = self.vae
opts.data["uni_pc_order"] = self.uni_pc_order
modules.sd_models.reload_model_weights()
modules.sd_vae.reload_vae_weights()
sd_models.reload_model_weights()
sd_vae.reload_vae_weights()
opts.data["CLIP_stop_at_last_layers"] = self.CLIP_stop_at_last_layers
@@ -390,15 +390,13 @@ class Script(scripts.Script):
fill_z_button = ToolButton(value=fill_values_symbol, elem_id="xyz_grid_fill_z_tool_button", visible=False)
with gr.Row(variant="compact", elem_id="axis_options"):
with gr.Column():
draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend"))
no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"))
with gr.Column():
include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images"))
include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids"))
with gr.Column():
margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size"))
draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend"))
no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"))
include_lone_images = gr.Checkbox(label='Include Sub Images', value=False, elem_id=self.elem_id("include_lone_images"))
include_sub_grids = gr.Checkbox(label='Include Sub Grids', value=False, elem_id=self.elem_id("include_sub_grids"))
with gr.Row(variant="compact", elem_id="axis_options"):
margin_size = gr.Slider(label="Grid margins (px)", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size"))
with gr.Row(variant="compact", elem_id="swap_axes"):
swap_xy_axes_button = gr.Button(value="Swap X/Y axes", elem_id="xy_grid_swap_axes_button")
swap_yz_axes_button = gr.Button(value="Swap Y/Z axes", elem_id="yz_grid_swap_axes_button")
@@ -428,6 +426,9 @@ class Script(scripts.Script):
current_values = axis_values_dropdown
if has_choices:
choices = choices()
if len(choices) > 12:
has_choices = False
if has_choices:
if isinstance(current_values,str):
current_values = current_values.split(",")
current_values = list(filter(lambda x: x in choices, current_values))
@@ -459,7 +460,7 @@ class Script(scripts.Script):
def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, draw_legend, include_lone_images, include_sub_grids, no_fixed_seeds, margin_size):
if not no_fixed_seeds:
modules.processing.fix_seed(p)
processing.fix_seed(p)
if not opts.return_grid:
p.batch_size = 1
@@ -489,7 +490,7 @@ class Script(scripts.Script):
start = int(mc.group(1))
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()]
else:
valslist_ext.append(val)
@@ -511,7 +512,7 @@ class Script(scripts.Script):
start = float(mc.group(1))
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()
else:
valslist_ext.append(val)
@@ -699,13 +700,12 @@ class Script(scripts.Script):
# Auto-save main and sub-grids:
grid_count = z_count + 1 if z_count > 1 else 1
for g in range(grid_count):
#TODO: See previous comment about intentional data misalignment.
adj_g = g-1 if g > 0 else g
images.save_image(processed.images[g], p.outpath_grids, "xyz_grid", info=processed.infotexts[g], extension=opts.grid_format, prompt=processed.all_prompts[adj_g], seed=processed.all_seeds[adj_g], grid=True, p=processed)
if not include_sub_grids:
# Done with sub-grids, drop all related information:
for sg in range(z_count):
for _sg in range(z_count):
del processed.images[1]
del processed.all_prompts[1]
del processed.all_seeds[1]
+13 -7
View File
@@ -52,6 +52,7 @@ def setup_logging(clean=False):
rh.set_name(logging.DEBUG if args.debug else logging.INFO)
log.addHandler(rh)
# check if package is installed
def installed(package, friendly: str = None):
import pkg_resources
@@ -392,7 +393,8 @@ def set_environment():
os.environ.setdefault('GRADIO_ANALYTICS_ENABLED', 'False')
os.environ.setdefault('SAFETENSORS_FAST_GPU', '1')
os.environ.setdefault('NUMEXPR_MAX_THREADS', '16')
os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1')
if sys.platform == 'darwin':
os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1')
def check_extensions():
@@ -405,6 +407,9 @@ def check_extensions():
for ext in extensions:
newest = 0
extension_dir = os.path.join(folder, ext)
if not os.path.isdir(extension_dir):
log.debug(f'Extension listed as installed but folder missing: {extension_dir}')
continue
for f in os.listdir(extension_dir):
if '.json' in f or '.csv' in f or '__pycache__' in f:
continue
@@ -496,12 +501,9 @@ def check_timestamp():
return ok
def parse_args():
# command line args
# parser = argparse.ArgumentParser(description = 'Setup for SD WebUI')
if vars(parser)['_option_string_actions'].get('--debug', None) is not None:
return
parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s")
def add_args():
if vars(parser)['_option_string_actions'].get('--debug', None) is None:
parser.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s")
parser.add_argument('--reset', default = False, action='store_true', help = "Reset main repository to latest version, default: %(default)s")
parser.add_argument('--upgrade', default = False, action='store_true', help = "Upgrade main repository to latest version, default: %(default)s")
parser.add_argument('--noupdate', default = False, action='store_true', help = "Skip update of extensions and submodules, default: %(default)s")
@@ -510,6 +512,10 @@ def parse_args():
parser.add_argument('--skip-extensions', default = False, action='store_true', help = "Skips running individual extension installers, default: %(default)s")
parser.add_argument('--skip-git', default = False, action='store_true', help = "Skips running all GIT operations, default: %(default)s")
parser.add_argument('--experimental', default = False, action='store_true', help = "Allow unsupported versions of libraries, default: %(default)s")
def parse_args():
# command line args
global args # pylint: disable=global-statement
args = parser.parse_args()
+66 -312
View File
@@ -1,165 +1,36 @@
/* general gradio fixes */
:root, .dark{
--checkbox-label-gap: 0.25em 0.1em;
--section-header-text-size: 12pt;
--block-background-fill: transparent;
}
.block.padded:not(.gradio-accordion) {
padding: 0 !important;
}
div.gradio-container{
max-width: unset !important;
}
.hidden{
display: none;
}
.compact{
background: transparent !important;
padding: 0 !important;
}
div.form{
border-width: 0;
box-shadow: none;
background: transparent;
overflow: visible;
gap: 0.5em;
}
.block.gradio-dropdown,
.block.gradio-slider,
.block.gradio-checkbox,
.block.gradio-textbox,
.block.gradio-radio,
.block.gradio-checkboxgroup,
.block.gradio-number,
.block.gradio-colorpicker
{
border-width: 0 !important;
box-shadow: none !important;
}
.gap.compact{
padding: 0;
gap: 0.2em 0;
}
div.compact{
gap: 1em;
}
.gradio-dropdown label span:not(.has-info),
.gradio-textbox label span:not(.has-info),
.gradio-number label span:not(.has-info)
{
margin-bottom: 0;
}
.gradio-dropdown ul.options{
z-index: 3000;
min-width: fit-content;
max-width: inherit;
white-space: nowrap;
}
.gradio-dropdown ul.options li.item {
padding: 0.05em 0;
}
.gradio-dropdown ul.options li.item:not(:has(.hide)) {
background-color: var(--neutral-100);
}
.dark .gradio-dropdown ul.options li.item:not(:has(.hide)) {
background-color: var(--neutral-900);
}
.gradio-dropdown div.wrap.wrap.wrap.wrap{
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{
flex-wrap: unset;
}
.gradio-dropdown .single-select{
white-space: nowrap;
overflow: hidden;
}
.gradio-dropdown .token-remove.remove-all.remove-all{
display: none;
}
.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{
display: flex;
}
.gradio-slider input[type="number"]{
width: 6em;
}
.block.gradio-checkbox {
margin: 0.75em 1.5em 0 0;
}
.gradio-html div.wrap{
height: 100%;
}
div.gradio-html.min{
min-height: 0;
}
.block.gradio-gallery{
background: var(--input-background-fill);
}
.gradio-container .prose a, .gradio-container .prose a:visited{
color: unset;
text-decoration: none;
}
:root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;}
.block.padded:not(.gradio-accordion) { padding: 0 !important; }
div.gradio-container{ max-width: unset !important; }
.hidden{ display: none; }
.compact{ background: transparent !important; padding: 0 !important; }
div.form{ border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em; }
.block.gradio-dropdown, .block.gradio-slider, .block.gradio-checkbox, .block.gradio-textbox, .block.gradio-radio, .block.gradio-checkboxgroup, .block.gradio-number, .block.gradio-colorpicker { border-width: 0 !important; box-shadow: none !important;}
.gap.compact{ padding: 0; gap: 0.2em 0; }
div.compact{ gap: 1em; }
.gradio-dropdown label span:not(.has-info), .gradio-textbox label span:not(.has-info), .gradio-number label span:not(.has-info) { margin-bottom: 0; }
.gradio-dropdown ul.options{ z-index: 3000; min-width: fit-content; max-width: inherit; white-space: nowrap; }
.gradio-dropdown ul.options li.item { padding: 0.05em 0; }
.gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-100); }
.dark .gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-900); }
.gradio-dropdown div.wrap.wrap.wrap.wrap{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); }
.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ flex-wrap: unset; }
.gradio-dropdown .single-select{ white-space: nowrap; overflow: hidden; }
.gradio-dropdown .token-remove.remove-all.remove-all{ display: none; }
.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{ display: flex; }
.gradio-slider input[type="number"]{ width: 6em; }
.block.gradio-checkbox { margin: 0.75em 1.5em 0 0; }
.gradio-html div.wrap{ height: 100%; }
div.gradio-html.min{ min-height: 0; }
.block.gradio-gallery{ background: var(--input-background-fill); }
.gradio-container .prose a, .gradio-container .prose a:visited{ color: unset; text-decoration: none; }
/* general styled components */
.gradio-button.tool{
max-width: 2.2em;
min-width: 2.2em !important;
height: 2.4em;
align-self: end;
line-height: 1em;
border-radius: 0.5em;
}
.gradio-button.secondary-down{
background: var(--button-secondary-background-fill);
color: var(--button-secondary-text-color);
}
.gradio-button.secondary-down, .gradio-button.secondary-down:hover{
box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset;
}
.gradio-button.secondary-down:hover{
background: var(--button-secondary-background-fill-hover);
color: var(--button-secondary-text-color-hover);
}
.checkboxes-row{
margin-bottom: 0.5em;
margin-left: 0em;
}
.checkboxes-row > div{
flex: 0;
white-space: nowrap;
min-width: auto;
}
.gradio-button.tool{ max-width: 2.2em; min-width: 2.2em !important; height: 2.4em; align-self: end; line-height: 1em; border-radius: 0.5em; }
.gradio-button.secondary-down{ background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); }
.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; }
.gradio-button.secondary-down:hover{ background: var(--button-secondary-background-fill-hover); color: var(--button-secondary-text-color-hover); }
.checkboxes-row{ margin-bottom: 0.5em; margin-left: 0em; }
.checkboxes-row > div{ flex: 0; white-space: nowrap; min-width: auto; }
button.custom-button{
border-radius: var(--button-large-radius);
padding: var(--button-large-padding);
@@ -176,9 +47,7 @@ button.custom-button{
text-align: center;
}
/* txt2img/img2img specific */
.block.token-counter{
position: absolute;
display: inline-block;
@@ -201,13 +70,8 @@ button.custom-button{
border: 2px solid rgba(255,0,0,0.4) !important;
}
.block.token-counter div{
display: inline;
}
.block.token-counter span{
padding: 0.1em 0.75em;
}
.block.token-counter div{ display: inline; }
.block.token-counter span{ padding: 0.1em 0.75em; }
[id$=_subseed_show]{
min-width: auto !important;
@@ -309,6 +173,10 @@ div.dimensions-tools{
align-content: center;
}
div#extras_scale_to_tab div.form{
flex-direction: row;
}
#mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{
height: 480px !important;
max-height: 480px !important;
@@ -638,47 +506,15 @@ footer {
}
/* extra networks UI */
.extra-networks > div > [id *= '_extra_']{ margin: 0.3em; }
.extra-network-subdirs{ padding: 0.2em 0.35em; }
.extra-network-subdirs button{ margin: 0 0.15em; }
.extra-networks .tab-nav .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; width: 16em; }
#txt2img_extra_view, #img2img_extra_view { width: auto; }
.extra-network-cards .nocards, .extra-network-thumbs .nocards{ margin: 1.25em 0.5em 0.5em 0.5em; }
.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ font-size: 1.5em; margin-bottom: 1em; }
.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{ margin-left: 0.5em; }
.extra-networks > div > [id *= '_extra_']{
margin: 0.3em;
}
.extra-network-subdirs{
padding: 0.2em 0.35em;
}
.extra-network-subdirs button{
margin: 0 0.15em;
}
.extra-networks .tab-nav .search{
display: inline-block;
max-width: 16em;
margin: 0.3em;
align-self: center;
width: 16em;
}
#txt2img_extra_view, #img2img_extra_view {
width: auto;
}
.extra-network-cards .nocards, .extra-network-thumbs .nocards{
margin: 1.25em 0.5em 0.5em 0.5em;
}
.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{
font-size: 1.5em;
margin-bottom: 1em;
}
.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{
margin-left: 0.5em;
}
.extra-network-cards .card .metadata-button:before, .extra-network-thumbs .card .metadata-button:before{
content: "🛈";
}
.extra-network-cards .card .metadata-button, .extra-network-thumbs .card .metadata-button{
display: none;
position: absolute;
@@ -689,23 +525,14 @@ footer {
font-size: 22pt;
width: 1.5em;
}
.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{
display: inline-block;
}
.extra-network-cards .card .metadata-button:hover, .extra-network-thumbs .card .metadata-button:hover{
color: red;
}
.extra-network-thumbs {
display: flex;
flex-flow: row wrap;
gap: 10px;
}
.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; }
.extra-network-thumbs { display: flex; flex-flow: row wrap; gap: 10px; }
.extra-network-cards .card .additional a:hover, .extra-network-thumbs .card .additional a:hover { color: darkorange }
.extra-network-thumbs .card {
height: 6em;
width: 6em;
display: inline-block;
height: 9em;
width: 9em;
cursor: pointer;
background-image: url('./file=html/card-no-preview.png');
background-size: cover;
@@ -713,25 +540,8 @@ footer {
position: relative;
}
.extra-network-thumbs .card:hover .additional a {
display: inline-block;
}
.extra-network-thumbs .actions .additional a {
background-image: url('./file=html/image-update.svg');
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
position: absolute;
top: 0;
left: 0;
width: 24px;
height: 24px;
display: none;
font-size: 0;
text-align: -9999;
}
.extra-network-cards .card .additional, .extra-network-thumbs .card .additional { white-space: nowrap; overflow: hidden; }
.extra-network-thumbs .card:hover .additional a { display: inline-block; }
.extra-network-thumbs .actions .name {
position: absolute;
bottom: 0;
@@ -745,11 +555,7 @@ footer {
color: white;
}
.extra-network-thumbs .card:hover .actions .name {
white-space: normal;
word-break: break-all;
}
.extra-network-thumbs .card:hover .actions .name { white-space: normal; word-break: break-all; }
.extra-network-cards .card{
display: inline-block;
margin: 0.5em;
@@ -758,22 +564,15 @@ footer {
box-shadow: 0 0 5px rgba(128, 128, 128, 0.5);
border-radius: 0.2em;
position: relative;
background-size: auto 100%;
background-position: center;
overflow: hidden;
cursor: pointer;
background-image: url('./file=html/card-no-preview.png')
}
.extra-network-cards .card:hover{
box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35);
}
.extra-network-cards .card .actions .additional{
display: none;
}
.extra-network-cards .card:hover { box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); }
.extra-network-cards .card .actions .additional, .extra-network-thumbs .card .actions .additional{ display: none; }
.extra-network-cards .card .actions{
position: absolute;
@@ -786,58 +585,13 @@ footer {
text-shadow: 0 0 0.2em black;
}
.extra-network-cards .card .actions *{
color: white;
}
.extra-network-cards .card .actions:hover{
box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important;
}
.extra-network-cards .card .actions .name{
font-size: 1.7em;
font-weight: bold;
line-break: anywhere;
}
.extra-network-cards .card .actions .description {
display: block;
max-height: 3em;
white-space: pre-wrap;
line-height: 1.1;
}
.extra-network-cards .card .actions .description:hover {
max-height: none;
}
.extra-network-cards .card .actions:hover .additional{
display: block;
}
.extra-network-cards .card ul{
margin: 0.25em 0 0.75em 0.25em;
cursor: unset;
}
.extra-network-cards .card ul a{
cursor: pointer;
}
.extra-network-cards .card ul a:hover{
color: red;
}
.theme-preview {
display: none;
position: fixed;
border: 4px solid var(--neutral-600);
box-shadow: 2px 2px 2px 2px var(--neutral-700);
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
max-width: 75vw;
z-index: 999;
}
.extra-network-cards .card .actions *{ color: white; }
.extra-network-cards .card .actions:hover { box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; }
.extra-network-cards .card .actions .name { font-size: 1.7em; font-weight: bold; line-break: anywhere; }
.extra-network-cards .card .actions .description { display: block; max-height: 3em; white-space: pre-wrap; line-height: 1.1; }
.extra-network-cards .card .actions .description:hover { max-height: none; }
.extra-network-cards .card .actions:hover .additional, .extra-network-thumbs .card:hover .additional{ display: block; }
.extra-network-cards .card ul{ margin: 0.25em 0 0.75em 0.25em; cursor: unset; }
.extra-network-cards .card ul a{ cursor: pointer; }
.extra-network-cards .card ul a:hover{ color: red; }
.theme-preview { display: none; position: fixed; border: 4px solid var(--neutral-600); box-shadow: 2px 2px 2px 2px var(--neutral-700); top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-width: 75vw; z-index: 999; }
+3 -6
View File
@@ -2,10 +2,7 @@
if not defined PYTHON (set PYTHON=python)
if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv")
set ERROR_REPORTING=FALSE
mkdir tmp 2>NUL
%PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt
@@ -38,14 +35,14 @@ goto :show_stdout_stderr
:activate_venv
set PYTHON="%VENV_DIR%\Scripts\Python.exe"
echo venv %PYTHON%
echo Using VENV: %VENV_DIR%
:skip_venv
if [%ACCELERATE%] == ["True"] goto :accelerate
goto :launch
:accelerate
echo Checking for accelerate
echo Checking for accelerate: %ACCELERATE%
set ACCELERATE="%VENV_DIR%\Scripts\accelerate.exe"
if EXIST %ACCELERATE% goto :accelerate_launch
@@ -56,7 +53,7 @@ exit /b
:accelerate_launch
echo Accelerating
%ACCELERATE% launch --num_cpu_threads_per_process=6 launch.py
%ACCELERATE% launch --num_cpu_threads_per_process=6 launch.py %*
pause
exit /b
+24 -6
View File
@@ -1,9 +1,11 @@
import os
import re
import sys
import time
import signal
import warnings
import asyncio
import logging
import warnings
from rich import print # pylint: disable=W0622
from modules import timer, errors
@@ -66,10 +68,13 @@ else:
def check_rollback_vae():
if shared.cmd_opts.rollback_vae:
if not torch.__version__.startswith('2.1'):
if not torch.cuda.is_available():
print("Rollback VAE functionality requires compatible GPU")
shared.cmd_opts.rollback_vae = False
elif not torch.__version__.startswith('2.1'):
print("Rollback VAE functionality requires Torch 2.1 or higher")
shared.cmd_opts.rollback_vae = False
if 0 < torch.cuda.get_device_capability()[0] < 8:
elif 0 < torch.cuda.get_device_capability()[0] < 8:
print('Rollback VAE functionality device capabilities not met')
shared.cmd_opts.rollback_vae = False
@@ -90,9 +95,6 @@ def initialize():
gfpgan.setup_model(opts.gfpgan_models_path)
startup_timer.record("gfpgan")
modelloader.list_builtin_upscalers()
startup_timer.record("upscalers")
modules.scripts.load_scripts()
startup_timer.record("scripts")
@@ -163,9 +165,25 @@ def create_api(app):
return api
def async_policy():
_BasePolicy = asyncio.WindowsSelectorEventLoopPolicy if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy") else asyncio.DefaultEventLoopPolicy
class AnyThreadEventLoopPolicy(_BasePolicy):
def get_event_loop(self) -> asyncio.AbstractEventLoop:
try:
return super().get_event_loop()
except (RuntimeError, AssertionError):
loop = self.new_event_loop()
self.set_event_loop(loop)
return loop
asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
def start_ui():
logging.disable(logging.INFO)
create_paths(opts)
async_policy()
initialize()
if shared.opts.clean_temp_dir_at_start:
ui_tempdir.cleanup_tmpdr()
+16 -93
View File
@@ -4,34 +4,19 @@
# change the variables in webui-user.sh instead #
#################################################
# If run from macOS, load defaults from webui-macos-env.sh
if [[ "$OSTYPE" == "darwin"* ]]; then
if [[ -f webui-macos-env.sh ]]
then
source ./webui-macos-env.sh
fi
fi
# change to local directory
cd -- "$(dirname -- "$0")"
can_run_as_root=0
export ERROR_REPORTING=FALSE
export PIP_IGNORE_INSTALLED=0
# Read variables from webui-user.sh
# shellcheck source=/dev/null
if [[ -f webui-user.sh ]]
then
source ./webui-user.sh
fi
# Set defaults
# Install directory without trailing slash
if [[ -z "${install_dir}" ]]
then
install_dir="${HOME}"
fi
# Name of the subdirectory (defaults to stable-diffusion-webui)
if [[ -z "${clone_dir}" ]]
then
clone_dir="stable-diffusion-webui"
fi
# python3 executable
if [[ -z "${python_cmd}" ]]
then
@@ -44,19 +29,11 @@ then
export GIT="git"
fi
# python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv)
if [[ -z "${venv_dir}" ]]
then
venv_dir="venv"
fi
if [[ -z "${LAUNCH_SCRIPT}" ]]
then
LAUNCH_SCRIPT="launch.py"
fi
# this script cannot be run as root by default
can_run_as_root=0
# read any command line flags to the webui.sh script
while getopts "f" flag > /dev/null 2>&1
@@ -67,102 +44,48 @@ do
esac
done
# Disable sentry logging
export ERROR_REPORTING=FALSE
# Do not reinstall existing pip packages on Debian/Ubuntu
export PIP_IGNORE_INSTALLED=0
# Pretty print
delimiter="################################################################"
printf "\n%s\n" "${delimiter}"
printf "\e[1m\e[32mInstall script for stable-diffusion + Web UI\n"
printf "\e[1m\e[34mTested on Debian 11 (Bullseye)\e[0m"
printf "\n%s\n" "${delimiter}"
# Do not run as root
if [[ $(id -u) -eq 0 && can_run_as_root -eq 0 ]]
then
printf "\n%s\n" "${delimiter}"
printf "\e[1m\e[31mERROR: This script must not be launched as root, aborting...\e[0m"
printf "\n%s\n" "${delimiter}"
echo "Cannot run as root"
exit 1
else
printf "\n%s\n" "${delimiter}"
printf "Running on \e[1m\e[32m%s\e[0m user" "$(whoami)"
printf "\n%s\n" "${delimiter}"
fi
if [[ -d .git ]]
then
printf "\n%s\n" "${delimiter}"
printf "Repo already cloned, using it as install directory"
printf "\n%s\n" "${delimiter}"
install_dir="${PWD}/../"
clone_dir="${PWD##*/}"
fi
for preq in "${GIT}" "${python_cmd}"
do
if ! hash "${preq}" &>/dev/null
then
printf "\n%s\n" "${delimiter}"
printf "\e[1m\e[31mERROR: %s is not installed, aborting...\e[0m" "${preq}"
printf "\n%s\n" "${delimiter}"
printf "Error: %s is not installed, aborting...\n" "${preq}"
exit 1
fi
done
if ! "${python_cmd}" -c "import venv" &>/dev/null
then
printf "\n%s\n" "${delimiter}"
printf "\e[1m\e[31mERROR: python3-venv is not installed, aborting...\e[0m"
printf "\n%s\n" "${delimiter}"
echo "Error: python3-venv is not installed"
exit 1
fi
cd "${install_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/, aborting...\e[0m" "${install_dir}"; exit 1; }
if [[ -d "${clone_dir}" ]]
then
cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; }
else
printf "\n%s\n" "${delimiter}"
printf "Clone stable-diffusion-webui"
printf "\n%s\n" "${delimiter}"
"${GIT}" clone https://github.com/vladmandic/automatic.git "${clone_dir}"
cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; }
fi
printf "\n%s\n" "${delimiter}"
printf "Create and activate python venv"
printf "\n%s\n" "${delimiter}"
cd "${install_dir}"/"${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; }
echo "Create and activate python venv"
if [[ ! -d "${venv_dir}" ]]
then
"${python_cmd}" -m venv "${venv_dir}"
first_launch=1
fi
# shellcheck source=/dev/null
if [[ -f "${venv_dir}"/bin/activate ]]
then
source "${venv_dir}"/bin/activate
else
printf "\n%s\n" "${delimiter}"
printf "\e[1m\e[31mERROR: Cannot activate python venv, aborting...\e[0m"
printf "\n%s\n" "${delimiter}"
echo "Error: Cannot activate python venv"
exit 1
fi
if [[ ! -z "${ACCELERATE}" ]] && [ ${ACCELERATE}="True" ] && [ -x "$(command -v accelerate)" ]
then
printf "\n%s\n" "${delimiter}"
printf "Accelerating launch.py..."
printf "\n%s\n" "${delimiter}"
exec accelerate launch --num_cpu_threads_per_process=6 "${LAUNCH_SCRIPT}" "$@"
echo "Accelerating launch.py..."
exec accelerate launch --num_cpu_threads_per_process=6 launch.py "$@"
else
printf "\n%s\n" "${delimiter}"
printf "Launching launch.py..."
printf "\n%s\n" "${delimiter}"
exec "${python_cmd}" "${LAUNCH_SCRIPT}" "$@"
echo "Launching launch.py..."
exec "${python_cmd}" launch.py "$@"
fi