Merge commit '4945fc99624afc661aae2d3c5c5d73a32ba21897'

# Conflicts:
#	fooocus_version.py
#	language/en.json
#	launch.py
#	modules/async_worker.py
#	modules/config.py
#	modules/flags.py
#	modules/meta_parser.py
#	modules/util.py
#	webui.py
This commit is contained in:
Manuel Schmid
2024-03-02 17:24:53 +01:00
28 changed files with 591 additions and 86 deletions
+19 -8
View File
@@ -45,14 +45,13 @@ def worker():
import args_manager
from modules.censor import censor_batch, censor_single
from modules.sdxl_styles import apply_style, apply_wildcards, fooocus_expansion
from modules.sdxl_styles import apply_style, apply_wildcards, fooocus_expansion, apply_arrays
from modules.private_logger import log
from extras.expansion import safe_str
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
from modules.upscaler import perform_upscale
from modules.flags import Performance, lora_count
from modules.flags import Performance
from modules.meta_parser import get_metadata_parser, MetadataScheme
pid = os.getpid()
@@ -127,6 +126,14 @@ def worker():
async_task.results = async_task.results + [wall]
return
def apply_enabled_loras(loras):
enabled_loras = []
for lora_enabled, lora_model, lora_weight in loras:
if lora_enabled:
enabled_loras.append([lora_model, lora_weight])
return enabled_loras
@torch.no_grad()
@torch.inference_mode()
def handler(async_task):
@@ -150,7 +157,7 @@ def worker():
base_model_name = args.pop()
refiner_model_name = args.pop()
refiner_switch = args.pop()
loras = [[str(args.pop()), float(args.pop())] for _ in range(lora_count)]
loras = apply_enabled_loras([[bool(args.pop()), str(args.pop()), float(args.pop()), ] for _ in range(modules.config.default_max_lora_number)])
input_image_checkbox = args.pop()
current_tab = args.pop()
uov_method = args.pop()
@@ -162,6 +169,7 @@ def worker():
disable_preview = args.pop()
disable_intermediate_results = args.pop()
disable_seed_increment = args.pop()
black_out_nsfw = args.pop()
adm_scaler_positive = args.pop()
adm_scaler_negative = args.pop()
@@ -423,10 +431,14 @@ def worker():
tasks = []
for i in range(image_number):
task_seed = (seed + i) % (constants.MAX_SEED + 1) # randint is inclusive, % is not
task_rng = random.Random(task_seed) # may bind to inpaint noise in the future
if disable_seed_increment:
task_seed = seed
else:
task_seed = (seed + i) % (constants.MAX_SEED + 1) # randint is inclusive, % is not
task_rng = random.Random(task_seed) # may bind to inpaint noise in the future
task_prompt = apply_wildcards(prompt, task_rng)
task_prompt = apply_arrays(task_prompt, i)
task_negative_prompt = apply_wildcards(negative_prompt, task_rng)
task_extra_positive_prompts = [apply_wildcards(pmt, task_rng) for pmt in extra_positive_prompts]
task_extra_negative_prompts = [apply_wildcards(pmt, task_rng) for pmt in extra_negative_prompts]
@@ -625,8 +637,7 @@ def worker():
)
if debugging_inpaint_preprocessor:
yield_result(async_task, inpaint_worker.current_task.visualize_mask_processing(), black_out_nsfw,
do_not_show_finished_images=True)
yield_result(async_task, inpaint_worker.current_task.visualize_mask_processing(), black_out_nsfw, do_not_show_finished_images=True)
return
progressbar(async_task, 13, 'VAE Inpaint encoding ...')
+81 -27
View File
@@ -7,11 +7,19 @@ import modules.flags
import modules.sdxl_styles
from modules.model_loader import load_file_from_url
from modules.util import get_files_from_folder
from modules.flags import Performance, MetadataScheme, lora_count
from modules.util import get_files_from_folder, makedirs_with_log
from modules.flags import Performance, MetadataScheme
config_path = os.path.abspath("./config.txt")
config_example_path = os.path.abspath("config_modification_tutorial.txt")
def get_config_path(key, default_value):
env = os.getenv(key)
if env is not None and isinstance(env, str):
print(f"Environment: {key} = {env}")
return env
else:
return os.path.abspath(default_value)
config_path = get_config_path('config_path', "./config.txt")
config_example_path = get_config_path('config_example_path', "config_modification_tutorial.txt")
config_dict = {}
always_save_keys = []
visited_keys = []
@@ -137,19 +145,20 @@ def try_load_preset_global(preset):
preset = args_manager.args.preset
try_load_preset_global(preset)
def get_path_output(make_directory=False) -> str:
def get_path_output() -> str:
"""
Checking output path argument and overriding default path.
"""
global config_dict
path_output = get_dir_or_set_default('path_outputs', '../outputs/', make_directory)
path_output = get_dir_or_set_default('path_outputs', '../outputs/', make_directory=True)
if args_manager.args.output_path:
print(f'[CONFIG] Overriding config value path_outputs with {args_manager.args.output_path}')
config_dict['path_outputs'] = path_output = args_manager.args.output_path
return path_output
def get_dir_or_set_default(key, default_value, make_directory=False):
def get_dir_or_set_default(key, default_value, as_array=False, make_directory=False):
global config_dict, visited_keys, always_save_keys
if key not in visited_keys:
@@ -158,26 +167,44 @@ def get_dir_or_set_default(key, default_value, make_directory=False):
if key not in always_save_keys:
always_save_keys.append(key)
v = config_dict.get(key, None)
v = os.getenv(key)
if v is not None:
print(f"Environment: {key} = {v}")
config_dict[key] = v
else:
v = config_dict.get(key, None)
if isinstance(v, str):
if make_directory:
try:
os.makedirs(v, exist_ok=True)
except OSError as error:
print(f'Directory {v} could not be created, reason: {error}')
makedirs_with_log(v)
if os.path.exists(v) and os.path.isdir(v):
return v if not as_array else [v]
elif isinstance(v, list):
if make_directory:
for d in v:
makedirs_with_log(d)
if all([os.path.exists(d) and os.path.isdir(d) for d in v]):
return v
if v is not None:
print(f'Failed to load config key: {json.dumps({key:v})} is invalid or does not exist; will use {json.dumps({key:default_value})} instead.')
dp = os.path.abspath(os.path.join(os.path.dirname(__file__), default_value))
os.makedirs(dp, exist_ok=True)
if isinstance(default_value, list):
dp = []
for path in default_value:
abs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), path))
dp.append(abs_path)
os.makedirs(abs_path, exist_ok=True)
else:
dp = os.path.abspath(os.path.join(os.path.dirname(__file__), default_value))
os.makedirs(dp, exist_ok=True)
if as_array:
dp = [dp]
config_dict[key] = dp
return dp
path_checkpoints = get_dir_or_set_default('path_checkpoints', '../models/checkpoints/')
path_loras = get_dir_or_set_default('path_loras', '../models/loras/')
paths_checkpoints = get_dir_or_set_default('path_checkpoints', ['../models/checkpoints/'], True)
paths_loras = get_dir_or_set_default('path_loras', ['../models/loras/'], True)
path_embeddings = get_dir_or_set_default('path_embeddings', '../models/embeddings/')
path_vae_approx = get_dir_or_set_default('path_vae_approx', '../models/vae_approx/')
path_upscale_models = get_dir_or_set_default('path_upscale_models', '../models/upscale_models/')
@@ -186,8 +213,7 @@ 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_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion')
path_safety_checker_models = get_dir_or_set_default('path_safety_checker_models', '../models/safety_checker_models/')
path_outputs = get_path_output(True)
path_outputs = get_path_output()
def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False):
global config_dict, visited_keys
@@ -195,6 +221,11 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
if key not in visited_keys:
visited_keys.append(key)
v = os.getenv(key)
if v is not None:
print(f"Environment: {key} = {v}")
config_dict[key] = v
if key not in config_dict:
config_dict[key] = default_value
return default_value
@@ -232,6 +263,16 @@ default_refiner_switch = get_config_item_or_set_default(
default_value=0.8,
validator=lambda x: isinstance(x, numbers.Number) and 0 <= x <= 1
)
default_loras_min_weight = get_config_item_or_set_default(
key='default_loras_min_weight',
default_value=-2,
validator=lambda x: isinstance(x, numbers.Number) and -10 <= x <= 10
)
default_loras_max_weight = get_config_item_or_set_default(
key='default_loras_max_weight',
default_value=2,
validator=lambda x: isinstance(x, numbers.Number) and -10 <= x <= 10
)
default_loras = get_config_item_or_set_default(
key='default_loras',
default_value=[
@@ -258,6 +299,11 @@ default_loras = get_config_item_or_set_default(
],
validator=lambda x: isinstance(x, list) and all(len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number) for y in x)
)
default_max_lora_number = get_config_item_or_set_default(
key='default_max_lora_number',
default_value=len(default_loras),
validator=lambda x: isinstance(x, int) and x >= 1
)
default_cfg_scale = get_config_item_or_set_default(
key='default_cfg_scale',
default_value=7.0,
@@ -302,7 +348,7 @@ default_prompt = get_config_item_or_set_default(
default_performance = get_config_item_or_set_default(
key='default_performance',
default_value=Performance.SPEED.value,
validator=lambda x: x in [y[1] for y in modules.flags.performance_selections if y[1] == x]
validator=lambda x: x in Performance.list()
)
default_advanced_checkbox = get_config_item_or_set_default(
key='default_advanced_checkbox',
@@ -428,7 +474,7 @@ default_inpaint_mask_sam_model = get_config_item_or_set_default(
validator=lambda x: x in modules.flags.inpaint_mask_sam_model
)
config_dict["default_loras"] = default_loras = default_loras[:lora_count] + [['None', 1.0] for _ in range(lora_count - len(default_loras))]
config_dict["default_loras"] = default_loras = default_loras[:default_max_lora_number] + [['None', 1.0] for _ in range(default_max_lora_number - len(default_loras))]
# mapping config to meta parameter
possible_preset_keys = {
@@ -436,6 +482,8 @@ possible_preset_keys = {
"default_refiner": "refiner_model",
"default_refiner_switch": "refiner_switch",
"previous_default_models": "previous_default_models",
"default_loras_min_weight": "default_loras_min_weight",
"default_loras_max_weight": "default_loras_max_weight",
"default_loras": "<processed>",
"default_cfg_scale": "guidance_scale",
"default_sample_sharpness": "sharpness",
@@ -447,6 +495,7 @@ possible_preset_keys = {
"default_prompt_negative": "negative_prompt",
"default_styles": "styles",
"default_aspect_ratio": "resolution",
"default_save_metadata_to_images": "default_save_metadata_to_images",
"checkpoint_downloads": "checkpoint_downloads",
"embeddings_downloads": "embeddings_downloads",
"lora_downloads": "lora_downloads"
@@ -491,16 +540,21 @@ with open(config_example_path, "w", encoding="utf-8") as json_file:
model_filenames = []
lora_filenames = []
sdxl_lcm_lora = 'sdxl_lcm_lora.safetensors'
def get_model_filenames(folder_path, name_filter=None):
return get_files_from_folder(folder_path, ['.pth', '.ckpt', '.bin', '.safetensors', '.fooocus.patch'], name_filter)
def get_model_filenames(folder_paths, name_filter=None):
extensions = ['.pth', '.ckpt', '.bin', '.safetensors', '.fooocus.patch']
files = []
for folder in folder_paths:
files += get_files_from_folder(folder, extensions, name_filter)
return files
def update_all_model_names():
global model_filenames, lora_filenames
model_filenames = get_model_filenames(path_checkpoints)
lora_filenames = get_model_filenames(path_loras)
model_filenames = get_model_filenames(paths_checkpoints)
lora_filenames = get_model_filenames(paths_loras)
return
@@ -545,10 +599,10 @@ def downloading_inpaint_models(v):
def downloading_sdxl_lcm_lora():
load_file_from_url(
url='https://huggingface.co/lllyasviel/misc/resolve/main/sdxl_lcm_lora.safetensors',
model_dir=path_loras,
file_name='sdxl_lcm_lora.safetensors'
model_dir=paths_loras[0],
file_name=sdxl_lcm_lora
)
return 'sdxl_lcm_lora.safetensors'
return sdxl_lcm_lora
def downloading_controlnet_canny():
+2 -1
View File
@@ -18,6 +18,7 @@ from ldm_patched.contrib.external import VAEDecode, EmptyLatentImage, VAEEncode,
from ldm_patched.contrib.external_freelunch import FreeU_V2
from ldm_patched.modules.sample import prepare_mask
from modules.lora import match_lora
from modules.util import get_file_from_folder_list
from ldm_patched.modules.lora import model_lora_keys_unet, model_lora_keys_clip
from modules.config import path_embeddings
from ldm_patched.contrib.external_model_advanced import ModelSamplingDiscrete
@@ -79,7 +80,7 @@ class StableDiffusionModel:
if os.path.exists(name):
lora_filename = name
else:
lora_filename = os.path.join(modules.config.path_loras, name)
lora_filename = get_file_from_folder_list(name, modules.config.paths_loras)
if not os.path.exists(lora_filename):
print(f'Lora file not found: {lora_filename}')
+3 -2
View File
@@ -11,6 +11,7 @@ from extras.expansion import FooocusExpansion
from ldm_patched.modules.model_base import SDXL, SDXLRefiner
from modules.sample_hijack import clip_separate
from modules.util import get_file_from_folder_list
model_base = core.StableDiffusionModel()
@@ -60,7 +61,7 @@ def assert_model_integrity():
def refresh_base_model(name):
global model_base
filename = os.path.abspath(os.path.realpath(os.path.join(modules.config.path_checkpoints, name)))
filename = get_file_from_folder_list(name, modules.config.paths_checkpoints)
if model_base.filename == filename:
return
@@ -76,7 +77,7 @@ def refresh_base_model(name):
def refresh_refiner_model(name):
global model_refiner
filename = os.path.abspath(os.path.realpath(os.path.join(modules.config.path_checkpoints, name)))
filename = get_file_from_folder_list(name, modules.config.paths_checkpoints)
if model_refiner.filename == filename:
return
+6 -14
View File
@@ -67,18 +67,13 @@ default_parameters = {
cn_ip: (0.5, 0.6), cn_ip_face: (0.9, 0.75), cn_canny: (0.5, 1.0), cn_cpds: (0.5, 1.0)
} # stop, weight
inpaint_engine_versions = ['None', 'v1', 'v2.5', 'v2.6']
output_formats = ['png', 'jpg', 'webp']
inpaint_mask_models = [
'u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam'
]
inpaint_mask_models = ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam']
inpaint_mask_cloth_category = ['full', 'upper', 'lower']
inpaint_mask_sam_model = ['sam_vit_b_01ec64', 'sam_vit_h_4b8939', 'sam_vit_l_0b3195']
inpaint_engine_versions = ['None', 'v1', 'v2.5', 'v2.6']
inpaint_option_default = 'Inpaint or Outpaint (default)'
inpaint_option_detail = 'Improve Detail (face, hand, eyes, etc.)'
inpaint_option_modify = 'Modify Content (add objects, change background, etc.)'
@@ -98,9 +93,6 @@ metadata_scheme = [
(f'{MetadataScheme.A1111.value} (plain text)', MetadataScheme.A1111.value),
]
lora_count = 5
lora_count_with_lcm = lora_count + 1
controlnet_image_count = 4
@@ -133,7 +125,7 @@ class Performance(Enum):
performance_selections = [
('Quality <span style="color: grey;"> \U00002223 60 steps</span>', Performance.QUALITY.value),
('Speed <span style="color: grey;"> \U00002223 30 steps</span>', Performance.SPEED.value),
('Extreme Speed (LCM) <span style="color: grey;"> \U00002223 8 steps, intermediate results disabled</span>', Performance.EXTREME_SPEED.value)
]
(f'Quality <span style="color: grey;"> \U00002223 {Steps.QUALITY.value} steps</span>', Performance.QUALITY.value),
(f'Speed <span style="color: grey;"> \U00002223 {Steps.SPEED.value} steps</span>', Performance.SPEED.value),
(f'Extreme Speed (LCM) <span style="color: grey;"> \U00002223 {Steps.EXTREME_SPEED.value} steps, intermediate results disabled</span>', Performance.EXTREME_SPEED.value)
]
+24
View File
@@ -112,6 +112,30 @@ progress::after {
margin-left: -5px !important;
}
.lora_enable {
flex-grow: 1 !important;
}
.lora_enable label {
height: 100%;
}
.lora_enable label input {
margin: auto;
}
.lora_enable label span {
display: none;
}
.lora_model {
flex-grow: 5 !important;
}
.lora_weight {
flex-grow: 5 !important;
}
'''
progress_html = '''
<div class="loader-container">
+16 -11
View File
@@ -11,8 +11,8 @@ import fooocus_version
import modules.config
import modules.sdxl_styles
from modules.flags import MetadataScheme, Performance, Steps
from modules.flags import lora_count, SAMPLERS, CIVITAI_NO_KARRAS
from modules.util import quote, unquote, extract_styles_from_prompt, is_json, calculate_sha256
from modules.flags import SAMPLERS, CIVITAI_NO_KARRAS
from modules.util import quote, unquote, extract_styles_from_prompt, is_json, get_file_from_folder_list, calculate_sha256
re_param_code = r'\s*(\w[\w \-/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)'
re_param = re.compile(re_param_code)
@@ -57,7 +57,7 @@ def load_parameter_button_click(raw_metadata: dict | str, is_generating: bool):
get_freeu('freeu', 'FreeU', loaded_parameter_dict, results)
for i in range(lora_count):
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)
return results
@@ -171,9 +171,11 @@ def get_lora(key: str, fallback: str | None, source_dict: dict, results: list):
try:
n, w = source_dict.get(key, source_dict.get(fallback)).split(' : ')
w = float(w)
results.append(True)
results.append(n)
results.append(w)
except:
results.append(True)
results.append('None')
results.append(1)
@@ -209,7 +211,7 @@ def parse_meta_from_preset(preset_content):
preset_prepared[meta_key] = (width, height)
else:
preset_prepared[meta_key] = items[settings_key] if settings_key in items and items[settings_key] is not None else getattr(modules.config, settings_key)
if settings_key == "default_styles" or settings_key == "default_aspect_ratio":
preset_prepared[meta_key] = str(preset_prepared[meta_key])
@@ -241,7 +243,8 @@ class MetadataParser(ABC):
def parse_string(self, metadata: dict) -> str:
raise NotImplementedError
def set_data(self, raw_prompt, full_prompt, raw_negative_prompt, full_negative_prompt, steps, base_model_name, refiner_model_name, loras):
def set_data(self, raw_prompt, full_prompt, raw_negative_prompt, full_negative_prompt, steps, base_model_name,
refiner_model_name, loras):
self.raw_prompt = raw_prompt
self.full_prompt = full_prompt
self.raw_negative_prompt = raw_negative_prompt
@@ -249,18 +252,18 @@ class MetadataParser(ABC):
self.steps = steps
self.base_model_name = Path(base_model_name).stem
base_model_path = os.path.join(modules.config.path_checkpoints, base_model_name)
base_model_path = get_file_from_folder_list(base_model_name, modules.config.paths_checkpoints)
self.base_model_hash = get_sha256(base_model_path)
if refiner_model_name not in ['', 'None']:
self.refiner_model_name = Path(refiner_model_name).stem
refiner_model_path = os.path.join(modules.config.path_checkpoints, refiner_model_name)
refiner_model_path = get_file_from_folder_list(refiner_model_name, modules.config.paths_checkpoints)
self.refiner_model_hash = get_sha256(refiner_model_path)
self.loras = []
for (lora_name, lora_weight) in loras:
if lora_name != 'None':
lora_path = os.path.join(modules.config.path_loras, lora_name)
lora_path = get_file_from_folder_list(lora_name, modules.config.paths_loras)
lora_hash = get_sha256(lora_path)
self.loras.append((Path(lora_name).stem, lora_weight, lora_hash))
@@ -327,7 +330,7 @@ class A1111MetadataParser(MetadataParser):
for k, v in re_param.findall(lastline):
try:
if v[0] == '"' and v[-1] == '"':
if v != '' and v[0] == '"' and v[-1] == '"':
v = unquote(v)
m = re_imagesize.match(v)
@@ -375,7 +378,8 @@ class A1111MetadataParser(MetadataParser):
if 'lora_hashes' in data:
lora_filenames = modules.config.lora_filenames.copy()
lora_filenames.remove(modules.config.downloading_sdxl_lcm_lora())
if modules.config.sdxl_lcm_lora in lora_filenames:
lora_filenames.remove(modules.config.sdxl_lcm_lora)
for li, lora in enumerate(data['lora_hashes'].split(', ')):
lora_name, lora_hash, lora_weight = lora.split(': ')
for filename in lora_filenames:
@@ -456,7 +460,8 @@ class FooocusMetadataParser(MetadataParser):
def parse_json(self, metadata: dict) -> dict:
model_filenames = modules.config.model_filenames.copy()
lora_filenames = modules.config.lora_filenames.copy()
lora_filenames.remove(modules.config.downloading_sdxl_lcm_lora())
if modules.config.sdxl_lcm_lora in lora_filenames:
lora_filenames.remove(modules.config.sdxl_lcm_lora)
for key, value in metadata.items():
if value in ['', 'None']:
+36
View File
@@ -1,6 +1,7 @@
import os
import re
import json
import math
from modules.util import get_files_from_folder
@@ -80,3 +81,38 @@ def apply_wildcards(wildcard_text, rng, directory=wildcards_path):
print(f'[Wildcards] BFS stack overflow. Current text: {wildcard_text}')
return wildcard_text
def get_words(arrays, totalMult, index):
if(len(arrays) == 1):
return [arrays[0].split(',')[index]]
else:
words = arrays[0].split(',')
word = words[index % len(words)]
index -= index % len(words)
index /= len(words)
index = math.floor(index)
return [word] + get_words(arrays[1:], math.floor(totalMult/len(words)), index)
def apply_arrays(text, index):
arrays = re.findall(r'\[\[([\s,\w-]+)\]\]', text)
if len(arrays) == 0:
return text
print(f'[Arrays] processing: {text}')
mult = 1
for arr in arrays:
words = arr.split(',')
mult *= len(words)
index %= mult
chosen_words = get_words(arrays, mult, index)
i = 0
for arr in arrays:
text = text.replace(f'[[{arr}]]', chosen_words[i], 1)
i = i+1
return text
+18 -1
View File
@@ -160,7 +160,7 @@ def generate_temp_filename(folder='./outputs/', extension='png'):
random_number = random.randint(1000, 9999)
filename = f"{time_string}_{random_number}.{extension}"
result = os.path.join(folder, date_string, filename)
return date_string, os.path.abspath(os.path.realpath(result)), filename
return date_string, os.path.abspath(result), filename
def get_files_from_folder(folder_path, exensions=None, name_filter=None):
@@ -341,5 +341,22 @@ def is_json(data: str) -> bool:
return False
return True
def get_file_from_folder_list(name, folders):
for folder in folders:
filename = os.path.abspath(os.path.realpath(os.path.join(folder, name)))
if os.path.isfile(filename):
return filename
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):
try:
os.makedirs(path, exist_ok=True)
except OSError as error:
print(f'Directory {path} could not be created, reason: {error}')