diff --git a/.eslintrc.json b/.eslintrc.json
index 22a9edee5..7ea7b8dae 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -112,6 +112,8 @@
"idbPut": "readonly",
"idbDel": "readonly",
"idbAdd": "readonly",
+ // changelog.js
+ "initChangelog": "readonly",
// notification.js
"sendNotification": "readonly"
},
diff --git a/.gitignore b/.gitignore
index 4d0ab36c6..dca4e17ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,11 @@ tunableop_results*.csv
!webui.sh
!package.json
+# pyinstaller
+*.spec
+build/
+dist/
+
# dynamically generated
/repositories/ip-instruct/
@@ -69,3 +74,4 @@ tunableop_results*.csv
!/models/VAE-approx/model.pt
!/models/Reference
!/models/Reference/**/*
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39892eb0f..6b60a7a36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,43 @@
# Change Log for SD.Next
+## Update for 2024-11-01
+
+Smaller release just 3 days after the last one, but with some important fixes and improvements.
+This release can be considered an LTS release before we kick off the next round of major updates.
+
+- XYZ grid:
+ - optional per-image time benchmark info
+- UI:
+ - add additional [hotkeys](https://github.com/vladmandic/automatic/wiki/Hotkeys)
+- Docs:
+ - add built-in [changelog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) search
+ since changelog is the best up-to-date source of info
+ go to system -> changelog and search/highligh/navigate directly in UI!
+- SD3: ControlNets:
+ - *InstantX Canny, Pose, Depth, Tile*
+ - *Alimama Inpainting, SoftEdge*
+ - *note*: that just like with FLUX.1 or any large model, ControlNet are also large and can push your system over the limit
+ e.g. SD3 controlnets vary from 1GB to over 4GB in size
+- SD3: all-in-one safetensors
+ - *examples*: [large](https://civitai.com/models/882666/sd35-large-google-flan?modelVersionId=1003031), [medium](https://civitai.com/models/900327)
+ - *note*: enable *bnb* on-the-fly quantization for even bigger gains
+- CLI:
+ - refactor command line params
+ run `webui.sh`/`webui.bat` with `--help` to see all options
+- Other:
+ - Repo: move screenshots to GH pages
+ - Update requirements
+- Fixes:
+ - detailer min/max size as fractions of image size
+ - ipadapter load on-demand
+ - ipadapter face use correct yolo model
+ - list diffusers remove duplicates
+ - fix legacy extensions access to shared objects
+ - fix diffusers load from folder
+ - fix lora enum logging on windows
+ - fix xyz grid with batch count
+ - move dowwloads of some auxillary models to hfcache instead of models folder
+
## Update for 2024-10-29
### Highlights for 2024-10-29
diff --git a/CITATION.cff b/CITATION.cff
index f7fd4bba3..c39efe474 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -24,5 +24,5 @@ abstract: >-
generation
keywords:
- stablediffusion diffusers sdnext
-license: AGPL-3.0
+license: Apache-2.0
date-released: 2022-12-24
diff --git a/LICENSE.txt b/LICENSE.txt
index 211d32e75..0ad25db4b 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,8 +1,6 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
- Copyright (c) 2023 AUTOMATIC1111
-
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -635,8 +633,8 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
diff --git a/README.md b/README.md
index ac96b2e32..a2caa5cb7 100644
--- a/README.md
+++ b/README.md
@@ -50,12 +50,13 @@ All individual features are not listed here, instead check [ChangeLog](CHANGELOG
*Main interface using **StandardUI***:
-
+
*Main interface using **ModernUI***:
-
-
-
+
+
+
+
For screenshots and informations on other available themes, see [Themes Wiki](https://github.com/vladmandic/automatic/wiki/Themes)
@@ -63,12 +64,13 @@ For screenshots and informations on other available themes, see [Themes Wiki](ht
## Model support
-Additional models will be added as they become available and there is public interest in them
+Additional models will be added as they become available and there is public interest in them
+See [models overview](https://github.com/vladmandic/automatic/wiki/Models) for details on each model, including their architecture, complexity and other info
- [RunwayML Stable Diffusion](https://github.com/Stability-AI/stablediffusion/) 1.x and 2.x *(all variants)*
- [StabilityAI Stable Diffusion XL](https://github.com/Stability-AI/generative-models)
-- [StabilityAI Stable Diffusion 3 Medium](https://stability.ai/news/stable-diffusion-3-medium)
-- [Stable Diffusion 3.5 Large](https://huggingface.co/stabilityai/stable-diffusion-3.5-large)
+- [StabilityAI Stable Diffusion](https://stability.ai/news/stable-diffusion-3-medium)
+- [Stable Diffusion 3.x](https://huggingface.co/stabilityai/stable-diffusion-3.5-large) 3.0 Medium, 3.5 Medium, 3.5 Large, 3.5 Large Turbo
- [StabilityAI Stable Video Diffusion](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid) Base, XT 1.0, XT 1.1
- [StabilityAI Stable Cascade](https://github.com/Stability-AI/StableCascade) *Full* and *Lite*
- [Black Forest Labs FLUX.1](https://blackforestlabs.ai/announcing-black-forest-labs/) Dev, Schnell
@@ -136,13 +138,13 @@ Also supported are modifiers such as:
## Examples
*IP Adapters*:
-
+
*Color grading*:
-
+
*InstantID*:
-
+
> [!IMPORTANT]
> - Loading any model other than standard SD 1.x / SD 2.x requires use of backend **Diffusers**
@@ -173,92 +175,34 @@ Also supported are modifiers such as:
Once SD.Next is installed, simply run `webui.ps1` or `webui.bat` (*Windows*) or `webui.sh` (*Linux or MacOS*)
-List of available parameters, run `webui --help` for the full & up-to-date list:
+For list of available command line options, run `webui --help` for the full & up-to-date list
- Server options:
- --config CONFIG Use specific server configuration file, default: config.json
- --ui-config UI_CONFIG Use specific UI configuration file, default: ui-config.json
- --medvram Split model stages and keep only active part in VRAM, default: False
- --lowvram Split model components and keep only active part in VRAM, default: False
- --ckpt CKPT Path to model checkpoint to load immediately, default: None
- --vae VAE Path to VAE checkpoint to load immediately, default: None
- --data-dir DATA_DIR Base path where all user data is stored, default:
- --models-dir MODELS_DIR Base path where all models are stored, default: models
- --allow-code Allow custom script execution, default: False
- --share Enable UI accessible through Gradio site, default: False
- --insecure Enable extensions tab regardless of other options, default: False
- --use-cpu USE_CPU [USE_CPU ...] Force use CPU for specified modules, default: []
- --listen Launch web server using public IP address, default: False
- --port PORT Launch web server with given server port, default: 7860
- --freeze Disable editing settings
- --auth AUTH Set access authentication like "user:pwd,user:pwd""
- --auth-file AUTH_FILE Set access authentication using file, default: None
- --autolaunch Open the UI URL in the system's default browser upon launch
- --docs Mount API docs, default: False
- --api-only Run in API only mode without starting UI
- --api-log Enable logging of all API requests, default: False
- --device-id DEVICE_ID Select the default CUDA device to use, default: None
- --cors-origins CORS_ORIGINS Allowed CORS origins as comma-separated list, default: None
- --cors-regex CORS_REGEX Allowed CORS origins as regular expression, default: None
- --tls-keyfile TLS_KEYFILE Enable TLS and specify key file, default: None
- --tls-certfile TLS_CERTFILE Enable TLS and specify cert file, default: None
- --tls-selfsign Enable TLS with self-signed certificates, default: False
- --server-name SERVER_NAME Sets hostname of server, default: None
- --no-hashing Disable hashing of checkpoints, default: False
- --no-metadata Disable reading of metadata from models, default: False
- --disable-queue Disable queues, default: False
- --subpath SUBPATH Customize the URL subpath for usage with reverse proxy
- --backend {original,diffusers} force model pipeline type
- --allowed-paths ALLOWED_PATHS [ALLOWED_PATHS ...] add additional paths to paths allowed for web access
-
- Setup options:
- --reset Reset main repository to latest version, default: False
- --upgrade Upgrade main repository to latest version, default: False
- --requirements Force re-check of requirements, default: False
- --quick Bypass version checks, default: False
- --use-directml Use DirectML if no compatible GPU is detected, default: False
- --use-openvino Use Intel OpenVINO backend, default: False
- --use-ipex Force use Intel OneAPI XPU backend, default: False
- --use-cuda Force use nVidia CUDA backend, default: False
- --use-rocm Force use AMD ROCm backend, default: False
- --use-zluda Force use ZLUDA, AMD GPUs only, default: False
- --use-xformers Force use xFormers cross-optimization, default: False
- --skip-requirements Skips checking and installing requirements, default: False
- --skip-extensions Skips running individual extension installers, default: False
- --skip-git Skips running all GIT operations, default: False
- --skip-torch Skips running Torch checks, default: False
- --skip-all Skips running all checks, default: False
- --skip-env Skips setting of env variables during startup, default: False
- --experimental Allow unsupported versions of libraries, default: False
- --reinstall Force reinstallation of all requirements, default: False
- --test Run test only and exit
- --version Print version information
- --ignore Ignore any errors and attempt to continue
- --safe Run in safe mode with no user extensions
- --uv Use uv as installer, default: False
-
- Logging options:
- --log LOG Set log file, default: None
- --debug Run installer with debug logging, default: False
- --profile Run profiler, default: False
+> [!TIP]
+> All command line options can also be set via env variable
+> For example `--debug` is same as `set SD_DEBUG=true`
## Notes
> [!TIP]
> If you don't want to use built-in `venv` support and prefer to run SD.Next in your own environment such as *Docker* container, *Conda* environment or any other virtual environment, you can skip `venv` create/activate and launch SD.Next directly using `python launch.py` (command line flags noted above still apply).
+### Quantization
+
+**SD.Next** comes with broad quantization support, including support for BitsAndBytes, Optimum.Quanto, TorchAO, NNCF and GGUF
+See [Quantization Wiki](https://github.com/vladmandic/automatic/wiki/Quantization)
+
### Control
**SD.Next** comes with built-in control for all types of text2image, image2image, video2video and batch processing
*Control interface*:
-
+
*Control processors*:
-
+
*Masking*:
-
+
### Extensions
diff --git a/TODO.md b/TODO.md
index 5726e67da..2f3f90852 100644
--- a/TODO.md
+++ b/TODO.md
@@ -6,7 +6,7 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma
- async lowvram:
- fp8:
-- ipadapter-negative: https://github.com/huggingface/diffusers/discussions/7167
+- ipadapter-negative:
- include reference styles
### Missing
diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py
index 83aa6b40b..160487e88 100644
--- a/extensions-builtin/Lora/networks.py
+++ b/extensions-builtin/Lora/networks.py
@@ -128,7 +128,7 @@ def load_diffusers(name, network_on_disk, lora_scale=shared.opts.extra_networks_
def load_network(name, network_on_disk) -> network.Network:
if not shared.sd_loaded:
- return
+ return None
t0 = time.time()
cached = lora_cache.get(name, None)
if debug:
@@ -533,18 +533,14 @@ def network_MultiheadAttention_load_state_dict(self, *args, **kwargs):
def list_available_networks():
+ t0 = time.time()
available_networks.clear()
available_network_aliases.clear()
forbidden_network_aliases.clear()
available_network_hash_lookup.clear()
forbidden_network_aliases.update({"none": 1, "Addams": 1})
- directories = []
- if os.path.exists(shared.cmd_opts.lora_dir):
- directories.append(shared.cmd_opts.lora_dir)
- else:
+ if not os.path.exists(shared.cmd_opts.lora_dir):
shared.log.warning(f'LoRA directory not found: path="{shared.cmd_opts.lora_dir}"')
- if os.path.exists(shared.cmd_opts.lyco_dir) and shared.cmd_opts.lyco_dir != shared.cmd_opts.lora_dir:
- directories.append(shared.cmd_opts.lyco_dir)
def add_network(filename):
if not os.path.isfile(filename):
@@ -565,11 +561,12 @@ def list_available_networks():
except OSError as e: # should catch FileNotFoundError and PermissionError etc.
shared.log.error(f'LoRA: filename="{filename}" {e}')
- candidates = list(files_cache.list_files(*directories, ext_filter=[".pt", ".ckpt", ".safetensors"]))
+ candidates = list(files_cache.list_files(shared.cmd_opts.lora_dir, ext_filter=[".pt", ".ckpt", ".safetensors"]))
with concurrent.futures.ThreadPoolExecutor(max_workers=shared.max_workers) as executor:
for fn in candidates:
executor.submit(add_network, fn)
- shared.log.info(f'Available LoRAs: items={len(available_networks)} folders={len(forbidden_network_aliases)}')
+ t1 = time.time()
+ shared.log.info(f'Available LoRAs: path="{shared.cmd_opts.lora_dir}" items={len(available_networks)} folders={len(forbidden_network_aliases)} time={t1 - t0:.2f}')
def infotext_pasted(infotext, params): # pylint: disable=W0613
diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui
index 9b721248d..71bdbbd9c 160000
--- a/extensions-builtin/sdnext-modernui
+++ b/extensions-builtin/sdnext-modernui
@@ -1 +1 @@
-Subproject commit 9b721248d55021cbb3e2976ccfa984a8e5b96f39
+Subproject commit 71bdbbd9c0a55ccea38cbf6fb01483323ac93676
diff --git a/html/card-no-preview.png b/html/card-no-preview.png
index 352b0a9df..6b89aad1a 100644
Binary files a/html/card-no-preview.png and b/html/card-no-preview.png differ
diff --git a/html/screenshot-color.jpg b/html/screenshot-color.jpg
deleted file mode 100644
index 2cd070a3a..000000000
Binary files a/html/screenshot-color.jpg and /dev/null differ
diff --git a/html/screenshot-control.jpg b/html/screenshot-control.jpg
deleted file mode 100644
index ffc71cdbe..000000000
Binary files a/html/screenshot-control.jpg and /dev/null differ
diff --git a/html/screenshot-corrections.jpg b/html/screenshot-corrections.jpg
deleted file mode 100644
index e202fa746..000000000
Binary files a/html/screenshot-corrections.jpg and /dev/null differ
diff --git a/html/screenshot-differential.jpg b/html/screenshot-differential.jpg
deleted file mode 100644
index c0bb1e78d..000000000
Binary files a/html/screenshot-differential.jpg and /dev/null differ
diff --git a/html/screenshot-instantid.jpg b/html/screenshot-instantid.jpg
deleted file mode 100644
index 5f780a3c0..000000000
Binary files a/html/screenshot-instantid.jpg and /dev/null differ
diff --git a/html/screenshot-ipadapter-mask.jpg b/html/screenshot-ipadapter-mask.jpg
deleted file mode 100644
index 3dd36a2af..000000000
Binary files a/html/screenshot-ipadapter-mask.jpg and /dev/null differ
diff --git a/html/screenshot-ipadapter.jpg b/html/screenshot-ipadapter.jpg
deleted file mode 100644
index b1d5c80b6..000000000
Binary files a/html/screenshot-ipadapter.jpg and /dev/null differ
diff --git a/html/screenshot-ipadapter02.jpg b/html/screenshot-ipadapter02.jpg
deleted file mode 100644
index d0e59a2ec..000000000
Binary files a/html/screenshot-ipadapter02.jpg and /dev/null differ
diff --git a/html/screenshot-ledit.jpg b/html/screenshot-ledit.jpg
deleted file mode 100644
index f43189d81..000000000
Binary files a/html/screenshot-ledit.jpg and /dev/null differ
diff --git a/html/screenshot-mask.jpg b/html/screenshot-mask.jpg
deleted file mode 100644
index c286803f2..000000000
Binary files a/html/screenshot-mask.jpg and /dev/null differ
diff --git a/html/screenshot-modernui-control.jpg b/html/screenshot-modernui-control.jpg
deleted file mode 100644
index 20a22a88a..000000000
Binary files a/html/screenshot-modernui-control.jpg and /dev/null differ
diff --git a/html/screenshot-modernui-f1.jpg b/html/screenshot-modernui-f1.jpg
deleted file mode 100644
index 3e7b27ce1..000000000
Binary files a/html/screenshot-modernui-f1.jpg and /dev/null differ
diff --git a/html/screenshot-modernui-img2img.jpg b/html/screenshot-modernui-img2img.jpg
deleted file mode 100644
index 22afd166d..000000000
Binary files a/html/screenshot-modernui-img2img.jpg and /dev/null differ
diff --git a/html/screenshot-modernui-sd3.jpg b/html/screenshot-modernui-sd3.jpg
deleted file mode 100644
index 81d0eb459..000000000
Binary files a/html/screenshot-modernui-sd3.jpg and /dev/null differ
diff --git a/html/screenshot-modernui.jpg b/html/screenshot-modernui.jpg
deleted file mode 100644
index dafadc28f..000000000
Binary files a/html/screenshot-modernui.jpg and /dev/null differ
diff --git a/html/screenshot-outpaint.jpg b/html/screenshot-outpaint.jpg
deleted file mode 100644
index 485c67c52..000000000
Binary files a/html/screenshot-outpaint.jpg and /dev/null differ
diff --git a/html/screenshot-processors.jpg b/html/screenshot-processors.jpg
deleted file mode 100644
index 8d3207fa2..000000000
Binary files a/html/screenshot-processors.jpg and /dev/null differ
diff --git a/html/screenshot-regional.jpg b/html/screenshot-regional.jpg
deleted file mode 100644
index 335297b37..000000000
Binary files a/html/screenshot-regional.jpg and /dev/null differ
diff --git a/html/screenshot-text2image.jpg b/html/screenshot-text2image.jpg
deleted file mode 100644
index f5b48dc3a..000000000
Binary files a/html/screenshot-text2image.jpg and /dev/null differ
diff --git a/installer.py b/installer.py
index a17efa83a..492961439 100644
--- a/installer.py
+++ b/installer.py
@@ -552,7 +552,7 @@ def install_rocm_zluda():
from modules import zluda_installer
zluda_installer.set_default_agent(device)
try:
- if args.reinstall_zluda:
+ if args.reinstall:
zluda_installer.uninstall()
zluda_path = zluda_installer.get_path()
zluda_installer.install(zluda_path)
@@ -1234,39 +1234,44 @@ def check_timestamp():
def add_args(parser):
- group = parser.add_argument_group('Setup options')
- group.add_argument('--reset', default = os.environ.get("SD_RESET",False), action='store_true', help = "Reset main repository to latest version, default: %(default)s")
- group.add_argument('--upgrade', '--update', default = os.environ.get("SD_UPGRADE",False), action='store_true', help = "Upgrade main repository to latest version, default: %(default)s")
- group.add_argument('--requirements', default = os.environ.get("SD_REQUIREMENTS",False), action='store_true', help = "Force re-check of requirements, default: %(default)s")
- group.add_argument('--quick', default = os.environ.get("SD_QUICK",False), action='store_true', help = "Bypass version checks, default: %(default)s")
- group.add_argument('--use-directml', default = os.environ.get("SD_USEDIRECTML",False), action='store_true', help = "Use DirectML if no compatible GPU is detected, default: %(default)s")
- group.add_argument("--use-openvino", default = os.environ.get("SD_USEOPENVINO",False), action='store_true', help="Use Intel OpenVINO backend, default: %(default)s")
- group.add_argument("--use-ipex", default = os.environ.get("SD_USEIPEX",False), action='store_true', help="Force use Intel OneAPI XPU backend, default: %(default)s")
- group.add_argument("--use-cuda", default = os.environ.get("SD_USECUDA",False), action='store_true', help="Force use nVidia CUDA backend, default: %(default)s")
- group.add_argument("--use-rocm", default = os.environ.get("SD_USEROCM",False), action='store_true', help="Force use AMD ROCm backend, default: %(default)s")
- group.add_argument('--use-zluda', default=os.environ.get("SD_USEZLUDA", False), action='store_true', help = "Force use ZLUDA, AMD GPUs only, default: %(default)s")
- group.add_argument("--use-xformers", default = os.environ.get("SD_USEXFORMERS",False), action='store_true', help="Force use xFormers cross-optimization, default: %(default)s")
- group.add_argument('--skip-requirements', default = os.environ.get("SD_SKIPREQUIREMENTS",False), action='store_true', help = "Skips checking and installing requirements, default: %(default)s")
- group.add_argument('--skip-extensions', default = os.environ.get("SD_SKIPEXTENSION",False), action='store_true', help = "Skips running individual extension installers, default: %(default)s")
- group.add_argument('--skip-git', default = os.environ.get("SD_SKIPGIT",False), action='store_true', help = "Skips running all GIT operations, default: %(default)s")
- group.add_argument('--skip-torch', default = os.environ.get("SD_SKIPTORCH",False), action='store_true', help = "Skips running Torch checks, default: %(default)s")
- group.add_argument('--skip-all', default = os.environ.get("SD_SKIPALL",False), action='store_true', help = "Skips running all checks, default: %(default)s")
- group.add_argument('--skip-env', default = os.environ.get("SD_SKIPENV",False), action='store_true', help = "Skips setting of env variables during startup, default: %(default)s")
- group.add_argument('--experimental', default = os.environ.get("SD_EXPERIMENTAL",False), action='store_true', help = "Allow unsupported versions of libraries, default: %(default)s")
- group.add_argument('--reinstall', default = os.environ.get("SD_REINSTALL",False), action='store_true', help = "Force reinstallation of all requirements, default: %(default)s")
- group.add_argument('--reinstall-zluda', default = os.environ.get("SD_REINSTALL_ZLUDA",False), action='store_true', help = "Force reinstallation of ZLUDA, default: %(default)s")
- group.add_argument('--test', default = os.environ.get("SD_TEST",False), action='store_true', help = "Run test only and exit")
- group.add_argument('--version', default = False, action='store_true', help = "Print version information")
- group.add_argument('--ignore', default = os.environ.get("SD_IGNORE",False), action='store_true', help = "Ignore any errors and attempt to continue")
- group.add_argument('--safe', default = os.environ.get("SD_SAFE",False), action='store_true', help = "Run in safe mode with no user extensions")
- group.add_argument('--uv', default = os.environ.get("SD_UV",False), action='store_true', help = "Use uv instead of pip to install the packages")
+ group_setup = parser.add_argument_group('Setup')
+ group_setup.add_argument('--reset', default = os.environ.get("SD_RESET",False), action='store_true', help = "Reset main repository to latest version, default: %(default)s")
+ group_setup.add_argument('--upgrade', '--update', default = os.environ.get("SD_UPGRADE",False), action='store_true', help = "Upgrade main repository to latest version, default: %(default)s")
+ group_setup.add_argument('--requirements', default = os.environ.get("SD_REQUIREMENTS",False), action='store_true', help = "Force re-check of requirements, default: %(default)s")
+ group_setup.add_argument('--reinstall', default = os.environ.get("SD_REINSTALL",False), action='store_true', help = "Force reinstallation of all requirements, default: %(default)s")
+ group_setup.add_argument('--uv', default = os.environ.get("SD_UV",False), action='store_true', help = "Use uv instead of pip to install the packages")
- group = parser.add_argument_group('Logging options')
- group.add_argument("--log", type=str, default=os.environ.get("SD_LOG", None), help="Set log file, default: %(default)s")
- group.add_argument('--debug', default = os.environ.get("SD_DEBUG",False), action='store_true', help = "Run installer with debug logging, default: %(default)s")
- group.add_argument("--profile", default=os.environ.get("SD_PROFILE", False), action='store_true', help="Run profiler, default: %(default)s")
- group.add_argument('--docs', default=os.environ.get("SD_DOCS", False), action='store_true', help = "Mount API docs, default: %(default)s")
- group.add_argument("--api-log", default=os.environ.get("SD_APILOG", False), action='store_true', help="Enable logging of all API requests, default: %(default)s")
+ group_startup = parser.add_argument_group('Startup')
+ group_startup.add_argument('--quick', default = os.environ.get("SD_QUICK",False), action='store_true', help = "Bypass version checks, default: %(default)s")
+ group_startup.add_argument('--skip-requirements', default = os.environ.get("SD_SKIPREQUIREMENTS",False), action='store_true', help = "Skips checking and installing requirements, default: %(default)s")
+ group_startup.add_argument('--skip-extensions', default = os.environ.get("SD_SKIPEXTENSION",False), action='store_true', help = "Skips running individual extension installers, default: %(default)s")
+ group_startup.add_argument('--skip-git', default = os.environ.get("SD_SKIPGIT",False), action='store_true', help = "Skips running all GIT operations, default: %(default)s")
+ group_startup.add_argument('--skip-torch', default = os.environ.get("SD_SKIPTORCH",False), action='store_true', help = "Skips running Torch checks, default: %(default)s")
+ group_startup.add_argument('--skip-all', default = os.environ.get("SD_SKIPALL",False), action='store_true', help = "Skips running all checks, default: %(default)s")
+ group_startup.add_argument('--skip-env', default = os.environ.get("SD_SKIPENV",False), action='store_true', help = "Skips setting of env variables during startup, default: %(default)s")
+
+ group_compute = parser.add_argument_group('Compute Engine')
+ group_compute.add_argument('--use-directml', default = os.environ.get("SD_USEDIRECTML",False), action='store_true', help = "Use DirectML if no compatible GPU is detected, default: %(default)s")
+ group_compute.add_argument("--use-openvino", default = os.environ.get("SD_USEOPENVINO",False), action='store_true', help="Use Intel OpenVINO backend, default: %(default)s")
+ group_compute.add_argument("--use-ipex", default = os.environ.get("SD_USEIPEX",False), action='store_true', help="Force use Intel OneAPI XPU backend, default: %(default)s")
+ group_compute.add_argument("--use-cuda", default = os.environ.get("SD_USECUDA",False), action='store_true', help="Force use nVidia CUDA backend, default: %(default)s")
+ group_compute.add_argument("--use-rocm", default = os.environ.get("SD_USEROCM",False), action='store_true', help="Force use AMD ROCm backend, default: %(default)s")
+ group_compute.add_argument('--use-zluda', default=os.environ.get("SD_USEZLUDA", False), action='store_true', help = "Force use ZLUDA, AMD GPUs only, default: %(default)s")
+ group_compute.add_argument("--use-xformers", default = os.environ.get("SD_USEXFORMERS",False), action='store_true', help="Force use xFormers cross-optimization, default: %(default)s")
+
+ group_diag = parser.add_argument_group('Diagnostics')
+ group_diag.add_argument('--safe', default = os.environ.get("SD_SAFE",False), action='store_true', help = "Run in safe mode with no user extensions")
+ group_diag.add_argument('--experimental', default = os.environ.get("SD_EXPERIMENTAL",False), action='store_true', help = "Allow unsupported versions of libraries, default: %(default)s")
+ group_diag.add_argument('--test', default = os.environ.get("SD_TEST",False), action='store_true', help = "Run test only and exit")
+ group_diag.add_argument('--version', default = False, action='store_true', help = "Print version information")
+ group_diag.add_argument('--ignore', default = os.environ.get("SD_IGNORE",False), action='store_true', help = "Ignore any errors and attempt to continue")
+
+ group_log = parser.add_argument_group('Logging')
+ group_log.add_argument("--log", type=str, default=os.environ.get("SD_LOG", None), help="Set log file, default: %(default)s")
+ group_log.add_argument('--debug', default = os.environ.get("SD_DEBUG",False), action='store_true', help = "Run installer with debug logging, default: %(default)s")
+ group_log.add_argument("--profile", default=os.environ.get("SD_PROFILE", False), action='store_true', help="Run profiler, default: %(default)s")
+ group_log.add_argument('--docs', default=os.environ.get("SD_DOCS", False), action='store_true', help = "Mount API docs, default: %(default)s")
+ group_log.add_argument("--api-log", default=os.environ.get("SD_APILOG", False), action='store_true', help="Enable logging of all API requests, default: %(default)s")
def parse_args(parser):
diff --git a/javascript/changelog.js b/javascript/changelog.js
new file mode 100644
index 000000000..80c9956b8
--- /dev/null
+++ b/javascript/changelog.js
@@ -0,0 +1,77 @@
+let changelogElements = [];
+
+const getAllChildren = (el) => {
+ const elements = [];
+ for (let i = 0; i < el.children.length; i++) {
+ elements.push(el.children[i]);
+ if (el.children[i].children.length) elements.push(...getAllChildren(el.children[i]));
+ }
+ return elements;
+};
+
+function getText(el) {
+ let text = '';
+ el.childNodes.forEach((node) => {
+ if (node.nodeType === Node.TEXT_NODE) text += node.nodeValue;
+ });
+ return text.trim();
+}
+
+let currentElement = -1;
+
+function changelogNavigate(found) {
+ const result = gradioApp().getElementById('changelog_result');
+ result.innerHTML = '';
+ const text = document.createElement('p');
+
+ const onPrev = () => {
+ if (currentElement > 0) {
+ currentElement--;
+ found[currentElement].scrollIntoView();
+ text.innerHTML = `   search item ${currentElement + 1} of ${found.length}`;
+ }
+ };
+ const onNext = () => {
+ if (currentElement < found.length - 1) {
+ currentElement++;
+ found[currentElement].scrollIntoView();
+ text.innerHTML = `   search item ${currentElement + 1} of ${found.length}`;
+ }
+ };
+
+ const prev = document.createElement('p');
+ prev.innerHTML = ' ⇦ ';
+ prev.className = 'changelog_arrow';
+ prev.onclick = onPrev;
+ prev.title = 'Search previous';
+ result.appendChild(prev);
+
+ const next = document.createElement('p');
+ next.innerHTML = ' ⇨ ';
+ next.className = 'changelog_arrow';
+ next.title = 'Search next';
+ next.onclick = onNext;
+ result.appendChild(next);
+
+ text.innerHTML = `   found ${found.length} items`;
+ result.appendChild(text);
+}
+
+async function initChangelog() {
+ const search = gradioApp().querySelector('#changelog_search > label> textarea');
+ const md = gradioApp().getElementById('changelog_markdown');
+ const searchChangelog = async (e) => {
+ if (changelogElements.length < 100) changelogElements = getAllChildren(md);
+ const found = [];
+ for (const el of changelogElements) {
+ if (search.value.length > 1 && getText(el).toLowerCase().includes(search.value.toLowerCase())) {
+ el.classList.add('changelog_highlight');
+ found.push(el);
+ } else {
+ el.classList.remove('changelog_highlight');
+ }
+ }
+ changelogNavigate(found);
+ };
+ search.addEventListener('keyup', searchChangelog);
+}
diff --git a/javascript/script.js b/javascript/script.js
index 6f52f4a3d..104567dd7 100644
--- a/javascript/script.js
+++ b/javascript/script.js
@@ -133,19 +133,23 @@ document.addEventListener('DOMContentLoaded', () => {
});
/**
- * Add a ctrl+enter as a shortcut to start a generation
+ * Add a listener to the document for keydown events
*/
document.addEventListener('keydown', (e) => {
- let handled = false;
- if (e.key !== undefined) {
- if ((e.key === 'Enter' && (e.metaKey || e.ctrlKey || e.altKey))) handled = true;
- } else if (e.keyCode !== undefined) {
- if ((e.keyCode === 13 && (e.metaKey || e.ctrlKey || e.altKey))) handled = true;
- }
- if (handled) {
- const button = getUICurrentTabContent().querySelector('button[id$=_generate]');
- if (button) button.click();
+ let elem;
+ if (e.key === 'Escape') elem = getUICurrentTabContent().querySelector('button[id$=_interrupt]');
+ if (e.key === 'Enter' && e.ctrlKey) elem = getUICurrentTabContent().querySelector('button[id$=_generate]');
+ if (e.key === 'Backspace' && e.ctrlKey) elem = getUICurrentTabContent().querySelector('button[id$=_reprocess]');
+ if (e.key === ' ' && e.ctrlKey) elem = getUICurrentTabContent().querySelector('button[id$=_extra_networks_btn]');
+ if (e.key === 's' && e.ctrlKey) elem = getUICurrentTabContent().querySelector('button[id^=save_]');
+ if (e.key === 'Insert' && e.ctrlKey) elem = getUICurrentTabContent().querySelector('button[id^=save_]');
+ if (e.key === 'Delete' && e.ctrlKey) elem = getUICurrentTabContent().querySelector('button[id^=delete_]');
+ // if (e.key === 'm' && e.ctrlKey) elem = gradioApp().getElementById('setting_sd_model_checkpoint');
+ if (elem) {
e.preventDefault();
+ log('hotkey', { key: e.key, meta: e.metaKey, ctrl: e.ctrlKey, alt: e.altKey }, elem?.id, elem.nodeName);
+ if (elem.nodeName === 'BUTTON') elem.click();
+ else elem.focus();
}
});
diff --git a/javascript/sdnext.css b/javascript/sdnext.css
index cbd3cac83..1b2a6b3c8 100644
--- a/javascript/sdnext.css
+++ b/javascript/sdnext.css
@@ -319,6 +319,13 @@ div:has(>#tab-gallery-folders) { flex-grow: 0 !important; background-color: var(
.gallery-sort { background: var(--input-background-fill) !important; margin: 0 !important; padding: 6px !important; }
.gallery-sort:hover { background: var(--button-primary-background-fill-hover) !important; }
+/* changelog */
+#changelog_markdown { max-height: 55vh; margin-top: 1em; }
+#changelog_result { display: flex; margin-left: 1em; align-items: center; }
+.changelog_arrow { font-size: 2em; padding: 0.1em; cursor: pointer; height: 1em; background-color: var(--button-secondary-background-fill); }
+.changelog_arrow:hover { background-color: var(--button-primary-border-color-hover); }
+.changelog_highlight { background-color: var(--color-warning); }
+
/* loader */
.splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 1000; display: block; text-align: center; }
.motd { margin-top: 2em; color: var(--body-text-color-subdued); font-family: monospace; font-variant: all-petite-caps; }
diff --git a/javascript/startup.js b/javascript/startup.js
index 81bfa1a61..637a8d15e 100644
--- a/javascript/startup.js
+++ b/javascript/startup.js
@@ -16,6 +16,7 @@ async function initStartup() {
initImageViewer();
initGallery();
initiGenerationParams();
+ initChangelog();
setupControlUI();
// reconnect server session
diff --git a/launch.py b/launch.py
index 222c24fe2..8379a5d6d 100755
--- a/launch.py
+++ b/launch.py
@@ -164,7 +164,6 @@ def start_server(immediate=True, server=None):
module_spec = importlib.util.spec_from_file_location('webui', 'webui.py')
server = importlib.util.module_from_spec(module_spec)
installer.log.debug(f'Starting module: {server}')
- get_custom_args()
module_spec.loader.exec_module(server)
uvicorn = None
if args.test:
@@ -209,6 +208,7 @@ def main():
installer.log.info('Skipping GIT operations')
installer.check_version()
installer.log.info(f'Platform: {installer.print_dict(installer.get_platform())}')
+ installer.log.info(f'Args: {sys.argv[1:]}')
if not args.skip_env:
installer.set_environment()
if args.uv:
@@ -246,6 +246,7 @@ def main():
installer.log.warning(f'See log file for more details: {installer.log_file}')
installer.extensions_preload(parser) # adds additional args from extensions
args = installer.parse_args(parser)
+ get_custom_args()
uv, instance = start_server(immediate=True, server=None)
while True:
diff --git a/modules/api/process.py b/modules/api/process.py
index dcb3b6487..f50d58381 100644
--- a/modules/api/process.py
+++ b/modules/api/process.py
@@ -113,10 +113,9 @@ class APIProcess():
images = []
scores = []
with self.queue_lock:
- yolo.load()
- faces = yolo.predict(image)
+ faces = yolo.predict('face-yolo8n', image)
for face in faces:
- images.append(encode_pil_to_base64(face.face))
+ images.append(encode_pil_to_base64(face.item))
scores.append(face.score)
shared.state.end(api=False)
return ResFace(images=images, scores=scores)
diff --git a/modules/cmd_args.py b/modules/cmd_args.py
index afbe239fe..752ad02c0 100644
--- a/modules/cmd_args.py
+++ b/modules/cmd_args.py
@@ -2,104 +2,134 @@ import os
import argparse
from modules.paths import data_path
+
parser = argparse.ArgumentParser(description="SD.Next", conflict_handler='resolve', epilog='For other options see UI Settings page', prog='', add_help=True, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=55, indent_increment=2, width=200))
parser._optionals = parser.add_argument_group('Other options') # pylint: disable=protected-access
-group = parser.add_argument_group('Server options')
-
-# main server args
-group.add_argument("--config", type=str, default=os.environ.get("SD_CONFIG", os.path.join(data_path, 'config.json')), help="Use specific server configuration file, default: %(default)s")
-group.add_argument("--ui-config", type=str, default=os.environ.get("SD_UICONFIG", os.path.join(data_path, 'ui-config.json')), help="Use specific UI configuration file, default: %(default)s")
-group.add_argument("--medvram", default=os.environ.get("SD_MEDVRAM", False), action='store_true', help="Split model stages and keep only active part in VRAM, default: %(default)s")
-group.add_argument("--lowvram", default=os.environ.get("SD_LOWVRAM", False), action='store_true', help="Split model components and keep only active part in VRAM, default: %(default)s")
-group.add_argument("--ckpt", type=str, default=os.environ.get("SD_MODEL", None), help="Path to model checkpoint to load immediately, default: %(default)s")
-group.add_argument('--vae', type=str, default=os.environ.get("SD_VAE", None), help='Path to VAE checkpoint to load immediately, default: %(default)s')
-group.add_argument("--data-dir", type=str, default=os.environ.get("SD_DATADIR", ''), help="Base path where all user data is stored, default: %(default)s")
-group.add_argument("--models-dir", type=str, default=os.environ.get("SD_MODELSDIR", 'models'), help="Base path where all models are stored, default: %(default)s",)
-group.add_argument("--allow-code", default=os.environ.get("SD_ALLOWCODE", False), action='store_true', help="Allow custom script execution, default: %(default)s")
-group.add_argument("--share", default=os.environ.get("SD_SHARE", False), action='store_true', help="Enable UI accessible through Gradio site, default: %(default)s")
-group.add_argument("--insecure", default=os.environ.get("SD_INSECURE", False), action='store_true', help="Enable extensions tab regardless of other options, default: %(default)s")
-group.add_argument("--use-cpu", nargs='+', default=[], type=str.lower, help="Force use CPU for specified modules, default: %(default)s")
-group.add_argument("--listen", default=os.environ.get("SD_LISTEN", False), action='store_true', help="Launch web server using public IP address, default: %(default)s")
-group.add_argument("--port", type=int, default=os.environ.get("SD_PORT", 7860), help="Launch web server with given server port, default: %(default)s")
-group.add_argument("--freeze", default=os.environ.get("SD_FREEZE", False), action='store_true', help="Disable editing settings")
-group.add_argument("--auth", type=str, default=os.environ.get("SD_AUTH", None), help='Set access authentication like "user:pwd,user:pwd""')
-group.add_argument("--auth-file", type=str, default=os.environ.get("SD_AUTHFILE", None), help='Set access authentication using file, default: %(default)s')
-group.add_argument("--autolaunch", default=os.environ.get("SD_AUTOLAUNCH", False), action='store_true', help="Open the UI URL in the system's default browser upon launch")
-group.add_argument('--docs', default=os.environ.get("SD_DOCS", False), action='store_true', help = "Mount API docs, default: %(default)s")
-group.add_argument('--api-only', default=os.environ.get("SD_APIONLY", False), action='store_true', help = "Run in API only mode without starting UI")
-group.add_argument("--device-id", type=str, default=os.environ.get("SD_DEVICEID", None), help="Select the default CUDA device to use, default: %(default)s")
-group.add_argument("--cors-origins", type=str, default=os.environ.get("SD_CORSORIGINS", None), help="Allowed CORS origins as comma-separated list, default: %(default)s")
-group.add_argument("--cors-regex", type=str, default=os.environ.get("SD_CORSREGEX", None), help="Allowed CORS origins as regular expression, default: %(default)s")
-group.add_argument("--tls-keyfile", type=str, default=os.environ.get("SD_TLSKEYFILE", None), help="Enable TLS and specify key file, default: %(default)s")
-group.add_argument("--tls-certfile", type=str, default=os.environ.get("SD_TLSCERTFILE", None), help="Enable TLS and specify cert file, default: %(default)s")
-group.add_argument("--tls-selfsign", action="store_true", default=os.environ.get("SD_TLSSELFSIGN", False), help="Enable TLS with self-signed certificates, default: %(default)s")
-group.add_argument("--server-name", type=str, default=os.environ.get("SD_SERVERNAME", None), help="Sets hostname of server, default: %(default)s")
-group.add_argument("--no-hashing", default=os.environ.get("SD_NOHASHING", False), action='store_true', help="Disable hashing of checkpoints, default: %(default)s")
-group.add_argument("--no-metadata", default=os.environ.get("SD_NOMETADATA", False), action='store_true', help="Disable reading of metadata from models, default: %(default)s")
-group.add_argument("--profile", default=os.environ.get("SD_PROFILE", False), action='store_true', help="Run profiler, default: %(default)s")
-group.add_argument("--disable-queue", default=os.environ.get("SD_DISABLEQUEUE", False), action='store_true', help="Disable queues, default: %(default)s")
-group.add_argument('--debug', default=os.environ.get("SD_DEBUG", False), action='store_true', help = "Run installer with debug logging, default: %(default)s")
-group.add_argument('--use-directml', default=os.environ.get("SD_USEDIRECTML", False), action='store_true', help = "Use DirectML if no compatible GPU is detected, default: %(default)s")
-group.add_argument('--use-zluda', default=os.environ.get("SD_USEZLUDA", False), action='store_true', help = "Force use ZLUDA, AMD GPUs only, default: %(default)s")
-group.add_argument("--use-openvino", default=os.environ.get("SD_USEOPENVINO", False), action='store_true', help="Use Intel OpenVINO backend, default: %(default)s")
-group.add_argument("--use-ipex", default=os.environ.get("SD_USEIPX", False), action='store_true', help="Force use Intel OneAPI XPU backend, default: %(default)s")
-group.add_argument("--use-cuda", default=os.environ.get("SD_USECUDA", False), action='store_true', help="Force use nVidia CUDA backend, default: %(default)s")
-group.add_argument("--use-rocm", default=os.environ.get("SD_USEROCM", False), action='store_true', help="Force use AMD ROCm backend, default: %(default)s")
-group.add_argument('--subpath', type=str, default=os.environ.get("SD_SUBPATH", None), help='Customize the URL subpath for usage with reverse proxy')
-group.add_argument('--backend', type=str, default=os.environ.get("SD_BACKEND", None), choices=['original', 'diffusers'], required=False, help='force model pipeline type')
-group.add_argument('--theme', type=str, default=os.environ.get("SD_THEME", None), help='Override UI theme')
-# removed args are added here as hidden in fixed format for compatbility reasons
-group.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui
-group.add_argument("--ui-settings-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'config.json'))
-group.add_argument("--ui-config-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'ui-config.json'))
-group.add_argument("--hide-ui-dir-config", action='store_true', help=argparse.SUPPRESS, default=False)
-group.add_argument("--theme", type=str, help=argparse.SUPPRESS, default=None)
-group.add_argument("--disable-console-progressbars", action='store_true', help=argparse.SUPPRESS, default=True)
-group.add_argument("--disable-safe-unpickle", action='store_true', help=argparse.SUPPRESS, default=True)
-group.add_argument("--lowram", action='store_true', help=argparse.SUPPRESS)
-group.add_argument("--disable-extension-access", default=False, action='store_true', help=argparse.SUPPRESS)
-group.add_argument("--allowed-paths", nargs='+', default=[], type=str, required=False, help="add additional paths to paths allowed for web access")
-group.add_argument("--api", help=argparse.SUPPRESS, default=True)
-group.add_argument("--api-auth", type=str, help=argparse.SUPPRESS, default=None)
+def main_args():
+ # main server args
+ group_config = parser.add_argument_group('Configuration')
+ group_config.add_argument('--backend', type=str, default=os.environ.get("SD_BACKEND", None), choices=['original', 'diffusers'], required=False, help='force model pipeline type')
+ group_config.add_argument("--config", type=str, default=os.environ.get("SD_CONFIG", os.path.join(data_path, 'config.json')), help="Use specific server configuration file, default: %(default)s")
+ group_config.add_argument("--ui-config", type=str, default=os.environ.get("SD_UICONFIG", os.path.join(data_path, 'ui-config.json')), help="Use specific UI configuration file, default: %(default)s")
+ group_config.add_argument("--medvram", default=os.environ.get("SD_MEDVRAM", False), action='store_true', help="Split model stages and keep only active part in VRAM, default: %(default)s")
+ group_config.add_argument("--lowvram", default=os.environ.get("SD_LOWVRAM", False), action='store_true', help="Split model components and keep only active part in VRAM, default: %(default)s")
+ group_config.add_argument("--freeze", default=os.environ.get("SD_FREEZE", False), action='store_true', help="Disable editing settings")
+
+ group_paths = parser.add_argument_group('Paths')
+ group_paths.add_argument("--ckpt", type=str, default=os.environ.get("SD_MODEL", None), help="Path to model checkpoint to load immediately, default: %(default)s")
+ group_paths.add_argument("--data-dir", type=str, default=os.environ.get("SD_DATADIR", ''), help="Base path where all user data is stored, default: %(default)s")
+ group_paths.add_argument("--models-dir", type=str, default=os.environ.get("SD_MODELSDIR", 'models'), help="Base path where all models are stored, default: %(default)s",)
+
+ group_diag = parser.add_argument_group('Diagnostics')
+ group_diag.add_argument("--no-hashing", default=os.environ.get("SD_NOHASHING", False), action='store_true', help="Disable hashing of checkpoints, default: %(default)s")
+ group_diag.add_argument("--no-metadata", default=os.environ.get("SD_NOMETADATA", False), action='store_true', help="Disable reading of metadata from models, default: %(default)s")
+ group_diag.add_argument("--profile", default=os.environ.get("SD_PROFILE", False), action='store_true', help="Run profiler, default: %(default)s")
+ group_diag.add_argument("--disable-queue", default=os.environ.get("SD_DISABLEQUEUE", False), action='store_true', help="Disable queues, default: %(default)s")
+ group_diag.add_argument('--debug', default=os.environ.get("SD_DEBUG", False), action='store_true', help = "Run installer with debug logging, default: %(default)s")
+
+ group_compute = parser.add_argument_group('Compute Engine')
+ group_compute.add_argument('--use-directml', default=os.environ.get("SD_USEDIRECTML", False), action='store_true', help = "Use DirectML if no compatible GPU is detected, default: %(default)s")
+ group_compute.add_argument('--use-zluda', default=os.environ.get("SD_USEZLUDA", False), action='store_true', help = "Force use ZLUDA, AMD GPUs only, default: %(default)s")
+ group_compute.add_argument("--use-openvino", default=os.environ.get("SD_USEOPENVINO", False), action='store_true', help="Use Intel OpenVINO backend, default: %(default)s")
+ group_compute.add_argument("--use-ipex", default=os.environ.get("SD_USEIPX", False), action='store_true', help="Force use Intel OneAPI XPU backend, default: %(default)s")
+ group_compute.add_argument("--use-cuda", default=os.environ.get("SD_USECUDA", False), action='store_true', help="Force use nVidia CUDA backend, default: %(default)s")
+ group_compute.add_argument("--use-rocm", default=os.environ.get("SD_USEROCM", False), action='store_true', help="Force use AMD ROCm backend, default: %(default)s")
+ group_diag.add_argument("--device-id", type=str, default=os.environ.get("SD_DEVICEID", None), help="Select the default CUDA device to use, default: %(default)s")
+
+ group_http = parser.add_argument_group('HTTP')
+ group_http.add_argument('--theme', type=str, default=os.environ.get("SD_THEME", None), help='Override UI theme')
+ group_http.add_argument("--server-name", type=str, default=os.environ.get("SD_SERVERNAME", None), help="Sets hostname of server, default: %(default)s")
+ group_http.add_argument("--tls-keyfile", type=str, default=os.environ.get("SD_TLSKEYFILE", None), help="Enable TLS and specify key file, default: %(default)s")
+ group_http.add_argument("--tls-certfile", type=str, default=os.environ.get("SD_TLSCERTFILE", None), help="Enable TLS and specify cert file, default: %(default)s")
+ group_http.add_argument("--tls-selfsign", action="store_true", default=os.environ.get("SD_TLSSELFSIGN", False), help="Enable TLS with self-signed certificates, default: %(default)s")
+ group_http.add_argument("--cors-origins", type=str, default=os.environ.get("SD_CORSORIGINS", None), help="Allowed CORS origins as comma-separated list, default: %(default)s")
+ group_http.add_argument("--cors-regex", type=str, default=os.environ.get("SD_CORSREGEX", None), help="Allowed CORS origins as regular expression, default: %(default)s")
+ group_http.add_argument('--subpath', type=str, default=os.environ.get("SD_SUBPATH", None), help='Customize the URL subpath for usage with reverse proxy')
+ group_http.add_argument("--autolaunch", default=os.environ.get("SD_AUTOLAUNCH", False), action='store_true', help="Open the UI URL in the system's default browser upon launch")
+ group_http.add_argument('--docs', default=os.environ.get("SD_DOCS", False), action='store_true', help = "Mount API docs, default: %(default)s")
+ group_http.add_argument("--auth", type=str, default=os.environ.get("SD_AUTH", None), help='Set access authentication like "user:pwd,user:pwd""')
+ group_http.add_argument("--auth-file", type=str, default=os.environ.get("SD_AUTHFILE", None), help='Set access authentication using file, default: %(default)s')
+ group_http.add_argument('--api-only', default=os.environ.get("SD_APIONLY", False), action='store_true', help = "Run in API only mode without starting UI")
+ group_http.add_argument("--allowed-paths", nargs='+', default=[], type=str, required=False, help="add additional paths to paths allowed for web access")
+ group_http.add_argument("--share", default=os.environ.get("SD_SHARE", False), action='store_true', help="Enable UI accessible through Gradio site, default: %(default)s")
+ group_http.add_argument("--insecure", default=os.environ.get("SD_INSECURE", False), action='store_true', help="Enable extensions tab regardless of other options, default: %(default)s")
+ group_http.add_argument("--listen", default=os.environ.get("SD_LISTEN", False), action='store_true', help="Launch web server using public IP address, default: %(default)s")
+ group_http.add_argument("--port", type=int, default=os.environ.get("SD_PORT", 7860), help="Launch web server with given server port, default: %(default)s")
-def compatibility_args(opts, args):
+def compatibility_args():
+ group_compat = parser.add_argument_group('Compatibility options')
+ # removed args are added here as hidden in fixed format for compatbility reasons
+ group_compat.add_argument("--allow-code", default=os.environ.get("SD_ALLOWCODE", False), action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--use-cpu", nargs='+', default=[], type=str.lower, help=argparse.SUPPRESS)
+ group_compat.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui
+ group_compat.add_argument('--vae', type=str, default=os.environ.get("SD_VAE", None), help=argparse.SUPPRESS)
+ group_compat.add_argument("--ui-settings-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'config.json'))
+ group_compat.add_argument("--ui-config-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'ui-config.json'))
+ group_compat.add_argument("--hide-ui-dir-config", action='store_true', help=argparse.SUPPRESS, default=False)
+ group_compat.add_argument("--theme", type=str, help=argparse.SUPPRESS, default=None)
+ group_compat.add_argument("--disable-console-progressbars", action='store_true', help=argparse.SUPPRESS, default=True)
+ group_compat.add_argument("--disable-safe-unpickle", action='store_true', help=argparse.SUPPRESS, default=True)
+ group_compat.add_argument("--lowram", action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--disable-extension-access", default=False, action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--api", help=argparse.SUPPRESS, default=True)
+ group_compat.add_argument("--api-auth", type=str, help=argparse.SUPPRESS, default=None)
+
+
+def settings_args(opts, args):
+ group_compat = parser.add_argument_group('Compatibility options')
+ # removed args are added here as hidden in fixed format for compatbility reasons
+ group_compat.add_argument("--allow-code", default=os.environ.get("SD_ALLOWCODE", False), action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--use-cpu", nargs='+', default=[], type=str.lower, help=argparse.SUPPRESS)
+ group_compat.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui
+ group_compat.add_argument('--vae', type=str, default=os.environ.get("SD_VAE", None), help=argparse.SUPPRESS)
+ group_compat.add_argument("--ui-settings-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'config.json'))
+ group_compat.add_argument("--ui-config-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'ui-config.json'))
+ group_compat.add_argument("--hide-ui-dir-config", action='store_true', help=argparse.SUPPRESS, default=False)
+ group_compat.add_argument("--theme", type=str, help=argparse.SUPPRESS, default=None)
+ group_compat.add_argument("--disable-console-progressbars", action='store_true', help=argparse.SUPPRESS, default=True)
+ group_compat.add_argument("--disable-safe-unpickle", action='store_true', help=argparse.SUPPRESS, default=True)
+ group_compat.add_argument("--lowram", action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--disable-extension-access", default=False, action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--allowed-paths", nargs='+', default=[], type=str, required=False, help="add additional paths to paths allowed for web access")
+ group_compat.add_argument("--api", help=argparse.SUPPRESS, default=True)
+ group_compat.add_argument("--api-auth", type=str, help=argparse.SUPPRESS, default=None)
# removed args that have been moved to opts are added here as hidden with default values as defined in opts
- group.add_argument("--ckpt-dir", type=str, help=argparse.SUPPRESS, default=opts.ckpt_dir)
- group.add_argument("--vae-dir", type=str, help=argparse.SUPPRESS, default=opts.vae_dir)
- group.add_argument("--embeddings-dir", type=str, help=argparse.SUPPRESS, default=opts.embeddings_dir)
- group.add_argument("--embeddings-templates-dir", type=str, help=argparse.SUPPRESS, default=opts.embeddings_templates_dir)
- group.add_argument("--hypernetwork-dir", type=str, help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
- group.add_argument("--codeformer-models-path", type=str, help=argparse.SUPPRESS, default=opts.codeformer_models_path)
- group.add_argument("--gfpgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.gfpgan_models_path)
- group.add_argument("--esrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.esrgan_models_path)
- group.add_argument("--bsrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.bsrgan_models_path)
- group.add_argument("--realesrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.realesrgan_models_path)
- group.add_argument("--scunet-models-path", help=argparse.SUPPRESS, default=opts.scunet_models_path)
- group.add_argument("--swinir-models-path", help=argparse.SUPPRESS, default=opts.swinir_models_path)
- group.add_argument("--ldsr-models-path", help=argparse.SUPPRESS, default=opts.ldsr_models_path)
- group.add_argument("--clip-models-path", type=str, help=argparse.SUPPRESS, default=opts.clip_models_path)
- group.add_argument("--opt-channelslast", help=argparse.SUPPRESS, action='store_true', default=opts.opt_channelslast)
- group.add_argument("--xformers", default=(opts.cross_attention_optimization == "xFormers"), action='store_true', help=argparse.SUPPRESS)
- group.add_argument("--disable-nan-check", help=argparse.SUPPRESS, action='store_true', default=opts.disable_nan_check)
- group.add_argument("--rollback-vae", help=argparse.SUPPRESS, default=opts.rollback_vae)
- group.add_argument("--no-half", help=argparse.SUPPRESS, action='store_true', default=opts.no_half)
- group.add_argument("--no-half-vae", help=argparse.SUPPRESS, action='store_true', default=opts.no_half_vae)
- group.add_argument("--precision", help=argparse.SUPPRESS, default=opts.precision)
- group.add_argument("--sub-quad-q-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_q_chunk_size)
- group.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size)
- group.add_argument("--sub-quad-chunk-threshold", help=argparse.SUPPRESS, default=opts.sub_quad_chunk_threshold)
- group.add_argument("--lora-dir", help=argparse.SUPPRESS, default=opts.lora_dir)
- group.add_argument("--lyco-dir", help=argparse.SUPPRESS, default=opts.lyco_dir)
- group.add_argument("--embeddings-dir", help=argparse.SUPPRESS, default=opts.embeddings_dir)
- group.add_argument("--hypernetwork-dir", help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
- group.add_argument("--lyco-patch-lora", help=argparse.SUPPRESS, action='store_true', default=False)
- group.add_argument("--lyco-debug", help=argparse.SUPPRESS, action='store_true', default=False)
- group.add_argument("--enable-console-prompts", help=argparse.SUPPRESS, action='store_true', default=False)
- group.add_argument("--safe", help=argparse.SUPPRESS, action='store_true', default=False)
- group.add_argument("--use-xformers", help=argparse.SUPPRESS, action='store_true', default=False)
+ group_compat.add_argument("--ckpt-dir", type=str, help=argparse.SUPPRESS, default=opts.ckpt_dir)
+ group_compat.add_argument("--vae-dir", type=str, help=argparse.SUPPRESS, default=opts.vae_dir)
+ group_compat.add_argument("--embeddings-dir", type=str, help=argparse.SUPPRESS, default=opts.embeddings_dir)
+ group_compat.add_argument("--embeddings-templates-dir", type=str, help=argparse.SUPPRESS, default=opts.embeddings_templates_dir)
+ group_compat.add_argument("--hypernetwork-dir", type=str, help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
+ group_compat.add_argument("--codeformer-models-path", type=str, help=argparse.SUPPRESS, default=opts.codeformer_models_path)
+ group_compat.add_argument("--gfpgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.gfpgan_models_path)
+ group_compat.add_argument("--esrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.esrgan_models_path)
+ group_compat.add_argument("--bsrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.bsrgan_models_path)
+ group_compat.add_argument("--realesrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.realesrgan_models_path)
+ group_compat.add_argument("--scunet-models-path", help=argparse.SUPPRESS, default=opts.scunet_models_path)
+ group_compat.add_argument("--swinir-models-path", help=argparse.SUPPRESS, default=opts.swinir_models_path)
+ group_compat.add_argument("--ldsr-models-path", help=argparse.SUPPRESS, default=opts.ldsr_models_path)
+ group_compat.add_argument("--clip-models-path", type=str, help=argparse.SUPPRESS, default=opts.clip_models_path)
+ group_compat.add_argument("--opt-channelslast", help=argparse.SUPPRESS, action='store_true', default=opts.opt_channelslast)
+ group_compat.add_argument("--xformers", default=(opts.cross_attention_optimization == "xFormers"), action='store_true', help=argparse.SUPPRESS)
+ group_compat.add_argument("--disable-nan-check", help=argparse.SUPPRESS, action='store_true', default=opts.disable_nan_check)
+ group_compat.add_argument("--rollback-vae", help=argparse.SUPPRESS, default=opts.rollback_vae)
+ group_compat.add_argument("--no-half", help=argparse.SUPPRESS, action='store_true', default=opts.no_half)
+ group_compat.add_argument("--no-half-vae", help=argparse.SUPPRESS, action='store_true', default=opts.no_half_vae)
+ group_compat.add_argument("--precision", help=argparse.SUPPRESS, default=opts.precision)
+ group_compat.add_argument("--sub-quad-q-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_q_chunk_size)
+ group_compat.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size)
+ group_compat.add_argument("--sub-quad-chunk-threshold", help=argparse.SUPPRESS, default=opts.sub_quad_chunk_threshold)
+ group_compat.add_argument("--lora-dir", help=argparse.SUPPRESS, default=opts.lora_dir)
+ group_compat.add_argument("--lyco-dir", help=argparse.SUPPRESS, default=opts.lyco_dir)
+ group_compat.add_argument("--embeddings-dir", help=argparse.SUPPRESS, default=opts.embeddings_dir)
+ group_compat.add_argument("--hypernetwork-dir", help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
+ group_compat.add_argument("--lyco-patch-lora", help=argparse.SUPPRESS, action='store_true', default=False)
+ group_compat.add_argument("--lyco-debug", help=argparse.SUPPRESS, action='store_true', default=False)
+ group_compat.add_argument("--enable-console-prompts", help=argparse.SUPPRESS, action='store_true', default=False)
+ group_compat.add_argument("--safe", help=argparse.SUPPRESS, action='store_true', default=False)
+ group_compat.add_argument("--use-xformers", help=argparse.SUPPRESS, action='store_true', default=False)
# removed opts are added here with fixed values for compatibility reasons
opts.use_old_emphasis_implementation = False
@@ -126,3 +156,7 @@ def compatibility_args(opts, args):
args = parser.parse_args()
return args
+
+
+main_args()
+compatibility_args()
diff --git a/modules/control/run.py b/modules/control/run.py
index e3c7bbf76..74bab35c4 100644
--- a/modules/control/run.py
+++ b/modules/control/run.py
@@ -564,8 +564,8 @@ def control_run(state: str = '',
return [], '', '', 'Reference mode without image'
elif unit_type == 'controlnet' and has_models:
if input_type == 0: # Control only
- if shared.sd_model_type == 'f1' and 'control_image' not in p.task_args:
- p.task_args['control_image'] = p.init_images # flux controlnet mandates this
+ if shared.sd_model_type in ['f1', 'sd3'] and 'control_image' not in p.task_args:
+ p.task_args['control_image'] = p.init_images # some controlnets mandate this
p.task_args['strength'] = p.denoising_strength
elif input_type == 1: # Init image same as control
p.task_args['control_image'] = p.init_images # switch image and control_image
diff --git a/modules/control/units/controlnet.py b/modules/control/units/controlnet.py
index 236892eb5..48a3d440d 100644
--- a/modules/control/units/controlnet.py
+++ b/modules/control/units/controlnet.py
@@ -1,7 +1,7 @@
import os
import time
from typing import Union
-from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline, FluxPipeline, ControlNetModel
+from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline, FluxPipeline, StableDiffusion3Pipeline, ControlNetModel
from modules.control.units import detect
from modules.shared import log, opts, listdir
from modules import errors, sd_models, devices, model_quant
@@ -50,7 +50,6 @@ predefined_sdxl = {
'Depth Zoe XL': 'diffusers/controlnet-zoe-depth-sdxl-1.0',
'Depth Mid XL': 'diffusers/controlnet-depth-sdxl-1.0-mid',
'OpenPose XL': 'thibaud/controlnet-openpose-sdxl-1.0/bin',
- # 'OpenPose XL': 'thibaud/controlnet-openpose-sdxl-1.0/OpenPoseXL2.safetensors',
'Xinsir Union XL': 'xinsir/controlnet-union-sdxl-1.0',
'Xinsir OpenPose XL': 'xinsir/controlnet-openpose-sdxl-1.0',
'Xinsir Canny XL': 'xinsir/controlnet-canny-sdxl-1.0',
@@ -79,11 +78,20 @@ predefined_f1 = {
"XLabs-AI Depth": 'XLabs-AI/flux-controlnet-depth-diffusers',
"XLabs-AI HED": 'XLabs-AI/flux-controlnet-hed-diffusers'
}
+predefined_sd3 = {
+ "InstantX Canny": 'InstantX/SD3-Controlnet-Canny',
+ "InstantX Pose": 'InstantX/SD3-Controlnet-Pose',
+ "InstantX Depth": 'InstantX/SD3-Controlnet-Depth',
+ "InstantX Tile": 'InstantX/SD3-Controlnet-Tile',
+ "Alimama Inpainting": 'alimama-creative/SD3-Controlnet-Inpainting',
+ "Alimama SoftEdge": 'alimama-creative/SD3-Controlnet-Softedge',
+}
models = {}
all_models = {}
all_models.update(predefined_sd15)
all_models.update(predefined_sdxl)
all_models.update(predefined_f1)
+all_models.update(predefined_sd3)
cache_dir = 'models/control/controlnet'
@@ -118,9 +126,11 @@ def list_models(refresh=False):
models = ['None'] + list(predefined_sd15) + sorted(find_models())
elif modules.shared.sd_model_type == 'f1':
models = ['None'] + list(predefined_f1) + sorted(find_models())
+ elif modules.shared.sd_model_type == 'sd3':
+ models = ['None'] + list(predefined_sd3) + sorted(find_models())
else:
log.warning(f'Control {what} model list failed: unknown model type')
- models = ['None'] + sorted(predefined_sd15) + sorted(predefined_sdxl) + sorted(find_models())
+ models = ['None'] + sorted(predefined_sd15) + sorted(predefined_sdxl) + sorted(predefined_f1) + sorted(predefined_sd3) + sorted(find_models())
debug(f'Control list {what}: path={cache_dir} models={models}')
return models
@@ -151,6 +161,8 @@ class ControlNet():
from diffusers import ControlNetModel as model_class # pylint: disable=reimported # sdxl shares same model class
elif modules.shared.sd_model_type == 'f1':
from diffusers import FluxControlNetModel as model_class
+ elif modules.shared.sd_model_type == 'sd3':
+ from diffusers import SD3ControlNetModel as model_class
else:
log.error(f'Control {what}: type={modules.shared.sd_model_type} unsupported model')
return None
@@ -247,7 +259,11 @@ class ControlNet():
class ControlNetPipeline():
- def __init__(self, controlnet: Union[ControlNetModel, list[ControlNetModel]], pipeline: Union[StableDiffusionXLPipeline, StableDiffusionPipeline, FluxPipeline], dtype = None):
+ def __init__(self,
+ controlnet: Union[ControlNetModel, list[ControlNetModel]],
+ pipeline: Union[StableDiffusionXLPipeline, StableDiffusionPipeline, FluxPipeline, StableDiffusion3Pipeline],
+ dtype = None,
+ ):
t0 = time.time()
self.orig_pipeline = pipeline
self.pipeline = None
@@ -293,6 +309,20 @@ class ControlNetPipeline():
scheduler=pipeline.scheduler,
controlnet=controlnet, # can be a list
)
+ elif detect.is_sd3(pipeline):
+ from diffusers import StableDiffusion3ControlNetPipeline
+ self.pipeline = StableDiffusion3ControlNetPipeline(
+ vae=pipeline.vae,
+ text_encoder=pipeline.text_encoder,
+ text_encoder_2=pipeline.text_encoder_2,
+ text_encoder_3=pipeline.text_encoder_3,
+ tokenizer=pipeline.tokenizer,
+ tokenizer_2=pipeline.tokenizer_2,
+ tokenizer_3=pipeline.tokenizer_3,
+ transformer=pipeline.transformer,
+ scheduler=pipeline.scheduler,
+ controlnet=controlnet, # can be a list
+ )
else:
log.error(f'Control {what} pipeline: class={pipeline.__class__.__name__} unsupported model type')
return
diff --git a/modules/control/units/detect.py b/modules/control/units/detect.py
index 70a1c8000..7cc8d144d 100644
--- a/modules/control/units/detect.py
+++ b/modules/control/units/detect.py
@@ -20,5 +20,12 @@ def is_f1(model):
if model is None:
return False
if hasattr(model, '__name__'):
- return model.__name__ == p.FluxPipeline.__name__
- return isinstance(model, p.FluxPipeline)
+ return model.__name__ == p.FluxPipeline.__name__ or model.__name__ == p.FluxImg2ImgPipeline.__name__ or model.__name__ == p.FluxInpaintPipeline.__name__
+ return isinstance(model, p.FluxPipeline) or isinstance(model, p.FluxImg2ImgPipeline) or isinstance(model, p.FluxInpaintPipeline)
+
+def is_sd3(model):
+ if model is None:
+ return False
+ if hasattr(model, '__name__'):
+ return model.__name__ == p.StableDiffusion3Pipeline.__name__ or model.__name__ == p.StableDiffusion3Img2ImgPipeline.__name__ or model.__name__ == p.StableDiffusion3InpaintPipeline.__name__
+ return isinstance(model, p.StableDiffusion3Pipeline) or isinstance(model, p.StableDiffusion3Img2ImgPipeline) or isinstance(model, p.StableDiffusion3InpaintPipeline)
diff --git a/modules/face/faceid.py b/modules/face/faceid.py
index e2d5efccb..b74e15dc5 100644
--- a/modules/face/faceid.py
+++ b/modules/face/faceid.py
@@ -79,7 +79,7 @@ def face_id(
ip_ckpt = FACEID_MODELS[model]
folder, filename = os.path.split(ip_ckpt)
basename, _ext = os.path.splitext(filename)
- model_path = hf.hf_hub_download(repo_id=folder, filename=filename, cache_dir=shared.opts.diffusers_dir)
+ model_path = hf.hf_hub_download(repo_id=folder, filename=filename, cache_dir=shared.opts.hfcache_dir)
if model_path is None:
shared.log.error(f'FaceID download failed: model={model} file="{ip_ckpt}"')
return None
diff --git a/modules/face/insightface.py b/modules/face/insightface.py
index 3eb7171bf..a59e48b41 100644
--- a/modules/face/insightface.py
+++ b/modules/face/insightface.py
@@ -32,7 +32,7 @@ def get_app(mp_name):
repo_id='vladmandic/insightface-faceanalysis',
filename=f'{mp_name}.zip',
local_dir_use_symlinks=False,
- cache_dir=opts.diffusers_dir,
+ cache_dir=opts.hfcache_dir,
local_dir=local_dir
)
if not os.path.exists(extract_dir):
diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py
index cf4278e80..36b5ea382 100644
--- a/modules/generation_parameters_copypaste.py
+++ b/modules/generation_parameters_copypaste.py
@@ -189,7 +189,7 @@ def create_override_settings_dict(text_pairs):
def connect_paste(button, local_paste_fields, input_comp, override_settings_component, tabname):
def paste_func(prompt):
- if prompt is None or len(prompt.strip()) == 0 and not shared.cmd_opts.hide_ui_dir_config:
+ if prompt is None or len(prompt.strip()) == 0:
filename = os.path.join(data_path, "params.txt")
if os.path.exists(filename):
with open(filename, "r", encoding="utf8") as file:
diff --git a/modules/images.py b/modules/images.py
index add7c5c7d..8e7ffd662 100644
--- a/modules/images.py
+++ b/modules/images.py
@@ -10,7 +10,7 @@ import threading
import numpy as np
import piexif
import piexif.helper
-from PIL import Image, PngImagePlugin, ExifTags
+from PIL import Image, PngImagePlugin, ExifTags, ImageDraw
from modules import sd_samplers, shared, script_callbacks, errors, paths
from modules.images_grid import image_grid, get_grid_size, split_grid, combine_grid, check_grid_size, get_font, draw_grid_annotations, draw_prompt_matrix, GridAnnotation, Grid # pylint: disable=unused-import
from modules.images_resize import resize_image # pylint: disable=unused-import
@@ -361,6 +361,14 @@ def flatten(img, bgcolor):
return img.convert('RGB')
+def draw_overlay(im, text):
+ d = ImageDraw.Draw(im)
+ fontsize = (im.width + im.height) // 50
+ font = get_font(fontsize)
+ d.text((fontsize//2, fontsize//2), text, font=font, fill=shared.opts.font_color)
+ return im
+
+
def set_watermark(image, watermark):
if shared.opts.image_watermark_position != 'none': # visible watermark
wm_image = None
diff --git a/modules/images_grid.py b/modules/images_grid.py
index 194b0d1cd..51371a0fe 100644
--- a/modules/images_grid.py
+++ b/modules/images_grid.py
@@ -113,7 +113,7 @@ def get_font(fontsize):
return ImageFont.truetype("javascript/notosans-nerdfont-regular.ttf", fontsize)
-def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, title=None):
+def draw_grid_annotations(im, width, height, x_texts, y_texts, margin=0, title=None):
def wrap(drawing, text, font, line_length):
lines = ['']
for word in text.split():
@@ -140,15 +140,15 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, tit
line_spacing = fontsize // 2
font = get_font(fontsize)
color_inactive = (127, 127, 127)
- pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in ver_texts]) == 0 else width * 3 // 4
- cols = len(hor_texts)
- rows = len(ver_texts)
+ pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in y_texts]) == 0 else width * 3 // 4
+ cols = len(x_texts)
+ rows = len(y_texts)
# assert cols == len(hor_texts), f'bad number of horizontal texts: {len(hor_texts)}; must be {cols}'
# assert rows == len(hor_texts), f'bad number of vertical texts: {len(ver_texts)}; must be {rows}'
calc_img = Image.new("RGB", (1, 1), shared.opts.grid_background)
calc_d = ImageDraw.Draw(calc_img)
title_texts = [title] if title else [[GridAnnotation()]]
- for texts, allowed_width in zip(hor_texts + ver_texts + title_texts, [width] * len(hor_texts) + [pad_left] * len(ver_texts) + [(width+margin)*cols]):
+ for texts, allowed_width in zip(x_texts + y_texts + title_texts, [width] * len(x_texts) + [pad_left] * len(y_texts) + [(width+margin)*cols]):
items = [] + texts
texts.clear()
for line in items:
@@ -158,8 +158,8 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, tit
bbox = calc_d.multiline_textbbox((0, 0), line.text, font=font)
line.size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
line.allowed_width = allowed_width
- hor_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing for lines in hor_texts]
- ver_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing * len(lines) for lines in ver_texts]
+ hor_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing for lines in x_texts]
+ ver_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing * len(lines) for lines in y_texts]
pad_top = 0 if sum(hor_text_heights) == 0 else max(hor_text_heights) + line_spacing * 2
title_pad = 0
if title:
@@ -178,11 +178,11 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0, tit
for col in range(cols):
x = pad_left + (width + margin) * col + width / 2
y = (pad_top / 2 - hor_text_heights[col] / 2) + title_pad
- draw_texts(d, x, y, hor_texts[col], font, fontsize)
+ draw_texts(d, x, y, x_texts[col], font, fontsize)
for row in range(rows):
x = pad_left / 2
y = (pad_top + (height + margin) * row + height / 2 - ver_text_heights[row] / 2) + title_pad
- draw_texts(d, x, y, ver_texts[row], font, fontsize)
+ draw_texts(d, x, y, y_texts[row], font, fontsize)
return result
diff --git a/modules/ipadapter.py b/modules/ipadapter.py
index 0a010c41c..4b67b9bb4 100644
--- a/modules/ipadapter.py
+++ b/modules/ipadapter.py
@@ -36,6 +36,7 @@ ADAPTERS_SDXL = {
'Ostris Composition ViT-H SDXL': { 'name': 'ip_plus_composition_sdxl.safetensors', 'repo': 'ostris/ip-composition-adapter', 'subfolder': '' },
}
ADAPTERS = { **ADAPTERS_SD15, **ADAPTERS_SDXL }
+ADAPTERS_ALL = { **ADAPTERS_SD15, **ADAPTERS_SDXL }
def get_adapters():
@@ -101,13 +102,12 @@ def crop_images(images, crops):
try:
for i in range(len(images)):
if crops[i]:
- from shared import yolo # pylint: disable=no-name-in-module
- yolo.load()
+ from modules.shared import yolo # pylint: disable=no-name-in-module
cropped = []
for image in images[i]:
- faces = yolo.predict(image)
+ faces = yolo.predict('face-yolo8n', image)
if len(faces) > 0:
- cropped.append(faces[0].face)
+ cropped.append(faces[0].item)
if len(cropped) == len(images[i]):
images[i] = cropped
else:
@@ -135,7 +135,7 @@ def apply(pipe, p: processing.StableDiffusionProcessing, adapter_names=[], adapt
if hasattr(p, 'ip_adapter_names'):
if isinstance(p.ip_adapter_names, str):
p.ip_adapter_names = [p.ip_adapter_names]
- adapters = [ADAPTERS.get(adapter_name, None) for adapter_name in p.ip_adapter_names if adapter_name is not None and adapter_name.lower() != 'none']
+ adapters = [ADAPTERS_ALL.get(adapter_name, None) for adapter_name in p.ip_adapter_names if adapter_name is not None and adapter_name.lower() != 'none']
adapter_names = p.ip_adapter_names
else:
if isinstance(adapter_names, str):
diff --git a/modules/model_quant.py b/modules/model_quant.py
index fab3a93a0..547a3d7ae 100644
--- a/modules/model_quant.py
+++ b/modules/model_quant.py
@@ -7,10 +7,10 @@ bnb = None
quanto = None
-def create_bnb_config(kwargs):
+def create_bnb_config(kwargs = None):
from modules import shared, devices
if len(shared.opts.bnb_quantization) > 0:
- if 'Model' in shared.opts.bnb_quantization and 'transformer' not in kwargs:
+ if 'Model' in shared.opts.bnb_quantization and 'transformer' not in (kwargs or {}):
load_bnb()
bnb_config = diffusers.BitsAndBytesConfig(
load_in_8bit=shared.opts.bnb_quantization_type in ['fp8'],
@@ -19,8 +19,12 @@ def create_bnb_config(kwargs):
bnb_4bit_quant_type=shared.opts.bnb_quantization_type,
bnb_4bit_compute_dtype=devices.dtype
)
- kwargs['quantization_config'] = bnb_config
shared.log.debug(f'Quantization: module=all type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}')
+ if kwargs is None:
+ return bnb_config
+ else:
+ kwargs['quantization_config'] = bnb_config
+ return kwargs
return kwargs
diff --git a/modules/model_sd3.py b/modules/model_sd3.py
index e369b197a..78eee7b4d 100644
--- a/modules/model_sd3.py
+++ b/modules/model_sd3.py
@@ -1,7 +1,7 @@
import os
import diffusers
import transformers
-from modules import shared, devices, sd_models, sd_unet, model_te, model_quant
+from modules import shared, devices, sd_models, sd_unet, model_te, model_quant, model_tools
def load_overrides(kwargs, cache_dir):
@@ -69,7 +69,7 @@ def load_quants(kwargs, repo_id, cache_dir):
def load_missing(kwargs, fn, cache_dir):
- keys = sd_models.get_safetensor_keys(fn)
+ keys = model_tools.get_safetensor_keys(fn)
size = os.stat(fn).st_size // 1024 // 1024
if size > 15000:
repo_id = 'stabilityai/stable-diffusion-3.5-large'
@@ -129,7 +129,11 @@ def load_sd3(checkpoint_info, cache_dir=None, config=None):
if fn is not None and os.path.exists(fn) and os.path.isfile(fn):
if fn.endswith('.safetensors'):
loader = diffusers.StableDiffusion3Pipeline.from_single_file
- kwargs = load_missing(kwargs, fn, cache_dir)
+ # required_modules = model_tools.get_modules(diffusers.StableDiffusion3Pipeline)
+ # have_modules = model_tools.get_safetensor_keys(fn)
+ # loaded_modules = model_tools.load_modules('stabilityai/stable-diffusion-3.5-medium', required_modules)
+ # kwargs = {**kwargs, **loaded_modules}
+ # kwargs = load_missing(kwargs, fn, cache_dir)
repo_id = fn
elif fn.endswith('.gguf'):
kwargs = load_gguf(kwargs, fn)
@@ -138,7 +142,7 @@ def load_sd3(checkpoint_info, cache_dir=None, config=None):
else:
kwargs['variant'] = 'fp16'
- shared.log.debug(f'Load model: type=SD3 kwargs={list(kwargs)}')
+ shared.log.debug(f'Load model: type=SD3 kwargs={list(kwargs)} repo="{repo_id}"')
kwargs = model_quant.create_bnb_config(kwargs)
pipe = loader(
diff --git a/modules/model_tools.py b/modules/model_tools.py
new file mode 100644
index 000000000..1212da244
--- /dev/null
+++ b/modules/model_tools.py
@@ -0,0 +1,49 @@
+import inspect
+import diffusers
+import transformers
+import safetensors.torch
+from modules import shared, devices, model_quant
+
+
+def get_safetensor_keys(filename):
+ keys = []
+ try:
+ with safetensors.torch.safe_open(filename, framework="pt", device="cpu") as f:
+ keys = f.keys()
+ except Exception as e:
+ shared.log.error(f'Load dict: path="{filename}" {e}')
+ return keys
+
+
+def get_modules(model: callable):
+ signature = inspect.signature(model.__init__, follow_wrapped=True)
+ params = {param.name: param.annotation for param in signature.parameters.values() if param.annotation != inspect._empty and hasattr(param.annotation, 'from_pretrained')} # pylint: disable=protected-access
+ for name, cls in params.items():
+ shared.log.debug(f'Analyze: model={model} module={name} class={cls.__name__} loadable={getattr(cls, "from_pretrained", None)}')
+ return params
+
+
+def load_modules(repo_id: str, params: dict):
+ cache_dir = shared.opts.hfcache_dir
+ modules = {}
+ for name, cls in params.items():
+ subfolder = None
+ kwargs = {}
+ if cls == diffusers.AutoencoderKL:
+ subfolder = 'vae'
+ if cls == transformers.CLIPTextModel: # clip-vit-l
+ subfolder = 'text_encoder'
+ if cls == transformers.CLIPTextModelWithProjection: # clip-vit-g
+ subfolder = 'text_encoder_2'
+ if cls == transformers.T5EncoderModel: # t5-xxl
+ subfolder = 'text_encoder_3'
+ kwargs['quantization_config'] = model_quant.create_bnb_config()
+ kwargs['variant'] = 'fp16'
+ if cls == diffusers.SD3Transformer2DModel:
+ subfolder = 'transformer'
+ kwargs['quantization_config'] = model_quant.create_bnb_config()
+ if subfolder is None:
+ continue
+ shared.log.debug(f'Load: module={name} class={cls.__name__} repo={repo_id} location={subfolder}')
+ modules[name] = cls.from_pretrained(repo_id, subfolder=subfolder, cache_dir=cache_dir, torch_dtype=devices.dtype, **kwargs)
+ return modules
diff --git a/modules/modelloader.py b/modules/modelloader.py
index 8eab91597..ce36a739b 100644
--- a/modules/modelloader.py
+++ b/modules/modelloader.py
@@ -273,6 +273,7 @@ def load_diffusers_models(clear=True):
place = os.path.join(models_path, 'Diffusers')
if clear:
diffuser_repos.clear()
+ already_found = []
try:
for folder in os.listdir(place):
try:
@@ -303,7 +304,11 @@ def load_diffusers_models(clear=True):
if (not os.path.exists(index)) and (not os.path.exists(info)) and (not os.path.exists(config)):
debug(f'Diffusers skip model no info: {name}')
continue
+ if name in already_found:
+ debug(f'Diffusers skip model already found: {name}')
+ continue
repo = { 'name': name, 'filename': name, 'friendly': friendly, 'folder': folder, 'path': commit, 'hash': snapshot, 'mtime': mtime, 'model_info': info, 'model_index': index, 'model_config': config }
+ already_found.append(name)
diffuser_repos.append(repo)
if os.path.exists(os.path.join(folder, 'hidden')):
continue
diff --git a/modules/postprocess/yolo.py b/modules/postprocess/yolo.py
index 44f72237e..44c7db20c 100644
--- a/modules/postprocess/yolo.py
+++ b/modules/postprocess/yolo.py
@@ -78,6 +78,10 @@ class YoloRestorer(Detailer):
) -> list[YoloResult]:
result = []
+ if isinstance(model, str):
+ model = self.models.get(model, None)
+ if model is None:
+ _, model = self.load(model)
if model is None:
return result
args = {
@@ -124,7 +128,9 @@ class YoloRestorer(Detailer):
mask_image = None
w, h = box[2] - box[0], box[3] - box[1]
size = w * h / (image.width * image.height)
- if (min(w, h) > shared.opts.detailer_min_size if shared.opts.detailer_min_size > 0 else True) and (max(w, h) < shared.opts.detailer_max_size if shared.opts.detailer_max_size > 0 else True):
+ min_size = (shared.opts.detailer_min_size if shared.opts.detailer_min_size > 0 else 0) * min(w, h)
+ max_size = (shared.opts.detailer_max_size if shared.opts.detailer_max_size > 0 else 1) * max(w, h)
+ if (min(w, h) > min_size) and (max(w, h) < max_size):
if mask:
mask_image = image.copy()
mask_image = Image.new('L', image.size, 0)
@@ -138,6 +144,7 @@ class YoloRestorer(Detailer):
def load(self, model_name: str = None):
from modules import modelloader
+ model = None
self.dependencies()
if model_name is None:
model_name = list(self.list)[0]
@@ -158,7 +165,7 @@ class YoloRestorer(Detailer):
return model_name, model
except Exception as e:
shared.log.error(f'Load: type=Detailer name="{model_name}" error="{e}"')
- return None
+ return None, None
def restore(self, np_image, p: processing.StableDiffusionProcessing = None):
if hasattr(p, 'recursion'):
@@ -323,8 +330,10 @@ class YoloRestorer(Detailer):
min_confidence = gr.Slider(label="Min confidence", elem_id=f"{tab}_detailer_conf", value=shared.opts.detailer_conf, minimum=0.0, maximum=1.0, step=0.05)
iou = gr.Slider(label="Max overlap", elem_id=f"{tab}_detailer_iou", value=shared.opts.detailer_iou, minimum=0, maximum=1.0, step=0.05)
with gr.Row():
- min_size = gr.Slider(label="Min size", elem_id=f"{tab}_detailer_min_size", value=shared.opts.detailer_min_size, minimum=0, maximum=1024, step=1)
- max_size = gr.Slider(label="Max size", elem_id=f"{tab}_detailer_max_size", value=shared.opts.detailer_max_size, minimum=0, maximum=1024, step=1)
+ min_size = shared.opts.detailer_min_size if shared.opts.detailer_min_size < 1 else 0.0
+ min_size = gr.Slider(label="Min size", elem_id=f"{tab}_detailer_min_size", value=min_size, minimum=0.1, maximum=1.0, step=0.05)
+ max_size = shared.opts.detailer_min_size if shared.opts.detailer_min_size < 1 and shared.opts.detailer_min_size > 0 else 1.0
+ max_size = gr.Slider(label="Max size", elem_id=f"{tab}_detailer_max_size", value=max_size, minimum=0.1, maximum=1.0, step=0.05)
detailers.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou], outputs=[])
classes.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou], outputs=[])
strength.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou], outputs=[])
diff --git a/modules/postprocessing.py b/modules/postprocessing.py
index 346758851..967bd04cc 100644
--- a/modules/postprocessing.py
+++ b/modules/postprocessing.py
@@ -37,7 +37,6 @@ def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemp
image_ext.append(ext)
shared.log.debug(f'Process: mode=batch inputs={len(image_folder)} images={len(image_data)}')
elif extras_mode == 2:
- assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled'
assert input_dir, 'input directory not selected'
image_list = os.listdir(input_dir)
for filename in image_list:
diff --git a/modules/script_loading.py b/modules/script_loading.py
index f39394625..37f64b33f 100644
--- a/modules/script_loading.py
+++ b/modules/script_loading.py
@@ -14,7 +14,7 @@ def load_module(path):
module_spec = importlib.util.spec_from_file_location(os.path.basename(path), path)
module = importlib.util.module_from_spec(module_spec)
try:
- if '/sd-extension-' in path or '/Lora' in path: # safe extensions without stdout intercept
+ if 'sd-extension-' in path or 'Lora' in path: # safe extensions without stdout intercept
module_spec.loader.exec_module(module)
else:
if debug:
diff --git a/modules/sd_detect.py b/modules/sd_detect.py
index 7144a7be7..148e788d5 100644
--- a/modules/sd_detect.py
+++ b/modules/sd_detect.py
@@ -84,6 +84,8 @@ def detect_pipeline(f: str, op: str = 'model', warning=True, quiet=False):
if 'omnigen' in f.lower():
guess = 'OmniGen'
pipeline = 'custom'
+ if 'sd3' in f.lower():
+ guess = 'Stable Diffusion 3'
if 'flux' in f.lower():
guess = 'FLUX'
if size > 11000 and size < 20000:
diff --git a/modules/sd_models.py b/modules/sd_models.py
index dc0679c58..7743ce27d 100644
--- a/modules/sd_models.py
+++ b/modules/sd_models.py
@@ -19,7 +19,7 @@ from modules import paths, shared, shared_state, modelloader, devices, script_ca
from modules.timer import Timer
from modules.memstats import memory_stats
from modules.modeldata import model_data
-from modules.sd_checkpoint import CheckpointInfo, select_checkpoint, list_models, checkpoints_list, checkpoint_titles, get_closet_checkpoint_match, update_model_hashes, setup_model, write_metadata, read_metadata_from_safetensors # pylint: disable=unused-import
+from modules.sd_checkpoint import CheckpointInfo, select_checkpoint, list_models, checkpoints_list, checkpoint_titles, get_closet_checkpoint_match, model_hash, update_model_hashes, setup_model, write_metadata, read_metadata_from_safetensors # pylint: disable=unused-import
model_dir = "Stable-diffusion"
@@ -75,16 +75,6 @@ def read_state_dict(checkpoint_file, map_location=None, what:str='model'): # pyl
return sd
-def get_safetensor_keys(filename):
- keys = []
- try:
- with safetensors.torch.safe_open(filename, framework="pt", device="cpu") as f:
- keys = f.keys()
- except Exception as e:
- shared.log.error(f'Load dict: path="{filename}" {e}')
- return keys
-
-
def get_state_dict_from_checkpoint(pl_sd):
checkpoint_dict_replacements = {
'cond_stage_model.transformer.embeddings.': 'cond_stage_model.transformer.text_model.embeddings.',
@@ -1052,6 +1042,15 @@ def clean_diffuser_pipe(pipe):
def set_diffuser_pipe(pipe, new_pipe_type):
+ exclude = [
+ 'StableDiffusionReferencePipeline',
+ 'StableDiffusionAdapterPipeline',
+ 'AnimateDiffPipeline',
+ 'AnimateDiffSDXLPipeline',
+ 'OmniGenPipeline',
+ 'StableDiffusion3ControlNetPipeline',
+ ]
+
n = getattr(pipe.__class__, '__name__', '')
if new_pipe_type == DiffusersTaskType.TEXT_2_IMAGE:
clean_diffuser_pipe(pipe)
@@ -1060,7 +1059,7 @@ def set_diffuser_pipe(pipe, new_pipe_type):
return pipe
# skip specific pipelines
- if n in ['StableDiffusionReferencePipeline', 'StableDiffusionAdapterPipeline', 'AnimateDiffPipeline', 'AnimateDiffSDXLPipeline', 'OmniGenPipeline']:
+ if n in exclude:
return pipe
if 'Onnx' in pipe.__class__.__name__:
return pipe
diff --git a/modules/shared.py b/modules/shared.py
index ae94f26cf..342b5222c 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -821,8 +821,8 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), {
"detailer_conf": OptionInfo(0.6, "Min confidence", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.05, "visible": False}),
"detailer_max": OptionInfo(2, "Max detected", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1, "visible": False}),
"detailer_iou": OptionInfo(0.5, "Max overlap", gr.Slider, {"minimum": 0, "maximum": 1.0, "step": 0.05, "visible": False}),
- "detailer_min_size": OptionInfo(0, "Min object size", gr.Slider, {"minimum": 0, "maximum": 1024, "step": 1, "visible": False}),
- "detailer_max_size": OptionInfo(0, "Max object size", gr.Slider, {"minimum": 0, "maximum": 1024, "step": 1, "visible": False}),
+ "detailer_min_size": OptionInfo(0.0, "Min object size", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.05, "visible": False}),
+ "detailer_max_size": OptionInfo(1.0, "Max object size", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.05, "visible": False}),
"detailer_padding": OptionInfo(20, "Item padding", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1, "visible": False}),
"detailer_blur": OptionInfo(10, "Item edge blur", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1, "visible": False}),
"detailer_strength": OptionInfo(0.5, "Detailer strength", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01, "visible": False}),
@@ -1101,7 +1101,7 @@ profiler = None
opts = Options()
config_filename = cmd_opts.config
opts.load(config_filename)
-cmd_opts = cmd_args.compatibility_args(opts, cmd_opts)
+cmd_opts = cmd_args.settings_args(opts, cmd_opts)
if cmd_opts.use_xformers:
opts.data['cross_attention_optimization'] = 'xFormers'
opts.data['uni_pc_lower_order_final'] = opts.schedulers_use_loworder # compatibility
diff --git a/modules/ui.py b/modules/ui.py
index 8ea565cfc..039ef6487 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -355,13 +355,20 @@ def create_ui(startup_timer = None):
ui_onnx.create_ui()
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)
+ def get_changelog():
+ with open('CHANGELOG.md', 'r', encoding='utf-8') as f:
+ content = f.read()
+ content = content.replace('# Change Log for SD.Next', ' ')
+ return content
- with gr.TabItem("Licenses", id="system_licenses", elem_id="system_tab_licenses"):
- gr.HTML(shared.html("licenses.html"), elem_id="licenses", elem_classes="licenses")
- create_dirty_indicator("tab_licenses", [], interactive=False)
+ with gr.Column():
+ get_changelog_btn = gr.Button(value='Get changelog', elem_id="get_changelog")
+ with gr.Column():
+ _changelog_search = gr.Textbox(label="Search", elem_id="changelog_search")
+ _changelog_result = gr.HTML(elem_id="changelog_result")
+
+ changelog_markdown = gr.Markdown('', elem_id="changelog_markdown")
+ get_changelog_btn.click(fn=get_changelog, outputs=[changelog_markdown], show_progress=True)
def unload_sd_weights():
modules.sd_models.unload_model_weights(op='model')
diff --git a/package.json b/package.json
index 2c0fa6b50..bf5a366ea 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"url": "https://github.com/vladmandic/automatic/issues"
},
"homepage": "https://github.com/vladmandic/automatic",
- "license": "AGPLv3",
+ "license": "Apache-2.0",
"engines": {
"node": ">=14.0.0"
},
diff --git a/requirements.txt b/requirements.txt
index d376fe320..c6fcf97ec 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -50,7 +50,7 @@ pandas
protobuf==4.25.3
pytorch_lightning==1.9.4
tokenizers==0.20.1
-transformers==4.46.0
+transformers==4.46.1
urllib3==1.26.19
Pillow==10.4.0
timm==0.9.16
diff --git a/scripts/prompt_enhance.py b/scripts/prompt_enhance.py
index 2fb513e76..a02aea608 100644
--- a/scripts/prompt_enhance.py
+++ b/scripts/prompt_enhance.py
@@ -32,10 +32,10 @@ class Script(scripts.Script):
def load(self):
if self.tokenizer is None:
- self.tokenizer = AutoTokenizer.from_pretrained('gokaygokay/Flux-Prompt-Enhance', cache_dir=shared.opts.diffusers_dir)
+ self.tokenizer = AutoTokenizer.from_pretrained('gokaygokay/Flux-Prompt-Enhance', cache_dir=shared.opts.hfcache_dir)
if self.model is None:
shared.log.info(f'Prompt enhance: model="{repo_id}"')
- self.model = AutoModelForSeq2SeqLM.from_pretrained('gokaygokay/Flux-Prompt-Enhance', cache_dir=shared.opts.diffusers_dir).to(device=devices.cpu, dtype=devices.dtype)
+ self.model = AutoModelForSeq2SeqLM.from_pretrained('gokaygokay/Flux-Prompt-Enhance', cache_dir=shared.opts.hfcache_dir).to(device=devices.cpu, dtype=devices.dtype)
def enhance(self, prompt, auto_apply: bool = False, temperature: float = 0.7, repetition_penalty: float = 1.2, max_length: int = 128):
self.load()
diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py
index e8f649182..e8ecb5bd4 100644
--- a/scripts/xyz_grid.py
+++ b/scripts/xyz_grid.py
@@ -48,10 +48,11 @@ class Script(scripts.Script):
csv_mode = gr.Checkbox(label='Text inputs', value=False, elem_id=self.elem_id("csv_mode"), container=False)
draw_legend = gr.Checkbox(label='Legend', value=True, elem_id=self.elem_id("draw_legend"), container=False)
no_fixed_seeds = gr.Checkbox(label='Random seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"), container=False)
+ include_time = gr.Checkbox(label='Add time info', value=False, elem_id=self.elem_id("include_time"), container=False)
with gr.Column():
- no_grid = gr.Checkbox(label='Skip grid', value=False, elem_id=self.elem_id("no_xyz_grid"), container=False)
- include_lone_images = gr.Checkbox(label='Sub-images', value=False, elem_id=self.elem_id("include_lone_images"), container=False)
- include_sub_grids = gr.Checkbox(label='Sub-grids', value=False, elem_id=self.elem_id("include_sub_grids"), container=False)
+ include_grid = gr.Checkbox(label='Include main grid', value=True, elem_id=self.elem_id("no_xyz_grid"), container=False)
+ include_subgrids = gr.Checkbox(label='Include sub grids', value=False, elem_id=self.elem_id("include_sub_grids"), container=False)
+ include_images = gr.Checkbox(label='Include images', value=False, elem_id=self.elem_id("include_lone_images"), container=False)
with gr.Row():
margin_size = gr.Slider(label="Grid margins", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size"))
with gr.Row():
@@ -130,10 +131,9 @@ class Script(scripts.Script):
(z_values_dropdown, lambda params:get_dropdown_update_from_params("Z",params)),
)
- return [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, no_grid, include_lone_images, include_sub_grids, margin_size]
+ return [x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, include_time, margin_size]
- def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, no_grid, include_lone_images, include_sub_grids, margin_size): # pylint: disable=W0221
- shared.log.debug(f'xyzgrid: x_type={x_type}|x_values={x_values}|x_values_dropdown={x_values_dropdown}|y_type={y_type}|{y_values}={y_values}|{y_values_dropdown}={y_values_dropdown}|z_type={z_type}|z_values={z_values}|z_values_dropdown={z_values_dropdown}|draw_legend={draw_legend}|include_lone_images={include_lone_images}|include_sub_grids={include_sub_grids}|no_grid={no_grid}|margin_size={margin_size}')
+ def run(self, p, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, include_time, margin_size): # pylint: disable=W0221
if not no_fixed_seeds:
processing.fix_seed(p)
if not shared.opts.return_grid:
@@ -307,38 +307,38 @@ class Script(scripts.Script):
z_labels=[z_opt.format_value(p, z_opt, z) for z in zs],
cell=cell,
draw_legend=draw_legend,
- include_lone_images=include_lone_images,
- include_sub_grids=include_sub_grids,
+ include_lone_images=include_images,
+ include_sub_grids=include_subgrids,
first_axes_processed=first_axes_processed,
second_axes_processed=second_axes_processed,
margin_size=margin_size,
- no_grid=no_grid,
+ no_grid=not include_grid,
+ include_time=include_time,
)
if not processed.images:
- return processed # It broke, no further handling needed.
+ return processed # something broke, no further handling needed.
z_count = len(zs)
- processed.infotexts[:1+z_count] = grid_infotext[:1+z_count] # Set the grid infotexts to the real ones with extra_generation_params (1 main grid + z_count sub-grids)
- if not include_lone_images:
- # Don't need sub-images anymore, drop from list:
- if no_grid and include_sub_grids:
+ processed.infotexts[:1+z_count] = grid_infotext[:1+z_count] # set the grid infotexts to the real ones with extra_generation_params (1 main grid + z_count sub-grids)
+ if not include_images: # dont need sub-images anymore, drop from list:
+ if not include_grid and include_subgrids:
processed.images = processed.images[:z_count] # we don't have the main grid image, and need zero additional sub-images
else:
processed.images = processed.images[:z_count+1] # we either have the main grid image, or need one sub-images
- if shared.opts.grid_save: # Auto-save main and sub-grids:
- grid_count = z_count + ( 1 if not no_grid and z_count > 1 else 0 )
+ if shared.opts.grid_save: # auto-save main and sub-grids:
+ grid_count = z_count + ( 1 if include_grid and z_count > 1 else 0 )
for g in range(grid_count):
adj_g = g-1 if g > 0 else g
info = processed.infotexts[g]
prompt = processed.all_prompts[adj_g]
seed = processed.all_seeds[adj_g]
images.save_image(processed.images[g], p.outpath_grids, "grid", info=info, extension=shared.opts.grid_format, prompt=prompt, seed=seed, grid=True, p=processed)
- if not include_sub_grids: # Done with sub-grids, drop all related information:
+ if not include_subgrids: # done with sub-grids, drop all related information:
for _sg in range(z_count):
del processed.images[1]
del processed.all_prompts[1]
del processed.all_seeds[1]
del processed.infotexts[1]
- elif no_grid:
+ elif include_grid:
del processed.infotexts[0]
return processed
diff --git a/scripts/xyz_grid_classes.py b/scripts/xyz_grid_classes.py
index 335d66186..202482157 100644
--- a/scripts/xyz_grid_classes.py
+++ b/scripts/xyz_grid_classes.py
@@ -84,13 +84,13 @@ class SharedSettingsStackHelper(object):
axis_options = [
AxisOption("Nothing", str, do_nothing, fmt=format_nothing),
- AxisOption("[Model] Model", str, apply_checkpoint, cost=1.0, fmt=format_value, choices=lambda: sorted(sd_models.checkpoints_list)),
+ AxisOption("[Model] Model", str, apply_checkpoint, cost=1.0, fmt=format_value_add_label, choices=lambda: sorted(sd_models.checkpoints_list)),
AxisOption("[Model] UNET", str, apply_unet, cost=0.8, choices=lambda: ['None'] + list(sd_unet.unet_dict)),
AxisOption("[Model] VAE", str, apply_vae, cost=0.6, choices=lambda: ['None'] + list(sd_vae.vae_dict)),
- AxisOption("[Model] Refiner", str, apply_refiner, cost=0.8, fmt=format_value, choices=lambda: ['None'] + sorted(sd_models.checkpoints_list)),
+ AxisOption("[Model] Refiner", str, apply_refiner, cost=0.8, fmt=format_value_add_label, choices=lambda: ['None'] + sorted(sd_models.checkpoints_list)),
AxisOption("[Model] Text encoder", str, apply_te, cost=0.7, choices=shared_items.sd_te_items),
- AxisOption("[Model] Dictionary", str, apply_dict, fmt=format_value, cost=0.9, choices=lambda: ['None'] + list(sd_models.checkpoints_list)),
- AxisOption("[Prompt] Search & replace", str, apply_prompt, fmt=format_value),
+ AxisOption("[Model] Dictionary", str, apply_dict, fmt=format_value_add_label, cost=0.9, choices=lambda: ['None'] + list(sd_models.checkpoints_list)),
+ AxisOption("[Prompt] Search & replace", str, apply_prompt, fmt=format_value_add_label),
AxisOption("[Prompt] Prompt order", str_permutations, apply_order, fmt=format_value_join_list),
AxisOption("[Network] LoRA", str, apply_lora, cost=0.5, choices=list_lora),
AxisOption("[Network] LoRA strength", float, apply_setting('extra_networks_default_multiplier')),
@@ -109,8 +109,8 @@ axis_options = [
AxisOption("[Process] Model args", str, apply_task_args),
AxisOption("[Process] Processing args", str, apply_processing),
AxisOption("[Process] Server options", str, apply_options),
- AxisOptionTxt2Img("[Sampler] Name", str, apply_sampler, fmt=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]),
- AxisOptionImg2Img("[Sampler] Name", str, apply_sampler, fmt=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img]),
+ AxisOptionTxt2Img("[Sampler] Name", str, apply_sampler, fmt=format_value_add_label, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]),
+ AxisOptionImg2Img("[Sampler] Name", str, apply_sampler, fmt=format_value_add_label, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img]),
AxisOption("[Sampler] Sigma method", str, apply_setting("schedulers_sigma"), choices=lambda: ['default', 'karras', 'beta', 'exponential']),
AxisOption("[Sampler] Timestep spacing", str, apply_setting("schedulers_timestep_spacing"), choices=lambda: ['default', 'linspace', 'leading', 'trailing']),
AxisOption("[Sampler] Timestep range", int, apply_setting("schedulers_timesteps_range")),
@@ -122,7 +122,7 @@ axis_options = [
AxisOption("[Sampler] eta delta", float, apply_setting("eta_noise_seed_delta")),
AxisOption("[Sampler] eta multiplier", float, apply_setting("scheduler_eta")),
AxisOption("[Refine] Upscaler", str, apply_field("hr_upscaler"), cost=0.3, choices=lambda: [*shared.latent_upscale_modes, *[x.name for x in shared.sd_upscalers]]),
- AxisOption("[Refine] Sampler", str, apply_hr_sampler_name, fmt=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]),
+ AxisOption("[Refine] Sampler", str, apply_hr_sampler_name, fmt=format_value_add_label, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]),
AxisOption("[Refine] Denoising strength", float, apply_field("denoising_strength")),
AxisOption("[Refine] Hires steps", int, apply_field("hr_second_pass_steps")),
AxisOption("[Refine] Guidance scale", float, apply_field("image_cfg_scale")),
@@ -131,7 +131,7 @@ axis_options = [
AxisOption("[Refine] Refiner steps", float, apply_field("refiner_steps")),
AxisOption("[Postprocess] Upscaler", str, apply_upscaler, cost=0.4, choices=lambda: [x.name for x in shared.sd_upscalers][1:]),
AxisOption("[Postprocess] Context", str, apply_context, choices=lambda: ["Add with forward", "Remove with forward", "Add with backward", "Remove with backward"]),
- AxisOption("[Postprocess] Detailer", str, apply_detailer, fmt=format_value),
+ AxisOption("[Postprocess] Detailer", str, apply_detailer, fmt=format_value_add_label),
AxisOption("[HDR] Mode", int, apply_field("hdr_mode")),
AxisOption("[HDR] Brightness", float, apply_field("hdr_brightness")),
AxisOption("[HDR] Color", float, apply_field("hdr_color")),
diff --git a/scripts/xyz_grid_draw.py b/scripts/xyz_grid_draw.py
index 8f95e696b..9a9f2246c 100644
--- a/scripts/xyz_grid_draw.py
+++ b/scripts/xyz_grid_draw.py
@@ -4,23 +4,28 @@ from PIL import Image
from modules import shared, images, processing
-def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend, include_lone_images, include_sub_grids, first_axes_processed, second_axes_processed, margin_size, no_grid): # pylint: disable=unused-argument
- hor_texts = [[images.GridAnnotation(x)] for x in x_labels]
- ver_texts = [[images.GridAnnotation(y)] for y in y_labels]
- title_texts = [[images.GridAnnotation(z)] for z in z_labels]
+def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend, include_lone_images, include_sub_grids, first_axes_processed, second_axes_processed, margin_size, no_grid: False, include_time: False): # pylint: disable=unused-argument
+ x_texts = [[images.GridAnnotation(x)] for x in x_labels]
+ y_texts = [[images.GridAnnotation(y)] for y in y_labels]
+ z_texts = [[images.GridAnnotation(z)] for z in z_labels]
list_size = (len(xs) * len(ys) * len(zs))
processed_result = None
shared.state.job_count = list_size * p.n_iter
t0 = time.time()
+ i = 0
def process_cell(x, y, z, ix, iy, iz):
- nonlocal processed_result
+ nonlocal processed_result, i
+ i += 1
+ shared.log.debug(f'XYZ grid process: x={ix+1}/{len(xs)} y={iy+1}/{len(ys)} z={iz+1}/{len(zs)} total={i/list_size:.2f}')
def index(ix, iy, iz):
return ix + iy * len(xs) + iz * len(xs) * len(ys)
shared.state.job = 'grid'
+ p0 = time.time()
processed: processing.Processed = cell(x, y, z, ix, iy, iz)
+ p1 = time.time()
if processed_result is None:
processed_result = copy(processed)
if processed_result is None:
@@ -30,13 +35,17 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend
processed_result.all_prompts = [None] * list_size
processed_result.all_seeds = [None] * list_size
processed_result.infotexts = [None] * list_size
+ processed_result.time = [0] * list_size
processed_result.index_of_first_image = 1
idx = index(ix, iy, iz)
if processed is not None and processed.images:
processed_result.images[idx] = processed.images[0]
+ if include_time:
+ processed_result.images[idx] = images.draw_overlay(processed_result.images[idx], f'time: {p1 - p0:.2f}')
processed_result.all_prompts[idx] = processed.prompt
processed_result.all_seeds[idx] = processed.seed
processed_result.infotexts[idx] = processed.infotexts[0]
+ processed_result.time[idx] = round(p1 - p0, 2)
else:
cell_mode = "P"
cell_size = (processed_result.width, processed_result.height)
@@ -44,6 +53,7 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend
cell_mode = processed_result.images[0].mode
cell_size = processed_result.images[0].size
processed_result.images[idx] = Image.new(cell_mode, cell_size)
+ return
if first_axes_processed == 'x':
for ix, x in enumerate(xs):
@@ -93,7 +103,7 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend
if (not no_grid or include_sub_grids) and images.check_grid_size(to_process):
grid = images.image_grid(to_process, rows=len(ys))
if draw_legend:
- grid = images.draw_grid_annotations(grid, w, h, hor_texts, ver_texts, margin_size, title=title_texts[i])
+ grid = images.draw_grid_annotations(grid, w, h, x_texts, y_texts, margin_size, title=z_texts[i])
processed_result.images.insert(i, grid)
processed_result.all_prompts.insert(i, processed_result.all_prompts[idx0])
processed_result.all_seeds.insert(i, processed_result.all_seeds[idx0])
diff --git a/scripts/xyz_grid_on.py b/scripts/xyz_grid_on.py
index ac1bc4c2f..cdef01e60 100644
--- a/scripts/xyz_grid_on.py
+++ b/scripts/xyz_grid_on.py
@@ -57,9 +57,10 @@ class Script(scripts.Script):
draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend"), container=False)
csv_mode = gr.Checkbox(label='Use text inputs', value=False, elem_id=self.elem_id("csv_mode"), container=False)
no_fixed_seeds = gr.Checkbox(label='Use random seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"), container=False)
+ include_time = gr.Checkbox(label='Add time info', value=False, elem_id=self.elem_id("include_time"), container=False)
with gr.Column():
- include_grid = gr.Checkbox(label='Create main grid', value=True, elem_id=self.elem_id("no_xyz_grid"), container=False)
- include_subgrids = gr.Checkbox(label='Create partial grids', value=False, elem_id=self.elem_id("include_sub_grids"), container=False)
+ include_grid = gr.Checkbox(label='Include main grid', value=True, elem_id=self.elem_id("no_xyz_grid"), container=False)
+ include_subgrids = gr.Checkbox(label='Include sub grids', value=False, elem_id=self.elem_id("include_sub_grids"), container=False)
include_images = gr.Checkbox(label='Include images', value=False, elem_id=self.elem_id("include_lone_images"), container=False)
with gr.Row():
margin_size = gr.Slider(label="Grid margins", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size"))
@@ -139,9 +140,9 @@ class Script(scripts.Script):
(z_values_dropdown, lambda params:get_dropdown_update_from_params("Z",params)),
)
- return [enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, margin_size]
+ return [enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, include_time, margin_size]
- def process(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, margin_size): # pylint: disable=W0221
+ def process(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, include_time, margin_size): # pylint: disable=W0221
global active, cache # pylint: disable=W0603
cache = None
if not enabled or active:
@@ -327,8 +328,10 @@ class Script(scripts.Script):
second_axes_processed=second_axes_processed,
margin_size=margin_size,
no_grid=not include_grid,
+ include_time=include_time,
)
+ """
if not processed.images:
active = False
return processed # It broke, no further handling needed.
@@ -354,6 +357,43 @@ class Script(scripts.Script):
active = False
cache = processed
return processed
+ """
- def process_images(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, margin_size): # pylint: disable=W0221, W0613
+ if not processed.images:
+ return processed # It broke, no further handling needed.
+ z_count = len(zs)
+ processed.infotexts[:1+z_count] = grid_infotext[:1+z_count] # set the grid infotexts to the real ones with extra_generation_params (1 main grid + z_count sub-grids)
+ if not include_images: # dont need sub-images anymore, drop from list:
+ if not include_grid and include_subgrids:
+ processed.images = processed.images[:z_count] # we don't have the main grid image, and need zero additional sub-images
+ else:
+ processed.images = processed.images[:z_count+1] # we either have the main grid image, or need one sub-images
+
+ if shared.opts.grid_save: # auto-save main and sub-grids:
+ grid_count = z_count + ( 1 if include_grid and z_count > 1 else 0 )
+ for g in range(grid_count):
+ adj_g = g-1 if g > 0 else g
+ info = processed.infotexts[g]
+ prompt = processed.all_prompts[adj_g]
+ seed = processed.all_seeds[adj_g]
+ images.save_image(processed.images[g], p.outpath_grids, "grid", info=info, extension=shared.opts.grid_format, prompt=prompt, seed=seed, grid=True, p=processed)
+ if not include_subgrids: # done with sub-grids, drop all related information:
+ for _sg in range(z_count):
+ del processed.images[1]
+ del processed.all_prompts[1]
+ del processed.all_seeds[1]
+ del processed.infotexts[1]
+ elif include_grid:
+ del processed.infotexts[0]
+
+ p.do_not_save_grid = True
+ p.do_not_save_samples = True
+ active = False
+ cache = processed
+ return processed
+
+
+ def process_images(self, p, *args): # pylint: disable=W0221, W0613
+ if p.iteration > 0 and cache is not None and len(cache.images) > 0:
+ cache.images = [] # avoid returning same images multiple items
return cache
diff --git a/wiki b/wiki
index 4360bc7fc..a64e3776f 160000
--- a/wiki
+++ b/wiki
@@ -1 +1 @@
-Subproject commit 4360bc7fcfd5dc301b825a6c5dcf4274c8eba983
+Subproject commit a64e3776fd357ccf85fa751a0efc65fead72c02a