Skip to content

Commit

Permalink
Merge pull request #2229 from lllyasviel/develop
Browse files Browse the repository at this point in the history
Release 2.1.865
  • Loading branch information
mashb1t committed Feb 11, 2024
2 parents fdc4dc1 + f4a8bf2 commit 1c999be
Show file tree
Hide file tree
Showing 16 changed files with 62 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ assignees: ''

**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**
A clear and concise description of what the bug is.

**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.
2 changes: 1 addition & 1 deletion fooocus_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = '2.1.864'
version = '2.1.865'
4 changes: 0 additions & 4 deletions javascript/contextMenus.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,8 @@ let cancelGenerateForever = function() {
let generateOnRepeatForButtons = function() {
generateOnRepeat('#generate_button', '#stop_button');
};

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

Expand Down
3 changes: 2 additions & 1 deletion launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
os.environ["PYTORCH_MPS_HIGH_WATERMARK_RATIO"] = "0.0"
os.environ["GRADIO_SERVER_PORT"] = "7865"
if "GRADIO_SERVER_PORT" not in os.environ:
os.environ["GRADIO_SERVER_PORT"] = "7865"

ssl._create_default_https_context = ssl._create_unverified_context

Expand Down
6 changes: 3 additions & 3 deletions ldm_patched/contrib/external_canny.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def spatial_gradient(input, normalized: bool = True):
Return:
the derivatives of the input feature map. with shape :math:`(B, C, 2, H, W)`.
.. 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>`__.
Examples:
>>> input = torch.rand(1, 3, 4, 4)
Expand Down Expand Up @@ -120,7 +120,7 @@ def rgb_to_grayscale(image, rgb_weights = None):
grayscale version of the image with shape :math:`(*,1,H,W)`.
.. 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>`__.
Example:
Expand Down Expand Up @@ -176,7 +176,7 @@ def canny(
- 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)`.
.. 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>`__.
Example:
>>> input = torch.rand(5, 3, 4, 4)
Expand Down
6 changes: 2 additions & 4 deletions ldm_patched/modules/conds.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
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:
def __init__(self, cond):
Expand Down Expand Up @@ -41,7 +39,7 @@ def can_concat(self, other):
if s1[0] != s2[0] or s1[2] != s2[2]: #these 2 cases should not happen
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])
if diff > 4: #arbitrary limit on the padding because it's probably going to impact performance negatively if it's too much
return False
Expand All @@ -52,7 +50,7 @@ def concat(self, others):
crossattn_max_len = self.cond.shape[1]
for x in others:
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)

out = []
Expand Down
4 changes: 2 additions & 2 deletions ldm_patched/pfn/architecture/HAT.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

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).
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:
return x
Expand All @@ -30,7 +30,7 @@ def drop_path(x, drop_prob: float = 0.0, training: bool = False):

class DropPath(nn.Module):
"""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):
Expand Down
2 changes: 1 addition & 1 deletion ldm_patched/pfn/architecture/RRDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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
class RRDBNet(nn.Module):
def __init__(
Expand Down
2 changes: 1 addition & 1 deletion ldm_patched/pfn/architecture/face/codeformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Modified from https://github.com/sczhou/CodeFormer
VQGAN code, adapted from the original created by the Unleashing Transformers authors:
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
from typing import Optional
Expand Down
9 changes: 4 additions & 5 deletions modules/async_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def worker():
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
get_image_shape_ceil, set_image_shape_ceil, get_shape_ceil, resample_image, erode_or_dilate, ordinal_suffix
from modules.upscaler import perform_upscale

try:
Expand Down Expand Up @@ -335,11 +335,11 @@ def handler(async_task):
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)

switch = int(round(steps * refiner_switch))

if advanced_parameters.overwrite_step > 0:
steps = advanced_parameters.overwrite_step

switch = int(round(steps * refiner_switch))

if advanced_parameters.overwrite_switch > 0:
switch = advanced_parameters.overwrite_switch

Expand Down Expand Up @@ -732,8 +732,7 @@ def callback(step, x0, x, total_steps, y):
done_steps = current_task_id * steps + step
async_task.yields.append(['preview', (
int(15.0 + 85.0 * float(done_steps) / float(all_steps)),
f'Step {step}/{total_steps} in the {current_task_id + 1}-th Sampling',
y)])
f'Step {step}/{total_steps} in the {current_task_id + 1}{ordinal_suffix(current_task_id + 1)} Sampling', y)])

for current_task_id, task in enumerate(tasks):
execution_start_time = time.perf_counter()
Expand Down
14 changes: 13 additions & 1 deletion modules/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ def replace_config(old_key, new_key):
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):
global config_dict, visited_keys, always_save_keys

Expand Down Expand Up @@ -132,7 +144,7 @@ def get_dir_or_set_default(key, default_value):
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/')
path_outputs = get_path_output()


def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False):
Expand Down
2 changes: 1 addition & 1 deletion modules/launch_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
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())

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
default_command_live = (os.environ.get('LAUNCH_LIVE_OUTPUT') == "1")
Expand Down
8 changes: 4 additions & 4 deletions modules/private_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def log(img, dic):
</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>'

middle_part = log_cache.get(html_name, "")
Expand All @@ -83,15 +83,15 @@ def log(img, dic):

div_name = only_name.replace('.', '_')
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'>"
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 += "</table>"

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 += "</tr></table></div>\n\n"
Expand Down
10 changes: 7 additions & 3 deletions modules/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,18 @@ def get_files_from_folder(folder_path, exensions=None, name_filter=None):

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)
if relative_path == ".":
relative_path = ""
for filename in files:
for filename in sorted(files):
_, file_extension = os.path.splitext(filename)
if (exensions == None or file_extension.lower() in exensions) and (name_filter == None or name_filter in _):
path = os.path.join(relative_path, filename)
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')
11 changes: 9 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ AMD is not intensively tested, however. The AMD support is in beta.

Use `python entry_with_update.py --preset anime` or `python entry_with_update.py --preset realistic` for Fooocus Anime/Realistic Edition.

### Windows(AMD GPUs)
### Windows (AMD GPUs)

Note that the [minimal requirement](#minimal-requirement) for different platforms is different.

Expand Down Expand Up @@ -281,14 +281,21 @@ 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.

## 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
<a name="tech_list"></a>

The below things are already inside the software, and **users do not need to do anything about these**.

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!)
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.)
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.
Expand Down
13 changes: 10 additions & 3 deletions webui.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,14 @@ def refresh_seed(r, seed_string):
seed_random.change(random_checked, inputs=[seed_random], outputs=[image_seed],
queue=False, show_progress=False)

if not args_manager.args.disable_image_log:
gr.HTML(f'<a href="file={get_current_html_path()}" target="_blank">\U0001F4DA History Log</a>')
def update_history_link():
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'):
style_sorter.try_load_sorted_styles(
Expand Down Expand Up @@ -586,6 +592,7 @@ def parse_meta(raw_prompt_txt, is_generating):
.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),
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')

for notification_file in ['notification.ogg', 'notification.mp3']:
Expand Down Expand Up @@ -618,6 +625,6 @@ def dump_default_english_config():
server_name=args_manager.args.listen,
server_port=args_manager.args.port,
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]
)

1 comment on commit 1c999be

@koruptioner
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! This is a translation of the Fooocus interface into Russian, if you still need it. Thank you for Fooocus !!!
ru.json

Please sign in to comment.