Compare commits

..

No commits in common. "main" and "v2.5.2" have entirely different histories.
main ... v2.5.2

25 changed files with 85 additions and 145 deletions

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @lllyasviel * @mashb1t

View File

@ -17,7 +17,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@v3 uses: docker/login-action@v3

View File

@ -216,9 +216,9 @@ def is_url(url_or_filename):
def load_checkpoint(model,url_or_filename): def load_checkpoint(model,url_or_filename):
if is_url(url_or_filename): if is_url(url_or_filename):
cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True)
checkpoint = torch.load(cached_file, map_location='cpu', weights_only=True) checkpoint = torch.load(cached_file, map_location='cpu')
elif os.path.isfile(url_or_filename): elif os.path.isfile(url_or_filename):
checkpoint = torch.load(url_or_filename, map_location='cpu', weights_only=True) checkpoint = torch.load(url_or_filename, map_location='cpu')
else: else:
raise RuntimeError('checkpoint url or path is invalid') raise RuntimeError('checkpoint url or path is invalid')

View File

@ -78,9 +78,9 @@ def blip_nlvr(pretrained='',**kwargs):
def load_checkpoint(model,url_or_filename): def load_checkpoint(model,url_or_filename):
if is_url(url_or_filename): if is_url(url_or_filename):
cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True)
checkpoint = torch.load(cached_file, map_location='cpu', weights_only=True) checkpoint = torch.load(cached_file, map_location='cpu')
elif os.path.isfile(url_or_filename): elif os.path.isfile(url_or_filename):
checkpoint = torch.load(url_or_filename, map_location='cpu', weights_only=True) checkpoint = torch.load(url_or_filename, map_location='cpu')
else: else:
raise RuntimeError('checkpoint url or path is invalid') raise RuntimeError('checkpoint url or path is invalid')
state_dict = checkpoint['model'] state_dict = checkpoint['model']

View File

@ -19,7 +19,7 @@ def init_detection_model(model_name, half=False, device='cuda', model_rootpath=N
url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath) url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath)
# TODO: clean pretrained model # TODO: clean pretrained model
load_net = torch.load(model_path, map_location=lambda storage, loc: storage, weights_only=True) load_net = torch.load(model_path, map_location=lambda storage, loc: storage)
# remove unnecessary 'module.' # remove unnecessary 'module.'
for k, v in deepcopy(load_net).items(): for k, v in deepcopy(load_net).items():
if k.startswith('module.'): if k.startswith('module.'):

View File

@ -17,7 +17,7 @@ def init_parsing_model(model_name='bisenet', half=False, device='cuda', model_ro
model_path = load_file_from_url( model_path = load_file_from_url(
url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath) url=model_url, model_dir='facexlib/weights', progress=True, file_name=None, save_dir=model_rootpath)
load_net = torch.load(model_path, map_location=lambda storage, loc: storage, weights_only=True) load_net = torch.load(model_path, map_location=lambda storage, loc: storage)
model.load_state_dict(load_net, strict=True) model.load_state_dict(load_net, strict=True)
model.eval() model.eval()
model = model.to(device) model = model.to(device)

View File

@ -104,7 +104,7 @@ def load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path):
offload_device = torch.device('cpu') offload_device = torch.device('cpu')
use_fp16 = model_management.should_use_fp16(device=load_device) use_fp16 = model_management.should_use_fp16(device=load_device)
ip_state_dict = torch.load(ip_adapter_path, map_location="cpu", weights_only=True) ip_state_dict = torch.load(ip_adapter_path, map_location="cpu")
plus = "latents" in ip_state_dict["image_proj"] plus = "latents" in ip_state_dict["image_proj"]
cross_attention_dim = ip_state_dict["ip_adapter"]["1.to_k_ip.weight"].shape[1] cross_attention_dim = ip_state_dict["ip_adapter"]["1.to_k_ip.weight"].shape[1]
sdxl = cross_attention_dim == 2048 sdxl = cross_attention_dim == 2048

View File

@ -1 +1 @@
version = '2.5.5' version = '2.5.2'

View File

@ -17,7 +17,6 @@
"Content Type": "Content Type", "Content Type": "Content Type",
"Photograph": "Photograph", "Photograph": "Photograph",
"Art/Anime": "Art/Anime", "Art/Anime": "Art/Anime",
"Apply Styles": "Apply Styles",
"Describe this Image into Prompt": "Describe this Image into Prompt", "Describe this Image into Prompt": "Describe this Image into Prompt",
"Image Size and Recommended Size": "Image Size and Recommended Size", "Image Size and Recommended Size": "Image Size and Recommended Size",
"Upscale or Variation:": "Upscale or Variation:", "Upscale or Variation:": "Upscale or Variation:",

View File

@ -80,13 +80,12 @@ if args.gpu_device_id is not None:
os.environ['CUDA_VISIBLE_DEVICES'] = str(args.gpu_device_id) os.environ['CUDA_VISIBLE_DEVICES'] = str(args.gpu_device_id)
print("Set device to:", args.gpu_device_id) print("Set device to:", args.gpu_device_id)
if args.hf_mirror is not None: if args.hf_mirror is not None :
os.environ['HF_MIRROR'] = str(args.hf_mirror) os.environ['HF_MIRROR'] = str(args.hf_mirror)
print("Set hf_mirror to:", args.hf_mirror) print("Set hf_mirror to:", args.hf_mirror)
from modules import config from modules import config
from modules.hash_cache import init_cache from modules.hash_cache import init_cache
os.environ["U2NET_HOME"] = config.path_inpaint os.environ["U2NET_HOME"] = config.path_inpaint
os.environ['GRADIO_TEMP_DIR'] = config.temp_path os.environ['GRADIO_TEMP_DIR'] = config.temp_path
@ -101,8 +100,6 @@ if config.temp_path_cleanup_on_launch:
def download_models(default_model, previous_default_models, checkpoint_downloads, embeddings_downloads, lora_downloads, vae_downloads): def download_models(default_model, previous_default_models, checkpoint_downloads, embeddings_downloads, lora_downloads, vae_downloads):
from modules.util import get_file_from_folder_list
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)
@ -117,9 +114,9 @@ def download_models(default_model, previous_default_models, checkpoint_downloads
return default_model, checkpoint_downloads return default_model, checkpoint_downloads
if not args.always_download_new_model: if not args.always_download_new_model:
if not os.path.isfile(get_file_from_folder_list(default_model, config.paths_checkpoints)): if not os.path.exists(os.path.join(config.paths_checkpoints[0], default_model)):
for alternative_model_name in previous_default_models: for alternative_model_name in previous_default_models:
if os.path.isfile(get_file_from_folder_list(alternative_model_name, config.paths_checkpoints)): if os.path.exists(os.path.join(config.paths_checkpoints[0], alternative_model_name)):
print(f'You do not have [{default_model}] but you have [{alternative_model_name}].') print(f'You do not have [{default_model}] but you have [{alternative_model_name}].')
print(f'Fooocus will use [{alternative_model_name}] to avoid downloading new models, ' print(f'Fooocus will use [{alternative_model_name}] to avoid downloading new models, '
f'but you are not using the latest models.') f'but you are not using the latest models.')
@ -129,13 +126,11 @@ def download_models(default_model, previous_default_models, checkpoint_downloads
break break
for file_name, url in checkpoint_downloads.items(): for file_name, url in checkpoint_downloads.items():
model_dir = os.path.dirname(get_file_from_folder_list(file_name, config.paths_checkpoints)) load_file_from_url(url=url, model_dir=config.paths_checkpoints[0], file_name=file_name)
load_file_from_url(url=url, model_dir=model_dir, file_name=file_name)
for file_name, url in embeddings_downloads.items(): for file_name, url in embeddings_downloads.items():
load_file_from_url(url=url, model_dir=config.path_embeddings, file_name=file_name) load_file_from_url(url=url, model_dir=config.path_embeddings, file_name=file_name)
for file_name, url in lora_downloads.items(): for file_name, url in lora_downloads.items():
model_dir = os.path.dirname(get_file_from_folder_list(file_name, config.paths_loras)) load_file_from_url(url=url, model_dir=config.paths_loras[0], file_name=file_name)
load_file_from_url(url=url, model_dir=model_dir, file_name=file_name)
for file_name, url in vae_downloads.items(): for file_name, url in vae_downloads.items():
load_file_from_url(url=url, model_dir=config.path_vae, file_name=file_name) load_file_from_url(url=url, model_dir=config.path_vae, file_name=file_name)

View File

@ -8,7 +8,7 @@ class CLIPEmbeddingNoiseAugmentation(ImageConcatWithNoiseAugmentation):
if clip_stats_path is None: if clip_stats_path is None:
clip_mean, clip_std = torch.zeros(timestep_dim), torch.ones(timestep_dim) clip_mean, clip_std = torch.zeros(timestep_dim), torch.ones(timestep_dim)
else: else:
clip_mean, clip_std = torch.load(clip_stats_path, map_location="cpu", weights_only=True) clip_mean, clip_std = torch.load(clip_stats_path, map_location="cpu")
self.register_buffer("data_mean", clip_mean[None, :], persistent=False) self.register_buffer("data_mean", clip_mean[None, :], persistent=False)
self.register_buffer("data_std", clip_std[None, :], persistent=False) self.register_buffer("data_std", clip_std[None, :], persistent=False)
self.time_embed = Timestep(timestep_dim) self.time_embed = Timestep(timestep_dim)

View File

@ -326,7 +326,7 @@ def load_embed(embedding_name, embedding_directory, embedding_size, embed_key=No
except: except:
embed_out = safe_load_embed_zip(embed_path) embed_out = safe_load_embed_zip(embed_path)
else: else:
embed = torch.load(embed_path, map_location="cpu", weights_only=True) embed = torch.load(embed_path, map_location="cpu")
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
print() print()

View File

@ -377,15 +377,15 @@ class VQAutoEncoder(nn.Module):
) )
if model_path is not None: if model_path is not None:
chkpt = torch.load(model_path, map_location="cpu", weights_only=True) chkpt = torch.load(model_path, map_location="cpu")
if "params_ema" in chkpt: if "params_ema" in chkpt:
self.load_state_dict( self.load_state_dict(
torch.load(model_path, map_location="cpu", weights_only=True)["params_ema"] torch.load(model_path, map_location="cpu")["params_ema"]
) )
logger.info(f"vqgan is loaded from: {model_path} [params_ema]") logger.info(f"vqgan is loaded from: {model_path} [params_ema]")
elif "params" in chkpt: elif "params" in chkpt:
self.load_state_dict( self.load_state_dict(
torch.load(model_path, map_location="cpu", weights_only=True)["params"] torch.load(model_path, map_location="cpu")["params"]
) )
logger.info(f"vqgan is loaded from: {model_path} [params]") logger.info(f"vqgan is loaded from: {model_path} [params]")
else: else:

View File

@ -273,8 +273,8 @@ class GFPGANBilinear(nn.Module):
if decoder_load_path: if decoder_load_path:
self.stylegan_decoder.load_state_dict( self.stylegan_decoder.load_state_dict(
torch.load( torch.load(
decoder_load_path, map_location=lambda storage, loc: storage, decoder_load_path, map_location=lambda storage, loc: storage
weights_only=True)["params_ema"] )["params_ema"]
) )
# fix decoder without updating params # fix decoder without updating params
if fix_decoder: if fix_decoder:

View File

@ -373,8 +373,8 @@ class GFPGANv1(nn.Module):
if decoder_load_path: if decoder_load_path:
self.stylegan_decoder.load_state_dict( self.stylegan_decoder.load_state_dict(
torch.load( torch.load(
decoder_load_path, map_location=lambda storage, loc: storage, decoder_load_path, map_location=lambda storage, loc: storage
weights_only=True)["params_ema"] )["params_ema"]
) )
# fix decoder without updating params # fix decoder without updating params
if fix_decoder: if fix_decoder:

View File

@ -284,8 +284,8 @@ class GFPGANv1Clean(nn.Module):
if decoder_load_path: if decoder_load_path:
self.stylegan_decoder.load_state_dict( self.stylegan_decoder.load_state_dict(
torch.load( torch.load(
decoder_load_path, map_location=lambda storage, loc: storage, decoder_load_path, map_location=lambda storage, loc: storage
weights_only=True)["params_ema"] )["params_ema"]
) )
# fix decoder without updating params # fix decoder without updating params
if fix_decoder: if fix_decoder:

View File

@ -1223,8 +1223,6 @@ def worker():
height, width, _ = async_task.enhance_input_image.shape height, width, _ = async_task.enhance_input_image.shape
# input image already provided, processing is skipped # input image already provided, processing is skipped
steps = 0 steps = 0
yield_result(async_task, async_task.enhance_input_image, current_progress, async_task.black_out_nsfw, False,
async_task.disable_intermediate_results)
all_steps = steps * async_task.image_number all_steps = steps * async_task.image_number

View File

@ -520,14 +520,10 @@ for image_count in range(default_controlnet_image_count):
image_count += 1 image_count += 1
default_ip_images[image_count] = get_config_item_or_set_default( default_ip_images[image_count] = get_config_item_or_set_default(
key=f'default_ip_image_{image_count}', key=f'default_ip_image_{image_count}',
default_value='None', default_value=None,
validator=lambda x: x == 'None' or isinstance(x, str) and os.path.exists(x), validator=lambda x: x is None or isinstance(x, str) and os.path.exists(x),
expected_type=str expected_type=str
) )
if default_ip_images[image_count] == 'None':
default_ip_images[image_count] = None
default_ip_types[image_count] = get_config_item_or_set_default( default_ip_types[image_count] = get_config_item_or_set_default(
key=f'default_ip_type_{image_count}', key=f'default_ip_type_{image_count}',
default_value=modules.flags.default_ip, default_value=modules.flags.default_ip,
@ -702,23 +698,10 @@ default_inpaint_mask_cloth_category = get_config_item_or_set_default(
default_inpaint_mask_sam_model = get_config_item_or_set_default( default_inpaint_mask_sam_model = get_config_item_or_set_default(
key='default_inpaint_mask_sam_model', key='default_inpaint_mask_sam_model',
default_value='vit_b', default_value='vit_b',
validator=lambda x: x in modules.flags.inpaint_mask_sam_model, validator=lambda x: x in [y[1] for y in modules.flags.inpaint_mask_sam_model if y[1] == x],
expected_type=str expected_type=str
) )
default_describe_apply_prompts_checkbox = get_config_item_or_set_default(
key='default_describe_apply_prompts_checkbox',
default_value=True,
validator=lambda x: isinstance(x, bool),
expected_type=bool
)
default_describe_content_type = get_config_item_or_set_default(
key='default_describe_content_type',
default_value=[modules.flags.describe_type_photo],
validator=lambda x: all(k in modules.flags.describe_types for k in x),
expected_type=list
)
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))] 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

View File

@ -231,7 +231,7 @@ def get_previewer(model):
if vae_approx_filename in VAE_approx_models: if vae_approx_filename in VAE_approx_models:
VAE_approx_model = VAE_approx_models[vae_approx_filename] VAE_approx_model = VAE_approx_models[vae_approx_filename]
else: else:
sd = torch.load(vae_approx_filename, map_location='cpu', weights_only=True) sd = torch.load(vae_approx_filename, map_location='cpu')
VAE_approx_model = VAEApprox() VAE_approx_model = VAEApprox()
VAE_approx_model.load_state_dict(sd) VAE_approx_model.load_state_dict(sd)
del sd del sd

View File

@ -96,7 +96,6 @@ inpaint_options = [inpaint_option_default, inpaint_option_detail, inpaint_option
describe_type_photo = 'Photograph' describe_type_photo = 'Photograph'
describe_type_anime = 'Art/Anime' describe_type_anime = 'Art/Anime'
describe_types = [describe_type_photo, describe_type_anime]
sdxl_aspect_ratios = [ sdxl_aspect_ratios = [
'704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152', '704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152',

View File

@ -196,7 +196,7 @@ class InpaintWorker:
if inpaint_head_model is None: if inpaint_head_model is None:
inpaint_head_model = InpaintHead() inpaint_head_model = InpaintHead()
sd = torch.load(inpaint_head_model_path, map_location='cpu', weights_only=True) sd = torch.load(inpaint_head_model_path, map_location='cpu')
inpaint_head_model.load_state_dict(sd) inpaint_head_model.load_state_dict(sd)
feed = torch.cat([ feed = torch.cat([

View File

@ -17,7 +17,7 @@ def perform_upscale(img):
if model is None: if model is None:
model_filename = downloading_upscale_model() model_filename = downloading_upscale_model()
sd = torch.load(model_filename, weights_only=True) sd = torch.load(model_filename)
sdo = OrderedDict() sdo = OrderedDict()
for k, v in sd.items(): for k, v in sd.items():
sdo[k.replace('residual_block_', 'RDB')] = v sdo[k.replace('residual_block_', 'RDB')] = v

View File

@ -1,30 +1,40 @@
<div align=center> <div align=center>
<img src="https://github.com/lllyasviel/Fooocus/assets/19834515/483fb86d-c9a2-4c20-997c-46dafc124f25"> <img src="https://github.com/lllyasviel/Fooocus/assets/19834515/483fb86d-c9a2-4c20-997c-46dafc124f25">
**Non-cherry-picked** random batch by just typing two words "forest elf",
without any parameter tweaking, without any strange prompt tags.
See also **non-cherry-picked** generalization and diversity tests [here](https://github.com/lllyasviel/Fooocus/discussions/2067) and [here](https://github.com/lllyasviel/Fooocus/discussions/808) and [here](https://github.com/lllyasviel/Fooocus/discussions/679) and [here](https://github.com/lllyasviel/Fooocus/discussions/679#realistic).
In the entire open source community, only Fooocus can achieve this level of **non-cherry-picked** quality.
</div> </div>
# Fooocus # Fooocus
[>>> Click Here to Install Fooocus <<<](#download) Fooocus is an image generating software (based on [Gradio](https://www.gradio.app/)).
Fooocus is an image generating software (based on [Gradio](https://www.gradio.app/) <a href='https://github.com/gradio-app/gradio'><img src='https://img.shields.io/github/stars/gradio-app/gradio'></a>). Fooocus is a rethinking of Stable Diffusion and Midjourneys designs:
Fooocus presents a rethinking of image generator designs. The software is offline, open source, and free, while at the same time, similar to many online image generators like Midjourney, the manual tweaking is not needed, and users only need to focus on the prompts and images. Fooocus has also simplified the installation: between pressing "download" and generating the first image, the number of needed mouse clicks is strictly limited to less than 3. Minimal GPU memory requirement is 4GB (Nvidia). * Learned from Stable Diffusion, the software is offline, open source, and free.
* Learned from Midjourney, the manual tweaking is not needed, and users only need to focus on the prompts and images.
Fooocus has included and automated [lots of inner optimizations and quality improvements](#tech_list). Users can forget all those difficult technical parameters, and just enjoy the interaction between human and computer to "explore new mediums of thought and expanding the imaginative powers of the human species" `[1]`.
Fooocus has simplified the installation. Between pressing "download" and generating the first image, the number of needed mouse clicks is strictly limited to less than 3. Minimal GPU memory requirement is 4GB (Nvidia).
`[1]` David Holz, 2019.
**Recently many fake websites exist on Google when you search “fooocus”. Do not trust those here is the only official source of Fooocus.** **Recently many fake websites exist on Google when you search “fooocus”. Do not trust those here is the only official source of Fooocus.**
# Project Status: Limited Long-Term Support (LTS) with Bug Fixes Only ## [Installing Fooocus](#download)
The Fooocus project, built entirely on the **Stable Diffusion XL** architecture, is now in a state of limited long-term support (LTS) with bug fixes only. As the existing functionalities are considered as nearly free of programmartic issues (Thanks to [mashb1t](https://github.com/mashb1t)'s huge efforts), future updates will focus exclusively on addressing any bugs that may arise. # Moving from Midjourney to Fooocus
**There are no current plans to migrate to or incorporate newer model architectures.** However, this may change during time with the development of open-source community. For example, if the community converge to one single dominant method for image generation (which may really happen in half or one years given the current status), Fooocus may also migrate to that exact method. Using Fooocus is as easy as (probably easier than) Midjourney but this does not mean we lack functionality. Below are the details.
For those interested in utilizing newer models such as **Flux**, we recommend exploring alternative platforms such as [WebUI Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) (also from us), [ComfyUI/SwarmUI](https://github.com/comfyanonymous/ComfyUI). Additionally, several [excellent forks of Fooocus](https://github.com/lllyasviel/Fooocus?tab=readme-ov-file#forks) are available for experimentation.
Again, recently many fake websites exist on Google when you search “fooocus”. Do **NOT** get Fooocus from those websites this page is the only official source of Fooocus. We never have any website like such as “fooocus.com”, “fooocus.net”, “fooocus.co”, “fooocus.ai”, “fooocus.org”, “fooocus.pro”, “fooocus.one”. Those websites are ALL FAKE. **They have ABSOLUTLY no relationship to us. Fooocus is a 100% non-commercial offline open-source software.**
# Features
Below is a quick list using Midjourney's examples:
| Midjourney | Fooocus | | Midjourney | Fooocus |
| - | - | | - | - |
@ -45,7 +55,7 @@ Below is a quick list using Midjourney's examples:
| InsightFace | Input Image -> Image Prompt -> Advanced -> FaceSwap | | InsightFace | Input Image -> Image Prompt -> Advanced -> FaceSwap |
| Describe | Input Image -> Describe | | Describe | Input Image -> Describe |
Below is a quick list using LeonardoAI's examples: We also have a few things borrowed from the best parts of LeonardoAI:
| LeonardoAI | Fooocus | | LeonardoAI | Fooocus |
| - | - | | - | - |
@ -53,7 +63,7 @@ Below is a quick list using LeonardoAI's examples:
| Advanced Sampler Parameters (like Contrast/Sharpness/etc) | Advanced -> Advanced -> Sampling Sharpness / etc | | Advanced Sampler Parameters (like Contrast/Sharpness/etc) | Advanced -> Advanced -> Sampling Sharpness / etc |
| User-friendly ControlNets | Input Image -> Image Prompt -> Advanced | | User-friendly ControlNets | Input Image -> Image Prompt -> Advanced |
Also, [click here to browse the advanced features.](https://github.com/lllyasviel/Fooocus/discussions/117) Fooocus also developed many "fooocus-only" features for advanced users to get perfect results. [Click here to browse the advanced features.](https://github.com/lllyasviel/Fooocus/discussions/117)
# Download # Download
@ -109,7 +119,7 @@ See also the common problems and troubleshoots [here](troubleshoot.md).
### Colab ### Colab
(Last tested - 2024 Aug 12 by [mashb1t](https://github.com/mashb1t)) (Last tested - 2024 Mar 18 by [mashb1t](https://github.com/mashb1t))
| Colab | Info | Colab | Info
| --- | --- | | --- | --- |
@ -215,7 +225,7 @@ Then run the `run.bat`.
AMD is not intensively tested, however. The AMD support is in beta. AMD is not intensively tested, however. The AMD support is in beta.
For AMD, use `.\python_embeded\python.exe Fooocus\entry_with_update.py --directml --preset anime` or `.\python_embeded\python.exe Fooocus\entry_with_update.py --directml --preset realistic` for Fooocus Anime/Realistic Edition. For AMD, use `.\python_embeded\python.exe entry_with_update.py --directml --preset anime` or `.\python_embeded\python.exe entry_with_update.py --directml --preset realistic` for Fooocus Anime/Realistic Edition.
### Mac ### Mac
@ -293,8 +303,7 @@ In both ways the access is unauthenticated by default. You can add basic authent
## List of "Hidden" Tricks ## List of "Hidden" Tricks
<a name="tech_list"></a> <a name="tech_list"></a>
<details> The below things are already inside the software, and **users do not need to do anything about these**.
<summary>Click to see a list of tricks. Those are based on SDXL and are not very up-to-date with latest models.</summary>
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-processing 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-processing 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!)
@ -310,7 +319,6 @@ In both ways the access is unauthenticated by default. You can add basic authent
12. Using automatic1111's method to normalize prompt emphasizing. This significantly improves results when users directly copy prompts from civitai. 12. Using automatic1111's method to normalize prompt emphasizing. This significantly improves results when users directly copy prompts from civitai.
13. The joint swap system of the refiner now also supports img2img and upscale in a seamless way. 13. The joint swap system of the refiner now also supports img2img and upscale in a seamless way.
14. CFG Scale and TSNR correction (tuned for SDXL) when CFG is bigger than 10. 14. CFG Scale and TSNR correction (tuned for SDXL) when CFG is bigger than 10.
</details>
## Customization ## Customization
@ -397,7 +405,6 @@ entry_with_update.py [-h] [--listen [IP]] [--port PORT]
## Inline Prompt Features ## Inline Prompt Features
### Wildcards ### Wildcards
Example prompt: `__color__ flower` Example prompt: `__color__ flower`
Processed for positive and negative prompt. Processed for positive and negative prompt.
@ -409,7 +416,6 @@ You can also disable randomness and process a wildcard file from top to bottom b
Wildcards can be nested and combined, and multiple wildcards can be used in the same prompt (example see `wildcards/color_flower.txt`). Wildcards can be nested and combined, and multiple wildcards can be used in the same prompt (example see `wildcards/color_flower.txt`).
### Array Processing ### Array Processing
Example prompt: `[[red, green, blue]] flower` Example prompt: `[[red, green, blue]] flower`
Processed only for positive prompt. Processed only for positive prompt.
@ -428,25 +434,23 @@ Processed only for positive prompt.
Applies a LoRA to the prompt. The LoRA file must be located in the `models/loras` directory. Applies a LoRA to the prompt. The LoRA file must be located in the `models/loras` directory.
## Advanced Features ## Advanced Features
[Click here to browse the advanced features.](https://github.com/lllyasviel/Fooocus/discussions/117) [Click here to browse the advanced features.](https://github.com/lllyasviel/Fooocus/discussions/117)
## Forks ## Forks
Fooocus also has many community forks, just like SD-WebUI's [vladmandic/automatic](https://github.com/vladmandic/automatic) and [anapnoe/stable-diffusion-webui-ux](https://github.com/anapnoe/stable-diffusion-webui-ux), for enthusiastic users who want to try!
Below are some Forks to Fooocus:
| Fooocus' forks | | Fooocus' forks |
| - | | - |
| [fenneishi/Fooocus-Control](https://github.com/fenneishi/Fooocus-Control) </br>[runew0lf/RuinedFooocus](https://github.com/runew0lf/RuinedFooocus) </br> [MoonRide303/Fooocus-MRE](https://github.com/MoonRide303/Fooocus-MRE) </br> [mashb1t/Fooocus](https://github.com/mashb1t/Fooocus) </br> and so on ... | | [fenneishi/Fooocus-Control](https://github.com/fenneishi/Fooocus-Control) </br>[runew0lf/RuinedFooocus](https://github.com/runew0lf/RuinedFooocus) </br> [MoonRide303/Fooocus-MRE](https://github.com/MoonRide303/Fooocus-MRE) </br> [metercai/SimpleSDXL](https://github.com/metercai/SimpleSDXL) </br> [mashb1t/Fooocus](https://github.com/mashb1t/Fooocus) </br> and so on ... |
See also [About Forking and Promotion of Forks](https://github.com/lllyasviel/Fooocus/discussions/699).
## Thanks ## Thanks
Many thanks to [twri](https://github.com/twri) and [3Diva](https://github.com/3Diva) and [Marc K3nt3L](https://github.com/K3nt3L) for creating additional SDXL styles available in Fooocus. Special thanks to [twri](https://github.com/twri) and [3Diva](https://github.com/3Diva) and [Marc K3nt3L](https://github.com/K3nt3L) for creating additional SDXL styles available in Fooocus. Thanks [daswer123](https://github.com/daswer123) for contributing the Canvas Zoom!
The project starts from a mixture of [Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) and [ComfyUI](https://github.com/comfyanonymous/ComfyUI) codebases.
Also, thanks [daswer123](https://github.com/daswer123) for contributing the Canvas Zoom!
## Update Log ## Update Log
@ -454,6 +458,8 @@ The log is [here](update_log.md).
## Localization/Translation/I18N ## Localization/Translation/I18N
**We need your help!** Please help translate Fooocus into international languages.
You can put json files in the `language` folder to translate the user interface. You can put json files in the `language` folder to translate the user interface.
For example, below is the content of `Fooocus/language/example.json`: For example, below is the content of `Fooocus/language/example.json`:

View File

@ -1,18 +1,3 @@
# [2.5.5](https://github.com/lllyasviel/Fooocus/releases/tag/v2.5.5)
* Fix colab inpaint issue by moving an import statement
# [2.5.4](https://github.com/lllyasviel/Fooocus/releases/tag/v2.5.4)
* Fix validation for default_ip_image_* and default_inpaint_mask_sam_model
* Fix enhance mask debugging in combination with image sorting
* Fix loading of checkpoints and LoRAs when using multiple directories in config and then switching presets
# [2.5.3](https://github.com/lllyasviel/Fooocus/releases/tag/v2.5.3)
* Only load weights from non-safetensors files, preventing harmful code injection
* Add checkbox for applying/resetting styles when describing images, also allowing multiple describe content types
# [2.5.2](https://github.com/lllyasviel/Fooocus/releases/tag/v2.5.2) # [2.5.2](https://github.com/lllyasviel/Fooocus/releases/tag/v2.5.2)
* Fix not adding positive prompt when styles didn't have a {prompt} placeholder in the positive prompt * Fix not adding positive prompt when styles didn't have a {prompt} placeholder in the positive prompt

View File

@ -337,11 +337,10 @@ with shared.gradio_root:
with gr.Column(): with gr.Column():
describe_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False) describe_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False)
with gr.Column(): with gr.Column():
describe_methods = gr.CheckboxGroup( describe_method = gr.Radio(
label='Content Type', label='Content Type',
choices=flags.describe_types, choices=[flags.describe_type_photo, flags.describe_type_anime],
value=modules.config.default_describe_content_type) value=flags.describe_type_photo)
describe_apply_styles = gr.Checkbox(label='Apply Styles', value=modules.config.default_describe_apply_prompts_checkbox)
describe_btn = gr.Button(value='Describe this Image into Prompt') describe_btn = gr.Button(value='Describe this Image into Prompt')
describe_image_size = gr.Textbox(label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False) describe_image_size = gr.Textbox(label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/1363" target="_blank">\U0001F4D4 Documentation</a>') gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/1363" target="_blank">\U0001F4D4 Documentation</a>')
@ -1061,54 +1060,30 @@ with shared.gradio_root:
gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False) gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False)
break break
def trigger_describe(modes, img, apply_styles): def trigger_describe(mode, img):
describe_prompts = [] if mode == flags.describe_type_photo:
styles = set()
if flags.describe_type_photo in modes:
from extras.interrogate import default_interrogator as default_interrogator_photo from extras.interrogate import default_interrogator as default_interrogator_photo
describe_prompts.append(default_interrogator_photo(img)) return default_interrogator_photo(img), ["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"]
styles.update(["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"]) if mode == flags.describe_type_anime:
if flags.describe_type_anime in modes:
from extras.wd14tagger import default_interrogator as default_interrogator_anime from extras.wd14tagger import default_interrogator as default_interrogator_anime
describe_prompts.append(default_interrogator_anime(img)) return default_interrogator_anime(img), ["Fooocus V2", "Fooocus Masterpiece"]
styles.update(["Fooocus V2", "Fooocus Masterpiece"]) return mode, ["Fooocus V2"]
if len(styles) == 0 or not apply_styles: describe_btn.click(trigger_describe, inputs=[describe_method, describe_input_image],
styles = gr.update() outputs=[prompt, style_selections], show_progress=True, queue=True)
else:
styles = list(styles)
if len(describe_prompts) == 0:
describe_prompt = gr.update()
else:
describe_prompt = ', '.join(describe_prompts)
return describe_prompt, styles
describe_btn.click(trigger_describe, inputs=[describe_methods, describe_input_image, describe_apply_styles],
outputs=[prompt, style_selections], show_progress=True, queue=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}')
if args_manager.args.enable_auto_describe_image: if args_manager.args.enable_auto_describe_image:
def trigger_auto_describe(mode, img, prompt, apply_styles): def trigger_auto_describe(mode, img, prompt):
# keep prompt if not empty # keep prompt if not empty
if prompt == '': if prompt == '':
return trigger_describe(mode, img, apply_styles) return trigger_describe(mode, img)
return gr.update(), gr.update() return gr.update(), gr.update()
uov_input_image.upload(trigger_auto_describe, inputs=[describe_methods, uov_input_image, prompt, describe_apply_styles], uov_input_image.upload(trigger_auto_describe, inputs=[describe_method, uov_input_image, prompt],
outputs=[prompt, style_selections], show_progress=True, queue=True) \ outputs=[prompt, style_selections], show_progress=True, queue=True)
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}')
enhance_input_image.upload(lambda: gr.update(value=True), outputs=enhance_checkbox, queue=False, show_progress=False) \ enhance_input_image.upload(lambda: gr.update(value=True), outputs=enhance_checkbox, queue=False, show_progress=False) \
.then(trigger_auto_describe, inputs=[describe_methods, enhance_input_image, prompt, describe_apply_styles], .then(trigger_auto_describe, inputs=[describe_method, enhance_input_image, prompt], outputs=[prompt, style_selections], show_progress=True, queue=True)
outputs=[prompt, style_selections], show_progress=True, queue=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}')
def dump_default_english_config(): def dump_default_english_config():
from modules.localization import dump_english_config from modules.localization import dump_english_config