Merge branch 'lllyasviel:main' into main
This commit is contained in:
commit
cf99255d2d
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -9,10 +9,10 @@ assignees: ''
|
|||||||
|
|
||||||
**Read Troubleshoot**
|
**Read Troubleshoot**
|
||||||
|
|
||||||
[x] I admit that I have read the [Troubleshoot](https://github.com/lllyasviel/Fooocus/blob/main/troubleshoot.md) before making this issue.
|
[x] I confirm that I have read the [Troubleshoot](https://github.com/lllyasviel/Fooocus/blob/main/troubleshoot.md) guide before making this issue.
|
||||||
|
|
||||||
**Describe the problem**
|
**Describe the problem**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**Full Console Log**
|
**Full Console Log**
|
||||||
Paste **full** console log here. You will make our job easier if you give a **full** log.
|
Paste the **full** console log here. You will make our job easier if you give a **full** log.
|
||||||
|
@ -1 +1 @@
|
|||||||
version = '2.1.864'
|
version = '2.1.865'
|
||||||
|
@ -154,12 +154,8 @@ let cancelGenerateForever = function() {
|
|||||||
let generateOnRepeatForButtons = function() {
|
let generateOnRepeatForButtons = function() {
|
||||||
generateOnRepeat('#generate_button', '#stop_button');
|
generateOnRepeat('#generate_button', '#stop_button');
|
||||||
};
|
};
|
||||||
|
|
||||||
appendContextMenuOption('#generate_button', 'Generate forever', generateOnRepeatForButtons);
|
appendContextMenuOption('#generate_button', 'Generate forever', generateOnRepeatForButtons);
|
||||||
// appendContextMenuOption('#stop_button', 'Generate forever', generateOnRepeatForButtons);
|
|
||||||
|
|
||||||
// appendContextMenuOption('#stop_button', 'Cancel generate forever', cancelGenerateForever);
|
|
||||||
// appendContextMenuOption('#generate_button', 'Cancel generate forever', cancelGenerateForever);
|
|
||||||
})();
|
})();
|
||||||
//End example Context Menu Items
|
//End example Context Menu Items
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ os.chdir(root)
|
|||||||
|
|
||||||
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
|
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
|
||||||
os.environ["PYTORCH_MPS_HIGH_WATERMARK_RATIO"] = "0.0"
|
os.environ["PYTORCH_MPS_HIGH_WATERMARK_RATIO"] = "0.0"
|
||||||
|
if "GRADIO_SERVER_PORT" not in os.environ:
|
||||||
os.environ["GRADIO_SERVER_PORT"] = "7865"
|
os.environ["GRADIO_SERVER_PORT"] = "7865"
|
||||||
|
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
@ -21,7 +22,6 @@ import fooocus_version
|
|||||||
from build_launcher import build_launcher
|
from build_launcher import build_launcher
|
||||||
from modules.launch_util import is_installed, run, python, run_pip, requirements_met
|
from modules.launch_util import is_installed, run, python, run_pip, requirements_met
|
||||||
from modules.model_loader import load_file_from_url
|
from modules.model_loader import load_file_from_url
|
||||||
from modules import config
|
|
||||||
|
|
||||||
|
|
||||||
REINSTALL_ALL = False
|
REINSTALL_ALL = False
|
||||||
@ -84,6 +84,8 @@ if args.gpu_device_id is not None:
|
|||||||
print("Set device to:", args.gpu_device_id)
|
print("Set device to:", args.gpu_device_id)
|
||||||
|
|
||||||
|
|
||||||
|
from modules import config
|
||||||
|
|
||||||
def download_models():
|
def download_models():
|
||||||
for file_name, url in vae_approx_filenames:
|
for file_name, url in vae_approx_filenames:
|
||||||
load_file_from_url(url=url, model_dir=config.path_vae_approx, file_name=file_name)
|
load_file_from_url(url=url, model_dir=config.path_vae_approx, file_name=file_name)
|
||||||
|
@ -78,7 +78,7 @@ def spatial_gradient(input, normalized: bool = True):
|
|||||||
Return:
|
Return:
|
||||||
the derivatives of the input feature map. with shape :math:`(B, C, 2, H, W)`.
|
the derivatives of the input feature map. with shape :math:`(B, C, 2, H, W)`.
|
||||||
.. note::
|
.. note::
|
||||||
See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/
|
See a working example `here <https://kornia.readthedocs.io/en/latest/
|
||||||
filtering_edges.html>`__.
|
filtering_edges.html>`__.
|
||||||
Examples:
|
Examples:
|
||||||
>>> input = torch.rand(1, 3, 4, 4)
|
>>> input = torch.rand(1, 3, 4, 4)
|
||||||
@ -120,7 +120,7 @@ def rgb_to_grayscale(image, rgb_weights = None):
|
|||||||
grayscale version of the image with shape :math:`(*,1,H,W)`.
|
grayscale version of the image with shape :math:`(*,1,H,W)`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/
|
See a working example `here <https://kornia.readthedocs.io/en/latest/
|
||||||
color_conversions.html>`__.
|
color_conversions.html>`__.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -176,7 +176,7 @@ def canny(
|
|||||||
- the canny edge magnitudes map, shape of :math:`(B,1,H,W)`.
|
- the canny edge magnitudes map, shape of :math:`(B,1,H,W)`.
|
||||||
- the canny edge detection filtered by thresholds and hysteresis, shape of :math:`(B,1,H,W)`.
|
- the canny edge detection filtered by thresholds and hysteresis, shape of :math:`(B,1,H,W)`.
|
||||||
.. note::
|
.. note::
|
||||||
See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/
|
See a working example `here <https://kornia.readthedocs.io/en/latest/
|
||||||
canny.html>`__.
|
canny.html>`__.
|
||||||
Example:
|
Example:
|
||||||
>>> input = torch.rand(5, 3, 4, 4)
|
>>> input = torch.rand(5, 3, 4, 4)
|
||||||
|
@ -3,8 +3,6 @@ import math
|
|||||||
import ldm_patched.modules.utils
|
import ldm_patched.modules.utils
|
||||||
|
|
||||||
|
|
||||||
def lcm(a, b): #TODO: eventually replace by math.lcm (added in python3.9)
|
|
||||||
return abs(a*b) // math.gcd(a, b)
|
|
||||||
|
|
||||||
class CONDRegular:
|
class CONDRegular:
|
||||||
def __init__(self, cond):
|
def __init__(self, cond):
|
||||||
@ -41,7 +39,7 @@ class CONDCrossAttn(CONDRegular):
|
|||||||
if s1[0] != s2[0] or s1[2] != s2[2]: #these 2 cases should not happen
|
if s1[0] != s2[0] or s1[2] != s2[2]: #these 2 cases should not happen
|
||||||
return False
|
return False
|
||||||
|
|
||||||
mult_min = lcm(s1[1], s2[1])
|
mult_min = math.lcm(s1[1], s2[1])
|
||||||
diff = mult_min // min(s1[1], s2[1])
|
diff = mult_min // min(s1[1], s2[1])
|
||||||
if diff > 4: #arbitrary limit on the padding because it's probably going to impact performance negatively if it's too much
|
if diff > 4: #arbitrary limit on the padding because it's probably going to impact performance negatively if it's too much
|
||||||
return False
|
return False
|
||||||
@ -52,7 +50,7 @@ class CONDCrossAttn(CONDRegular):
|
|||||||
crossattn_max_len = self.cond.shape[1]
|
crossattn_max_len = self.cond.shape[1]
|
||||||
for x in others:
|
for x in others:
|
||||||
c = x.cond
|
c = x.cond
|
||||||
crossattn_max_len = lcm(crossattn_max_len, c.shape[1])
|
crossattn_max_len = math.lcm(crossattn_max_len, c.shape[1])
|
||||||
conds.append(c)
|
conds.append(c)
|
||||||
|
|
||||||
out = []
|
out = []
|
||||||
|
@ -14,7 +14,7 @@ from .timm.weight_init import trunc_normal_
|
|||||||
|
|
||||||
def drop_path(x, drop_prob: float = 0.0, training: bool = False):
|
def drop_path(x, drop_prob: float = 0.0, training: bool = False):
|
||||||
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
||||||
From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py
|
From: https://github.com/huggingface/pytorch-image-models/blob/main/timm/layers/drop.py
|
||||||
"""
|
"""
|
||||||
if drop_prob == 0.0 or not training:
|
if drop_prob == 0.0 or not training:
|
||||||
return x
|
return x
|
||||||
@ -30,7 +30,7 @@ def drop_path(x, drop_prob: float = 0.0, training: bool = False):
|
|||||||
|
|
||||||
class DropPath(nn.Module):
|
class DropPath(nn.Module):
|
||||||
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
||||||
From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py
|
From: https://github.com/huggingface/pytorch-image-models/blob/main/timm/layers/drop.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, drop_prob=None):
|
def __init__(self, drop_prob=None):
|
||||||
|
@ -13,7 +13,7 @@ import torch.nn.functional as F
|
|||||||
from . import block as B
|
from . import block as B
|
||||||
|
|
||||||
|
|
||||||
# Borrowed from https://github.com/rlaphoenix/VSGAN/blob/master/vsgan/archs/ESRGAN.py
|
# Borrowed from https://github.com/rlaphoenix/VSGAN/blob/master/vsgan/archs/esrgan.py
|
||||||
# Which enhanced stuff that was already here
|
# Which enhanced stuff that was already here
|
||||||
class RRDBNet(nn.Module):
|
class RRDBNet(nn.Module):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Modified from https://github.com/sczhou/CodeFormer
|
Modified from https://github.com/sczhou/CodeFormer
|
||||||
VQGAN code, adapted from the original created by the Unleashing Transformers authors:
|
VQGAN code, adapted from the original created by the Unleashing Transformers authors:
|
||||||
https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py
|
https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py
|
||||||
This verison of the arch specifically was gathered from an old version of GFPGAN. If this is a problem, please contact me.
|
This version of the arch specifically was gathered from an old version of GFPGAN. If this is a problem, please contact me.
|
||||||
"""
|
"""
|
||||||
import math
|
import math
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -40,7 +40,7 @@ def worker():
|
|||||||
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, \
|
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
|
get_image_shape_ceil, set_image_shape_ceil, get_shape_ceil, resample_image, erode_or_dilate, ordinal_suffix
|
||||||
from modules.upscaler import perform_upscale
|
from modules.upscaler import perform_upscale
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -335,11 +335,11 @@ def worker():
|
|||||||
ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path)
|
ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path)
|
||||||
ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_face_path)
|
ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_face_path)
|
||||||
|
|
||||||
switch = int(round(steps * refiner_switch))
|
|
||||||
|
|
||||||
if advanced_parameters.overwrite_step > 0:
|
if advanced_parameters.overwrite_step > 0:
|
||||||
steps = advanced_parameters.overwrite_step
|
steps = advanced_parameters.overwrite_step
|
||||||
|
|
||||||
|
switch = int(round(steps * refiner_switch))
|
||||||
|
|
||||||
if advanced_parameters.overwrite_switch > 0:
|
if advanced_parameters.overwrite_switch > 0:
|
||||||
switch = advanced_parameters.overwrite_switch
|
switch = advanced_parameters.overwrite_switch
|
||||||
|
|
||||||
@ -732,8 +732,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}-th 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()
|
||||||
|
@ -102,6 +102,18 @@ if isinstance(preset, str):
|
|||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
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/')
|
||||||
|
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):
|
def get_dir_or_set_default(key, default_value):
|
||||||
global config_dict, visited_keys, always_save_keys
|
global config_dict, visited_keys, always_save_keys
|
||||||
|
|
||||||
@ -132,7 +144,7 @@ path_inpaint = get_dir_or_set_default('path_inpaint', '../models/inpaint/')
|
|||||||
path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlnet/')
|
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_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_outputs = get_dir_or_set_default('path_outputs', '../outputs/')
|
path_outputs = get_path_output()
|
||||||
|
|
||||||
|
|
||||||
def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False):
|
def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False):
|
||||||
|
@ -15,7 +15,7 @@ from packaging.requirements import Requirement
|
|||||||
logging.getLogger("torch.distributed.nn").setLevel(logging.ERROR) # sshh...
|
logging.getLogger("torch.distributed.nn").setLevel(logging.ERROR) # sshh...
|
||||||
logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage())
|
logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage())
|
||||||
|
|
||||||
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
|
re_requirement = re.compile(r"\s*([-\w]+)\s*(?:==\s*([-+.\w]+))?\s*")
|
||||||
|
|
||||||
python = sys.executable
|
python = sys.executable
|
||||||
default_command_live = (os.environ.get('LAUNCH_LIVE_OUTPUT') == "1")
|
default_command_live = (os.environ.get('LAUNCH_LIVE_OUTPUT') == "1")
|
||||||
|
@ -68,7 +68,7 @@ def log(img, dic):
|
|||||||
</script>"""
|
</script>"""
|
||||||
)
|
)
|
||||||
|
|
||||||
begin_part = f"<html><head><title>Fooocus Log {date_string}</title>{css_styles}</head><body>{js}<p>Fooocus Log {date_string} (private)</p>\n<p>All images are clean, without any hidden data/meta, and safe to share with others.</p><!--fooocus-log-split-->\n\n"
|
begin_part = f"<!DOCTYPE html><html><head><title>Fooocus Log {date_string}</title>{css_styles}</head><body>{js}<p>Fooocus Log {date_string} (private)</p>\n<p>All images are clean, without any hidden data/meta, and safe to share with others.</p><!--fooocus-log-split-->\n\n"
|
||||||
end_part = f'\n<!--fooocus-log-split--></body></html>'
|
end_part = f'\n<!--fooocus-log-split--></body></html>'
|
||||||
|
|
||||||
middle_part = log_cache.get(html_name, "")
|
middle_part = log_cache.get(html_name, "")
|
||||||
@ -83,15 +83,15 @@ def log(img, dic):
|
|||||||
|
|
||||||
div_name = only_name.replace('.', '_')
|
div_name = only_name.replace('.', '_')
|
||||||
item = f"<div id=\"{div_name}\" class=\"image-container\"><hr><table><tr>\n"
|
item = f"<div id=\"{div_name}\" class=\"image-container\"><hr><table><tr>\n"
|
||||||
item += f"<td><a href=\"{only_name}\" target=\"_blank\"><img src='{only_name}' onerror=\"this.closest('.image-container').style.display='none';\" loading='lazy'></img></a><div>{only_name}</div></td>"
|
item += f"<td><a href=\"{only_name}\" target=\"_blank\"><img src='{only_name}' onerror=\"this.closest('.image-container').style.display='none';\" loading='lazy'/></a><div>{only_name}</div></td>"
|
||||||
item += "<td><table class='metadata'>"
|
item += "<td><table class='metadata'>"
|
||||||
for key, value in dic:
|
for key, value in dic:
|
||||||
value_txt = str(value).replace('\n', ' </br> ')
|
value_txt = str(value).replace('\n', ' <br/> ')
|
||||||
item += f"<tr><td class='key'>{key}</td><td class='value'>{value_txt}</td></tr>\n"
|
item += f"<tr><td class='key'>{key}</td><td class='value'>{value_txt}</td></tr>\n"
|
||||||
item += "</table>"
|
item += "</table>"
|
||||||
|
|
||||||
js_txt = urllib.parse.quote(json.dumps({k: v for k, v in dic}, indent=0), safe='')
|
js_txt = urllib.parse.quote(json.dumps({k: v for k, v in dic}, 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>"
|
||||||
item += "</tr></table></div>\n\n"
|
item += "</tr></table></div>\n\n"
|
||||||
|
@ -164,14 +164,18 @@ def get_files_from_folder(folder_path, exensions=None, name_filter=None):
|
|||||||
|
|
||||||
filenames = []
|
filenames = []
|
||||||
|
|
||||||
for root, dirs, files in os.walk(folder_path):
|
for root, dirs, files in os.walk(folder_path, topdown=False):
|
||||||
relative_path = os.path.relpath(root, folder_path)
|
relative_path = os.path.relpath(root, folder_path)
|
||||||
if relative_path == ".":
|
if relative_path == ".":
|
||||||
relative_path = ""
|
relative_path = ""
|
||||||
for filename in files:
|
for filename in sorted(files):
|
||||||
_, file_extension = os.path.splitext(filename)
|
_, file_extension = os.path.splitext(filename)
|
||||||
if (exensions == None or file_extension.lower() in exensions) and (name_filter == None or name_filter in _):
|
if (exensions == None or file_extension.lower() in exensions) and (name_filter == None or name_filter in _):
|
||||||
path = os.path.join(relative_path, filename)
|
path = os.path.join(relative_path, filename)
|
||||||
filenames.append(path)
|
filenames.append(path)
|
||||||
|
|
||||||
return sorted(filenames, key=lambda x: -1 if os.sep in x else 1)
|
return filenames
|
||||||
|
|
||||||
|
|
||||||
|
def ordinal_suffix(number: int) -> str:
|
||||||
|
return 'th' if 10 <= number % 100 <= 20 else {1: 'st', 2: 'nd', 3: 'rd'}.get(number % 10, 'th')
|
||||||
|
@ -281,6 +281,13 @@ Given different goals, the default models and configs of Fooocus are different:
|
|||||||
|
|
||||||
Note that the download is **automatic** - you do not need to do anything if the internet connection is okay. However, you can download them manually if you (or move them from somewhere else) have your own preparation.
|
Note that the download is **automatic** - you do not need to do anything if the internet connection is okay. However, you can download them manually if you (or move them from somewhere else) have your own preparation.
|
||||||
|
|
||||||
|
## UI Access and Authentication
|
||||||
|
In addition to running on localhost, Fooocus can also expose its UI in two ways:
|
||||||
|
* Local UI listener: use `--listen` (specify port e.g. with `--port 8888`).
|
||||||
|
* API access: use `--share` (registers an endpoint at `.gradio.live`).
|
||||||
|
|
||||||
|
In both ways the access is unauthenticated by default. You can add basic authentication by creating a file called `auth.json` in the main directory, which contains a list of JSON objects with the keys `user` and `pass` (see example in [auth-example.json](./auth-example.json)).
|
||||||
|
|
||||||
## List of "Hidden" Tricks
|
## List of "Hidden" Tricks
|
||||||
<a name="tech_list"></a>
|
<a name="tech_list"></a>
|
||||||
|
|
||||||
@ -288,7 +295,7 @@ The below things are already inside the software, and **users do not need to do
|
|||||||
|
|
||||||
1. GPT2-based [prompt expansion as a dynamic style "Fooocus V2".](https://github.com/lllyasviel/Fooocus/discussions/117#raw) (similar to Midjourney's hidden pre-processsing and "raw" mode, or the LeonardoAI's Prompt Magic).
|
1. GPT2-based [prompt expansion as a dynamic style "Fooocus V2".](https://github.com/lllyasviel/Fooocus/discussions/117#raw) (similar to Midjourney's hidden pre-processsing and "raw" mode, or the LeonardoAI's Prompt Magic).
|
||||||
2. Native refiner swap inside one single k-sampler. The advantage is that the refiner model can now reuse the base model's momentum (or ODE's history parameters) collected from k-sampling to achieve more coherent sampling. In Automatic1111's high-res fix and ComfyUI's node system, the base model and refiner use two independent k-samplers, which means the momentum is largely wasted, and the sampling continuity is broken. Fooocus uses its own advanced k-diffusion sampling that ensures seamless, native, and continuous swap in a refiner setup. (Update Aug 13: Actually, I discussed this with Automatic1111 several days ago, and it seems that the “native refiner swap inside one single k-sampler” is [merged]( https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12371) into the dev branch of webui. Great!)
|
2. Native refiner swap inside one single k-sampler. The advantage is that the refiner model can now reuse the base model's momentum (or ODE's history parameters) collected from k-sampling to achieve more coherent sampling. In Automatic1111's high-res fix and ComfyUI's node system, the base model and refiner use two independent k-samplers, which means the momentum is largely wasted, and the sampling continuity is broken. Fooocus uses its own advanced k-diffusion sampling that ensures seamless, native, and continuous swap in a refiner setup. (Update Aug 13: Actually, I discussed this with Automatic1111 several days ago, and it seems that the “native refiner swap inside one single k-sampler” is [merged]( https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12371) into the dev branch of webui. Great!)
|
||||||
3. Negative ADM guidance. Because the highest resolution level of XL Base does not have cross attentions, the positive and negative signals for XL's highest resolution level cannot receive enough contrasts during the CFG sampling, causing the results to look a bit plastic or overly smooth in certain cases. Fortunately, since the XL's highest resolution level is still conditioned on image aspect ratios (ADM), we can modify the adm on the positive/negative side to compensate for the lack of CFG contrast in the highest resolution level. (Update Aug 16, the IOS App [Drawing Things](https://apps.apple.com/us/app/draw-things-ai-generation/id6444050820) will support Negative ADM Guidance. Great!)
|
3. Negative ADM guidance. Because the highest resolution level of XL Base does not have cross attentions, the positive and negative signals for XL's highest resolution level cannot receive enough contrasts during the CFG sampling, causing the results to look a bit plastic or overly smooth in certain cases. Fortunately, since the XL's highest resolution level is still conditioned on image aspect ratios (ADM), we can modify the adm on the positive/negative side to compensate for the lack of CFG contrast in the highest resolution level. (Update Aug 16, the IOS App [Draw Things](https://apps.apple.com/us/app/draw-things-ai-generation/id6444050820) will support Negative ADM Guidance. Great!)
|
||||||
4. We implemented a carefully tuned variation of Section 5.1 of ["Improving Sample Quality of Diffusion Models Using Self-Attention Guidance"](https://arxiv.org/pdf/2210.00939.pdf). The weight is set to very low, but this is Fooocus's final guarantee to make sure that the XL will never yield an overly smooth or plastic appearance (examples [here](https://github.com/lllyasviel/Fooocus/discussions/117#sharpness)). This can almost eliminate all cases for which XL still occasionally produces overly smooth results, even with negative ADM guidance. (Update 2023 Aug 18, the Gaussian kernel of SAG is changed to an anisotropic kernel for better structure preservation and fewer artifacts.)
|
4. We implemented a carefully tuned variation of Section 5.1 of ["Improving Sample Quality of Diffusion Models Using Self-Attention Guidance"](https://arxiv.org/pdf/2210.00939.pdf). The weight is set to very low, but this is Fooocus's final guarantee to make sure that the XL will never yield an overly smooth or plastic appearance (examples [here](https://github.com/lllyasviel/Fooocus/discussions/117#sharpness)). This can almost eliminate all cases for which XL still occasionally produces overly smooth results, even with negative ADM guidance. (Update 2023 Aug 18, the Gaussian kernel of SAG is changed to an anisotropic kernel for better structure preservation and fewer artifacts.)
|
||||||
5. We modified the style templates a bit and added the "cinematic-default".
|
5. We modified the style templates a bit and added the "cinematic-default".
|
||||||
6. We tested the "sd_xl_offset_example-lora_1.0.safetensors" and it seems that when the lora weight is below 0.5, the results are always better than XL without lora.
|
6. We tested the "sd_xl_offset_example-lora_1.0.safetensors" and it seems that when the lora weight is below 0.5, the results are always better than XL without lora.
|
||||||
|
13
webui.py
13
webui.py
@ -255,8 +255,14 @@ with shared.gradio_root:
|
|||||||
seed_random.change(random_checked, inputs=[seed_random], outputs=[image_seed],
|
seed_random.change(random_checked, inputs=[seed_random], outputs=[image_seed],
|
||||||
queue=False, show_progress=False)
|
queue=False, show_progress=False)
|
||||||
|
|
||||||
if not args_manager.args.disable_image_log:
|
def update_history_link():
|
||||||
gr.HTML(f'<a href="file={get_current_html_path()}" target="_blank">\U0001F4DA History Log</a>')
|
if args_manager.args.disable_image_log:
|
||||||
|
return gr.update(value='')
|
||||||
|
|
||||||
|
return gr.update(value=f'<a href="file={get_current_html_path()}" target="_blank">\U0001F4DA History Log</a>')
|
||||||
|
|
||||||
|
history_link = gr.HTML()
|
||||||
|
shared.gradio_root.load(update_history_link, outputs=history_link, queue=False, show_progress=False)
|
||||||
|
|
||||||
with gr.Tab(label='Style'):
|
with gr.Tab(label='Style'):
|
||||||
style_sorter.try_load_sorted_styles(
|
style_sorter.try_load_sorted_styles(
|
||||||
@ -586,6 +592,7 @@ with shared.gradio_root:
|
|||||||
.then(fn=generate_clicked, inputs=ctrls, outputs=[progress_html, progress_window, progress_gallery, gallery]) \
|
.then(fn=generate_clicked, inputs=ctrls, outputs=[progress_html, progress_window, progress_gallery, gallery]) \
|
||||||
.then(lambda: (gr.update(visible=True, interactive=True), gr.update(visible=False, interactive=False), gr.update(visible=False, interactive=False), False),
|
.then(lambda: (gr.update(visible=True, interactive=True), gr.update(visible=False, interactive=False), gr.update(visible=False, interactive=False), False),
|
||||||
outputs=[generate_button, stop_button, skip_button, state_is_generating]) \
|
outputs=[generate_button, stop_button, skip_button, state_is_generating]) \
|
||||||
|
.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')
|
||||||
|
|
||||||
for notification_file in ['notification.ogg', 'notification.mp3']:
|
for notification_file in ['notification.ogg', 'notification.mp3']:
|
||||||
@ -618,6 +625,6 @@ shared.gradio_root.launch(
|
|||||||
server_name=args_manager.args.listen,
|
server_name=args_manager.args.listen,
|
||||||
server_port=args_manager.args.port,
|
server_port=args_manager.args.port,
|
||||||
share=args_manager.args.share,
|
share=args_manager.args.share,
|
||||||
auth=check_auth if args_manager.args.share and auth_enabled else None,
|
auth=check_auth if (args_manager.args.share or args_manager.args.listen) and auth_enabled else None,
|
||||||
blocked_paths=[constants.AUTH_FILENAME]
|
blocked_paths=[constants.AUTH_FILENAME]
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user