diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd92b2c7e..2917625ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,13 @@
# Change Log for SD.Next
-## Update for 2023-09-15
+## Update for 2023-09-18
Downgrade of `diffusers` to 0.20.2 due to critical issue with model offloading
This means that new model **Wuerstchen** is not supported until diffusers issue is resolved
+- Added **change log** to UI, see *System -> Changelog*
+- **Extra networks**: faster search, ability to show/hide/sort networks
+
## Update for 2023-09-13
Started as a mostly a service release with quite a few fixes, but then...
diff --git a/README.md b/README.md
index bc1f07692..19318a2eb 100644
--- a/README.md
+++ b/README.md
@@ -69,10 +69,10 @@ Additional models will be added as they become available and there is public int
- *nVidia* GPUs using **CUDA** libraries on both *Windows and Linux*
- *AMD* GPUs using **ROCm** libraries on *Linux*.
Support will be extended to *Windows* once AMD releases ROCm for Windows
-- Any GPU compatibile with *DirectX* on *Windows* using **DirectML** libraries.
- This includes support for AMD GPUs that are not supported by native ROCm libraries
- *Intel Arc* GPUs using **OneAPI** with *IPEX XPU* libraries on both *Windows and Linux*
-- *Intel* GPUs using **OpenVINO** libraries on both *Windows and Linux*
+- Any GPU compatible with *DirectX* on *Windows* using **DirectML** libraries.
+ This includes support for AMD GPUs that are not supported by native ROCm libraries
+- Any GPU compatible with **OpenVINO** libraries on both *Windows and Linux*
- *Apple M1/M2* on *OSX* using built-in support in Torch with **MPS** optimizations
## Install & Run
@@ -175,7 +175,7 @@ General goals:
### **Sponsors**
diff --git a/extensions-builtin/LDSR/scripts/ldsr_model.py b/extensions-builtin/LDSR/scripts/ldsr_model.py
index 9c40740a5..e2c671b15 100644
--- a/extensions-builtin/LDSR/scripts/ldsr_model.py
+++ b/extensions-builtin/LDSR/scripts/ldsr_model.py
@@ -2,8 +2,6 @@ import os
import sys
import traceback
-from basicsr.utils.download_util import load_file_from_url
-
from modules.upscaler import Upscaler, UpscalerData
from ldsr_model_arch import LDSR
from modules import shared, script_callbacks
@@ -42,6 +40,7 @@ class UpscalerLDSR(Upscaler):
print("Renaming model from model.pth to model.ckpt")
os.rename(old_model_path, new_model_path)
+ from modules.modelloader import load_file_from_url
if local_safetensors_path is not None and os.path.exists(local_safetensors_path):
model = local_safetensors_path
else:
diff --git a/extensions-builtin/ScuNET/scripts/scunet_model.py b/extensions-builtin/ScuNET/scripts/scunet_model.py
index c1aa25230..120ec0ffc 100644
--- a/extensions-builtin/ScuNET/scripts/scunet_model.py
+++ b/extensions-builtin/ScuNET/scripts/scunet_model.py
@@ -7,8 +7,6 @@ import numpy as np
import torch
from tqdm import tqdm
-from basicsr.utils.download_util import load_file_from_url
-
import modules.upscaler
from modules import devices, modelloader, script_callbacks
from scunet_model_arch import SCUNet as net
@@ -121,6 +119,7 @@ class UpscalerScuNET(modules.upscaler.Upscaler):
def load_model(self, path: str):
device = devices.get_device_for('scunet')
if "http" in path:
+ from modules.modelloader import load_file_from_url
filename = load_file_from_url(url=self.model_url, model_dir=self.model_download_path, file_name="%s.pth" % self.name, progress=True)
else:
filename = path
diff --git a/extensions-builtin/SwinIR/scripts/swinir_model.py b/extensions-builtin/SwinIR/scripts/swinir_model.py
index e470db874..4527b4c79 100644
--- a/extensions-builtin/SwinIR/scripts/swinir_model.py
+++ b/extensions-builtin/SwinIR/scripts/swinir_model.py
@@ -2,8 +2,7 @@ import os
import numpy as np
import torch
from PIL import Image
-from basicsr.utils.download_util import load_file_from_url
-from tqdm.rich import tqdm
+from rich.progress import Progress, TextColumn, BarColumn, TaskProgressColumn, TimeRemainingColumn, TimeElapsedColumn
from swinir_model_arch import SwinIR as net
from swinir_model_arch_v2 import Swin2SR as net2
from modules import modelloader, devices, script_callbacks, shared
@@ -45,11 +44,13 @@ class UpscalerSwinIR(Upscaler):
def load_model(self, path, scale=4):
if "http" in path:
- dl_name = "%s%s" % (self.model_name.replace(" ", "_"), ".pth")
+ from modules.modelloader import load_file_from_url
+ dl_name = "%s%s" % (self.model_name.replace(" ", "_"), ".pth") # pylint: disable=consider-using-f-string
filename = load_file_from_url(url=path, model_dir=self.model_download_path, file_name=dl_name, progress=True)
else:
filename = path
if filename is None or not os.path.exists(filename):
+ shared.log.error(f"Model failed loading: type=SwinIR model={filename}")
return None
model_v2 = net2(
upscale=scale,
@@ -78,6 +79,8 @@ class UpscalerSwinIR(Upscaler):
resi_connection="3conv",
)
pretrained_model = torch.load(filename)
+ shared.log.info(f"Model loaded: type=SwinIR model={filename}")
+
for model in [model_v1, model_v2]:
for param in ["params_ema", "params", None]:
try:
@@ -140,7 +143,8 @@ def inference(img, model, tile, tile_overlap, window_size, scale):
E = torch.zeros(b, c, h * sf, w * sf, dtype=devices.dtype, device=device_swinir).type_as(img)
W = torch.zeros_like(E, dtype=devices.dtype, device=device_swinir)
- with tqdm(total=len(h_idx_list) * len(w_idx_list), desc="Upscaling SwinIR") as pbar:
+ with Progress(TextColumn('[cyan]{task.description}'), BarColumn(), TaskProgressColumn(), TimeRemainingColumn(), TimeElapsedColumn(), console=shared.console) as progress:
+ task = progress.add_task(description="Upscaling Initializing", total=len(h_idx_list) * len(w_idx_list))
for h_idx in h_idx_list:
if state.interrupted or state.skipped:
break
@@ -159,7 +163,7 @@ def inference(img, model, tile, tile_overlap, window_size, scale):
W[
..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf
].add_(out_patch_mask)
- pbar.update(1)
+ progress.update(task, advance=1, description="Upscaling")
output = E.div_(W)
return output
diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info
index 83dd4d8f6..b73ffdcc8 160000
--- a/extensions-builtin/sd-extension-system-info
+++ b/extensions-builtin/sd-extension-system-info
@@ -1 +1 @@
-Subproject commit 83dd4d8f65511af720ddb034125fe4ac0a46fb18
+Subproject commit b73ffdcc8d6a9622d3d8074862b109b20726ac73
diff --git a/html/locale_en.json b/html/locale_en.json
index 692a82929..f17742039 100644
--- a/html/locale_en.json
+++ b/html/locale_en.json
@@ -356,7 +356,7 @@
{"id":"","label":"Enable splitting of hires batch processing","localized":"","hint":"Reduces VRAM usage when using hires fix on batches of images"},
{"id":"","label":"Load models using stream loading method","localized":"","hint":"When loading models attempt stream loading optimized for slow or network storage"},
{"id":"","label":"When loading models attempt to reuse previous model dictionary","localized":"","hint":""},
- {"id":"","label":"Disable cross-attention layer optimization","localized":"","hint":"Disable the all cross-attention optimization. May result in higher VRAM usage and longer generation times"},
+ {"id":"","label":"Disabled","localized":"","hint":""},
{"id":"","label":"xFormers","localized":"","hint":"Memory optimization. Non-Deterministic (different results each time)"},
{"id":"","label":"Scaled-Dot-Product","localized":"","hint":"Memory optimization. Non-Deterministic unless SDP memory attention is disabled."},
{"id":"","label":"Doggettx's","localized":"","hint":""},
@@ -365,9 +365,9 @@
{"id":"","label":"Split attention","localized":"","hint":""},
{"id":"","label":"xFormers enable flash Attention","localized":"","hint":""},
{"id":"","label":"SDP disable memory attention","localized":"","hint":""},
- {"id":"","label":"Sub-quadratic cross-attention query chunk size","localized":"","hint":""},
- {"id":"","label":"Sub-quadratic cross-attention kv chunk size","localized":"","hint":""},
- {"id":"","label":"Sub-quadratic cross-attention chunking threshold","localized":"","hint":""},
+ {"id":"","label":"cross-attention query chunk size","localized":"","hint":""},
+ {"id":"","label":"cross-attention kv chunk size","localized":"","hint":""},
+ {"id":"","label":"cross-attention chunking threshold","localized":"","hint":""},
{"id":"","label":"Full parser","localized":"","hint":""},
{"id":"","label":"Compel parser","localized":"","hint":""},
{"id":"","label":"A1111 parser","localized":"","hint":""},
@@ -466,9 +466,9 @@
{"id":"","label":"Auto","localized":"","hint":""},
{"id":"","label":"Dark","localized":"","hint":""},
{"id":"","label":"Light","localized":"","hint":""},
- {"id":"","label":"Show grid in results for web","localized":"","hint":""},
- {"id":"","label":"For inpainting, include the greyscale mask in results for web","localized":"","hint":""},
- {"id":"","label":"For inpainting, include masked composite in results for web","localized":"","hint":""},
+ {"id":"","label":"Show grid in results","localized":"","hint":""},
+ {"id":"","label":"For inpainting, include the greyscale mask in results","localized":"","hint":""},
+ {"id":"","label":"For inpainting, include masked composite in results","localized":"","hint":""},
{"id":"","label":"Do not change selected model when reading generation parameters","localized":"","hint":""},
{"id":"","label":"Send seed when sending prompt or image to other interface","localized":"","hint":""},
{"id":"","label":"Send size when sending prompt or image to another interface","localized":"","hint":""},
diff --git a/html/logo-bg-dark.jpg b/html/logo-bg-dark.jpg
index 20392e1ae..40af4bbbc 100644
Binary files a/html/logo-bg-dark.jpg and b/html/logo-bg-dark.jpg differ
diff --git a/installer.py b/installer.py
index ae227723d..d7350654f 100644
--- a/installer.py
+++ b/installer.py
@@ -430,7 +430,7 @@ def check_torch():
elif allow_openvino and args.use_openvino:
#Remove this after 2.1.0 releases
log.info('Using OpenVINO')
- torch_command = os.environ.get('TORCH_COMMAND', '--pre torch==2.1.0.dev20230726+cpu torchvision==0.16.0.dev20230726+cpu -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html')
+ torch_command = os.environ.get('TORCH_COMMAND', '--pre torch==2.1.0.dev20230820+cpu torchvision==0.16.0.dev20230820+cpu -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html')
else:
machine = platform.machine()
if sys.platform == 'darwin':
@@ -496,7 +496,7 @@ def check_torch():
if opts.get('cuda_compile_backend', '') == 'hidet':
install('hidet', 'hidet')
if args.use_openvino or opts.get('cuda_compile_backend', '') == 'openvino_fx':
- install('openvino==2023.1.0.dev20230811', 'openvino')
+ install('openvino==2023.1.0', 'openvino')
os.environ.setdefault('PYTORCH_TRACING_MODE', 'TORCHFX')
if args.profile:
print_profile(pr, 'Torch')
diff --git a/javascript/invokeai.css b/javascript/invokeai.css
new file mode 100644
index 000000000..363aff97b
--- /dev/null
+++ b/javascript/invokeai.css
@@ -0,0 +1,313 @@
+/* generic html tags */
+:root, .light, .dark {
+ --font: 'system-ui', 'ui-sans-serif', 'system-ui', "Roboto", sans-serif;
+ --font-mono: 'ui-monospace', 'Consolas', monospace;
+ --font-size: 16px;
+ --left-column: 490px;
+ --primary-100: #2b303b;
+ --primary-200: #15181e;
+ --primary-300: #0a0c0e;
+ --primary-400: #566176;
+ --primary-500: #483a90;
+ --primary-700: #6b7994;
+ --primary-800: #5b49b3;
+ --highlight-color: var(--primary-500);
+ --inactive-color: var(--primary--800);
+ --body-text-color: var(--neutral-100);
+ --body-text-color-subdued: var(--neutral-300);
+ --background-color: var(--primary-100);
+ --background-fill-primary: var(--input-background-fill);
+ --input-padding: 8px;
+ --input-background-fill: var(--primary-200);
+ --input-shadow: none;
+ --button-secondary-text-color: white;
+ --button-secondary-background-fill: var(--primary-400);
+ --button-secondary-background-fill-hover: var(--primary-700);
+ --block-title-text-color: var(--neutral-300);
+ --radius-sm: 1px;
+ --radius-lg: 6px;
+ --spacing-md: 4px;
+ --spacing-xxl: 8px;
+ --line-sm: 1.2em;
+ --line-md: 1.4em;
+}
+
+html { font-size: var(--font-size); }
+body, button, input, select, textarea { font-family: var(--font);}
+button { font-size: 1.2rem; max-width: 400px; }
+img { background-color: var(--background-color); }
+input[type=range] { height: var(--line-sm); appearance: none; margin-top: 0; min-width: 160px; background-color: var(--background-color); width: 100%; background: transparent; }
+input[type=range]::-webkit-slider-runnable-track, input[type=range]::-moz-range-track { width: 100%; height: 6px; cursor: pointer; background: var(--primary-400); border-radius: var(--radius-lg); border: 0px solid #222222; }
+input[type=range]::-webkit-slider-thumb, input[type=range]::-moz-range-thumb { border: 0px solid #000000; height: var(--line-sm); width: 8px; border-radius: var(--radius-lg); background: white; cursor: pointer; appearance: none; margin-top: 0px; }
+input[type=range]::-moz-range-progress { background-color: var(--primary-500); height: 6px; border-radius: var(--radius-lg); }
+::-webkit-scrollbar-track { background: #333333; }
+::-webkit-scrollbar-thumb { background-color: var(--highlight-color); border-radius: var(--radius-lg); border-width: 0; box-shadow: 2px 2px 3px #111111; }
+div.form { border-width: 0; box-shadow: none; background: transparent; overflow: visible; margin-bottom: 6px; }
+div.compact { gap: 1em; }
+
+/* gradio style classes */
+fieldset .gr-block.gr-box, label.block span { padding: 0; margin-top: -4px; }
+.border-2 { border-width: 0; }
+.border-b-2 { border-bottom-width: 2px; border-color: var(--highlight-color) !important; padding-bottom: 2px; margin-bottom: 8px; }
+.bg-white { color: lightyellow; background-color: var(--inactive-color); }
+.gr-box { border-radius: var(--radius-sm) !important; background-color: #111111 !important; box-shadow: 2px 2px 3px #111111; border-width: 0; padding: 4px; margin: 12px 0px 12px 0px }
+.gr-button { font-weight: normal; box-shadow: 2px 2px 3px #111111; font-size: 0.8rem; min-width: 32px; min-height: 32px; padding: 3px; margin: 3px; }
+.gr-check-radio { background-color: var(--inactive-color); border-width: 0; border-radius: var(--radius-lg); box-shadow: 2px 2px 3px #111111; }
+.gr-check-radio:checked { background-color: var(--highlight-color); }
+.gr-compact { background-color: var(--background-color); }
+.gr-form { border-width: 0; }
+.gr-input { background-color: #333333 !important; padding: 4px; margin: 4px; }
+.gr-input-label { color: lightyellow; border-width: 0; background: transparent; padding: 2px !important; }
+.gr-panel { background-color: var(--background-color); }
+.eta-bar { display: none !important }
+svg.feather.feather-image, .feather .feather-image { display: none }
+.gap-2 { padding-top: 8px; }
+.gr-box > div > div > input.gr-text-input { right: 0; width: 4em; padding: 0; top: -12px; border: none; max-height: 20px; }
+.output-html { line-height: 1.2rem; overflow-x: hidden; }
+.output-html > div { margin-bottom: 8px; }
+.overflow-hidden .flex .flex-col .relative col .gap-4 { min-width: var(--left-column); max-width: var(--left-column); } /* this is a problematic one */
+.p-2 { padding: 0; }
+.px-4 { padding-lefT: 1rem; padding-right: 1rem; }
+.py-6 { padding-bottom: 0; }
+.tabs { background-color: var(--background-color); }
+.block.token-counter span { background-color: var(--input-background-fill) !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; }
+.tab-nav { zoom: 120%; margin-top: 10px; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; }
+div.tab-nav button.selected {background-color: var(--button-primary-background-fill);}
+#settings div.tab-nav button.selected {background-color: var(--background-color); color: var(--primary-800); font-weight: bold;}
+.label-wrap { background-color: #363c4a; padding: 16px 8px 8px 8px; border-radius: var(--radius-lg);}
+.gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; }
+.gradio-button.tool { border: none; box-shadow: none; border-radius: var(--radius-lg);}
+button.selected {background: var(--button-primary-background-fill);}
+.center.boundedheight.flex {background-color: var(--input-background-fill);}
+.compact {border-radius: var(--border-radius-lg);}
+#logMonitorData {background-color: var(--input-background-fill);}
+#tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: none; padding: 0.5em; background-color: var(--primary-200); }
+#tab_extensions table, #tab_config table { width: 96vw; }
+#tab_extensions table input[type=checkbox] {appearance: none; border-radius: 0px;}
+#tab_extensions button:hover { background-color: var(--button-secondary-background-fill-hover);}
+
+/* automatic style classes */
+.progressDiv { border-radius: var(--radius-sm) !important; position: fixed; top: 44px; right: 26px; max-width: 262px; height: 48px; z-index: 99; box-shadow: var(--button-shadow); }
+.progressDiv .progress { border-radius: var(--radius-lg) !important; background: var(--highlight-color); line-height: 3rem; height: 48px; }
+.gallery-item { box-shadow: none !important; }
+.performance { color: #888; }
+.extra-networks { border-left: 2px solid var(--highlight-color) !important; padding-left: 4px; }
+.image-buttons { gap: 10px !important; justify-content: center; }
+.image-buttons > button { max-width: 160px; }
+.tooltip { background: var(--primary-800); color: white; border: none; border-radius: var(--radius-lg) }
+#system_row > button, #settings_row > button, #config_row > button { max-width: 190px; }
+
+/* gradio elements overrides */
+#div.gradio-container { overflow-x: hidden; }
+#img2img_label_copy_to_img2img { font-weight: normal; }
+#txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt { background-color: var(--background-color); box-shadow: 4px 4px 4px 0px #333333 !important; }
+#txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea { font-size: 1.1rem; }
+#img2img_settings { min-width: calc(2 * var(--left-column)); max-width: calc(2 * var(--left-column)); background-color: #111111; padding-top: 16px; }
+#interrogate, #deepbooru { margin: 0 0px 10px 0px; max-width: 80px; max-height: 80px; font-weight: normal; font-size: 0.95em; }
+#quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-top: -2px; height: 2.4em; }
+#quicksettings button {padding: 0 0.5em 0.1em 0.5em;}
+#open_folder_extras, #footer, #style_pos_col, #style_neg_col, #roll_col, #extras_upscaler_2, #extras_upscaler_2_visibility, #txt2img_seed_resize_from_w, #txt2img_seed_resize_from_h { display: none; }
+#save-animation { border-radius: var(--radius-sm) !important; margin-bottom: 16px; background-color: #111111; }
+#script_list { padding: 4px; margin-top: 20px; margin-bottom: 20px; }
+#settings > div.flex-wrap { width: 15em; }
+#txt2img_cfg_scale { min-width: 200px; }
+#txt2img_checkboxes, #img2img_checkboxes { background-color: transparent; }
+#txt2img_checkboxes, #img2img_checkboxes { margin-bottom: 0.2em; }
+#txt2img_actions_column, #img2img_actions_column { flex-flow: wrap; justify-content: space-between; }
+#txt2img_enqueue_wrapper, #img2img_enqueue_wrapper { min-width: unset; width: 48%; }
+#txt2img_generate_box, #img2img_generate_box { min-width: unset; width: 48%; }
+
+#extras_upscale { margin-top: 10px }
+#txt2img_progress_row > div { min-width: var(--left-column); max-width: var(--left-column); }
+#txt2img_results, #img2img_results, #extras_results { background-color: var(--background-color); padding: 0; }
+#txt2img_settings { min-width: var(--left-column); max-width: var(--left-column); background-color: #111111; padding-top: 16px; }
+#pnginfo_html2_info { margin-top: -18px; background-color: var(--input-background-fill); padding: var(--input-padding) }
+#txt2img_tools, #img2img_tools { margin-top: -4px; margin-bottom: -4px; }
+#txt2img_styles_row, #img2img_styles_row { margin-top: -6px; z-index: 200; }
+
+/* custom elements overrides */
+#steps-animation, #controlnet { border-width: 0; }
+
+/* based on gradio built-in dark theme */
+:root, .light, .dark {
+ --body-background-fill: var(--background-color);
+ --color-accent-soft: var(--neutral-700);
+ --background-fill-secondary: none;
+ --border-color-accent: var(--background-color);
+ --border-color-primary: var(--background-color);
+ --link-text-color-active: var(--primary-500);
+ --link-text-color: var(--secondary-500);
+ --link-text-color-hover: var(--secondary-400);
+ --link-text-color-visited: var(--secondary-600);
+ --shadow-spread: 1px;
+ --block-background-fill: None;
+ --block-border-color: var(--border-color-primary);
+ --block_border_width: None;
+ --block-info-text-color: var(--body-text-color-subdued);
+ --block-label-background-fill: var(--background-fill-secondary);
+ --block-label-border-color: var(--border-color-primary);
+ --block_label_border_width: None;
+ --block-label-text-color: var(--neutral-200);
+ --block_shadow: None;
+ --block_title_background_fill: None;
+ --block_title_border_color: None;
+ --block_title_border_width: None;
+ --panel-background-fill: var(--background-fill-secondary);
+ --panel-border-color: var(--border-color-primary);
+ --panel_border_width: None;
+ --checkbox-background-color: var(--primary-400);
+ --checkbox-background-color-focus: var(--primary-700);
+ --checkbox-background-color-hover: var(--primary-700);
+ --checkbox-background-color-selected: var(--primary-500);
+ --checkbox-border-color: transparent;
+ --checkbox-border-color-focus: var(--primary-800);
+ --checkbox-border-color-hover: var(--primary-800);
+ --checkbox-border-color-selected: var(--primary-800);
+ --checkbox-border-width: var(--input-border-width);
+ --checkbox-label-background-fill: None;
+ --checkbox-label-background-fill-hover: None;
+ --checkbox-label-background-fill-selected: var(--checkbox-label-background-fill);
+ --checkbox-label-border-color: var(--border-color-primary);
+ --checkbox-label-border-color-hover: var(--checkbox-label-border-color);
+ --checkbox-label-border-width: var(--input-border-width);
+ --checkbox-label-text-color: var(--body-text-color);
+ --checkbox-label-text-color-selected: var(--checkbox-label-text-color);
+ --error-background-fill: var(--background-fill-primary);
+ --error-border-color: var(--border-color-primary);
+ --error-text-color: #ef4444;
+ --input-background-fill-focus: var(--secondary-600);
+ --input-background-fill-hover: var(--input-background-fill);
+ --input-border-color: var(--background-color);
+ --input-border-color-focus: var(--primary-800);
+ --input-placeholder-color: var(--neutral-500);
+ --input-shadow-focus: None;
+ --loader_color: None;
+ --slider_color: None;
+ --stat-background-fill: linear-gradient(to right, var(--primary-400), var(--primary-800));
+ --table-border-color: var(--neutral-700);
+ --table-even-background-fill: var(--primary-300);
+ --table-odd-background-fill: var(--primary-200);
+ --table-row-focus: var(--color-accent-soft);
+ --button-border-width: var(--input-border-width);
+ --button-cancel-background-fill: linear-gradient(to bottom right, #dc2626, #b91c1c);
+ --button-cancel-background-fill-hover: linear-gradient(to bottom right, #dc2626, #dc2626);
+ --button-cancel-border-color: #dc2626;
+ --button-cancel-border-color-hover: var(--button-cancel-border-color);
+ --button-cancel-text-color: white;
+ --button-cancel-text-color-hover: var(--button-cancel-text-color);
+ --button-primary-background-fill: var(--primary-500);
+ --button-primary-background-fill-hover: var(--primary-800);
+ --button-primary-border-color: var(--primary-500);
+ --button-primary-border-color-hover: var(--button-primary-border-color);
+ --button-primary-text-color: white;
+ --button-primary-text-color-hover: var(--button-primary-text-color);
+ --button-secondary-border-color: var(--neutral-600);
+ --button-secondary-border-color-hover: var(--button-secondary-border-color);
+ --button-secondary-text-color-hover: var(--button-secondary-text-color);
+ --secondary-50: #eff6ff;
+ --secondary-100: #dbeafe;
+ --secondary-200: #bfdbfe;
+ --secondary-300: #93c5fd;
+ --secondary-400: #60a5fa;
+ --secondary-500: #3b82f6;
+ --secondary-600: #2563eb;
+ --secondary-700: #1d4ed8;
+ --secondary-800: #1e40af;
+ --secondary-900: #1e3a8a;
+ --secondary-950: #1d3660;
+ --neutral-50: #f0f0f0;
+ --neutral-100: #e0e0e0;
+ --neutral-200: #d0d0d0;
+ --neutral-300: #b0b0b0;
+ --neutral-400: #909090;
+ --neutral-500: #707070;
+ --neutral-600: #606060;
+ --neutral-700: #404040;
+ --neutral-800: #333333;
+ --neutral-900: #111827;
+ --neutral-950: #0b0f19;
+ --spacing-xxs: 1px;
+ --spacing-xs: 2px;
+ --spacing-sm: 4px;
+ --spacing-lg: 8px;
+ --spacing-xl: 10px;
+ --radius-xxs: 0;
+ --radius-xs: 0;
+ --radius-md: 0;
+ --radius-xl: 0;
+ --radius-xxl: 0;
+ --text-xxs: 9px;
+ --text-xs: 10px;
+ --text-sm: 12px;
+ --text-md: 14px;
+ --text-lg: 16px;
+ --text-xl: 22px;
+ --text-xxl: 26px;
+ --body-text-size: var(--text-md);
+ --body-text-weight: 400;
+ --embed-radius: var(--radius-lg);
+ --color-accent: var(--primary-500);
+ --shadow-drop: 0;
+ --shadow-drop-lg: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
+ --shadow-inset: rgba(0,0,0,0.05) 0px 2px 4px 0px inset;
+ --block-border-width: 1px;
+ --block-info-text-size: var(--text-sm);
+ --block-info-text-weight: 400;
+ --block-label-border-width: 1px;
+ --block-label-margin: 0;
+ --block-label-padding: var(--spacing-sm) var(--spacing-lg);
+ --block-label-radius: calc(var(--radius-lg) - 1px) 0 calc(var(--radius-lg) - 1px) 0;
+ --block-label-right-radius: 0 calc(var(--radius-lg) - 1px) 0 calc(var(--radius-lg) - 1px);
+ --block-label-text-size: var(--text-sm);
+ --block-label-text-weight: 400;
+ --block-padding: var(--spacing-xl) calc(var(--spacing-xl) + 2px);
+ --block-radius: var(--radius-lg);
+ --block-shadow: var(--shadow-drop);
+ --block-title-background-fill: none;
+ --block-title-border-color: none;
+ --block-title-border-width: 0;
+ --block-title-padding: 0;
+ --block-title-radius: none;
+ --block-title-text-size: var(--text-md);
+ --block-title-text-weight: 400;
+ --container-radius: var(--radius-lg);
+ --form-gap-width: 1px;
+ --layout-gap: var(--spacing-xxl);
+ --panel-border-width: 0;
+ --section-header-text-size: var(--text-md);
+ --section-header-text-weight: 400;
+ --checkbox-border-radius: var(--radius-sm);
+ --checkbox-label-gap: 2px;
+ --checkbox-label-padding: var(--spacing-md);
+ --checkbox-label-shadow: var(--shadow-drop);
+ --checkbox-label-text-size: var(--text-md);
+ --checkbox-label-text-weight: 400;
+ --checkbox-check: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
+ --radio-circle: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
+ --checkbox-shadow: var(--input-shadow);
+ --error-border-width: 1px;
+ --input-border-width: 1px;
+ --input-radius: var(--radius-lg);
+ --input-text-size: var(--text-md);
+ --input-text-weight: 400;
+ --loader-color: var(--color-accent);
+ --prose-text-size: var(--text-md);
+ --prose-text-weight: 400;
+ --prose-header-text-weight: 600;
+ --slider-color: ;
+ --table-radius: var(--radius-lg);
+ --button-large-padding: 2px 10px;
+ --button-large-radius: var(--radius-lg);
+ --button-large-text-size: var(--text-lg);
+ --button-large-text-weight: 400;
+ --button-shadow: none;
+ --button-shadow-active: none;
+ --button-shadow-hover: none;
+ --button-small-padding: var(--spacing-sm) calc(2 * var(--spacing-sm));
+ --button-small-radius: var(--radius-lg);
+ --button-small-text-size: var(--text-md);
+ --button-small-text-weight: 400;
+ --button-transition: none;
+ --size-9: 64px;
+ --size-14: 64px;
+}
diff --git a/javascript/loader.js b/javascript/loader.js
index a280f3cf5..f32c7d294 100644
--- a/javascript/loader.js
+++ b/javascript/loader.js
@@ -34,9 +34,10 @@ async function createSplash() {
`;
document.body.insertAdjacentHTML('beforeend', splash);
await preloadImages();
- const imgElement = ``;
+ const imgElement = ``;
document.getElementById('splash').insertAdjacentHTML('afterbegin', imgElement);
}
+
async function removeSplash() {
const splash = document.getElementById('splash');
if (splash) splash.remove();
diff --git a/javascript/script.js b/javascript/script.js
index 6ad77aa26..d9e35376f 100644
--- a/javascript/script.js
+++ b/javascript/script.js
@@ -75,7 +75,7 @@ let executedOnLoaded = false;
document.addEventListener('DOMContentLoaded', () => {
const mutationObserver = new MutationObserver((m) => {
- if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) {
+ if (!executedOnLoaded && gradioApp().getElementById('txt2img_prompt')) {
executedOnLoaded = true;
executeCallbacks(uiLoadedCallbacks);
}
diff --git a/javascript/setHints.js b/javascript/setHints.js
index 7398c7a5e..1ce221723 100644
--- a/javascript/setHints.js
+++ b/javascript/setHints.js
@@ -51,7 +51,7 @@ async function setHints() {
if (localeData.data.length === 0) {
const res = await fetch('/file=html/locale_en.json');
const json = await res.json();
- localeData.data = Object.values(json).flat();
+ localeData.data = Object.values(json).flat().filter((e) => e.hint.length > 0);
for (const e of localeData.data) e.label = e.label.toLowerCase().trim();
}
const elements = [
diff --git a/javascript/style.css b/javascript/style.css
index 52554cca3..4c4950fe7 100644
--- a/javascript/style.css
+++ b/javascript/style.css
@@ -1,6 +1,7 @@
:root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;}
a { font-weight: bold; cursor: pointer; }
h2 { margin-top: 1em !important; font-size: 1.4em !important; }
+table { overflow-x: auto !important; overflow-y: auto !important; }
div.gradio-container{ max-width: unset !important; padding: 8px !important; }
div.tabitem { padding: 0 !important; }
div.form { border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em 1em; flex-grow: 1 !important; }
@@ -230,7 +231,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
.extra-networks .description { flex: 3; }
.extra-networks .tab-nav > button { margin-right: 0; height: 24px; padding: 2px 4px 2px 4px; }
.extra-networks-tab { padding: 0 !important; }
-.extra-network-subdirs { background: var(--input-background-fill); overflow-x: hidden; overflow-y: auto; min-width: max(20%, 120px); padding-top: 0.5em; }
+.extra-network-subdirs { background: var(--input-background-fill); overflow-x: hidden; overflow-y: auto; min-width: max(15%, 120px); padding-top: 0.5em; }
.extra-networks-page { display: flex }
.extra-networks .custom-button { width: 120px; width: 100%; background: none; justify-content: left; text-align: left; padding: 2px 8px 2px 16px; text-indent: -8px; box-shadow: none; line-break: auto; }
.extra-networks .custom-button:hover { background: var(--button-primary-background-fill) }
@@ -252,7 +253,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
#txt2img_description, #img2img_description { max-height: 63px; overflow-y: auto !important; }
#txt2img_description > label > textarea, #img2img_description > label > textarea { font-size: 0.9em }
-/* controlnet */
+/* controlnet
.controlnet_control_type .controlnet_control_type_filter_group .wrap:last-of-type { display: grid; grid-auto-flow: row; grid-template-columns: repeat(4, minmax(0, 1fr)); }
fieldset.controlnet_resize_mode_radio .wrap:last-of-type, fieldset.controlnet_control_mode_radio .wrap:last-of-type { flex-direction: column; }
div.controlnet_preprocessor_model { display: grid; grid-auto-flow: row; grid-template-columns: 1fr max-content; }
@@ -263,6 +264,7 @@ div.controlnet_image_controls { display: grid; grid-template-columns: repeat(4,
div.controlnet_image_controls .controlnet_invert_warning { grid-column: 1 / -1; }
div.controlnet_image_controls button { justify-self: center; }
div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; grid-auto-flow: row; }
+ */
/* specific elements */
#modelmerger_interp_description { margin-top: 1em; margin-bottom: 1em; }
@@ -281,20 +283,27 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri
.log-monitor { display: none; justify-content: unset !important; overflow: hidden; padding: 0; margin-top: auto; font-family: monospace; font-size: 0.85em; }
.log-monitor td, .log-monitor th { padding-left: 1em; }
+/* changelog */
+.md h2 { background-color: var(--background-fill-primary); padding: 0.5em; }
+.md ul { list-style-type: square !important; text-indent: 1em; margin-left: 4em; }
+.md li { list-style-position: outside !important; text-indent: 0; }
+.md p { margin-left: 2em; }
+
/* custom component */
.folder-selector textarea { height: 2em !important; padding: 6px !important; }
/* Workaround for Gradio dropdowns capturing clicks during and after fadeout */
.gradio-dropdown > label > div > div:first-child:not(.showOptions) ~ ul.options { pointer-events: none; }
-.splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 100; display: block; text-align: center; }
-.splash-img { margin: 10% auto 0 auto; width: 512px; background-repeat: no-repeat; height: 512px; animation: move 5s infinite alternate; }
+.splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 1000; display: block; text-align: center; }
+.splash-img { margin: 10% auto 0 auto; width: 512px; background-repeat: no-repeat; height: 512px; animation: color 10s infinite alternate; }
.loading { color: white; position: absolute; top: 20%; left: 50%; transform: translateX(-50%); }
.loader { width: 300px; height: 300px; border: var(--spacing-md) solid transparent; border-radius: 50%; border-top: var(--spacing-md) solid var(--primary-600); animation: spin 4s linear infinite; position: relative; }
.loader::before, .loader::after { content: ""; position: absolute; top: 6px; bottom: 6px; left: 6px; right: 6px; border-radius: 50%; border: var(--spacing-md) solid transparent; }
.loader::before { border-top-color: var(--primary-900); animation: 3s spin linear infinite; }
.loader::after { border-top-color: var(--primary-300); animation: spin 1.5s linear infinite; }
+
@keyframes move {
from { background-position-x: 0, -40px; }
to { background-position-x: 0, 40px; }
@@ -303,3 +312,8 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
+
+@keyframes color {
+ from { filter: hue-rotate(0deg) }
+ to { filter: hue-rotate(360deg) }
+}
diff --git a/modules/api/api.py b/modules/api/api.py
index 36a22e423..e750fccb4 100644
--- a/modules/api/api.py
+++ b/modules/api/api.py
@@ -504,9 +504,8 @@ class Api:
}
def get_extra_networks(self, page: Optional[str] = None, name: Optional[str] = None, filename: Optional[str] = None, title: Optional[str] = None, fullname: Optional[str] = None, hash: Optional[str] = None): # pylint: disable=redefined-builtin
- import modules.ui_extra_networks
res = []
- for pg in modules.ui_extra_networks.extra_pages:
+ for pg in shared.extra_networks:
if page is not None and pg.name != page.lower():
continue
for item in pg.items:
diff --git a/modules/devices.py b/modules/devices.py
index 8a7db4244..2808760b5 100644
--- a/modules/devices.py
+++ b/modules/devices.py
@@ -115,7 +115,7 @@ def torch_gc(force=False):
if oom > previous_oom:
previous_oom = oom
shared.log.warning(f'GPU out-of-memory error: {mem}')
- if used > 95:
+ if used > 90:
shared.log.info(f'GPU high memory utilization: {used}% {mem}')
force = True
if not force:
diff --git a/modules/dml/hijack/realesrgan_model.py b/modules/dml/hijack/realesrgan_model.py
index ad3b01cce..b55e14647 100644
--- a/modules/dml/hijack/realesrgan_model.py
+++ b/modules/dml/hijack/realesrgan_model.py
@@ -1,6 +1,6 @@
import math
import torch
-from realesrgan import RealESRGANer
+from modules.realesrgan_model_arch import RealESRGANer
# DML Solution: Some of contents of output tensor turn to 0 after Extended Slices. Move it to cpu.
diff --git a/modules/esrgan_model.py b/modules/esrgan_model.py
index 7ed5c33ec..a9682bceb 100644
--- a/modules/esrgan_model.py
+++ b/modules/esrgan_model.py
@@ -3,12 +3,12 @@ import os
import numpy as np
import torch
from PIL import Image
-from basicsr.utils.download_util import load_file_from_url
+from rich.progress import Progress, TextColumn, BarColumn, TaskProgressColumn, TimeRemainingColumn, TimeElapsedColumn
import modules.esrgan_model_arch as arch
from modules import modelloader, images, devices
from modules.upscaler import Upscaler, UpscalerData
-from modules.shared import opts
+from modules.shared import opts, log, console
@@ -152,6 +152,7 @@ class UpscalerESRGAN(Upscaler):
def load_model(self, path: str):
if "http" in path:
+ from modules.modelloader import load_file_from_url
filename = load_file_from_url(
url=self.model_url,
model_dir=self.model_download_path,
@@ -161,10 +162,11 @@ class UpscalerESRGAN(Upscaler):
else:
filename = path
if not os.path.exists(filename) or filename is None:
- print(f"Unable to load {self.model_path} from {filename}")
+ log.error(f"Model failed loading: type=ESRGAN model={filename}")
return None
state_dict = torch.load(filename, map_location='cpu' if devices.device_esrgan.type == 'mps' else None)
+ log.info(f"Model loaded: type=ESRGAN model={filename}")
if "params_ema" in state_dict:
state_dict = state_dict["params_ema"]
@@ -216,16 +218,20 @@ def esrgan_upscale(model, img):
newtiles = []
scale_factor = 1
- for y, h, row in grid.tiles:
- newrow = []
- for tiledata in row:
- x, w, tile = tiledata
-
- output = upscale_without_tiling(model, tile)
- scale_factor = output.width // tile.width
-
- newrow.append([x * scale_factor, w * scale_factor, output])
- newtiles.append([y * scale_factor, h * scale_factor, newrow])
+ with Progress(TextColumn('[cyan]{task.description}'), BarColumn(), TaskProgressColumn(), TimeRemainingColumn(), TimeElapsedColumn(), console=console) as progress:
+ total = 0
+ for _y, _h, row in grid.tiles:
+ total += len(row)
+ task = progress.add_task(description="Upscaling", total=total)
+ for y, h, row in grid.tiles:
+ newrow = []
+ for tiledata in row:
+ x, w, tile = tiledata
+ output = upscale_without_tiling(model, tile)
+ scale_factor = output.width // tile.width
+ newrow.append([x * scale_factor, w * scale_factor, output])
+ progress.update(task, advance=1, description="Upscaling")
+ newtiles.append([y * scale_factor, h * scale_factor, newrow])
newgrid = images.Grid(newtiles, grid.tile_w * scale_factor, grid.tile_h * scale_factor, grid.image_w * scale_factor, grid.image_h * scale_factor, grid.overlap * scale_factor)
output = images.combine_grid(newgrid)
diff --git a/modules/gfpgan_model.py b/modules/gfpgan_model.py
index 728df70bf..2988cd407 100644
--- a/modules/gfpgan_model.py
+++ b/modules/gfpgan_model.py
@@ -29,12 +29,13 @@ def gfpgann():
latest_file = max(models, key=os.path.getctime)
model_file = latest_file
else:
- print("Unable to load gfpgan model!")
+ shared.log.error(f"Model failed loading: type=GFPGAN model={model_file}")
return None
if hasattr(facexlib.detection.retinaface, 'device'):
facexlib.detection.retinaface.device = devices.device_gfpgan
model = gfpgan_constructor(model_path=model_file, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None, device=devices.device_gfpgan)
loaded_gfpgan_model = model
+ shared.log.info(f"Model loaded: type=GFPGAN model={model_file}")
return model
diff --git a/modules/images.py b/modules/images.py
index 39d9e7246..98b44c4d0 100644
--- a/modules/images.py
+++ b/modules/images.py
@@ -291,7 +291,8 @@ def sanitize_filename_part(text, replace_spaces=True):
class FilenameGenerator:
replacements = {
- 'batch_number': lambda self: NOTHING if self.index <= 1 else self.index,
+ 'batch_number': lambda self: self.batch_number,
+ 'iter_number': lambda self: self.iter_number,
'cfg': lambda self: self.p and self.p.cfg_scale,
'clip_skip': lambda self: self.p and self.p.clip_skip,
'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'),
@@ -320,12 +321,17 @@ class FilenameGenerator:
}
default_time_format = '%Y%m%d%H%M%S'
- def __init__(self, p, seed, prompt, image, index):
+ def __init__(self, p, seed, prompt, image, grid=False):
self.p = p
self.seed = seed
self.prompt = prompt
self.image = image
- self.index = index if self.p is None or self.p.batch_size == 1 else self.p.batch_index + 1
+ if not grid:
+ self.batch_number = NOTHING if self.p is None or getattr(self.p, 'batch_size', 1) == 1 else (self.p.batch_index + 1 if hasattr(self.p, 'batch_index') else NOTHING)
+ self.iter_number = NOTHING if self.p is None or getattr(self.p, 'n_iter', 1) == 1 else (self.p.iteration + 1 if hasattr(self.p, 'iteration') else NOTHING)
+ else:
+ self.batch_number = NOTHING
+ self.iter_number = NOTHING
def hasprompt(self, *args):
lower = self.prompt.lower()
@@ -449,14 +455,14 @@ def atomically_save_image():
image_format = 'JPEG'
if shared.opts.image_watermark_enabled:
image = set_watermark(image, shared.opts.image_watermark)
- shared.log.debug(f'Saving: image={fn} type={image_format} size={image.width}x{image.height}')
+ shared.log.debug(f'Saving: image="{fn}" type={image_format} size={image.width}x{image.height}')
# actual save
exifinfo = (exifinfo or "") if shared.opts.image_metadata else ""
if image_format == 'PNG':
pnginfo_data = PngImagePlugin.PngInfo()
for k, v in params.pnginfo.items():
pnginfo_data.add_text(k, str(v))
- image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, pnginfo=pnginfo_data if shared.opts.image_metadata else None)
+ image.save(fn, format=image_format, optimize=True, compress_level=9, pnginfo=pnginfo_data if shared.opts.image_metadata else None)
elif image_format == 'JPEG':
if image.mode == 'RGBA':
shared.log.warning('Saving RGBA image as JPEG: Alpha channel will be lost')
@@ -464,7 +470,7 @@ def atomically_save_image():
elif image.mode == 'I;16':
image = image.point(lambda p: p * 0.0038910505836576).convert("L")
exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo, encoding="unicode") } })
- image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, exif=exif_bytes)
+ image.save(fn, format=image_format, optimize=True, quality=shared.opts.jpeg_quality, exif=exif_bytes)
elif image_format == 'WEBP':
if image.mode == 'I;16':
image = image.point(lambda p: p * 0.0038910505836576).convert("RGB")
@@ -489,8 +495,14 @@ def atomically_save_image():
with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file:
file.write(exifinfo)
if shared.opts.save_log_fn != '' and len(exifinfo) > 0:
- entry = { 'filename': filename, 'time': datetime.datetime.now().isoformat(), 'info': exifinfo }
- shared.writefile(entry, os.path.join(paths.data_path, shared.opts.save_log_fn), mode='a+')
+ fn = os.path.join(paths.data_path, shared.opts.save_log_fn)
+ entries = shared.readfile(fn)
+ idx = len(list(entries))
+ if idx == 0:
+ entries = []
+ entry = { 'id': idx, 'filename': filename, 'time': datetime.datetime.now().isoformat(), 'info': exifinfo }
+ entries.append(entry)
+ shared.writefile(entries, fn, mode='w')
save_queue.task_done()
@@ -499,7 +511,7 @@ save_thread = threading.Thread(target=atomically_save_image, daemon=True)
save_thread.start()
-def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None, index=0):
+def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None):
"""Save an image.
Args:
image (`PIL.Image`):
@@ -537,7 +549,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i
return None, None
if path is None or len(path) == 0: # set default path to avoid errors when functions are triggered manually or via api and param is not set
path = shared.opts.outdir_save
- namegen = FilenameGenerator(p, seed, prompt, image, index)
+ namegen = FilenameGenerator(p, seed, prompt, image, grid=grid)
if save_to_dirs is None:
save_to_dirs = (grid and shared.opts.grid_save_to_dirs) or (not grid and shared.opts.save_to_dirs and not no_prompt)
if save_to_dirs:
diff --git a/modules/intel/ipex/__init__.py b/modules/intel/ipex/__init__.py
index bd2e8e142..03cee82c7 100644
--- a/modules/intel/ipex/__init__.py
+++ b/modules/intel/ipex/__init__.py
@@ -17,7 +17,11 @@ def ipex_init(): # pylint: disable=too-many-statements
torch.cuda.device = torch.xpu.device
torch.cuda.device_count = torch.xpu.device_count
torch.cuda.device_of = torch.xpu.device_of
- torch.cuda.getDeviceIdListForCard = torch.xpu.getDeviceIdListForCard
+ # getDeviceIdListForCard is renamed since https://github.com/intel/intel-extension-for-pytorch/commit/835b41fd5c8b6facf9efee8312f20699850ee592
+ if hasattr(torch.xpu, 'getDeviceIdListForCard'):
+ torch.cuda.getDeviceIdListForCard = torch.xpu.getDeviceIdListForCard
+ else:
+ torch.cuda.getDeviceIdListForCard = torch.xpu.get_device_id_list_per_card
torch.cuda.get_device_name = torch.xpu.get_device_name
torch.cuda.get_device_properties = torch.xpu.get_device_properties
torch.cuda.init = torch.xpu.init
diff --git a/modules/intel/ipex/attention.py b/modules/intel/ipex/attention.py
index fc4ab6e26..e38689f21 100644
--- a/modules/intel/ipex/attention.py
+++ b/modules/intel/ipex/attention.py
@@ -64,7 +64,13 @@ def torch_bmm(input, mat2, *, out=None):
original_scaled_dot_product_attention = torch.nn.functional.scaled_dot_product_attention
def scaled_dot_product_attention(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False):
#ARC GPUs can't allocate more than 4GB to a single block, Slice it:
- shape_one, batch_size_attention, query_tokens, shape_four = query.shape
+ if len(query.shape) == 3:
+ batch_size_attention, query_tokens, shape_four = query.shape
+ shape_one = 1
+ no_shape_one = True
+ else:
+ shape_one, batch_size_attention, query_tokens, shape_four = query.shape
+ no_shape_one = False
block_multiply = 3.6 if query.dtype == torch.float32 else 1.8
block_size = (shape_one * batch_size_attention * query_tokens * shape_four) / 1024 * block_multiply #MB
split_slice_size = batch_size_attention
@@ -101,21 +107,39 @@ def scaled_dot_product_attention(query, key, value, attn_mask=None, dropout_p=0.
for i2 in range(query_tokens // split_2_slice_size): # pylint: disable=invalid-name
start_idx_2 = i2 * split_2_slice_size
end_idx_2 = (i2 + 1) * split_2_slice_size
- hidden_states[:, start_idx:end_idx, start_idx_2:end_idx_2] = original_scaled_dot_product_attention(
- query[:, start_idx:end_idx, start_idx_2:end_idx_2],
- key[:, start_idx:end_idx, start_idx_2:end_idx_2],
- value[:, start_idx:end_idx, start_idx_2:end_idx_2],
- attn_mask=attn_mask[:, start_idx:end_idx, start_idx_2:end_idx_2] if attn_mask is not None else attn_mask,
+ if no_shape_one:
+ hidden_states[start_idx:end_idx, start_idx_2:end_idx_2] = original_scaled_dot_product_attention(
+ query[start_idx:end_idx, start_idx_2:end_idx_2],
+ key[start_idx:end_idx, start_idx_2:end_idx_2],
+ value[start_idx:end_idx, start_idx_2:end_idx_2],
+ attn_mask=attn_mask[start_idx:end_idx, start_idx_2:end_idx_2] if attn_mask is not None else attn_mask,
+ dropout_p=dropout_p, is_causal=is_causal
+ )
+ else:
+ hidden_states[:, start_idx:end_idx, start_idx_2:end_idx_2] = original_scaled_dot_product_attention(
+ query[:, start_idx:end_idx, start_idx_2:end_idx_2],
+ key[:, start_idx:end_idx, start_idx_2:end_idx_2],
+ value[:, start_idx:end_idx, start_idx_2:end_idx_2],
+ attn_mask=attn_mask[:, start_idx:end_idx, start_idx_2:end_idx_2] if attn_mask is not None else attn_mask,
+ dropout_p=dropout_p, is_causal=is_causal
+ )
+ else:
+ if no_shape_one:
+ hidden_states[start_idx:end_idx] = original_scaled_dot_product_attention(
+ query[start_idx:end_idx],
+ key[start_idx:end_idx],
+ value[start_idx:end_idx],
+ attn_mask=attn_mask[start_idx:end_idx] if attn_mask is not None else attn_mask,
+ dropout_p=dropout_p, is_causal=is_causal
+ )
+ else:
+ hidden_states[:, start_idx:end_idx] = original_scaled_dot_product_attention(
+ query[:, start_idx:end_idx],
+ key[:, start_idx:end_idx],
+ value[:, start_idx:end_idx],
+ attn_mask=attn_mask[:, start_idx:end_idx] if attn_mask is not None else attn_mask,
dropout_p=dropout_p, is_causal=is_causal
)
- else:
- hidden_states[:, start_idx:end_idx] = original_scaled_dot_product_attention(
- query[:, start_idx:end_idx],
- key[:, start_idx:end_idx],
- value[:, start_idx:end_idx],
- attn_mask=attn_mask[:, start_idx:end_idx] if attn_mask is not None else attn_mask,
- dropout_p=dropout_p, is_causal=is_causal
- )
else:
return original_scaled_dot_product_attention(
query, key, value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal
diff --git a/modules/intel/openvino/__init__.py b/modules/intel/openvino/__init__.py
index 460062a7b..698c162be 100644
--- a/modules/intel/openvino/__init__.py
+++ b/modules/intel/openvino/__init__.py
@@ -1,4 +1,5 @@
import os
+import sys
import torch
from openvino.frontend import FrontEndManager
from openvino.frontend.pytorch.fx_decoder import TorchFXPythonDecoder
@@ -13,6 +14,25 @@ from hashlib import sha256
import functools
from modules import shared, devices
+def BUILD_MAP_UNPACK(self, inst):
+ items = self.popn(inst.argval)
+ # ensure everything is a dict
+ items = [BuiltinVariable(dict).call_function(self, [x], {}) for x in items] # noqa: F821
+ result = dict()
+ for x in items:
+ assert isinstance(x, ConstDictVariable) # noqa: F821
+ result.update(x.items)
+ self.push(
+ ConstDictVariable( # noqa: F821
+ result,
+ dict,
+ mutable_local=MutableLocal(), # noqa: F821
+ **VariableTracker.propagate(items), # noqa: F821
+ )
+ )
+tmp_torch = sys.modules["torch"]
+tmp_torch.BUILD_MAP_UNPACK_WITH_CALL = BUILD_MAP_UNPACK
+
compiled_cache = {}
max_openvino_partitions = 0
partitioned_modules = {}
@@ -65,10 +85,6 @@ def get_device():
shared.log.warning("OpenVINO: No compatible GPU detected!")
os.environ.setdefault('OPENVINO_TORCH_BACKEND_DEVICE', device)
shared.log.debug(f"OpenVINO Device: {device}")
- if shared.opts.cuda_compile_errors and device not in core.available_devices:
- shared.log.error(f"OpenVINO: Specified device {device} is not in the list of OpenVINO Available Devices")
- assert device in core.available_devices, f"OpenVINO: Specified device {device} is not in the list of OpenVINO Available Devices"
-
return device
def cache_root_path():
diff --git a/modules/processing.py b/modules/processing.py
index 772bbafba..f325e6000 100644
--- a/modules/processing.py
+++ b/modules/processing.py
@@ -472,6 +472,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
"CFG scale": p.cfg_scale,
"Size": f"{p.width}x{p.height}",
"Batch": f'{p.n_iter}x{p.batch_size}' if p.n_iter > 1 or p.batch_size > 1 else None,
+ "Index": f'{p.iteration + 1}x{index + 1}' if (p.n_iter > 1 or p.batch_size > 1) and index >= 0 else None,
"Parser": shared.opts.prompt_attention,
"Model": None if (not shared.opts.add_model_name_to_info) or (not shared.sd_model.sd_checkpoint_info.model_name) else shared.sd_model.sd_checkpoint_info.model_name.replace(',', '').replace(':', ''),
"Model hash": getattr(p, 'sd_model_hash', None if (not shared.opts.add_model_hash_to_info) or (not shared.sd_model.sd_model_hash) else shared.sd_model.sd_model_hash),
@@ -798,7 +799,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
p.scripts.postprocess_batch_list(p, batch_params, batch_number=n)
x_samples_ddim = batch_params.images
- def infotext(index=0): # pylint: disable=function-redefined # noqa: F811
+ def infotext(index): # pylint: disable=function-redefined # noqa: F811
return create_infotext(p, p.prompts, p.seeds, p.subseeds, index=index, all_negative_prompts=p.negative_prompts)
for i, x_sample in enumerate(x_samples_ddim):
@@ -816,7 +817,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
p.restore_faces = False
info = infotext(i)
p.restore_faces = orig
- images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restoration")
+ images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restore")
p.ops.append('face')
x_sample = modules.face_restoration.restore_faces(x_sample)
image = Image.fromarray(x_sample)
@@ -831,7 +832,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
info = infotext(i)
p.color_corrections = orig
image_without_cc = apply_overlay(image, p.paste_to, i, p.overlay_images)
- images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correction")
+ images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correct")
p.ops.append('color')
image = apply_color_correction(p.color_corrections[i], image)
image = apply_overlay(image, p.paste_to, i, p.overlay_images)
@@ -840,7 +841,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
image.info["parameters"] = text
output_images.append(image)
if shared.opts.samples_save and not p.do_not_save_samples:
- images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p)
+ images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p) # main save image
if hasattr(p, 'mask_for_overlay') and p.mask_for_overlay and any([shared.opts.save_mask, shared.opts.save_mask_composite, shared.opts.return_mask, shared.opts.return_mask_composite]):
image_mask = p.mask_for_overlay.convert('RGB')
image_mask_composite = Image.composite(image.convert('RGBA').convert('RGBa'), Image.new('RGBa', image.size), images.resize_image(3, p.mask_for_overlay, image.width, image.height).convert('L')).convert('RGBA')
@@ -865,13 +866,13 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if images.check_grid_size(output_images):
grid = images.image_grid(output_images, p.batch_size)
if shared.opts.return_grid:
- text = infotext()
+ text = infotext(-1)
infotexts.insert(0, text)
grid.info["parameters"] = text
output_images.insert(0, grid)
index_of_first_image = 1
if shared.opts.grid_save:
- images.save_image(grid, p.outpath_grids, "grid", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=infotext(), short_filename=not shared.opts.grid_extended_filename, p=p, grid=True)
+ images.save_image(grid, p.outpath_grids, "", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=infotext(-1), short_filename=not shared.opts.grid_extended_filename, p=p, grid=True, suffix="-grid") # main save grid
if not p.disable_extra_networks and extra_network_data:
modules.extra_networks.deactivate(p, extra_network_data)
@@ -880,7 +881,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
p,
images_list=output_images,
seed=p.all_seeds[0],
- info=infotext(),
+ info=infotext(0),
comments="\n".join(comments),
subseed=p.all_subseeds[0],
index_of_first_image=index_of_first_image,
@@ -964,6 +965,9 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
if (self.hr_upscale_to_x == self.width and self.hr_upscale_to_y == self.height) or self.hr_upscaler is None or self.hr_upscaler == 'None':
self.is_hr_pass = False
return
+ if self.denoising_strength == 0:
+ self.is_hr_pass = False
+ return
self.is_hr_pass = True
if not shared.state.processing_has_refined_job_count:
if shared.state.job_count == -1:
@@ -987,7 +991,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
info = create_infotext(self, self.all_prompts, self.all_seeds, self.all_subseeds, [], iteration=self.iteration, position_in_batch=index)
self.extra_generation_params = orig1
self.restore_faces = orig2
- images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], shared.opts.samples_format, info=info, suffix="-before-hires", index=index+1)
+ images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], shared.opts.samples_format, info=info, suffix="-before-hires")
if shared.backend == shared.Backend.DIFFUSERS:
modules.sd_models.set_diffuser_pipe(self.sd_model, modules.sd_models.DiffusersTaskType.TEXT_2_IMAGE)
@@ -1140,7 +1144,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
self.init_img_width = img.width # pylint: disable=attribute-defined-outside-init
self.init_img_height = img.height # pylint: disable=attribute-defined-outside-init
if shared.opts.save_init_img:
- images.save_image(img, path=shared.opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False)
+ images.save_image(img, path=shared.opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False, suffix="-init-image")
image = images.flatten(img, shared.opts.img2img_background_color)
if crop_region is None and self.resize_mode != 4:
image = images.resize_image(self.resize_mode, image, self.width, self.height)
diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py
index 9cd74f162..56444d4ec 100644
--- a/modules/processing_diffusers.py
+++ b/modules/processing_diffusers.py
@@ -65,7 +65,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
def full_vae_decode(latents, model):
t0 = time.time()
- if shared.opts.diffusers_move_unet and not model.has_accelerate:
+ if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False):
shared.log.debug('Moving to CPU: model=UNet')
unet_device = model.unet.device
model.unet.to(devices.cpu)
@@ -80,7 +80,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
latents = latents.to(next(iter(model.vae.post_quant_conv.parameters())).dtype)
decoded = model.vae.decode(latents / model.vae.config.scaling_factor, return_dict=False)[0]
- if shared.opts.diffusers_move_unet and not model.has_accelerate:
+ if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False):
model.unet.to(unet_device)
t1 = time.time()
shared.log.debug(f'VAE decode: name={sd_vae.loaded_vae_file if sd_vae.loaded_vae_file is not None else "baked"} dtype={model.vae.dtype} upcast={upcast} images={latents.shape[0]} latents={latents.shape} time={round(t1-t0, 3)}s')
@@ -88,7 +88,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
def full_vae_encode(image, model):
shared.log.debug(f'VAE encode: name={sd_vae.loaded_vae_file if sd_vae.loaded_vae_file is not None else "baked"} dtype={model.vae.dtype} upcast={model.vae.config.get("force_upcast", None)}')
- if shared.opts.diffusers_move_unet and not model.has_accelerate:
+ if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False):
shared.log.debug('Moving to CPU: model=UNet')
unet_device = model.unet.device
model.unet.to(devices.cpu)
@@ -96,7 +96,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
if not shared.cmd_opts.lowvram and not shared.opts.diffusers_seq_cpu_offload:
model.vae.to(devices.device)
encoded = model.vae.encode(image.to(model.vae.device, model.vae.dtype))
- if shared.opts.diffusers_move_unet and not model.has_accelerate:
+ if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False):
model.unet.to(unet_device)
return encoded
@@ -315,10 +315,11 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength, "height": 8 * math.ceil(p.height / 8), "width": 8 * math.ceil(p.width / 8)}
if shared.state.interrupted or shared.state.skipped:
- unload_diffusers_lora()
+ if lora_state['active']:
+ unload_diffusers_lora()
return results
- if shared.opts.diffusers_move_base and not shared.sd_model.has_accelerate:
+ if shared.opts.diffusers_move_base and not getattr(shared.sd_model, 'has_accelerate', False):
shared.sd_model.to(devices.device)
is_img2img = bool(sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.IMAGE_2_IMAGE or sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.INPAINTING)
@@ -357,15 +358,16 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
output = shared.sd_model(**base_args) # pylint: disable=not-callable
except AssertionError as e:
shared.log.info(e)
+ except ValueError as e:
+ shared.state.interrupted = True
+ shared.log.error(e)
if hasattr(shared.sd_model, 'embedding_db') and len(shared.sd_model.embedding_db.embeddings_used) > 0:
p.extra_generation_params['Embeddings'] = ', '.join(shared.sd_model.embedding_db.embeddings_used)
- if lora_state['active']:
- p.extra_generation_params['LoRA method'] = shared.opts.diffusers_lora_loader
- unload_diffusers_lora()
-
if shared.state.interrupted or shared.state.skipped:
+ if lora_state['active']:
+ unload_diffusers_lora()
return results
# optional hires pass
@@ -407,11 +409,15 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
except AssertionError as e:
shared.log.info(e)
+ if lora_state['active']:
+ p.extra_generation_params['LoRA method'] = shared.opts.diffusers_lora_loader
+ unload_diffusers_lora()
+
# optional refiner pass or decode
if is_refiner_enabled:
if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_refiner and hasattr(shared.sd_model, 'vae'):
save_intermediate(latents=output.images, suffix="-before-refiner")
- if shared.opts.diffusers_move_base and not shared.sd_model.has_accelerate:
+ if shared.opts.diffusers_move_base and not getattr(shared.sd_model, 'has_accelerate', False):
shared.log.debug('Moving to CPU: model=base')
shared.sd_model.to(devices.cpu)
devices.torch_gc()
@@ -423,9 +429,11 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
sd_samplers.create_sampler(sampler.name, shared.sd_refiner) # TODO(Patrick): For wrapped pipelines this is currently a no-op
if shared.state.interrupted or shared.state.skipped:
+ if lora_state['active']:
+ unload_diffusers_lora()
return results
- if shared.opts.diffusers_move_refiner and not shared.sd_refiner.has_accelerate:
+ if shared.opts.diffusers_move_refiner and not getattr(shared.sd_refiner, 'has_accelerate', False):
shared.sd_refiner.to(devices.device)
refiner_is_sdxl = bool("StableDiffusionXL" in shared.sd_refiner.__class__.__name__)
p.ops.append('refine')
@@ -461,7 +469,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro
for refiner_image in refiner_images:
results.append(refiner_image)
- if shared.opts.diffusers_move_refiner and not shared.sd_refiner.has_accelerate:
+ if shared.opts.diffusers_move_refiner and not getattr(shared.sd_refiner, 'has_accelerate', False):
shared.log.debug('Moving to CPU: model=refiner')
shared.sd_refiner.to(devices.cpu)
devices.torch_gc()
diff --git a/modules/realesrgan_model.py b/modules/realesrgan_model.py
index 5f5b132e5..5a88f2330 100644
--- a/modules/realesrgan_model.py
+++ b/modules/realesrgan_model.py
@@ -1,12 +1,9 @@
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 opts, device
+from modules.shared import opts, device, log
from modules import modelloader
-import modules.errors as errors
class UpscalerRealESRGAN(Upscaler):
@@ -16,8 +13,7 @@ class UpscalerRealESRGAN(Upscaler):
super().__init__()
try:
from basicsr.archs.rrdbnet_arch import RRDBNet # pylint: disable=unused-import
- from realesrgan import RealESRGANer # pylint: disable=unused-import
- from realesrgan.archs.srvgg_arch import SRVGGNetCompact # pylint: disable=unused-import
+ from modules.realesrgan_model_arch import RealESRGANer, SRVGGNetCompact # pylint: disable=unused-import
self.enable = True
self.scalers = []
scalers = self.load_models(path)
@@ -28,11 +24,9 @@ class UpscalerRealESRGAN(Upscaler):
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)
-
+ self.scalers.append(scaler)
except Exception as e:
- errors.display(e, 'real-esrgan')
+ log.error(f"Error loading Real-ESRGAN: model={path} {e}")
self.enable = False
self.scalers = []
@@ -41,14 +35,13 @@ class UpscalerRealESRGAN(Upscaler):
return img
try:
- from realesrgan import RealESRGANer
+ from modules.realesrgan_model_arch import RealESRGANer
except Exception:
- print("Error importing Real-ESRGAN:", file=sys.stderr)
+ log.error("Error importing Real-ESRGAN:")
return img
info = self.load_model(selected_model)
- if not os.path.exists(info.local_data_path):
- print(f"Unable to load RealESRGAN model: {info.name}")
+ if info is None or not os.path.exists(info.local_data_path):
return img
upsampler = RealESRGANer(
@@ -70,13 +63,15 @@ class UpscalerRealESRGAN(Upscaler):
try:
info = next(iter([scaler for scaler in self.scalers if scaler.data_path == path]), None)
if info is None:
- print(f"Unable to find model info: {path}")
+ log.error(f"Model failed loading: type=R-ESRGAN model={info.name}")
return None
if info.local_data_path.startswith("http"):
+ from modules.modelloader import load_file_from_url
info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_download_path, progress=True)
+ log.info(f"Model loaded: type=R-ESRGAN model={info.name}")
return info
except Exception as e:
- errors.display(e, 'real-esrgan model list')
+ log.error(f"Model failed loading: type=R-ESRGAN model={info.name} {e}")
return None
def load_models(self, _):
@@ -86,7 +81,7 @@ class UpscalerRealESRGAN(Upscaler):
def get_realesrgan_models(scaler):
try:
from basicsr.archs.rrdbnet_arch import RRDBNet
- from realesrgan.archs.srvgg_arch import SRVGGNetCompact
+ from modules.realesrgan_model_arch import SRVGGNetCompact # pylint: disable=unused-import
models = [
UpscalerData(
name="R-ESRGAN General 4xV3",
@@ -132,6 +127,6 @@ def get_realesrgan_models(scaler):
),
]
return models
- except Exception:
- print("Error creating Real-ESRGAN models list", file=sys.stderr)
+ except Exception as e:
+ log.error(f'Error creating Real-ESRGAN models list: {e}')
return []
diff --git a/modules/realesrgan_model_arch.py b/modules/realesrgan_model_arch.py
new file mode 100644
index 000000000..50f085255
--- /dev/null
+++ b/modules/realesrgan_model_arch.py
@@ -0,0 +1,383 @@
+import os
+import math
+import queue
+import threading
+import cv2
+import numpy as np
+import torch
+from torch.nn import functional as F
+from rich.progress import Progress, TextColumn, BarColumn, TaskProgressColumn, TimeRemainingColumn, TimeElapsedColumn
+from modules.shared import log, console
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+class RealESRGANer():
+ """A helper class for upsampling images with RealESRGAN.
+
+ Args:
+ scale (int): Upsampling scale factor used in the networks. It is usually 2 or 4.
+ model_path (str): The path to the pretrained model. It can be urls (will first download it automatically).
+ model (nn.Module): The defined network. Default: None.
+ tile (int): As too large images result in the out of GPU memory issue, so this tile option will first crop
+ input images into tiles, and then process each of them. Finally, they will be merged into one image.
+ 0 denotes for do not use tile. Default: 0.
+ tile_pad (int): The pad size for each tile, to remove border artifacts. Default: 10.
+ pre_pad (int): Pad the input images to avoid border artifacts. Default: 10.
+ half (float): Whether to use half precision during inference. Default: False.
+ """
+
+ def __init__(self,
+ scale,
+ model_path,
+ dni_weight=None,
+ model=None,
+ tile=0,
+ tile_pad=10,
+ pre_pad=10,
+ half=False,
+ device=None,
+ gpu_id=None):
+ self.scale = scale
+ self.tile_size = tile
+ self.tile_pad = tile_pad
+ self.pre_pad = pre_pad
+ self.mod_scale = None
+ self.half = half
+
+ # initialize model
+ if gpu_id:
+ self.device = torch.device(
+ f'cuda:{gpu_id}' if torch.cuda.is_available() else 'cpu') if device is None else device
+ else:
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') if device is None else device
+
+ if isinstance(model_path, list):
+ # dni
+ assert len(model_path) == len(dni_weight), 'model_path and dni_weight should have the save length.'
+ loadnet = self.dni(model_path[0], model_path[1], dni_weight)
+ else:
+ # if the model_path starts with https, it will first download models to the folder: weights
+ if model_path.startswith('https://'):
+ from modules.modelloader import load_file_from_url
+ model_path = load_file_from_url(url=model_path, model_dir=os.path.join(ROOT_DIR, 'weights'), progress=True, file_name=None)
+ loadnet = torch.load(model_path, map_location=torch.device('cpu'))
+
+ # prefer to use params_ema
+ if 'params_ema' in loadnet:
+ keyname = 'params_ema'
+ else:
+ keyname = 'params'
+ model.load_state_dict(loadnet[keyname], strict=True)
+
+ model.eval()
+ self.model = model.to(self.device)
+ if self.half:
+ self.model = self.model.half()
+
+ def dni(self, net_a, net_b, dni_weight, key='params', loc='cpu'):
+ """Deep network interpolation.
+
+ ``Paper: Deep Network Interpolation for Continuous Imagery Effect Transition``
+ """
+ net_a = torch.load(net_a, map_location=torch.device(loc))
+ net_b = torch.load(net_b, map_location=torch.device(loc))
+ for k, v_a in net_a[key].items():
+ net_a[key][k] = dni_weight[0] * v_a + dni_weight[1] * net_b[key][k]
+ return net_a
+
+ def pre_process(self, img):
+ """Pre-process, such as pre-pad and mod pad, so that the images can be divisible
+ """
+ img = torch.from_numpy(np.transpose(img, (2, 0, 1))).float()
+ self.img = img.unsqueeze(0).to(self.device)
+ if self.half:
+ self.img = self.img.half()
+
+ # pre_pad
+ if self.pre_pad != 0:
+ self.img = F.pad(self.img, (0, self.pre_pad, 0, self.pre_pad), 'reflect')
+ # mod pad for divisible borders
+ if self.scale == 2:
+ self.mod_scale = 2
+ elif self.scale == 1:
+ self.mod_scale = 4
+ if self.mod_scale is not None:
+ self.mod_pad_h, self.mod_pad_w = 0, 0
+ _, _, h, w = self.img.size()
+ if (h % self.mod_scale != 0):
+ self.mod_pad_h = (self.mod_scale - h % self.mod_scale)
+ if (w % self.mod_scale != 0):
+ self.mod_pad_w = (self.mod_scale - w % self.mod_scale)
+ self.img = F.pad(self.img, (0, self.mod_pad_w, 0, self.mod_pad_h), 'reflect')
+
+ def process(self):
+ # model inference
+ self.output = self.model(self.img)
+
+ def tile_process(self):
+ """It will first crop input images to tiles, and then process each tile.
+ Finally, all the processed tiles are merged into one images.
+
+ Modified from: https://github.com/ata4/esrgan-launcher
+ """
+ batch, channel, height, width = self.img.shape
+ output_height = height * self.scale
+ output_width = width * self.scale
+ output_shape = (batch, channel, output_height, output_width)
+
+ # start with black image
+ self.output = self.img.new_zeros(output_shape)
+ tiles_x = math.ceil(width / self.tile_size)
+ tiles_y = math.ceil(height / self.tile_size)
+
+ # loop over all tiles
+ with Progress(TextColumn('[cyan]{task.description}'), BarColumn(), TaskProgressColumn(), TimeRemainingColumn(), TimeElapsedColumn(), console=console) as progress:
+ task = progress.add_task(description="Upscaling", total=tiles_y * tiles_x)
+ with torch.no_grad():
+ for y in range(tiles_y):
+ for x in range(tiles_x):
+ # extract tile from input image
+ ofs_x = x * self.tile_size
+ ofs_y = y * self.tile_size
+ # input tile area on total image
+ input_start_x = ofs_x
+ input_end_x = min(ofs_x + self.tile_size, width)
+ input_start_y = ofs_y
+ input_end_y = min(ofs_y + self.tile_size, height)
+
+ # input tile area on total image with padding
+ input_start_x_pad = max(input_start_x - self.tile_pad, 0)
+ input_end_x_pad = min(input_end_x + self.tile_pad, width)
+ input_start_y_pad = max(input_start_y - self.tile_pad, 0)
+ input_end_y_pad = min(input_end_y + self.tile_pad, height)
+
+ # input tile dimensions
+ input_tile_width = input_end_x - input_start_x
+ input_tile_height = input_end_y - input_start_y
+ tile_idx = y * tiles_x + x + 1 # noqa
+ input_tile = self.img[:, :, input_start_y_pad:input_end_y_pad, input_start_x_pad:input_end_x_pad]
+
+ # upscale tile
+ try:
+ output_tile = self.model(input_tile)
+ except Exception as e:
+ log.error(f'Upscale error: type=R-ESRGAN {e}')
+
+ # output tile area on total image
+ output_start_x = input_start_x * self.scale
+ output_end_x = input_end_x * self.scale
+ output_start_y = input_start_y * self.scale
+ output_end_y = input_end_y * self.scale
+
+ # output tile area without padding
+ output_start_x_tile = (input_start_x - input_start_x_pad) * self.scale
+ output_end_x_tile = output_start_x_tile + input_tile_width * self.scale
+ output_start_y_tile = (input_start_y - input_start_y_pad) * self.scale
+ output_end_y_tile = output_start_y_tile + input_tile_height * self.scale
+
+ # put tile into output image
+ self.output[:, :, output_start_y:output_end_y,
+ output_start_x:output_end_x] = output_tile[:, :, output_start_y_tile:output_end_y_tile,
+ output_start_x_tile:output_end_x_tile]
+ progress.update(task, advance=1, description="Upscaling")
+
+ def post_process(self):
+ # remove extra pad
+ if self.mod_scale is not None:
+ _, _, h, w = self.output.size()
+ self.output = self.output[:, :, 0:h - self.mod_pad_h * self.scale, 0:w - self.mod_pad_w * self.scale]
+ # remove prepad
+ if self.pre_pad != 0:
+ _, _, h, w = self.output.size()
+ self.output = self.output[:, :, 0:h - self.pre_pad * self.scale, 0:w - self.pre_pad * self.scale]
+ return self.output
+
+ @torch.no_grad()
+ def enhance(self, img, outscale=None, alpha_upsampler='realesrgan'):
+ h_input, w_input = img.shape[0:2]
+ # img: numpy
+ img = img.astype(np.float32)
+ if np.max(img) > 256: # 16-bit image
+ max_range = 65535
+ else:
+ max_range = 255
+ img = img / max_range
+ if len(img.shape) == 2: # gray image
+ img_mode = 'L'
+ img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+ elif img.shape[2] == 4: # RGBA image with alpha channel
+ img_mode = 'RGBA'
+ alpha = img[:, :, 3]
+ img = img[:, :, 0:3]
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
+ if alpha_upsampler == 'realesrgan':
+ alpha = cv2.cvtColor(alpha, cv2.COLOR_GRAY2RGB)
+ else:
+ img_mode = 'RGB'
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
+
+ # ------------------- process image (without the alpha channel) ------------------- #
+ self.pre_process(img)
+ if self.tile_size > 0:
+ self.tile_process()
+ else:
+ self.process()
+ output_img = self.post_process()
+ output_img = output_img.data.squeeze().float().cpu().clamp_(0, 1).numpy()
+ output_img = np.transpose(output_img[[2, 1, 0], :, :], (1, 2, 0))
+ if img_mode == 'L':
+ output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2GRAY)
+
+ # ------------------- process the alpha channel if necessary ------------------- #
+ if img_mode == 'RGBA':
+ if alpha_upsampler == 'realesrgan':
+ self.pre_process(alpha)
+ if self.tile_size > 0:
+ self.tile_process()
+ else:
+ self.process()
+ output_alpha = self.post_process()
+ output_alpha = output_alpha.data.squeeze().float().cpu().clamp_(0, 1).numpy()
+ output_alpha = np.transpose(output_alpha[[2, 1, 0], :, :], (1, 2, 0))
+ output_alpha = cv2.cvtColor(output_alpha, cv2.COLOR_BGR2GRAY)
+ else: # use the cv2 resize for alpha channel
+ h, w = alpha.shape[0:2]
+ output_alpha = cv2.resize(alpha, (w * self.scale, h * self.scale), interpolation=cv2.INTER_LINEAR)
+
+ # merge the alpha channel
+ output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2BGRA)
+ output_img[:, :, 3] = output_alpha
+
+ # ------------------------------ return ------------------------------ #
+ if max_range == 65535: # 16-bit image
+ output = (output_img * 65535.0).round().astype(np.uint16)
+ else:
+ output = (output_img * 255.0).round().astype(np.uint8)
+
+ if outscale is not None and outscale != float(self.scale):
+ output = cv2.resize(
+ output, (
+ int(w_input * outscale),
+ int(h_input * outscale),
+ ), interpolation=cv2.INTER_LANCZOS4)
+
+ return output, img_mode
+
+
+class PrefetchReader(threading.Thread):
+ """Prefetch images.
+
+ Args:
+ img_list (list[str]): A image list of image paths to be read.
+ num_prefetch_queue (int): Number of prefetch queue.
+ """
+
+ def __init__(self, img_list, num_prefetch_queue):
+ super().__init__()
+ self.que = queue.Queue(num_prefetch_queue)
+ self.img_list = img_list
+
+ def run(self):
+ for img_path in self.img_list:
+ img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
+ self.que.put(img)
+
+ self.que.put(None)
+
+ def __next__(self):
+ next_item = self.que.get()
+ if next_item is None:
+ raise StopIteration
+ return next_item
+
+ def __iter__(self):
+ return self
+
+
+class IOConsumer(threading.Thread):
+
+ def __init__(self, opt, que, qid):
+ super().__init__()
+ self._queue = que
+ self.qid = qid
+ self.opt = opt
+
+ def run(self):
+ while True:
+ msg = self._queue.get()
+ if isinstance(msg, str) and msg == 'quit':
+ break
+
+ output = msg['output']
+ save_path = msg['save_path']
+ cv2.imwrite(save_path, output)
+
+from basicsr.utils.registry import ARCH_REGISTRY
+from torch import nn as nn
+from torch.nn import functional as F # noqa
+
+
+class SRVGGNetCompact(nn.Module):
+ """A compact VGG-style network structure for super-resolution.
+
+ It is a compact network structure, which performs upsampling in the last layer and no convolution is
+ conducted on the HR feature space.
+
+ Args:
+ num_in_ch (int): Channel number of inputs. Default: 3.
+ num_out_ch (int): Channel number of outputs. Default: 3.
+ num_feat (int): Channel number of intermediate features. Default: 64.
+ num_conv (int): Number of convolution layers in the body network. Default: 16.
+ upscale (int): Upsampling factor. Default: 4.
+ act_type (str): Activation type, options: 'relu', 'prelu', 'leakyrelu'. Default: prelu.
+ """
+
+ def __init__(self, num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=4, act_type='prelu'):
+ super(SRVGGNetCompact, self).__init__()
+ self.num_in_ch = num_in_ch
+ self.num_out_ch = num_out_ch
+ self.num_feat = num_feat
+ self.num_conv = num_conv
+ self.upscale = upscale
+ self.act_type = act_type
+
+ self.body = nn.ModuleList()
+ # the first conv
+ self.body.append(nn.Conv2d(num_in_ch, num_feat, 3, 1, 1))
+ # the first activation
+ if act_type == 'relu':
+ activation = nn.ReLU(inplace=True)
+ elif act_type == 'prelu':
+ activation = nn.PReLU(num_parameters=num_feat)
+ elif act_type == 'leakyrelu':
+ activation = nn.LeakyReLU(negative_slope=0.1, inplace=True)
+ self.body.append(activation)
+
+ # the body structure
+ for _ in range(num_conv):
+ self.body.append(nn.Conv2d(num_feat, num_feat, 3, 1, 1))
+ # activation
+ if act_type == 'relu':
+ activation = nn.ReLU(inplace=True)
+ elif act_type == 'prelu':
+ activation = nn.PReLU(num_parameters=num_feat)
+ elif act_type == 'leakyrelu':
+ activation = nn.LeakyReLU(negative_slope=0.1, inplace=True)
+ self.body.append(activation)
+
+ # the last conv
+ self.body.append(nn.Conv2d(num_feat, num_out_ch * upscale * upscale, 3, 1, 1))
+ # upsample
+ self.upsampler = nn.PixelShuffle(upscale)
+
+ def forward(self, x):
+ out = x
+ for i in range(0, len(self.body)):
+ out = self.body[i](out)
+
+ out = self.upsampler(out)
+ # add the nearest upsampled image, so that the network learns the residual
+ base = F.interpolate(x, scale_factor=self.upscale, mode='nearest')
+ out += base
+ return out
diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py
index 5cd407e19..3f465a600 100644
--- a/modules/sd_hijack.py
+++ b/modules/sd_hijack.py
@@ -37,45 +37,38 @@ def apply_optimizations():
can_use_sdp = hasattr(torch.nn.functional, "scaled_dot_product_attention") and callable(torch.nn.functional.scaled_dot_product_attention)
if devices.device == torch.device("cpu"):
if opts.cross_attention_optimization == "Scaled-Dot-Product":
- shared.log.warning("Scaled dot product cross attention is not available on CPU")
+ shared.log.warning("Cross-attention: Scaled dot product is not available on CPU")
can_use_sdp = False
if opts.cross_attention_optimization == "xFormers":
- shared.log.warning("xFormers cross attention is not available on CPU")
+ shared.log.warning("Cross-attention: xFormers is not available on CPU")
shared.xformers_available = False
- if opts.cross_attention_optimization == "Disable cross-attention layer optimization":
- shared.log.warning("Cross-attention optimization disabled")
+ shared.log.info(f"Cross-attention: optimization={opts.cross_attention_optimization} options={opts.cross_attention_options}")
+ if opts.cross_attention_optimization == "Disabled":
optimization_method = 'none'
if can_use_sdp and opts.cross_attention_optimization == "Scaled-Dot-Product" and 'SDP disable memory attention' in opts.cross_attention_options:
- shared.log.info("Applying scaled dot product cross attention optimization (without memory efficient attention)")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_no_mem_attention_forward
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_no_mem_attnblock_forward
optimization_method = 'sdp-no-mem'
elif can_use_sdp and opts.cross_attention_optimization == "Scaled-Dot-Product":
- shared.log.info("Applying scaled dot product cross attention optimization")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_attention_forward
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_attnblock_forward
optimization_method = 'sdp'
if shared.xformers_available and opts.cross_attention_optimization == "xFormers":
- shared.log.info("Applying xformers cross attention optimization")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.xformers_attention_forward
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.xformers_attnblock_forward
optimization_method = 'xformers'
if opts.cross_attention_optimization == "Sub-quadratic":
- shared.log.info("Applying sub-quadratic cross attention optimization")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.sub_quad_attention_forward
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sub_quad_attnblock_forward
optimization_method = 'sub-quadratic'
if opts.cross_attention_optimization == "Split attention":
- shared.log.info("Applying split attention optimization")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_v1
optimization_method = 'v1'
if opts.cross_attention_optimization == "InvokeAI's":
- shared.log.info("Applying InvokeAI cross attention optimization")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_invokeAI
optimization_method = 'invokeai'
if opts.cross_attention_optimization == "Doggettx's":
- shared.log.info("Applying Doggettx cross attention optimization")
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.cross_attention_attnblock_forward
optimization_method = 'doggettx'
diff --git a/modules/sd_models.py b/modules/sd_models.py
index 5278015e6..4ace52368 100644
--- a/modules/sd_models.py
+++ b/modules/sd_models.py
@@ -261,7 +261,7 @@ def select_checkpoint(op='model'):
return None
checkpoint_info = get_closet_checkpoint_match(model_checkpoint)
if checkpoint_info is not None:
- shared.log.debug(f'Select checkpoint: {op} {checkpoint_info.title if checkpoint_info is not None else None}')
+ shared.log.debug(f'Select checkpoint: {op}="{checkpoint_info.title if checkpoint_info is not None else None}"')
return checkpoint_info
if len(checkpoints_list) == 0 and not shared.cmd_opts.no_download:
shared.log.error("Cannot run without a checkpoint")
@@ -275,7 +275,7 @@ def select_checkpoint(op='model'):
shared.log.info("Selecting first available checkpoint")
# shared.log.warning(f"Loading fallback checkpoint: {checkpoint_info.title}")
shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title
- shared.log.debug(f'Select checkpoint: {checkpoint_info.title if checkpoint_info is not None else None}')
+ shared.log.debug(f'Select checkpoint: {op}="{checkpoint_info.title if checkpoint_info is not None else None}"')
return checkpoint_info
@@ -896,7 +896,7 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
base_sent_to_cpu=False
if (shared.opts.cuda_compile and shared.opts.cuda_compile_backend != 'none') or shared.opts.ipex_optimize:
- if op == 'refiner' and not sd_model.has_accelerate:
+ if op == 'refiner' and not getattr(sd_model, 'has_accelerate', False):
gpu_vram = memory_stats().get('gpu', {})
free_vram = gpu_vram.get('total', 0) - gpu_vram.get('used', 0)
refiner_enough_vram = free_vram >= 7 if "StableDiffusionXL" in sd_model.__class__.__name__ else 3
@@ -917,7 +917,7 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
devices.torch_gc(force=True)
sd_model.to(devices.device)
base_sent_to_cpu=True
- elif not sd_model.has_accelerate:
+ elif not getattr(sd_model, 'has_accelerate', False):
sd_model.to(devices.device)
compile_diffusers(sd_model)
@@ -931,10 +931,10 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
shared.opts.data["sd_checkpoint_hash"] = checkpoint_info.sha256
if hasattr(sd_model, "set_progress_bar_config"):
sd_model.set_progress_bar_config(bar_format='Progress {rate_fmt}{postfix} {bar} {percentage:3.0f}% {n_fmt}/{total_fmt} {elapsed} {remaining}', ncols=80, colour='#327fba')
- if op == 'refiner' and shared.opts.diffusers_move_refiner and not sd_model.has_accelerate:
+ if op == 'refiner' and shared.opts.diffusers_move_refiner and not getattr(sd_model, 'has_accelerate', False):
shared.log.debug('Moving refiner model to CPU')
sd_model.to(devices.cpu)
- elif not sd_model.has_accelerate: # In offload modes, accelerate will move models around
+ elif not getattr(sd_model, 'has_accelerate', False): # In offload modes, accelerate will move models around
sd_model.to(devices.device)
if op == 'refiner' and base_sent_to_cpu:
shared.log.debug('Moving base model back to GPU')
@@ -965,10 +965,10 @@ class DiffusersTaskType(Enum):
INPAINTING = 3
def set_diffuser_pipe(pipe, new_pipe_type):
- sd_checkpoint_info = pipe.sd_checkpoint_info if hasattr(pipe, "sd_checkpoint_info") else None
- sd_model_checkpoint = pipe.sd_model_checkpoint if hasattr(pipe, "sd_model_checkpoint") else None
- sd_model_hash = pipe.sd_model_hash if hasattr(pipe, "sd_model_hash") else None
- has_accelerate = pipe.has_accelerate if hasattr(pipe, "has_accelerate") else None
+ sd_checkpoint_info = getattr(pipe, "sd_checkpoint_info", None)
+ sd_model_checkpoint = getattr(pipe, "sd_model_checkpoint", None)
+ sd_model_hash = getattr(pipe, "sd_model_hash", None)
+ has_accelerate = getattr(pipe, "has_accelerate", None)
if new_pipe_type == DiffusersTaskType.TEXT_2_IMAGE:
new_pipe = diffusers.AutoPipelineForText2Image.from_pipe(pipe)
@@ -1067,6 +1067,7 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None,
shared.log.info(f'LDM: {line.strip()}')
shared.log.debug(f"Model created from config: {checkpoint_config}")
sd_model.used_config = checkpoint_config
+ sd_model.has_accelerate = False
timer.record("create")
ok = load_model_weights(sd_model, checkpoint_info, state_dict, timer)
if not ok:
@@ -1090,7 +1091,6 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None,
sd_hijack.model_hijack.hijack(sd_model)
timer.record("hijack")
sd_model.eval()
- sd_model.has_accelerate = False
if op == 'refiner':
model_data.sd_refiner = sd_model
else:
@@ -1126,12 +1126,12 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model')
current_checkpoint_info = getattr(sd_model, 'sd_checkpoint_info', None)
if current_checkpoint_info is not None and checkpoint_info is not None and current_checkpoint_info.filename == checkpoint_info.filename:
return
- if not sd_model.has_accelerate:
+ if not getattr(sd_model, 'has_accelerate', False):
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
lowvram.send_everything_to_cpu()
else:
sd_model.to(devices.cpu)
- if (reuse_dict or shared.opts.model_reuse_dict) and not sd_model.has_accelerate:
+ if (reuse_dict or shared.opts.model_reuse_dict) and not getattr(sd_model, 'has_accelerate', False):
shared.log.info('Reusing previous model dictionary')
sd_hijack.model_hijack.undo_hijack(sd_model)
else:
@@ -1164,7 +1164,7 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model')
timer.record("hijack")
script_callbacks.model_loaded_callback(sd_model)
timer.record("callbacks")
- if sd_model is not None and not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not sd_model.has_accelerate:
+ if sd_model is not None and not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not getattr(sd_model, 'has_accelerate', False):
sd_model.to(devices.device)
timer.record("device")
shared.log.info(f"Weights loaded in {timer.summary()}")
@@ -1172,7 +1172,7 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model')
def disable_offload(sd_model):
from accelerate.hooks import remove_hook_from_module
- if not sd_model.has_accelerate:
+ if not getattr(sd_model, 'has_accelerate', False):
return
for _name, model in sd_model.components.items():
if not isinstance(model, torch.nn.Module):
diff --git a/modules/sd_samplers_common.py b/modules/sd_samplers_common.py
index 4c2b3655a..b2e2daf36 100644
--- a/modules/sd_samplers_common.py
+++ b/modules/sd_samplers_common.py
@@ -55,6 +55,29 @@ def samples_to_image_grid(samples, approximation=None):
return images.image_grid([single_sample_to_image(sample, approximation) for sample in samples])
+def images_tensor_to_samples(image, approximation=None, model=None):
+ '''image[0, 1] -> latent'''
+ if approximation is None:
+ approximation = approximation_indexes.get(shared.opts.show_progress_type, 0)
+ if approximation == 3:
+ image = image.to(devices.device, devices.dtype)
+ x_latent = sd_vae_taesd.encode(image)
+ else:
+ if model is None:
+ model = shared.sd_model
+ model.first_stage_model.to(devices.dtype_vae)
+ image = image.to(shared.device, dtype=devices.dtype_vae)
+ image = image * 2 - 1
+ if len(image) > 1:
+ x_latent = torch.stack([
+ model.get_first_stage_encoding(model.encode_first_stage(torch.unsqueeze(img, 0)))[0]
+ for img in image
+ ])
+ else:
+ x_latent = model.get_first_stage_encoding(model.encode_first_stage(image))
+ return x_latent
+
+
def store_latent(decoded):
shared.state.current_latent = decoded
if shared.opts.live_previews_enable and shared.opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % shared.opts.show_progress_every_n_steps == 0:
diff --git a/modules/sd_vae.py b/modules/sd_vae.py
index 0360d83b3..173f54337 100644
--- a/modules/sd_vae.py
+++ b/modules/sd_vae.py
@@ -243,7 +243,7 @@ def reload_vae_weights(sd_model=None, vae_file=unspecified):
vae_source = "function-argument"
if loaded_vae_file == vae_file:
return
- if not sd_model.has_accelerate:
+ if not getattr(sd_model, 'has_accelerate', False):
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
lowvram.send_everything_to_cpu()
else:
@@ -265,6 +265,6 @@ def reload_vae_weights(sd_model=None, vae_file=unspecified):
if vae is not None:
sd_model.vae = vae
- if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not sd_model.has_accelerate:
+ if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not getattr(sd_model, 'has_accelerate', False):
sd_model.to(devices.device)
return sd_model
diff --git a/modules/shared.py b/modules/shared.py
index 713074f45..d69f0ff3b 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -11,7 +11,7 @@ from enum import Enum
import gradio as gr
import fasteners
from rich.console import Console
-from modules import errors, ui_components, shared_items, cmd_args
+from modules import errors, shared_items, cmd_args, ui_components
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
from modules.dml import memory_providers, default_memory_provider, directml_do_hijack
import modules.interrogate
@@ -37,6 +37,7 @@ interrogator = modules.interrogate.InterrogateModels("interrogate")
sd_upscalers = []
face_restorers = []
tab_names = []
+extra_networks = []
options_templates = {}
hypernetworks = {}
loaded_hypernetworks = []
@@ -71,21 +72,12 @@ restricted_opts = {
compatibility_opts = ['clip_skip', 'uni_pc_lower_order_final', 'uni_pc_order']
console = Console(log_time=True, log_time_format='%H:%M:%S-%f')
-def is_url(string):
- parsed_url = urlparse(string)
- return all([parsed_url.scheme, parsed_url.netloc])
-
class Backend(Enum):
ORIGINAL = 1
DIFFUSERS = 2
-def reload_hypernetworks():
- from modules.hypernetworks import hypernetwork
- global hypernetworks # pylint: disable=W0603
- hypernetworks = hypernetwork.list_hypernetworks(opts.hypernetwork_dir)
-
class State:
skipped = False
@@ -192,6 +184,7 @@ class State:
self.current_image = image
self.id_live_preview += 1
+
state = State()
state.server_start = time.time()
if not hasattr(cmd_opts, "use_openvino"):
@@ -251,6 +244,17 @@ def list_checkpoint_tiles():
default_checkpoint = list_checkpoint_tiles()[0] if len(list_checkpoint_tiles()) > 0 else "model.ckpt"
+def is_url(string):
+ parsed_url = urlparse(string)
+ return all([parsed_url.scheme, parsed_url.netloc])
+
+
+def reload_hypernetworks():
+ from modules.hypernetworks import hypernetwork
+ global hypernetworks # pylint: disable=W0603
+ hypernetworks = hypernetwork.list_hypernetworks(opts.hypernetwork_dir)
+
+
def refresh_checkpoints():
import modules.sd_models # pylint: disable=W0621
return modules.sd_models.list_models()
@@ -336,7 +340,7 @@ def readfile(filename, silent=False):
return data
-def writefile(data, filename, mode='w'):
+def writefile(data, filename, mode='w', silent=False):
def default(obj):
log.error(f"Saving: {filename} not a valid object: {obj}")
@@ -346,7 +350,8 @@ def writefile(data, filename, mode='w'):
with fasteners.InterProcessLock(f"{filename}.lock"):
# skipkeys=True, ensure_ascii=True, check_circular=True, allow_nan=True
output = json.dumps(data, indent=2, default=default)
- log.debug(f'Saving: {filename} len={len(output)}')
+ if not silent:
+ log.debug(f'Saving: {filename} len={len(output)}')
with open(filename, mode, encoding="utf8") as file:
file.write(output)
except Exception as e:
@@ -372,24 +377,26 @@ options_templates.update(options_section(('sd', "Execution & Models"), {
"sd_checkpoint_autoload": OptionInfo(True, "Model autoload on server start"),
"sd_model_checkpoint": OptionInfo(default_checkpoint, "Base model", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints),
"sd_model_refiner": OptionInfo('None', "Refiner model", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints),
- "sd_checkpoint_cache": OptionInfo(0, "Number of cached models", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
- "sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAEs", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_vae": OptionInfo("Automatic", "VAE model", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list),
"sd_model_dict": OptionInfo('None', "Use baseline data from a different model", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints),
"stream_load": OptionInfo(False, "Load models using stream loading method"),
- "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary"),
+ "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary", gr.Checkbox, {"visible": False}),
"prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }),
"prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"),
"comma_padding_backtrack": OptionInfo(20, "Prompt padding for long prompts", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
+ "sd_checkpoint_cache": OptionInfo(0, "Number of cached models", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
+ "sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAEs", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_disable_ckpt": OptionInfo(False, "Disallow usage of models in ckpt format"),
}))
options_templates.update(options_section(('optimizations', "Optimizations"), {
"cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
"cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}),
- "sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}),
- "sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
- "sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
+ "sub_quad_sep": OptionInfo("Sub-quadratic options
", "", gr.HTML),
+ "sub_quad_q_chunk_size": OptionInfo(512, "cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}),
+ "sub_quad_kv_chunk_size": OptionInfo(512, "cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
+ "sub_quad_chunk_threshold": OptionInfo(80, "cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
+ "token_merging_sep": OptionInfo("Token Merging
", "", gr.HTML),
"token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}),
"token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}),
"token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for hires pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}),
@@ -437,7 +444,7 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), {
"diffusers_attention_slicing": OptionInfo(True if devices.backend == "ipex" else False, "Enable attention slicing"),
"diffusers_model_load_variant": OptionInfo("default", "Diffusers model loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}),
"diffusers_vae_load_variant": OptionInfo("default", "Diffusers VAE loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}),
- "diffusers_lora_loader": OptionInfo("diffusers", "Diffusers LoRA loading variant", gr.Radio, lambda: {"choices": ['diffusers', 'sequential apply', 'merge and apply']}),
+ "diffusers_lora_loader": OptionInfo("diffusers" if cmd_opts.use_openvino else "sequential apply", "Diffusers LoRA loading variant", gr.Radio, lambda: {"choices": ['diffusers', 'sequential apply', 'merge and apply']}),
"diffusers_force_zeros": OptionInfo(True, "Force zeros for prompts when empty"),
"diffusers_aesthetics_score": OptionInfo(False, "Require aesthetics score"),
}))
@@ -525,19 +532,19 @@ options_templates.update(options_section(('saving-paths', "Image Naming & Paths"
options_templates.update(options_section(('ui', "User Interface"), {
"gradio_theme": OptionInfo("black-teal", "UI theme", gr.Dropdown, lambda: {"choices": list_themes()}, refresh=refresh_themes),
"theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}),
- "tooltips": OptionInfo("UI Tooltips", "UI tooltips", gr.Radio, {"choices": ["None", "Browser default", "UI tooltips"]}),
- "return_grid": OptionInfo(True, "Show grid in results for web"),
- "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"),
- "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"),
+ "tooltips": OptionInfo("UI Tooltips", "UI tooltips", gr.Radio, {"choices": ["None", "Browser default", "UI tooltips"], "visible": False}),
+ "return_grid": OptionInfo(True, "Show grid in results"),
+ "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results"),
+ "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results"),
"disable_weights_auto_swap": OptionInfo(True, "Do not change selected model when reading generation parameters"),
"send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"),
"send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"),
"font": OptionInfo("", "Font for image grids that have text"),
- "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 ", 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
+ "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, "visible": False}),
+ "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001, "visible": False}),
+ "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters", gr.Textbox, { "visible": False }), # pylint: disable=anomalous-backslash-in-string
"quicksettings_list": OptionInfo(["sd_model_checkpoint"] if backend == Backend.ORIGINAL else ["sd_model_checkpoint", "sd_model_refiner"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
- "ui_scripts_reorder": OptionInfo("", "UI scripts order"),
+ "ui_scripts_reorder": OptionInfo("", "UI scripts order", gr.Textbox, { "visible": False }),
}))
options_templates.update(options_section(('live-preview', "Live Previews"), {
@@ -548,7 +555,7 @@ options_templates.update(options_section(('live-preview', "Live Previews"), {
"notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs, folder=True),
"show_progress_every_n_steps": OptionInfo(1, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}),
"show_progress_type": OptionInfo("Approximate NN", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}),
- "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}),
+ "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"], "visible": False}),
"live_preview_refresh_period": OptionInfo(500, "Progress update period", gr.Slider, {"minimum": 0, "maximum": 5000, "step": 25}),
"logmonitor_show": OptionInfo(True, "Show log view"),
"logmonitor_refresh_period": OptionInfo(5000, "Log view update period", gr.Slider, {"minimum": 0, "maximum": 30000, "step": 25}),
@@ -606,7 +613,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), {
"postprocessing_sep_upscalers": OptionInfo("Upscaling
", "", gr.HTML),
'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
- "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
+ # "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"SCUNET_tile": OptionInfo(256, "Tile size for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
@@ -634,7 +641,7 @@ options_templates.update(options_section(('interrogate', "Interrogate"), {
"interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}),
"interrogate_clip_min_length": OptionInfo(32, "Interrogate: minimum description length", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}),
"interrogate_clip_max_length": OptionInfo(192, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}),
- "interrogate_clip_dict_limit": OptionInfo(2048, "CLIP: maximum number of lines in text file"),
+ "interrogate_clip_dict_limit": OptionInfo(2048, "CLIP: maximum number of lines in text file", gr.Slider, { "visible": False }),
"interrogate_clip_skip_categories": OptionInfo(["artists", "movements", "flavors"], "Interrogate: skip categories", gr.CheckboxGroup, lambda: {"choices": modules.interrogate.category_types()}, refresh=modules.interrogate.category_types),
"interrogate_deepbooru_score_threshold": OptionInfo(0.65, "Interrogate: deepbooru score threshold", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
"deepbooru_sort_alpha": OptionInfo(False, "Interrogate: deepbooru sort alphabetically"),
@@ -644,17 +651,18 @@ options_templates.update(options_section(('interrogate', "Interrogate"), {
}))
options_templates.update(options_section(('extra_networks', "Extra Networks"), {
+ "extra_networks": OptionInfo(["All"], "Extra networks", ui_components.DropdownMulti, lambda: {"choices": ['All'] + [en.title for en in extra_networks]}),
"extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, lambda: {"choices": ["cover", "inline", "sidebar"]}),
"extra_networks_height": OptionInfo(53, "UI height (%)", gr.Slider, {"minimum": 10, "maximum": 100, "step": 1}),
"extra_networks_sidebar_width": OptionInfo(35, "UI sidebar width (%)", gr.Slider, {"minimum": 10, "maximum": 80, "step": 1}),
- "extra_networks_card_lazy": OptionInfo(True, "UI card preview lazy loading"),
+ "extra_networks_card_lazy": OptionInfo(True, "UI card preview lazy loading", gr.Checkbox, { "visible": False }),
"extra_networks_card_size": OptionInfo(160, "UI card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
"extra_networks_card_square": OptionInfo(True, "UI disable variable aspect ratio"),
- "extra_networks_card_fit": OptionInfo("cover", "UI image contain method", gr.Radio, lambda: {"choices": ["contain", "cover", "fill"]}),
+ "extra_networks_card_fit": OptionInfo("cover", "UI image contain method", gr.Radio, lambda: {"choices": ["contain", "cover", "fill"], "visible": False}),
"extra_network_skip_indexing": OptionInfo(False, "Do not automatically build extra network pages", gr.Checkbox),
"lyco_patch_lora": OptionInfo(False, "Use LyCoris handler for all LoRA types", gr.Checkbox),
- "lora_functional": OptionInfo(False, "Use Kohya method for handling multiple LoRA", gr.Checkbox),
- "extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ "lora_functional": OptionInfo(False, "Use Kohya method for handling multiple LoRA", gr.Checkbox, { "visible": False }),
+ "extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01, "visible": False}),
"sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: { "choices": ["None"] + list(hypernetworks.keys()), "visible": False }, refresh=reload_hypernetworks),
}))
@@ -829,7 +837,7 @@ else:
opts.data['sd_backend'] = 'diffusers' if backend == Backend.DIFFUSERS else 'original'
opts.data['uni_pc_lower_order_final'] = opts.schedulers_use_loworder
opts.data['uni_pc_order'] = opts.schedulers_solver_order
-opts.data['diffusers_lora_loader'] = 'diffusers' # TODO broken in diffusers=0.21
+# opts.data['diffusers_lora_loader'] = 'diffusers' # TODO broken in diffusers=0.21
log.info(f'Engine: backend={backend} compute={devices.backend} mode={devices.inference_context.__name__} device={devices.get_optimal_device_name()}')
log.info(f'Device: {print_dict(devices.get_gpu_info())}')
diff --git a/modules/shared_items.py b/modules/shared_items.py
index 8bb05b064..061fe4929 100644
--- a/modules/shared_items.py
+++ b/modules/shared_items.py
@@ -20,7 +20,7 @@ def refresh_vae_list():
def list_crossattention():
return [
- "Disable cross-attention layer optimization",
+ "Disabled",
"xFormers",
"Scaled-Dot-Product",
"Doggettx's",
diff --git a/modules/ui.py b/modules/ui.py
index d6f7bb62c..898da57d1 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -392,7 +392,7 @@ def create_ui(startup_timer = None):
with FormGroup():
with FormRow(elem_id="sampler_selection_txt2img_alt_row1"):
latent_index = gr.Dropdown(label='Secondary sampler', elem_id="txt2img_sampling_alt", choices=[x.name for x in modules.sd_samplers.samplers], value='Default', type="index")
- denoising_strength = gr.Slider(minimum=0.05, maximum=1.0, step=0.01, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength")
+ denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength")
with FormRow(elem_id="txt2img_hires_finalres", variant="compact"):
hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False)
with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"):
@@ -678,7 +678,7 @@ def create_ui(startup_timer = None):
with FormGroup(visible=show_denoise.value, elem_id=f"{tab}_denoise_group") as denoise_group:
with FormRow():
- denoising_strength = gr.Slider(minimum=0.05, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength")
+ denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength")
refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise start', value=0.0, elem_id="img2img_refiner_start")
with FormGroup(visible=show_advanced.value, elem_id=f"{tab}_advanced_group") as advanced_group:
@@ -1076,6 +1076,11 @@ def create_ui(startup_timer = None):
loadsave.create_ui()
create_dirty_indicator("tab_defaults", [], interactive=False)
+ with gr.TabItem("Change log", id="change_log", elem_id="system_tab_changelog"):
+ with open('CHANGELOG.md', 'r', encoding='utf-8') as f:
+ md = f.read()
+ gr.Markdown(md)
+
with gr.TabItem("Licenses", id="system_licenses", elem_id="system_tab_licenses"):
gr.HTML(modules.shared.html("licenses.html"), elem_id="licenses", elem_classes="licenses")
create_dirty_indicator("tab_licenses", [], interactive=False)
diff --git a/modules/ui_common.py b/modules/ui_common.py
index b4f31734c..8d1bb48fd 100644
--- a/modules/ui_common.py
+++ b/modules/ui_common.py
@@ -75,15 +75,21 @@ def save_files(js_data, images, html_info, index):
class PObject: # pylint: disable=too-few-public-methods
def __init__(self, d=None):
if d is not None:
- for key, value in d.items():
- setattr(self, key, value)
- self.seed = getattr(self, 'seed', None) or getattr(self, 'Seed', None)
+ for k, v in d.items():
+ setattr(self, k, v)
self.prompt = getattr(self, 'prompt', None) or getattr(self, 'Prompt', None)
- self.all_seeds = getattr(self, 'all_seeds', [self.seed])
self.all_prompts = getattr(self, 'all_prompts', [self.prompt])
+ self.negative_prompt = getattr(self, 'negative_prompt', None)
+ self.all_negative_prompt = getattr(self, 'all_negative_prompts', [self.negative_prompt])
+ self.seed = getattr(self, 'seed', None) or getattr(self, 'Seed', None)
+ self.all_seeds = getattr(self, 'all_seeds', [self.seed])
+ self.subseed = getattr(self, 'subseed', None)
+ self.all_subseeds = getattr(self, 'all_subseeds', [self.subseed])
+ self.width = getattr(self, 'width', None)
+ self.height = getattr(self, 'height', None)
+ self.index_of_first_image = getattr(self, 'index_of_first_image', 0)
self.infotexts = getattr(self, 'infotexts', [html_info])
self.infotext = self.infotexts[0] if len(self.infotexts) > 0 else html_info
- self.index_of_first_image = getattr(self, 'index_of_first_image', 0)
try:
data = json.loads(js_data)
except Exception:
@@ -115,7 +121,7 @@ def save_files(js_data, images, html_info, index):
destination = os.path.join(destination, dirname)
os.makedirs(destination, exist_ok = True)
shutil.copy(fullfn, destination)
- shared.log.info(f"Copying image: {fullfn} -> {destination}")
+ shared.log.info(f'Copying image: file="{fullfn}" folder="{destination}"')
tgt_filename = os.path.join(destination, os.path.basename(fullfn))
modules.script_callbacks.image_save_btn_callback(tgt_filename)
else:
diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py
index 62662b9fb..bf70cf4d7 100644
--- a/modules/ui_extra_networks.py
+++ b/modules/ui_extra_networks.py
@@ -16,10 +16,10 @@ from modules.ui_components import ToolButton
import modules.ui_symbols as symbols
-extra_pages = []
allowed_dirs = []
dir_cache = {} # key=path, value=(mtime, listdir(path))
refresh_time = None
+extra_pages = shared.extra_networks
def listdir(path):
@@ -37,9 +37,9 @@ def listdir(path):
def register_page(page):
# registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions
- extra_pages.append(page)
+ shared.extra_networks.append(page)
allowed_dirs.clear()
- for page in extra_pages:
+ for page in shared.extra_networks:
for folder in page.allowed_directories_for_previews():
if folder not in allowed_dirs:
allowed_dirs.append(os.path.abspath(folder))
@@ -58,7 +58,7 @@ def fetch_file(filename: str = ""):
def get_metadata(page: str = "", item: str = ""):
- page = next(iter([x for x in extra_pages if x.name == page]), None)
+ page = next(iter([x for x in shared.extra_networks if x.name == page]), None)
if page is None:
return JSONResponse({ 'metadata': 'none' })
metadata = page.metadata.get(item, 'none')
@@ -69,7 +69,7 @@ def get_metadata(page: str = "", item: str = ""):
def get_info(page: str = "", item: str = ""):
- page = next(iter([x for x in extra_pages if x.name == page]), None)
+ page = next(iter([x for x in shared.extra_networks if x.name == page]), None)
if page is None:
return JSONResponse({ 'info': 'none' })
info = page.info.get(item, 'none')
@@ -128,7 +128,7 @@ class ExtraNetworksPage:
shared.log.error(f'Cannot evaluate extra network prompt: {item["prompt"]} {e}')
if not any(self.title in x.label for x in xyz_grid.axis_options):
- if self.title == 'Checkpoints':
+ if self.title == 'Model':
return
opt = xyz_grid.AxisOption(f"[Network] {self.title}", str, add_prompt, choices=lambda: [x["name"] for x in self.items])
xyz_grid.axis_options.append(opt)
@@ -308,7 +308,7 @@ class ExtraNetworksPage:
shared.log.error(f'Extra network save preview: {filename} {e}')
return
is_allowed = False
- for page in extra_pages:
+ for page in shared.extra_networks:
if any(path_is_parent(x, filename) for x in page.allowed_directories_for_previews()):
is_allowed = True
break
@@ -339,7 +339,7 @@ class ExtraNetworksPage:
def initialize():
- extra_pages.clear()
+ shared.extra_networks.clear()
def register_pages():
@@ -353,6 +353,21 @@ def register_pages():
register_page(ExtraNetworksPageHypernetworks())
+def get_pages():
+ pages = []
+ if 'All' in shared.opts.extra_networks:
+ pages = shared.extra_networks
+ else:
+ titles = [page.title for page in shared.extra_networks]
+ for page in shared.opts.extra_networks:
+ try:
+ idx = titles.index(page)
+ except ValueError:
+ continue
+ pages.append(shared.extra_networks[idx])
+ return pages
+
+
class ExtraNetworksUi:
def __init__(self):
self.pages = None
@@ -384,7 +399,7 @@ def create_ui(container, button, tabname, skip_indexing = False):
if ui.tabname == 'txt2img': # refresh only once
global refresh_time # pylint: disable=global-statement
refresh_time = time.time()
- for page in extra_pages:
+ for page in get_pages():
page.create_page(ui.tabname, skip_indexing)
with gr.Tab(page.title, id=page.title.lower().replace(" ", "_"), elem_classes="extra-networks-tab"):
hmtl = gr.HTML(page.html, elem_id=f'{tabname}{page.name}_extra_page', elem_classes="extra-networks-page")
@@ -396,16 +411,16 @@ def create_ui(container, button, tabname, skip_indexing = False):
return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary"))
def en_refresh(title):
- res = []
- for page in extra_pages:
+ pages = []
+ for page in get_pages():
if title is None or title == '' or title == page.title or len(page.html) == 0:
page.refresh()
page.refresh_time = None
page.create_page(ui.tabname)
shared.log.debug(f"Refreshing Extra networks: page='{page.title}' items={len(page.items)} tab={ui.tabname}")
- res.append(page.html)
+ pages.append(page.html)
ui.search.update(value = ui.search.value)
- return res
+ return pages
state_visible = gr.State(value=False) # pylint: disable=abstract-class-instantiated
button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button])
@@ -423,15 +438,13 @@ def path_is_parent(parent_path, child_path):
def setup_ui(ui, gallery):
def save_preview(pagename, index, images, filename):
- res = []
- for page in extra_pages:
+ pages = []
+ for page in get_pages():
if pagename is None or pagename == '' or pagename == page.title or len(page.html) == 0:
page.save_preview(index, images, filename)
- res.append(page.create_page(ui.tabname))
- else:
- res.append(page.html)
- return res
-
+ page.create_page(ui.tabname)
+ pages.append(page.html)
+ return pages
ui.button_save_preview.click(
fn=save_preview,
@@ -441,14 +454,13 @@ def setup_ui(ui, gallery):
)
def save_description(pagename, filename, desc):
- res = []
- for page in extra_pages:
+ pages = []
+ for page in get_pages():
if pagename is None or pagename == '' or pagename == page.title or len(page.html) == 0:
page.save_description(filename, desc)
- res.append(page.create_page(ui.tabname))
- else:
- res.append(page.html)
- return res
+ page.create_page(ui.tabname)
+ pages.append(page.html)
+ return pages
ui.button_save_description.click(
fn=save_description,
diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py
index 4a4a18e1d..39ce706be 100644
--- a/modules/ui_extra_networks_checkpoints.py
+++ b/modules/ui_extra_networks_checkpoints.py
@@ -7,7 +7,7 @@ from modules import shared, ui_extra_networks, sd_models
class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
def __init__(self):
- super().__init__('Checkpoints')
+ super().__init__('Model')
def refresh(self):
shared.refresh_checkpoints()
diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py
index 66ddca527..a61f5ffb6 100644
--- a/modules/ui_extra_networks_hypernets.py
+++ b/modules/ui_extra_networks_hypernets.py
@@ -5,7 +5,7 @@ from modules import shared, ui_extra_networks
class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
def __init__(self):
- super().__init__('Hypernetworks')
+ super().__init__('Hypernetwork')
def refresh(self):
shared.reload_hypernetworks()
diff --git a/modules/ui_extra_networks_styles.py b/modules/ui_extra_networks_styles.py
index c3e402d70..db772efd1 100644
--- a/modules/ui_extra_networks_styles.py
+++ b/modules/ui_extra_networks_styles.py
@@ -6,7 +6,7 @@ from modules import shared, ui_extra_networks
class ExtraNetworksPageStyles(ui_extra_networks.ExtraNetworksPage):
def __init__(self):
- super().__init__('Styles')
+ super().__init__('Style')
def refresh(self):
shared.prompt_styles.reload()
diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py
index 438ce86f1..78e900858 100644
--- a/modules/ui_extra_networks_textual_inversion.py
+++ b/modules/ui_extra_networks_textual_inversion.py
@@ -7,7 +7,7 @@ from modules.textual_inversion.textual_inversion import Embedding
class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
def __init__(self):
- super().__init__('Textual Inversion')
+ super().__init__('Embedding')
self.allow_negative_prompt = True
def refresh(self):
diff --git a/modules/ui_tempdir.py b/modules/ui_tempdir.py
index 945a004b6..38b4b5923 100644
--- a/modules/ui_tempdir.py
+++ b/modules/ui_tempdir.py
@@ -61,7 +61,7 @@ def pil_to_temp_file(self, img, dir: str, format="png") -> str: # pylint: disabl
file_obj = tempfile.NamedTemporaryFile(delete=False, suffix=".png", dir=dir)
img.save(file_obj, pnginfo=(metadata if use_metadata else None))
name = file_obj.name
- shared.log.debug(f'Saving temp image: {name}')
+ shared.log.debug(f'Saving temp: image="{name}"')
return name
diff --git a/requirements.txt b/requirements.txt
index d926ae426..59d4a3f55 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -27,7 +27,6 @@ opencv-contrib-python-headless
piexif
psutil
pyyaml
-realesrgan
resize-right
rich
safetensors