Compare commits

..
Author SHA1 Message Date
Manuel Schmid a8a82647a9 feat: remove hyper-sd8 performance
still waiting for the release of hyper-sd 4step CFG LoRA, not yet satisfied with any of the CFG LoRAs compared to non-cfg ones.
see https://huggingface.co/ByteDance/Hyper-SD
2024-05-19 12:26:59 +02:00
Manuel Schmid 8ef1a2119c wip: add hyper-sd 8 step cfg lora with negative prompt support 2024-05-04 16:24:33 +02:00
Manuel Schmid 1a9faf4d1b feat: change ByteDance huggingface profile with mashb1t 2024-05-02 21:19:22 +02:00
Manuel Schmid 27df5df20b feat: use LoRA weight 0.8, sampler dpmpp_sde_gpu and scheduler_name karras
suggested in https://github.com/lllyasviel/Fooocus/discussions/2813#discussioncomment-9245251
results see https://github.com/lllyasviel/Fooocus/discussions/2813#discussioncomment-9275251
2024-04-30 15:06:33 +02:00
Manuel Schmid fa74a0c7fe feat: add performance hyper-sd based on 4step LoRA 2024-04-26 22:22:09 +02:00
Manuel Schmid e2f9bcb11d docs: bump version number to 2.3.1, add changelog (#2616) 2024-03-23 16:57:11 +01:00
Manuel Schmid 523ef5c70e fix: add Civitai compatibility for LoRAs in a1111 metadata scheme by switching schema (#2615)
* feat: update sha256 generation functions

https://github.com/lllyasviel/stable-diffusion-webui-forge/blob/29be1da7cf2b5dccfc70fbdd33eb35c56a31ffb7/modules/hashes.py

* feat: add compatibility for LoRAs in a1111 metadata scheme

* feat: add backwards compatibility

* refactor: extract remove_special_loras

* fix: correctly apply LoRA weight for legacy schema
2024-03-23 16:37:18 +01:00
Manuel Schmid 9aaa400553 fix: use correct base dimensions for outpaint mask padding (#2612) 2024-03-23 13:10:21 +01:00
Manuel Schmid 7564dd5131 fix: load image number from preset (#2611)
* fix: add default_image_number to preset handling

* fix: use minimum image number of preset and config to prevent UI overflow
2024-03-23 12:49:20 +01:00
Manuel Schmid 978267f461 fix: correctly set preset config and loras in meta parser 2024-03-20 21:16:03 +01:00
Manuel Schmid e9bc5e50c6 Merge pull request #2576 from mashb1t/hotfix/default-max-lora-number-adjustments
fix: add enabled value to LoRA when setting default_max_lora_number
2024-03-19 23:10:03 +01:00
Manuel Schmid 856eb750ab fix: add enabled value to LoRA when setting default_max_lora_number 2024-03-19 23:08:38 +01:00
Manuel Schmid 6b41af7140 Merge pull request #2571 from mashb1t/hotfix/remove-positive-prompt-from-anime-preset
fix: remove positive prompt from anime prefix
2024-03-19 19:11:53 +01:00
Manuel Schmid 532a6e2e67 fix: remove positive prompt from anime prefix
prevents the prompt from getting overridden when switching presets in browser
2024-03-19 19:10:37 +01:00
8 changed files with 150 additions and 43 deletions
+1 -1
View File
@@ -1 +1 @@
version = '2.3.0' version = '2.3.1'
+31 -4
View File
@@ -263,6 +263,33 @@ 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
elif performance_selection == Performance.HYPER_SD8:
print('Enter Hyper-SD8 mode.')
progressbar(async_task, 1, 'Downloading Hyper-SD components ...')
loras += [(modules.config.downloading_sdxl_hyper_sd_cfg_lora(), 0.3)]
sampler_name = 'dpmpp_sde_gpu'
scheduler_name = 'normal'
print(f'[Parameters] Adaptive CFG = {adaptive_cfg}') print(f'[Parameters] Adaptive CFG = {adaptive_cfg}')
print(f'[Parameters] Sharpness = {sharpness}') print(f'[Parameters] Sharpness = {sharpness}')
print(f'[Parameters] ControlNet Softness = {controlnet_softness}') print(f'[Parameters] ControlNet Softness = {controlnet_softness}')
@@ -614,12 +641,12 @@ def worker():
H, W, C = inpaint_image.shape H, W, C = inpaint_image.shape
if 'left' in outpaint_selections: if 'left' in outpaint_selections:
inpaint_image = np.pad(inpaint_image, [[0, 0], [int(H * 0.3), 0], [0, 0]], mode='edge') inpaint_image = np.pad(inpaint_image, [[0, 0], [int(W * 0.3), 0], [0, 0]], mode='edge')
inpaint_mask = np.pad(inpaint_mask, [[0, 0], [int(H * 0.3), 0]], mode='constant', inpaint_mask = np.pad(inpaint_mask, [[0, 0], [int(W * 0.3), 0]], mode='constant',
constant_values=255) constant_values=255)
if 'right' in outpaint_selections: if 'right' in outpaint_selections:
inpaint_image = np.pad(inpaint_image, [[0, 0], [0, int(H * 0.3)], [0, 0]], mode='edge') inpaint_image = np.pad(inpaint_image, [[0, 0], [0, int(W * 0.3)], [0, 0]], mode='edge')
inpaint_mask = np.pad(inpaint_mask, [[0, 0], [0, int(H * 0.3)]], mode='constant', inpaint_mask = np.pad(inpaint_mask, [[0, 0], [0, int(W * 0.3)]], mode='constant',
constant_values=255) constant_values=255)
inpaint_image = np.ascontiguousarray(inpaint_image.copy()) inpaint_image = np.ascontiguousarray(inpaint_image.copy())
+14 -10
View File
@@ -124,14 +124,6 @@ def try_get_preset_content(preset):
print(e) print(e)
return {} return {}
try:
with open(os.path.abspath(f'./presets/default.json'), "r", encoding="utf-8") as json_file:
config_dict.update(json.load(json_file))
except Exception as e:
print(f'Load default preset failed.')
print(e)
available_presets = get_presets() available_presets = get_presets()
preset = args_manager.args.preset preset = args_manager.args.preset
config_dict.update(try_get_preset_content(preset)) config_dict.update(try_get_preset_content(preset))
@@ -476,7 +468,7 @@ metadata_created_by = get_config_item_or_set_default(
example_inpaint_prompts = [[x] for x in example_inpaint_prompts] example_inpaint_prompts = [[x] for x in example_inpaint_prompts]
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))] config_dict["default_loras"] = default_loras = default_loras[:default_max_lora_number] + [[True, 'None', 1.0] for _ in range(default_max_lora_number - len(default_loras))]
# mapping config to meta parameter # mapping config to meta parameter
possible_preset_keys = { possible_preset_keys = {
@@ -493,6 +485,7 @@ possible_preset_keys = {
"default_scheduler": "scheduler", "default_scheduler": "scheduler",
"default_overwrite_step": "steps", "default_overwrite_step": "steps",
"default_performance": "performance", "default_performance": "performance",
"default_image_number": "image_number",
"default_prompt": "prompt", "default_prompt": "prompt",
"default_prompt_negative": "negative_prompt", "default_prompt_negative": "negative_prompt",
"default_styles": "styles", "default_styles": "styles",
@@ -546,6 +539,8 @@ 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, 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):
@@ -614,13 +609,22 @@ 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/ByteDance/SDXL-Lightning/resolve/main/sdxl_lightning_4step_lora.safetensors', url='https://huggingface.co/mashb1t/misc/resolve/main/sdxl_lightning_4step_lora.safetensors',
model_dir=paths_loras[0], model_dir=paths_loras[0],
file_name=sdxl_lightning_lora file_name=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',
+4 -1
View File
@@ -107,6 +107,7 @@ 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):
@@ -114,6 +115,7 @@ 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):
@@ -121,6 +123,7 @@ 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:
@@ -130,7 +133,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] return x in [cls.EXTREME_SPEED.value, cls.LIGHTNING.value, cls.HYPER_SD.value]
def steps(self) -> int | None: def steps(self) -> int | None:
return Steps[self.name].value if Steps[self.name] else None return Steps[self.name].value if Steps[self.name] else None
+59 -21
View File
@@ -1,5 +1,4 @@
import json import json
import os
import re import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from pathlib import Path from pathlib import Path
@@ -12,7 +11,7 @@ import modules.config
import modules.sdxl_styles import modules.sdxl_styles
from modules.flags import MetadataScheme, Performance, Steps from modules.flags import MetadataScheme, Performance, Steps
from modules.flags import SAMPLERS, CIVITAI_NO_KARRAS 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 from modules.util import quote, unquote, extract_styles_from_prompt, is_json, get_file_from_folder_list, sha256
re_param_code = r'\s*(\w[\w \-/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' re_param_code = r'\s*(\w[\w \-/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)'
re_param = re.compile(re_param_code) re_param = re.compile(re_param_code)
@@ -27,8 +26,9 @@ def load_parameter_button_click(raw_metadata: dict | str, is_generating: bool):
loaded_parameter_dict = json.loads(raw_metadata) loaded_parameter_dict = json.loads(raw_metadata)
assert isinstance(loaded_parameter_dict, dict) assert isinstance(loaded_parameter_dict, dict)
results = [len(loaded_parameter_dict) > 0, 1] results = [len(loaded_parameter_dict) > 0]
get_image_number('image_number', 'Image Number', loaded_parameter_dict, results)
get_str('prompt', 'Prompt', loaded_parameter_dict, results) get_str('prompt', 'Prompt', loaded_parameter_dict, results)
get_str('negative_prompt', 'Negative Prompt', loaded_parameter_dict, results) get_str('negative_prompt', 'Negative Prompt', loaded_parameter_dict, results)
get_list('styles', 'Styles', loaded_parameter_dict, results) get_list('styles', 'Styles', loaded_parameter_dict, results)
@@ -92,13 +92,25 @@ def get_float(key: str, fallback: str | None, source_dict: dict, results: list,
results.append(gr.update()) results.append(gr.update())
def get_image_number(key: str, fallback: str | None, source_dict: dict, results: list, default=None):
try:
h = source_dict.get(key, source_dict.get(fallback, default))
assert h is not None
h = int(h)
h = min(h, modules.config.default_max_image_number)
results.append(h)
except:
results.append(1)
def get_steps(key: str, fallback: str | None, source_dict: dict, results: list, default=None): def get_steps(key: str, fallback: str | None, source_dict: dict, results: list, default=None):
try: try:
h = source_dict.get(key, source_dict.get(fallback, default)) h = source_dict.get(key, source_dict.get(fallback, default))
assert h is not None assert h is not None
h = int(h) h = int(h)
# if not in steps or in steps and performance is not the same # if not in steps or in steps and performance is not the same
if h not in iter(Steps) or Steps(h).name.casefold() != source_dict.get('performance', '').replace(' ', '_').casefold(): if h not in iter(Steps) or Steps(h).name.casefold() != source_dict.get('performance', '').replace(' ',
'_').casefold():
results.append(h) results.append(h)
return return
results.append(-1) results.append(-1)
@@ -169,11 +181,20 @@ def get_freeu(key: str, fallback: str | None, source_dict: dict, results: list,
def get_lora(key: str, fallback: str | None, source_dict: dict, results: list): def get_lora(key: str, fallback: str | None, source_dict: dict, results: list):
try: try:
n, w = source_dict.get(key, source_dict.get(fallback)).split(' : ') split_data = source_dict.get(key, source_dict.get(fallback)).split(' : ')
w = float(w) enabled = True
results.append(True) name = split_data[0]
results.append(n) weight = split_data[1]
results.append(w)
if len(split_data) == 3:
enabled = split_data[0] == 'True'
name = split_data[1]
weight = split_data[2]
weight = float(weight)
results.append(enabled)
results.append(name)
results.append(weight)
except: except:
results.append(True) results.append(True)
results.append('None') results.append('None')
@@ -183,7 +204,8 @@ def get_lora(key: str, fallback: str | None, source_dict: dict, results: list):
def get_sha256(filepath): def get_sha256(filepath):
global hash_cache global hash_cache
if filepath not in hash_cache: if filepath not in hash_cache:
hash_cache[filepath] = calculate_sha256(filepath) # is_safetensors = os.path.splitext(filepath)[1].lower() == '.safetensors'
hash_cache[filepath] = sha256(filepath)
return hash_cache[filepath] return hash_cache[filepath]
@@ -210,8 +232,9 @@ def parse_meta_from_preset(preset_content):
height = height[:height.index(" ")] height = height[:height.index(" ")]
preset_prepared[meta_key] = (width, height) preset_prepared[meta_key] = (width, height)
else: 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) 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": if settings_key == "default_styles" or settings_key == "default_aspect_ratio":
preset_prepared[meta_key] = str(preset_prepared[meta_key]) preset_prepared[meta_key] = str(preset_prepared[meta_key])
@@ -267,6 +290,12 @@ class MetadataParser(ABC):
lora_hash = get_sha256(lora_path) lora_hash = get_sha256(lora_path)
self.loras.append((Path(lora_name).stem, lora_weight, lora_hash)) self.loras.append((Path(lora_name).stem, lora_weight, lora_hash))
@staticmethod
def remove_special_loras(lora_filenames):
for lora_to_remove in modules.config.loras_metadata_remove:
if lora_to_remove in lora_filenames:
lora_filenames.remove(lora_to_remove)
class A1111MetadataParser(MetadataParser): class A1111MetadataParser(MetadataParser):
def get_scheme(self) -> MetadataScheme: def get_scheme(self) -> MetadataScheme:
@@ -376,12 +405,19 @@ class A1111MetadataParser(MetadataParser):
data[key] = filename data[key] = filename
break break
if 'lora_hashes' in data and data['lora_hashes'] != '': lora_data = ''
if 'lora_weights' in data and data['lora_weights'] != '':
lora_data = data['lora_weights']
elif 'lora_hashes' in data and data['lora_hashes'] != '' and data['lora_hashes'].split(', ')[0].count(':') == 2:
lora_data = data['lora_hashes']
if lora_data != '':
lora_filenames = modules.config.lora_filenames.copy() lora_filenames = modules.config.lora_filenames.copy()
if modules.config.sdxl_lcm_lora in lora_filenames: self.remove_special_loras(lora_filenames)
lora_filenames.remove(modules.config.sdxl_lcm_lora) for li, lora in enumerate(lora_data.split(', ')):
for li, lora in enumerate(data['lora_hashes'].split(', ')): lora_split = lora.split(': ')
lora_name, lora_hash, lora_weight = lora.split(': ') lora_name = lora_split[0]
lora_weight = lora_split[2] if len(lora_split) == 3 else lora_split[1]
for filename in lora_filenames: for filename in lora_filenames:
path = Path(filename) path = Path(filename)
if lora_name == path.stem: if lora_name == path.stem:
@@ -432,11 +468,15 @@ class A1111MetadataParser(MetadataParser):
if len(self.loras) > 0: if len(self.loras) > 0:
lora_hashes = [] lora_hashes = []
lora_weights = []
for index, (lora_name, lora_weight, lora_hash) in enumerate(self.loras): for index, (lora_name, lora_weight, lora_hash) in enumerate(self.loras):
# workaround for Fooocus not knowing LoRA name in LoRA metadata # workaround for Fooocus not knowing LoRA name in LoRA metadata
lora_hashes.append(f'{lora_name}: {lora_hash}: {lora_weight}') lora_hashes.append(f'{lora_name}: {lora_hash}')
lora_weights.append(f'{lora_name}: {lora_weight}')
lora_hashes_string = ', '.join(lora_hashes) lora_hashes_string = ', '.join(lora_hashes)
lora_weights_string = ', '.join(lora_weights)
generation_params[self.fooocus_to_a1111['lora_hashes']] = lora_hashes_string generation_params[self.fooocus_to_a1111['lora_hashes']] = lora_hashes_string
generation_params[self.fooocus_to_a1111['lora_weights']] = lora_weights_string
generation_params[self.fooocus_to_a1111['version']] = data['version'] generation_params[self.fooocus_to_a1111['version']] = data['version']
@@ -459,9 +499,7 @@ class FooocusMetadataParser(MetadataParser):
def parse_json(self, metadata: dict) -> dict: def parse_json(self, metadata: dict) -> dict:
model_filenames = modules.config.model_filenames.copy() model_filenames = modules.config.model_filenames.copy()
lora_filenames = modules.config.lora_filenames.copy() lora_filenames = modules.config.lora_filenames.copy()
if modules.config.sdxl_lcm_lora in lora_filenames: self.remove_special_loras(lora_filenames)
lora_filenames.remove(modules.config.sdxl_lcm_lora)
for key, value in metadata.items(): for key, value in metadata.items():
if value in ['', 'None']: if value in ['', 'None']:
continue continue
+33 -5
View File
@@ -7,9 +7,9 @@ import math
import os import os
import cv2 import cv2
import json import json
import hashlib
from PIL import Image from PIL import Image
from hashlib import sha256
import modules.sdxl_styles import modules.sdxl_styles
@@ -182,16 +182,44 @@ def get_files_from_folder(folder_path, extensions=None, name_filter=None):
return filenames return filenames
def calculate_sha256(filename, length=HASH_SHA256_LENGTH) -> str: def sha256(filename, use_addnet_hash=False, length=HASH_SHA256_LENGTH):
hash_sha256 = sha256() print(f"Calculating sha256 for {filename}: ", end='')
if use_addnet_hash:
with open(filename, "rb") as file:
sha256_value = addnet_hash_safetensors(file)
else:
sha256_value = calculate_sha256(filename)
print(f"{sha256_value}")
return sha256_value[:length] if length is not None else sha256_value
def addnet_hash_safetensors(b):
"""kohya-ss hash for safetensors from https://github.com/kohya-ss/sd-scripts/blob/main/library/train_util.py"""
hash_sha256 = hashlib.sha256()
blksize = 1024 * 1024
b.seek(0)
header = b.read(8)
n = int.from_bytes(header, "little")
offset = n + 8
b.seek(offset)
for chunk in iter(lambda: b.read(blksize), b""):
hash_sha256.update(chunk)
return hash_sha256.hexdigest()
def calculate_sha256(filename) -> str:
hash_sha256 = hashlib.sha256()
blksize = 1024 * 1024 blksize = 1024 * 1024
with open(filename, "rb") as f: with open(filename, "rb") as f:
for chunk in iter(lambda: f.read(blksize), b""): for chunk in iter(lambda: f.read(blksize), b""):
hash_sha256.update(chunk) hash_sha256.update(chunk)
res = hash_sha256.hexdigest() return hash_sha256.hexdigest()
return res[:length] if length else res
def quote(text): def quote(text):
+1 -1
View File
@@ -34,7 +34,7 @@
"default_sampler": "dpmpp_2m_sde_gpu", "default_sampler": "dpmpp_2m_sde_gpu",
"default_scheduler": "karras", "default_scheduler": "karras",
"default_performance": "Speed", "default_performance": "Speed",
"default_prompt": "1girl, ", "default_prompt": "",
"default_prompt_negative": "", "default_prompt_negative": "",
"default_styles": [ "default_styles": [
"Fooocus V2", "Fooocus V2",
+7
View File
@@ -1,3 +1,10 @@
# [2.3.1](https://github.com/lllyasviel/Fooocus/releases/tag/2.3.1)
* Remove positive prompt from anime prefix to not reset prompt after switching presets
* Fix image number being reset to 1 when switching preset, now doesn't reset anymore
* Fix outpainting dimension calculation when extending left/right
* Fix LoRA compatibility for LoRAs in a1111 metadata scheme
# [2.3.0](https://github.com/lllyasviel/Fooocus/releases/tag/2.3.0) # [2.3.0](https://github.com/lllyasviel/Fooocus/releases/tag/2.3.0)
* Add performance "lightning" (based on [SDXL-Lightning 4 step LoRA](https://huggingface.co/ByteDance/SDXL-Lightning/blob/main/sdxl_lightning_4step_lora.safetensors)) * Add performance "lightning" (based on [SDXL-Lightning 4 step LoRA](https://huggingface.co/ByteDance/SDXL-Lightning/blob/main/sdxl_lightning_4step_lora.safetensors))