Compare commits

..
41 Commits
Author SHA1 Message Date
Manuel Schmid ba77e7f706 release: bump version to 2.4.3, update changelog (#3109) 2024-06-06 19:34:44 +02:00
Manuel Schmid 5abae220c5 feat: parse env var strings to expected config value types (#3107)
* fix: add try_parse_bool for env var strings to enable config overrides of boolean values

* fix: fallback to given value if not parseable

* feat: extend eval to all valid types

* fix: remove return type

* fix: prevent strange type conversions by providing expected type

* feat: add tests
2024-06-06 19:29:08 +02:00
Manuel Schmid 04d764820e fix: correctly set alphas_cumprod (#3106) 2024-06-06 13:42:26 +02:00
Manuel Schmid 350fdd9021 Merge pull request #3095 from lllyasviel/develop
release v2.4.2
2024-06-05 21:50:42 +02:00
Manuel Schmid 85a8deecee release: bump version to 2.4.2, update changelog 2024-06-05 21:30:43 +02:00
Manuel Schmid b58bc7774e fix: correct sampling when gamma is 0 (#3093) 2024-06-04 21:03:37 +02:00
Manuel Schmid 2d55a5f257 feat: add support for playground v2.5 (#3073)
* feat: add support for playground v2.5

* feat: add preset for playground v2.5

* feat: change URL to mashb1t

* feat: optimize playground v2.5 preset
2024-06-04 20:15:49 +02:00
Manuel Schmid cb24c686b0 Merge branch 'main_upstream' into develop_upstream 2024-06-04 20:11:42 +02:00
Manuel Schmid ab01104d42 feat: make textboxes (incl. positive prompt) resizable (#3074)
* feat: make textboxes (incl. positive prompt) resizable again

* wip: auto-resize positive prompt on new line

dirty approach as container is hidden and 1px padding is applied for border shadow to actually work

* feat: set row height to 84, exactly matching 3 lines for positive prompt

eliminate need for JS to resize positive prompt onUiLoaded
2024-06-02 13:40:42 +02:00
Manuel Schmid 3d43976e8e feat: update cmd args (#3075) 2024-06-02 02:13:16 +02:00
Manuel Schmid 07c6c89edf fix: chown files directly at copy (#3066) 2024-05-31 22:41:36 +02:00
Manuel Schmid 7899261755 fix: turbo scheduler loading issue (#3065)
* fix: correctly load ModelPatcher

* feat: do not load model at all, not needed
2024-05-31 22:24:19 +02:00
Manuel Schmid 64c29a8c43 feat: rework intermediate image display for restricted performances (#3050)
disable intermediate results for all performacnes with restricted features

make disable_intermediate_results interactive again even if performance has restricted features
users who want to disable this option should be able to do so, even if performance will be impacted
2024-05-30 16:17:36 +02:00
Manuel Schmid 4e658bb63a feat: optimize performance lora filtering in metadata (#3048)
* feat: add remove_performance_lora method

* feat: use class PerformanceLoRA instead of strings in config

* refactor: cleanup flags, use __member__ to check if enums contains key

* feat: only filter lora of selected performance instead of all performance LoRAs

* fix: disable intermediate results for all restricted performances

too fast for Gradio, which becomes a bottleneck

* refactor: rename parse_json to to_json, rename parse_string to to_string

* feat: use speed steps as default instead of hardcoded 30

* feat: add method to_steps to Performance

* refactor: remove method ordinal_suffix, not needed anymore

* feat: only filter lora of selected performance instead of all performance LoRAs

both metadata and history log

* feat: do not filter LoRAs in metadata parser but rather in metadata load action
2024-05-30 16:14:28 +02:00
Manuel Schmid 3ef663c5b7 fix: do not set textContent on undefined when no translation was given #2 (#3046)
* fix: do not set textContent on undefined when no translation was given
2024-05-29 20:33:15 +02:00
Manuel Schmid bf70815a66 fix: use default vae name instead of None on file refresh (#3045) 2024-05-29 19:49:07 +02:00
Manuel Schmid 725bf05c31 release: bump version to 2.4.1, update changelog (#3027) 2024-05-28 01:10:45 +02:00
Manuel Schmid 4a070a9d61 feat: build docker image tagged "edge" on push to main branch (#3026)
* feat: build docker image on push to main branch

* feat: add tag "edge" for main when building the docker image

* feat: update name of build container workflow
2024-05-28 00:49:47 +02:00
Manuel Schmid 0e621ae34e fix: add type check for undefined, use fallback when no translation for aspect ratios was given (#3025) 2024-05-28 00:09:39 +02:00
Manuel Schmid dfff9b7dcf fix: adjust clip skip default value from 1 to 2 (#3011)
* Revert "Revert "feat: add clip skip handling (#2999)" (#3008)"

This reverts commit 989a1ad52b.

* feat: use clip skip 2 as default
2024-05-27 00:28:22 +02:00
Manuel Schmid 989a1ad52b Revert "feat: add clip skip handling (#2999)" (#3008)
This reverts commit cc58fe5270.
2024-05-26 22:07:44 +02:00
Manuel Schmid de34023c79 fix: use translation for aspect ratios label (#3001)
use javascript code instead of python handling for updates for https://github.com/lllyasviel/Fooocus/pull/2590
2024-05-26 19:23:21 +02:00
Manuel Schmid 12dc2396f6 Merge pull request #3000 from lllyasviel/develop
Release 2.4.0
2024-05-26 18:18:53 +02:00
Manuel Schmid c227cf1f56 docs: update changelog 2024-05-26 18:16:18 +02:00
57d2f2a0dd feat: make ui settings more compact (#2590)
* Slightly more compact ui settings

Changed Radio to Dropdown.

* feat: change preset from option to select, add accordion for resolution

* feat: change title of aspect ratios accordion on load and update

* refactor: reorder image number slider, code cleanup

* fix: add missing scroll down for metadata tab

* fix: adjust indent

---------

Co-authored-by: Manuel Schmid <dev@mash1t.de>
Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>
2024-05-26 18:10:29 +02:00
Manuel Schmid 67289dd0fe release: bump version to 2.4.0, update changelog 2024-05-26 15:13:54 +02:00
Manuel Schmid cc58fe5270 feat: add clip skip handling (#2999) 2024-05-26 14:18:19 +02:00
Manuel Schmid 4e5509351f feat: remove labels from most of the image input fields (#2998) 2024-05-26 11:47:33 +02:00
Manuel Schmid 1d1a4a3ebd feat: add inpaint color picker (#2997)
Workaround as tool color-sketch applies changes directly to the image canvas and not the mask canvas.
Color picker is not correctly implemented in Gradio 3.41.2 => does always get displayed as separate containers and not merged with other elements
2024-05-26 11:40:15 +02:00
Alexdnk d850bca09f feat: read value 'CFG Mimicking from TSNR' (adaptive_cfg) from presets (#2990) 2024-05-24 22:05:28 +02:00
Manuel Schmid 04f64ab0bc feat: add translation for image size describe (#2992) 2024-05-24 21:58:17 +02:00
Manuel Schmid 7b70d27032 feat: configure line ending format LF for *.sh files (#2991) 2024-05-24 21:36:07 +02:00
4da5a68c10 feat: build and push container image for ghcr.io, update docker.md, and other related fixes (#2805)
* chore: update cuda version in container

* fix: use symlink to fix error libcuda.so: cannot open shared object file:

* fix: update docker entrypoint to use entry_with_update.py

* feat: add container build & push workflow

* fix: container action run conditions

* fix: container action versions

* fix: container action versions v2

* fix: docker action registry login and metadata

* docs: adjust docker documentation based on latest changes, add docs for podman and docker

* chore: replace image name env var with github.event.repository.name

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>

* chore: replace image name env var with github.event.repository.name (pt2)

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>

* fix: switch to semver versioning

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>

* fix: build only on versioned tags

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>

* fix: don't update in entrypoint

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>

* fix: remove dash in "docker-compose"

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>

* feat: sync pytorch for docker with version used in prepare_environment

* feat: update cuda to 12.4.1

* fix: correctly clone checked out version in builds, not always main

* refactor: remove irrelevant version in docker-compose.yml

---------

Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>
Co-authored-by: Manuel Schmid <dev@mash1t.de>
2024-05-23 00:19:54 +02:00
302bfdf855 feat: read size and ratio of an image and provide the recommended size (#2971)
* Add the information about the size and ratio of the read image

* feat: use available aspect ratios from config, move function to util, change default visibility of label

* refactor: extract sdxl aspect ratios to flags, use in describe

as discussed in
https://github.com/lllyasviel/Fooocus/pull/2971#discussion_r1608493765
https://github.com/lllyasviel/Fooocus/pull/2971#issuecomment-2123620595

---------

Co-authored-by: Manuel Schmid <dev@mash1t.de>
Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com>
2024-05-22 20:47:44 +02:00
Manuel Schmid 7537612bcc feat: only use valid inline loras, add subfolder support (#2968) 2024-05-20 19:21:41 +02:00
Manuel Schmid ac14d9d03c feat: change code owner from @lllyasviel to @mashb1t (#2948) 2024-05-20 17:33:12 +02:00
Manuel Schmid 65a8b25129 feat: inline lora optimisations (#2967)
* feat: add performance loras to the end of the loras array

* fix: resolve circular dependency for unit tests

* feat: allow multiple matches for each token, optimize and extract method cleanup_prompt

* fix: update unit tests

* feat: ignore custom wildcards
2024-05-20 17:31:51 +02:00
Manuel Schmid c995511705 feat: progress bar improvements (#2962)
* feat: align progress bar vertically

* feat: use fixed width for status text, remove ordinals

* refactor: align progress to actions
2024-05-19 20:43:11 +02:00
Manuel Schmid e94b97604f release: bump version number to 2.4.0-rc2 2024-05-19 18:37:18 +02:00
Manuel Schmid 35b74dfa64 feat: optimize model management of image censoring (#2960)
now follows general Fooocus model management principles + includes code optimisations for reusability
2024-05-19 18:36:47 +02:00
Manuel Schmid dad228907e fix: remove leftover code from hyper-sd8 testing (#2959) 2024-05-19 17:42:46 +02:00
40 changed files with 1186 additions and 318 deletions
+54 -1
View File
@@ -1 +1,54 @@
.idea __pycache__
*.ckpt
*.safetensors
*.pth
*.pt
*.bin
*.patch
*.backup
*.corrupted
*.partial
*.onnx
sorted_styles.json
/input
/cache
/language/default.json
/test_imgs
config.txt
config_modification_tutorial.txt
user_path_config.txt
user_path_config-deprecated.txt
/modules/*.png
/repositories
/fooocus_env
/venv
/tmp
/ui-config.json
/outputs
/config.json
/log
/webui.settings.bat
/embeddings
/styles.csv
/params.txt
/styles.csv.bak
/webui-user.bat
/webui-user.sh
/interrogate
/user.css
/.idea
/notification.ogg
/notification.mp3
/SwinIR
/textual_inversion
.vscode
/extensions
/test/stdout.txt
/test/stderr.txt
/cache.json*
/config_states/
/node_modules
/package-lock.json
/.coverage*
/auth.json
.DS_Store
+3
View File
@@ -0,0 +1,3 @@
# Ensure that shell scripts always use lf line endings, e.g. entrypoint.sh for docker
* text=auto
*.sh text eol=lf
+1 -1
View File
@@ -1 +1 @@
* @lllyasviel * @mashb1t
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
+47
View File
@@ -0,0 +1,47 @@
name: Docker image build
on:
push:
branches:
- main
tags:
- v*
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=edge,branch=main
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+2 -2
View File
@@ -1,4 +1,4 @@
FROM nvidia/cuda:12.3.1-base-ubuntu22.04 FROM nvidia/cuda:12.4.1-base-ubuntu22.04
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
ENV CMDARGS --listen ENV CMDARGS --listen
@@ -23,7 +23,7 @@ RUN chown -R user:user /content
WORKDIR /content WORKDIR /content
USER user USER user
RUN git clone https://github.com/lllyasviel/Fooocus /content/app COPY --chown=user:user . /content/app
RUN mv /content/app/models /content/app/models.org RUN mv /content/app/models /content/app/models.org
CMD [ "sh", "-c", "/content/entrypoint.sh ${CMDARGS}" ] CMD [ "sh", "-c", "/content/entrypoint.sh ${CMDARGS}" ]
-3
View File
@@ -1,7 +1,4 @@
import ldm_patched.modules.args_parser as args_parser import ldm_patched.modules.args_parser as args_parser
import os
from tempfile import gettempdir
args_parser.parser.add_argument("--share", action='store_true', help="Set whether to share on Gradio.") args_parser.parser.add_argument("--share", action='store_true', help="Set whether to share on Gradio.")
+32 -10
View File
@@ -27,6 +27,7 @@ progress {
border-radius: 5px; /* Round the corners of the progress bar */ border-radius: 5px; /* Round the corners of the progress bar */
background-color: #f3f3f3; /* Light grey background */ background-color: #f3f3f3; /* Light grey background */
width: 100%; width: 100%;
vertical-align: middle !important;
} }
/* Style the progress bar container */ /* Style the progress bar container */
@@ -69,30 +70,39 @@ progress::after {
height: 30px !important; height: 30px !important;
} }
.type_row{ .progress-bar span {
height: 80px !important; text-align: right;
width: 215px;
}
div:has(> #positive_prompt) {
border: none;
} }
.type_row_half{ #positive_prompt {
height: 32px !important; padding: 1px;
background: var(--background-fill-primary);
} }
.scroll-hide{ .type_row {
resize: none !important; height: 84px !important;
} }
.refresh_button{ .type_row_half {
height: 34px !important;
}
.refresh_button {
border: none !important; border: none !important;
background: none !important; background: none !important;
font-size: none !important; font-size: none !important;
box-shadow: none !important; box-shadow: none !important;
} }
.advanced_check_row{ .advanced_check_row {
width: 250px !important; width: 250px !important;
} }
.min_check{ .min_check {
min-width: min(1px, 100%) !important; min-width: min(1px, 100%) !important;
} }
@@ -101,10 +111,14 @@ progress::after {
overflow: auto !important; overflow: auto !important;
} }
.aspect_ratios label { .performance_selection label {
width: 140px !important; width: 140px !important;
} }
.aspect_ratios label {
flex: calc(50% - 5px) !important;
}
.aspect_ratios label span { .aspect_ratios label span {
white-space: nowrap !important; white-space: nowrap !important;
} }
@@ -393,4 +407,12 @@ progress::after {
text-align: center; text-align: center;
border-radius: 5px 5px 0px 0px; border-radius: 5px 5px 0px 0px;
display: none; /* remove this to enable tooltip in preview image */ display: none; /* remove this to enable tooltip in preview image */
}
#inpaint_canvas .canvas-tooltip-info {
top: 2px;
}
#inpaint_brush_color input[type=color]{
background: none;
} }
+1 -3
View File
@@ -1,12 +1,10 @@
version: '3.9'
volumes: volumes:
fooocus-data: fooocus-data:
services: services:
app: app:
build: . build: .
image: fooocus image: ghcr.io/lllyasviel/fooocus
ports: ports:
- "7865:7865" - "7865:7865"
environment: environment:
+73 -9
View File
@@ -1,35 +1,99 @@
# Fooocus on Docker # Fooocus on Docker
The docker image is based on NVIDIA CUDA 12.3 and PyTorch 2.0, see [Dockerfile](Dockerfile) and [requirements_docker.txt](requirements_docker.txt) for details. The docker image is based on NVIDIA CUDA 12.4 and PyTorch 2.1, see [Dockerfile](Dockerfile) and [requirements_docker.txt](requirements_docker.txt) for details.
## Requirements
- A computer with specs good enough to run Fooocus, and proprietary Nvidia drivers
- Docker, Docker Compose, or Podman
## Quick start ## Quick start
**This is just an easy way for testing. Please find more information in the [notes](#notes).** **More information in the [notes](#notes).**
### Running with Docker Compose
1. Clone this repository 1. Clone this repository
2. Build the image with `docker compose build` 2. Run the docker container with `docker compose up`.
3. Run the docker container with `docker compose up`. Building the image takes some time.
### Running with Docker
```sh
docker run -p 7865:7865 -v fooocus-data:/content/data -it \
--gpus all \
-e CMDARGS=--listen \
-e DATADIR=/content/data \
-e config_path=/content/data/config.txt \
-e config_example_path=/content/data/config_modification_tutorial.txt \
-e path_checkpoints=/content/data/models/checkpoints/ \
-e path_loras=/content/data/models/loras/ \
-e path_embeddings=/content/data/models/embeddings/ \
-e path_vae_approx=/content/data/models/vae_approx/ \
-e path_upscale_models=/content/data/models/upscale_models/ \
-e path_inpaint=/content/data/models/inpaint/ \
-e path_controlnet=/content/data/models/controlnet/ \
-e path_clip_vision=/content/data/models/clip_vision/ \
-e path_fooocus_expansion=/content/data/models/prompt_expansion/fooocus_expansion/ \
-e path_outputs=/content/app/outputs/ \
ghcr.io/lllyasviel/fooocus
```
### Running with Podman
```sh
podman run -p 7865:7865 -v fooocus-data:/content/data -it \
--security-opt=no-new-privileges --cap-drop=ALL --security-opt label=type:nvidia_container_t --device=nvidia.com/gpu=all \
-e CMDARGS=--listen \
-e DATADIR=/content/data \
-e config_path=/content/data/config.txt \
-e config_example_path=/content/data/config_modification_tutorial.txt \
-e path_checkpoints=/content/data/models/checkpoints/ \
-e path_loras=/content/data/models/loras/ \
-e path_embeddings=/content/data/models/embeddings/ \
-e path_vae_approx=/content/data/models/vae_approx/ \
-e path_upscale_models=/content/data/models/upscale_models/ \
-e path_inpaint=/content/data/models/inpaint/ \
-e path_controlnet=/content/data/models/controlnet/ \
-e path_clip_vision=/content/data/models/clip_vision/ \
-e path_fooocus_expansion=/content/data/models/prompt_expansion/fooocus_expansion/ \
-e path_outputs=/content/app/outputs/ \
ghcr.io/lllyasviel/fooocus
```
When you see the message `Use the app with http://0.0.0.0:7865/` in the console, you can access the URL in your browser. When you see the message `Use the app with http://0.0.0.0:7865/` in the console, you can access the URL in your browser.
Your models and outputs are stored in the `fooocus-data` volume, which, depending on OS, is stored in `/var/lib/docker/volumes`. Your models and outputs are stored in the `fooocus-data` volume, which, depending on OS, is stored in `/var/lib/docker/volumes/` (or `~/.local/share/containers/storage/volumes/` when using `podman`).
## Building the container locally
Clone the repository first, and open a terminal in the folder.
Build with `docker`:
```sh
docker build . -t fooocus
```
Build with `podman`:
```sh
podman build . -t fooocus
```
## Details ## Details
### Update the container manually ### Update the container manually (`docker compose`)
When you are using `docker compose up` continuously, the container is not updated to the latest version of Fooocus automatically. When you are using `docker compose up` continuously, the container is not updated to the latest version of Fooocus automatically.
Run `git pull` before executing `docker compose build --no-cache` to build an image with the latest Fooocus version. Run `git pull` before executing `docker compose build --no-cache` to build an image with the latest Fooocus version.
You can then start it with `docker compose up` You can then start it with `docker compose up`
### Import models, outputs ### Import models, outputs
If you want to import files from models or the outputs folder, you can uncomment the following settings in the [docker-compose.yml](docker-compose.yml):
If you want to import files from models or the outputs folder, you can add the following bind mounts in the [docker-compose.yml](docker-compose.yml) or your preferred method of running the container:
``` ```
#- ./models:/import/models # Once you import files, you don't need to mount again. #- ./models:/import/models # Once you import files, you don't need to mount again.
#- ./outputs:/import/outputs # Once you import files, you don't need to mount again. #- ./outputs:/import/outputs # Once you import files, you don't need to mount again.
``` ```
After running `docker compose up`, your files will be copied into `/content/data/models` and `/content/data/outputs` After running the container, your files will be copied into `/content/data/models` and `/content/data/outputs`
Since `/content/data` is a persistent volume folder, your files will be persisted even when you re-run `docker compose up --build` without above volume settings. Since `/content/data` is a persistent volume folder, your files will be persisted even when you re-run the container without the above mounts.
### Paths inside the container ### Paths inside the container
+50 -46
View File
@@ -1,56 +1,60 @@
# modified version of https://github.com/AUTOMATIC1111/stable-diffusion-webui-nsfw-censor/blob/master/scripts/censor.py
import numpy as np
import os import os
from extras.safety_checker.models.safety_checker import StableDiffusionSafetyChecker import numpy as np
from transformers import CLIPFeatureExtractor, CLIPConfig import torch
from PIL import Image from transformers import CLIPConfig, CLIPImageProcessor
import ldm_patched.modules.model_management as model_management
import modules.config import modules.config
from extras.safety_checker.models.safety_checker import StableDiffusionSafetyChecker
from ldm_patched.modules.model_patcher import ModelPatcher
safety_checker_repo_root = os.path.join(os.path.dirname(__file__), 'safety_checker') safety_checker_repo_root = os.path.join(os.path.dirname(__file__), 'safety_checker')
config_path = os.path.join(safety_checker_repo_root, "configs", "config.json") config_path = os.path.join(safety_checker_repo_root, "configs", "config.json")
preprocessor_config_path = os.path.join(safety_checker_repo_root, "configs", "preprocessor_config.json") preprocessor_config_path = os.path.join(safety_checker_repo_root, "configs", "preprocessor_config.json")
safety_feature_extractor = None
safety_checker = None class Censor:
def __init__(self):
self.safety_checker_model: ModelPatcher | None = None
self.clip_image_processor: CLIPImageProcessor | None = None
self.load_device = torch.device('cpu')
self.offload_device = torch.device('cpu')
def init(self):
if self.safety_checker_model is None and self.clip_image_processor is None:
safety_checker_model = modules.config.downloading_safety_checker_model()
self.clip_image_processor = CLIPImageProcessor.from_json_file(preprocessor_config_path)
clip_config = CLIPConfig.from_json_file(config_path)
model = StableDiffusionSafetyChecker.from_pretrained(safety_checker_model, config=clip_config)
model.eval()
self.load_device = model_management.text_encoder_device()
self.offload_device = model_management.text_encoder_offload_device()
model.to(self.offload_device)
self.safety_checker_model = ModelPatcher(model, load_device=self.load_device, offload_device=self.offload_device)
def censor(self, images: list | np.ndarray) -> list | np.ndarray:
self.init()
model_management.load_model_gpu(self.safety_checker_model)
single = False
if not isinstance(images, list) or isinstance(images, np.ndarray):
images = [images]
single = True
safety_checker_input = self.clip_image_processor(images, return_tensors="pt")
safety_checker_input.to(device=self.load_device)
checked_images, has_nsfw_concept = self.safety_checker_model.model(images=images,
clip_input=safety_checker_input.pixel_values)
checked_images = [image.astype(np.uint8) for image in checked_images]
if single:
checked_images = checked_images[0]
return checked_images
def numpy_to_pil(image): default_censor = Censor().censor
image = (image * 255).round().astype("uint8")
pil_image = Image.fromarray(image)
return pil_image
# check and replace nsfw content
def check_safety(x_image):
global safety_feature_extractor, safety_checker
if safety_feature_extractor is None or safety_checker is None:
safety_checker_model = modules.config.downloading_safety_checker_model()
safety_feature_extractor = CLIPFeatureExtractor.from_json_file(preprocessor_config_path)
clip_config = CLIPConfig.from_json_file(config_path)
safety_checker = StableDiffusionSafetyChecker.from_pretrained(safety_checker_model, config=clip_config)
safety_checker_input = safety_feature_extractor(numpy_to_pil(x_image), return_tensors="pt")
x_checked_image, has_nsfw_concept = safety_checker(images=x_image, clip_input=safety_checker_input.pixel_values)
return x_checked_image, has_nsfw_concept
def censor_single(x):
x_checked_image, has_nsfw_concept = check_safety(x)
# replace image with black pixels, keep dimensions
# workaround due to different numpy / pytorch image matrix format
if has_nsfw_concept[0]:
imageshape = x_checked_image.shape
x_checked_image = np.zeros((imageshape[0], imageshape[1], 3), dtype = np.uint8)
return x_checked_image
def censor_batch(images):
images = [censor_single(image) for image in images]
return images
+1 -1
View File
@@ -1 +1 @@
version = '2.4.0-rc1' version = '2.4.3'
+9
View File
@@ -80,6 +80,15 @@ function refresh_style_localization() {
processNode(document.querySelector('.style_selections')); processNode(document.querySelector('.style_selections'));
} }
function refresh_aspect_ratios_label(value) {
label = document.querySelector('#aspect_ratios_accordion div span');
translation = getTranslation("Aspect Ratios");
if (typeof translation == "undefined") {
translation = "Aspect Ratios";
}
label.textContent = translation + " " + htmlDecode(value);
}
function localizeWholePage() { function localizeWholePage() {
processNode(gradioApp()); processNode(gradioApp());
+5
View File
@@ -256,3 +256,8 @@ function set_theme(theme) {
window.location.replace(gradioURL + '?__theme=' + theme); window.location.replace(gradioURL + '?__theme=' + theme);
} }
} }
function htmlDecode(input) {
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}
+12 -3
View File
@@ -9,8 +9,15 @@
"Advanced": "Advanced", "Advanced": "Advanced",
"Upscale or Variation": "Upscale or Variation", "Upscale or Variation": "Upscale or Variation",
"Image Prompt": "Image Prompt", "Image Prompt": "Image Prompt",
"Inpaint or Outpaint (beta)": "Inpaint or Outpaint (beta)", "Inpaint or Outpaint": "Inpaint or Outpaint",
"Drag above image to here": "Drag above image to here", "Outpaint Direction": "Outpaint Direction",
"Method": "Method",
"Describe": "Describe",
"Content Type": "Content Type",
"Photograph": "Photograph",
"Art/Anime": "Art/Anime",
"Describe this Image into Prompt": "Describe this Image into Prompt",
"Image Size and Recommended Size": "Image Size and Recommended Size",
"Upscale or Variation:": "Upscale or Variation:", "Upscale or Variation:": "Upscale or Variation:",
"Disabled": "Disabled", "Disabled": "Disabled",
"Vary (Subtle)": "Vary (Subtle)", "Vary (Subtle)": "Vary (Subtle)",
@@ -313,6 +320,8 @@
"vae": "vae", "vae": "vae",
"CFG Mimicking from TSNR": "CFG Mimicking from TSNR", "CFG Mimicking from TSNR": "CFG Mimicking from TSNR",
"Enabling Fooocus's implementation of CFG mimicking for TSNR (effective when real CFG > mimicked CFG).": "Enabling Fooocus's implementation of CFG mimicking for TSNR (effective when real CFG > mimicked CFG).", "Enabling Fooocus's implementation of CFG mimicking for TSNR (effective when real CFG > mimicked CFG).": "Enabling Fooocus's implementation of CFG mimicking for TSNR (effective when real CFG > mimicked CFG).",
"CLIP Skip": "CLIP Skip",
"Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).": "Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).",
"Sampler": "Sampler", "Sampler": "Sampler",
"dpmpp_2m_sde_gpu": "dpmpp_2m_sde_gpu", "dpmpp_2m_sde_gpu": "dpmpp_2m_sde_gpu",
"Only effective in non-inpaint mode.": "Only effective in non-inpaint mode.", "Only effective in non-inpaint mode.": "Only effective in non-inpaint mode.",
@@ -384,7 +393,7 @@
"Fooocus Enhance": "Fooocus Enhance", "Fooocus Enhance": "Fooocus Enhance",
"Fooocus Cinematic": "Fooocus Cinematic", "Fooocus Cinematic": "Fooocus Cinematic",
"Fooocus Sharp": "Fooocus Sharp", "Fooocus Sharp": "Fooocus Sharp",
"Drag any image generated by Fooocus here": "Drag any image generated by Fooocus here", "For images created by Fooocus": "For images created by Fooocus",
"Metadata": "Metadata", "Metadata": "Metadata",
"Apply Metadata": "Apply Metadata", "Apply Metadata": "Apply Metadata",
"Metadata Scheme": "Metadata Scheme", "Metadata Scheme": "Metadata Scheme",
@@ -107,8 +107,7 @@ class SDTurboScheduler:
def get_sigmas(self, model, steps, denoise): def get_sigmas(self, model, steps, denoise):
start_step = 10 - int(10 * denoise) start_step = 10 - int(10 * denoise)
timesteps = torch.flip(torch.arange(1, 11) * 100 - 1, (0,))[start_step:start_step + steps] timesteps = torch.flip(torch.arange(1, 11) * 100 - 1, (0,))[start_step:start_step + steps]
ldm_patched.modules.model_management.load_models_gpu([model]) sigmas = model.model_sampling.sigma(timesteps)
sigmas = model.model.model_sampling.sigma(timesteps)
sigmas = torch.cat([sigmas, sigmas.new_zeros([1])]) sigmas = torch.cat([sigmas, sigmas.new_zeros([1])])
return (sigmas, ) return (sigmas, )
+10 -2
View File
@@ -108,7 +108,7 @@ class ModelSamplingContinuousEDM:
@classmethod @classmethod
def INPUT_TYPES(s): def INPUT_TYPES(s):
return {"required": { "model": ("MODEL",), return {"required": { "model": ("MODEL",),
"sampling": (["v_prediction", "eps"],), "sampling": (["v_prediction", "edm_playground_v2.5", "eps"],),
"sigma_max": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}), "sigma_max": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}),
"sigma_min": ("FLOAT", {"default": 0.002, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}), "sigma_min": ("FLOAT", {"default": 0.002, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}),
}} }}
@@ -121,17 +121,25 @@ class ModelSamplingContinuousEDM:
def patch(self, model, sampling, sigma_max, sigma_min): def patch(self, model, sampling, sigma_max, sigma_min):
m = model.clone() m = model.clone()
latent_format = None
sigma_data = 1.0
if sampling == "eps": if sampling == "eps":
sampling_type = ldm_patched.modules.model_sampling.EPS sampling_type = ldm_patched.modules.model_sampling.EPS
elif sampling == "v_prediction": elif sampling == "v_prediction":
sampling_type = ldm_patched.modules.model_sampling.V_PREDICTION sampling_type = ldm_patched.modules.model_sampling.V_PREDICTION
elif sampling == "edm_playground_v2.5":
sampling_type = ldm_patched.modules.model_sampling.EDM
sigma_data = 0.5
latent_format = ldm_patched.modules.latent_formats.SDXL_Playground_2_5()
class ModelSamplingAdvanced(ldm_patched.modules.model_sampling.ModelSamplingContinuousEDM, sampling_type): class ModelSamplingAdvanced(ldm_patched.modules.model_sampling.ModelSamplingContinuousEDM, sampling_type):
pass pass
model_sampling = ModelSamplingAdvanced(model.model.model_config) model_sampling = ModelSamplingAdvanced(model.model.model_config)
model_sampling.set_sigma_range(sigma_min, sigma_max) model_sampling.set_parameters(sigma_min, sigma_max, sigma_data)
m.add_object_patch("model_sampling", model_sampling) m.add_object_patch("model_sampling", model_sampling)
if latent_format is not None:
m.add_object_patch("latent_format", latent_format)
return (m, ) return (m, )
class RescaleCFG: class RescaleCFG:
+2
View File
@@ -832,5 +832,7 @@ def sample_tcd(model, x, sigmas, extra_args=None, callback=None, disable=None, n
if eta > 0 and sigmas[i + 1] > 0: if eta > 0 and sigmas[i + 1] > 0:
noise = noise_sampler(sigmas[i], sigmas[i + 1]) noise = noise_sampler(sigmas[i], sigmas[i + 1])
x = x / alpha_prod_s[i+1].sqrt() + noise * (sigmas[i+1]**2 + 1 - 1/alpha_prod_s[i+1]).sqrt() x = x / alpha_prod_s[i+1].sqrt() + noise * (sigmas[i+1]**2 + 1 - 1/alpha_prod_s[i+1]).sqrt()
else:
x *= torch.sqrt(1.0 + sigmas[i + 1] ** 2)
return x return x
+65
View File
@@ -1,3 +1,4 @@
import torch
class LatentFormat: class LatentFormat:
scale_factor = 1.0 scale_factor = 1.0
@@ -34,6 +35,70 @@ class SDXL(LatentFormat):
] ]
self.taesd_decoder_name = "taesdxl_decoder" self.taesd_decoder_name = "taesdxl_decoder"
class SDXL_Playground_2_5(LatentFormat):
def __init__(self):
self.scale_factor = 0.5
self.latents_mean = torch.tensor([-1.6574, 1.886, -1.383, 2.5155]).view(1, 4, 1, 1)
self.latents_std = torch.tensor([8.4927, 5.9022, 6.5498, 5.2299]).view(1, 4, 1, 1)
self.latent_rgb_factors = [
# R G B
[ 0.3920, 0.4054, 0.4549],
[-0.2634, -0.0196, 0.0653],
[ 0.0568, 0.1687, -0.0755],
[-0.3112, -0.2359, -0.2076]
]
self.taesd_decoder_name = "taesdxl_decoder"
def process_in(self, latent):
latents_mean = self.latents_mean.to(latent.device, latent.dtype)
latents_std = self.latents_std.to(latent.device, latent.dtype)
return (latent - latents_mean) * self.scale_factor / latents_std
def process_out(self, latent):
latents_mean = self.latents_mean.to(latent.device, latent.dtype)
latents_std = self.latents_std.to(latent.device, latent.dtype)
return latent * latents_std / self.scale_factor + latents_mean
class SD_X4(LatentFormat): class SD_X4(LatentFormat):
def __init__(self): def __init__(self):
self.scale_factor = 0.08333 self.scale_factor = 0.08333
self.latent_rgb_factors = [
[-0.2340, -0.3863, -0.3257],
[ 0.0994, 0.0885, -0.0908],
[-0.2833, -0.2349, -0.3741],
[ 0.2523, -0.0055, -0.1651]
]
class SC_Prior(LatentFormat):
def __init__(self):
self.scale_factor = 1.0
self.latent_rgb_factors = [
[-0.0326, -0.0204, -0.0127],
[-0.1592, -0.0427, 0.0216],
[ 0.0873, 0.0638, -0.0020],
[-0.0602, 0.0442, 0.1304],
[ 0.0800, -0.0313, -0.1796],
[-0.0810, -0.0638, -0.1581],
[ 0.1791, 0.1180, 0.0967],
[ 0.0740, 0.1416, 0.0432],
[-0.1745, -0.1888, -0.1373],
[ 0.2412, 0.1577, 0.0928],
[ 0.1908, 0.0998, 0.0682],
[ 0.0209, 0.0365, -0.0092],
[ 0.0448, -0.0650, -0.1728],
[-0.1658, -0.1045, -0.1308],
[ 0.0542, 0.1545, 0.1325],
[-0.0352, -0.1672, -0.2541]
]
class SC_B(LatentFormat):
def __init__(self):
self.scale_factor = 1.0 / 0.43
self.latent_rgb_factors = [
[ 0.1121, 0.2006, 0.1023],
[-0.2093, -0.0222, -0.0195],
[-0.3087, -0.1535, 0.0366],
[ 0.0290, -0.1574, -0.4078]
]
+82 -9
View File
@@ -1,7 +1,7 @@
import torch import torch
import numpy as np
from ldm_patched.ldm.modules.diffusionmodules.util import make_beta_schedule from ldm_patched.ldm.modules.diffusionmodules.util import make_beta_schedule
import math import math
import numpy as np
class EPS: class EPS:
def calculate_input(self, sigma, noise): def calculate_input(self, sigma, noise):
@@ -12,12 +12,28 @@ class EPS:
sigma = sigma.view(sigma.shape[:1] + (1,) * (model_output.ndim - 1)) sigma = sigma.view(sigma.shape[:1] + (1,) * (model_output.ndim - 1))
return model_input - model_output * sigma return model_input - model_output * sigma
def noise_scaling(self, sigma, noise, latent_image, max_denoise=False):
if max_denoise:
noise = noise * torch.sqrt(1.0 + sigma ** 2.0)
else:
noise = noise * sigma
noise += latent_image
return noise
def inverse_noise_scaling(self, sigma, latent):
return latent
class V_PREDICTION(EPS): class V_PREDICTION(EPS):
def calculate_denoised(self, sigma, model_output, model_input): def calculate_denoised(self, sigma, model_output, model_input):
sigma = sigma.view(sigma.shape[:1] + (1,) * (model_output.ndim - 1)) sigma = sigma.view(sigma.shape[:1] + (1,) * (model_output.ndim - 1))
return model_input * self.sigma_data ** 2 / (sigma ** 2 + self.sigma_data ** 2) - model_output * sigma * self.sigma_data / (sigma ** 2 + self.sigma_data ** 2) ** 0.5 return model_input * self.sigma_data ** 2 / (sigma ** 2 + self.sigma_data ** 2) - model_output * sigma * self.sigma_data / (sigma ** 2 + self.sigma_data ** 2) ** 0.5
class EDM(V_PREDICTION):
def calculate_denoised(self, sigma, model_output, model_input):
sigma = sigma.view(sigma.shape[:1] + (1,) * (model_output.ndim - 1))
return model_input * self.sigma_data ** 2 / (sigma ** 2 + self.sigma_data ** 2) + model_output * sigma * self.sigma_data / (sigma ** 2 + self.sigma_data ** 2) ** 0.5
class ModelSamplingDiscrete(torch.nn.Module): class ModelSamplingDiscrete(torch.nn.Module):
def __init__(self, model_config=None): def __init__(self, model_config=None):
@@ -42,21 +58,25 @@ class ModelSamplingDiscrete(torch.nn.Module):
else: else:
betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, cosine_s=cosine_s) betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, cosine_s=cosine_s)
alphas = 1. - betas alphas = 1. - betas
alphas_cumprod = torch.tensor(np.cumprod(alphas, axis=0), dtype=torch.float32) alphas_cumprod = torch.cumprod(alphas, dim=0)
# alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1])
timesteps, = betas.shape timesteps, = betas.shape
self.num_timesteps = int(timesteps) self.num_timesteps = int(timesteps)
self.linear_start = linear_start self.linear_start = linear_start
self.linear_end = linear_end self.linear_end = linear_end
# self.register_buffer('betas', torch.tensor(betas, dtype=torch.float32))
# self.register_buffer('alphas_cumprod', torch.tensor(alphas_cumprod, dtype=torch.float32))
# self.register_buffer('alphas_cumprod_prev', torch.tensor(alphas_cumprod_prev, dtype=torch.float32))
sigmas = ((1 - alphas_cumprod) / alphas_cumprod) ** 0.5 sigmas = ((1 - alphas_cumprod) / alphas_cumprod) ** 0.5
alphas_cumprod = torch.tensor(np.cumprod(alphas, axis=0), dtype=torch.float32)
self.set_sigmas(sigmas) self.set_sigmas(sigmas)
self.set_alphas_cumprod(alphas_cumprod.float()) self.set_alphas_cumprod(alphas_cumprod.float())
def set_sigmas(self, sigmas): def set_sigmas(self, sigmas):
self.register_buffer('sigmas', sigmas) self.register_buffer('sigmas', sigmas.float())
self.register_buffer('log_sigmas', sigmas.log()) self.register_buffer('log_sigmas', sigmas.log().float())
def set_alphas_cumprod(self, alphas_cumprod): def set_alphas_cumprod(self, alphas_cumprod):
self.register_buffer("alphas_cumprod", alphas_cumprod.float()) self.register_buffer("alphas_cumprod", alphas_cumprod.float())
@@ -94,8 +114,6 @@ class ModelSamplingDiscrete(torch.nn.Module):
class ModelSamplingContinuousEDM(torch.nn.Module): class ModelSamplingContinuousEDM(torch.nn.Module):
def __init__(self, model_config=None): def __init__(self, model_config=None):
super().__init__() super().__init__()
self.sigma_data = 1.0
if model_config is not None: if model_config is not None:
sampling_settings = model_config.sampling_settings sampling_settings = model_config.sampling_settings
else: else:
@@ -103,9 +121,11 @@ class ModelSamplingContinuousEDM(torch.nn.Module):
sigma_min = sampling_settings.get("sigma_min", 0.002) sigma_min = sampling_settings.get("sigma_min", 0.002)
sigma_max = sampling_settings.get("sigma_max", 120.0) sigma_max = sampling_settings.get("sigma_max", 120.0)
self.set_sigma_range(sigma_min, sigma_max) sigma_data = sampling_settings.get("sigma_data", 1.0)
self.set_parameters(sigma_min, sigma_max, sigma_data)
def set_sigma_range(self, sigma_min, sigma_max): def set_parameters(self, sigma_min, sigma_max, sigma_data):
self.sigma_data = sigma_data
sigmas = torch.linspace(math.log(sigma_min), math.log(sigma_max), 1000).exp() sigmas = torch.linspace(math.log(sigma_min), math.log(sigma_max), 1000).exp()
self.register_buffer('sigmas', sigmas) #for compatibility with some schedulers self.register_buffer('sigmas', sigmas) #for compatibility with some schedulers
@@ -134,3 +154,56 @@ class ModelSamplingContinuousEDM(torch.nn.Module):
log_sigma_min = math.log(self.sigma_min) log_sigma_min = math.log(self.sigma_min)
return math.exp((math.log(self.sigma_max) - log_sigma_min) * percent + log_sigma_min) return math.exp((math.log(self.sigma_max) - log_sigma_min) * percent + log_sigma_min)
class StableCascadeSampling(ModelSamplingDiscrete):
def __init__(self, model_config=None):
super().__init__()
if model_config is not None:
sampling_settings = model_config.sampling_settings
else:
sampling_settings = {}
self.set_parameters(sampling_settings.get("shift", 1.0))
def set_parameters(self, shift=1.0, cosine_s=8e-3):
self.shift = shift
self.cosine_s = torch.tensor(cosine_s)
self._init_alpha_cumprod = torch.cos(self.cosine_s / (1 + self.cosine_s) * torch.pi * 0.5) ** 2
#This part is just for compatibility with some schedulers in the codebase
self.num_timesteps = 10000
sigmas = torch.empty((self.num_timesteps), dtype=torch.float32)
for x in range(self.num_timesteps):
t = (x + 1) / self.num_timesteps
sigmas[x] = self.sigma(t)
self.set_sigmas(sigmas)
def sigma(self, timestep):
alpha_cumprod = (torch.cos((timestep + self.cosine_s) / (1 + self.cosine_s) * torch.pi * 0.5) ** 2 / self._init_alpha_cumprod)
if self.shift != 1.0:
var = alpha_cumprod
logSNR = (var/(1-var)).log()
logSNR += 2 * torch.log(1.0 / torch.tensor(self.shift))
alpha_cumprod = logSNR.sigmoid()
alpha_cumprod = alpha_cumprod.clamp(0.0001, 0.9999)
return ((1 - alpha_cumprod) / alpha_cumprod) ** 0.5
def timestep(self, sigma):
var = 1 / ((sigma * sigma) + 1)
var = var.clamp(0, 1.0)
s, min_var = self.cosine_s.to(var.device), self._init_alpha_cumprod.to(var.device)
t = (((var * min_var) ** 0.5).acos() / (torch.pi * 0.5)) * (1 + s) - s
return t
def percent_to_sigma(self, percent):
if percent <= 0.0:
return 999999999.9
if percent >= 1.0:
return 0.0
percent = 1.0 - percent
return self.sigma(torch.tensor(percent))
+1 -1
View File
@@ -523,7 +523,7 @@ class UNIPCBH2(Sampler):
KSAMPLER_NAMES = ["euler", "euler_ancestral", "heun", "heunpp2","dpm_2", "dpm_2_ancestral", KSAMPLER_NAMES = ["euler", "euler_ancestral", "heun", "heunpp2","dpm_2", "dpm_2_ancestral",
"lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_sde", "dpmpp_sde_gpu", "lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_sde", "dpmpp_sde_gpu",
"dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm", "tcd"] "dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm", "tcd", "edm_playground_v2.5"]
class KSAMPLER(Sampler): class KSAMPLER(Sampler):
def __init__(self, sampler_function, extra_options={}, inpaint_options={}): def __init__(self, sampler_function, extra_options={}, inpaint_options={}):
+61 -40
View File
@@ -44,12 +44,12 @@ def worker():
import fooocus_version import fooocus_version
import args_manager import args_manager
from extras.censor import censor_batch, censor_single from extras.censor import default_censor
from modules.sdxl_styles import apply_style, get_random_style, fooocus_expansion, apply_arrays, random_style_name from modules.sdxl_styles import apply_style, get_random_style, fooocus_expansion, apply_arrays, random_style_name
from modules.private_logger import log from modules.private_logger import log
from extras.expansion import safe_str from extras.expansion import safe_str
from modules.util import (remove_empty_str, HWC3, resize_image, get_image_shape_ceil, set_image_shape_ceil, from modules.util import (remove_empty_str, HWC3, resize_image, get_image_shape_ceil, set_image_shape_ceil,
get_shape_ceil, resample_image, erode_or_dilate, ordinal_suffix, get_enabled_loras, get_shape_ceil, resample_image, erode_or_dilate, get_enabled_loras,
parse_lora_references_from_prompt, apply_wildcards) parse_lora_references_from_prompt, apply_wildcards)
from modules.upscaler import perform_upscale from modules.upscaler import perform_upscale
from modules.flags import Performance from modules.flags import Performance
@@ -72,13 +72,13 @@ def worker():
async_task.yields.append(['preview', (number, text, None)]) async_task.yields.append(['preview', (number, text, None)])
def yield_result(async_task, imgs, black_out_nsfw, censor=True, do_not_show_finished_images=False, def yield_result(async_task, imgs, black_out_nsfw, censor=True, do_not_show_finished_images=False,
progressbar_index=13): progressbar_index=flags.preparation_step_count):
if not isinstance(imgs, list): if not isinstance(imgs, list):
imgs = [imgs] imgs = [imgs]
if censor and (modules.config.default_black_out_nsfw or black_out_nsfw): if censor and (modules.config.default_black_out_nsfw or black_out_nsfw):
progressbar(async_task, progressbar_index, 'Checking for NSFW content ...') progressbar(async_task, progressbar_index, 'Checking for NSFW content ...')
imgs = censor_batch(imgs) imgs = default_censor(imgs)
async_task.results = async_task.results + imgs async_task.results = async_task.results + imgs
@@ -174,6 +174,7 @@ def worker():
adm_scaler_negative = args.pop() adm_scaler_negative = args.pop()
adm_scaler_end = args.pop() adm_scaler_end = args.pop()
adaptive_cfg = args.pop() adaptive_cfg = args.pop()
clip_skip = args.pop()
sampler_name = args.pop() sampler_name = args.pop()
scheduler_name = args.pop() scheduler_name = args.pop()
vae_name = args.pop() vae_name = args.pop()
@@ -237,10 +238,12 @@ def worker():
steps = performance_selection.steps() steps = performance_selection.steps()
performance_loras = []
if performance_selection == Performance.EXTREME_SPEED: if performance_selection == Performance.EXTREME_SPEED:
print('Enter LCM mode.') print('Enter LCM mode.')
progressbar(async_task, 1, 'Downloading LCM components ...') progressbar(async_task, 1, 'Downloading LCM components ...')
loras += [(modules.config.downloading_sdxl_lcm_lora(), 1.0)] performance_loras += [(modules.config.downloading_sdxl_lcm_lora(), 1.0)]
if refiner_model_name != 'None': if refiner_model_name != 'None':
print(f'Refiner disabled in LCM mode.') print(f'Refiner disabled in LCM mode.')
@@ -259,7 +262,7 @@ def worker():
elif performance_selection == Performance.LIGHTNING: elif performance_selection == Performance.LIGHTNING:
print('Enter Lightning mode.') print('Enter Lightning mode.')
progressbar(async_task, 1, 'Downloading Lightning components ...') progressbar(async_task, 1, 'Downloading Lightning components ...')
loras += [(modules.config.downloading_sdxl_lightning_lora(), 1.0)] performance_loras += [(modules.config.downloading_sdxl_lightning_lora(), 1.0)]
if refiner_model_name != 'None': if refiner_model_name != 'None':
print(f'Refiner disabled in Lightning mode.') print(f'Refiner disabled in Lightning mode.')
@@ -278,7 +281,7 @@ def worker():
elif performance_selection == Performance.HYPER_SD: elif performance_selection == Performance.HYPER_SD:
print('Enter Hyper-SD mode.') print('Enter Hyper-SD mode.')
progressbar(async_task, 1, 'Downloading Hyper-SD components ...') progressbar(async_task, 1, 'Downloading Hyper-SD components ...')
loras += [(modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)] performance_loras += [(modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)]
if refiner_model_name != 'None': if refiner_model_name != 'None':
print(f'Refiner disabled in Hyper-SD mode.') print(f'Refiner disabled in Hyper-SD mode.')
@@ -294,15 +297,8 @@ def worker():
adm_scaler_negative = 1.0 adm_scaler_negative = 1.0
adm_scaler_end = 0.0 adm_scaler_end = 0.0
elif performance_selection == Performance.HYPER_SD8:
print('Enter Hyper-SD8 mode.')
progressbar(async_task, 1, 'Downloading Hyper-SD components ...')
loras += [(modules.config.downloading_sdxl_hyper_sd_cfg_lora(), 0.3)]
sampler_name = 'dpmpp_sde_gpu'
scheduler_name = 'normal'
print(f'[Parameters] Adaptive CFG = {adaptive_cfg}') print(f'[Parameters] Adaptive CFG = {adaptive_cfg}')
print(f'[Parameters] CLIP Skip = {clip_skip}')
print(f'[Parameters] Sharpness = {sharpness}') print(f'[Parameters] Sharpness = {sharpness}')
print(f'[Parameters] ControlNet Softness = {controlnet_softness}') print(f'[Parameters] ControlNet Softness = {controlnet_softness}')
print(f'[Parameters] ADM Scale = ' print(f'[Parameters] ADM Scale = '
@@ -464,14 +460,18 @@ def worker():
extra_positive_prompts = prompts[1:] if len(prompts) > 1 else [] extra_positive_prompts = prompts[1:] if len(prompts) > 1 else []
extra_negative_prompts = negative_prompts[1:] if len(negative_prompts) > 1 else [] extra_negative_prompts = negative_prompts[1:] if len(negative_prompts) > 1 else []
progressbar(async_task, 3, 'Loading models ...') progressbar(async_task, 2, 'Loading models ...')
loras = parse_lora_references_from_prompt(prompt, loras, modules.config.default_max_lora_number) lora_filenames = modules.util.remove_performance_lora(modules.config.lora_filenames, performance_selection)
loras, prompt = parse_lora_references_from_prompt(prompt, loras, modules.config.default_max_lora_number, lora_filenames=lora_filenames)
loras += performance_loras
pipeline.refresh_everything(refiner_model_name=refiner_model_name, base_model_name=base_model_name, pipeline.refresh_everything(refiner_model_name=refiner_model_name, base_model_name=base_model_name,
loras=loras, base_model_additional_loras=base_model_additional_loras, loras=loras, base_model_additional_loras=base_model_additional_loras,
use_synthetic_refiner=use_synthetic_refiner, vae_name=vae_name) use_synthetic_refiner=use_synthetic_refiner, vae_name=vae_name)
pipeline.set_clip_skip(clip_skip)
progressbar(async_task, 3, 'Processing prompts ...') progressbar(async_task, 3, 'Processing prompts ...')
tasks = [] tasks = []
@@ -531,25 +531,25 @@ def worker():
if use_expansion: if use_expansion:
for i, t in enumerate(tasks): for i, t in enumerate(tasks):
progressbar(async_task, 5, f'Preparing Fooocus text #{i + 1} ...') progressbar(async_task, 4, f'Preparing Fooocus text #{i + 1} ...')
expansion = pipeline.final_expansion(t['task_prompt'], t['task_seed']) expansion = pipeline.final_expansion(t['task_prompt'], t['task_seed'])
print(f'[Prompt Expansion] {expansion}') print(f'[Prompt Expansion] {expansion}')
t['expansion'] = expansion t['expansion'] = expansion
t['positive'] = copy.deepcopy(t['positive']) + [expansion] # Deep copy. t['positive'] = copy.deepcopy(t['positive']) + [expansion] # Deep copy.
for i, t in enumerate(tasks): for i, t in enumerate(tasks):
progressbar(async_task, 7, f'Encoding positive #{i + 1} ...') progressbar(async_task, 5, f'Encoding positive #{i + 1} ...')
t['c'] = pipeline.clip_encode(texts=t['positive'], pool_top_k=t['positive_top_k']) t['c'] = pipeline.clip_encode(texts=t['positive'], pool_top_k=t['positive_top_k'])
for i, t in enumerate(tasks): for i, t in enumerate(tasks):
if abs(float(cfg_scale) - 1.0) < 1e-4: if abs(float(cfg_scale) - 1.0) < 1e-4:
t['uc'] = pipeline.clone_cond(t['c']) t['uc'] = pipeline.clone_cond(t['c'])
else: else:
progressbar(async_task, 10, f'Encoding negative #{i + 1} ...') progressbar(async_task, 6, f'Encoding negative #{i + 1} ...')
t['uc'] = pipeline.clip_encode(texts=t['negative'], pool_top_k=t['negative_top_k']) t['uc'] = pipeline.clip_encode(texts=t['negative'], pool_top_k=t['negative_top_k'])
if len(goals) > 0: if len(goals) > 0:
progressbar(async_task, 13, 'Image processing ...') progressbar(async_task, 7, 'Image processing ...')
if 'vary' in goals: if 'vary' in goals:
if 'subtle' in uov_method: if 'subtle' in uov_method:
@@ -570,7 +570,7 @@ def worker():
uov_input_image = set_image_shape_ceil(uov_input_image, shape_ceil) uov_input_image = set_image_shape_ceil(uov_input_image, shape_ceil)
initial_pixels = core.numpy_to_pytorch(uov_input_image) initial_pixels = core.numpy_to_pytorch(uov_input_image)
progressbar(async_task, 13, 'VAE encoding ...') progressbar(async_task, 8, 'VAE encoding ...')
candidate_vae, _ = pipeline.get_candidate_vae( candidate_vae, _ = pipeline.get_candidate_vae(
steps=steps, steps=steps,
@@ -587,7 +587,7 @@ def worker():
if 'upscale' in goals: if 'upscale' in goals:
H, W, C = uov_input_image.shape H, W, C = uov_input_image.shape
progressbar(async_task, 13, f'Upscaling image from {str((H, W))} ...') progressbar(async_task, 9, f'Upscaling image from {str((H, W))} ...')
uov_input_image = perform_upscale(uov_input_image) uov_input_image = perform_upscale(uov_input_image)
print(f'Image upscaled.') print(f'Image upscaled.')
@@ -623,7 +623,8 @@ def worker():
d = [('Upscale (Fast)', 'upscale_fast', '2x')] d = [('Upscale (Fast)', 'upscale_fast', '2x')]
if modules.config.default_black_out_nsfw or black_out_nsfw: if modules.config.default_black_out_nsfw or black_out_nsfw:
progressbar(async_task, 100, 'Checking for NSFW content ...') progressbar(async_task, 100, 'Checking for NSFW content ...')
uov_input_image = censor_single(uov_input_image) uov_input_image = default_censor(uov_input_image)
progressbar(async_task, 100, 'Saving image to system ...')
uov_input_image_path = log(uov_input_image, d, output_format=output_format) uov_input_image_path = log(uov_input_image, d, output_format=output_format)
yield_result(async_task, uov_input_image_path, black_out_nsfw, False, do_not_show_finished_images=True) yield_result(async_task, uov_input_image_path, black_out_nsfw, False, do_not_show_finished_images=True)
return return
@@ -635,7 +636,7 @@ def worker():
denoising_strength = overwrite_upscale_strength denoising_strength = overwrite_upscale_strength
initial_pixels = core.numpy_to_pytorch(uov_input_image) initial_pixels = core.numpy_to_pytorch(uov_input_image)
progressbar(async_task, 13, 'VAE encoding ...') progressbar(async_task, 10, 'VAE encoding ...')
candidate_vae, _ = pipeline.get_candidate_vae( candidate_vae, _ = pipeline.get_candidate_vae(
steps=steps, steps=steps,
@@ -693,7 +694,7 @@ def worker():
do_not_show_finished_images=True) do_not_show_finished_images=True)
return return
progressbar(async_task, 13, 'VAE Inpaint encoding ...') progressbar(async_task, 11, 'VAE Inpaint encoding ...')
inpaint_pixel_fill = core.numpy_to_pytorch(inpaint_worker.current_task.interested_fill) inpaint_pixel_fill = core.numpy_to_pytorch(inpaint_worker.current_task.interested_fill)
inpaint_pixel_image = core.numpy_to_pytorch(inpaint_worker.current_task.interested_image) inpaint_pixel_image = core.numpy_to_pytorch(inpaint_worker.current_task.interested_image)
@@ -713,7 +714,7 @@ def worker():
latent_swap = None latent_swap = None
if candidate_vae_swap is not None: if candidate_vae_swap is not None:
progressbar(async_task, 13, 'VAE SD15 encoding ...') progressbar(async_task, 12, 'VAE SD15 encoding ...')
latent_swap = core.encode_vae( latent_swap = core.encode_vae(
vae=candidate_vae_swap, vae=candidate_vae_swap,
pixels=inpaint_pixel_fill)['samples'] pixels=inpaint_pixel_fill)['samples']
@@ -827,28 +828,46 @@ def worker():
if scheduler_name in ['lcm', 'tcd']: if scheduler_name in ['lcm', 'tcd']:
final_scheduler_name = 'sgm_uniform' final_scheduler_name = 'sgm_uniform'
if pipeline.final_unet is not None:
pipeline.final_unet = core.opModelSamplingDiscrete.patch( def patch_discrete(unet):
return core.opModelSamplingDiscrete.patch(
pipeline.final_unet, pipeline.final_unet,
sampling=scheduler_name, sampling=scheduler_name,
zsnr=False)[0] zsnr=False)[0]
if pipeline.final_unet is not None:
pipeline.final_unet = patch_discrete(pipeline.final_unet)
if pipeline.final_refiner_unet is not None: if pipeline.final_refiner_unet is not None:
pipeline.final_refiner_unet = core.opModelSamplingDiscrete.patch( pipeline.final_refiner_unet = patch_discrete(pipeline.final_refiner_unet)
pipeline.final_refiner_unet, print(f'Using {scheduler_name} scheduler.')
elif scheduler_name == 'edm_playground_v2.5':
final_scheduler_name = 'karras'
def patch_edm(unet):
return core.opModelSamplingContinuousEDM.patch(
unet,
sampling=scheduler_name, sampling=scheduler_name,
zsnr=False)[0] sigma_max=120.0,
sigma_min=0.002)[0]
if pipeline.final_unet is not None:
pipeline.final_unet = patch_edm(pipeline.final_unet)
if pipeline.final_refiner_unet is not None:
pipeline.final_refiner_unet = patch_edm(pipeline.final_refiner_unet)
print(f'Using {scheduler_name} scheduler.') print(f'Using {scheduler_name} scheduler.')
async_task.yields.append(['preview', (13, 'Moving model to GPU ...', None)]) async_task.yields.append(['preview', (flags.preparation_step_count, 'Moving model to GPU ...', None)])
def callback(step, x0, x, total_steps, y): def callback(step, x0, x, total_steps, y):
done_steps = current_task_id * steps + step done_steps = current_task_id * steps + step
async_task.yields.append(['preview', ( async_task.yields.append(['preview', (
int(15.0 + 85.0 * float(done_steps) / float(all_steps)), int(flags.preparation_step_count + (100 - flags.preparation_step_count) * float(done_steps) / float(all_steps)),
f'Step {step}/{total_steps} in the {current_task_id + 1}{ordinal_suffix(current_task_id + 1)} Sampling', f'Sampling step {step + 1}/{total_steps}, image {current_task_id + 1}/{image_number} ...', y)])
y)])
for current_task_id, task in enumerate(tasks): for current_task_id, task in enumerate(tasks):
current_progress = int(flags.preparation_step_count + (100 - flags.preparation_step_count) * float(current_task_id * steps) / float(all_steps))
progressbar(async_task, current_progress, f'Preparing task {current_task_id + 1}/{image_number} ...')
execution_start_time = time.perf_counter() execution_start_time = time.perf_counter()
try: try:
@@ -891,12 +910,12 @@ def worker():
imgs = [inpaint_worker.current_task.post_process(x) for x in imgs] imgs = [inpaint_worker.current_task.post_process(x) for x in imgs]
img_paths = [] img_paths = []
current_progress = int(flags.preparation_step_count + (100 - flags.preparation_step_count) * float((current_task_id + 1) * steps) / float(all_steps))
if modules.config.default_black_out_nsfw or black_out_nsfw: if modules.config.default_black_out_nsfw or black_out_nsfw:
progressbar(async_task, int(15.0 + 85.0 * float((current_task_id + 1) * steps) / float(all_steps)), progressbar(async_task, current_progress, 'Checking for NSFW content ...')
'Checking for NSFW content ...') imgs = default_censor(imgs)
imgs = censor_batch(imgs)
progressbar(async_task, current_progress, f'Saving image {current_task_id + 1}/{image_number} to system ...')
for x in imgs: for x in imgs:
d = [('Prompt', 'prompt', task['log_positive_prompt']), d = [('Prompt', 'prompt', task['log_positive_prompt']),
('Negative Prompt', 'negative_prompt', task['log_negative_prompt']), ('Negative Prompt', 'negative_prompt', task['log_negative_prompt']),
@@ -928,6 +947,8 @@ def worker():
d.append( d.append(
('CFG Mimicking from TSNR', 'adaptive_cfg', modules.patch.patch_settings[pid].adaptive_cfg)) ('CFG Mimicking from TSNR', 'adaptive_cfg', modules.patch.patch_settings[pid].adaptive_cfg))
if clip_skip > 1:
d.append(('CLIP Skip', 'clip_skip', clip_skip))
d.append(('Sampler', 'sampler', sampler_name)) d.append(('Sampler', 'sampler', sampler_name))
d.append(('Scheduler', 'scheduler', scheduler_name)) d.append(('Scheduler', 'scheduler', scheduler_name))
d.append(('VAE', 'vae', vae_name)) d.append(('VAE', 'vae', vae_name))
+94 -58
View File
@@ -2,14 +2,14 @@ import os
import json import json
import math import math
import numbers import numbers
import args_manager import args_manager
import tempfile import tempfile
import modules.flags import modules.flags
import modules.sdxl_styles import modules.sdxl_styles
from modules.model_loader import load_file_from_url from modules.model_loader import load_file_from_url
from modules.util import makedirs_with_log from modules.extra_utils import makedirs_with_log, get_files_from_folder, try_eval_env_var
from modules.extra_utils import get_files_from_folder
from modules.flags import OutputFormat, Performance, MetadataScheme from modules.flags import OutputFormat, Performance, MetadataScheme
@@ -201,7 +201,7 @@ path_safety_checker = get_dir_or_set_default('path_safety_checker', '../models/s
path_outputs = get_path_output() path_outputs = get_path_output()
def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False): def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False, expected_type=None):
global config_dict, visited_keys global config_dict, visited_keys
if key not in visited_keys: if key not in visited_keys:
@@ -209,6 +209,7 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
v = os.getenv(key) v = os.getenv(key)
if v is not None: if v is not None:
v = try_eval_env_var(v, expected_type)
print(f"Environment: {key} = {v}") print(f"Environment: {key} = {v}")
config_dict[key] = v config_dict[key] = v
@@ -253,41 +254,49 @@ temp_path = init_temp_path(get_config_item_or_set_default(
key='temp_path', key='temp_path',
default_value=default_temp_path, default_value=default_temp_path,
validator=lambda x: isinstance(x, str), validator=lambda x: isinstance(x, str),
expected_type=str
), default_temp_path) ), default_temp_path)
temp_path_cleanup_on_launch = get_config_item_or_set_default( temp_path_cleanup_on_launch = get_config_item_or_set_default(
key='temp_path_cleanup_on_launch', key='temp_path_cleanup_on_launch',
default_value=True, default_value=True,
validator=lambda x: isinstance(x, bool) validator=lambda x: isinstance(x, bool),
expected_type=bool
) )
default_base_model_name = default_model = get_config_item_or_set_default( default_base_model_name = default_model = get_config_item_or_set_default(
key='default_model', key='default_model',
default_value='model.safetensors', default_value='model.safetensors',
validator=lambda x: isinstance(x, str) validator=lambda x: isinstance(x, str),
expected_type=str
) )
previous_default_models = get_config_item_or_set_default( previous_default_models = get_config_item_or_set_default(
key='previous_default_models', key='previous_default_models',
default_value=[], default_value=[],
validator=lambda x: isinstance(x, list) and all(isinstance(k, str) for k in x) validator=lambda x: isinstance(x, list) and all(isinstance(k, str) for k in x),
expected_type=list
) )
default_refiner_model_name = default_refiner = get_config_item_or_set_default( default_refiner_model_name = default_refiner = get_config_item_or_set_default(
key='default_refiner', key='default_refiner',
default_value='None', default_value='None',
validator=lambda x: isinstance(x, str) validator=lambda x: isinstance(x, str),
expected_type=str
) )
default_refiner_switch = get_config_item_or_set_default( default_refiner_switch = get_config_item_or_set_default(
key='default_refiner_switch', key='default_refiner_switch',
default_value=0.8, default_value=0.8,
validator=lambda x: isinstance(x, numbers.Number) and 0 <= x <= 1 validator=lambda x: isinstance(x, numbers.Number) and 0 <= x <= 1,
expected_type=numbers.Number
) )
default_loras_min_weight = get_config_item_or_set_default( default_loras_min_weight = get_config_item_or_set_default(
key='default_loras_min_weight', key='default_loras_min_weight',
default_value=-2, default_value=-2,
validator=lambda x: isinstance(x, numbers.Number) and -10 <= x <= 10 validator=lambda x: isinstance(x, numbers.Number) and -10 <= x <= 10,
expected_type=numbers.Number
) )
default_loras_max_weight = get_config_item_or_set_default( default_loras_max_weight = get_config_item_or_set_default(
key='default_loras_max_weight', key='default_loras_max_weight',
default_value=2, default_value=2,
validator=lambda x: isinstance(x, numbers.Number) and -10 <= x <= 10 validator=lambda x: isinstance(x, numbers.Number) and -10 <= x <= 10,
expected_type=numbers.Number
) )
default_loras = get_config_item_or_set_default( default_loras = get_config_item_or_set_default(
key='default_loras', key='default_loras',
@@ -321,38 +330,45 @@ default_loras = get_config_item_or_set_default(
validator=lambda x: isinstance(x, list) and all( validator=lambda x: isinstance(x, list) and all(
len(y) == 3 and isinstance(y[0], bool) and isinstance(y[1], str) and isinstance(y[2], numbers.Number) len(y) == 3 and isinstance(y[0], bool) and isinstance(y[1], str) and isinstance(y[2], numbers.Number)
or len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number) or len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number)
for y in x) for y in x),
expected_type=list
) )
default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else (True, y[0], y[1]) for y in default_loras] default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else (True, y[0], y[1]) for y in default_loras]
default_max_lora_number = get_config_item_or_set_default( default_max_lora_number = get_config_item_or_set_default(
key='default_max_lora_number', key='default_max_lora_number',
default_value=len(default_loras) if isinstance(default_loras, list) and len(default_loras) > 0 else 5, default_value=len(default_loras) if isinstance(default_loras, list) and len(default_loras) > 0 else 5,
validator=lambda x: isinstance(x, int) and x >= 1 validator=lambda x: isinstance(x, int) and x >= 1,
expected_type=int
) )
default_cfg_scale = get_config_item_or_set_default( default_cfg_scale = get_config_item_or_set_default(
key='default_cfg_scale', key='default_cfg_scale',
default_value=7.0, default_value=7.0,
validator=lambda x: isinstance(x, numbers.Number) validator=lambda x: isinstance(x, numbers.Number),
expected_type=numbers.Number
) )
default_sample_sharpness = get_config_item_or_set_default( default_sample_sharpness = get_config_item_or_set_default(
key='default_sample_sharpness', key='default_sample_sharpness',
default_value=2.0, default_value=2.0,
validator=lambda x: isinstance(x, numbers.Number) validator=lambda x: isinstance(x, numbers.Number),
expected_type=numbers.Number
) )
default_sampler = get_config_item_or_set_default( default_sampler = get_config_item_or_set_default(
key='default_sampler', key='default_sampler',
default_value='dpmpp_2m_sde_gpu', default_value='dpmpp_2m_sde_gpu',
validator=lambda x: x in modules.flags.sampler_list validator=lambda x: x in modules.flags.sampler_list,
expected_type=str
) )
default_scheduler = get_config_item_or_set_default( default_scheduler = get_config_item_or_set_default(
key='default_scheduler', key='default_scheduler',
default_value='karras', default_value='karras',
validator=lambda x: x in modules.flags.scheduler_list validator=lambda x: x in modules.flags.scheduler_list,
expected_type=str
) )
default_vae = get_config_item_or_set_default( default_vae = get_config_item_or_set_default(
key='default_vae', key='default_vae',
default_value=modules.flags.default_vae, default_value=modules.flags.default_vae,
validator=lambda x: isinstance(x, str) validator=lambda x: isinstance(x, str),
expected_type=str
) )
default_styles = get_config_item_or_set_default( default_styles = get_config_item_or_set_default(
key='default_styles', key='default_styles',
@@ -361,122 +377,144 @@ default_styles = get_config_item_or_set_default(
"Fooocus Enhance", "Fooocus Enhance",
"Fooocus Sharp" "Fooocus Sharp"
], ],
validator=lambda x: isinstance(x, list) and all(y in modules.sdxl_styles.legal_style_names for y in x) validator=lambda x: isinstance(x, list) and all(y in modules.sdxl_styles.legal_style_names for y in x),
expected_type=list
) )
default_prompt_negative = get_config_item_or_set_default( default_prompt_negative = get_config_item_or_set_default(
key='default_prompt_negative', key='default_prompt_negative',
default_value='', default_value='',
validator=lambda x: isinstance(x, str), validator=lambda x: isinstance(x, str),
disable_empty_as_none=True disable_empty_as_none=True,
expected_type=str
) )
default_prompt = get_config_item_or_set_default( default_prompt = get_config_item_or_set_default(
key='default_prompt', key='default_prompt',
default_value='', default_value='',
validator=lambda x: isinstance(x, str), validator=lambda x: isinstance(x, str),
disable_empty_as_none=True disable_empty_as_none=True,
expected_type=str
) )
default_performance = get_config_item_or_set_default( default_performance = get_config_item_or_set_default(
key='default_performance', key='default_performance',
default_value=Performance.SPEED.value, default_value=Performance.SPEED.value,
validator=lambda x: x in Performance.list() validator=lambda x: x in Performance.list(),
expected_type=str
) )
default_advanced_checkbox = get_config_item_or_set_default( default_advanced_checkbox = get_config_item_or_set_default(
key='default_advanced_checkbox', key='default_advanced_checkbox',
default_value=False, default_value=False,
validator=lambda x: isinstance(x, bool) validator=lambda x: isinstance(x, bool),
expected_type=bool
) )
default_max_image_number = get_config_item_or_set_default( default_max_image_number = get_config_item_or_set_default(
key='default_max_image_number', key='default_max_image_number',
default_value=32, default_value=32,
validator=lambda x: isinstance(x, int) and x >= 1 validator=lambda x: isinstance(x, int) and x >= 1,
expected_type=int
) )
default_output_format = get_config_item_or_set_default( default_output_format = get_config_item_or_set_default(
key='default_output_format', key='default_output_format',
default_value='png', default_value='png',
validator=lambda x: x in OutputFormat.list() validator=lambda x: x in OutputFormat.list(),
expected_type=str
) )
default_image_number = get_config_item_or_set_default( default_image_number = get_config_item_or_set_default(
key='default_image_number', key='default_image_number',
default_value=2, default_value=2,
validator=lambda x: isinstance(x, int) and 1 <= x <= default_max_image_number validator=lambda x: isinstance(x, int) and 1 <= x <= default_max_image_number,
expected_type=int
) )
checkpoint_downloads = get_config_item_or_set_default( checkpoint_downloads = get_config_item_or_set_default(
key='checkpoint_downloads', key='checkpoint_downloads',
default_value={}, default_value={},
validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()) validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()),
expected_type=dict
) )
lora_downloads = get_config_item_or_set_default( lora_downloads = get_config_item_or_set_default(
key='lora_downloads', key='lora_downloads',
default_value={}, default_value={},
validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()) validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()),
expected_type=dict
) )
embeddings_downloads = get_config_item_or_set_default( embeddings_downloads = get_config_item_or_set_default(
key='embeddings_downloads', key='embeddings_downloads',
default_value={}, default_value={},
validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()) validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()),
expected_type=dict
) )
available_aspect_ratios = get_config_item_or_set_default( available_aspect_ratios = get_config_item_or_set_default(
key='available_aspect_ratios', key='available_aspect_ratios',
default_value=[ default_value=modules.flags.sdxl_aspect_ratios,
'704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152', validator=lambda x: isinstance(x, list) and all('*' in v for v in x) and len(x) > 1,
'896*1152', '896*1088', '960*1088', '960*1024', '1024*1024', '1024*960', expected_type=list
'1088*960', '1088*896', '1152*896', '1152*832', '1216*832', '1280*768',
'1344*768', '1344*704', '1408*704', '1472*704', '1536*640', '1600*640',
'1664*576', '1728*576'
],
validator=lambda x: isinstance(x, list) and all('*' in v for v in x) and len(x) > 1
) )
default_aspect_ratio = get_config_item_or_set_default( default_aspect_ratio = get_config_item_or_set_default(
key='default_aspect_ratio', key='default_aspect_ratio',
default_value='1152*896' if '1152*896' in available_aspect_ratios else available_aspect_ratios[0], default_value='1152*896' if '1152*896' in available_aspect_ratios else available_aspect_ratios[0],
validator=lambda x: x in available_aspect_ratios validator=lambda x: x in available_aspect_ratios,
expected_type=str
) )
default_inpaint_engine_version = get_config_item_or_set_default( default_inpaint_engine_version = get_config_item_or_set_default(
key='default_inpaint_engine_version', key='default_inpaint_engine_version',
default_value='v2.6', default_value='v2.6',
validator=lambda x: x in modules.flags.inpaint_engine_versions validator=lambda x: x in modules.flags.inpaint_engine_versions,
expected_type=str
) )
default_cfg_tsnr = get_config_item_or_set_default( default_cfg_tsnr = get_config_item_or_set_default(
key='default_cfg_tsnr', key='default_cfg_tsnr',
default_value=7.0, default_value=7.0,
validator=lambda x: isinstance(x, numbers.Number) validator=lambda x: isinstance(x, numbers.Number),
expected_type=numbers.Number
)
default_clip_skip = get_config_item_or_set_default(
key='default_clip_skip',
default_value=2,
validator=lambda x: isinstance(x, int) and 1 <= x <= modules.flags.clip_skip_max,
expected_type=int
) )
default_overwrite_step = get_config_item_or_set_default( default_overwrite_step = get_config_item_or_set_default(
key='default_overwrite_step', key='default_overwrite_step',
default_value=-1, default_value=-1,
validator=lambda x: isinstance(x, int) validator=lambda x: isinstance(x, int),
expected_type=int
) )
default_overwrite_switch = get_config_item_or_set_default( default_overwrite_switch = get_config_item_or_set_default(
key='default_overwrite_switch', key='default_overwrite_switch',
default_value=-1, default_value=-1,
validator=lambda x: isinstance(x, int) validator=lambda x: isinstance(x, int),
expected_type=int
) )
example_inpaint_prompts = get_config_item_or_set_default( example_inpaint_prompts = get_config_item_or_set_default(
key='example_inpaint_prompts', key='example_inpaint_prompts',
default_value=[ default_value=[
'highly detailed face', 'detailed girl face', 'detailed man face', 'detailed hand', 'beautiful eyes' 'highly detailed face', 'detailed girl face', 'detailed man face', 'detailed hand', 'beautiful eyes'
], ],
validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x) validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x),
expected_type=list
) )
default_black_out_nsfw = get_config_item_or_set_default( default_black_out_nsfw = get_config_item_or_set_default(
key='default_black_out_nsfw', key='default_black_out_nsfw',
default_value=False, default_value=False,
validator=lambda x: isinstance(x, bool) validator=lambda x: isinstance(x, bool),
expected_type=bool
) )
default_save_metadata_to_images = get_config_item_or_set_default( default_save_metadata_to_images = get_config_item_or_set_default(
key='default_save_metadata_to_images', key='default_save_metadata_to_images',
default_value=False, default_value=False,
validator=lambda x: isinstance(x, bool) validator=lambda x: isinstance(x, bool),
expected_type=bool
) )
default_metadata_scheme = get_config_item_or_set_default( default_metadata_scheme = get_config_item_or_set_default(
key='default_metadata_scheme', key='default_metadata_scheme',
default_value=MetadataScheme.FOOOCUS.value, default_value=MetadataScheme.FOOOCUS.value,
validator=lambda x: x in [y[1] for y in modules.flags.metadata_scheme if y[1] == x] validator=lambda x: x in [y[1] for y in modules.flags.metadata_scheme if y[1] == x],
expected_type=str
) )
metadata_created_by = get_config_item_or_set_default( metadata_created_by = get_config_item_or_set_default(
key='metadata_created_by', key='metadata_created_by',
default_value='', default_value='',
validator=lambda x: isinstance(x, str) validator=lambda x: isinstance(x, str),
expected_type=str
) )
example_inpaint_prompts = [[x] for x in example_inpaint_prompts] example_inpaint_prompts = [[x] for x in example_inpaint_prompts]
@@ -494,6 +532,8 @@ possible_preset_keys = {
"default_loras": "<processed>", "default_loras": "<processed>",
"default_cfg_scale": "guidance_scale", "default_cfg_scale": "guidance_scale",
"default_sample_sharpness": "sharpness", "default_sample_sharpness": "sharpness",
"default_cfg_tsnr": "adaptive_cfg",
"default_clip_skip": "clip_skip",
"default_sampler": "sampler", "default_sampler": "sampler",
"default_scheduler": "scheduler", "default_scheduler": "scheduler",
"default_overwrite_step": "steps", "default_overwrite_step": "steps",
@@ -527,7 +567,7 @@ def add_ratio(x):
default_aspect_ratio = add_ratio(default_aspect_ratio) default_aspect_ratio = add_ratio(default_aspect_ratio)
available_aspect_ratios = [add_ratio(x) for x in available_aspect_ratios] available_aspect_ratios_labels = [add_ratio(x) for x in available_aspect_ratios]
# Only write config in the first launch. # Only write config in the first launch.
@@ -551,11 +591,6 @@ lora_filenames = []
vae_filenames = [] vae_filenames = []
wildcard_filenames = [] wildcard_filenames = []
sdxl_lcm_lora = 'sdxl_lcm_lora.safetensors'
sdxl_lightning_lora = 'sdxl_lightning_4step_lora.safetensors'
sdxl_hyper_sd_lora = 'sdxl_hyper_sd_4step_lora.safetensors'
loras_metadata_remove = [sdxl_lcm_lora, sdxl_lightning_lora, sdxl_hyper_sd_lora]
def get_model_filenames(folder_paths, extensions=None, name_filter=None): def get_model_filenames(folder_paths, extensions=None, name_filter=None):
if extensions is None: if extensions is None:
@@ -622,26 +657,27 @@ def downloading_sdxl_lcm_lora():
load_file_from_url( load_file_from_url(
url='https://huggingface.co/lllyasviel/misc/resolve/main/sdxl_lcm_lora.safetensors', url='https://huggingface.co/lllyasviel/misc/resolve/main/sdxl_lcm_lora.safetensors',
model_dir=paths_loras[0], model_dir=paths_loras[0],
file_name=sdxl_lcm_lora file_name=modules.flags.PerformanceLoRA.EXTREME_SPEED.value
) )
return sdxl_lcm_lora return modules.flags.PerformanceLoRA.EXTREME_SPEED.value
def downloading_sdxl_lightning_lora(): def downloading_sdxl_lightning_lora():
load_file_from_url( load_file_from_url(
url='https://huggingface.co/mashb1t/misc/resolve/main/sdxl_lightning_4step_lora.safetensors', url='https://huggingface.co/mashb1t/misc/resolve/main/sdxl_lightning_4step_lora.safetensors',
model_dir=paths_loras[0], model_dir=paths_loras[0],
file_name=sdxl_lightning_lora file_name=modules.flags.PerformanceLoRA.LIGHTNING.value
) )
return sdxl_lightning_lora return modules.flags.PerformanceLoRA.LIGHTNING.value
def downloading_sdxl_hyper_sd_lora(): def downloading_sdxl_hyper_sd_lora():
load_file_from_url( load_file_from_url(
url='https://huggingface.co/mashb1t/misc/resolve/main/sdxl_hyper_sd_4step_lora.safetensors', url='https://huggingface.co/mashb1t/misc/resolve/main/sdxl_hyper_sd_4step_lora.safetensors',
model_dir=paths_loras[0], model_dir=paths_loras[0],
file_name=sdxl_hyper_sd_lora file_name=modules.flags.PerformanceLoRA.HYPER_SD.value
) )
return sdxl_hyper_sd_lora return modules.flags.PerformanceLoRA.HYPER_SD.value
def downloading_controlnet_canny(): def downloading_controlnet_canny():
+2 -2
View File
@@ -21,8 +21,7 @@ from modules.lora import match_lora
from modules.util import get_file_from_folder_list from modules.util import get_file_from_folder_list
from ldm_patched.modules.lora import model_lora_keys_unet, model_lora_keys_clip from ldm_patched.modules.lora import model_lora_keys_unet, model_lora_keys_clip
from modules.config import path_embeddings from modules.config import path_embeddings
from ldm_patched.contrib.external_model_advanced import ModelSamplingDiscrete from ldm_patched.contrib.external_model_advanced import ModelSamplingDiscrete, ModelSamplingContinuousEDM
opEmptyLatentImage = EmptyLatentImage() opEmptyLatentImage = EmptyLatentImage()
opVAEDecode = VAEDecode() opVAEDecode = VAEDecode()
@@ -32,6 +31,7 @@ opVAEEncodeTiled = VAEEncodeTiled()
opControlNetApplyAdvanced = ControlNetApplyAdvanced() opControlNetApplyAdvanced = ControlNetApplyAdvanced()
opFreeU = FreeU_V2() opFreeU = FreeU_V2()
opModelSamplingDiscrete = ModelSamplingDiscrete() opModelSamplingDiscrete = ModelSamplingDiscrete()
opModelSamplingContinuousEDM = ModelSamplingContinuousEDM()
class StableDiffusionModel: class StableDiffusionModel:
+11
View File
@@ -201,6 +201,17 @@ def clip_encode(texts, pool_top_k=1):
return [[torch.cat(cond_list, dim=1), {"pooled_output": pooled_acc}]] return [[torch.cat(cond_list, dim=1), {"pooled_output": pooled_acc}]]
@torch.no_grad()
@torch.inference_mode()
def set_clip_skip(clip_skip: int):
global final_clip
if final_clip is None:
return
final_clip.clip_layer(-abs(clip_skip))
return
@torch.no_grad() @torch.no_grad()
@torch.inference_mode() @torch.inference_mode()
def clear_all_caches(): def clear_all_caches():
+21
View File
@@ -1,4 +1,12 @@
import os import os
from ast import literal_eval
def makedirs_with_log(path):
try:
os.makedirs(path, exist_ok=True)
except OSError as error:
print(f'Directory {path} could not be created, reason: {error}')
def get_files_from_folder(folder_path, extensions=None, name_filter=None): def get_files_from_folder(folder_path, extensions=None, name_filter=None):
@@ -18,3 +26,16 @@ def get_files_from_folder(folder_path, extensions=None, name_filter=None):
filenames.append(path) filenames.append(path)
return filenames return filenames
def try_eval_env_var(value: str, expected_type=None):
try:
value_eval = value
if expected_type is bool:
value_eval = value.title()
value_eval = literal_eval(value_eval)
if expected_type is not None and not isinstance(value_eval, expected_type):
return value
return value_eval
except:
return value
+29 -3
View File
@@ -48,12 +48,14 @@ SAMPLERS = KSAMPLER | SAMPLER_EXTRA
KSAMPLER_NAMES = list(KSAMPLER.keys()) KSAMPLER_NAMES = list(KSAMPLER.keys())
SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "lcm", "turbo", "align_your_steps", "tcd"] SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "lcm", "turbo", "align_your_steps", "tcd", "edm_playground_v2.5"]
SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys()) SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys())
sampler_list = SAMPLER_NAMES sampler_list = SAMPLER_NAMES
scheduler_list = SCHEDULER_NAMES scheduler_list = SCHEDULER_NAMES
clip_skip_max = 12
default_vae = 'Default (model)' default_vae = 'Default (model)'
refiner_swap_method = 'joint' refiner_swap_method = 'joint'
@@ -81,6 +83,14 @@ inpaint_options = [inpaint_option_default, inpaint_option_detail, inpaint_option
desc_type_photo = 'Photograph' desc_type_photo = 'Photograph'
desc_type_anime = 'Art/Anime' desc_type_anime = 'Art/Anime'
sdxl_aspect_ratios = [
'704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152',
'896*1152', '896*1088', '960*1088', '960*1024', '1024*1024', '1024*960',
'1088*960', '1088*896', '1152*896', '1152*832', '1216*832', '1280*768',
'1344*768', '1344*704', '1408*704', '1472*704', '1536*640', '1600*640',
'1664*576', '1728*576'
]
class MetadataScheme(Enum): class MetadataScheme(Enum):
FOOOCUS = 'fooocus' FOOOCUS = 'fooocus'
@@ -93,6 +103,7 @@ metadata_scheme = [
] ]
controlnet_image_count = 4 controlnet_image_count = 4
preparation_step_count = 13
class OutputFormat(Enum): class OutputFormat(Enum):
@@ -105,6 +116,14 @@ class OutputFormat(Enum):
return list(map(lambda c: c.value, cls)) return list(map(lambda c: c.value, cls))
class PerformanceLoRA(Enum):
QUALITY = None
SPEED = None
EXTREME_SPEED = 'sdxl_lcm_lora.safetensors'
LIGHTNING = 'sdxl_lightning_4step_lora.safetensors'
HYPER_SD = 'sdxl_hyper_sd_4step_lora.safetensors'
class Steps(IntEnum): class Steps(IntEnum):
QUALITY = 60 QUALITY = 60
SPEED = 30 SPEED = 30
@@ -132,6 +151,10 @@ class Performance(Enum):
def list(cls) -> list: def list(cls) -> list:
return list(map(lambda c: c.value, cls)) return list(map(lambda c: c.value, cls))
@classmethod
def by_steps(cls, steps: int | str):
return cls[Steps(int(steps)).name]
@classmethod @classmethod
def has_restricted_features(cls, x) -> bool: def has_restricted_features(cls, x) -> bool:
if isinstance(x, Performance): if isinstance(x, Performance):
@@ -139,7 +162,10 @@ class Performance(Enum):
return x in [cls.EXTREME_SPEED.value, cls.LIGHTNING.value, cls.HYPER_SD.value] return x in [cls.EXTREME_SPEED.value, cls.LIGHTNING.value, cls.HYPER_SD.value]
def steps(self) -> int | None: def steps(self) -> int | None:
return Steps[self.name].value if Steps[self.name] else None return Steps[self.name].value if self.name in Steps.__members__ else None
def steps_uov(self) -> int | None: def steps_uov(self) -> int | None:
return StepsUOV[self.name].value if Steps[self.name] else None return StepsUOV[self.name].value if self.name in StepsUOV.__members__ else None
def lora_filename(self) -> str | None:
return PerformanceLoRA[self.name].value if self.name in PerformanceLoRA.__members__ else None
+41 -39
View File
@@ -32,18 +32,19 @@ def load_parameter_button_click(raw_metadata: dict | str, is_generating: bool):
get_str('prompt', 'Prompt', loaded_parameter_dict, results) get_str('prompt', 'Prompt', loaded_parameter_dict, results)
get_str('negative_prompt', 'Negative Prompt', loaded_parameter_dict, results) get_str('negative_prompt', 'Negative Prompt', loaded_parameter_dict, results)
get_list('styles', 'Styles', loaded_parameter_dict, results) get_list('styles', 'Styles', loaded_parameter_dict, results)
get_str('performance', 'Performance', loaded_parameter_dict, results) performance = get_str('performance', 'Performance', loaded_parameter_dict, results)
get_steps('steps', 'Steps', loaded_parameter_dict, results) get_steps('steps', 'Steps', loaded_parameter_dict, results)
get_float('overwrite_switch', 'Overwrite Switch', loaded_parameter_dict, results) get_number('overwrite_switch', 'Overwrite Switch', loaded_parameter_dict, results)
get_resolution('resolution', 'Resolution', loaded_parameter_dict, results) get_resolution('resolution', 'Resolution', loaded_parameter_dict, results)
get_float('guidance_scale', 'Guidance Scale', loaded_parameter_dict, results) get_number('guidance_scale', 'Guidance Scale', loaded_parameter_dict, results)
get_float('sharpness', 'Sharpness', loaded_parameter_dict, results) get_number('sharpness', 'Sharpness', loaded_parameter_dict, results)
get_adm_guidance('adm_guidance', 'ADM Guidance', loaded_parameter_dict, results) get_adm_guidance('adm_guidance', 'ADM Guidance', loaded_parameter_dict, results)
get_str('refiner_swap_method', 'Refiner Swap Method', loaded_parameter_dict, results) get_str('refiner_swap_method', 'Refiner Swap Method', loaded_parameter_dict, results)
get_float('adaptive_cfg', 'CFG Mimicking from TSNR', loaded_parameter_dict, results) get_number('adaptive_cfg', 'CFG Mimicking from TSNR', loaded_parameter_dict, results)
get_number('clip_skip', 'CLIP Skip', loaded_parameter_dict, results, cast_type=int)
get_str('base_model', 'Base Model', loaded_parameter_dict, results) get_str('base_model', 'Base Model', loaded_parameter_dict, results)
get_str('refiner_model', 'Refiner Model', loaded_parameter_dict, results) get_str('refiner_model', 'Refiner Model', loaded_parameter_dict, results)
get_float('refiner_switch', 'Refiner Switch', loaded_parameter_dict, results) get_number('refiner_switch', 'Refiner Switch', loaded_parameter_dict, results)
get_str('sampler', 'Sampler', loaded_parameter_dict, results) get_str('sampler', 'Sampler', loaded_parameter_dict, results)
get_str('scheduler', 'Scheduler', loaded_parameter_dict, results) get_str('scheduler', 'Scheduler', loaded_parameter_dict, results)
get_str('vae', 'VAE', loaded_parameter_dict, results) get_str('vae', 'VAE', loaded_parameter_dict, results)
@@ -58,19 +59,27 @@ def load_parameter_button_click(raw_metadata: dict | str, is_generating: bool):
get_freeu('freeu', 'FreeU', loaded_parameter_dict, results) get_freeu('freeu', 'FreeU', loaded_parameter_dict, results)
# prevent performance LoRAs to be added twice, by performance and by lora
performance_filename = None
if performance is not None and performance in Performance.list():
performance = Performance(performance)
performance_filename = performance.lora_filename()
for i in range(modules.config.default_max_lora_number): for i in range(modules.config.default_max_lora_number):
get_lora(f'lora_combined_{i + 1}', f'LoRA {i + 1}', loaded_parameter_dict, results) get_lora(f'lora_combined_{i + 1}', f'LoRA {i + 1}', loaded_parameter_dict, results, performance_filename)
return results return results
def get_str(key: str, fallback: str | None, source_dict: dict, results: list, default=None): def get_str(key: str, fallback: str | None, source_dict: dict, results: list, default=None) -> str | None:
try: try:
h = source_dict.get(key, source_dict.get(fallback, default)) h = source_dict.get(key, source_dict.get(fallback, default))
assert isinstance(h, str) assert isinstance(h, str)
results.append(h) results.append(h)
return h
except: except:
results.append(gr.update()) results.append(gr.update())
return None
def get_list(key: str, fallback: str | None, source_dict: dict, results: list, default=None): def get_list(key: str, fallback: str | None, source_dict: dict, results: list, default=None):
@@ -83,11 +92,11 @@ def get_list(key: str, fallback: str | None, source_dict: dict, results: list, d
results.append(gr.update()) results.append(gr.update())
def get_float(key: str, fallback: str | None, source_dict: dict, results: list, default=None): def get_number(key: str, fallback: str | None, source_dict: dict, results: list, default=None, cast_type=float):
try: try:
h = source_dict.get(key, source_dict.get(fallback, default)) h = source_dict.get(key, source_dict.get(fallback, default))
assert h is not None assert h is not None
h = float(h) h = cast_type(h)
results.append(h) results.append(h)
except: except:
results.append(gr.update()) results.append(gr.update())
@@ -124,7 +133,7 @@ def get_resolution(key: str, fallback: str | None, source_dict: dict, results: l
h = source_dict.get(key, source_dict.get(fallback, default)) h = source_dict.get(key, source_dict.get(fallback, default))
width, height = eval(h) width, height = eval(h)
formatted = modules.config.add_ratio(f'{width}*{height}') formatted = modules.config.add_ratio(f'{width}*{height}')
if formatted in modules.config.available_aspect_ratios: if formatted in modules.config.available_aspect_ratios_labels:
results.append(formatted) results.append(formatted)
results.append(-1) results.append(-1)
results.append(-1) results.append(-1)
@@ -180,7 +189,7 @@ def get_freeu(key: str, fallback: str | None, source_dict: dict, results: list,
results.append(gr.update()) results.append(gr.update())
def get_lora(key: str, fallback: str | None, source_dict: dict, results: list): def get_lora(key: str, fallback: str | None, source_dict: dict, results: list, performance_filename: str | None):
try: try:
split_data = source_dict.get(key, source_dict.get(fallback)).split(' : ') split_data = source_dict.get(key, source_dict.get(fallback)).split(' : ')
enabled = True enabled = True
@@ -192,6 +201,9 @@ def get_lora(key: str, fallback: str | None, source_dict: dict, results: list):
name = split_data[1] name = split_data[1]
weight = split_data[2] weight = split_data[2]
if name == performance_filename:
raise Exception
weight = float(weight) weight = float(weight)
results.append(enabled) results.append(enabled)
results.append(name) results.append(name)
@@ -205,7 +217,6 @@ def get_lora(key: str, fallback: str | None, source_dict: dict, results: list):
def get_sha256(filepath): def get_sha256(filepath):
global hash_cache global hash_cache
if filepath not in hash_cache: if filepath not in hash_cache:
# is_safetensors = os.path.splitext(filepath)[1].lower() == '.safetensors'
hash_cache[filepath] = sha256(filepath) hash_cache[filepath] = sha256(filepath)
return hash_cache[filepath] return hash_cache[filepath]
@@ -248,7 +259,7 @@ class MetadataParser(ABC):
self.full_prompt: str = '' self.full_prompt: str = ''
self.raw_negative_prompt: str = '' self.raw_negative_prompt: str = ''
self.full_negative_prompt: str = '' self.full_negative_prompt: str = ''
self.steps: int = 30 self.steps: int = Steps.SPEED.value
self.base_model_name: str = '' self.base_model_name: str = ''
self.base_model_hash: str = '' self.base_model_hash: str = ''
self.refiner_model_name: str = '' self.refiner_model_name: str = ''
@@ -261,11 +272,11 @@ class MetadataParser(ABC):
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def parse_json(self, metadata: dict | str) -> dict: def to_json(self, metadata: dict | str) -> dict:
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def parse_string(self, metadata: dict) -> str: def to_string(self, metadata: dict) -> str:
raise NotImplementedError raise NotImplementedError
def set_data(self, raw_prompt, full_prompt, raw_negative_prompt, full_negative_prompt, steps, base_model_name, def set_data(self, raw_prompt, full_prompt, raw_negative_prompt, full_negative_prompt, steps, base_model_name,
@@ -293,12 +304,6 @@ class MetadataParser(ABC):
self.loras.append((Path(lora_name).stem, lora_weight, lora_hash)) self.loras.append((Path(lora_name).stem, lora_weight, lora_hash))
self.vae_name = Path(vae_name).stem self.vae_name = Path(vae_name).stem
@staticmethod
def remove_special_loras(lora_filenames):
for lora_to_remove in modules.config.loras_metadata_remove:
if lora_to_remove in lora_filenames:
lora_filenames.remove(lora_to_remove)
class A1111MetadataParser(MetadataParser): class A1111MetadataParser(MetadataParser):
def get_scheme(self) -> MetadataScheme: def get_scheme(self) -> MetadataScheme:
@@ -321,6 +326,7 @@ class A1111MetadataParser(MetadataParser):
'adm_guidance': 'ADM Guidance', 'adm_guidance': 'ADM Guidance',
'refiner_swap_method': 'Refiner Swap Method', 'refiner_swap_method': 'Refiner Swap Method',
'adaptive_cfg': 'Adaptive CFG', 'adaptive_cfg': 'Adaptive CFG',
'clip_skip': 'Clip skip',
'overwrite_switch': 'Overwrite Switch', 'overwrite_switch': 'Overwrite Switch',
'freeu': 'FreeU', 'freeu': 'FreeU',
'base_model': 'Model', 'base_model': 'Model',
@@ -333,7 +339,7 @@ class A1111MetadataParser(MetadataParser):
'version': 'Version' 'version': 'Version'
} }
def parse_json(self, metadata: str) -> dict: def to_json(self, metadata: str) -> dict:
metadata_prompt = '' metadata_prompt = ''
metadata_negative_prompt = '' metadata_negative_prompt = ''
@@ -387,9 +393,9 @@ class A1111MetadataParser(MetadataParser):
data['styles'] = str(found_styles) data['styles'] = str(found_styles)
# try to load performance based on steps, fallback for direct A1111 imports # try to load performance based on steps, fallback for direct A1111 imports
if 'steps' in data and 'performance' not in data: if 'steps' in data and 'performance' in data is None:
try: try:
data['performance'] = Performance[Steps(int(data['steps'])).name].value data['performance'] = Performance.by_steps(data['steps']).value
except ValueError | KeyError: except ValueError | KeyError:
pass pass
@@ -415,13 +421,11 @@ class A1111MetadataParser(MetadataParser):
lora_data = data['lora_hashes'] lora_data = data['lora_hashes']
if lora_data != '': if lora_data != '':
lora_filenames = modules.config.lora_filenames.copy()
self.remove_special_loras(lora_filenames)
for li, lora in enumerate(lora_data.split(', ')): for li, lora in enumerate(lora_data.split(', ')):
lora_split = lora.split(': ') lora_split = lora.split(': ')
lora_name = lora_split[0] lora_name = lora_split[0]
lora_weight = lora_split[2] if len(lora_split) == 3 else lora_split[1] lora_weight = lora_split[2] if len(lora_split) == 3 else lora_split[1]
for filename in lora_filenames: for filename in modules.config.lora_filenames:
path = Path(filename) path = Path(filename)
if lora_name == path.stem: if lora_name == path.stem:
data[f'lora_combined_{li + 1}'] = f'{filename} : {lora_weight}' data[f'lora_combined_{li + 1}'] = f'{filename} : {lora_weight}'
@@ -429,7 +433,7 @@ class A1111MetadataParser(MetadataParser):
return data return data
def parse_string(self, metadata: dict) -> str: def to_string(self, metadata: dict) -> str:
data = {k: v for _, k, v in metadata} data = {k: v for _, k, v in metadata}
width, height = eval(data['resolution']) width, height = eval(data['resolution'])
@@ -467,7 +471,7 @@ class A1111MetadataParser(MetadataParser):
self.fooocus_to_a1111['refiner_model_hash']: self.refiner_model_hash self.fooocus_to_a1111['refiner_model_hash']: self.refiner_model_hash
} }
for key in ['adaptive_cfg', 'overwrite_switch', 'refiner_swap_method', 'freeu']: for key in ['adaptive_cfg', 'clip_skip', 'overwrite_switch', 'refiner_swap_method', 'freeu']:
if key in data: if key in data:
generation_params[self.fooocus_to_a1111[key]] = data[key] generation_params[self.fooocus_to_a1111[key]] = data[key]
@@ -509,26 +513,22 @@ class FooocusMetadataParser(MetadataParser):
def get_scheme(self) -> MetadataScheme: def get_scheme(self) -> MetadataScheme:
return MetadataScheme.FOOOCUS return MetadataScheme.FOOOCUS
def parse_json(self, metadata: dict) -> dict: def to_json(self, metadata: dict) -> dict:
model_filenames = modules.config.model_filenames.copy()
lora_filenames = modules.config.lora_filenames.copy()
vae_filenames = modules.config.vae_filenames.copy()
self.remove_special_loras(lora_filenames)
for key, value in metadata.items(): for key, value in metadata.items():
if value in ['', 'None']: if value in ['', 'None']:
continue continue
if key in ['base_model', 'refiner_model']: if key in ['base_model', 'refiner_model']:
metadata[key] = self.replace_value_with_filename(key, value, model_filenames) metadata[key] = self.replace_value_with_filename(key, value, modules.config.model_filenames)
elif key.startswith('lora_combined_'): elif key.startswith('lora_combined_'):
metadata[key] = self.replace_value_with_filename(key, value, lora_filenames) metadata[key] = self.replace_value_with_filename(key, value, modules.config.lora_filenames)
elif key == 'vae': elif key == 'vae':
metadata[key] = self.replace_value_with_filename(key, value, vae_filenames) metadata[key] = self.replace_value_with_filename(key, value, modules.config.vae_filenames)
else: else:
continue continue
return metadata return metadata
def parse_string(self, metadata: list) -> str: def to_string(self, metadata: list) -> str:
for li, (label, key, value) in enumerate(metadata): for li, (label, key, value) in enumerate(metadata):
# remove model folder paths from metadata # remove model folder paths from metadata
if key.startswith('lora_combined_'): if key.startswith('lora_combined_'):
@@ -568,6 +568,8 @@ class FooocusMetadataParser(MetadataParser):
elif value == path.stem: elif value == path.stem:
return filename return filename
return None
def get_metadata_parser(metadata_scheme: MetadataScheme) -> MetadataParser: def get_metadata_parser(metadata_scheme: MetadataScheme) -> MetadataParser:
match metadata_scheme: match metadata_scheme:
+1 -1
View File
@@ -27,7 +27,7 @@ def log(img, metadata, metadata_parser: MetadataParser | None = None, output_for
date_string, local_temp_filename, only_name = generate_temp_filename(folder=path_outputs, extension=output_format) date_string, local_temp_filename, only_name = generate_temp_filename(folder=path_outputs, extension=output_format)
os.makedirs(os.path.dirname(local_temp_filename), exist_ok=True) os.makedirs(os.path.dirname(local_temp_filename), exist_ok=True)
parsed_parameters = metadata_parser.parse_string(metadata.copy()) if metadata_parser is not None else '' parsed_parameters = metadata_parser.to_string(metadata.copy()) if metadata_parser is not None else ''
image = Image.fromarray(img) image = Image.fromarray(img)
if output_format == OutputFormat.PNG.value: if output_format == OutputFormat.PNG.value:
+1 -1
View File
@@ -175,7 +175,7 @@ def calculate_sigmas_scheduler_hacked(model, scheduler_name, steps):
elif scheduler_name == "sgm_uniform": elif scheduler_name == "sgm_uniform":
sigmas = normal_scheduler(model, steps, sgm=True) sigmas = normal_scheduler(model, steps, sgm=True)
elif scheduler_name == "turbo": elif scheduler_name == "turbo":
sigmas = SDTurboScheduler().get_sigmas(namedtuple('Patcher', ['model'])(model=model), steps=steps, denoise=1.0)[0] sigmas = SDTurboScheduler().get_sigmas(model=model, steps=steps, denoise=1.0)[0]
elif scheduler_name == "align_your_steps": elif scheduler_name == "align_your_steps":
model_type = 'SDXL' if isinstance(model.latent_format, ldm_patched.modules.latent_formats.SDXL) else 'SD1' model_type = 'SDXL' if isinstance(model.latent_format, ldm_patched.modules.latent_formats.SDXL) else 'SD1'
sigmas = AlignYourStepsScheduler().get_sigmas(model_type=model_type, steps=steps, denoise=1.0)[0] sigmas = AlignYourStepsScheduler().get_sigmas(model_type=model_type, steps=steps, denoise=1.0)[0]
+107 -16
View File
@@ -1,3 +1,5 @@
from pathlib import Path
import numpy as np import numpy as np
import datetime import datetime
import random import random
@@ -12,15 +14,16 @@ import hashlib
from PIL import Image from PIL import Image
import modules.config
import modules.sdxl_styles import modules.sdxl_styles
from modules.flags import Performance
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS)
# Regexp compiled once. Matches entries with the following pattern: # Regexp compiled once. Matches entries with the following pattern:
# <lora:some_lora:1> # <lora:some_lora:1>
# <lora:aNotherLora:-1.6> # <lora:aNotherLora:-1.6>
LORAS_PROMPT_PATTERN = re.compile(r".* <lora : ([^:]+) : ([+-]? (?: (?:\d+ (?:\.\d*)?) | (?:\.\d+)))> .*", re.X) LORAS_PROMPT_PATTERN = re.compile(r"(<lora:([^:]+):([+-]?(?:\d+(?:\.\d*)?|\.\d+))>)", re.X)
HASH_SHA256_LENGTH = 10 HASH_SHA256_LENGTH = 10
@@ -360,6 +363,14 @@ def is_json(data: str) -> bool:
return True return True
def get_filname_by_stem(lora_name, filenames: List[str]) -> str | None:
for filename in filenames:
path = Path(filename)
if lora_name == path.stem:
return filename
return None
def get_file_from_folder_list(name, folders): def get_file_from_folder_list(name, folders):
if not isinstance(folders, list): if not isinstance(folders, list):
folders = [folders] folders = [folders]
@@ -372,10 +383,6 @@ def get_file_from_folder_list(name, folders):
return os.path.abspath(os.path.realpath(os.path.join(folders[0], name))) return os.path.abspath(os.path.realpath(os.path.join(folders[0], name)))
def ordinal_suffix(number: int) -> str:
return 'th' if 10 <= number % 100 <= 20 else {1: 'st', 2: 'nd', 3: 'rd'}.get(number % 10, 'th')
def makedirs_with_log(path): def makedirs_with_log(path):
try: try:
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
@@ -383,24 +390,85 @@ def makedirs_with_log(path):
print(f'Directory {path} could not be created, reason: {error}') print(f'Directory {path} could not be created, reason: {error}')
def get_enabled_loras(loras: list) -> list: def get_enabled_loras(loras: list, remove_none=True) -> list:
return [(lora[1], lora[2]) for lora in loras if lora[0]] return [(lora[1], lora[2]) for lora in loras if lora[0] and (lora[1] != 'None' if remove_none else True)]
def parse_lora_references_from_prompt(prompt: str, loras: List[Tuple[AnyStr, float]], loras_limit: int = 5) -> List[Tuple[AnyStr, float]]: def parse_lora_references_from_prompt(prompt: str, loras: List[Tuple[AnyStr, float]], loras_limit: int = 5,
skip_file_check=False, prompt_cleanup=True, deduplicate_loras=True,
lora_filenames=None) -> tuple[List[Tuple[AnyStr, float]], str]:
if lora_filenames is None:
lora_filenames = []
found_loras = []
prompt_without_loras = ''
cleaned_prompt = ''
for token in prompt.split(','):
matches = LORAS_PROMPT_PATTERN.findall(token)
if len(matches) == 0:
prompt_without_loras += token + ', '
continue
for match in matches:
lora_name = match[1] + '.safetensors'
if not skip_file_check:
lora_name = get_filname_by_stem(match[1], lora_filenames)
if lora_name is not None:
found_loras.append((lora_name, float(match[2])))
token = token.replace(match[0], '')
prompt_without_loras += token + ', '
if prompt_without_loras != '':
cleaned_prompt = prompt_without_loras[:-2]
if prompt_cleanup:
cleaned_prompt = cleanup_prompt(prompt_without_loras)
new_loras = [] new_loras = []
lora_names = [lora[0] for lora in loras]
for found_lora in found_loras:
if deduplicate_loras and (found_lora[0] in lora_names or found_lora in new_loras):
continue
new_loras.append(found_lora)
if len(new_loras) == 0:
return loras, cleaned_prompt
updated_loras = [] updated_loras = []
for token in prompt.split(","):
m = LORAS_PROMPT_PATTERN.match(token)
if m:
new_loras.append((f"{m.group(1)}.safetensors", float(m.group(2))))
for lora in loras + new_loras: for lora in loras + new_loras:
if lora[0] != "None": if lora[0] != "None":
updated_loras.append(lora) updated_loras.append(lora)
return updated_loras[:loras_limit] return updated_loras[:loras_limit], cleaned_prompt
def remove_performance_lora(filenames: list, performance: Performance | None):
loras_without_performance = filenames.copy()
if performance is None:
return loras_without_performance
performance_lora = performance.lora_filename()
for filename in filenames:
path = Path(filename)
if performance_lora == path.name:
loras_without_performance.remove(filename)
return loras_without_performance
def cleanup_prompt(prompt):
prompt = re.sub(' +', ' ', prompt)
prompt = re.sub(',+', ',', prompt)
cleaned_prompt = ''
for token in prompt.split(','):
token = token.strip()
if token == '':
continue
cleaned_prompt += token + ', '
return cleaned_prompt[:-2]
def apply_wildcards(wildcard_text, rng, i, read_wildcards_in_order) -> str: def apply_wildcards(wildcard_text, rng, i, read_wildcards_in_order) -> str:
@@ -428,3 +496,26 @@ def apply_wildcards(wildcard_text, rng, i, read_wildcards_in_order) -> str:
print(f'[Wildcards] BFS stack overflow. Current text: {wildcard_text}') print(f'[Wildcards] BFS stack overflow. Current text: {wildcard_text}')
return wildcard_text return wildcard_text
def get_image_size_info(image: np.ndarray, aspect_ratios: list) -> str:
try:
image = Image.fromarray(np.uint8(image))
width, height = image.size
ratio = round(width / height, 2)
gcd = math.gcd(width, height)
lcm_ratio = f'{width // gcd}:{height // gcd}'
size_info = f'Image Size: {width} x {height}, Ratio: {ratio}, {lcm_ratio}'
closest_ratio = min(aspect_ratios, key=lambda x: abs(ratio - float(x.split('*')[0]) / float(x.split('*')[1])))
recommended_width, recommended_height = map(int, closest_ratio.split('*'))
recommended_ratio = round(recommended_width / recommended_height, 2)
recommended_gcd = math.gcd(recommended_width, recommended_height)
recommended_lcm_ratio = f'{recommended_width // recommended_gcd}:{recommended_height // recommended_gcd}'
size_info = f'{width} x {height}, {ratio}, {lcm_ratio}'
size_info += f'\n{recommended_width} x {recommended_height}, {recommended_ratio}, {recommended_lcm_ratio}'
return size_info
except Exception as e:
return f'Error reading image: {e}'
+1
View File
@@ -2,5 +2,6 @@
!anime.json !anime.json
!default.json !default.json
!lcm.json !lcm.json
!playground_v2.5.json
!realistic.json !realistic.json
!sai.json !sai.json
+51
View File
@@ -0,0 +1,51 @@
{
"default_model": "playground-v2.5-1024px-aesthetic.fp16.safetensors",
"default_refiner": "None",
"default_refiner_switch": 0.5,
"default_loras": [
[
true,
"None",
1.0
],
[
true,
"None",
1.0
],
[
true,
"None",
1.0
],
[
true,
"None",
1.0
],
[
true,
"None",
1.0
]
],
"default_cfg_scale": 3.0,
"default_sample_sharpness": 2.0,
"default_sampler": "dpmpp_2m",
"default_scheduler": "edm_playground_v2.5",
"default_performance": "Speed",
"default_prompt": "",
"default_prompt_negative": "",
"default_styles": [
"Fooocus V2",
"Fooocus Enhance",
"Fooocus Sharp"
],
"default_aspect_ratio": "1024*1024",
"checkpoint_downloads": {
"playground-v2.5-1024px-aesthetic.fp16.safetensors": "https://huggingface.co/mashb1t/fav_models/resolve/main/fav/playground-v2.5-1024px-aesthetic.fp16.safetensors"
},
"embeddings_downloads": {},
"lora_downloads": {},
"previous_default_models": []
}
+23 -12
View File
@@ -370,25 +370,36 @@ entry_with_update.py [-h] [--listen [IP]] [--port PORT]
[--web-upload-size WEB_UPLOAD_SIZE] [--web-upload-size WEB_UPLOAD_SIZE]
[--hf-mirror HF_MIRROR] [--hf-mirror HF_MIRROR]
[--external-working-path PATH [PATH ...]] [--external-working-path PATH [PATH ...]]
[--output-path OUTPUT_PATH] [--temp-path TEMP_PATH] [--output-path OUTPUT_PATH]
[--temp-path TEMP_PATH]
[--cache-path CACHE_PATH] [--in-browser] [--cache-path CACHE_PATH] [--in-browser]
[--disable-in-browser] [--gpu-device-id DEVICE_ID] [--disable-in-browser]
[--gpu-device-id DEVICE_ID]
[--async-cuda-allocation | --disable-async-cuda-allocation] [--async-cuda-allocation | --disable-async-cuda-allocation]
[--disable-attention-upcast] [--all-in-fp32 | --all-in-fp16] [--disable-attention-upcast]
[--all-in-fp32 | --all-in-fp16]
[--unet-in-bf16 | --unet-in-fp16 | --unet-in-fp8-e4m3fn | --unet-in-fp8-e5m2] [--unet-in-bf16 | --unet-in-fp16 | --unet-in-fp8-e4m3fn | --unet-in-fp8-e5m2]
[--vae-in-fp16 | --vae-in-fp32 | --vae-in-bf16] [--vae-in-fp16 | --vae-in-fp32 | --vae-in-bf16]
[--vae-in-cpu]
[--clip-in-fp8-e4m3fn | --clip-in-fp8-e5m2 | --clip-in-fp16 | --clip-in-fp32] [--clip-in-fp8-e4m3fn | --clip-in-fp8-e5m2 | --clip-in-fp16 | --clip-in-fp32]
[--directml [DIRECTML_DEVICE]] [--disable-ipex-hijack] [--directml [DIRECTML_DEVICE]]
[--disable-ipex-hijack]
[--preview-option [none,auto,fast,taesd]] [--preview-option [none,auto,fast,taesd]]
[--attention-split | --attention-quad | --attention-pytorch] [--attention-split | --attention-quad | --attention-pytorch]
[--disable-xformers] [--disable-xformers]
[--always-gpu | --always-high-vram | --always-normal-vram | [--always-gpu | --always-high-vram | --always-normal-vram |
--always-low-vram | --always-no-vram | --always-cpu [CPU_NUM_THREADS]] --always-low-vram | --always-no-vram | --always-cpu [CPU_NUM_THREADS]]
[--always-offload-from-vram] [--disable-server-log] [--always-offload-from-vram]
[--debug-mode] [--is-windows-embedded-python] [--pytorch-deterministic] [--disable-server-log]
[--disable-server-info] [--share] [--preset PRESET] [--debug-mode] [--is-windows-embedded-python]
[--language LANGUAGE] [--disable-offload-from-vram] [--disable-server-info] [--multi-user] [--share]
[--theme THEME] [--disable-image-log] [--preset PRESET] [--disable-preset-selection]
[--language LANGUAGE]
[--disable-offload-from-vram] [--theme THEME]
[--disable-image-log] [--disable-analytics]
[--disable-metadata] [--disable-preset-download]
[--enable-describe-uov-image]
[--always-download-new-model]
``` ```
## Advanced Features ## Advanced Features
+2 -5
View File
@@ -1,5 +1,2 @@
torch==2.0.1 torch==2.1.0
torchvision==0.15.2 torchvision==0.16.0
torchaudio==2.0.2
torchtext==0.15.2
torchdata==0.6.1
+74
View File
@@ -0,0 +1,74 @@
import numbers
import os
import unittest
import modules.flags
from modules import extra_utils
class TestUtils(unittest.TestCase):
def test_try_eval_env_var(self):
test_cases = [
{
"input": ("foo", str),
"output": "foo"
},
{
"input": ("1", int),
"output": 1
},
{
"input": ("1.0", float),
"output": 1.0
},
{
"input": ("1", numbers.Number),
"output": 1
},
{
"input": ("1.0", numbers.Number),
"output": 1.0
},
{
"input": ("true", bool),
"output": True
},
{
"input": ("True", bool),
"output": True
},
{
"input": ("false", bool),
"output": False
},
{
"input": ("False", bool),
"output": False
},
{
"input": ("True", str),
"output": "True"
},
{
"input": ("False", str),
"output": "False"
},
{
"input": ("['a', 'b', 'c']", list),
"output": ['a', 'b', 'c']
},
{
"input": ("{'a':1}", dict),
"output": {'a': 1}
},
{
"input": ("('foo', 1)", tuple),
"output": ('foo', 1)
}
]
for test in test_cases:
value, expected_type = test["input"]
expected = test["output"]
actual = extra_utils.try_eval_env_var(value, expected_type)
self.assertEqual(expected, actual)
+107 -18
View File
@@ -1,5 +1,7 @@
import os
import unittest import unittest
import modules.flags
from modules import util from modules import util
@@ -7,13 +9,17 @@ class TestUtils(unittest.TestCase):
def test_can_parse_tokens_with_lora(self): def test_can_parse_tokens_with_lora(self):
test_cases = [ test_cases = [
{ {
"input": ("some prompt, very cool, <lora:hey-lora:0.4>, cool <lora:you-lora:0.2>", [], 5), "input": ("some prompt, very cool, <lora:hey-lora:0.4>, cool <lora:you-lora:0.2>", [], 5, True),
"output": [("hey-lora.safetensors", 0.4), ("you-lora.safetensors", 0.2)], "output": (
[('hey-lora.safetensors', 0.4), ('you-lora.safetensors', 0.2)], 'some prompt, very cool, cool'),
}, },
# Test can not exceed limit # Test can not exceed limit
{ {
"input": ("some prompt, very cool, <lora:hey-lora:0.4>, cool <lora:you-lora:0.2>", [], 1), "input": ("some prompt, very cool, <lora:hey-lora:0.4>, cool <lora:you-lora:0.2>", [], 1, True),
"output": [("hey-lora.safetensors", 0.4)], "output": (
[('hey-lora.safetensors', 0.4)],
'some prompt, very cool, cool'
),
}, },
# test Loras from UI take precedence over prompt # test Loras from UI take precedence over prompt
{ {
@@ -21,28 +27,111 @@ class TestUtils(unittest.TestCase):
"some prompt, very cool, <lora:l1:0.4>, <lora:l2:-0.2>, <lora:l3:0.3>, <lora:l4:0.5>, <lora:l6:0.24>, <lora:l7:0.1>", "some prompt, very cool, <lora:l1:0.4>, <lora:l2:-0.2>, <lora:l3:0.3>, <lora:l4:0.5>, <lora:l6:0.24>, <lora:l7:0.1>",
[("hey-lora.safetensors", 0.4)], [("hey-lora.safetensors", 0.4)],
5, 5,
True
), ),
"output": [ "output": (
("hey-lora.safetensors", 0.4), [
("l1.safetensors", 0.4), ('hey-lora.safetensors', 0.4),
("l2.safetensors", -0.2), ('l1.safetensors', 0.4),
("l3.safetensors", 0.3), ('l2.safetensors', -0.2),
("l4.safetensors", 0.5), ('l3.safetensors', 0.3),
], ('l4.safetensors', 0.5)
],
'some prompt, very cool'
)
}, },
# Test lora specification not separated by comma are ignored, only latest specified is used # test correct matching even if there is no space separating loras in the same token
{ {
"input": ("some prompt, very cool, <lora:hey-lora:0.4><lora:you-lora:0.2>", [], 3), "input": ("some prompt, very cool, <lora:hey-lora:0.4><lora:you-lora:0.2>", [], 3, True),
"output": [("you-lora.safetensors", 0.2)], "output": (
[
('hey-lora.safetensors', 0.4),
('you-lora.safetensors', 0.2)
],
'some prompt, very cool'
),
},
# test deduplication, also selected loras are never overridden with loras in prompt
{
"input": (
"some prompt, very cool, <lora:hey-lora:0.4><lora:hey-lora:0.4><lora:you-lora:0.2>",
[('you-lora.safetensors', 0.3)],
3,
True
),
"output": (
[
('you-lora.safetensors', 0.3),
('hey-lora.safetensors', 0.4)
],
'some prompt, very cool'
),
}, },
{ {
"input": ("<lora:foo:1..2>, <lora:bar:.>, <lora:baz:+> and <lora:quux:>", [], 6), "input": ("<lora:foo:1..2>, <lora:bar:.>, <test:1.0>, <lora:baz:+> and <lora:quux:>", [], 6, True),
"output": [] "output": (
[],
'<lora:foo:1..2>, <lora:bar:.>, <test:1.0>, <lora:baz:+> and <lora:quux:>'
)
} }
] ]
for test in test_cases: for test in test_cases:
prompt, loras, loras_limit = test["input"] prompt, loras, loras_limit, skip_file_check = test["input"]
expected = test["output"] expected = test["output"]
actual = util.parse_lora_references_from_prompt(prompt, loras, loras_limit) actual = util.parse_lora_references_from_prompt(prompt, loras, loras_limit=loras_limit,
skip_file_check=skip_file_check)
self.assertEqual(expected, actual)
def test_can_parse_tokens_and_strip_performance_lora(self):
lora_filenames = [
'hey-lora.safetensors',
modules.flags.PerformanceLoRA.EXTREME_SPEED.value,
modules.flags.PerformanceLoRA.LIGHTNING.value,
os.path.join('subfolder', modules.flags.PerformanceLoRA.HYPER_SD.value)
]
test_cases = [
{
"input": ("some prompt, <lora:hey-lora:0.4>", [], 5, True, modules.flags.Performance.QUALITY),
"output": (
[('hey-lora.safetensors', 0.4)],
'some prompt'
),
},
{
"input": ("some prompt, <lora:hey-lora:0.4>", [], 5, True, modules.flags.Performance.SPEED),
"output": (
[('hey-lora.safetensors', 0.4)],
'some prompt'
),
},
{
"input": ("some prompt, <lora:sdxl_lcm_lora:1>, <lora:hey-lora:0.4>", [], 5, True, modules.flags.Performance.EXTREME_SPEED),
"output": (
[('hey-lora.safetensors', 0.4)],
'some prompt'
),
},
{
"input": ("some prompt, <lora:sdxl_lightning_4step_lora:1>, <lora:hey-lora:0.4>", [], 5, True, modules.flags.Performance.LIGHTNING),
"output": (
[('hey-lora.safetensors', 0.4)],
'some prompt'
),
},
{
"input": ("some prompt, <lora:sdxl_hyper_sd_4step_lora:1>, <lora:hey-lora:0.4>", [], 5, True, modules.flags.Performance.HYPER_SD),
"output": (
[('hey-lora.safetensors', 0.4)],
'some prompt'
),
}
]
for test in test_cases:
prompt, loras, loras_limit, skip_file_check, performance = test["input"]
lora_filenames = modules.util.remove_performance_lora(lora_filenames, performance)
expected = test["output"]
actual = util.parse_lora_references_from_prompt(prompt, loras, loras_limit=loras_limit, lora_filenames=lora_filenames)
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
+38
View File
@@ -1,3 +1,41 @@
# [2.4.3](https://github.com/lllyasviel/Fooocus/releases/tag/v2.4.3)
* Fix alphas_cumprod setter for TCD sampler
* Add parser for env var strings to expected config value types to allow override of all non-path config keys
# [2.4.2](https://github.com/lllyasviel/Fooocus/releases/tag/v2.4.2)
* Fix some small bugs (tcd scheduler when gamma is 0, chown in Dockerfile, update cmd args in readme, translation for aspect ratios, vae default after file reload)
* Fix performance LoRA replacement when data is loaded from history log and inline prompt
* Add support and preset for playground v2.5 (only works with performance Quality or Speed, use with scheduler edm_playground_v2)
* Make textboxes (incl. positive prompt) resizable
* Hide intermediate images when performance of Gradio would bottleneck the generation process (Extreme Speed, Lightning, Hyper-SD)
# [2.4.1](https://github.com/lllyasviel/Fooocus/releases/tag/v2.4.1)
* Fix some small bugs (e.g. adjust clip skip default value from 1 to 2, add type check to aspect ratios js update function)
* Add automated docker build on push to main, tagged with `edge`. See [available docker images](https://github.com/lllyasviel/Fooocus/pkgs/container/fooocus).
# [2.4.0](https://github.com/lllyasviel/Fooocus/releases/tag/v2.4.0)
* Change settings tab elements to be more compact
* Add clip skip slider
* Add select for custom VAE
* Add new style "Random Style"
* Update default anime model to animaPencilXL_v310
* Add button to reconnect the UI after Fooocus crashed without having to configure everything again (no page reload required)
* Add performance "hyper-sd" (based on [Hyper-SDXL 4 step LoRA](https://huggingface.co/ByteDance/Hyper-SD/blob/main/Hyper-SDXL-4steps-lora.safetensors))
* Add [AlignYourSteps](https://research.nvidia.com/labs/toronto-ai/AlignYourSteps/) scheduler by Nvidia, see
* Add [TCD](https://github.com/jabir-zheng/TCD) sampler and scheduler (based on sgm_uniform)
* Add NSFW image censoring (disables intermediate image preview while generating). Set config value `default_black_out_nsfw` to True to always enable.
* Add argument `--enable-describe-uov-image` to automatically describe uploaded images for upscaling
* Add inline lora prompt references with subfolder support, example prompt: `colorful bird <lora:toucan:1.2>`
* Add size and aspect ratio recommendation on image describe
* Add inpaint brush color picker, helpful when image and mask brush have the same color
* Add automated Docker image build using Github Actions on each release.
* Add full raw prompts to history logs
* Change code ownership from @lllyasviel to @mashb1t for automated issue / MR notification
# [2.3.1](https://github.com/lllyasviel/Fooocus/releases/tag/2.3.1) # [2.3.1](https://github.com/lllyasviel/Fooocus/releases/tag/2.3.1)
* Remove positive prompt from anime prefix to not reset prompt after switching presets * Remove positive prompt from anime prefix to not reset prompt after switching presets
+57 -30
View File
@@ -112,10 +112,10 @@ with shared.gradio_root:
gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', visible=True, height=768, gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', visible=True, height=768,
elem_classes=['resizable_area', 'main_view', 'final_gallery', 'image_gallery'], elem_classes=['resizable_area', 'main_view', 'final_gallery', 'image_gallery'],
elem_id='final_gallery') elem_id='final_gallery')
with gr.Row(elem_classes='type_row'): with gr.Row():
with gr.Column(scale=17): with gr.Column(scale=17):
prompt = gr.Textbox(show_label=False, placeholder="Type prompt here or paste parameters.", elem_id='positive_prompt', prompt = gr.Textbox(show_label=False, placeholder="Type prompt here or paste parameters.", elem_id='positive_prompt',
container=False, autofocus=True, elem_classes='type_row', lines=1024) autofocus=True, lines=3)
default_prompt = modules.config.default_prompt default_prompt = modules.config.default_prompt
if isinstance(default_prompt, str) and default_prompt != '': if isinstance(default_prompt, str) and default_prompt != '':
@@ -152,7 +152,7 @@ with shared.gradio_root:
with gr.TabItem(label='Upscale or Variation') as uov_tab: with gr.TabItem(label='Upscale or Variation') as uov_tab:
with gr.Row(): with gr.Row():
with gr.Column(): with gr.Column():
uov_input_image = grh.Image(label='Drag above image to here', source='upload', type='numpy') uov_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False)
with gr.Column(): with gr.Column():
uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, value=flags.disabled) uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, value=flags.disabled)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/390" target="_blank">\U0001F4D4 Document</a>') gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/390" target="_blank">\U0001F4D4 Document</a>')
@@ -201,7 +201,7 @@ with shared.gradio_root:
queue=False, show_progress=False) queue=False, show_progress=False)
with gr.TabItem(label='Inpaint or Outpaint') as inpaint_tab: with gr.TabItem(label='Inpaint or Outpaint') as inpaint_tab:
with gr.Row(): with gr.Row():
inpaint_input_image = grh.Image(label='Drag inpaint or outpaint image to here', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas') inpaint_input_image = grh.Image(label='Image', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas', show_label=False)
inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', height=500, visible=False) inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', height=500, visible=False)
with gr.Row(): with gr.Row():
@@ -214,17 +214,26 @@ with shared.gradio_root:
with gr.TabItem(label='Describe') as desc_tab: with gr.TabItem(label='Describe') as desc_tab:
with gr.Row(): with gr.Row():
with gr.Column(): with gr.Column():
desc_input_image = grh.Image(label='Drag any image to here', source='upload', type='numpy') desc_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False)
with gr.Column(): with gr.Column():
desc_method = gr.Radio( desc_method = gr.Radio(
label='Content Type', label='Content Type',
choices=[flags.desc_type_photo, flags.desc_type_anime], choices=[flags.desc_type_photo, flags.desc_type_anime],
value=flags.desc_type_photo) value=flags.desc_type_photo)
desc_btn = gr.Button(value='Describe this Image into Prompt') desc_btn = gr.Button(value='Describe this Image into Prompt')
desc_image_size = gr.Textbox(label='Image Size and Recommended Size', elem_id='desc_image_size', visible=False)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/1363" target="_blank">\U0001F4D4 Document</a>') gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/1363" target="_blank">\U0001F4D4 Document</a>')
with gr.TabItem(label='Metadata') as load_tab:
def trigger_show_image_properties(image):
value = modules.util.get_image_size_info(image, modules.flags.sdxl_aspect_ratios)
return gr.update(value=value, visible=True)
desc_input_image.upload(trigger_show_image_properties, inputs=desc_input_image,
outputs=desc_image_size, show_progress=False, queue=False)
with gr.TabItem(label='Metadata') as metadata_tab:
with gr.Column(): with gr.Column():
metadata_input_image = grh.Image(label='Drag any image generated by Fooocus here', source='upload', type='filepath') metadata_input_image = grh.Image(label='For images created by Fooocus', source='upload', type='filepath')
metadata_json = gr.JSON(label='Metadata') metadata_json = gr.JSON(label='Metadata')
metadata_import_button = gr.Button(value='Apply Metadata') metadata_import_button = gr.Button(value='Apply Metadata')
@@ -255,25 +264,34 @@ with shared.gradio_root:
inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, queue=False, _js=down_js, show_progress=False) inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
ip_tab.select(lambda: 'ip', outputs=current_tab, queue=False, _js=down_js, show_progress=False) ip_tab.select(lambda: 'ip', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
desc_tab.select(lambda: 'desc', outputs=current_tab, queue=False, _js=down_js, show_progress=False) desc_tab.select(lambda: 'desc', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
metadata_tab.select(lambda: 'metadata', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
with gr.Column(scale=1, visible=modules.config.default_advanced_checkbox) as advanced_column: with gr.Column(scale=1, visible=modules.config.default_advanced_checkbox) as advanced_column:
with gr.Tab(label='Setting'): with gr.Tab(label='Setting'):
if not args_manager.args.disable_preset_selection: if not args_manager.args.disable_preset_selection:
preset_selection = gr.Radio(label='Preset', preset_selection = gr.Dropdown(label='Preset',
choices=modules.config.available_presets, choices=modules.config.available_presets,
value=args_manager.args.preset if args_manager.args.preset else "initial", value=args_manager.args.preset if args_manager.args.preset else "initial",
interactive=True) interactive=True)
performance_selection = gr.Radio(label='Performance', performance_selection = gr.Radio(label='Performance',
choices=flags.Performance.list(), choices=flags.Performance.list(),
value=modules.config.default_performance) value=modules.config.default_performance,
aspect_ratios_selection = gr.Radio(label='Aspect Ratios', choices=modules.config.available_aspect_ratios, elem_classes=['performance_selection'])
value=modules.config.default_aspect_ratio, info='width × height', with gr.Accordion(label='Aspect Ratios', open=False, elem_id='aspect_ratios_accordion') as aspect_ratios_accordion:
elem_classes='aspect_ratios') aspect_ratios_selection = gr.Radio(label='Aspect Ratios', show_label=False,
choices=modules.config.available_aspect_ratios_labels,
value=modules.config.default_aspect_ratio,
info='width × height',
elem_classes='aspect_ratios')
aspect_ratios_selection.change(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}')
shared.gradio_root.load(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}')
image_number = gr.Slider(label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number) image_number = gr.Slider(label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number)
output_format = gr.Radio(label='Output Format', output_format = gr.Radio(label='Output Format',
choices=flags.OutputFormat.list(), choices=flags.OutputFormat.list(),
value=modules.config.default_output_format) value=modules.config.default_output_format)
negative_prompt = gr.Textbox(label='Negative Prompt', show_label=True, placeholder="Type prompt here.", negative_prompt = gr.Textbox(label='Negative Prompt', show_label=True, placeholder="Type prompt here.",
info='Describing what you do not want to see.', lines=2, info='Describing what you do not want to see.', lines=2,
@@ -403,6 +421,9 @@ with shared.gradio_root:
value=modules.config.default_cfg_tsnr, value=modules.config.default_cfg_tsnr,
info='Enabling Fooocus\'s implementation of CFG mimicking for TSNR ' info='Enabling Fooocus\'s implementation of CFG mimicking for TSNR '
'(effective when real CFG > mimicked CFG).') '(effective when real CFG > mimicked CFG).')
clip_skip = gr.Slider(label='CLIP Skip', minimum=1, maximum=flags.clip_skip_max, step=1,
value=modules.config.default_clip_skip,
info='Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).')
sampler_name = gr.Dropdown(label='Sampler', choices=flags.sampler_list, sampler_name = gr.Dropdown(label='Sampler', choices=flags.sampler_list,
value=modules.config.default_sampler) value=modules.config.default_sampler)
scheduler_name = gr.Dropdown(label='Scheduler', choices=flags.scheduler_list, scheduler_name = gr.Dropdown(label='Scheduler', choices=flags.scheduler_list,
@@ -439,9 +460,8 @@ with shared.gradio_root:
disable_preview = gr.Checkbox(label='Disable Preview', value=modules.config.default_black_out_nsfw, disable_preview = gr.Checkbox(label='Disable Preview', value=modules.config.default_black_out_nsfw,
interactive=not modules.config.default_black_out_nsfw, interactive=not modules.config.default_black_out_nsfw,
info='Disable preview during generation.') info='Disable preview during generation.')
disable_intermediate_results = gr.Checkbox(label='Disable Intermediate Results', disable_intermediate_results = gr.Checkbox(label='Disable Intermediate Results',
value=modules.config.default_performance == flags.Performance.EXTREME_SPEED.value, value=flags.Performance.has_restricted_features(modules.config.default_performance),
interactive=modules.config.default_performance != flags.Performance.EXTREME_SPEED.value,
info='Disable intermediate results during generation, only show final gallery.') info='Disable intermediate results during generation, only show final gallery.')
disable_seed_increment = gr.Checkbox(label='Disable seed increment', disable_seed_increment = gr.Checkbox(label='Disable seed increment',
info='Disable automatic seed increment when image number is > 1.', info='Disable automatic seed increment when image number is > 1.',
@@ -515,13 +535,20 @@ with shared.gradio_root:
inpaint_mask_upload_checkbox = gr.Checkbox(label='Enable Mask Upload', value=False) inpaint_mask_upload_checkbox = gr.Checkbox(label='Enable Mask Upload', value=False)
invert_mask_checkbox = gr.Checkbox(label='Invert Mask', value=False) invert_mask_checkbox = gr.Checkbox(label='Invert Mask', value=False)
inpaint_mask_color = gr.ColorPicker(label='Inpaint brush color', value='#FFFFFF', elem_id='inpaint_brush_color')
inpaint_ctrls = [debugging_inpaint_preprocessor, inpaint_disable_initial_latent, inpaint_engine, inpaint_ctrls = [debugging_inpaint_preprocessor, inpaint_disable_initial_latent, inpaint_engine,
inpaint_strength, inpaint_respective_field, inpaint_strength, inpaint_respective_field,
inpaint_mask_upload_checkbox, invert_mask_checkbox, inpaint_erode_or_dilate] inpaint_mask_upload_checkbox, invert_mask_checkbox, inpaint_erode_or_dilate]
inpaint_mask_upload_checkbox.change(lambda x: gr.update(visible=x), inpaint_mask_upload_checkbox.change(lambda x: gr.update(visible=x),
inputs=inpaint_mask_upload_checkbox, inputs=inpaint_mask_upload_checkbox,
outputs=inpaint_mask_image, queue=False, show_progress=False) outputs=inpaint_mask_image, queue=False,
show_progress=False)
inpaint_mask_color.change(lambda x: gr.update(brush_color=x), inputs=inpaint_mask_color,
outputs=inpaint_input_image,
queue=False, show_progress=False)
with gr.Tab(label='FreeU'): with gr.Tab(label='FreeU'):
freeu_enabled = gr.Checkbox(label='Enabled', value=False) freeu_enabled = gr.Checkbox(label='Enabled', value=False)
@@ -541,7 +568,7 @@ with shared.gradio_root:
modules.config.update_files() modules.config.update_files()
results = [gr.update(choices=modules.config.model_filenames)] results = [gr.update(choices=modules.config.model_filenames)]
results += [gr.update(choices=['None'] + modules.config.model_filenames)] results += [gr.update(choices=['None'] + modules.config.model_filenames)]
results += [gr.update(choices=['None'] + modules.config.vae_filenames)] results += [gr.update(choices=[flags.default_vae] + modules.config.vae_filenames)]
if not args_manager.args.disable_preset_selection: if not args_manager.args.disable_preset_selection:
results += [gr.update(choices=modules.config.available_presets)] results += [gr.update(choices=modules.config.available_presets)]
for i in range(modules.config.default_max_lora_number): for i in range(modules.config.default_max_lora_number):
@@ -560,9 +587,9 @@ with shared.gradio_root:
load_data_outputs = [advanced_checkbox, image_number, prompt, negative_prompt, style_selections, load_data_outputs = [advanced_checkbox, image_number, prompt, negative_prompt, style_selections,
performance_selection, overwrite_step, overwrite_switch, aspect_ratios_selection, performance_selection, overwrite_step, overwrite_switch, aspect_ratios_selection,
overwrite_width, overwrite_height, guidance_scale, sharpness, adm_scaler_positive, overwrite_width, overwrite_height, guidance_scale, sharpness, adm_scaler_positive,
adm_scaler_negative, adm_scaler_end, refiner_swap_method, adaptive_cfg, base_model, adm_scaler_negative, adm_scaler_end, refiner_swap_method, adaptive_cfg, clip_skip,
refiner_model, refiner_switch, sampler_name, scheduler_name, vae_name, seed_random, base_model, refiner_model, refiner_switch, sampler_name, scheduler_name, vae_name,
image_seed, generate_button, load_parameter_button] + freeu_ctrls + lora_ctrls seed_random, image_seed, generate_button, load_parameter_button] + freeu_ctrls + lora_ctrls
if not args_manager.args.disable_preset_selection: if not args_manager.args.disable_preset_selection:
def preset_selection_change(preset, is_generating): def preset_selection_change(preset, is_generating):
@@ -584,11 +611,11 @@ with shared.gradio_root:
return modules.meta_parser.load_parameter_button_click(json.dumps(preset_prepared), is_generating) return modules.meta_parser.load_parameter_button_click(json.dumps(preset_prepared), is_generating)
preset_selection.change(preset_selection_change, inputs=[preset_selection, state_is_generating], outputs=load_data_outputs, queue=False, show_progress=True) \ preset_selection.change(preset_selection_change, inputs=[preset_selection, state_is_generating], outputs=load_data_outputs, queue=False, show_progress=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \ .then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False)
performance_selection.change(lambda x: [gr.update(interactive=not flags.Performance.has_restricted_features(x))] * 11 + performance_selection.change(lambda x: [gr.update(interactive=not flags.Performance.has_restricted_features(x))] * 11 +
[gr.update(visible=not flags.Performance.has_restricted_features(x))] * 1 + [gr.update(visible=not flags.Performance.has_restricted_features(x))] * 1 +
[gr.update(interactive=not flags.Performance.has_restricted_features(x), value=flags.Performance.has_restricted_features(x))] * 1, [gr.update(value=flags.Performance.has_restricted_features(x))] * 1,
inputs=performance_selection, inputs=performance_selection,
outputs=[ outputs=[
guidance_scale, sharpness, adm_scaler_end, adm_scaler_positive, guidance_scale, sharpness, adm_scaler_end, adm_scaler_positive,
@@ -647,7 +674,7 @@ with shared.gradio_root:
ctrls += [uov_method, uov_input_image] ctrls += [uov_method, uov_input_image]
ctrls += [outpaint_selections, inpaint_input_image, inpaint_additional_prompt, inpaint_mask_image] ctrls += [outpaint_selections, inpaint_input_image, inpaint_additional_prompt, inpaint_mask_image]
ctrls += [disable_preview, disable_intermediate_results, disable_seed_increment, black_out_nsfw] ctrls += [disable_preview, disable_intermediate_results, disable_seed_increment, black_out_nsfw]
ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg] ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg, clip_skip]
ctrls += [sampler_name, scheduler_name, vae_name] ctrls += [sampler_name, scheduler_name, vae_name]
ctrls += [overwrite_step, overwrite_switch, overwrite_width, overwrite_height, overwrite_vary_strength] ctrls += [overwrite_step, overwrite_switch, overwrite_width, overwrite_height, overwrite_vary_strength]
ctrls += [overwrite_upscale_strength, mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint] ctrls += [overwrite_upscale_strength, mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint]
@@ -685,7 +712,7 @@ with shared.gradio_root:
parsed_parameters = {} parsed_parameters = {}
else: else:
metadata_parser = modules.meta_parser.get_metadata_parser(metadata_scheme) metadata_parser = modules.meta_parser.get_metadata_parser(metadata_scheme)
parsed_parameters = metadata_parser.parse_json(parameters) parsed_parameters = metadata_parser.to_json(parameters)
return modules.meta_parser.load_parameter_button_click(parsed_parameters, state_is_generating) return modules.meta_parser.load_parameter_button_click(parsed_parameters, state_is_generating)
+8
View File
@@ -0,0 +1,8 @@
*.txt
!animal.txt
!artist.txt
!color.txt
!color_flower.txt
!extended-color.txt
!flower.txt
!nationality.txt