mirror of
https://github.com/vladmandic/automatic
synced 2026-09-18 08:44:33 +02:00
Merge branch 'master' into directml
This commit is contained in:
@@ -24,6 +24,11 @@ venv
|
||||
*.zip
|
||||
*.rar
|
||||
*.pyc
|
||||
/*.bat
|
||||
/*.sh
|
||||
/*.txt
|
||||
!webui.bat
|
||||
!webui.sh
|
||||
|
||||
# all dynamic stuff
|
||||
/repositories/**/*
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
mediapipe
|
||||
colormap
|
||||
invisible-watermark
|
||||
filetype
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Vendored
+14
-37
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
)
|
||||
|
||||
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user