mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 01:04:32 +02:00
jumbo patch
This commit is contained in:
@@ -25,8 +25,6 @@ Stuff to be added...
|
||||
|
||||
Stuff to be investigated...
|
||||
|
||||
- TXT2IMG: <https://github.com/vladmandic/automatic/discussions/814>
|
||||
- `TensorRT`
|
||||
|
||||
## Merge PRs
|
||||
|
||||
@@ -51,6 +49,7 @@ Tech that can be integrated as part of the core workflow...
|
||||
- [Custom diffusion](https://github.com/guaneec/custom-diffusion-webui), [Custom diffusion](https://www.cs.cmu.edu/~custom-diffusion/)
|
||||
- [Dream artist](https://github.com/7eu7d7/DreamArtist-sd-webui-extension)
|
||||
- [QuickEmbedding](https://github.com/ethansmith2000/QuickEmbedding)
|
||||
- `TensorRT`
|
||||
|
||||
## Random
|
||||
|
||||
@@ -58,7 +57,19 @@ Tech that can be integrated as part of the core workflow...
|
||||
|
||||
### Pending Code Updates
|
||||
|
||||
- add `--safe` mode which skips loading user extensions
|
||||
This is a massive one due to huge number of changes, but hopefully it will fo ok...
|
||||
|
||||
- new **prompt parsers**
|
||||
select in UI -> Settings -> Stable Diffusion
|
||||
- **Full**: my new implementation
|
||||
- **A1111**: for backward compatibility
|
||||
- **Compel**: as used in ComfyUI and InvokeAI (a.k.a *Temporal Weighting*)
|
||||
- **Fixed**: for really old backward compatibility
|
||||
- added `--safe` command line flag mode which skips loading user extensions
|
||||
please try to use it before opening new issue
|
||||
- add option in settings: **Prompt attention parser**
|
||||
to allow for backward compatibility with a1111 (broken) parser
|
||||
- reintroduce `--api-only` mode to start server without ui
|
||||
- monitor **extensions** install/startup and
|
||||
log if they modify any packages/requirements
|
||||
this is a *deep-experimental* python hack, but i think its worth it as extensions modifying requirements is one of most common causes of issues
|
||||
- port *all* upstream code from [A1111](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
|
||||
up to today - commit hash `89f9faa`
|
||||
|
||||
@@ -20,7 +20,6 @@ def before_ui():
|
||||
ui_extra_networks.register_page(ui_extra_networks_lora.ExtraNetworksPageLora())
|
||||
extra_networks.register_extra_network(extra_networks_lora.ExtraNetworkLora())
|
||||
|
||||
|
||||
if not hasattr(torch.nn, 'Linear_forward_before_lora'):
|
||||
torch.nn.Linear_forward_before_lora = torch.nn.Linear.forward
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
|
||||
def list_items(self):
|
||||
for name, lora_on_disk in lora.available_loras.items():
|
||||
path, ext = os.path.splitext(lora_on_disk.filename)
|
||||
path, _ext = os.path.splitext(lora_on_disk.filename)
|
||||
|
||||
if shared.opts.lora_preferred_name == "Filename" or lora_on_disk.alias.lower() in lora.forbidden_lora_aliases:
|
||||
alias = name
|
||||
@@ -34,4 +34,3 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
|
||||
def allowed_directories_for_previews(self):
|
||||
return [shared.cmd_opts.lora_dir]
|
||||
|
||||
|
||||
+14
-1
@@ -43,7 +43,7 @@ args = Dot({
|
||||
'version': False,
|
||||
'ignore': False,
|
||||
})
|
||||
|
||||
git_commit = "unknown"
|
||||
|
||||
# setup console and file logging
|
||||
def setup_logging(clean=False):
|
||||
@@ -369,6 +369,10 @@ def list_extensions(folder):
|
||||
|
||||
# run installer for each installed and enabled extension and optionally update them
|
||||
def install_extensions():
|
||||
import pkg_resources
|
||||
pkg_resources._initialize_master_working_set() # pylint: disable=protected-access
|
||||
pkgs = [f'{p.project_name}=={p._version}' for p in pkg_resources.working_set] # pylint: disable=protected-access,not-an-iterable
|
||||
log.debug(f'Installed packages: {len(pkgs)}')
|
||||
from modules.paths_internal import extensions_builtin_dir, extensions_dir
|
||||
extensions_duplicates = []
|
||||
extensions_enabled = []
|
||||
@@ -390,6 +394,12 @@ def install_extensions():
|
||||
log.error(f'Error updating extension: {os.path.join(folder, ext)}')
|
||||
if not args.skip_extensions:
|
||||
run_extension_installer(os.path.join(folder, ext))
|
||||
pkg_resources._initialize_master_working_set() # pylint: disable=protected-access
|
||||
updated = [f'{p.project_name}=={p._version}' for p in pkg_resources.working_set] # pylint: disable=protected-access,not-an-iterable
|
||||
diff = [x for x in updated if x not in pkgs]
|
||||
pkgs = updated
|
||||
if len(diff) > 0:
|
||||
log.info(f'Extension installed packages: {ext} {diff}')
|
||||
log.info(f'Extensions enabled: {extensions_enabled}')
|
||||
if len(extensions_duplicates) > 0:
|
||||
log.warning(f'Extensions duplicates: {extensions_duplicates}')
|
||||
@@ -500,6 +510,8 @@ def check_version(offline=False, reset=True): # pylint: disable=unused-argument
|
||||
if args.version:
|
||||
return
|
||||
commit = git('rev-parse HEAD')
|
||||
global git_commit # pylint: disable=global-statement
|
||||
git_commit = commit[:7]
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
@@ -583,6 +595,7 @@ def add_args():
|
||||
group.add_argument('--debug', default = False, action='store_true', help = "Run installer with debug logging, default: %(default)s")
|
||||
group.add_argument('--reset', default = False, action='store_true', help = "Reset main repository to latest version, default: %(default)s")
|
||||
group.add_argument('--upgrade', default = False, action='store_true', help = "Upgrade main repository to latest version, default: %(default)s")
|
||||
group.add_argument('--api-only', default = False, action='store_true', help = "Run in API only mode without starting UI")
|
||||
group.add_argument("--use-ipex", default = False, action='store_true', help="Use Intel OneAPI XPU backend, default: %(default)s")
|
||||
group.add_argument('--use-directml', default = False, action='store_true', help = "Use DirectML if no compatible GPU is detected, default: %(default)s")
|
||||
group.add_argument("--use-cuda", default=False, action='store_true', help="Force use nVidia CUDA backend, default: %(default)s")
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"no-unused-vars":"off",
|
||||
"no-plusplus":"off",
|
||||
"no-param-reassign":"off",
|
||||
"no-restricted-syntax":"off"
|
||||
"no-restricted-syntax":"off",
|
||||
"no-mixed-operators":"off"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* global gradioApp, onUiUpdate, get_tab_index */
|
||||
|
||||
let currentWidth = null;
|
||||
let currentHeight = null;
|
||||
let arFrameTimeout = setTimeout(() => {}, 0);
|
||||
@@ -9,7 +11,7 @@ function dimensionChange(e, is_width, is_height) {
|
||||
if (!inImg2img) return;
|
||||
let targetElement = null;
|
||||
const tabIndex = get_tab_index('mode_img2img');
|
||||
if (tabIndex === 0) targetElement = gradioApp().querySelector('#img2img_image div[data-testid=image] img'); // img2img
|
||||
if (tabIndex === 0) targetElement = gradioApp().querySelector('#img2img_image div[data-testid=image] img'); // img2img
|
||||
else if (tabIndex === 1) targetElement = gradioApp().querySelector('#img2img_sketch div[data-testid=image] img'); // Sketch
|
||||
else if (tabIndex === 2) targetElement = gradioApp().querySelector('#img2maskimg div[data-testid=image] img'); // Inpaint
|
||||
else if (tabIndex === 3) targetElement = gradioApp().querySelector('#inpaint_sketch div[data-testid=image] img'); // Inpaint sketch
|
||||
@@ -23,26 +25,20 @@ function dimensionChange(e, is_width, is_height) {
|
||||
}
|
||||
|
||||
const viewportOffset = targetElement.getBoundingClientRect();
|
||||
|
||||
const viewportscale = Math.min(targetElement.clientWidth / targetElement.naturalWidth, targetElement.clientHeight / targetElement.naturalHeight);
|
||||
|
||||
const scaledx = targetElement.naturalWidth * viewportscale;
|
||||
const scaledy = targetElement.naturalHeight * viewportscale;
|
||||
|
||||
const cleintRectTop = (viewportOffset.top + window.scrollY);
|
||||
const cleintRectLeft = (viewportOffset.left + window.scrollX);
|
||||
const cleintRectCentreY = cleintRectTop + (targetElement.clientHeight / 2);
|
||||
const cleintRectCentreX = cleintRectLeft + (targetElement.clientWidth / 2);
|
||||
|
||||
const arscale = Math.min(scaledx / currentWidth, scaledy / currentHeight);
|
||||
const arscaledx = currentWidth * arscale;
|
||||
const arscaledy = currentHeight * arscale;
|
||||
|
||||
const arRectTop = cleintRectCentreY - (arscaledy / 2);
|
||||
const arRectLeft = cleintRectCentreX - (arscaledx / 2);
|
||||
const arRectWidth = arscaledx;
|
||||
const arRectHeight = arscaledy;
|
||||
|
||||
arPreviewRect.style.top = `${arRectTop}px`;
|
||||
arPreviewRect.style.left = `${arRectLeft}px`;
|
||||
arPreviewRect.style.width = `${arRectWidth}px`;
|
||||
@@ -58,9 +54,7 @@ function dimensionChange(e, is_width, is_height) {
|
||||
|
||||
onUiUpdate(() => {
|
||||
const arPreviewRect = gradioApp().querySelector('#imageARPreview');
|
||||
if (arPreviewRect) {
|
||||
arPreviewRect.style.display = 'none';
|
||||
}
|
||||
if (arPreviewRect) arPreviewRect.style.display = 'none';
|
||||
const tabImg2img = gradioApp().querySelector('#tab_img2img');
|
||||
if (tabImg2img) {
|
||||
const inImg2img = tabImg2img.style.display === 'block';
|
||||
@@ -69,17 +63,12 @@ onUiUpdate(() => {
|
||||
inputs.forEach((e) => {
|
||||
const is_width = e.parentElement.id === 'img2img_width';
|
||||
const is_height = e.parentElement.id === 'img2img_height';
|
||||
|
||||
if ((is_width || is_height) && !e.classList.contains('scrollwatch')) {
|
||||
e.addEventListener('input', (e) => { dimensionChange(e, is_width, is_height); });
|
||||
e.addEventListener('input', (evt) => { dimensionChange(evt, is_width, is_height); });
|
||||
e.classList.add('scrollwatch');
|
||||
}
|
||||
if (is_width) {
|
||||
currentWidth = e.value * 1.0;
|
||||
}
|
||||
if (is_height) {
|
||||
currentHeight = e.value * 1.0;
|
||||
}
|
||||
if (is_width) currentWidth = e.value * 1.0;
|
||||
if (is_height) currentHeight = e.value * 1.0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+17
-47
@@ -1,23 +1,18 @@
|
||||
contextMenuInit = function () {
|
||||
/* global gradioApp, uiCurrentTab, onUiUpdate, get_uiCurrentTabContent */
|
||||
|
||||
const contextMenuInit = () => {
|
||||
let eventListenerApplied = false;
|
||||
const menuSpecs = new Map();
|
||||
|
||||
const uid = function () {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
||||
};
|
||||
const uid = () => Date.now().toString(36) + Math.random().toString(36).substring(2);
|
||||
|
||||
function showContextMenu(event, element, menuEntries) {
|
||||
const posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
|
||||
const posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
|
||||
|
||||
const oldMenu = gradioApp().querySelector('#context-menu');
|
||||
if (oldMenu) {
|
||||
oldMenu.remove();
|
||||
}
|
||||
|
||||
if (oldMenu) oldMenu.remove();
|
||||
const tabButton = uiCurrentTab;
|
||||
const baseStyle = window.getComputedStyle(tabButton);
|
||||
|
||||
const contextMenu = document.createElement('nav');
|
||||
contextMenu.id = 'context-menu';
|
||||
contextMenu.style.background = baseStyle.background;
|
||||
@@ -25,40 +20,26 @@ contextMenuInit = function () {
|
||||
contextMenu.style.fontFamily = baseStyle.fontFamily;
|
||||
contextMenu.style.top = `${posy}px`;
|
||||
contextMenu.style.left = `${posx}px`;
|
||||
|
||||
const contextMenuList = document.createElement('ul');
|
||||
contextMenuList.className = 'context-menu-items';
|
||||
contextMenu.append(contextMenuList);
|
||||
|
||||
menuEntries.forEach((entry) => {
|
||||
const contextMenuEntry = document.createElement('a');
|
||||
contextMenuEntry.innerHTML = entry.name;
|
||||
contextMenuEntry.addEventListener('click', (e) => {
|
||||
entry.func();
|
||||
});
|
||||
contextMenuEntry.addEventListener('click', (e) => entry.func());
|
||||
contextMenuList.append(contextMenuEntry);
|
||||
});
|
||||
|
||||
gradioApp().appendChild(contextMenu);
|
||||
|
||||
const menuWidth = contextMenu.offsetWidth + 4;
|
||||
const menuHeight = contextMenu.offsetHeight + 4;
|
||||
|
||||
const windowWidth = window.innerWidth;
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
if ((windowWidth - posx) < menuWidth) {
|
||||
contextMenu.style.left = `${windowWidth - menuWidth}px`;
|
||||
}
|
||||
|
||||
if ((windowHeight - posy) < menuHeight) {
|
||||
contextMenu.style.top = `${windowHeight - menuHeight}px`;
|
||||
}
|
||||
if ((windowWidth - posx) < menuWidth) contextMenu.style.left = `${windowWidth - menuWidth}px`;
|
||||
if ((windowHeight - posy) < menuHeight) contextMenu.style.top = `${windowHeight - menuHeight}px`;
|
||||
}
|
||||
|
||||
function appendContextMenuOption(targetElementSelector, entryName, entryFunction) {
|
||||
currentItems = menuSpecs.get(targetElementSelector);
|
||||
|
||||
let currentItems = menuSpecs.get(targetElementSelector);
|
||||
if (!currentItems) {
|
||||
currentItems = [];
|
||||
menuSpecs.set(targetElementSelector, currentItems);
|
||||
@@ -69,7 +50,6 @@ contextMenuInit = function () {
|
||||
func: entryFunction,
|
||||
isNew: true,
|
||||
};
|
||||
|
||||
currentItems.push(newItem);
|
||||
return newItem.id;
|
||||
}
|
||||
@@ -89,15 +69,11 @@ contextMenuInit = function () {
|
||||
gradioApp().addEventListener('click', (e) => {
|
||||
if (!e.isTrusted) return;
|
||||
const oldMenu = gradioApp().querySelector('#context-menu');
|
||||
if (oldMenu) {
|
||||
oldMenu.remove();
|
||||
}
|
||||
if (oldMenu) oldMenu.remove();
|
||||
});
|
||||
gradioApp().addEventListener('contextmenu', (e) => {
|
||||
const oldMenu = gradioApp().querySelector('#context-menu');
|
||||
if (oldMenu) {
|
||||
oldMenu.remove();
|
||||
}
|
||||
if (oldMenu) oldMenu.remove();
|
||||
menuSpecs.forEach((v, k) => {
|
||||
if (e.composedPath()[0].matches(k)) {
|
||||
showContextMenu(e, e.composedPath()[0], v);
|
||||
@@ -107,14 +83,13 @@ contextMenuInit = function () {
|
||||
});
|
||||
eventListenerApplied = true;
|
||||
}
|
||||
|
||||
return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener];
|
||||
};
|
||||
|
||||
initResponse = contextMenuInit();
|
||||
appendContextMenuOption = initResponse[0];
|
||||
removeContextMenuOption = initResponse[1];
|
||||
addContextMenuEventListener = initResponse[2];
|
||||
const initResponse = contextMenuInit();
|
||||
const appendContextMenuOption = initResponse[0];
|
||||
const removeContextMenuOption = initResponse[1];
|
||||
const addContextMenuEventListener = initResponse[2];
|
||||
|
||||
(function () {
|
||||
// Start example Context Menu Items
|
||||
@@ -128,9 +103,7 @@ addContextMenuEventListener = initResponse[2];
|
||||
window.generateOnRepeatInterval = setInterval(
|
||||
() => {
|
||||
const busy = document.getElementById('progressbar')?.style.display === 'block';
|
||||
if (!busy) {
|
||||
genbutton.click();
|
||||
}
|
||||
if (!busy) genbutton.click();
|
||||
},
|
||||
500,
|
||||
);
|
||||
@@ -151,7 +124,6 @@ addContextMenuEventListener = initResponse[2];
|
||||
appendContextMenuOption('#txt2img_generate', 'Cancel generate forever', cancelGenerateForever);
|
||||
appendContextMenuOption('#img2img_interrupt', 'Cancel generate forever', cancelGenerateForever);
|
||||
appendContextMenuOption('#img2img_generate', 'Cancel generate forever', cancelGenerateForever);
|
||||
|
||||
appendContextMenuOption(
|
||||
'#roll',
|
||||
'Roll three',
|
||||
@@ -165,6 +137,4 @@ addContextMenuEventListener = initResponse[2];
|
||||
}());
|
||||
// End example Context Menu Items
|
||||
|
||||
onUiUpdate(() => {
|
||||
addContextMenuEventListener();
|
||||
});
|
||||
onUiUpdate(() => addContextMenuEventListener());
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
function setInactive(elem, inactive) {
|
||||
if (inactive) elem.classList.add('inactive');
|
||||
else elem.classList.remove('inactive');
|
||||
}
|
||||
|
||||
/* global gradioApp, opts */
|
||||
function onCalcResolutionHires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y) {
|
||||
function setInactive(elem, inactive) {
|
||||
elem.classList.toggle('inactive', !!inactive);
|
||||
}
|
||||
const hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale');
|
||||
const hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x');
|
||||
const hrResizeY = gradioApp().getElementById('txt2img_hr_resize_y');
|
||||
gradioApp().getElementById('txt2img_hires_fix_row2').style.display = opts.use_old_hires_fix_width_height ? 'none' : '';
|
||||
setInactive(hrUpscaleBy, opts.use_old_hires_fix_width_height || hr_resize_x > 0 || hr_resize_y > 0);
|
||||
setInactive(hrResizeX, opts.use_old_hires_fix_width_height || hr_resize_x === 0);
|
||||
setInactive(hrResizeY, opts.use_old_hires_fix_width_height || hr_resize_y === 0);
|
||||
// return [enable, width, height, hr_scale, hr_resize_x, hr_resize_y];
|
||||
setTimeout(() => [enable, width, height, hr_scale, hr_resize_x, hr_resize_y], 100);
|
||||
setInactive(hrResizeX, opts.use_old_hires_fix_width_height || hr_resize_x == 0);
|
||||
setInactive(hrResizeY, opts.use_old_hires_fix_width_height || hr_resize_y == 0);
|
||||
return [enable, width, height, hr_scale, hr_resize_x, hr_resize_y];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global gradioApp, onUiUpdate */
|
||||
/**
|
||||
* temporary fix for https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/668
|
||||
* @see https://github.com/gradio-app/gradio/issues/1721
|
||||
@@ -5,7 +6,6 @@
|
||||
function imageMaskResize() {
|
||||
const canvases = gradioApp().querySelectorAll('#img2maskimg .touch-none canvas');
|
||||
if (!canvases.length) {
|
||||
canvases_fixed = false;
|
||||
window.removeEventListener('resize', imageMaskResize);
|
||||
return;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ function imageMaskResize() {
|
||||
const previewImage = wrapper.previousElementSibling;
|
||||
|
||||
if (!previewImage.complete) {
|
||||
previewImage.addEventListener('load', () => imageMaskResize());
|
||||
previewImage.addEventListener('load', imageMaskResize);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ function imageMaskResize() {
|
||||
const nw = previewImage.naturalWidth;
|
||||
const nh = previewImage.naturalHeight;
|
||||
const portrait = nh > nw;
|
||||
const factor = portrait;
|
||||
|
||||
const wW = Math.min(w, portrait ? h / nh * nw : w / nw * nw);
|
||||
const wH = Math.min(h, portrait ? h / nh * nh : w / nw * nh);
|
||||
@@ -34,12 +33,13 @@ function imageMaskResize() {
|
||||
wrapper.style.top = '0px';
|
||||
|
||||
canvases.forEach((c) => {
|
||||
c.style.width = c.style.height = '';
|
||||
c.style.width = '';
|
||||
c.style.height = '';
|
||||
c.style.maxWidth = '100%';
|
||||
c.style.maxHeight = '100%';
|
||||
c.style.objectFit = 'contain';
|
||||
});
|
||||
}
|
||||
|
||||
onUiUpdate(imageMaskResize);
|
||||
window.addEventListener('resize', imageMaskResize);
|
||||
onUiUpdate(() => imageMaskResize());
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/* global gradioApp, get_tab_index */
|
||||
window.onload = (function () {
|
||||
window.addEventListener('drop', (e) => {
|
||||
const target = e.composedPath()[0];
|
||||
if (!target.placeholder) return;
|
||||
const idx = selected_gallery_index();
|
||||
if (target.placeholder.indexOf('Prompt') == -1) return;
|
||||
|
||||
const prompt_target = get_tab_index('tabs') == 1 ? 'img2img_prompt_image' : 'txt2img_prompt_image';
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const imgParent = gradioApp().getElementById(prompt_target);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global gradioApp, onUiUpdate */
|
||||
// A full size 'lightbox' preview modal shown when left clicking on gallery previews
|
||||
function closeModal() {
|
||||
gradioApp().getElementById('lightboxModal').style.display = 'none';
|
||||
@@ -96,6 +97,11 @@ function modalKeyHandler(event) {
|
||||
}
|
||||
}
|
||||
|
||||
function modalZoomSet(modalImage, enable) {
|
||||
localStorage.setItem('modalZoom', enable ? 'yes' : 'no');
|
||||
if (modalImage) modalImage.classList.toggle('modalImageFullscreen', !!enable);
|
||||
}
|
||||
|
||||
function setupImageForLightbox(e) {
|
||||
if (e.dataset.modded) return;
|
||||
e.dataset.modded = true;
|
||||
@@ -106,21 +112,15 @@ function setupImageForLightbox(e) {
|
||||
const event = isFirefox ? 'mousedown' : 'click';
|
||||
e.addEventListener(event, (evt) => {
|
||||
if (evt.button != 0) return;
|
||||
initialZoom = (localStorage.getItem('modalZoom') || true) == 'yes';
|
||||
const initialZoom = (localStorage.getItem('modalZoom') || true) == 'yes';
|
||||
modalZoomSet(gradioApp().getElementById('modalImage'), initialZoom);
|
||||
evt.preventDefault();
|
||||
showModal(evt);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function modalZoomSet(modalImage, enable) {
|
||||
if (enable) modalImage.classList.add('modalImageFullscreen');
|
||||
else modalImage.classList.remove('modalImageFullscreen');
|
||||
localStorage.setItem('modalZoom', enable ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
function modalZoomToggle(event) {
|
||||
modalImage = gradioApp().getElementById('modalImage');
|
||||
const modalImage = gradioApp().getElementById('modalImage');
|
||||
modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen'));
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
+106
-106
@@ -1,133 +1,133 @@
|
||||
/* global opts */
|
||||
function rememberGallerySelection(id_gallery) {}
|
||||
|
||||
function getGallerySelectedIndex(id_gallery) {}
|
||||
|
||||
function request(url, data, handler, errorHandler) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var url = url;
|
||||
xhr.open("POST", url, true);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
var js = JSON.parse(xhr.responseText);
|
||||
handler(js)
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
errorHandler()
|
||||
}
|
||||
} else{
|
||||
errorHandler()
|
||||
}
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
const js = JSON.parse(xhr.responseText);
|
||||
handler(js);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
errorHandler();
|
||||
}
|
||||
};
|
||||
var js = JSON.stringify(data);
|
||||
xhr.send(js);
|
||||
} else {
|
||||
errorHandler();
|
||||
}
|
||||
}
|
||||
};
|
||||
const js = JSON.stringify(data);
|
||||
xhr.send(js);
|
||||
}
|
||||
|
||||
function pad2(x) {
|
||||
return x<10 ? '0'+x : x
|
||||
return x < 10 ? `0${x}` : x;
|
||||
}
|
||||
|
||||
function formatTime(secs) {
|
||||
if(secs > 3600) return pad2(Math.floor(secs/60/60)) + ":" + pad2(Math.floor(secs/60)%60) + ":" + pad2(Math.floor(secs)%60)
|
||||
else if(secs > 60) return pad2(Math.floor(secs/60)) + ":" + pad2(Math.floor(secs)%60)
|
||||
else return Math.floor(secs) + "s"
|
||||
if (secs > 3600) return `${pad2(Math.floor(secs / 60 / 60))}:${pad2(Math.floor(secs / 60) % 60)}:${pad2(Math.floor(secs) % 60)}`;
|
||||
if (secs > 60) return `${pad2(Math.floor(secs / 60))}:${pad2(Math.floor(secs) % 60)}`;
|
||||
return `${Math.floor(secs)}s`;
|
||||
}
|
||||
|
||||
function setTitle(progress) {
|
||||
var title = 'SD.Next'
|
||||
if (progress) title += ' ' + progress.split(' ')[0].trim();
|
||||
if (document.title != title) document.title = title;
|
||||
let title = 'SD.Next';
|
||||
if (progress) title += ` ${progress.split(' ')[0].trim()}`;
|
||||
if (document.title != title) document.title = title;
|
||||
}
|
||||
|
||||
function randomId() {
|
||||
return "task(" + Math.random().toString(36).slice(2, 7) + Math.random().toString(36).slice(2, 7) + Math.random().toString(36).slice(2, 7)+")"
|
||||
return `task(${Math.random().toString(36).slice(2, 7)}${Math.random().toString(36).slice(2, 7)}${Math.random().toString(36).slice(2, 7)})`;
|
||||
}
|
||||
|
||||
// starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and
|
||||
// preview inside gallery element. Cleans up all created stuff when the task is over and calls atEnd.
|
||||
// calls onProgress every time there is a progress update
|
||||
function requestProgress(id_task, progressbarContainer, gallery, atEnd = null, onProgress = null, once = false) {
|
||||
var hasStarted = false
|
||||
var dateStart = new Date()
|
||||
var prevProgress = null
|
||||
var parentProgressbar = progressbarContainer.parentNode
|
||||
var parentGallery = gallery ? gallery.parentNode : null
|
||||
var divProgress = document.createElement('div')
|
||||
divProgress.className='progressDiv'
|
||||
divProgress.id = 'progressbar'
|
||||
divProgress.style.display = opts.show_progressbar ? "block" : "none"
|
||||
var divInner = document.createElement('div')
|
||||
divInner.className='progress'
|
||||
divProgress.appendChild(divInner)
|
||||
parentProgressbar.insertBefore(divProgress, progressbarContainer)
|
||||
localStorage.setItem('task', id_task);
|
||||
console.debug('task active:', id_task)
|
||||
if (parentGallery) {
|
||||
var livePreview = document.createElement('div')
|
||||
livePreview.className='livePreview'
|
||||
parentGallery.insertBefore(livePreview, gallery)
|
||||
}
|
||||
let hasStarted = false;
|
||||
const dateStart = new Date();
|
||||
const prevProgress = null;
|
||||
const parentProgressbar = progressbarContainer.parentNode;
|
||||
const parentGallery = gallery ? gallery.parentNode : null;
|
||||
const divProgress = document.createElement('div');
|
||||
divProgress.className = 'progressDiv';
|
||||
divProgress.id = 'progressbar';
|
||||
divProgress.style.display = opts.show_progressbar ? 'block' : 'none';
|
||||
const divInner = document.createElement('div');
|
||||
divInner.className = 'progress';
|
||||
divProgress.appendChild(divInner);
|
||||
parentProgressbar.insertBefore(divProgress, progressbarContainer);
|
||||
localStorage.setItem('task', id_task);
|
||||
console.debug('task active:', id_task);
|
||||
if (parentGallery) {
|
||||
const livePreview = document.createElement('div');
|
||||
livePreview.className = 'livePreview';
|
||||
parentGallery.insertBefore(livePreview, gallery);
|
||||
}
|
||||
|
||||
var removeProgressBar = function() {
|
||||
console.debug('task end: ', id_task)
|
||||
localStorage.removeItem('task');
|
||||
setTitle("")
|
||||
if (divProgress) parentProgressbar.removeChild(divProgress)
|
||||
if (parentGallery) parentGallery.removeChild(livePreview)
|
||||
if (atEnd) atEnd()
|
||||
}
|
||||
const removeProgressBar = function () {
|
||||
console.debug('task end: ', id_task);
|
||||
localStorage.removeItem('task');
|
||||
setTitle('');
|
||||
if (divProgress) parentProgressbar.removeChild(divProgress);
|
||||
if (parentGallery) parentGallery.removeChild(livePreview);
|
||||
if (atEnd) atEnd();
|
||||
};
|
||||
|
||||
var fun = function(id_task, id_live_preview){
|
||||
request("./internal/progress", {"id_task": id_task, "id_live_preview": id_live_preview}, function(res){
|
||||
var elapsedFromStart = (new Date() - dateStart) / 1000
|
||||
if (res.completed) {
|
||||
removeProgressBar()
|
||||
return
|
||||
}
|
||||
var rect = progressbarContainer.getBoundingClientRect()
|
||||
if (rect.width) divProgress.style.width = rect.width + "px";
|
||||
progressText = ""
|
||||
divInner.style.width = ((res.progress || 0) * 100.0) + '%'
|
||||
divInner.style.background = res.progress ? "" : "transparent"
|
||||
if (res.progress > 0) progressText = ((res.progress || 0) * 100.0).toFixed(0) + '%'
|
||||
if (res.eta) progressText += " ETA: " + formatTime(res.eta)
|
||||
setTitle(progressText)
|
||||
if (res.textinfo && res.textinfo.indexOf("\n") == -1) progressText = res.textinfo + " " + progressText
|
||||
divInner.textContent = progressText
|
||||
hasStarted |= res.active
|
||||
if (!res.active && (hasStarted || once)) {
|
||||
removeProgressBar()
|
||||
return
|
||||
}
|
||||
if (res.completed) {
|
||||
removeProgressBar()
|
||||
return
|
||||
}
|
||||
if (elapsedFromStart > 30 && !res.queued && res.progress == prevProgress) {
|
||||
removeProgressBar()
|
||||
return
|
||||
}
|
||||
if (res.live_preview && gallery) {
|
||||
var rect = gallery.getBoundingClientRect()
|
||||
if(rect.width){
|
||||
livePreview.style.width = rect.width + "px"
|
||||
livePreview.style.height = rect.height + "px"
|
||||
}
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
livePreview.appendChild(img)
|
||||
if (livePreview.childElementCount > 2) livePreview.removeChild(livePreview.firstElementChild)
|
||||
}
|
||||
img.src = res.live_preview;
|
||||
}
|
||||
if (onProgress) onProgress(res)
|
||||
setTimeout(() => fun(id_task, res.id_live_preview), opts.live_preview_refresh_period || 250)
|
||||
}, function() {
|
||||
removeProgressBar()
|
||||
})
|
||||
}
|
||||
fun(id_task, 0)
|
||||
const fun = function (id_task, id_live_preview) {
|
||||
request('./internal/progress', { id_task, id_live_preview }, (res) => {
|
||||
const elapsedFromStart = (new Date() - dateStart) / 1000;
|
||||
if (res.completed) {
|
||||
removeProgressBar();
|
||||
return;
|
||||
}
|
||||
var rect = progressbarContainer.getBoundingClientRect();
|
||||
if (rect.width) divProgress.style.width = `${rect.width}px`;
|
||||
progressText = '';
|
||||
divInner.style.width = `${(res.progress || 0) * 100.0}%`;
|
||||
divInner.style.background = res.progress ? '' : 'transparent';
|
||||
if (res.progress > 0) progressText = `${((res.progress || 0) * 100.0).toFixed(0)}%`;
|
||||
if (res.eta) progressText += ` ETA: ${formatTime(res.eta)}`;
|
||||
setTitle(progressText);
|
||||
if (res.textinfo && res.textinfo.indexOf('\n') == -1) progressText = `${res.textinfo} ${progressText}`;
|
||||
divInner.textContent = progressText;
|
||||
hasStarted |= res.active;
|
||||
if (!res.active && (hasStarted || once)) {
|
||||
removeProgressBar();
|
||||
return;
|
||||
}
|
||||
if (res.completed) {
|
||||
removeProgressBar();
|
||||
return;
|
||||
}
|
||||
if (elapsedFromStart > 30 && !res.queued && res.progress == prevProgress) {
|
||||
removeProgressBar();
|
||||
return;
|
||||
}
|
||||
if (res.live_preview && gallery) {
|
||||
var rect = gallery.getBoundingClientRect();
|
||||
if (rect.width) {
|
||||
livePreview.style.width = `${rect.width}px`;
|
||||
livePreview.style.height = `${rect.height}px`;
|
||||
}
|
||||
const img = new Image();
|
||||
img.onload = function () {
|
||||
livePreview.appendChild(img);
|
||||
if (livePreview.childElementCount > 2) livePreview.removeChild(livePreview.firstElementChild);
|
||||
};
|
||||
img.src = res.live_preview;
|
||||
}
|
||||
if (onProgress) onProgress(res);
|
||||
setTimeout(() => fun(id_task, res.id_live_preview), opts.live_preview_refresh_period || 250);
|
||||
}, () => {
|
||||
removeProgressBar();
|
||||
});
|
||||
};
|
||||
fun(id_task, 0);
|
||||
}
|
||||
|
||||
+21
-1
@@ -108,7 +108,12 @@ button.custom-button{
|
||||
}
|
||||
}
|
||||
|
||||
#txt2img_gallery img, #img2img_gallery img{
|
||||
a{
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#txt2img_gallery img, #img2img_gallery img, #extras_gallery img{
|
||||
object-fit: scale-down;
|
||||
}
|
||||
#txt2img_actions_column, #img2img_actions_column {
|
||||
@@ -406,6 +411,21 @@ div#extras_scale_to_tab div.form{
|
||||
#lightboxModal > img.modalImageFullscreen{
|
||||
object-fit: contain;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
table.settings-value-table{
|
||||
background: white;
|
||||
border-collapse: collapse;
|
||||
margin: 1em;
|
||||
border: 4px solid white;
|
||||
}
|
||||
|
||||
table.settings-value-table td{
|
||||
padding: 0.4em;
|
||||
border: 1px solid #ccc;
|
||||
max-width: 36em;
|
||||
}
|
||||
|
||||
.modalPrev,
|
||||
|
||||
@@ -118,7 +118,10 @@ def start_server(immediate=True, server=None):
|
||||
installer.log.info("Test only")
|
||||
server.wants_restart = False
|
||||
else:
|
||||
server = server.webui()
|
||||
if args.api_only:
|
||||
server = server.api_only()
|
||||
else:
|
||||
server = server.webui()
|
||||
installer.log.info(f'Memory {get_memory_stats()}')
|
||||
return server
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ group.add_argument("--use-ipex", default = False, action='store_true', help="Use
|
||||
group.add_argument('--use-directml', default = False, action='store_true', help = "Use DirectML if no compatible GPU is detected, default: %(default)s")
|
||||
group.add_argument("--use-cuda", default=False, action='store_true', help="Force use nVidia CUDA backend, default: %(default)s")
|
||||
group.add_argument("--use-rocm", default=False, action='store_true', help="Force use AMD ROCm backend, default: %(default)s")
|
||||
group.add_argument('--subpath', type=str, help='Customize the URL subpath for usage with reverse proxy')
|
||||
|
||||
# removed args are added here as hidden in fixed format for compatbility reasons
|
||||
group.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui
|
||||
|
||||
+2
-2
@@ -60,8 +60,8 @@ def get_device_for(task):
|
||||
return get_optimal_device()
|
||||
|
||||
|
||||
def torch_gc():
|
||||
if shared.opts.disable_gc:
|
||||
def torch_gc(force=False):
|
||||
if shared.opts.disable_gc and not force:
|
||||
return
|
||||
gc.collect()
|
||||
if shared.cmd_opts.use_ipex:
|
||||
|
||||
@@ -156,13 +156,16 @@ class UpscalerESRGAN(Upscaler):
|
||||
|
||||
def load_model(self, path: str):
|
||||
if "http" in path:
|
||||
filename = load_file_from_url(url=self.model_url, model_dir=self.model_path,
|
||||
file_name="%s.pth" % self.model_name,
|
||||
progress=True)
|
||||
filename = load_file_from_url(
|
||||
url=self.model_url,
|
||||
model_dir=self.model_path,
|
||||
file_name=f"{self.model_name}.pth",
|
||||
progress=True,
|
||||
)
|
||||
else:
|
||||
filename = path
|
||||
if not os.path.exists(filename) or filename is None:
|
||||
print("Unable to load %s from %s" % (self.model_path, filename))
|
||||
print(f"Unable to load {self.model_path} from {filename}")
|
||||
return None
|
||||
|
||||
state_dict = torch.load(filename, map_location='cpu' if devices.device_esrgan.type == 'mps' else None)
|
||||
|
||||
@@ -36,7 +36,7 @@ class RRDBNet(nn.Module):
|
||||
elif upsample_mode == 'pixelshuffle':
|
||||
upsample_block = pixelshuffle_block
|
||||
else:
|
||||
raise NotImplementedError('upsample mode [{:s}] is not found'.format(upsample_mode))
|
||||
raise NotImplementedError(f'upsample mode [{upsample_mode}] is not found')
|
||||
if upscale == 3:
|
||||
upsampler = upsample_block(nf, nf, 3, act_type=act_type, convtype=convtype)
|
||||
else:
|
||||
@@ -169,7 +169,7 @@ class GaussianNoise(nn.Module):
|
||||
scale = self.sigma * x.detach() if self.is_relative_detach else self.sigma * x
|
||||
sampled_noise = self.noise.repeat(*x.size()).normal_() * scale
|
||||
x = x + sampled_noise
|
||||
return x
|
||||
return x
|
||||
|
||||
def conv1x1(in_planes, out_planes, stride=1):
|
||||
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
|
||||
@@ -259,10 +259,10 @@ class Upsample(nn.Module):
|
||||
|
||||
def extra_repr(self):
|
||||
if self.scale_factor is not None:
|
||||
info = 'scale_factor=' + str(self.scale_factor)
|
||||
info = f'scale_factor={self.scale_factor}'
|
||||
else:
|
||||
info = 'size=' + str(self.size)
|
||||
info += ', mode=' + self.mode
|
||||
info = f'size={self.size}'
|
||||
info += f', mode={self.mode}'
|
||||
return info
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ def act(act_type, inplace=True, neg_slope=0.2, n_prelu=1, beta=1.0):
|
||||
elif act_type == 'sigmoid': # [0, 1] range output
|
||||
layer = nn.Sigmoid()
|
||||
else:
|
||||
raise NotImplementedError('activation layer [{:s}] is not found'.format(act_type))
|
||||
raise NotImplementedError(f'activation layer [{act_type}] is not found')
|
||||
return layer
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ def norm(norm_type, nc):
|
||||
elif norm_type == 'none':
|
||||
def norm_layer(x): return Identity()
|
||||
else:
|
||||
raise NotImplementedError('normalization layer [{:s}] is not found'.format(norm_type))
|
||||
raise NotImplementedError(f'normalization layer [{norm_type}] is not found')
|
||||
return layer
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ def pad(pad_type, padding):
|
||||
elif pad_type == 'zero':
|
||||
layer = nn.ZeroPad2d(padding)
|
||||
else:
|
||||
raise NotImplementedError('padding layer [{:s}] is not implemented'.format(pad_type))
|
||||
raise NotImplementedError(f'padding layer [{pad_type}] is not implemented')
|
||||
return layer
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ def conv_block(in_nc, out_nc, kernel_size, stride=1, dilation=1, groups=1, bias=
|
||||
pad_type='zero', norm_type=None, act_type='relu', mode='CNA', convtype='Conv2D',
|
||||
spectral_norm=False):
|
||||
""" Conv layer with padding, normalization, activation """
|
||||
assert mode in ['CNA', 'NAC', 'CNAC'], 'Wrong conv mode [{:s}]'.format(mode)
|
||||
assert mode in ['CNA', 'NAC', 'CNAC'], f'Wrong conv mode [{mode}]'
|
||||
padding = get_valid_padding(kernel_size, dilation)
|
||||
p = pad(pad_type, padding) if pad_type and pad_type != 'zero' else None
|
||||
padding = padding if pad_type == 'zero' else 0
|
||||
|
||||
@@ -10,7 +10,8 @@ class ExtraNetworkHypernet(extra_networks.ExtraNetwork):
|
||||
additional = shared.opts.sd_hypernetwork
|
||||
|
||||
if additional != "None" and additional in shared.hypernetworks and len([x for x in params_list if x.items[0] == additional]) == 0:
|
||||
p.all_prompts = [x + f"<hypernet:{additional}:{shared.opts.extra_networks_default_multiplier}>" for x in p.all_prompts]
|
||||
hypernet_prompt_text = f"<hypernet:{additional}:{shared.opts.extra_networks_default_multiplier}>"
|
||||
p.all_prompts = [f"{prompt}{hypernet_prompt_text}" for prompt in p.all_prompts]
|
||||
params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier]))
|
||||
|
||||
names = []
|
||||
|
||||
@@ -51,6 +51,7 @@ def image_from_url_text(filedata):
|
||||
filename = filedata["name"]
|
||||
is_in_right_dir = ui_tempdir.check_tmp_file(shared.demo, filename)
|
||||
if is_in_right_dir:
|
||||
filename = filename.rsplit('?', 1)[0]
|
||||
image = Image.open(filename)
|
||||
geninfo, _items = images.read_info_from_image(image)
|
||||
image.info['parameters'] = geninfo
|
||||
@@ -134,6 +135,7 @@ def connect_paste_params_buttons():
|
||||
_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],
|
||||
show_progress=False,
|
||||
)
|
||||
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)
|
||||
@@ -149,6 +151,7 @@ def connect_paste_params_buttons():
|
||||
_js=f"switch_to_{binding.tabname}",
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -257,8 +260,8 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
|
||||
v = v[1:-1] if v[0] == '"' and v[-1] == '"' else v
|
||||
m = re_imagesize.match(v)
|
||||
if m is not None:
|
||||
res[k+"-1"] = m.group(1)
|
||||
res[k+"-2"] = m.group(2)
|
||||
res[f"{k}-1"] = m.group(1)
|
||||
res[f"{k}-2"] = m.group(2)
|
||||
else:
|
||||
res[k] = v
|
||||
# Missing CLIP skip means it was set to 1 (the default)
|
||||
@@ -404,10 +407,12 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp
|
||||
fn=paste_func,
|
||||
inputs=[input_comp],
|
||||
outputs=[x[0] for x in local_paste_fields],
|
||||
show_progress=False,
|
||||
)
|
||||
button.click(
|
||||
fn=None,
|
||||
_js=f"recalculate_prompts_{tabname}",
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ cache_data = None
|
||||
|
||||
|
||||
def dump_cache():
|
||||
with filelock.FileLock(cache_filename+".lock"):
|
||||
with filelock.FileLock(f"{cache_filename}.lock"):
|
||||
with open(cache_filename, "w", encoding="utf8") as file:
|
||||
json.dump(cache_data, file, indent=4)
|
||||
|
||||
@@ -19,7 +19,7 @@ def dump_cache():
|
||||
def cache(subsection):
|
||||
global cache_data # pylint: disable=global-statement
|
||||
if cache_data is None:
|
||||
with filelock.FileLock(cache_filename+".lock"):
|
||||
with filelock.FileLock(f"{cache_filename}.lock"):
|
||||
if not os.path.isfile(cache_filename):
|
||||
cache_data = {}
|
||||
else:
|
||||
|
||||
@@ -235,7 +235,7 @@ class Hypernetwork:
|
||||
if shared.opts.save_optimizer_state and self.optimizer_state_dict:
|
||||
optimizer_saved_dict['hash'] = self.shorthash()
|
||||
optimizer_saved_dict['optimizer_state_dict'] = self.optimizer_state_dict
|
||||
torch.save(optimizer_saved_dict, filename + '.optim')
|
||||
torch.save(optimizer_saved_dict, f"{filename}.optim")
|
||||
|
||||
def load(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
+4
-3
@@ -308,6 +308,7 @@ class FilenameGenerator:
|
||||
'generation_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.n_iter == 1 and self.p.batch_size == 1 else self.p.iteration * self.p.batch_size + self.p.batch_index + 1,
|
||||
'hasprompt': lambda self, *args: self.hasprompt(*args), # accepts formats:[hasprompt<prompt1|default><prompt2>..]
|
||||
'clip_skip': lambda self: shared.opts.data["CLIP_stop_at_last_layers"],
|
||||
'denoising': lambda self: self.p.denoising_strength if self.p and self.p.denoising_strength else NOTHING_AND_SKIP_PREVIOUS_TEXT,
|
||||
}
|
||||
default_time_format = '%Y%m%d%H%M%S'
|
||||
|
||||
@@ -401,7 +402,7 @@ def get_next_sequence_number(path, basename):
|
||||
"""
|
||||
result = -1
|
||||
if basename != '':
|
||||
basename = basename + "-"
|
||||
basename = f"{basename}-"
|
||||
prefix_length = len(basename)
|
||||
for p in os.listdir(path):
|
||||
if p.startswith(basename):
|
||||
@@ -448,7 +449,7 @@ def atomically_save_image():
|
||||
# additional metadata saved in files
|
||||
if shared.opts.save_txt and len(exifinfo_data) > 0:
|
||||
with open(txt_fullfn, "w", encoding="utf8") as file:
|
||||
file.write(exifinfo_data + "\n")
|
||||
file.write(f"{exifinfo_data}\n")
|
||||
with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file:
|
||||
file.write(exifinfo_data)
|
||||
if shared.opts.save_log_fn != '' and len(exifinfo_data) > 0:
|
||||
@@ -524,7 +525,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i
|
||||
file_decoration = shared.opts.samples_filename_pattern or "[seed]-[prompt_spaces]"
|
||||
add_number = shared.opts.save_images_add_number or file_decoration == ''
|
||||
if file_decoration != "" and add_number:
|
||||
file_decoration = "-" + file_decoration
|
||||
file_decoration = f"-{file_decoration}"
|
||||
file_decoration = namegen.apply(file_decoration) + suffix
|
||||
if add_number:
|
||||
basecount = get_next_sequence_number(path, basename)
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args):
|
||||
break
|
||||
try:
|
||||
img = Image.open(image)
|
||||
except UnidentifiedImageError:
|
||||
except UnidentifiedImageError as e:
|
||||
shared.log.error(f"Image error: {e}")
|
||||
continue
|
||||
# Use the EXIF orientation of photos taken by smartphones.
|
||||
img = ImageOps.exif_transpose(img)
|
||||
|
||||
@@ -26,7 +26,7 @@ def category_types():
|
||||
def download_default_clip_interrogate_categories(content_dir):
|
||||
shared.log.info("Downloading CLIP categories...")
|
||||
|
||||
tmpdir = content_dir + "_tmp"
|
||||
tmpdir = f"{content_dir}_tmp"
|
||||
cat_types = ["artists", "flavors", "mediums", "movements"]
|
||||
|
||||
try:
|
||||
@@ -211,7 +211,7 @@ class InterrogateModels:
|
||||
if shared.opts.interrogate_return_ranks:
|
||||
res += f", ({match}:{score/100:.3f})"
|
||||
else:
|
||||
res += ", " + match
|
||||
res += f", {match}"
|
||||
|
||||
except Exception as e:
|
||||
errors.display(e, 'interrogate')
|
||||
|
||||
@@ -53,6 +53,11 @@ if has_mps:
|
||||
CondFunc('torch.cumsum', cumsum_fix_func, None)
|
||||
CondFunc('torch.Tensor.cumsum', cumsum_fix_func, None)
|
||||
CondFunc('torch.narrow', lambda orig_func, *args, **kwargs: orig_func(*args, **kwargs).clone(), None)
|
||||
if version.parse(torch.__version__) == version.parse("2.0"):
|
||||
|
||||
# MPS workaround for https://github.com/pytorch/pytorch/issues/96113
|
||||
CondFunc('torch.nn.functional.layer_norm', lambda orig_func, x, normalized_shape, weight, bias, eps, **kwargs: orig_func(x.float(), normalized_shape, weight.float() if weight is not None else None, bias.float() if bias is not None else bias, eps).to(x.dtype), lambda *args, **kwargs: len(args) == 6)
|
||||
CondFunc('torch.nn.functional.layer_norm', lambda orig_func, x, normalized_shape, weight, bias, eps, **kwargs: orig_func(x.float(), normalized_shape, weight.float() if weight is not None else None, bias.float() if bias is not None else bias, eps).to(x.dtype), lambda _, input, *args, **kwargs: len(args) == 4 and input.device.type == 'mps')
|
||||
|
||||
# MPS workaround for https://github.com/pytorch/pytorch/issues/92311
|
||||
if platform.processor() == 'i386':
|
||||
for funcName in ['torch.argmax', 'torch.Tensor.argmax']:
|
||||
CondFunc(funcName, lambda _, input, *args, **kwargs: torch.max(input.float() if input.dtype == torch.int64 else input, *args, **kwargs)[1], lambda _, input, *args, **kwargs: input.device.type == 'mps')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ssl
|
||||
import time
|
||||
import datetime
|
||||
import logging
|
||||
@@ -17,6 +18,7 @@ errors.install()
|
||||
|
||||
def setup_middleware(app: FastAPI, cmd_opts):
|
||||
log.info('Initializing middleware')
|
||||
ssl._create_default_https_context = ssl._create_unverified_context # pylint: disable=protected-access
|
||||
uvicorn_logger=logging.getLogger("uvicorn.error")
|
||||
uvicorn_logger.disabled = True
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
+17
-36
@@ -22,9 +22,6 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
|
||||
"""
|
||||
output = []
|
||||
|
||||
if ext_filter is None:
|
||||
ext_filter = []
|
||||
|
||||
try:
|
||||
places = []
|
||||
|
||||
@@ -39,22 +36,14 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
|
||||
places.append(model_path)
|
||||
|
||||
for place in places:
|
||||
if os.path.exists(place):
|
||||
for file in glob.iglob(os.path.join(place, '**/**'), recursive=True):
|
||||
full_path = file
|
||||
if os.path.isdir(full_path):
|
||||
continue
|
||||
if os.path.islink(full_path) and not os.path.exists(full_path):
|
||||
print(f"Skipping broken symlink: {full_path}")
|
||||
continue
|
||||
if ext_blacklist is not None and any([full_path.endswith(x) for x in ext_blacklist]):
|
||||
continue
|
||||
if len(ext_filter) != 0:
|
||||
_model_name, extension = os.path.splitext(file)
|
||||
if extension not in ext_filter:
|
||||
continue
|
||||
if file not in output:
|
||||
output.append(full_path)
|
||||
for full_path in shared.walk_files(place, allowed_extensions=ext_filter):
|
||||
if os.path.islink(full_path) and not os.path.exists(full_path):
|
||||
print(f"Skipping broken symlink: {full_path}")
|
||||
continue
|
||||
if ext_blacklist is not None and any([full_path.endswith(x) for x in ext_blacklist]):
|
||||
continue
|
||||
if full_path not in output:
|
||||
output.append(full_path)
|
||||
|
||||
if model_url is not None and len(output) == 0:
|
||||
if download_name is not None:
|
||||
@@ -131,20 +120,6 @@ def move_files(src_path: str, dest_path: str, ext_filter: str = None):
|
||||
pass
|
||||
|
||||
|
||||
builtin_upscaler_classes = []
|
||||
forbidden_upscaler_classes = set()
|
||||
|
||||
|
||||
def list_builtin_upscalers():
|
||||
load_upscalers()
|
||||
builtin_upscaler_classes.clear()
|
||||
builtin_upscaler_classes.extend(Upscaler.__subclasses__())
|
||||
|
||||
def forbid_loaded_nonbuiltin_upscalers():
|
||||
for cls in Upscaler.__subclasses__():
|
||||
if cls not in builtin_upscaler_classes:
|
||||
forbidden_upscaler_classes.add(cls)
|
||||
|
||||
|
||||
def load_upscalers():
|
||||
# We can only do this 'magic' method to dynamically load upscalers if they are referenced,
|
||||
@@ -161,10 +136,16 @@ def load_upscalers():
|
||||
|
||||
datas = []
|
||||
commandline_options = vars(shared.cmd_opts)
|
||||
for cls in Upscaler.__subclasses__():
|
||||
if cls in forbidden_upscaler_classes:
|
||||
continue
|
||||
# some of upscaler classes will not go away after reloading their modules, and we'll end
|
||||
# up with two copies of those classes. The newest copy will always be the last in the list,
|
||||
# so we go from end to beginning and ignore duplicates
|
||||
used_classes = {}
|
||||
for cls in reversed(Upscaler.__subclasses__()):
|
||||
classname = str(cls)
|
||||
if classname not in used_classes:
|
||||
used_classes[classname] = cls
|
||||
|
||||
for cls in reversed(used_classes.values()):
|
||||
name = cls.__name__
|
||||
cmd_name = f"{name.lower().replace('upscaler', '')}_models_path"
|
||||
scaler = cls(commandline_options.get(cmd_name, None))
|
||||
|
||||
@@ -223,7 +223,7 @@ class DDPM(pl.LightningModule):
|
||||
for k in keys:
|
||||
for ik in ignore_keys:
|
||||
if k.startswith(ik):
|
||||
print("Deleting key {} from state_dict.".format(k))
|
||||
print(f"Deleting key {k} from state_dict.")
|
||||
del sd[k]
|
||||
missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict(
|
||||
sd, strict=False)
|
||||
@@ -386,7 +386,7 @@ class DDPM(pl.LightningModule):
|
||||
_, loss_dict_no_ema = self.shared_step(batch)
|
||||
with self.ema_scope():
|
||||
_, loss_dict_ema = self.shared_step(batch)
|
||||
loss_dict_ema = {key + '_ema': loss_dict_ema[key] for key in loss_dict_ema}
|
||||
loss_dict_ema = {f"{key}_ema": loss_dict_ema[key] for key in loss_dict_ema}
|
||||
self.log_dict(loss_dict_no_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True)
|
||||
self.log_dict(loss_dict_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class NoiseScheduleVP:
|
||||
"""
|
||||
|
||||
if schedule not in ['discrete', 'linear', 'cosine']:
|
||||
raise ValueError("Unsupported noise schedule {}. The schedule needs to be 'discrete' or 'linear' or 'cosine'".format(schedule))
|
||||
raise ValueError(f"Unsupported noise schedule {schedule}. The schedule needs to be 'discrete' or 'linear' or 'cosine'")
|
||||
|
||||
self.schedule = schedule
|
||||
if schedule == 'discrete':
|
||||
@@ -382,7 +382,7 @@ def get_time_steps(noise_schedule, skip_type, t_T, t_0, N, device):
|
||||
t = torch.linspace(t_T**(1. / t_order), t_0**(1. / t_order), N + 1).pow(t_order).to(device)
|
||||
return t
|
||||
else:
|
||||
raise ValueError("Unsupported skip_type {}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'".format(skip_type))
|
||||
raise ValueError(f"Unsupported skip_type {skip_type}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'")
|
||||
|
||||
class UniPC:
|
||||
def __init__(
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ for possible_sd_path in possible_sd_paths:
|
||||
sd_path = os.path.abspath(possible_sd_path)
|
||||
break
|
||||
|
||||
assert sd_path is not None, "Couldn't find Stable Diffusion in any of: " + str(possible_sd_paths)
|
||||
assert sd_path is not None, f"Couldn't find Stable Diffusion in any of: {possible_sd_paths}"
|
||||
|
||||
path_dirs = [
|
||||
(sd_path, 'ldm', 'Stable Diffusion', []),
|
||||
|
||||
+14
-33
@@ -14,6 +14,7 @@ from ldm.data.util import AddMiDaS
|
||||
from ldm.models.diffusion.ddpm import LatentDepth2ImageDiffusion
|
||||
from einops import repeat, rearrange
|
||||
from blendmodes.blend import blendLayers, BlendType
|
||||
from installer import git_commit
|
||||
import modules.sd_hijack
|
||||
from modules import devices, prompt_parser, masking, sd_samplers, lowvram, generation_parameters_copypaste, script_callbacks, extra_networks, sd_vae_approx, scripts # pylint: disable=unused-import
|
||||
from modules.sd_hijack import model_hijack
|
||||
@@ -40,56 +41,41 @@ def setup_color_correction(image):
|
||||
def apply_color_correction(correction, original_image):
|
||||
logging.info("Applying color correction.")
|
||||
image = Image.fromarray(cv2.cvtColor(exposure.match_histograms(
|
||||
cv2.cvtColor(
|
||||
np.asarray(original_image),
|
||||
cv2.COLOR_RGB2LAB
|
||||
),
|
||||
cv2.cvtColor(np.asarray(original_image), cv2.COLOR_RGB2LAB),
|
||||
correction,
|
||||
channel_axis=2
|
||||
), cv2.COLOR_LAB2RGB).astype("uint8"))
|
||||
|
||||
image = blendLayers(image, original_image, BlendType.LUMINOSITY)
|
||||
|
||||
return image
|
||||
|
||||
|
||||
def apply_overlay(image, paste_loc, index, overlays):
|
||||
if overlays is None or index >= len(overlays):
|
||||
return image
|
||||
|
||||
overlay = overlays[index]
|
||||
|
||||
if paste_loc is not None:
|
||||
x, y, w, h = paste_loc
|
||||
base_image = Image.new('RGBA', (overlay.width, overlay.height))
|
||||
image = images.resize_image(1, image, w, h)
|
||||
base_image.paste(image, (x, y))
|
||||
image = base_image
|
||||
|
||||
image = image.convert('RGBA')
|
||||
image.alpha_composite(overlay)
|
||||
image = image.convert('RGB')
|
||||
|
||||
return image
|
||||
|
||||
|
||||
def txt2img_image_conditioning(sd_model, x, width, height):
|
||||
if sd_model.model.conditioning_key in {'hybrid', 'concat'}: # Inpainting models
|
||||
|
||||
# The "masked-image" in this case will just be all zeros since the entire image is masked.
|
||||
image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device)
|
||||
image_conditioning = sd_model.get_first_stage_encoding(sd_model.encode_first_stage(image_conditioning))
|
||||
|
||||
# Add the fake full 1s mask to the first dimension.
|
||||
image_conditioning = torch.nn.functional.pad(image_conditioning, (0, 0, 0, 0, 1, 0), value=1.0)
|
||||
image_conditioning = image_conditioning.to(x.dtype)
|
||||
|
||||
return image_conditioning
|
||||
|
||||
elif sd_model.model.conditioning_key == "crossattn-adm": # UnCLIP models
|
||||
|
||||
return x.new_zeros(x.shape[0], 2*sd_model.noise_augmentor.time_embed.dim, dtype=x.dtype, device=x.device)
|
||||
|
||||
else:
|
||||
# Dummy zero conditioning if we're not using inpainting or unclip models.
|
||||
# Still takes up a bit of memory, but no encoder call.
|
||||
@@ -165,7 +151,6 @@ class StableDiffusionProcessing:
|
||||
|
||||
def txt2img_image_conditioning(self, x, width=None, height=None):
|
||||
self.is_using_inpainting_conditioning = self.sd_model.model.conditioning_key in {'hybrid', 'concat'}
|
||||
|
||||
return txt2img_image_conditioning(self.sd_model, x, width or self.width, height or self.height)
|
||||
|
||||
def depth2img_image_conditioning(self, source_image):
|
||||
@@ -174,7 +159,6 @@ class StableDiffusionProcessing:
|
||||
transformed = transformer({"jpg": rearrange(source_image[0], "c h w -> h w c")})
|
||||
midas_in = torch.from_numpy(transformed["midas_in"][None, ...]).to(device=shared.device)
|
||||
midas_in = repeat(midas_in, "1 ... -> n ...", n=self.batch_size)
|
||||
|
||||
conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(source_image))
|
||||
conditioning = torch.nn.functional.interpolate(
|
||||
self.sd_model.depth_model(midas_in),
|
||||
@@ -182,14 +166,12 @@ class StableDiffusionProcessing:
|
||||
mode="bicubic",
|
||||
align_corners=False,
|
||||
)
|
||||
|
||||
(depth_min, depth_max) = torch.aminmax(conditioning)
|
||||
conditioning = 2. * (conditioning - depth_min) / (depth_max - depth_min) - 1.
|
||||
return conditioning
|
||||
|
||||
def edit_image_conditioning(self, source_image):
|
||||
conditioning_image = self.sd_model.encode_first_stage(source_image).mode()
|
||||
|
||||
return conditioning_image
|
||||
|
||||
def unclip_image_conditioning(self, source_image):
|
||||
@@ -202,7 +184,6 @@ class StableDiffusionProcessing:
|
||||
|
||||
def inpainting_image_conditioning(self, source_image, latent_image, image_mask=None):
|
||||
self.is_using_inpainting_conditioning = True
|
||||
|
||||
# Handle the different mask inputs
|
||||
if image_mask is not None:
|
||||
if torch.is_tensor(image_mask):
|
||||
@@ -216,7 +197,6 @@ class StableDiffusionProcessing:
|
||||
conditioning_mask = torch.round(conditioning_mask)
|
||||
else:
|
||||
conditioning_mask = source_image.new_ones(1, 1, *source_image.shape[-2:])
|
||||
|
||||
# Create another latent image, this time with a masked version of the original input.
|
||||
# Smoothly interpolate between the masked and unmasked latent conditioning image using a parameter.
|
||||
conditioning_mask = conditioning_mask.to(device=source_image.device, dtype=source_image.dtype)
|
||||
@@ -225,35 +205,27 @@ class StableDiffusionProcessing:
|
||||
source_image * (1.0 - conditioning_mask),
|
||||
getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight)
|
||||
)
|
||||
|
||||
# Encode the new masked image using first stage of network.
|
||||
conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(conditioning_image))
|
||||
|
||||
# Create the concatenated conditioning tensor to be fed to `c_concat`
|
||||
conditioning_mask = torch.nn.functional.interpolate(conditioning_mask, size=latent_image.shape[-2:])
|
||||
conditioning_mask = conditioning_mask.expand(conditioning_image.shape[0], -1, -1, -1)
|
||||
image_conditioning = torch.cat([conditioning_mask, conditioning_image], dim=1)
|
||||
image_conditioning = image_conditioning.to(shared.device).type(self.sd_model.dtype)
|
||||
|
||||
return image_conditioning
|
||||
|
||||
def img2img_image_conditioning(self, source_image, latent_image, image_mask=None):
|
||||
source_image = devices.cond_cast_float(source_image)
|
||||
|
||||
# HACK: Using introspection as the Depth2Image model doesn't appear to uniquely
|
||||
# identify itself with a field common to all models. The conditioning_key is also hybrid.
|
||||
if isinstance(self.sd_model, LatentDepth2ImageDiffusion):
|
||||
return self.depth2img_image_conditioning(source_image)
|
||||
|
||||
if self.sd_model.cond_stage_key == "edit":
|
||||
return self.edit_image_conditioning(source_image)
|
||||
|
||||
if self.sampler.conditioning_key in {'hybrid', 'concat'}:
|
||||
return self.inpainting_image_conditioning(source_image, latent_image, image_mask=image_mask)
|
||||
|
||||
if self.sampler.conditioning_key == "crossattn-adm":
|
||||
return self.unclip_image_conditioning(source_image)
|
||||
|
||||
# Dummy zero conditioning if we're not using inpainting or depth model.
|
||||
return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1)
|
||||
|
||||
@@ -344,7 +316,6 @@ class Processed:
|
||||
"clip_skip": self.clip_skip,
|
||||
"is_using_inpainting_conditioning": self.is_using_inpainting_conditioning,
|
||||
}
|
||||
|
||||
return json.dumps(obj)
|
||||
|
||||
def infotext(self, p: StableDiffusionProcessing, index):
|
||||
@@ -468,6 +439,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
|
||||
"Clip skip": p.clip_skip,
|
||||
"ENSD": None if opts.eta_noise_seed_delta == 0 else opts.eta_noise_seed_delta,
|
||||
"Init image hash": getattr(p, 'init_img_hash', None),
|
||||
"Version": git_commit,
|
||||
"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,
|
||||
"Token merging random": None if opts.token_merging_random is False else opts.token_merging_random,
|
||||
@@ -479,7 +451,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
|
||||
}
|
||||
generation_params.update(p.extra_generation_params)
|
||||
generation_params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in generation_params.items() if v is not None])
|
||||
negative_prompt_text = "\nNegative prompt: " + p.all_negative_prompts[index] if p.all_negative_prompts[index] else ""
|
||||
negative_prompt_text = f"\nNegative prompt: {p.all_negative_prompts[index]}" if p.all_negative_prompts[index] else ""
|
||||
return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
|
||||
|
||||
|
||||
@@ -726,7 +698,16 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if not p.disable_extra_networks and extra_network_data:
|
||||
extra_networks.deactivate(p, extra_network_data)
|
||||
devices.torch_gc()
|
||||
res = Processed(p, output_images, p.all_seeds[0], infotext(), comments="".join(["\n\n" + x for x in comments]), subseed=p.all_subseeds[0], index_of_first_image=index_of_first_image, infotexts=infotexts)
|
||||
res = Processed(
|
||||
p,
|
||||
images_list=output_images,
|
||||
seed=p.all_seeds[0],
|
||||
info=infotext(),
|
||||
comments="".join(f"\n\n{comment}" for comment in comments),
|
||||
subseed=p.all_subseeds[0],
|
||||
index_of_first_image=index_of_first_image,
|
||||
infotexts=infotexts,
|
||||
)
|
||||
if p.scripts is not None:
|
||||
p.scripts.postprocess(p, res)
|
||||
return res
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from basicsr.utils.download_util import load_file_from_url
|
||||
|
||||
from modules.upscaler import Upscaler, UpscalerData
|
||||
from modules.shared import cmd_opts, opts, device
|
||||
from modules.shared import opts, device
|
||||
from modules import modelloader
|
||||
import modules.errors as errors
|
||||
|
||||
@@ -27,9 +25,9 @@ class UpscalerRealESRGAN(Upscaler):
|
||||
for scaler in scalers:
|
||||
if scaler.local_data_path.startswith("http"):
|
||||
filename = modelloader.friendly_name(scaler.local_data_path)
|
||||
local = next(iter([local_model for local_model in local_model_paths if local_model.endswith(filename + '.pth')]), None)
|
||||
if local:
|
||||
scaler.local_data_path = local
|
||||
local_model_candidates = [local_model for local_model in local_model_paths if local_model.endswith(f"{filename}.pth")]
|
||||
if local_model_candidates:
|
||||
scaler.local_data_path = local_model_candidates[0]
|
||||
if scaler.name in opts.realesrgan_enabled_models:
|
||||
self.scalers.append(scaler)
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class RestrictedUnpickler(pickle.Unpickler):
|
||||
return getattr(collections, name)
|
||||
if module == 'torch._utils' and name in ['_rebuild_tensor_v2', '_rebuild_parameter', '_rebuild_device_tensor_from_numpy']:
|
||||
return getattr(torch._utils, name) # pylint: disable=protected-access
|
||||
if module == 'torch' and name in ['FloatStorage', 'HalfStorage', 'IntStorage', 'LongStorage', 'DoubleStorage', 'ByteStorage', 'float32']:
|
||||
if module == 'torch' and name in ['FloatStorage', 'HalfStorage', 'IntStorage', 'LongStorage', 'DoubleStorage', 'ByteStorage', 'float32', 'BFloat16Storage']:
|
||||
return getattr(torch, name)
|
||||
if module == 'torch.nn.modules.container' and name in ['ParameterDict']:
|
||||
return getattr(torch.nn.modules.container, name)
|
||||
|
||||
+3
-2
@@ -142,7 +142,8 @@ 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 ""
|
||||
tabkind = 'img2img' if self.is_img2img else 'txt2txt'
|
||||
tabname = f"{tabkind}_" 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}'
|
||||
|
||||
@@ -481,7 +482,7 @@ def add_classes_to_gradio_component(comp):
|
||||
elem_classes = comp.elem_classes
|
||||
if elem_classes is None:
|
||||
elem_classes = []
|
||||
comp.elem_classes = ["gradio-" + comp.get_block_name(), *(elem_classes)]
|
||||
comp.elem_classes = [f"gradio-{comp.get_block_name()}", *(comp.elem_classes or [])]
|
||||
if getattr(comp, 'multiselect', False):
|
||||
comp.elem_classes.append('multiselect')
|
||||
|
||||
|
||||
@@ -237,8 +237,9 @@ class StableDiffusionModelHijack:
|
||||
self.comments = []
|
||||
|
||||
def get_prompt_lengths(self, text):
|
||||
if self.clip is None:
|
||||
return 0, 0
|
||||
_, token_count = self.clip.process_texts([text])
|
||||
|
||||
return token_count, self.clip.get_target_prompt_token_count(token_count)
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ def forward_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, text
|
||||
self.hijack.comments += hijack_comments
|
||||
|
||||
if len(used_custom_terms) > 0:
|
||||
self.hijack.comments.append("Used embeddings: " + ", ".join([f'{word} [{checksum}]' for word, checksum in used_custom_terms]))
|
||||
embedding_names = ", ".join(f"{word} [{checksum}]" for word, checksum in used_custom_terms)
|
||||
self.hijack.comments.append(f"Used embeddings: {embedding_names}")
|
||||
|
||||
self.hijack.fixes = hijack_fixes
|
||||
return self.process_tokens(remade_batch_tokens, batch_multipliers)
|
||||
|
||||
@@ -275,6 +275,9 @@ def sub_quad_attention_forward(self, x, context=None, mask=None):
|
||||
k = k.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
|
||||
v = v.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
|
||||
|
||||
if q.device.type == 'mps':
|
||||
q, k, v = q.contiguous(), k.contiguous(), v.contiguous()
|
||||
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k = q.float(), k.float()
|
||||
|
||||
@@ -19,7 +19,7 @@ class TorchHijackForUnet:
|
||||
if hasattr(torch, item):
|
||||
return getattr(torch, item)
|
||||
|
||||
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item))
|
||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'")
|
||||
|
||||
def cat(self, tensors, *args, **kwargs):
|
||||
if len(tensors) == 2:
|
||||
|
||||
+39
-16
@@ -2,6 +2,7 @@ import collections
|
||||
import os.path
|
||||
import re
|
||||
import io
|
||||
import threading
|
||||
from os import mkdir
|
||||
from urllib import request
|
||||
from rich import progress # pylint: disable=redefined-builtin
|
||||
@@ -41,7 +42,7 @@ class CheckpointInfo:
|
||||
self.name_for_extra = os.path.splitext(os.path.basename(filename))[0]
|
||||
self.model_name = os.path.splitext(name.replace("/", "_").replace("\\", "_"))[0]
|
||||
self.hash = model_hash(filename)
|
||||
self.sha256 = hashes.sha256_from_cache(self.filename, "checkpoint/" + name)
|
||||
self.sha256 = hashes.sha256_from_cache(self.filename, f"checkpoint/{name}")
|
||||
self.shorthash = self.sha256[0:10] if self.sha256 else None
|
||||
self.title = name if self.shorthash is None else f'{name} [{self.shorthash}]'
|
||||
self.ids = [self.hash, self.model_name, self.title, name, f'{name} [{self.hash}]'] + ([self.shorthash, self.sha256, f'{self.name} [{self.shorthash}]'] if self.shorthash else [])
|
||||
@@ -59,7 +60,7 @@ class CheckpointInfo:
|
||||
checkpoint_aliases[i] = self
|
||||
|
||||
def calculate_shorthash(self):
|
||||
self.sha256 = hashes.sha256(self.filename, "checkpoint/" + self.name)
|
||||
self.sha256 = hashes.sha256(self.filename, f"checkpoint/{self.name}")
|
||||
if self.sha256 is None:
|
||||
return
|
||||
self.shorthash = self.sha256[0:10]
|
||||
@@ -349,6 +350,29 @@ sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embe
|
||||
sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight'
|
||||
|
||||
|
||||
class SdModelData:
|
||||
def __init__(self):
|
||||
self.sd_model = None
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def get_sd_model(self):
|
||||
if self.sd_model is None:
|
||||
with self.lock:
|
||||
try:
|
||||
load_model()
|
||||
except Exception as e:
|
||||
shared.log.error("Failed to load stable diffusion model")
|
||||
errors.display(e, "loading stable diffusion model")
|
||||
self.sd_model = None
|
||||
return self.sd_model
|
||||
|
||||
def set_sd_model(self, v):
|
||||
self.sd_model = v
|
||||
|
||||
|
||||
model_data = SdModelData()
|
||||
|
||||
|
||||
def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None):
|
||||
shared.log.debug(f'Load model: info={checkpoint_info is not None} dict={already_loaded_state_dict is not None}')
|
||||
from modules import lowvram, sd_hijack
|
||||
@@ -358,9 +382,11 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None)
|
||||
if timer is None:
|
||||
timer = Timer()
|
||||
current_checkpoint_info = None
|
||||
if shared.sd_model:
|
||||
current_checkpoint_info = shared.sd_model.sd_checkpoint_info
|
||||
if model_data.sd_model is not None:
|
||||
sd_hijack.model_hijack.undo_hijack(model_data.sd_model)
|
||||
current_checkpoint_info = model_data.sd_model.sd_checkpoint_info
|
||||
unload_model_weights()
|
||||
model_data.sd_model = None
|
||||
do_inpainting_hijack()
|
||||
devices.set_cuda_params()
|
||||
if already_loaded_state_dict is not None:
|
||||
@@ -405,14 +431,14 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None)
|
||||
if shared.cmd_opts.use_ipex:
|
||||
sd_model = torch.xpu.optimize(sd_model, dtype=devices.dtype)
|
||||
shared.log.info("Applied IPEX Optimize")
|
||||
shared.sd_model = sd_model
|
||||
model_data.sd_model = sd_model
|
||||
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True) # Reload embeddings after model load as they may or may not fit the model
|
||||
timer.record("embeddings")
|
||||
script_callbacks.model_loaded_callback(sd_model)
|
||||
timer.record("callbacks")
|
||||
shared.log.info(f"Model loaded in {timer.summary()}")
|
||||
current_checkpoint_info = None
|
||||
devices.torch_gc()
|
||||
devices.torch_gc(force=True)
|
||||
shared.log.info(f'Model load finished: {memory_stats()}')
|
||||
|
||||
|
||||
@@ -426,7 +452,7 @@ def reload_model_weights(sd_model=None, info=None):
|
||||
from modules import lowvram, sd_hijack
|
||||
checkpoint_info = info or select_checkpoint()
|
||||
if not sd_model:
|
||||
sd_model = shared.sd_model
|
||||
sd_model = model_data.sd_model
|
||||
if sd_model is None: # previous model load failed
|
||||
current_checkpoint_info = None
|
||||
else:
|
||||
@@ -443,7 +469,6 @@ def reload_model_weights(sd_model=None, info=None):
|
||||
else:
|
||||
unload_model_weights()
|
||||
sd_model = None
|
||||
shared.sd_model = None
|
||||
timer = Timer()
|
||||
state_dict = get_checkpoint_state_dict(checkpoint_info, timer)
|
||||
checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info)
|
||||
@@ -452,7 +477,7 @@ def reload_model_weights(sd_model=None, info=None):
|
||||
del sd_model
|
||||
checkpoints_loaded.clear()
|
||||
load_model(checkpoint_info, already_loaded_state_dict=state_dict, timer=timer)
|
||||
return shared.sd_model
|
||||
return model_data.sd_model
|
||||
try:
|
||||
load_model_weights(sd_model, checkpoint_info, state_dict, timer)
|
||||
except Exception:
|
||||
@@ -471,14 +496,12 @@ def reload_model_weights(sd_model=None, info=None):
|
||||
|
||||
def unload_model_weights(sd_model=None, _info=None):
|
||||
from modules import sd_hijack
|
||||
if shared.sd_model:
|
||||
# shared.sd_model.cond_stage_model.to(devices.cpu)
|
||||
# shared.sd_model.first_stage_model.to(devices.cpu)
|
||||
shared.sd_model.to(devices.cpu)
|
||||
sd_hijack.model_hijack.undo_hijack(shared.sd_model)
|
||||
shared.sd_model = None
|
||||
if model_data.sd_model:
|
||||
model_data.sd_model.to(devices.cpu)
|
||||
sd_hijack.model_hijack.undo_hijack(model_data.sd_model)
|
||||
model_data.sd_model = None
|
||||
sd_model = None
|
||||
devices.torch_gc()
|
||||
devices.torch_gc(force=True)
|
||||
shared.log.debug(f'Model weights unloaded: {memory_stats()}')
|
||||
return sd_model
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ def find_checkpoint_config_near_filename(info):
|
||||
if info is None:
|
||||
return None
|
||||
|
||||
config = os.path.splitext(info.filename)[0] + ".yaml"
|
||||
config = f"{os.path.splitext(info.filename)[0]}.yaml"
|
||||
if os.path.exists(config):
|
||||
return config
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ class TorchHijack:
|
||||
if hasattr(torch, item):
|
||||
return getattr(torch, item)
|
||||
|
||||
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item))
|
||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'")
|
||||
|
||||
def randn_like(self, x):
|
||||
if self.sampler_noises:
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ def refresh_vae_list():
|
||||
|
||||
def find_vae_near_checkpoint(checkpoint_file):
|
||||
checkpoint_path = os.path.splitext(checkpoint_file)[0]
|
||||
for vae_location in [checkpoint_path + ".vae.pt", checkpoint_path + ".vae.ckpt", checkpoint_path + ".vae.safetensors"]:
|
||||
for vae_location in [f"{checkpoint_path}.vae.pt", f"{checkpoint_path}.vae.ckpt", f"{checkpoint_path}.vae.safetensors"]:
|
||||
if os.path.isfile(vae_location):
|
||||
return vae_location
|
||||
|
||||
|
||||
+22
-2
@@ -7,6 +7,7 @@ import urllib.request
|
||||
import gradio as gr
|
||||
import tqdm
|
||||
import requests
|
||||
from ldm.models.diffusion.ddpm import LatentDiffusion
|
||||
from modules import errors, ui_components, shared_items, cmd_args
|
||||
from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir # pylint: disable=W0611
|
||||
import modules.interrogate
|
||||
@@ -27,7 +28,6 @@ cmd_opts, _ = parser.parse_known_args()
|
||||
hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
|
||||
is_device_dml = False
|
||||
xformers_available = False
|
||||
sd_model = None
|
||||
clip_model = None
|
||||
interrogator = modules.interrogate.InterrogateModels("interrogate")
|
||||
sd_upscalers = []
|
||||
@@ -411,7 +411,7 @@ options_templates.update(options_section(('ui', "User interface"), {
|
||||
"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}),
|
||||
"keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), # pylint: disable=anomalous-backslash-in-string
|
||||
"quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"),
|
||||
"quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
|
||||
"hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": [x for x in tab_names]}),
|
||||
"ui_tab_reorder": OptionInfo("From Text, From Image, Process Image", "UI tabs order"),
|
||||
"ui_scripts_reorder": OptionInfo("Enable Dynamic Thresholding, ControlNet", "UI scripts order"),
|
||||
@@ -550,6 +550,8 @@ class Options:
|
||||
def load(self, filename):
|
||||
with open(filename, "r", encoding="utf8") as file:
|
||||
self.data = json.load(file)
|
||||
if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
|
||||
self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]
|
||||
bad_settings = 0
|
||||
for k, v in self.data.items():
|
||||
info = self.data_labels.get(k, None)
|
||||
@@ -750,3 +752,21 @@ def html(filename):
|
||||
with open(path, encoding="utf8") as file:
|
||||
return file.read()
|
||||
return ""
|
||||
|
||||
class Shared(sys.modules[__name__].__class__):
|
||||
# this class is here to provide sd_model field as a property, so that it can be created and loaded on demand rather than at program startup.
|
||||
sd_model_val = None
|
||||
|
||||
@property
|
||||
def sd_model(self):
|
||||
import modules.sd_models # pylint: disable=W0621
|
||||
# return modules.sd_models.model_data.sd_model
|
||||
return modules.sd_models.model_data.get_sd_model()
|
||||
|
||||
@sd_model.setter
|
||||
def sd_model(self, value):
|
||||
import modules.sd_models # pylint: disable=W0621
|
||||
modules.sd_models.model_data.set_sd_model(value)
|
||||
|
||||
sd_model: LatentDiffusion = None # this var is here just for IDE's type checking; it cannot be accessed because the class field above will be accessed instead
|
||||
sys.modules[__name__].__class__ = Shared
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import os
|
||||
import cv2
|
||||
import requests
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from math import log, sqrt
|
||||
import numpy as np
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import ImageDraw
|
||||
|
||||
GREEN = "#0F0"
|
||||
BLUE = "#00F"
|
||||
@@ -12,63 +10,63 @@ RED = "#F00"
|
||||
|
||||
|
||||
def crop_image(im, settings):
|
||||
""" Intelligently crop an image to the subject matter """
|
||||
""" Intelligently crop an image to the subject matter """
|
||||
|
||||
scale_by = 1
|
||||
if is_landscape(im.width, im.height):
|
||||
scale_by = settings.crop_height / im.height
|
||||
elif is_portrait(im.width, im.height):
|
||||
scale_by = settings.crop_width / im.width
|
||||
elif is_square(im.width, im.height):
|
||||
if is_square(settings.crop_width, settings.crop_height):
|
||||
scale_by = settings.crop_width / im.width
|
||||
elif is_landscape(settings.crop_width, settings.crop_height):
|
||||
scale_by = settings.crop_width / im.width
|
||||
elif is_portrait(settings.crop_width, settings.crop_height):
|
||||
scale_by = settings.crop_height / im.height
|
||||
scale_by = 1
|
||||
if is_landscape(im.width, im.height):
|
||||
scale_by = settings.crop_height / im.height
|
||||
elif is_portrait(im.width, im.height):
|
||||
scale_by = settings.crop_width / im.width
|
||||
elif is_square(im.width, im.height):
|
||||
if is_square(settings.crop_width, settings.crop_height):
|
||||
scale_by = settings.crop_width / im.width
|
||||
elif is_landscape(settings.crop_width, settings.crop_height):
|
||||
scale_by = settings.crop_width / im.width
|
||||
elif is_portrait(settings.crop_width, settings.crop_height):
|
||||
scale_by = settings.crop_height / im.height
|
||||
|
||||
im = im.resize((int(im.width * scale_by), int(im.height * scale_by)))
|
||||
im_debug = im.copy()
|
||||
im = im.resize((int(im.width * scale_by), int(im.height * scale_by)))
|
||||
im_debug = im.copy()
|
||||
|
||||
focus = focal_point(im_debug, settings)
|
||||
focus = focal_point(im_debug, settings)
|
||||
|
||||
# take the focal point and turn it into crop coordinates that try to center over the focal
|
||||
# point but then get adjusted back into the frame
|
||||
y_half = int(settings.crop_height / 2)
|
||||
x_half = int(settings.crop_width / 2)
|
||||
# take the focal point and turn it into crop coordinates that try to center over the focal
|
||||
# point but then get adjusted back into the frame
|
||||
y_half = int(settings.crop_height / 2)
|
||||
x_half = int(settings.crop_width / 2)
|
||||
|
||||
x1 = focus.x - x_half
|
||||
if x1 < 0:
|
||||
x1 = 0
|
||||
elif x1 + settings.crop_width > im.width:
|
||||
x1 = im.width - settings.crop_width
|
||||
x1 = focus.x - x_half
|
||||
if x1 < 0:
|
||||
x1 = 0
|
||||
elif x1 + settings.crop_width > im.width:
|
||||
x1 = im.width - settings.crop_width
|
||||
|
||||
y1 = focus.y - y_half
|
||||
if y1 < 0:
|
||||
y1 = 0
|
||||
elif y1 + settings.crop_height > im.height:
|
||||
y1 = im.height - settings.crop_height
|
||||
y1 = focus.y - y_half
|
||||
if y1 < 0:
|
||||
y1 = 0
|
||||
elif y1 + settings.crop_height > im.height:
|
||||
y1 = im.height - settings.crop_height
|
||||
|
||||
x2 = x1 + settings.crop_width
|
||||
y2 = y1 + settings.crop_height
|
||||
x2 = x1 + settings.crop_width
|
||||
y2 = y1 + settings.crop_height
|
||||
|
||||
crop = [x1, y1, x2, y2]
|
||||
crop = [x1, y1, x2, y2]
|
||||
|
||||
results = []
|
||||
results = []
|
||||
|
||||
results.append(im.crop(tuple(crop)))
|
||||
results.append(im.crop(tuple(crop)))
|
||||
|
||||
if settings.annotate_image:
|
||||
d = ImageDraw.Draw(im_debug)
|
||||
rect = list(crop)
|
||||
rect[2] -= 1
|
||||
rect[3] -= 1
|
||||
d.rectangle(rect, outline=GREEN)
|
||||
results.append(im_debug)
|
||||
if settings.destop_view_image:
|
||||
im_debug.show()
|
||||
if settings.annotate_image:
|
||||
d = ImageDraw.Draw(im_debug)
|
||||
rect = list(crop)
|
||||
rect[2] -= 1
|
||||
rect[3] -= 1
|
||||
d.rectangle(rect, outline=GREEN)
|
||||
results.append(im_debug)
|
||||
if settings.destop_view_image:
|
||||
im_debug.show()
|
||||
|
||||
return results
|
||||
return results
|
||||
|
||||
def focal_point(im, settings):
|
||||
corner_points = image_corner_points(im, settings) if settings.corner_points_weight > 0 else []
|
||||
@@ -79,118 +77,118 @@ def focal_point(im, settings):
|
||||
|
||||
weight_pref_total = 0
|
||||
if len(corner_points) > 0:
|
||||
weight_pref_total += settings.corner_points_weight
|
||||
weight_pref_total += settings.corner_points_weight
|
||||
if len(entropy_points) > 0:
|
||||
weight_pref_total += settings.entropy_points_weight
|
||||
weight_pref_total += settings.entropy_points_weight
|
||||
if len(face_points) > 0:
|
||||
weight_pref_total += settings.face_points_weight
|
||||
weight_pref_total += settings.face_points_weight
|
||||
|
||||
corner_centroid = None
|
||||
if len(corner_points) > 0:
|
||||
corner_centroid = centroid(corner_points)
|
||||
corner_centroid.weight = settings.corner_points_weight / weight_pref_total
|
||||
pois.append(corner_centroid)
|
||||
corner_centroid = centroid(corner_points)
|
||||
corner_centroid.weight = settings.corner_points_weight / weight_pref_total
|
||||
pois.append(corner_centroid)
|
||||
|
||||
entropy_centroid = None
|
||||
if len(entropy_points) > 0:
|
||||
entropy_centroid = centroid(entropy_points)
|
||||
entropy_centroid.weight = settings.entropy_points_weight / weight_pref_total
|
||||
pois.append(entropy_centroid)
|
||||
entropy_centroid = centroid(entropy_points)
|
||||
entropy_centroid.weight = settings.entropy_points_weight / weight_pref_total
|
||||
pois.append(entropy_centroid)
|
||||
|
||||
face_centroid = None
|
||||
if len(face_points) > 0:
|
||||
face_centroid = centroid(face_points)
|
||||
face_centroid.weight = settings.face_points_weight / weight_pref_total
|
||||
pois.append(face_centroid)
|
||||
face_centroid = centroid(face_points)
|
||||
face_centroid.weight = settings.face_points_weight / weight_pref_total
|
||||
pois.append(face_centroid)
|
||||
|
||||
average_point = poi_average(pois, settings)
|
||||
|
||||
if settings.annotate_image:
|
||||
d = ImageDraw.Draw(im)
|
||||
max_size = min(im.width, im.height) * 0.07
|
||||
if corner_centroid is not None:
|
||||
color = BLUE
|
||||
box = corner_centroid.bounding(max_size * corner_centroid.weight)
|
||||
d.text((box[0], box[1]-15), "Edge: %.02f" % corner_centroid.weight, fill=color)
|
||||
d.ellipse(box, outline=color)
|
||||
if len(corner_points) > 1:
|
||||
for f in corner_points:
|
||||
d.rectangle(f.bounding(4), outline=color)
|
||||
if entropy_centroid is not None:
|
||||
color = "#ff0"
|
||||
box = entropy_centroid.bounding(max_size * entropy_centroid.weight)
|
||||
d.text((box[0], box[1]-15), "Entropy: %.02f" % entropy_centroid.weight, fill=color)
|
||||
d.ellipse(box, outline=color)
|
||||
if len(entropy_points) > 1:
|
||||
for f in entropy_points:
|
||||
d.rectangle(f.bounding(4), outline=color)
|
||||
if face_centroid is not None:
|
||||
color = RED
|
||||
box = face_centroid.bounding(max_size * face_centroid.weight)
|
||||
d.text((box[0], box[1]-15), "Face: %.02f" % face_centroid.weight, fill=color)
|
||||
d.ellipse(box, outline=color)
|
||||
if len(face_points) > 1:
|
||||
for f in face_points:
|
||||
d.rectangle(f.bounding(4), outline=color)
|
||||
d = ImageDraw.Draw(im)
|
||||
max_size = min(im.width, im.height) * 0.07
|
||||
if corner_centroid is not None:
|
||||
color = BLUE
|
||||
box = corner_centroid.bounding(max_size * corner_centroid.weight)
|
||||
d.text((box[0], box[1]-15), f"Edge: {corner_centroid.weight:.02f}", fill=color)
|
||||
d.ellipse(box, outline=color)
|
||||
if len(corner_points) > 1:
|
||||
for f in corner_points:
|
||||
d.rectangle(f.bounding(4), outline=color)
|
||||
if entropy_centroid is not None:
|
||||
color = "#ff0"
|
||||
box = entropy_centroid.bounding(max_size * entropy_centroid.weight)
|
||||
d.text((box[0], box[1]-15), f"Entropy: {entropy_centroid.weight:.02f}", fill=color)
|
||||
d.ellipse(box, outline=color)
|
||||
if len(entropy_points) > 1:
|
||||
for f in entropy_points:
|
||||
d.rectangle(f.bounding(4), outline=color)
|
||||
if face_centroid is not None:
|
||||
color = RED
|
||||
box = face_centroid.bounding(max_size * face_centroid.weight)
|
||||
d.text((box[0], box[1]-15), f"Face: {face_centroid.weight:.02f}", fill=color)
|
||||
d.ellipse(box, outline=color)
|
||||
if len(face_points) > 1:
|
||||
for f in face_points:
|
||||
d.rectangle(f.bounding(4), outline=color)
|
||||
|
||||
d.ellipse(average_point.bounding(max_size), outline=GREEN)
|
||||
|
||||
d.ellipse(average_point.bounding(max_size), outline=GREEN)
|
||||
|
||||
return average_point
|
||||
|
||||
|
||||
def image_face_points(im, settings):
|
||||
if settings.dnn_model_path is not None:
|
||||
detector = cv2.FaceDetectorYN.create(
|
||||
settings.dnn_model_path,
|
||||
"",
|
||||
(im.width, im.height),
|
||||
0.9, # score threshold
|
||||
0.3, # nms threshold
|
||||
5000 # keep top k before nms
|
||||
)
|
||||
faces = detector.detect(np.array(im))
|
||||
results = []
|
||||
if faces[1] is not None:
|
||||
for face in faces[1]:
|
||||
x = face[0]
|
||||
y = face[1]
|
||||
w = face[2]
|
||||
h = face[3]
|
||||
results.append(
|
||||
PointOfInterest(
|
||||
int(x + (w * 0.5)), # face focus left/right is center
|
||||
int(y + (h * 0.33)), # face focus up/down is close to the top of the head
|
||||
size = w,
|
||||
weight = 1/len(faces[1])
|
||||
)
|
||||
)
|
||||
return results
|
||||
detector = cv2.FaceDetectorYN.create(
|
||||
settings.dnn_model_path,
|
||||
"",
|
||||
(im.width, im.height),
|
||||
0.9, # score threshold
|
||||
0.3, # nms threshold
|
||||
5000 # keep top k before nms
|
||||
)
|
||||
faces = detector.detect(np.array(im))
|
||||
results = []
|
||||
if faces[1] is not None:
|
||||
for face in faces[1]:
|
||||
x = face[0]
|
||||
y = face[1]
|
||||
w = face[2]
|
||||
h = face[3]
|
||||
results.append(
|
||||
PointOfInterest(
|
||||
int(x + (w * 0.5)), # face focus left/right is center
|
||||
int(y + (h * 0.33)), # face focus up/down is close to the top of the head
|
||||
size = w,
|
||||
weight = 1/len(faces[1])
|
||||
)
|
||||
)
|
||||
return results
|
||||
else:
|
||||
np_im = np.array(im)
|
||||
gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY)
|
||||
np_im = np.array(im)
|
||||
gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
tries = [
|
||||
[ f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05 ]
|
||||
]
|
||||
for t in tries:
|
||||
classifier = cv2.CascadeClassifier(t[0])
|
||||
minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side
|
||||
try:
|
||||
faces = classifier.detectMultiScale(gray, scaleFactor=1.1,
|
||||
minNeighbors=7, minSize=(minsize, minsize), flags=cv2.CASCADE_SCALE_IMAGE)
|
||||
except:
|
||||
continue
|
||||
tries = [
|
||||
[ f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05 ],
|
||||
[ f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05 ]
|
||||
]
|
||||
for t in tries:
|
||||
classifier = cv2.CascadeClassifier(t[0])
|
||||
minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side
|
||||
try:
|
||||
faces = classifier.detectMultiScale(gray, scaleFactor=1.1,
|
||||
minNeighbors=7, minSize=(minsize, minsize), flags=cv2.CASCADE_SCALE_IMAGE)
|
||||
except:
|
||||
continue
|
||||
|
||||
if len(faces) > 0:
|
||||
rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces]
|
||||
return [PointOfInterest((r[0] +r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0]-r[2]), weight=1/len(rects)) for r in rects]
|
||||
if len(faces) > 0:
|
||||
rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces]
|
||||
return [PointOfInterest((r[0] +r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0]-r[2]), weight=1/len(rects)) for r in rects]
|
||||
return []
|
||||
|
||||
|
||||
@@ -204,11 +202,11 @@ def image_corner_points(im, settings):
|
||||
np_im = np.array(grayscale)
|
||||
|
||||
points = cv2.goodFeaturesToTrack(
|
||||
np_im,
|
||||
maxCorners=100,
|
||||
qualityLevel=0.04,
|
||||
minDistance=min(grayscale.width, grayscale.height)*0.06,
|
||||
useHarrisDetector=False,
|
||||
np_im,
|
||||
maxCorners=100,
|
||||
qualityLevel=0.04,
|
||||
minDistance=min(grayscale.width, grayscale.height)*0.06,
|
||||
useHarrisDetector=False,
|
||||
)
|
||||
|
||||
if points is None:
|
||||
@@ -216,8 +214,8 @@ def image_corner_points(im, settings):
|
||||
|
||||
focal_points = []
|
||||
for point in points:
|
||||
x, y = point.ravel()
|
||||
focal_points.append(PointOfInterest(x, y, size=4, weight=1/len(points)))
|
||||
x, y = point.ravel()
|
||||
focal_points.append(PointOfInterest(x, y, size=4, weight=1/len(points)))
|
||||
|
||||
return focal_points
|
||||
|
||||
@@ -226,13 +224,13 @@ def image_entropy_points(im, settings):
|
||||
landscape = im.height < im.width
|
||||
portrait = im.height > im.width
|
||||
if landscape:
|
||||
move_idx = [0, 2]
|
||||
move_max = im.size[0]
|
||||
move_idx = [0, 2]
|
||||
move_max = im.size[0]
|
||||
elif portrait:
|
||||
move_idx = [1, 3]
|
||||
move_max = im.size[1]
|
||||
move_idx = [1, 3]
|
||||
move_max = im.size[1]
|
||||
else:
|
||||
return []
|
||||
return []
|
||||
|
||||
e_max = 0
|
||||
crop_current = [0, 0, settings.crop_width, settings.crop_height]
|
||||
@@ -241,9 +239,9 @@ def image_entropy_points(im, settings):
|
||||
crop = im.crop(tuple(crop_current))
|
||||
e = image_entropy(crop)
|
||||
|
||||
if (e > e_max):
|
||||
e_max = e
|
||||
crop_best = list(crop_current)
|
||||
if e > e_max:
|
||||
e_max = e
|
||||
crop_best = list(crop_current)
|
||||
|
||||
crop_current[move_idx[0]] += 4
|
||||
crop_current[move_idx[1]] += 4
|
||||
@@ -263,9 +261,9 @@ def image_entropy(im):
|
||||
return -np.log2(hist / hist.sum()).sum()
|
||||
|
||||
def centroid(pois):
|
||||
x = [poi.x for poi in pois]
|
||||
y = [poi.y for poi in pois]
|
||||
return PointOfInterest(sum(x)/len(pois), sum(y)/len(pois))
|
||||
x = [poi.x for poi in pois]
|
||||
y = [poi.y for poi in pois]
|
||||
return PointOfInterest(sum(x)/len(pois), sum(y)/len(pois))
|
||||
|
||||
|
||||
def poi_average(pois, settings):
|
||||
@@ -283,59 +281,59 @@ def poi_average(pois, settings):
|
||||
|
||||
|
||||
def is_landscape(w, h):
|
||||
return w > h
|
||||
return w > h
|
||||
|
||||
|
||||
def is_portrait(w, h):
|
||||
return h > w
|
||||
return h > w
|
||||
|
||||
|
||||
def is_square(w, h):
|
||||
return w == h
|
||||
return w == h
|
||||
|
||||
|
||||
def download_and_cache_models(dirname):
|
||||
download_url = 'https://github.com/opencv/opencv_zoo/blob/91fb0290f50896f38a0ab1e558b74b16bc009428/models/face_detection_yunet/face_detection_yunet_2022mar.onnx?raw=true'
|
||||
model_file_name = 'face_detection_yunet.onnx'
|
||||
download_url = 'https://github.com/opencv/opencv_zoo/blob/91fb0290f50896f38a0ab1e558b74b16bc009428/models/face_detection_yunet/face_detection_yunet_2022mar.onnx?raw=true'
|
||||
model_file_name = 'face_detection_yunet.onnx'
|
||||
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
cache_file = os.path.join(dirname, model_file_name)
|
||||
if not os.path.exists(cache_file):
|
||||
print(f"downloading face detection model from '{download_url}' to '{cache_file}'")
|
||||
response = requests.get(download_url)
|
||||
with open(cache_file, "wb") as f:
|
||||
f.write(response.content)
|
||||
cache_file = os.path.join(dirname, model_file_name)
|
||||
if not os.path.exists(cache_file):
|
||||
print(f"downloading face detection model from '{download_url}' to '{cache_file}'")
|
||||
response = requests.get(download_url, timeout=60*60*2)
|
||||
with open(cache_file, "wb") as f:
|
||||
f.write(response.content)
|
||||
|
||||
if os.path.exists(cache_file):
|
||||
return cache_file
|
||||
return None
|
||||
if os.path.exists(cache_file):
|
||||
return cache_file
|
||||
return None
|
||||
|
||||
|
||||
class PointOfInterest:
|
||||
def __init__(self, x, y, weight=1.0, size=10):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.weight = weight
|
||||
self.size = size
|
||||
def __init__(self, x, y, weight=1.0, size=10):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.weight = weight
|
||||
self.size = size
|
||||
|
||||
def bounding(self, size):
|
||||
return [
|
||||
self.x - size//2,
|
||||
self.y - size//2,
|
||||
self.x + size//2,
|
||||
self.y + size//2
|
||||
]
|
||||
def bounding(self, size):
|
||||
return [
|
||||
self.x - size//2,
|
||||
self.y - size//2,
|
||||
self.x + size//2,
|
||||
self.y + size//2
|
||||
]
|
||||
|
||||
|
||||
class Settings:
|
||||
def __init__(self, crop_width=512, crop_height=512, corner_points_weight=0.5, entropy_points_weight=0.5, face_points_weight=0.5, annotate_image=False, dnn_model_path=None):
|
||||
self.crop_width = crop_width
|
||||
self.crop_height = crop_height
|
||||
self.corner_points_weight = corner_points_weight
|
||||
self.entropy_points_weight = entropy_points_weight
|
||||
self.face_points_weight = face_points_weight
|
||||
self.annotate_image = annotate_image
|
||||
self.destop_view_image = False
|
||||
self.dnn_model_path = dnn_model_path
|
||||
def __init__(self, crop_width=512, crop_height=512, corner_points_weight=0.5, entropy_points_weight=0.5, face_points_weight=0.5, annotate_image=False, dnn_model_path=None):
|
||||
self.crop_width = crop_width
|
||||
self.crop_height = crop_height
|
||||
self.corner_points_weight = corner_points_weight
|
||||
self.entropy_points_weight = entropy_points_weight
|
||||
self.face_points_weight = face_points_weight
|
||||
self.annotate_image = annotate_image
|
||||
self.destop_view_image = False
|
||||
self.dnn_model_path = dnn_model_path
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import os
|
||||
import re
|
||||
import random
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
import PIL
|
||||
import torch
|
||||
from PIL import Image
|
||||
from torch.utils.data import Dataset, DataLoader, Sampler
|
||||
from torchvision import transforms
|
||||
from collections import defaultdict
|
||||
from random import shuffle, choices
|
||||
|
||||
import random
|
||||
import tqdm
|
||||
from modules import devices, shared
|
||||
import re
|
||||
|
||||
from ldm.modules.distributions.distributions import DiagonalGaussianDistribution
|
||||
from modules import devices, shared
|
||||
|
||||
re_numbers_at_start = re.compile(r"^[-\d]+\s*")
|
||||
|
||||
@@ -72,7 +69,7 @@ class PersonalizedBase(Dataset):
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
text_filename = os.path.splitext(path)[0] + ".txt"
|
||||
text_filename = f"{os.path.splitext(path)[0]}.txt"
|
||||
filename = os.path.basename(path)
|
||||
|
||||
if os.path.exists(text_filename):
|
||||
@@ -118,7 +115,7 @@ class PersonalizedBase(Dataset):
|
||||
weight = torch.ones(latent_sample.shape)
|
||||
else:
|
||||
weight = None
|
||||
|
||||
|
||||
if latent_sampling_method == "random":
|
||||
entry = DatasetEntry(filename=path, filename_text=filename_text, latent_dist=latent_dist, weight=weight)
|
||||
else:
|
||||
@@ -193,16 +190,16 @@ class GroupedBatchSampler(Sampler):
|
||||
b = self.batch_size
|
||||
|
||||
for g in self.groups:
|
||||
shuffle(g)
|
||||
random.shuffle(g)
|
||||
|
||||
batches = []
|
||||
for g in self.groups:
|
||||
batches.extend(g[i*b:(i+1)*b] for i in range(len(g) // b))
|
||||
for _ in range(self.n_rand_batches):
|
||||
rand_group = choices(self.groups, self.probs)[0]
|
||||
batches.append(choices(rand_group, k=b))
|
||||
rand_group = random.choices(self.groups, self.probs)[0]
|
||||
batches.append(random.choices(rand_group, k=b))
|
||||
|
||||
shuffle(batches)
|
||||
random.shuffle(batches)
|
||||
|
||||
yield from batches
|
||||
|
||||
@@ -243,4 +240,4 @@ class BatchLoaderRandom(BatchLoader):
|
||||
return self
|
||||
|
||||
def collate_wrapper_random(batch):
|
||||
return BatchLoaderRandom(batch)
|
||||
return BatchLoaderRandom(batch)
|
||||
|
||||
@@ -16,7 +16,7 @@ def save_settings_to_file(log_directory, all_params):
|
||||
if all_params.get('preview_from_txt2img'):
|
||||
keys = keys | saved_params_previews
|
||||
params.update({k: v for k, v in all_params.items() if k in keys})
|
||||
filename = f"{params['embedding_name']}-{now.strftime('%Y-%m-%d_%H-%M-%S')}.json"
|
||||
filename = f"settings-{now.strftime('%Y-%m-%d_%H-%M-%S')}.json"
|
||||
with open(os.path.join(log_directory, filename), "w", encoding='utf-8') as file:
|
||||
print(f'Training settings file: {os.path.join(log_directory, filename)}')
|
||||
json.dump(params, file, indent=2)
|
||||
|
||||
@@ -58,9 +58,9 @@ def save_pic_with_caption(image, index, params: PreprocessParams, existing_capti
|
||||
image.save(os.path.join(params.dstdir, f"{basename}.png"))
|
||||
|
||||
if params.preprocess_txt_action == 'prepend' and existing_caption:
|
||||
caption = existing_caption + ' ' + caption
|
||||
caption = f"{existing_caption} {caption}"
|
||||
elif params.preprocess_txt_action == 'append' and existing_caption:
|
||||
caption = caption + ' ' + existing_caption
|
||||
caption = f"{caption} {existing_caption}"
|
||||
elif params.preprocess_txt_action == 'copy' and existing_caption:
|
||||
caption = existing_caption
|
||||
caption = caption.strip()
|
||||
@@ -173,7 +173,7 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
|
||||
params.src = filename
|
||||
|
||||
existing_caption = None
|
||||
existing_caption_filename = os.path.splitext(filename)[0] + '.txt'
|
||||
existing_caption_filename = f"{os.path.splitext(filename)[0]}.txt"
|
||||
if os.path.exists(existing_caption_filename):
|
||||
with open(existing_caption_filename, 'r', encoding="utf8") as file:
|
||||
existing_caption = file.read()
|
||||
|
||||
@@ -60,7 +60,7 @@ class Embedding:
|
||||
'hash': self.checksum(),
|
||||
'optimizer_state_dict': self.optimizer_state_dict,
|
||||
}
|
||||
torch.save(optimizer_saved_dict, filename + '.optim')
|
||||
torch.save(optimizer_saved_dict, f"{filename}.optim")
|
||||
|
||||
def checksum(self):
|
||||
if self.cached_checksum is not None:
|
||||
@@ -419,8 +419,8 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st
|
||||
optimizer = torch.optim.AdamW([embedding.vec], lr=scheduler.learn_rate, weight_decay=0.0)
|
||||
if shared.opts.save_optimizer_state:
|
||||
optimizer_state_dict = None
|
||||
if os.path.exists(filename + '.optim'):
|
||||
optimizer_saved_dict = torch.load(filename + '.optim', map_location='cpu')
|
||||
if os.path.exists(f"{filename}.optim"):
|
||||
optimizer_saved_dict = torch.load(f"{filename}.optim", map_location='cpu')
|
||||
if embedding.checksum() == optimizer_saved_dict.get('hash', None):
|
||||
optimizer_state_dict = optimizer_saved_dict.get('optimizer_state_dict', None)
|
||||
if optimizer_state_dict is not None:
|
||||
|
||||
+43
-30
@@ -76,7 +76,7 @@ def visit(x, func, path=""):
|
||||
for c in x.children:
|
||||
visit(c, func, path)
|
||||
elif x.label is not None:
|
||||
func(path + "/" + str(x.label), x)
|
||||
func(f"{path}/{x.label}", x)
|
||||
|
||||
|
||||
def add_style(name: str, prompt: str, negative_prompt: str):
|
||||
@@ -127,7 +127,7 @@ def process_interrogate(interrogation_function, mode, ii_input_dir, ii_output_di
|
||||
img = Image.open(image)
|
||||
filename = os.path.basename(image)
|
||||
left, _ = os.path.splitext(filename)
|
||||
print(interrogation_function(img), file=open(os.path.join(ii_output_dir, left + ".txt"), 'a', encoding='utf-8'))
|
||||
print(interrogation_function(img), file=open(os.path.join(ii_output_dir, f"{left}.txt"), 'a', encoding='utf-8'))
|
||||
|
||||
return [gr.update(), None]
|
||||
|
||||
@@ -147,21 +147,21 @@ def change_clip_skip(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')
|
||||
with FormRow(elem_id=f"{target_interface}_seed_row", variant="compact"):
|
||||
seed = gr.Number(label='Seed', value=-1, elem_id=f"{target_interface}_seed")
|
||||
seed.style(container=False)
|
||||
random_seed = ToolButton(random_symbol, elem_id=target_interface + '_random_seed', label='Random seed')
|
||||
reuse_seed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_seed', label='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')
|
||||
random_seed = ToolButton(random_symbol, elem_id=f"{target_interface}_random_seed", label='Random seed')
|
||||
reuse_seed = ToolButton(reuse_symbol, elem_id=f"{target_interface}_reuse_seed", label='Reuse seed')
|
||||
seed_checkbox = gr.Checkbox(label='Extra', elem_id=f"{target_interface}_subseed_show", value=False) # Ghost checkbox for compatibility
|
||||
with FormRow(visible=True, elem_id=f"{target_interface}_subseed_row"):
|
||||
subseed = gr.Number(label='Variation seed', value=-1, elem_id=f"{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')
|
||||
random_subseed = ToolButton(random_symbol, elem_id=f"{target_interface}_random_subseed")
|
||||
reuse_subseed = ToolButton(reuse_symbol, elem_id=f"{target_interface}_reuse_subseed")
|
||||
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=f"{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')
|
||||
seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=f"{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=f"{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
|
||||
@@ -615,7 +615,7 @@ def create_ui():
|
||||
)
|
||||
button.click(
|
||||
fn=lambda: None,
|
||||
_js="switch_to_"+name.replace(" ", "_"),
|
||||
_js=f"switch_to_{name.replace(' ', '_')}",
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
)
|
||||
@@ -679,7 +679,7 @@ def create_ui():
|
||||
with FormGroup():
|
||||
with FormRow():
|
||||
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=6.0, elem_id="img2img_cfg_scale")
|
||||
image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=modules.shared.sd_model and modules.shared.sd_model.cond_stage_key == "edit")
|
||||
image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=False)
|
||||
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength")
|
||||
clip_skip = gr.Slider(label='CLIP Skip', value=modules.shared.opts.CLIP_stop_at_last_layers, minimum=1, maximum=4, step=1, elem_id='img2img_clip_skip', interactive=True)
|
||||
clip_skip.change(fn=change_clip_skip, show_progress=False, inputs=clip_skip)
|
||||
@@ -1271,16 +1271,16 @@ def create_ui():
|
||||
elif t == bool:
|
||||
comp = gr.Checkbox
|
||||
else:
|
||||
raise ValueError(f'bad options item type: {str(t)} for key {key}')
|
||||
elem_id = "setting_"+key
|
||||
raise ValueError(f'bad options item type: {t} for key {key}')
|
||||
elem_id = f"setting_{key}"
|
||||
if info.refresh is not None:
|
||||
if is_quicksettings:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key)
|
||||
create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
else:
|
||||
with FormRow():
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key)
|
||||
create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
else:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
return res
|
||||
@@ -1331,7 +1331,7 @@ def create_ui():
|
||||
|
||||
result = gr.HTML(elem_id="settings_result")
|
||||
|
||||
quicksettings_names = [x.strip() for x in opts.quicksettings.split(",")]
|
||||
quicksettings_names = opts.quicksettings_list
|
||||
quicksettings_names = {x: i for i, x in enumerate(quicksettings_names) if x != 'quicksettings'}
|
||||
quicksettings_list = []
|
||||
previous_section = None
|
||||
@@ -1366,7 +1366,7 @@ def create_ui():
|
||||
|
||||
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")
|
||||
with gr.TabItem("Licenses", id="licenses"):
|
||||
with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"):
|
||||
gr.HTML(modules.shared.html("licenses.html"), elem_id="licenses")
|
||||
|
||||
def unload_sd_weights():
|
||||
@@ -1443,7 +1443,7 @@ def create_ui():
|
||||
for interface, label, ifid in interfaces:
|
||||
if label in modules.shared.opts.hidden_tabs:
|
||||
continue
|
||||
with gr.TabItem(label, id=ifid, elem_id='tab_' + ifid):
|
||||
with gr.TabItem(label, id=ifid, elem_id=f"tab_{ifid}"):
|
||||
interface.render()
|
||||
|
||||
if opts.notification_audio_enable and os.path.exists(os.path.join(script_path, opts.notification_audio_path)):
|
||||
@@ -1471,11 +1471,9 @@ def create_ui():
|
||||
show_progress=info.refresh is not None,
|
||||
)
|
||||
|
||||
text_settings.change(
|
||||
fn=lambda: gr.update(visible=modules.shared.sd_model and modules.shared.sd_model.cond_stage_key == "edit"),
|
||||
inputs=[],
|
||||
outputs=[image_cfg_scale],
|
||||
)
|
||||
update_image_cfg_scale_visibility = lambda: gr.update(visible=modules.shared.sd_model and modules.shared.sd_model.cond_stage_key == "edit") # pylint: disable=unnecessary-lambda-assignment
|
||||
text_settings.change(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
|
||||
demo.load(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
|
||||
|
||||
button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False)
|
||||
button_set_checkpoint.click(
|
||||
@@ -1549,10 +1547,10 @@ def create_ui():
|
||||
|
||||
def loadsave(path, x):
|
||||
def apply_field(obj, field, condition=None, init_field=None):
|
||||
key = path + "/" + field
|
||||
key = f"{path}/{field}"
|
||||
|
||||
if getattr(obj, 'custom_script_source', None) is not None:
|
||||
key = 'customscript/' + obj.custom_script_source + '/' + key
|
||||
key = f"customscript/{obj.custom_script_source}/{key}"
|
||||
|
||||
if getattr(obj, 'do_not_save_to_config', False):
|
||||
return
|
||||
@@ -1699,5 +1697,20 @@ def reload_javascript():
|
||||
gradio.routes.templates.TemplateResponse = template_response
|
||||
|
||||
|
||||
def setup_ui_api(app):
|
||||
from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
|
||||
from typing import List
|
||||
|
||||
class QuicksettingsHint(BaseModel):
|
||||
name: str = Field(title="Name of the quicksettings field")
|
||||
label: str = Field(title="Label of the quicksettings field")
|
||||
|
||||
def quicksettings_hint():
|
||||
return [QuicksettingsHint(name=k, label=v.label) for k, v in opts.data_labels.items()]
|
||||
|
||||
app.add_api_route("/internal/quicksettings-hint", quicksettings_hint, methods=["GET"], response_model=List[QuicksettingsHint])
|
||||
app.add_api_route("/internal/ping", lambda: {}, methods=["GET"])
|
||||
|
||||
|
||||
if not hasattr(modules.shared, 'GradioTemplateResponseOriginal'):
|
||||
modules.shared.GradioTemplateResponseOriginal = gradio.routes.templates.TemplateResponse
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import json
|
||||
import html
|
||||
import glob
|
||||
import os.path
|
||||
import urllib.parse
|
||||
from pathlib import Path
|
||||
@@ -63,7 +62,9 @@ class ExtraNetworksPage:
|
||||
pass
|
||||
|
||||
def link_preview(self, filename):
|
||||
return "./sd_extra_networks/thumb?filename=" + urllib.parse.quote(filename.replace('\\', '/')) + "&mtime=" + str(os.path.getmtime(filename))
|
||||
quoted_filename = urllib.parse.quote(filename.replace('\\', '/'))
|
||||
mtime = os.path.getmtime(filename)
|
||||
return f"./sd_extra_networks/thumb?filename={quoted_filename}&mtime={mtime}"
|
||||
|
||||
def search_terms_from_path(self, filename, possible_directories=None):
|
||||
abspath = os.path.abspath(filename)
|
||||
@@ -78,17 +79,20 @@ class ExtraNetworksPage:
|
||||
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
|
||||
allowed_folders = [os.path.abspath(x) for x in self.allowed_directories_for_previews()]
|
||||
for parentdir in [*set(allowed_folders)]:
|
||||
for root, dirs, _files in os.walk(parentdir):
|
||||
for dirname in dirs:
|
||||
x = os.path.join(root, dirname)
|
||||
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"""
|
||||
@@ -181,7 +185,7 @@ def intialize():
|
||||
class ExtraNetworksUi:
|
||||
def __init__(self):
|
||||
self.pages = None
|
||||
self.stored_extra_pages = None
|
||||
self.stored_extra_pages = []
|
||||
self.button_save_preview = None
|
||||
self.preview_target_filename = None
|
||||
self.button_save_description = None
|
||||
|
||||
@@ -39,7 +39,7 @@ def save_pil_to_file(pil_image, dir=None): # pylint: disable=redefined-builtin
|
||||
already_saved_as = getattr(pil_image, 'already_saved_as', None)
|
||||
if already_saved_as and os.path.isfile(already_saved_as):
|
||||
register_tmp_file(shared.demo, already_saved_as)
|
||||
file_obj = Savedfile(already_saved_as)
|
||||
file_obj = Savedfile(f'{already_saved_as}?{os.path.getmtime(already_saved_as)}')
|
||||
return file_obj
|
||||
if shared.opts.temp_dir != "":
|
||||
dir = shared.opts.temp_dir
|
||||
|
||||
@@ -77,7 +77,7 @@ return process_images(p)
|
||||
module.display = display
|
||||
|
||||
indent = " " * indent_level
|
||||
indented = code.replace('\n', '\n' + indent)
|
||||
indented = code.replace('\n', f"\n{indent}")
|
||||
body = f"""def __webuitemp__():
|
||||
{indent}{indented}
|
||||
__webuitemp__()"""
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ class Script(scripts.Script):
|
||||
p.color_corrections = initial_color_corrections
|
||||
|
||||
if append_interrogation != "None":
|
||||
p.prompt = original_prompt + ", " if original_prompt != "" else ""
|
||||
p.prompt = f"{original_prompt}, " if original_prompt else ""
|
||||
if append_interrogation == "CLIP":
|
||||
p.prompt += shared.interrogator.interrogate(p.init_images[0])
|
||||
elif append_interrogation == "DeepBooru":
|
||||
|
||||
+2
-2
@@ -227,7 +227,7 @@ axis_options = [
|
||||
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, fmt=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("VAE", str, apply_vae, cost=0.7, choices=lambda: ['None'] + list(sd_vae.vae_dict)),
|
||||
AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)),
|
||||
AxisOption("UniPC Order", int, apply_uni_pc_order, cost=0.5),
|
||||
AxisOption("Face restore", str, apply_face_restore, fmt=format_value),
|
||||
@@ -446,7 +446,7 @@ class Script(scripts.Script):
|
||||
z_type.change(fn=select_axis, inputs=[z_type,z_values_dropdown], outputs=[fill_z_button,z_values,z_values_dropdown])
|
||||
|
||||
def get_dropdown_update_from_params(axis,params):
|
||||
val_key = axis + " Values"
|
||||
val_key = f"{axis} Values"
|
||||
vals = params.get(val_key,"")
|
||||
valslist = [x.strip() for x in chain.from_iterable(csv.reader(StringIO(vals))) if x]
|
||||
return gr.update(value = valslist)
|
||||
|
||||
@@ -5,6 +5,7 @@ import signal
|
||||
import asyncio
|
||||
import logging
|
||||
import warnings
|
||||
from threading import Thread
|
||||
from modules import timer, errors
|
||||
|
||||
startup_timer = timer.Timer()
|
||||
@@ -27,6 +28,7 @@ warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvisi
|
||||
startup_timer.record("torch")
|
||||
|
||||
from modules import import_hook # pylint: disable=W0611,C0411,C0412
|
||||
from fastapi import FastAPI # pylint: disable=W0611,C0411
|
||||
import gradio # pylint: disable=W0611,C0411
|
||||
startup_timer.record("gradio")
|
||||
errors.install([gradio])
|
||||
@@ -148,6 +150,8 @@ def initialize():
|
||||
def load_model():
|
||||
shared.state.begin()
|
||||
shared.state.job = 'load model'
|
||||
|
||||
"""
|
||||
try:
|
||||
modules.sd_models.load_model()
|
||||
modules.sd_models.skip_next_load = True
|
||||
@@ -155,17 +159,22 @@ def load_model():
|
||||
errors.display(e, "loading stable diffusion model")
|
||||
log.error("Stable diffusion model failed to load")
|
||||
exit(1)
|
||||
"""
|
||||
Thread(target=lambda: shared.sd_model).start()
|
||||
|
||||
if shared.sd_model is None:
|
||||
log.warning("No stable diffusion model loaded")
|
||||
# exit(1)
|
||||
else:
|
||||
shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title
|
||||
shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights()))
|
||||
shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights()), call=False)
|
||||
|
||||
shared.state.end()
|
||||
startup_timer.record("checkpoint")
|
||||
|
||||
|
||||
def create_api(app):
|
||||
log.debug('Creating API')
|
||||
from modules.api.api import Api
|
||||
api = Api(app, queue_lock)
|
||||
return api
|
||||
@@ -177,7 +186,6 @@ def monkey_patch_docs():
|
||||
self.redoc_url = "/redoc"
|
||||
self.setup_original()
|
||||
|
||||
from fastapi import FastAPI
|
||||
setup_original = getattr(FastAPI, "setup_original", None)
|
||||
if setup_original is None:
|
||||
FastAPI.setup_original = FastAPI.setup
|
||||
@@ -199,8 +207,8 @@ def async_policy():
|
||||
asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
|
||||
|
||||
|
||||
def start_ui():
|
||||
log.debug('Entering StartUI')
|
||||
def start_common():
|
||||
log.debug('Entering start sequence')
|
||||
logging.disable(logging.NOTSET if cmd_opts.debug else logging.DEBUG)
|
||||
create_paths(opts)
|
||||
async_policy()
|
||||
@@ -208,6 +216,10 @@ def start_ui():
|
||||
if shared.opts.clean_temp_dir_at_start:
|
||||
ui_tempdir.cleanup_tmpdr()
|
||||
startup_timer.record("cleanup")
|
||||
|
||||
|
||||
def start_ui():
|
||||
log.debug('Creating UI')
|
||||
modules.script_callbacks.before_ui_callback()
|
||||
startup_timer.record("scripts before_ui_callback")
|
||||
shared.demo = modules.ui.create_ui()
|
||||
@@ -250,6 +262,12 @@ def start_ui():
|
||||
shared.demo.server.wants_restart = False
|
||||
setup_middleware(app, cmd_opts)
|
||||
|
||||
if cmd_opts.subpath:
|
||||
redirector = FastAPI()
|
||||
redirector.get("/")
|
||||
_mounted_app = gradio.mount_gradio_app(redirector, shared.demo, path=f"/{cmd_opts.subpath}")
|
||||
shared.log.info('Redirector mounted: /{cmd_opts.subpath}')
|
||||
|
||||
cmd_opts.autolaunch = False
|
||||
startup_timer.record("start")
|
||||
|
||||
@@ -262,12 +280,27 @@ def start_ui():
|
||||
|
||||
|
||||
def webui():
|
||||
log.debug('Entering WebUI')
|
||||
start_common()
|
||||
start_ui()
|
||||
load_model()
|
||||
log.info(f"Startup time: {startup_timer.summary()}")
|
||||
return shared.demo.server
|
||||
|
||||
|
||||
def api_only():
|
||||
start_common()
|
||||
app = FastAPI()
|
||||
setup_middleware(app, cmd_opts)
|
||||
api = create_api(app)
|
||||
api.wants_restart = False
|
||||
modules.script_callbacks.app_started_callback(None, app)
|
||||
log.info(f"Startup time: {startup_timer.summary()}")
|
||||
api.launch(server_name="0.0.0.0" if cmd_opts.listen else "127.0.0.1", port=cmd_opts.port if cmd_opts.port else 7861)
|
||||
return api
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
webui()
|
||||
if cmd_opts.api_only:
|
||||
api_only()
|
||||
else:
|
||||
webui()
|
||||
|
||||
Reference in New Issue
Block a user