Compare commits

..
39 changed files with 150 additions and 922 deletions
-3
View File
@@ -31,9 +31,6 @@ args_parser.parser.add_argument("--disable-metadata", action='store_true',
args_parser.parser.add_argument("--disable-preset-download", action='store_true', args_parser.parser.add_argument("--disable-preset-download", action='store_true',
help="Disables downloading models for presets", default=False) help="Disables downloading models for presets", default=False)
args_parser.parser.add_argument("--enable-describe-uov-image", action='store_true',
help="Disables automatic description of uov images when prompt is empty", default=False)
args_parser.parser.add_argument("--always-download-new-model", action='store_true', args_parser.parser.add_argument("--always-download-new-model", action='store_true',
help="Always download newer models ", default=False) help="Always download newer models ", default=False)
+1 -1
View File
@@ -391,6 +391,6 @@ progress::after {
background-color: #fff8; background-color: #fff8;
font-family: monospace; font-family: monospace;
text-align: center; text-align: center;
border-radius: 5px 5px 0px 0px; border-radius-top: 5px;
display: none; /* remove this to enable tooltip in preview image */ display: none; /* remove this to enable tooltip in preview image */
} }
-11
View File
@@ -1,11 +0,0 @@
## Running unit tests
Native python:
```
python -m unittest tests/
```
Embedded python (Windows zip file installation method):
```
..\python_embeded\python.exe -m unittest
```
-1
View File
@@ -54,7 +54,6 @@ Docker specified environments are there. They are used by 'entrypoint.sh'
|CMDARGS|Arguments for [entry_with_update.py](entry_with_update.py) which is called by [entrypoint.sh](entrypoint.sh)| |CMDARGS|Arguments for [entry_with_update.py](entry_with_update.py) which is called by [entrypoint.sh](entrypoint.sh)|
|config_path|'config.txt' location| |config_path|'config.txt' location|
|config_example_path|'config_modification_tutorial.txt' location| |config_example_path|'config_modification_tutorial.txt' location|
|HF_MIRROR| huggingface mirror site domain|
You can also use the same json key names and values explained in the 'config_modification_tutorial.txt' as the environments. You can also use the same json key names and values explained in the 'config_modification_tutorial.txt' as the environments.
See examples in the [docker-compose.yml](docker-compose.yml) See examples in the [docker-compose.yml](docker-compose.yml)
-60
View File
@@ -1,60 +0,0 @@
import os
import numpy as np
import torch
from transformers import CLIPConfig, CLIPImageProcessor
import ldm_patched.modules.model_management as model_management
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')
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")
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
default_censor = Censor().censor
-171
View File
@@ -1,171 +0,0 @@
{
"_name_or_path": "clip-vit-large-patch14/",
"architectures": [
"SafetyChecker"
],
"initializer_factor": 1.0,
"logit_scale_init_value": 2.6592,
"model_type": "clip",
"projection_dim": 768,
"text_config": {
"_name_or_path": "",
"add_cross_attention": false,
"architectures": null,
"attention_dropout": 0.0,
"bad_words_ids": null,
"bos_token_id": 0,
"chunk_size_feed_forward": 0,
"cross_attention_hidden_size": null,
"decoder_start_token_id": null,
"diversity_penalty": 0.0,
"do_sample": false,
"dropout": 0.0,
"early_stopping": false,
"encoder_no_repeat_ngram_size": 0,
"eos_token_id": 2,
"exponential_decay_length_penalty": null,
"finetuning_task": null,
"forced_bos_token_id": null,
"forced_eos_token_id": null,
"hidden_act": "quick_gelu",
"hidden_size": 768,
"id2label": {
"0": "LABEL_0",
"1": "LABEL_1"
},
"initializer_factor": 1.0,
"initializer_range": 0.02,
"intermediate_size": 3072,
"is_decoder": false,
"is_encoder_decoder": false,
"label2id": {
"LABEL_0": 0,
"LABEL_1": 1
},
"layer_norm_eps": 1e-05,
"length_penalty": 1.0,
"max_length": 20,
"max_position_embeddings": 77,
"min_length": 0,
"model_type": "clip_text_model",
"no_repeat_ngram_size": 0,
"num_attention_heads": 12,
"num_beam_groups": 1,
"num_beams": 1,
"num_hidden_layers": 12,
"num_return_sequences": 1,
"output_attentions": false,
"output_hidden_states": false,
"output_scores": false,
"pad_token_id": 1,
"prefix": null,
"problem_type": null,
"pruned_heads": {},
"remove_invalid_values": false,
"repetition_penalty": 1.0,
"return_dict": true,
"return_dict_in_generate": false,
"sep_token_id": null,
"task_specific_params": null,
"temperature": 1.0,
"tie_encoder_decoder": false,
"tie_word_embeddings": true,
"tokenizer_class": null,
"top_k": 50,
"top_p": 1.0,
"torch_dtype": null,
"torchscript": false,
"transformers_version": "4.21.0.dev0",
"typical_p": 1.0,
"use_bfloat16": false,
"vocab_size": 49408
},
"text_config_dict": {
"hidden_size": 768,
"intermediate_size": 3072,
"num_attention_heads": 12,
"num_hidden_layers": 12
},
"torch_dtype": "float32",
"transformers_version": null,
"vision_config": {
"_name_or_path": "",
"add_cross_attention": false,
"architectures": null,
"attention_dropout": 0.0,
"bad_words_ids": null,
"bos_token_id": null,
"chunk_size_feed_forward": 0,
"cross_attention_hidden_size": null,
"decoder_start_token_id": null,
"diversity_penalty": 0.0,
"do_sample": false,
"dropout": 0.0,
"early_stopping": false,
"encoder_no_repeat_ngram_size": 0,
"eos_token_id": null,
"exponential_decay_length_penalty": null,
"finetuning_task": null,
"forced_bos_token_id": null,
"forced_eos_token_id": null,
"hidden_act": "quick_gelu",
"hidden_size": 1024,
"id2label": {
"0": "LABEL_0",
"1": "LABEL_1"
},
"image_size": 224,
"initializer_factor": 1.0,
"initializer_range": 0.02,
"intermediate_size": 4096,
"is_decoder": false,
"is_encoder_decoder": false,
"label2id": {
"LABEL_0": 0,
"LABEL_1": 1
},
"layer_norm_eps": 1e-05,
"length_penalty": 1.0,
"max_length": 20,
"min_length": 0,
"model_type": "clip_vision_model",
"no_repeat_ngram_size": 0,
"num_attention_heads": 16,
"num_beam_groups": 1,
"num_beams": 1,
"num_hidden_layers": 24,
"num_return_sequences": 1,
"output_attentions": false,
"output_hidden_states": false,
"output_scores": false,
"pad_token_id": null,
"patch_size": 14,
"prefix": null,
"problem_type": null,
"pruned_heads": {},
"remove_invalid_values": false,
"repetition_penalty": 1.0,
"return_dict": true,
"return_dict_in_generate": false,
"sep_token_id": null,
"task_specific_params": null,
"temperature": 1.0,
"tie_encoder_decoder": false,
"tie_word_embeddings": true,
"tokenizer_class": null,
"top_k": 50,
"top_p": 1.0,
"torch_dtype": null,
"torchscript": false,
"transformers_version": "4.21.0.dev0",
"typical_p": 1.0,
"use_bfloat16": false
},
"vision_config_dict": {
"hidden_size": 1024,
"intermediate_size": 4096,
"num_attention_heads": 16,
"num_hidden_layers": 24,
"patch_size": 14
}
}
@@ -1,20 +0,0 @@
{
"crop_size": 224,
"do_center_crop": true,
"do_convert_rgb": true,
"do_normalize": true,
"do_resize": true,
"feature_extractor_type": "CLIPFeatureExtractor",
"image_mean": [
0.48145466,
0.4578275,
0.40821073
],
"image_std": [
0.26862954,
0.26130258,
0.27577711
],
"resample": 3,
"size": 224
}
@@ -1,126 +0,0 @@
# from https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/safety_checker.py
# Copyright 2024 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy as np
import torch
import torch.nn as nn
from transformers import CLIPConfig, CLIPVisionModel, PreTrainedModel
from transformers.utils import logging
logger = logging.get_logger(__name__)
def cosine_distance(image_embeds, text_embeds):
normalized_image_embeds = nn.functional.normalize(image_embeds)
normalized_text_embeds = nn.functional.normalize(text_embeds)
return torch.mm(normalized_image_embeds, normalized_text_embeds.t())
class StableDiffusionSafetyChecker(PreTrainedModel):
config_class = CLIPConfig
main_input_name = "clip_input"
_no_split_modules = ["CLIPEncoderLayer"]
def __init__(self, config: CLIPConfig):
super().__init__(config)
self.vision_model = CLIPVisionModel(config.vision_config)
self.visual_projection = nn.Linear(config.vision_config.hidden_size, config.projection_dim, bias=False)
self.concept_embeds = nn.Parameter(torch.ones(17, config.projection_dim), requires_grad=False)
self.special_care_embeds = nn.Parameter(torch.ones(3, config.projection_dim), requires_grad=False)
self.concept_embeds_weights = nn.Parameter(torch.ones(17), requires_grad=False)
self.special_care_embeds_weights = nn.Parameter(torch.ones(3), requires_grad=False)
@torch.no_grad()
def forward(self, clip_input, images):
pooled_output = self.vision_model(clip_input)[1] # pooled_output
image_embeds = self.visual_projection(pooled_output)
# we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
special_cos_dist = cosine_distance(image_embeds, self.special_care_embeds).cpu().float().numpy()
cos_dist = cosine_distance(image_embeds, self.concept_embeds).cpu().float().numpy()
result = []
batch_size = image_embeds.shape[0]
for i in range(batch_size):
result_img = {"special_scores": {}, "special_care": [], "concept_scores": {}, "bad_concepts": []}
# increase this value to create a stronger `nfsw` filter
# at the cost of increasing the possibility of filtering benign images
adjustment = 0.0
for concept_idx in range(len(special_cos_dist[0])):
concept_cos = special_cos_dist[i][concept_idx]
concept_threshold = self.special_care_embeds_weights[concept_idx].item()
result_img["special_scores"][concept_idx] = round(concept_cos - concept_threshold + adjustment, 3)
if result_img["special_scores"][concept_idx] > 0:
result_img["special_care"].append({concept_idx, result_img["special_scores"][concept_idx]})
adjustment = 0.01
for concept_idx in range(len(cos_dist[0])):
concept_cos = cos_dist[i][concept_idx]
concept_threshold = self.concept_embeds_weights[concept_idx].item()
result_img["concept_scores"][concept_idx] = round(concept_cos - concept_threshold + adjustment, 3)
if result_img["concept_scores"][concept_idx] > 0:
result_img["bad_concepts"].append(concept_idx)
result.append(result_img)
has_nsfw_concepts = [len(res["bad_concepts"]) > 0 for res in result]
for idx, has_nsfw_concept in enumerate(has_nsfw_concepts):
if has_nsfw_concept:
if torch.is_tensor(images) or torch.is_tensor(images[0]):
images[idx] = torch.zeros_like(images[idx]) # black image
else:
images[idx] = np.zeros(images[idx].shape) # black image
if any(has_nsfw_concepts):
logger.warning(
"Potential NSFW content was detected in one or more images. A black image will be returned instead."
" Try again with a different prompt and/or seed."
)
return images, has_nsfw_concepts
@torch.no_grad()
def forward_onnx(self, clip_input: torch.Tensor, images: torch.Tensor):
pooled_output = self.vision_model(clip_input)[1] # pooled_output
image_embeds = self.visual_projection(pooled_output)
special_cos_dist = cosine_distance(image_embeds, self.special_care_embeds)
cos_dist = cosine_distance(image_embeds, self.concept_embeds)
# increase this value to create a stronger `nsfw` filter
# at the cost of increasing the possibility of filtering benign images
adjustment = 0.0
special_scores = special_cos_dist - self.special_care_embeds_weights + adjustment
# special_scores = special_scores.round(decimals=3)
special_care = torch.any(special_scores > 0, dim=1)
special_adjustment = special_care * 0.01
special_adjustment = special_adjustment.unsqueeze(1).expand(-1, cos_dist.shape[1])
concept_scores = (cos_dist - self.concept_embeds_weights) + special_adjustment
# concept_scores = concept_scores.round(decimals=3)
has_nsfw_concepts = torch.any(concept_scores > 0, dim=1)
images[has_nsfw_concepts] = 0.0 # black image
return images, has_nsfw_concepts
+38 -54
View File
@@ -1,85 +1,69 @@
# https://github.com/city96/SD-Latent-Interposer/blob/main/interposer.py # https://github.com/city96/SD-Latent-Interposer/blob/main/interposer.py
import os import os
import safetensors.torch as sf
import torch import torch
import safetensors.torch as sf
import torch.nn as nn import torch.nn as nn
import ldm_patched.modules.model_management import ldm_patched.modules.model_management
from ldm_patched.modules.model_patcher import ModelPatcher from ldm_patched.modules.model_patcher import ModelPatcher
from modules.config import path_vae_approx from modules.config import path_vae_approx
class ResBlock(nn.Module): class Block(nn.Module):
"""Block with residuals""" def __init__(self, size):
def __init__(self, ch):
super().__init__() super().__init__()
self.join = nn.ReLU() self.join = nn.ReLU()
self.norm = nn.BatchNorm2d(ch)
self.long = nn.Sequential( self.long = nn.Sequential(
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1), nn.Conv2d(size, size, kernel_size=3, stride=1, padding=1),
nn.SiLU(), nn.LeakyReLU(0.1),
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1), nn.Conv2d(size, size, kernel_size=3, stride=1, padding=1),
nn.SiLU(), nn.LeakyReLU(0.1),
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1), nn.Conv2d(size, size, kernel_size=3, stride=1, padding=1),
nn.Dropout(0.1)
) )
def forward(self, x): def forward(self, x):
x = self.norm(x) y = self.long(x)
return self.join(self.long(x) + x) z = self.join(y + x)
return z
class ExtractBlock(nn.Module): class Interposer(nn.Module):
"""Increase no. of channels by [out/in]""" def __init__(self):
def __init__(self, ch_in, ch_out):
super().__init__() super().__init__()
self.join = nn.ReLU() self.chan = 4
self.short = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1) self.hid = 128
self.long = nn.Sequential(
nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1), self.head_join = nn.ReLU()
nn.SiLU(), self.head_short = nn.Conv2d(self.chan, self.hid, kernel_size=3, stride=1, padding=1)
nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1), self.head_long = nn.Sequential(
nn.SiLU(), nn.Conv2d(self.chan, self.hid, kernel_size=3, stride=1, padding=1),
nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1), nn.LeakyReLU(0.1),
nn.Dropout(0.1) nn.Conv2d(self.hid, self.hid, kernel_size=3, stride=1, padding=1),
nn.LeakyReLU(0.1),
nn.Conv2d(self.hid, self.hid, kernel_size=3, stride=1, padding=1),
) )
def forward(self, x):
return self.join(self.long(x) + self.short(x))
class InterposerModel(nn.Module):
"""Main neural network"""
def __init__(self, ch_in=4, ch_out=4, ch_mid=64, scale=1.0, blocks=12):
super().__init__()
self.ch_in = ch_in
self.ch_out = ch_out
self.ch_mid = ch_mid
self.blocks = blocks
self.scale = scale
self.head = ExtractBlock(self.ch_in, self.ch_mid)
self.core = nn.Sequential( self.core = nn.Sequential(
nn.Upsample(scale_factor=self.scale, mode="nearest"), Block(self.hid),
*[ResBlock(self.ch_mid) for _ in range(blocks)], Block(self.hid),
nn.BatchNorm2d(self.ch_mid), Block(self.hid),
nn.SiLU(), )
self.tail = nn.Sequential(
nn.ReLU(),
nn.Conv2d(self.hid, self.chan, kernel_size=3, stride=1, padding=1)
) )
self.tail = nn.Conv2d(self.ch_mid, self.ch_out, kernel_size=3, stride=1, padding=1)
def forward(self, x): def forward(self, x):
y = self.head(x) y = self.head_join(
self.head_long(x) +
self.head_short(x)
)
z = self.core(y) z = self.core(y)
return self.tail(z) return self.tail(z)
vae_approx_model = None vae_approx_model = None
vae_approx_filename = os.path.join(path_vae_approx, 'xl-to-v1_interposer-v4.0.safetensors') vae_approx_filename = os.path.join(path_vae_approx, 'xl-to-v1_interposer-v3.1.safetensors')
def parse(x): def parse(x):
@@ -88,7 +72,7 @@ def parse(x):
x_origin = x.clone() x_origin = x.clone()
if vae_approx_model is None: if vae_approx_model is None:
model = InterposerModel() model = Interposer()
model.eval() model.eval()
sd = sf.load_file(vae_approx_filename) sd = sf.load_file(vae_approx_filename)
model.load_state_dict(sd) model.load_state_dict(sd)
+1 -1
View File
@@ -1 +1 @@
version = '2.4.0-rc2' version = '2.3.1'
-37
View File
@@ -122,43 +122,6 @@ document.addEventListener("DOMContentLoaded", function() {
initStylePreviewOverlay(); initStylePreviewOverlay();
}); });
var onAppend = function(elem, f) {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(m) {
if (m.addedNodes.length) {
f(m.addedNodes);
}
});
});
observer.observe(elem, {childList: true});
}
function addObserverIfDesiredNodeAvailable(querySelector, callback) {
var elem = document.querySelector(querySelector);
if (!elem) {
window.setTimeout(() => addObserverIfDesiredNodeAvailable(querySelector, callback), 1000);
return;
}
onAppend(elem, callback);
}
/**
* Show reset button on toast "Connection errored out."
*/
addObserverIfDesiredNodeAvailable(".toast-wrap", function(added) {
added.forEach(function(element) {
if (element.innerText.includes("Connection errored out.")) {
window.setTimeout(function() {
document.getElementById("reset_button").classList.remove("hidden");
document.getElementById("generate_button").classList.add("hidden");
document.getElementById("skip_button").classList.add("hidden");
document.getElementById("stop_button").classList.add("hidden");
});
}
});
});
/** /**
* Add a ctrl+enter as a shortcut to start a generation * Add a ctrl+enter as a shortcut to start a generation
*/ */
-4
View File
@@ -4,7 +4,6 @@
"Generate": "Generate", "Generate": "Generate",
"Skip": "Skip", "Skip": "Skip",
"Stop": "Stop", "Stop": "Stop",
"Reconnect": "Reconnect",
"Input Image": "Input Image", "Input Image": "Input Image",
"Advanced": "Advanced", "Advanced": "Advanced",
"Upscale or Variation": "Upscale or Variation", "Upscale or Variation": "Upscale or Variation",
@@ -55,12 +54,9 @@
"Disable seed increment": "Disable seed increment", "Disable seed increment": "Disable seed increment",
"Disable automatic seed increment when image number is > 1.": "Disable automatic seed increment when image number is > 1.", "Disable automatic seed increment when image number is > 1.": "Disable automatic seed increment when image number is > 1.",
"Read wildcards in order": "Read wildcards in order", "Read wildcards in order": "Read wildcards in order",
"Black Out NSFW": "Black Out NSFW",
"Use black image if NSFW is detected.": "Use black image if NSFW is detected.",
"\ud83d\udcda History Log": "\uD83D\uDCDA History Log", "\ud83d\udcda History Log": "\uD83D\uDCDA History Log",
"Image Style": "Image Style", "Image Style": "Image Style",
"Fooocus V2": "Fooocus V2", "Fooocus V2": "Fooocus V2",
"Random Style": "Random Style",
"Default (Slightly Cinematic)": "Default (Slightly Cinematic)", "Default (Slightly Cinematic)": "Default (Slightly Cinematic)",
"Fooocus Masterpiece": "Fooocus Masterpiece", "Fooocus Masterpiece": "Fooocus Masterpiece",
"Fooocus Photograph": "Fooocus Photograph", "Fooocus Photograph": "Fooocus Photograph",
+2 -6
View File
@@ -62,8 +62,8 @@ def prepare_environment():
vae_approx_filenames = [ vae_approx_filenames = [
('xlvaeapp.pth', 'https://huggingface.co/lllyasviel/misc/resolve/main/xlvaeapp.pth'), ('xlvaeapp.pth', 'https://huggingface.co/lllyasviel/misc/resolve/main/xlvaeapp.pth'),
('vaeapp_sd15.pth', 'https://huggingface.co/lllyasviel/misc/resolve/main/vaeapp_sd15.pt'), ('vaeapp_sd15.pth', 'https://huggingface.co/lllyasviel/misc/resolve/main/vaeapp_sd15.pt'),
('xl-to-v1_interposer-v4.0.safetensors', ('xl-to-v1_interposer-v3.1.safetensors',
'https://huggingface.co/mashb1t/misc/resolve/main/xl-to-v1_interposer-v4.0.safetensors') 'https://huggingface.co/lllyasviel/misc/resolve/main/xl-to-v1_interposer-v3.1.safetensors')
] ]
@@ -80,10 +80,6 @@ if args.gpu_device_id is not None:
os.environ['CUDA_VISIBLE_DEVICES'] = str(args.gpu_device_id) os.environ['CUDA_VISIBLE_DEVICES'] = str(args.gpu_device_id)
print("Set device to:", args.gpu_device_id) print("Set device to:", args.gpu_device_id)
if args.hf_mirror is not None :
os.environ['HF_MIRROR'] = str(args.hf_mirror)
print("Set hf_mirror to:", args.hf_mirror)
from modules import config from modules import config
os.environ['GRADIO_TEMP_DIR'] = config.temp_path os.environ['GRADIO_TEMP_DIR'] = config.temp_path
@@ -1,55 +0,0 @@
# https://github.com/comfyanonymous/ComfyUI/blob/master/nodes.py
#from: https://research.nvidia.com/labs/toronto-ai/AlignYourSteps/howto.html
import numpy as np
import torch
def loglinear_interp(t_steps, num_steps):
"""
Performs log-linear interpolation of a given array of decreasing numbers.
"""
xs = np.linspace(0, 1, len(t_steps))
ys = np.log(t_steps[::-1])
new_xs = np.linspace(0, 1, num_steps)
new_ys = np.interp(new_xs, xs, ys)
interped_ys = np.exp(new_ys)[::-1].copy()
return interped_ys
NOISE_LEVELS = {"SD1": [14.6146412293, 6.4745760956, 3.8636745985, 2.6946151520, 1.8841921177, 1.3943805092, 0.9642583904, 0.6523686016, 0.3977456272, 0.1515232662, 0.0291671582],
"SDXL":[14.6146412293, 6.3184485287, 3.7681790315, 2.1811480769, 1.3405244945, 0.8620721141, 0.5550693289, 0.3798540708, 0.2332364134, 0.1114188177, 0.0291671582],
"SVD": [700.00, 54.5, 15.886, 7.977, 4.248, 1.789, 0.981, 0.403, 0.173, 0.034, 0.002]}
class AlignYourStepsScheduler:
@classmethod
def INPUT_TYPES(s):
return {"required":
{"model_type": (["SD1", "SDXL", "SVD"], ),
"steps": ("INT", {"default": 10, "min": 10, "max": 10000}),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("SIGMAS",)
CATEGORY = "sampling/custom_sampling/schedulers"
FUNCTION = "get_sigmas"
def get_sigmas(self, model_type, steps, denoise):
total_steps = steps
if denoise < 1.0:
if denoise <= 0.0:
return (torch.FloatTensor([]),)
total_steps = round(steps * denoise)
sigmas = NOISE_LEVELS[model_type][:]
if (steps + 1) != len(sigmas):
sigmas = loglinear_interp(sigmas, steps + 1)
sigmas = sigmas[-(total_steps + 1):]
sigmas[-1] = 0
return (torch.FloatTensor(sigmas), )
NODE_CLASS_MAPPINGS = {
"AlignYourStepsScheduler": AlignYourStepsScheduler,
}
@@ -230,25 +230,6 @@ class SamplerDPMPP_SDE:
sampler = ldm_patched.modules.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "r": r}) sampler = ldm_patched.modules.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "r": r})
return (sampler, ) return (sampler, )
class SamplerTCD:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"eta": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("SAMPLER",)
CATEGORY = "sampling/custom_sampling/samplers"
FUNCTION = "get_sampler"
def get_sampler(self, eta=0.3):
sampler = ldm_patched.modules.samplers.ksampler("tcd", {"eta": eta})
return (sampler, )
class SamplerCustom: class SamplerCustom:
@classmethod @classmethod
def INPUT_TYPES(s): def INPUT_TYPES(s):
@@ -311,7 +292,6 @@ NODE_CLASS_MAPPINGS = {
"KSamplerSelect": KSamplerSelect, "KSamplerSelect": KSamplerSelect,
"SamplerDPMPP_2M_SDE": SamplerDPMPP_2M_SDE, "SamplerDPMPP_2M_SDE": SamplerDPMPP_2M_SDE,
"SamplerDPMPP_SDE": SamplerDPMPP_SDE, "SamplerDPMPP_SDE": SamplerDPMPP_SDE,
"SamplerTCD": SamplerTCD,
"SplitSigmas": SplitSigmas, "SplitSigmas": SplitSigmas,
"FlipSigmas": FlipSigmas, "FlipSigmas": FlipSigmas,
} }
@@ -70,7 +70,7 @@ class ModelSamplingDiscrete:
@classmethod @classmethod
def INPUT_TYPES(s): def INPUT_TYPES(s):
return {"required": { "model": ("MODEL",), return {"required": { "model": ("MODEL",),
"sampling": (["eps", "v_prediction", "lcm", "tcd"]), "sampling": (["eps", "v_prediction", "lcm"],),
"zsnr": ("BOOLEAN", {"default": False}), "zsnr": ("BOOLEAN", {"default": False}),
}} }}
@@ -90,9 +90,6 @@ class ModelSamplingDiscrete:
elif sampling == "lcm": elif sampling == "lcm":
sampling_type = LCM sampling_type = LCM
sampling_base = ModelSamplingDiscreteDistilled sampling_base = ModelSamplingDiscreteDistilled
elif sampling == "tcd":
sampling_type = ldm_patched.modules.model_sampling.EPS
sampling_base = ModelSamplingDiscreteDistilled
class ModelSamplingAdvanced(sampling_base, sampling_type): class ModelSamplingAdvanced(sampling_base, sampling_type):
pass pass
+1 -27
View File
@@ -752,6 +752,7 @@ def sample_lcm(model, x, sigmas, extra_args=None, callback=None, disable=None, n
return x return x
@torch.no_grad() @torch.no_grad()
def sample_heunpp2(model, x, sigmas, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.): def sample_heunpp2(model, x, sigmas, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1.):
# From MIT licensed: https://github.com/Carzit/sd-webui-samplers-scheduler/ # From MIT licensed: https://github.com/Carzit/sd-webui-samplers-scheduler/
@@ -807,30 +808,3 @@ def sample_heunpp2(model, x, sigmas, extra_args=None, callback=None, disable=Non
d_prime = w1 * d + w2 * d_2 + w3 * d_3 d_prime = w1 * d + w2 * d_2 + w3 * d_3
x = x + d_prime * dt x = x + d_prime * dt
return x return x
@torch.no_grad()
def sample_tcd(model, x, sigmas, extra_args=None, callback=None, disable=None, noise_sampler=None, eta=0.3):
extra_args = {} if extra_args is None else extra_args
noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler
s_in = x.new_ones([x.shape[0]])
model_sampling = model.inner_model.inner_model.model_sampling
timesteps_s = torch.floor((1 - eta) * model_sampling.timestep(sigmas)).to(dtype=torch.long).detach().cpu()
timesteps_s[-1] = 0
alpha_prod_s = model_sampling.alphas_cumprod[timesteps_s]
beta_prod_s = 1 - alpha_prod_s
for i in trange(len(sigmas) - 1, disable=disable):
denoised = model(x, sigmas[i] * s_in, **extra_args) # predicted_original_sample
eps = (x - denoised) / sigmas[i]
denoised = alpha_prod_s[i + 1].sqrt() * denoised + beta_prod_s[i + 1].sqrt() * eps
if callback is not None:
callback({"x": x, "i": i, "sigma": sigmas[i], "sigma_hat": sigmas[i], "denoised": denoised})
x = denoised
if eta > 0 and sigmas[i + 1] > 0:
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()
return x
-1
View File
@@ -37,7 +37,6 @@ parser.add_argument("--listen", type=str, default="127.0.0.1", metavar="IP", nar
parser.add_argument("--port", type=int, default=8188) parser.add_argument("--port", type=int, default=8188)
parser.add_argument("--disable-header-check", type=str, default=None, metavar="ORIGIN", nargs="?", const="*") parser.add_argument("--disable-header-check", type=str, default=None, metavar="ORIGIN", nargs="?", const="*")
parser.add_argument("--web-upload-size", type=float, default=100) parser.add_argument("--web-upload-size", type=float, default=100)
parser.add_argument("--hf-mirror", type=str, default=None)
parser.add_argument("--external-working-path", type=str, default=None, metavar="PATH", nargs='+', action='append') parser.add_argument("--external-working-path", type=str, default=None, metavar="PATH", nargs='+', action='append')
parser.add_argument("--output-path", type=str, default=None) parser.add_argument("--output-path", type=str, default=None)
+4 -4
View File
@@ -50,17 +50,17 @@ class ModelSamplingDiscrete(torch.nn.Module):
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
self.set_sigmas(sigmas) self.set_sigmas(sigmas)
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)
self.register_buffer('log_sigmas', sigmas.log()) self.register_buffer('log_sigmas', sigmas.log())
def set_alphas_cumprod(self, alphas_cumprod):
self.register_buffer("alphas_cumprod", alphas_cumprod.float())
@property @property
def sigma_min(self): def sigma_min(self):
return self.sigmas[0] return self.sigmas[0]
+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"]
class KSAMPLER(Sampler): class KSAMPLER(Sampler):
def __init__(self, sampler_function, extra_options={}, inpaint_options={}): def __init__(self, sampler_function, extra_options={}, inpaint_options={}):
View File
+25 -80
View File
@@ -4,7 +4,6 @@ from modules.patch import PatchSettings, patch_settings, patch_all
patch_all() patch_all()
class AsyncTask: class AsyncTask:
def __init__(self, args): def __init__(self, args):
self.args = args self.args = args
@@ -44,13 +43,11 @@ def worker():
import fooocus_version import fooocus_version
import args_manager import args_manager
from extras.censor import default_censor from modules.sdxl_styles import apply_style, apply_wildcards, fooocus_expansion, apply_arrays
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, ordinal_suffix, get_enabled_loras
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
from modules.meta_parser import get_metadata_parser, MetadataScheme from modules.meta_parser import get_metadata_parser, MetadataScheme
@@ -71,15 +68,10 @@ def worker():
print(f'[Fooocus] {text}') print(f'[Fooocus] {text}')
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, do_not_show_finished_images=False):
progressbar_index=13):
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):
progressbar(async_task, progressbar_index, 'Checking for NSFW content ...')
imgs = default_censor(imgs)
async_task.results = async_task.results + imgs async_task.results = async_task.results + imgs
if do_not_show_finished_images: if do_not_show_finished_images:
@@ -155,8 +147,7 @@ def worker():
base_model_name = args.pop() base_model_name = args.pop()
refiner_model_name = args.pop() refiner_model_name = args.pop()
refiner_switch = args.pop() refiner_switch = args.pop()
loras = get_enabled_loras([(bool(args.pop()), str(args.pop()), float(args.pop())) for _ in loras = get_enabled_loras([[bool(args.pop()), str(args.pop()), float(args.pop())] for _ in range(modules.config.default_max_lora_number)])
range(modules.config.default_max_lora_number)])
input_image_checkbox = args.pop() input_image_checkbox = args.pop()
current_tab = args.pop() current_tab = args.pop()
uov_method = args.pop() uov_method = args.pop()
@@ -169,7 +160,6 @@ def worker():
disable_preview = args.pop() disable_preview = args.pop()
disable_intermediate_results = args.pop() disable_intermediate_results = args.pop()
disable_seed_increment = args.pop() disable_seed_increment = args.pop()
black_out_nsfw = args.pop()
adm_scaler_positive = args.pop() adm_scaler_positive = args.pop()
adm_scaler_negative = args.pop() adm_scaler_negative = args.pop()
adm_scaler_end = args.pop() adm_scaler_end = args.pop()
@@ -206,8 +196,7 @@ def worker():
inpaint_erode_or_dilate = args.pop() inpaint_erode_or_dilate = args.pop()
save_metadata_to_images = args.pop() if not args_manager.args.disable_metadata else False save_metadata_to_images = args.pop() if not args_manager.args.disable_metadata else False
metadata_scheme = MetadataScheme( metadata_scheme = MetadataScheme(args.pop()) if not args_manager.args.disable_metadata else MetadataScheme.FOOOCUS
args.pop()) if not args_manager.args.disable_metadata else MetadataScheme.FOOOCUS
cn_tasks = {x: [] for x in flags.ip_list} cn_tasks = {x: [] for x in flags.ip_list}
for _ in range(flags.controlnet_image_count): for _ in range(flags.controlnet_image_count):
@@ -275,25 +264,6 @@ 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_SD:
print('Enter Hyper-SD mode.')
progressbar(async_task, 1, 'Downloading Hyper-SD components ...')
loras += [(modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)]
if refiner_model_name != 'None':
print(f'Refiner disabled in Hyper-SD mode.')
refiner_model_name = 'None'
sampler_name = 'dpmpp_sde_gpu'
scheduler_name = 'karras'
sharpness = 0.0
guidance_scale = 1.0
adaptive_cfg = 1.0
refiner_switch = 1.0
adm_scaler_positive = 1.0
adm_scaler_negative = 1.0
adm_scaler_end = 0.0
print(f'[Parameters] Adaptive CFG = {adaptive_cfg}') print(f'[Parameters] Adaptive CFG = {adaptive_cfg}')
print(f'[Parameters] Sharpness = {sharpness}') print(f'[Parameters] Sharpness = {sharpness}')
print(f'[Parameters] ControlNet Softness = {controlnet_softness}') print(f'[Parameters] ControlNet Softness = {controlnet_softness}')
@@ -457,9 +427,6 @@ def worker():
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, 3, 'Loading models ...')
loras = parse_lora_references_from_prompt(prompt, loras, modules.config.default_max_lora_number)
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)
@@ -477,20 +444,14 @@ def worker():
task_prompt = apply_wildcards(prompt, task_rng, i, read_wildcards_in_order) task_prompt = apply_wildcards(prompt, task_rng, i, read_wildcards_in_order)
task_prompt = apply_arrays(task_prompt, i) task_prompt = apply_arrays(task_prompt, i)
task_negative_prompt = apply_wildcards(negative_prompt, task_rng, i, read_wildcards_in_order) task_negative_prompt = apply_wildcards(negative_prompt, task_rng, i, read_wildcards_in_order)
task_extra_positive_prompts = [apply_wildcards(pmt, task_rng, i, read_wildcards_in_order) for pmt in task_extra_positive_prompts = [apply_wildcards(pmt, task_rng, i, read_wildcards_in_order) for pmt in extra_positive_prompts]
extra_positive_prompts] task_extra_negative_prompts = [apply_wildcards(pmt, task_rng, i, read_wildcards_in_order) for pmt in extra_negative_prompts]
task_extra_negative_prompts = [apply_wildcards(pmt, task_rng, i, read_wildcards_in_order) for pmt in
extra_negative_prompts]
positive_basic_workloads = [] positive_basic_workloads = []
negative_basic_workloads = [] negative_basic_workloads = []
task_styles = style_selections.copy()
if use_style: if use_style:
for i, s in enumerate(task_styles): for s in style_selections:
if s == random_style_name:
s = get_random_style(task_rng)
task_styles[i] = s
p, n = apply_style(s, positive=task_prompt) p, n = apply_style(s, positive=task_prompt)
positive_basic_workloads = positive_basic_workloads + p positive_basic_workloads = positive_basic_workloads + p
negative_basic_workloads = negative_basic_workloads + n negative_basic_workloads = negative_basic_workloads + n
@@ -518,7 +479,6 @@ def worker():
negative_top_k=len(negative_basic_workloads), negative_top_k=len(negative_basic_workloads),
log_positive_prompt='\n'.join([task_prompt] + task_extra_positive_prompts), log_positive_prompt='\n'.join([task_prompt] + task_extra_positive_prompts),
log_negative_prompt='\n'.join([task_negative_prompt] + task_extra_negative_prompts), log_negative_prompt='\n'.join([task_negative_prompt] + task_extra_negative_prompts),
styles=task_styles
)) ))
if use_expansion: if use_expansion:
@@ -613,12 +573,8 @@ def worker():
if direct_return: if direct_return:
d = [('Upscale (Fast)', 'upscale_fast', '2x')] d = [('Upscale (Fast)', 'upscale_fast', '2x')]
if modules.config.default_black_out_nsfw or black_out_nsfw:
progressbar(async_task, 100, 'Checking for NSFW content ...')
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, do_not_show_finished_images=True)
return return
tiled = True tiled = True
@@ -682,7 +638,7 @@ def worker():
) )
if debugging_inpaint_preprocessor: if debugging_inpaint_preprocessor:
yield_result(async_task, inpaint_worker.current_task.visualize_mask_processing(), black_out_nsfw, yield_result(async_task, inpaint_worker.current_task.visualize_mask_processing(),
do_not_show_finished_images=True) do_not_show_finished_images=True)
return return
@@ -746,7 +702,7 @@ def worker():
cn_img = HWC3(cn_img) cn_img = HWC3(cn_img)
task[0] = core.numpy_to_pytorch(cn_img) task[0] = core.numpy_to_pytorch(cn_img)
if debugging_cn_preprocessor: if debugging_cn_preprocessor:
yield_result(async_task, cn_img, black_out_nsfw, do_not_show_finished_images=True) yield_result(async_task, cn_img, do_not_show_finished_images=True)
return return
for task in cn_tasks[flags.cn_cpds]: for task in cn_tasks[flags.cn_cpds]:
cn_img, cn_stop, cn_weight = task cn_img, cn_stop, cn_weight = task
@@ -758,7 +714,7 @@ def worker():
cn_img = HWC3(cn_img) cn_img = HWC3(cn_img)
task[0] = core.numpy_to_pytorch(cn_img) task[0] = core.numpy_to_pytorch(cn_img)
if debugging_cn_preprocessor: if debugging_cn_preprocessor:
yield_result(async_task, cn_img, black_out_nsfw, do_not_show_finished_images=True) yield_result(async_task, cn_img, do_not_show_finished_images=True)
return return
for task in cn_tasks[flags.cn_ip]: for task in cn_tasks[flags.cn_ip]:
cn_img, cn_stop, cn_weight = task cn_img, cn_stop, cn_weight = task
@@ -769,7 +725,7 @@ def worker():
task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_path) task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_path)
if debugging_cn_preprocessor: if debugging_cn_preprocessor:
yield_result(async_task, cn_img, black_out_nsfw, do_not_show_finished_images=True) yield_result(async_task, cn_img, do_not_show_finished_images=True)
return return
for task in cn_tasks[flags.cn_ip_face]: for task in cn_tasks[flags.cn_ip_face]:
cn_img, cn_stop, cn_weight = task cn_img, cn_stop, cn_weight = task
@@ -783,7 +739,7 @@ def worker():
task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_face_path) task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_face_path)
if debugging_cn_preprocessor: if debugging_cn_preprocessor:
yield_result(async_task, cn_img, black_out_nsfw, do_not_show_finished_images=True) yield_result(async_task, cn_img, do_not_show_finished_images=True)
return return
all_ip_tasks = cn_tasks[flags.cn_ip] + cn_tasks[flags.cn_ip_face] all_ip_tasks = cn_tasks[flags.cn_ip] + cn_tasks[flags.cn_ip_face]
@@ -818,19 +774,19 @@ def worker():
final_sampler_name = sampler_name final_sampler_name = sampler_name
final_scheduler_name = scheduler_name final_scheduler_name = scheduler_name
if scheduler_name in ['lcm', 'tcd']: if scheduler_name == 'lcm':
final_scheduler_name = 'sgm_uniform' final_scheduler_name = 'sgm_uniform'
if pipeline.final_unet is not None: if pipeline.final_unet is not None:
pipeline.final_unet = core.opModelSamplingDiscrete.patch( pipeline.final_unet = core.opModelSamplingDiscrete.patch(
pipeline.final_unet, pipeline.final_unet,
sampling=scheduler_name, sampling='lcm',
zsnr=False)[0] zsnr=False)[0]
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 = core.opModelSamplingDiscrete.patch(
pipeline.final_refiner_unet, pipeline.final_refiner_unet,
sampling=scheduler_name, sampling='lcm',
zsnr=False)[0] zsnr=False)[0]
print(f'Using {scheduler_name} scheduler.') print('Using lcm scheduler.')
async_task.yields.append(['preview', (13, 'Moving model to GPU ...', None)]) async_task.yields.append(['preview', (13, 'Moving model to GPU ...', None)])
@@ -838,8 +794,7 @@ def worker():
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(15.0 + 85.0 * 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'Step {step}/{total_steps} in the {current_task_id + 1}{ordinal_suffix(current_task_id + 1)} Sampling', y)])
y)])
for current_task_id, task in enumerate(tasks): for current_task_id, task in enumerate(tasks):
execution_start_time = time.perf_counter() execution_start_time = time.perf_counter()
@@ -884,18 +839,11 @@ 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(15.0 + 85.0 * float((current_task_id + 1) * steps) / float(all_steps))
if modules.config.default_black_out_nsfw or black_out_nsfw:
progressbar(async_task, current_progress, 'Checking for NSFW content ...')
imgs = default_censor(imgs)
progressbar(async_task, current_progress, 'Saving image 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']),
('Fooocus V2 Expansion', 'prompt_expansion', task['expansion']), ('Fooocus V2 Expansion', 'prompt_expansion', task['expansion']),
('Styles', 'styles', ('Styles', 'styles', str(raw_style_selections)),
str(task['styles'] if not use_expansion else [fooocus_expansion] + task['styles'])),
('Performance', 'performance', performance_selection.value)] ('Performance', 'performance', performance_selection.value)]
if performance_selection.steps() != steps: if performance_selection.steps() != steps:
@@ -918,8 +866,7 @@ def worker():
if refiner_swap_method != flags.refiner_swap_method: if refiner_swap_method != flags.refiner_swap_method:
d.append(('Refiner Swap Method', 'refiner_swap_method', refiner_swap_method)) d.append(('Refiner Swap Method', 'refiner_swap_method', refiner_swap_method))
if modules.patch.patch_settings[pid].adaptive_cfg != modules.config.default_cfg_tsnr: if modules.patch.patch_settings[pid].adaptive_cfg != modules.config.default_cfg_tsnr:
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))
d.append(('Sampler', 'sampler', sampler_name)) d.append(('Sampler', 'sampler', sampler_name))
d.append(('Scheduler', 'scheduler', scheduler_name)) d.append(('Scheduler', 'scheduler', scheduler_name))
@@ -939,13 +886,11 @@ def worker():
metadata_parser.set_data(task['log_positive_prompt'], task['positive'], metadata_parser.set_data(task['log_positive_prompt'], task['positive'],
task['log_negative_prompt'], task['negative'], task['log_negative_prompt'], task['negative'],
steps, base_model_name, refiner_model_name, loras, vae_name) steps, base_model_name, refiner_model_name, loras, vae_name)
d.append(('Metadata Scheme', 'metadata_scheme', d.append(('Metadata Scheme', 'metadata_scheme', metadata_scheme.value if save_metadata_to_images else save_metadata_to_images))
metadata_scheme.value if save_metadata_to_images else save_metadata_to_images))
d.append(('Version', 'version', 'Fooocus v' + fooocus_version.version)) d.append(('Version', 'version', 'Fooocus v' + fooocus_version.version))
img_paths.append(log(x, d, metadata_parser, output_format, task)) img_paths.append(log(x, d, metadata_parser, output_format))
yield_result(async_task, img_paths, black_out_nsfw, False, yield_result(async_task, img_paths, do_not_show_finished_images=len(tasks) == 1 or disable_intermediate_results)
do_not_show_finished_images=len(tasks) == 1 or disable_intermediate_results)
except ldm_patched.modules.model_management.InterruptProcessingException as e: except ldm_patched.modules.model_management.InterruptProcessingException as e:
if async_task.last_stop == 'skip': if async_task.last_stop == 'skip':
print('User skipped') print('User skipped')
+4 -29
View File
@@ -8,8 +8,7 @@ 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.util import get_files_from_folder, makedirs_with_log
from modules.extra_utils import get_files_from_folder
from modules.flags import OutputFormat, Performance, MetadataScheme from modules.flags import OutputFormat, Performance, MetadataScheme
@@ -21,7 +20,7 @@ def get_config_path(key, default_value):
else: else:
return os.path.abspath(default_value) return os.path.abspath(default_value)
wildcards_max_bfs_depth = 64
config_path = get_config_path('config_path', "./config.txt") config_path = get_config_path('config_path', "./config.txt")
config_example_path = get_config_path('config_example_path', "config_modification_tutorial.txt") config_example_path = get_config_path('config_example_path', "config_modification_tutorial.txt")
config_dict = {} config_dict = {}
@@ -197,7 +196,6 @@ path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlne
path_clip_vision = get_dir_or_set_default('path_clip_vision', '../models/clip_vision/') path_clip_vision = get_dir_or_set_default('path_clip_vision', '../models/clip_vision/')
path_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion') path_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion')
path_wildcards = get_dir_or_set_default('path_wildcards', '../wildcards/') path_wildcards = get_dir_or_set_default('path_wildcards', '../wildcards/')
path_safety_checker = get_dir_or_set_default('path_safety_checker', '../models/safety_checker/')
path_outputs = get_path_output() path_outputs = get_path_output()
@@ -458,11 +456,6 @@ example_inpaint_prompts = get_config_item_or_set_default(
], ],
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)
) )
default_black_out_nsfw = get_config_item_or_set_default(
key='default_black_out_nsfw',
default_value=False,
validator=lambda x: isinstance(x, 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,
@@ -553,8 +546,7 @@ wildcard_filenames = []
sdxl_lcm_lora = 'sdxl_lcm_lora.safetensors' sdxl_lcm_lora = 'sdxl_lcm_lora.safetensors'
sdxl_lightning_lora = 'sdxl_lightning_4step_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]
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):
@@ -628,22 +620,13 @@ def downloading_sdxl_lcm_lora():
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/ByteDance/SDXL-Lightning/resolve/main/sdxl_lightning_4step_lora.safetensors',
model_dir=paths_loras[0], model_dir=paths_loras[0],
file_name=sdxl_lightning_lora file_name=sdxl_lightning_lora
) )
return sdxl_lightning_lora return sdxl_lightning_lora
def downloading_sdxl_hyper_sd_lora():
load_file_from_url(
url='https://huggingface.co/mashb1t/misc/resolve/main/sdxl_hyper_sd_4step_lora.safetensors',
model_dir=paths_loras[0],
file_name=sdxl_hyper_sd_lora
)
return sdxl_hyper_sd_lora
def downloading_controlnet_canny(): def downloading_controlnet_canny():
load_file_from_url( load_file_from_url(
url='https://huggingface.co/lllyasviel/misc/resolve/main/control-lora-canny-rank128.safetensors', url='https://huggingface.co/lllyasviel/misc/resolve/main/control-lora-canny-rank128.safetensors',
@@ -708,13 +691,5 @@ def downloading_upscale_model():
) )
return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin') return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin')
def downloading_safety_checker_model():
load_file_from_url(
url='https://huggingface.co/mashb1t/misc/resolve/main/stable-diffusion-safety-checker.bin',
model_dir=path_safety_checker,
file_name='stable-diffusion-safety-checker.bin'
)
return os.path.join(path_safety_checker, 'stable-diffusion-safety-checker.bin')
update_files() update_files()
-20
View File
@@ -1,20 +0,0 @@
import os
def get_files_from_folder(folder_path, extensions=None, name_filter=None):
if not os.path.isdir(folder_path):
raise ValueError("Folder path is not a valid directory.")
filenames = []
for root, _, files in os.walk(folder_path, topdown=False):
relative_path = os.path.relpath(root, folder_path)
if relative_path == ".":
relative_path = ""
for filename in sorted(files, key=lambda s: s.casefold()):
_, file_extension = os.path.splitext(filename)
if (extensions is None or file_extension.lower() in extensions) and (name_filter is None or name_filter in _):
path = os.path.join(relative_path, filename)
filenames.append(path)
return filenames
+3 -7
View File
@@ -34,8 +34,7 @@ KSAMPLER = {
"dpmpp_3m_sde": "", "dpmpp_3m_sde": "",
"dpmpp_3m_sde_gpu": "", "dpmpp_3m_sde_gpu": "",
"ddpm": "", "ddpm": "",
"lcm": "LCM", "lcm": "LCM"
"tcd": "TCD"
} }
SAMPLER_EXTRA = { SAMPLER_EXTRA = {
@@ -48,7 +47,7 @@ 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"]
SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys()) SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys())
sampler_list = SAMPLER_NAMES sampler_list = SAMPLER_NAMES
@@ -110,7 +109,6 @@ class Steps(IntEnum):
SPEED = 30 SPEED = 30
EXTREME_SPEED = 8 EXTREME_SPEED = 8
LIGHTNING = 4 LIGHTNING = 4
HYPER_SD = 4
class StepsUOV(IntEnum): class StepsUOV(IntEnum):
@@ -118,7 +116,6 @@ class StepsUOV(IntEnum):
SPEED = 18 SPEED = 18
EXTREME_SPEED = 8 EXTREME_SPEED = 8
LIGHTNING = 4 LIGHTNING = 4
HYPER_SD = 4
class Performance(Enum): class Performance(Enum):
@@ -126,7 +123,6 @@ class Performance(Enum):
SPEED = 'Speed' SPEED = 'Speed'
EXTREME_SPEED = 'Extreme Speed' EXTREME_SPEED = 'Extreme Speed'
LIGHTNING = 'Lightning' LIGHTNING = 'Lightning'
HYPER_SD = 'Hyper-SD'
@classmethod @classmethod
def list(cls) -> list: def list(cls) -> list:
@@ -136,7 +132,7 @@ class Performance(Enum):
def has_restricted_features(cls, x) -> bool: def has_restricted_features(cls, x) -> bool:
if isinstance(x, Performance): if isinstance(x, Performance):
x = x.value x = x.value
return x in [cls.EXTREME_SPEED.value, cls.LIGHTNING.value, cls.HYPER_SD.value] return x in [cls.EXTREME_SPEED.value, cls.LIGHTNING.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 Steps[self.name] else None
-2
View File
@@ -14,8 +14,6 @@ def load_file_from_url(
Returns the path to the downloaded file. Returns the path to the downloaded file.
""" """
domain = os.environ.get("HF_MIRROR", "https://huggingface.co").rstrip('/')
url = str.replace(url, "https://huggingface.co", domain, 1)
os.makedirs(model_dir, exist_ok=True) os.makedirs(model_dir, exist_ok=True)
if not file_name: if not file_name:
parts = urlparse(url) parts = urlparse(url)
-2
View File
@@ -51,8 +51,6 @@ def patched_register_schedule(self, given_betas=None, beta_schedule="linear", ti
self.linear_end = linear_end self.linear_end = linear_end
sigmas = torch.tensor(((1 - alphas_cumprod) / alphas_cumprod) ** 0.5, dtype=torch.float32) sigmas = torch.tensor(((1 - alphas_cumprod) / alphas_cumprod) ** 0.5, dtype=torch.float32)
self.set_sigmas(sigmas) self.set_sigmas(sigmas)
alphas_cumprod = torch.tensor(alphas_cumprod, dtype=torch.float32)
self.set_alphas_cumprod(alphas_cumprod)
return return
+2 -8
View File
@@ -21,7 +21,7 @@ def get_current_html_path(output_format=None):
return html_name return html_name
def log(img, metadata, metadata_parser: MetadataParser | None = None, output_format=None, task=None) -> str: def log(img, metadata, metadata_parser: MetadataParser | None = None, output_format=None) -> str:
path_outputs = modules.config.temp_path if args_manager.args.disable_image_log else modules.config.path_outputs path_outputs = modules.config.temp_path if args_manager.args.disable_image_log else modules.config.path_outputs
output_format = output_format if output_format else modules.config.default_output_format output_format = output_format if output_format else modules.config.default_output_format
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)
@@ -111,15 +111,9 @@ def log(img, metadata, metadata_parser: MetadataParser | None = None, output_for
for label, key, value in metadata: for label, key, value in metadata:
value_txt = str(value).replace('\n', ' </br> ') value_txt = str(value).replace('\n', ' </br> ')
item += f"<tr><td class='label'>{label}</td><td class='value'>{value_txt}</td></tr>\n" item += f"<tr><td class='label'>{label}</td><td class='value'>{value_txt}</td></tr>\n"
if task is not None and 'positive' in task and 'negative' in task:
full_prompt_details = f"""<details><summary>Positive</summary>{', '.join(task['positive'])}</details>
<details><summary>Negative</summary>{', '.join(task['negative'])}</details>"""
item += f"<tr><td class='label'>Full raw prompt</td><td class='value'>{full_prompt_details}</td></tr>\n"
item += "</table>" item += "</table>"
js_txt = urllib.parse.quote(json.dumps({k: v for _, k, v, in metadata}, indent=0), safe='') js_txt = urllib.parse.quote(json.dumps({k: v for _, k, v in metadata}, indent=0), safe='')
item += f"</br><button onclick=\"to_clipboard('{js_txt}')\">Copy to Clipboard</button>" item += f"</br><button onclick=\"to_clipboard('{js_txt}')\">Copy to Clipboard</button>"
item += "</td>" item += "</td>"
-4
View File
@@ -3,7 +3,6 @@ import ldm_patched.modules.samplers
import ldm_patched.modules.model_management import ldm_patched.modules.model_management
from collections import namedtuple from collections import namedtuple
from ldm_patched.contrib.external_align_your_steps import AlignYourStepsScheduler
from ldm_patched.contrib.external_custom_sampler import SDTurboScheduler from ldm_patched.contrib.external_custom_sampler import SDTurboScheduler
from ldm_patched.k_diffusion import sampling as k_diffusion_sampling from ldm_patched.k_diffusion import sampling as k_diffusion_sampling
from ldm_patched.modules.samplers import normal_scheduler, simple_scheduler, ddim_scheduler from ldm_patched.modules.samplers import normal_scheduler, simple_scheduler, ddim_scheduler
@@ -176,9 +175,6 @@ def calculate_sigmas_scheduler_hacked(model, scheduler_name, steps):
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(namedtuple('Patcher', ['model'])(model=model), steps=steps, denoise=1.0)[0]
elif scheduler_name == "align_your_steps":
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]
else: else:
raise TypeError("error invalid scheduler") raise TypeError("error invalid scheduler")
return sigmas return sigmas
+35 -11
View File
@@ -2,12 +2,13 @@ import os
import re import re
import json import json
import math import math
import modules.config
from modules.extra_utils import get_files_from_folder from modules.util import get_files_from_folder
from random import Random
# cannot use modules.config - validators causing circular imports # cannot use modules.config - validators causing circular imports
styles_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../sdxl_styles/')) styles_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../sdxl_styles/'))
wildcards_max_bfs_depth = 64
def normalize_key(k): def normalize_key(k):
@@ -23,6 +24,7 @@ def normalize_key(k):
styles = {} styles = {}
styles_files = get_files_from_folder(styles_path, ['.json']) styles_files = get_files_from_folder(styles_path, ['.json'])
for x in ['sdxl_styles_fooocus.json', for x in ['sdxl_styles_fooocus.json',
@@ -48,13 +50,8 @@ for styles_file in styles_files:
print(f'Failed to load style file {styles_file}') print(f'Failed to load style file {styles_file}')
style_keys = list(styles.keys()) style_keys = list(styles.keys())
fooocus_expansion = 'Fooocus V2' fooocus_expansion = "Fooocus V2"
random_style_name = 'Random Style' legal_style_names = [fooocus_expansion] + style_keys
legal_style_names = [fooocus_expansion, random_style_name] + style_keys
def get_random_style(rng: Random) -> str:
return rng.choice(list(styles.items()))[0]
def apply_style(style, positive): def apply_style(style, positive):
@@ -62,7 +59,34 @@ def apply_style(style, positive):
return p.replace('{prompt}', positive).splitlines(), n.splitlines() return p.replace('{prompt}', positive).splitlines(), n.splitlines()
def get_words(arrays, total_mult, index): def apply_wildcards(wildcard_text, rng, i, read_wildcards_in_order):
for _ in range(wildcards_max_bfs_depth):
placeholders = re.findall(r'__([\w-]+)__', wildcard_text)
if len(placeholders) == 0:
return wildcard_text
print(f'[Wildcards] processing: {wildcard_text}')
for placeholder in placeholders:
try:
matches = [x for x in modules.config.wildcard_filenames if os.path.splitext(os.path.basename(x))[0] == placeholder]
words = open(os.path.join(modules.config.path_wildcards, matches[0]), encoding='utf-8').read().splitlines()
words = [x for x in words if x != '']
assert len(words) > 0
if read_wildcards_in_order:
wildcard_text = wildcard_text.replace(f'__{placeholder}__', words[i % len(words)], 1)
else:
wildcard_text = wildcard_text.replace(f'__{placeholder}__', rng.choice(words), 1)
except:
print(f'[Wildcards] Warning: {placeholder}.txt missing or empty. '
f'Using "{placeholder}" as a normal word.')
wildcard_text = wildcard_text.replace(f'__{placeholder}__', placeholder)
print(f'[Wildcards] {wildcard_text}')
print(f'[Wildcards] BFS stack overflow. Current text: {wildcard_text}')
return wildcard_text
def get_words(arrays, totalMult, index):
if len(arrays) == 1: if len(arrays) == 1:
return [arrays[0].split(',')[index]] return [arrays[0].split(',')[index]]
else: else:
@@ -71,7 +95,7 @@ def get_words(arrays, total_mult, index):
index -= index % len(words) index -= index % len(words)
index /= len(words) index /= len(words)
index = math.floor(index) index = math.floor(index)
return [word] + get_words(arrays[1:], math.floor(total_mult / len(words)), index) return [word] + get_words(arrays[1:], math.floor(totalMult/len(words)), index)
def apply_arrays(text, index): def apply_arrays(text, index):
+1 -1
View File
@@ -39,7 +39,7 @@ def javascript_html():
head += f'<script type="text/javascript" src="{edit_attention_js_path}"></script>\n' head += f'<script type="text/javascript" src="{edit_attention_js_path}"></script>\n'
head += f'<script type="text/javascript" src="{viewer_js_path}"></script>\n' head += f'<script type="text/javascript" src="{viewer_js_path}"></script>\n'
head += f'<script type="text/javascript" src="{image_viewer_js_path}"></script>\n' head += f'<script type="text/javascript" src="{image_viewer_js_path}"></script>\n'
head += f'<meta name="samples-path" content="{samples_path}">\n' head += f'<meta name="samples-path" content="{samples_path}"></meta>\n'
if args_manager.args.theme: if args_manager.args.theme:
head += f'<script type="text/javascript">set_theme(\"{args_manager.args.theme}\");</script>\n' head += f'<script type="text/javascript">set_theme(\"{args_manager.args.theme}\");</script>\n'
+23 -56
View File
@@ -1,12 +1,11 @@
import typing
import numpy as np import numpy as np
import datetime import datetime
import random import random
import math import math
import os import os
import cv2 import cv2
import re
from typing import List, Tuple, AnyStr, NamedTuple
import json import json
import hashlib import hashlib
@@ -15,16 +14,8 @@ from PIL import Image
import modules.sdxl_styles import modules.sdxl_styles
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:
# <lora:some_lora:1>
# <lora:aNotherLora:-1.6>
LORAS_PROMPT_PATTERN = re.compile(r".* <lora : ([^:]+) : ([+-]? (?: (?:\d+ (?:\.\d*)?) | (?:\.\d+)))> .*", re.X)
HASH_SHA256_LENGTH = 10 HASH_SHA256_LENGTH = 10
def erode_or_dilate(x, k): def erode_or_dilate(x, k):
k = int(k) k = int(k)
if k > 0: if k > 0:
@@ -172,6 +163,25 @@ def generate_temp_filename(folder='./outputs/', extension='png'):
return date_string, os.path.abspath(result), filename return date_string, os.path.abspath(result), filename
def get_files_from_folder(folder_path, extensions=None, name_filter=None):
if not os.path.isdir(folder_path):
raise ValueError("Folder path is not a valid directory.")
filenames = []
for root, dirs, files in os.walk(folder_path, topdown=False):
relative_path = os.path.relpath(root, folder_path)
if relative_path == ".":
relative_path = ""
for filename in sorted(files, key=lambda s: s.casefold()):
_, file_extension = os.path.splitext(filename)
if (extensions is None or file_extension.lower() in extensions) and (name_filter is None or name_filter in _):
path = os.path.join(relative_path, filename)
filenames.append(path)
return filenames
def sha256(filename, use_addnet_hash=False, length=HASH_SHA256_LENGTH): def sha256(filename, use_addnet_hash=False, length=HASH_SHA256_LENGTH):
print(f"Calculating sha256 for {filename}: ", end='') print(f"Calculating sha256 for {filename}: ", end='')
if use_addnet_hash: if use_addnet_hash:
@@ -345,7 +355,7 @@ def extract_styles_from_prompt(prompt, negative_prompt):
return list(reversed(extracted)), real_prompt, negative_prompt return list(reversed(extracted)), real_prompt, negative_prompt
class PromptStyle(NamedTuple): class PromptStyle(typing.NamedTuple):
name: str name: str
prompt: str prompt: str
negative_prompt: str negative_prompt: str
@@ -384,47 +394,4 @@ def makedirs_with_log(path):
def get_enabled_loras(loras: list) -> list: def get_enabled_loras(loras: list) -> 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]]
def parse_lora_references_from_prompt(prompt: str, loras: List[Tuple[AnyStr, float]], loras_limit: int = 5) -> List[Tuple[AnyStr, float]]:
new_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:
if lora[0] != "None":
updated_loras.append(lora)
return updated_loras[:loras_limit]
def apply_wildcards(wildcard_text, rng, i, read_wildcards_in_order) -> str:
for _ in range(modules.config.wildcards_max_bfs_depth):
placeholders = re.findall(r'__([\w-]+)__', wildcard_text)
if len(placeholders) == 0:
return wildcard_text
print(f'[Wildcards] processing: {wildcard_text}')
for placeholder in placeholders:
try:
matches = [x for x in modules.config.wildcard_filenames if os.path.splitext(os.path.basename(x))[0] == placeholder]
words = open(os.path.join(modules.config.path_wildcards, matches[0]), encoding='utf-8').read().splitlines()
words = [x for x in words if x != '']
assert len(words) > 0
if read_wildcards_in_order:
wildcard_text = wildcard_text.replace(f'__{placeholder}__', words[i % len(words)], 1)
else:
wildcard_text = wildcard_text.replace(f'__{placeholder}__', rng.choice(words), 1)
except:
print(f'[Wildcards] Warning: {placeholder}.txt missing or empty. '
f'Using "{placeholder}" as a normal word.')
wildcard_text = wildcard_text.replace(f'__{placeholder}__', placeholder)
print(f'[Wildcards] {wildcard_text}')
print(f'[Wildcards] BFS stack overflow. Current text: {wildcard_text}')
return wildcard_text
+4 -10
View File
@@ -1,5 +1,5 @@
{ {
"default_model": "animaPencilXL_v310.safetensors", "default_model": "animaPencilXL_v100.safetensors",
"default_refiner": "None", "default_refiner": "None",
"default_refiner_switch": 0.5, "default_refiner_switch": 0.5,
"default_loras": [ "default_loras": [
@@ -29,7 +29,7 @@
1.0 1.0
] ]
], ],
"default_cfg_scale": 6.0, "default_cfg_scale": 7.0,
"default_sample_sharpness": 2.0, "default_sample_sharpness": 2.0,
"default_sampler": "dpmpp_2m_sde_gpu", "default_sampler": "dpmpp_2m_sde_gpu",
"default_scheduler": "karras", "default_scheduler": "karras",
@@ -43,15 +43,9 @@
], ],
"default_aspect_ratio": "896*1152", "default_aspect_ratio": "896*1152",
"checkpoint_downloads": { "checkpoint_downloads": {
"animaPencilXL_v310.safetensors": "https://huggingface.co/mashb1t/fav_models/resolve/main/fav/animaPencilXL_v310.safetensors" "animaPencilXL_v100.safetensors": "https://huggingface.co/lllyasviel/fav_models/resolve/main/fav/animaPencilXL_v100.safetensors"
}, },
"embeddings_downloads": {}, "embeddings_downloads": {},
"lora_downloads": {}, "lora_downloads": {},
"previous_default_models": [ "previous_default_models": []
"animaPencilXL_v300.safetensors",
"animaPencilXL_v260.safetensors",
"animaPencilXL_v210.safetensors",
"animaPencilXL_v200.safetensors",
"animaPencilXL_v100.safetensors"
]
} }
-1
View File
@@ -368,7 +368,6 @@ A safer way is just to try "run_anime.bat" or "run_realistic.bat" - they should
entry_with_update.py [-h] [--listen [IP]] [--port PORT] entry_with_update.py [-h] [--listen [IP]] [--port PORT]
[--disable-header-check [ORIGIN]] [--disable-header-check [ORIGIN]]
[--web-upload-size WEB_UPLOAD_SIZE] [--web-upload-size WEB_UPLOAD_SIZE]
[--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]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-4
View File
@@ -1,4 +0,0 @@
import sys
import pathlib
sys.path.append(pathlib.Path(f'{__file__}/../modules').parent.resolve())
-48
View File
@@ -1,48 +0,0 @@
import unittest
from modules import util
class TestUtils(unittest.TestCase):
def test_can_parse_tokens_with_lora(self):
test_cases = [
{
"input": ("some prompt, very cool, <lora:hey-lora:0.4>, cool <lora:you-lora:0.2>", [], 5),
"output": [("hey-lora.safetensors", 0.4), ("you-lora.safetensors", 0.2)],
},
# Test can not exceed limit
{
"input": ("some prompt, very cool, <lora:hey-lora:0.4>, cool <lora:you-lora:0.2>", [], 1),
"output": [("hey-lora.safetensors", 0.4)],
},
# test Loras from UI take precedence over prompt
{
"input": (
"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)],
5,
),
"output": [
("hey-lora.safetensors", 0.4),
("l1.safetensors", 0.4),
("l2.safetensors", -0.2),
("l3.safetensors", 0.3),
("l4.safetensors", 0.5),
],
},
# Test lora specification not separated by comma are ignored, only latest specified is used
{
"input": ("some prompt, very cool, <lora:hey-lora:0.4><lora:you-lora:0.2>", [], 3),
"output": [("you-lora.safetensors", 0.2)],
},
{
"input": ("<lora:foo:1..2>, <lora:bar:.>, <lora:baz:+> and <lora:quux:>", [], 6),
"output": []
}
]
for test in test_cases:
prompt, loras, loras_limit = test["input"]
expected = test["output"]
actual = util.parse_lora_references_from_prompt(prompt, loras, loras_limit)
self.assertEqual(expected, actual)
+3 -31
View File
@@ -123,9 +123,8 @@ with shared.gradio_root:
with gr.Column(scale=3, min_width=0): with gr.Column(scale=3, min_width=0):
generate_button = gr.Button(label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True) generate_button = gr.Button(label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True)
reset_button = gr.Button(label="Reconnect", value="Reconnect", elem_classes='type_row', elem_id='reset_button', visible=False)
load_parameter_button = gr.Button(label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False) load_parameter_button = gr.Button(label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False)
skip_button = gr.Button(label="Skip", value="Skip", elem_classes='type_row_half', elem_id='skip_button', visible=False) skip_button = gr.Button(label="Skip", value="Skip", elem_classes='type_row_half', visible=False)
stop_button = gr.Button(label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False) stop_button = gr.Button(label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False)
def stop_clicked(currentTask): def stop_clicked(currentTask):
@@ -436,8 +435,7 @@ with shared.gradio_root:
overwrite_upscale_strength = gr.Slider(label='Forced Overwrite of Denoising Strength of "Upscale"', overwrite_upscale_strength = gr.Slider(label='Forced Overwrite of Denoising Strength of "Upscale"',
minimum=-1, maximum=1.0, step=0.001, value=-1, minimum=-1, maximum=1.0, step=0.001, value=-1,
info='Set as negative number to disable. For developer debugging.') info='Set as negative number to disable. For developer debugging.')
disable_preview = gr.Checkbox(label='Disable Preview', value=modules.config.default_black_out_nsfw, disable_preview = gr.Checkbox(label='Disable Preview', value=False,
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=modules.config.default_performance == flags.Performance.EXTREME_SPEED.value,
@@ -448,15 +446,6 @@ with shared.gradio_root:
value=False) value=False)
read_wildcards_in_order = gr.Checkbox(label="Read wildcards in order", value=False) read_wildcards_in_order = gr.Checkbox(label="Read wildcards in order", value=False)
black_out_nsfw = gr.Checkbox(label='Black Out NSFW',
value=modules.config.default_black_out_nsfw,
interactive=not modules.config.default_black_out_nsfw,
info='Use black image if NSFW is detected.')
black_out_nsfw.change(lambda x: gr.update(value=x, interactive=not x),
inputs=black_out_nsfw, outputs=disable_preview, queue=False,
show_progress=False)
if not args_manager.args.disable_metadata: if not args_manager.args.disable_metadata:
save_metadata_to_images = gr.Checkbox(label='Save Metadata to Images', value=modules.config.default_save_metadata_to_images, save_metadata_to_images = gr.Checkbox(label='Save Metadata to Images', value=modules.config.default_save_metadata_to_images,
info='Adds parameters to generated images allowing manual regeneration.') info='Adds parameters to generated images allowing manual regeneration.')
@@ -646,7 +635,7 @@ with shared.gradio_root:
ctrls += [input_image_checkbox, current_tab] ctrls += [input_image_checkbox, current_tab]
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]
ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg] ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg]
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]
@@ -702,14 +691,6 @@ with shared.gradio_root:
.then(fn=update_history_link, outputs=history_link) \ .then(fn=update_history_link, outputs=history_link) \
.then(fn=lambda: None, _js='playNotification').then(fn=lambda: None, _js='refresh_grid_delayed') .then(fn=lambda: None, _js='playNotification').then(fn=lambda: None, _js='refresh_grid_delayed')
reset_button.click(lambda: [worker.AsyncTask(args=[]), False, gr.update(visible=True, interactive=True)] +
[gr.update(visible=False)] * 6 +
[gr.update(visible=True, value=[])],
outputs=[currentTask, state_is_generating, generate_button,
reset_button, stop_button, skip_button,
progress_html, progress_window, progress_gallery, gallery],
queue=False)
for notification_file in ['notification.ogg', 'notification.mp3']: for notification_file in ['notification.ogg', 'notification.mp3']:
if os.path.exists(notification_file): if os.path.exists(notification_file):
gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False) gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False)
@@ -727,15 +708,6 @@ with shared.gradio_root:
desc_btn.click(trigger_describe, inputs=[desc_method, desc_input_image], desc_btn.click(trigger_describe, inputs=[desc_method, desc_input_image],
outputs=[prompt, style_selections], show_progress=True, queue=True) outputs=[prompt, style_selections], show_progress=True, queue=True)
if args_manager.args.enable_describe_uov_image:
def trigger_uov_describe(mode, img, prompt):
# keep prompt if not empty
if prompt == '':
return trigger_describe(mode, img)
return gr.update(), gr.update()
uov_input_image.upload(trigger_uov_describe, inputs=[desc_method, uov_input_image, prompt],
outputs=[prompt, style_selections], show_progress=True, queue=True)
def dump_default_english_config(): def dump_default_english_config():
from modules.localization import dump_english_config from modules.localization import dump_english_config