|
import os |
|
import json |
|
import math |
|
import numbers |
|
import args_manager |
|
import modules.flags |
|
import modules.sdxl_styles |
|
|
|
from modules.model_loader import load_file_from_url |
|
from modules.util import get_files_from_folder |
|
|
|
|
|
config_path = os.path.abspath("./config.txt") |
|
config_example_path = os.path.abspath("config_modification_tutorial.txt") |
|
config_dict = {} |
|
always_save_keys = [] |
|
visited_keys = [] |
|
|
|
try: |
|
if os.path.exists(config_path): |
|
with open(config_path, "r", encoding="utf-8") as json_file: |
|
config_dict = json.load(json_file) |
|
always_save_keys = list(config_dict.keys()) |
|
except Exception as e: |
|
print(f'Failed to load config file "{config_path}" . The reason is: {str(e)}') |
|
print('Please make sure that:') |
|
print(f'1. The file "{config_path}" is a valid text file, and you have access to read it.') |
|
print('2. Use "\\\\" instead of "\\" when describing paths.') |
|
print('3. There is no "," before the last "}".') |
|
print('4. All key/value formats are correct.') |
|
|
|
|
|
def try_load_deprecated_user_path_config(): |
|
global config_dict |
|
|
|
if not os.path.exists('user_path_config.txt'): |
|
return |
|
|
|
try: |
|
deprecated_config_dict = json.load(open('user_path_config.txt', "r", encoding="utf-8")) |
|
|
|
def replace_config(old_key, new_key): |
|
if old_key in deprecated_config_dict: |
|
config_dict[new_key] = deprecated_config_dict[old_key] |
|
del deprecated_config_dict[old_key] |
|
|
|
replace_config('modelfile_path', 'path_checkpoints') |
|
replace_config('lorafile_path', 'path_loras') |
|
replace_config('embeddings_path', 'path_embeddings') |
|
replace_config('vae_approx_path', 'path_vae_approx') |
|
replace_config('upscale_models_path', 'path_upscale_models') |
|
replace_config('inpaint_models_path', 'path_inpaint') |
|
replace_config('controlnet_models_path', 'path_controlnet') |
|
replace_config('clip_vision_models_path', 'path_clip_vision') |
|
replace_config('fooocus_expansion_path', 'path_fooocus_expansion') |
|
replace_config('temp_outputs_path', 'path_outputs') |
|
|
|
if deprecated_config_dict.get("default_model", None) == 'juggernautXL_version6Rundiffusion.safetensors': |
|
os.replace('user_path_config.txt', 'user_path_config-deprecated.txt') |
|
print('Config updated successfully in silence. ' |
|
'A backup of previous config is written to "user_path_config-deprecated.txt".') |
|
return |
|
|
|
if input("Newer models and configs are available. " |
|
"Download and update files? [Y/n]:") in ['n', 'N', 'No', 'no', 'NO']: |
|
config_dict.update(deprecated_config_dict) |
|
print('Loading using deprecated old models and deprecated old configs.') |
|
return |
|
else: |
|
os.replace('user_path_config.txt', 'user_path_config-deprecated.txt') |
|
print('Config updated successfully by user. ' |
|
'A backup of previous config is written to "user_path_config-deprecated.txt".') |
|
return |
|
except Exception as e: |
|
print('Processing deprecated config failed') |
|
print(e) |
|
return |
|
|
|
|
|
try_load_deprecated_user_path_config() |
|
|
|
preset = args_manager.args.preset |
|
|
|
if isinstance(preset, str): |
|
preset_path = os.path.abspath(f'./presets/{preset}.json') |
|
try: |
|
if os.path.exists(preset_path): |
|
with open(preset_path, "r", encoding="utf-8") as json_file: |
|
config_dict.update(json.load(json_file)) |
|
print(f'Loaded preset: {preset_path}') |
|
else: |
|
raise FileNotFoundError |
|
except Exception as e: |
|
print(f'Load preset [{preset_path}] failed') |
|
print(e) |
|
|
|
|
|
def get_dir_or_set_default(key, default_value): |
|
global config_dict, visited_keys, always_save_keys |
|
|
|
if key not in visited_keys: |
|
visited_keys.append(key) |
|
|
|
if key not in always_save_keys: |
|
always_save_keys.append(key) |
|
|
|
v = config_dict.get(key, None) |
|
if isinstance(v, str) and os.path.exists(v) and os.path.isdir(v): |
|
return v |
|
else: |
|
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) |
|
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/') |
|
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/') |
|
path_inpaint = get_dir_or_set_default('path_inpaint', '../models/inpaint/') |
|
path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlnet/') |
|
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_outputs = get_dir_or_set_default('path_outputs', '../outputs/') |
|
|
|
|
|
def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False): |
|
global config_dict, visited_keys |
|
|
|
if key not in visited_keys: |
|
visited_keys.append(key) |
|
|
|
if key not in config_dict: |
|
config_dict[key] = default_value |
|
return default_value |
|
|
|
v = config_dict.get(key, None) |
|
if not disable_empty_as_none: |
|
if v is None or v == '': |
|
v = 'None' |
|
if validator(v): |
|
return v |
|
else: |
|
if v is not None: |
|
print(f'Failed to load config key: {json.dumps({key:v})} is invalid; will use {json.dumps({key:default_value})} instead.') |
|
config_dict[key] = default_value |
|
return default_value |
|
|
|
|
|
default_base_model_name = get_config_item_or_set_default( |
|
key='default_model', |
|
default_value='juggernautXL_version6Rundiffusion.safetensors', |
|
validator=lambda x: isinstance(x, str) |
|
) |
|
default_refiner_model_name = get_config_item_or_set_default( |
|
key='default_refiner', |
|
default_value='None', |
|
validator=lambda x: isinstance(x, str) |
|
) |
|
default_refiner_switch = get_config_item_or_set_default( |
|
key='default_refiner_switch', |
|
default_value=0.5, |
|
validator=lambda x: isinstance(x, numbers.Number) and 0 <= x <= 1 |
|
) |
|
default_loras = get_config_item_or_set_default( |
|
key='default_loras', |
|
default_value=[ |
|
[ |
|
"sd_xl_offset_example-lora_1.0.safetensors", |
|
0.1 |
|
], |
|
[ |
|
"None", |
|
1.0 |
|
], |
|
[ |
|
"None", |
|
1.0 |
|
], |
|
[ |
|
"None", |
|
1.0 |
|
], |
|
[ |
|
"None", |
|
1.0 |
|
] |
|
], |
|
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_cfg_scale = get_config_item_or_set_default( |
|
key='default_cfg_scale', |
|
default_value=4.0, |
|
validator=lambda x: isinstance(x, numbers.Number) |
|
) |
|
default_sample_sharpness = get_config_item_or_set_default( |
|
key='default_sample_sharpness', |
|
default_value=2.0, |
|
validator=lambda x: isinstance(x, numbers.Number) |
|
) |
|
default_sampler = get_config_item_or_set_default( |
|
key='default_sampler', |
|
default_value='dpmpp_2m_sde_gpu', |
|
validator=lambda x: x in modules.flags.sampler_list |
|
) |
|
default_scheduler = get_config_item_or_set_default( |
|
key='default_scheduler', |
|
default_value='karras', |
|
validator=lambda x: x in modules.flags.scheduler_list |
|
) |
|
default_styles = get_config_item_or_set_default( |
|
key='default_styles', |
|
default_value=[ |
|
"Fooocus V2", |
|
"Fooocus Enhance", |
|
"Fooocus Sharp" |
|
], |
|
validator=lambda x: isinstance(x, list) and all(y in modules.sdxl_styles.legal_style_names for y in x) |
|
) |
|
default_prompt_negative = get_config_item_or_set_default( |
|
key='default_prompt_negative', |
|
default_value='', |
|
validator=lambda x: isinstance(x, str), |
|
disable_empty_as_none=True |
|
) |
|
default_prompt = get_config_item_or_set_default( |
|
key='default_prompt', |
|
default_value='', |
|
validator=lambda x: isinstance(x, str), |
|
disable_empty_as_none=True |
|
) |
|
default_performance = get_config_item_or_set_default( |
|
key='default_performance', |
|
default_value='Speed', |
|
validator=lambda x: x in modules.flags.performance_selections |
|
) |
|
default_advanced_checkbox = get_config_item_or_set_default( |
|
key='default_advanced_checkbox', |
|
default_value=False, |
|
validator=lambda x: isinstance(x, bool) |
|
) |
|
default_image_number = get_config_item_or_set_default( |
|
key='default_image_number', |
|
default_value=2, |
|
validator=lambda x: isinstance(x, int) and 1 <= x <= 32 |
|
) |
|
checkpoint_downloads = get_config_item_or_set_default( |
|
key='checkpoint_downloads', |
|
default_value={ |
|
"juggernautXL_version6Rundiffusion.safetensors": "https://huggingface.co/lllyasviel/fav_models/resolve/main/fav/juggernautXL_version6Rundiffusion.safetensors" |
|
}, |
|
validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()) |
|
) |
|
lora_downloads = get_config_item_or_set_default( |
|
key='lora_downloads', |
|
default_value={ |
|
"sd_xl_offset_example-lora_1.0.safetensors": "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_offset_example-lora_1.0.safetensors" |
|
}, |
|
validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()) |
|
) |
|
embeddings_downloads = get_config_item_or_set_default( |
|
key='embeddings_downloads', |
|
default_value={}, |
|
validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()) |
|
) |
|
available_aspect_ratios = get_config_item_or_set_default( |
|
key='available_aspect_ratios', |
|
default_value=[ |
|
'704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152', |
|
'896*1152', '896*1088', '960*1088', '960*1024', '1024*1024', '1024*960', |
|
'1088*960', '1088*896', '1152*896', '1152*832', '1216*832', '1280*768', |
|
'1344*768', '1344*704', '1408*704', '1472*704', '1536*640', '1600*640', |
|
'1664*576', '1728*576' |
|
], |
|
validator=lambda x: isinstance(x, list) and all('*' in v for v in x) and len(x) > 1 |
|
) |
|
default_aspect_ratio = get_config_item_or_set_default( |
|
key='default_aspect_ratio', |
|
default_value='1152*896' if '1152*896' in available_aspect_ratios else available_aspect_ratios[0], |
|
validator=lambda x: x in available_aspect_ratios |
|
) |
|
default_inpaint_engine_version = get_config_item_or_set_default( |
|
key='default_inpaint_engine_version', |
|
default_value='v2.6', |
|
validator=lambda x: x in modules.flags.inpaint_engine_versions |
|
) |
|
default_cfg_tsnr = get_config_item_or_set_default( |
|
key='default_cfg_tsnr', |
|
default_value=7.0, |
|
validator=lambda x: isinstance(x, numbers.Number) |
|
) |
|
default_overwrite_step = get_config_item_or_set_default( |
|
key='default_overwrite_step', |
|
default_value=-1, |
|
validator=lambda x: isinstance(x, int) |
|
) |
|
default_overwrite_switch = get_config_item_or_set_default( |
|
key='default_overwrite_switch', |
|
default_value=-1, |
|
validator=lambda x: isinstance(x, int) |
|
) |
|
example_inpaint_prompts = get_config_item_or_set_default( |
|
key='example_inpaint_prompts', |
|
default_value=[ |
|
'highly detailed face', 'detailed girl face', 'detailed man face', 'detailed hand', 'beautiful eyes' |
|
], |
|
validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x) |
|
) |
|
|
|
example_inpaint_prompts = [[x] for x in example_inpaint_prompts] |
|
|
|
config_dict["default_loras"] = default_loras = default_loras[:5] + [['None', 1.0] for _ in range(5 - len(default_loras))] |
|
|
|
possible_preset_keys = [ |
|
"default_model", |
|
"default_refiner", |
|
"default_refiner_switch", |
|
"default_loras", |
|
"default_cfg_scale", |
|
"default_sample_sharpness", |
|
"default_sampler", |
|
"default_scheduler", |
|
"default_performance", |
|
"default_prompt", |
|
"default_prompt_negative", |
|
"default_styles", |
|
"default_aspect_ratio", |
|
"checkpoint_downloads", |
|
"embeddings_downloads", |
|
"lora_downloads", |
|
] |
|
|
|
|
|
REWRITE_PRESET = False |
|
|
|
if REWRITE_PRESET and isinstance(args_manager.args.preset, str): |
|
save_path = 'presets/' + args_manager.args.preset + '.json' |
|
with open(save_path, "w", encoding="utf-8") as json_file: |
|
json.dump({k: config_dict[k] for k in possible_preset_keys}, json_file, indent=4) |
|
print(f'Preset saved to {save_path}. Exiting ...') |
|
exit(0) |
|
|
|
|
|
def add_ratio(x): |
|
a, b = x.replace('*', ' ').split(' ')[:2] |
|
a, b = int(a), int(b) |
|
g = math.gcd(a, b) |
|
return f'{a}×{b} <span style="color: grey;"> \U00002223 {a // g}:{b // g}</span>' |
|
|
|
|
|
default_aspect_ratio = add_ratio(default_aspect_ratio) |
|
available_aspect_ratios = [add_ratio(x) for x in available_aspect_ratios] |
|
|
|
|
|
|
|
if not os.path.exists(config_path): |
|
with open(config_path, "w", encoding="utf-8") as json_file: |
|
json.dump({k: config_dict[k] for k in always_save_keys}, json_file, indent=4) |
|
|
|
|
|
|
|
with open(config_example_path, "w", encoding="utf-8") as json_file: |
|
cpa = config_path.replace("\\", "\\\\") |
|
json_file.write(f'You can modify your "{cpa}" using the below keys, formats, and examples.\n' |
|
f'Do not modify this file. Modifications in this file will not take effect.\n' |
|
f'This file is a tutorial and example. Please edit "{cpa}" to really change any settings.\n' |
|
+ 'Remember to split the paths with "\\\\" rather than "\\", ' |
|
'and there is no "," before the last "}". \n\n\n') |
|
json.dump({k: config_dict[k] for k in visited_keys}, json_file, indent=4) |
|
|
|
|
|
os.makedirs(path_outputs, exist_ok=True) |
|
|
|
model_filenames = [] |
|
lora_filenames = [] |
|
|
|
|
|
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 update_all_model_names(): |
|
global model_filenames, lora_filenames |
|
model_filenames = get_model_filenames(path_checkpoints) |
|
lora_filenames = get_model_filenames(path_loras) |
|
return |
|
|
|
|
|
def downloading_inpaint_models(v): |
|
assert v in modules.flags.inpaint_engine_versions |
|
|
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/fooocus_inpaint/resolve/main/fooocus_inpaint_head.pth', |
|
model_dir=path_inpaint, |
|
file_name='fooocus_inpaint_head.pth' |
|
) |
|
head_file = os.path.join(path_inpaint, 'fooocus_inpaint_head.pth') |
|
patch_file = None |
|
|
|
if v == 'v1': |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/fooocus_inpaint/resolve/main/inpaint.fooocus.patch', |
|
model_dir=path_inpaint, |
|
file_name='inpaint.fooocus.patch' |
|
) |
|
patch_file = os.path.join(path_inpaint, 'inpaint.fooocus.patch') |
|
|
|
if v == 'v2.5': |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/fooocus_inpaint/resolve/main/inpaint_v25.fooocus.patch', |
|
model_dir=path_inpaint, |
|
file_name='inpaint_v25.fooocus.patch' |
|
) |
|
patch_file = os.path.join(path_inpaint, 'inpaint_v25.fooocus.patch') |
|
|
|
if v == 'v2.6': |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/fooocus_inpaint/resolve/main/inpaint_v26.fooocus.patch', |
|
model_dir=path_inpaint, |
|
file_name='inpaint_v26.fooocus.patch' |
|
) |
|
patch_file = os.path.join(path_inpaint, 'inpaint_v26.fooocus.patch') |
|
|
|
return head_file, patch_file |
|
|
|
|
|
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' |
|
) |
|
return 'sdxl_lcm_lora.safetensors' |
|
|
|
|
|
def downloading_controlnet_canny(): |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/control-lora-canny-rank128.safetensors', |
|
model_dir=path_controlnet, |
|
file_name='control-lora-canny-rank128.safetensors' |
|
) |
|
return os.path.join(path_controlnet, 'control-lora-canny-rank128.safetensors') |
|
|
|
|
|
def downloading_controlnet_cpds(): |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/fooocus_xl_cpds_128.safetensors', |
|
model_dir=path_controlnet, |
|
file_name='fooocus_xl_cpds_128.safetensors' |
|
) |
|
return os.path.join(path_controlnet, 'fooocus_xl_cpds_128.safetensors') |
|
|
|
|
|
def downloading_ip_adapters(v): |
|
assert v in ['ip', 'face'] |
|
|
|
results = [] |
|
|
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/clip_vision_vit_h.safetensors', |
|
model_dir=path_clip_vision, |
|
file_name='clip_vision_vit_h.safetensors' |
|
) |
|
results += [os.path.join(path_clip_vision, 'clip_vision_vit_h.safetensors')] |
|
|
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/fooocus_ip_negative.safetensors', |
|
model_dir=path_controlnet, |
|
file_name='fooocus_ip_negative.safetensors' |
|
) |
|
results += [os.path.join(path_controlnet, 'fooocus_ip_negative.safetensors')] |
|
|
|
if v == 'ip': |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/ip-adapter-plus_sdxl_vit-h.bin', |
|
model_dir=path_controlnet, |
|
file_name='ip-adapter-plus_sdxl_vit-h.bin' |
|
) |
|
results += [os.path.join(path_controlnet, 'ip-adapter-plus_sdxl_vit-h.bin')] |
|
|
|
if v == 'face': |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/ip-adapter-plus-face_sdxl_vit-h.bin', |
|
model_dir=path_controlnet, |
|
file_name='ip-adapter-plus-face_sdxl_vit-h.bin' |
|
) |
|
results += [os.path.join(path_controlnet, 'ip-adapter-plus-face_sdxl_vit-h.bin')] |
|
|
|
return results |
|
|
|
|
|
def downloading_upscale_model(): |
|
load_file_from_url( |
|
url='https://huggingface.co/lllyasviel/misc/resolve/main/fooocus_upscaler_s409985e5.bin', |
|
model_dir=path_upscale_models, |
|
file_name='fooocus_upscaler_s409985e5.bin' |
|
) |
|
return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin') |
|
|
|
|
|
update_all_model_names() |
|
|