Merge branch 'master' into vlad3

This commit is contained in:
Symbiomatrix
2023-10-03 10:45:28 +03:00
committed by GitHub
42 changed files with 1054 additions and 223 deletions
+4 -1
View File
@@ -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...
+4 -4
View File
@@ -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**
<div align="center">
<!-- sponsors --><a href="https://github.com/Tillerz"><img src="https://github.com/Tillerz.png" width="60px" alt="Tillerz" /></a><a href="https://github.com/allangrant"><img src="https://github.com/allangrant.png" width="60px" alt="Allan Grant" /></a><a href="https://github.com/demib72"><img src="https://github.com/demib72.png" width="60px" alt="Michael Harris" /></a><a href="https://github.com/BrentOzar"><img src="https://github.com/BrentOzar.png" width="60px" alt="Brent Ozar" /></a><a href="https://github.com/AimwiseTonix"><img src="https://github.com/AimwiseTonix.png" width="60px" alt="ToniX" /></a><a href="https://github.com/inktomi"><img src="https://github.com/inktomi.png" width="60px" alt="Matthew Runo" /></a><a href="https://github.com/HELLO-WORLD-SAS"><img src="https://github.com/HELLO-WORLD-SAS.png" width="60px" alt="HELLO WORLD SAS" /></a><a href="https://github.com/4joeknight4"><img src="https://github.com/4joeknight4.png" width="60px" alt="" /></a><a href="https://github.com/SaladTechnologies"><img src="https://github.com/SaladTechnologies.png" width="60px" alt="Salad Technologies" /></a><a href="https://github.com/gymdreams8"><img src="https://github.com/gymdreams8.png" width="60px" alt="Gym Dreams • GymDreams8" /></a><!-- sponsors -->
<!-- sponsors --><a href="https://github.com/allangrant"><img src="https://github.com/allangrant.png" width="60px" alt="Allan Grant" /></a><a href="https://github.com/demib72"><img src="https://github.com/demib72.png" width="60px" alt="Michael Harris" /></a><a href="https://github.com/BrentOzar"><img src="https://github.com/BrentOzar.png" width="60px" alt="Brent Ozar" /></a><a href="https://github.com/AimwiseTonix"><img src="https://github.com/AimwiseTonix.png" width="60px" alt="ToniX" /></a><a href="https://github.com/inktomi"><img src="https://github.com/inktomi.png" width="60px" alt="Matthew Runo" /></a><a href="https://github.com/HELLO-WORLD-SAS"><img src="https://github.com/HELLO-WORLD-SAS.png" width="60px" alt="HELLO WORLD SAS" /></a><a href="https://github.com/4joeknight4"><img src="https://github.com/4joeknight4.png" width="60px" alt="" /></a><a href="https://github.com/SaladTechnologies"><img src="https://github.com/SaladTechnologies.png" width="60px" alt="Salad Technologies" /></a><a href="https://github.com/gymdreams8"><img src="https://github.com/gymdreams8.png" width="60px" alt="Gym Dreams • GymDreams8" /></a><!-- sponsors -->
</div>
<br>
@@ -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:
@@ -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
@@ -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
+7 -7
View File
@@ -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":""},
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

+2 -2
View File
@@ -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')
+313
View File
@@ -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;
}
+2 -1
View File
@@ -34,9 +34,10 @@ async function createSplash() {
</div>`;
document.body.insertAdjacentHTML('beforeend', splash);
await preloadImages();
const imgElement = `<div class="splash-img" alt="logo" style="background-image: url(file=html/logo-bg-${dark ? 'dark' : 'light'}.jpg), url(file=html/logo-bg-${num}.jpg); background-blend-mode: ${dark ? 'darken' : 'lighten'}"></div>`;
const imgElement = `<div class="splash-img" alt="logo" style="background-image: url(file=html/logo-bg-${dark ? 'dark' : 'light'}.jpg), url(file=html/logo-bg-${num}.jpg); background-blend-mode: ${dark ? 'multiply' : 'lighten'}"></div>`;
document.getElementById('splash').insertAdjacentHTML('afterbegin', imgElement);
}
async function removeSplash() {
const splash = document.getElementById('splash');
if (splash) splash.remove();
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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 = [
+18 -4
View File
@@ -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) }
}
+1 -2
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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.
+19 -13
View File
@@ -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)
+2 -1
View File
@@ -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
+22 -10
View File
@@ -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:
+5 -1
View File
@@ -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
+38 -14
View File
@@ -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
+20 -4
View File
@@ -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():
+13 -9
View File
@@ -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)
+21 -13
View File
@@ -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()
+14 -19
View File
@@ -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 []
+383
View File
@@ -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
+4 -11
View File
@@ -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'
+15 -15
View File
@@ -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):
+23
View File
@@ -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:
+2 -2
View File
@@ -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
+43 -35
View File
@@ -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("<h2>Sub-quadratic options</h2>", "", 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("<h2>Token Merging</h2>", "", 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 <extra networks:0.9>", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
"keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), # pylint: disable=anomalous-backslash-in-string
"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 <extra networks:0.9>", 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("<h2>Upscaling</h2>", "", 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())}')
+1 -1
View File
@@ -20,7 +20,7 @@ def refresh_vae_list():
def list_crossattention():
return [
"Disable cross-attention layer optimization",
"Disabled",
"xFormers",
"Scaled-Dot-Product",
"Doggettx's",
+7 -2
View File
@@ -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)
+12 -6
View File
@@ -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:
+38 -26
View File
@@ -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,
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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()
@@ -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):
+1 -1
View File
@@ -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
-1
View File
@@ -27,7 +27,6 @@ opencv-contrib-python-headless
piexif
psutil
pyyaml
realesrgan
resize-right
rich
safetensors