Skip to content

Commit 1ebf6a4

Browse files
committed
Added support for FreeU
1 parent a19663b commit 1ebf6a4

11 files changed

+100
-16
lines changed

fooocus_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
version = '2.0.78.1 MRE'
1+
version = '2.0.78.2 MRE'
22
full_version = 'Fooocus ' + version

launch.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def prepare_environment():
2424
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.21')
2525

2626
comfy_repo = os.environ.get('COMFY_REPO', "https://github.com/comfyanonymous/ComfyUI")
27-
comfy_commit_hash = os.environ.get('COMFY_COMMIT_HASH', "492db2de8db7e082addf131b40adb4a1b7535821")
27+
comfy_commit_hash = os.environ.get('COMFY_COMMIT_HASH', "2381d36e6db8e8150e42ff2ede628db5b00ae26f")
2828

2929
print(f"Python {sys.version}")
3030
print(f"Fooocus version: {fooocus_version.version}")

modules/async_worker.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def handler(task):
7777
revision_strength_3, revision_strength_4, same_seed_for_all, output_format, \
7878
control_lora_canny, canny_edge_low, canny_edge_high, canny_start, canny_stop, canny_strength, canny_model, \
7979
control_lora_depth, depth_start, depth_stop, depth_strength, depth_model, use_expansion, \
80+
freeu, freeu_b1, freeu_b2, freeu_s1, freeu_s2, \
8081
input_image_checkbox, current_tab, \
8182
uov_method, uov_input_image, outpaint_selections, inpaint_input_image, \
8283
input_gallery, revision_gallery, keep_input_names = task
@@ -302,7 +303,12 @@ def handler(task):
302303
pipeline.refresh_everything(
303304
refiner_model_name=refiner_model_name,
304305
base_model_name=base_model_name,
305-
loras=loras)
306+
loras=loras,
307+
freeu=freeu,
308+
b1=freeu_b1,
309+
b2=freeu_b2,
310+
s1=freeu_s1,
311+
s2=freeu_s2)
306312

307313
pipeline.set_clip_skips(base_clip_skip, refiner_clip_skip)
308314
if revision_mode:
@@ -503,11 +509,16 @@ def callback(step, x0, x, total_steps, y):
503509
'base_clip_skip': base_clip_skip, 'refiner_clip_skip': refiner_clip_skip,
504510
'base_model': base_model_name, 'refiner_model': refiner_model_name,
505511
'l1': l1, 'w1': w1, 'l2': l2, 'w2': w2, 'l3': l3, 'w3': w3,
506-
'l4': l4, 'w4': w4, 'l5': l5, 'w5': w5, 'img2img': img2img_mode, 'revision': revision_mode,
512+
'l4': l4, 'w4': w4, 'l5': l5, 'w5': w5, 'freeu': freeu,
513+
'img2img': img2img_mode, 'revision': revision_mode,
507514
'positive_prompt_strength': positive_prompt_strength, 'negative_prompt_strength': negative_prompt_strength,
508515
'control_lora_canny': control_lora_canny, 'control_lora_depth': control_lora_depth,
509516
'prompt_expansion': use_expansion
510517
}
518+
if freeu:
519+
metadata |= {
520+
'freeu_b1': freeu_b1, 'freeu_b2': freeu_b2, 'freeu_s1': freeu_s1, 'freeu_s2': freeu_s2
521+
}
511522
if img2img_mode:
512523
metadata |= {
513524
'start_step': start_step, 'denoise': denoise, 'scale': img2img_scale, 'input_image': input_image_filename
@@ -548,6 +559,7 @@ def callback(step, x0, x, total_steps, y):
548559
('CFG & CLIP Skips', (cfg, base_clip_skip, refiner_clip_skip)),
549560
('Base Model', base_model_name),
550561
('Refiner Model', refiner_model_name),
562+
('FreeU', (freeu, freeu_b1, freeu_b2, freeu_s1, freeu_s2) if freeu else (freeu)),
551563
('Image-2-Image', (img2img_mode, start_step, denoise, img2img_scale, input_image_filename) if img2img_mode else (img2img_mode)),
552564
('Revision', (revision_mode, revision_strength_1, revision_strength_2, revision_strength_3,
553565
revision_strength_4, revision_images_filenames) if revision_mode else (revision_mode)),

modules/core.py

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from comfy.sample import prepare_mask, broadcast_cond, get_additional_models, cleanup_additional_models
1919
from comfy_extras.nodes_post_processing import ImageScaleToTotalPixels
2020
from comfy_extras.nodes_canny import Canny
21+
from comfy_extras.nodes_freelunch import FreeU
2122
from comfy.model_base import SDXLRefiner
2223
from comfy.lora import model_lora_keys_unet, model_lora_keys_clip, load_lora
2324
from modules.samplers_advanced import KSamplerBasic, KSamplerWithRefiner
@@ -36,6 +37,7 @@
3637
opCLIPVisionEncode = CLIPVisionEncode()
3738
opUnCLIPConditioning = unCLIPConditioning()
3839
opCanny = Canny()
40+
opFreeU = FreeU()
3941
opControlNetApplyAdvanced = ControlNetApplyAdvanced()
4042

4143

@@ -182,6 +184,12 @@ def detect_edge(image, low_threshold, high_threshold):
182184
return opCanny.detect_edge(image=image, low_threshold=low_threshold, high_threshold=high_threshold)[0]
183185

184186

187+
@torch.no_grad()
188+
@torch.inference_mode()
189+
def freeu(model, b1, b2, s1, s2):
190+
return opFreeU.patch(model=model, b1=b1, b2=b2, s1=s1, s2=s2)[0]
191+
192+
185193
@torch.no_grad()
186194
@torch.inference_mode()
187195
def apply_controlnet(positive, negative, control_net, image, strength, start_percent, end_percent):

modules/default_pipeline.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ def refresh_refiner_model(name):
104104

105105
@torch.no_grad()
106106
@torch.inference_mode()
107-
def refresh_loras(loras):
107+
def patch_base(loras, freeu, b1, b2, s1, s2):
108108
global xl_base, xl_base_patched, xl_base_patched_hash
109-
if xl_base_patched_hash == str(loras):
109+
if xl_base_patched_hash == str(loras + [freeu, b1, b2, s1, s2]):
110110
return
111111

112112
model = xl_base
@@ -123,13 +123,16 @@ def refresh_loras(loras):
123123

124124
model = core.load_sd_lora(model, filename, strength_model=weight, strength_clip=weight)
125125
xl_base_patched = model
126-
xl_base_patched_hash = str(loras)
127-
print(f'LoRAs loaded: {xl_base_patched_hash}')
126+
if freeu:
127+
xl_base_patched.unet = core.freeu(xl_base_patched.unet, b1, b2, s1, s2)
128+
xl_base_patched_hash = str(loras + [freeu, b1, b2, s1, s2])
129+
print(f'LoRAs loaded: {loras}')
130+
if freeu:
131+
print(f'FreeU applied: {[b1, b2, s1, s2]}')
128132

129133
return
130134

131135

132-
133136
@torch.no_grad()
134137
@torch.inference_mode()
135138
def refresh_clip_vision():
@@ -281,7 +284,7 @@ def clear_all_caches():
281284

282285
@torch.no_grad()
283286
@torch.inference_mode()
284-
def refresh_everything(refiner_model_name, base_model_name, loras):
287+
def refresh_everything(refiner_model_name, base_model_name, loras, freeu, b1, b2, s1, s2):
285288
refresh_refiner_model(refiner_model_name)
286289
if xl_refiner is not None:
287290
virtual_memory.try_move_to_virtual_memory(xl_refiner.unet.model)
@@ -290,7 +293,7 @@ def refresh_everything(refiner_model_name, base_model_name, loras):
290293
refresh_base_model(base_model_name)
291294
virtual_memory.load_from_virtual_memory(xl_base.unet.model)
292295

293-
refresh_loras(loras)
296+
patch_base(loras, freeu, b1, b2, s1, s2)
294297
clear_all_caches()
295298
return
296299

@@ -302,7 +305,12 @@ def refresh_everything(refiner_model_name, base_model_name, loras):
302305
(default_settings['lora_2_model'], default_settings['lora_2_weight']),
303306
(default_settings['lora_3_model'], default_settings['lora_3_weight']),
304307
(default_settings['lora_4_model'], default_settings['lora_4_weight']),
305-
(default_settings['lora_5_model'], default_settings['lora_5_weight'])]
308+
(default_settings['lora_5_model'], default_settings['lora_5_weight'])],
309+
freeu=default_settings['freeu'],
310+
b1=default_settings['freeu_b1'],
311+
b2=default_settings['freeu_b2'],
312+
s1=default_settings['freeu_s1'],
313+
s2=default_settings['freeu_s2']
306314
)
307315

308316
expansion = FooocusExpansion()

modules/patch.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,24 @@ def blend_latent(a, b, w):
305305

306306

307307
def patched_unet_forward(self, x, timesteps=None, context=None, y=None, control=None, transformer_options={}, **kwargs):
308+
"""
309+
Apply the model to an input batch.
310+
:param x: an [N x C x ...] Tensor of inputs.
311+
:param timesteps: a 1-D batch of timesteps.
312+
:param context: conditioning plugged in via crossattn
313+
:param y: an [N] Tensor of labels, if class-conditional.
314+
:return: an [N x C x ...] Tensor of outputs.
315+
"""
308316
inpaint_fix = None
309317
if inpaint_worker.current_task is not None:
310318
inpaint_fix = inpaint_worker.current_task.inpaint_head_feature
311319

312320
transformer_options["original_shape"] = list(x.shape)
313321
transformer_options["current_index"] = 0
322+
transformer_patches = transformer_options.get("patches", {})
314323

315324
assert (y is not None) == (
316-
self.num_classes is not None
325+
self.num_classes is not None
317326
), "must specify y if and only if the model is class-conditional"
318327
hs = []
319328
t_emb = timestep_embedding(timesteps, self.model_channels, repeat_only=False).to(self.dtype)
@@ -353,6 +362,11 @@ def patched_unet_forward(self, x, timesteps=None, context=None, y=None, control=
353362
if ctrl is not None:
354363
hsp += ctrl
355364

365+
if "output_block_patch" in transformer_patches:
366+
patch = transformer_patches["output_block_patch"]
367+
for p in patch:
368+
h, hsp = p(h, hsp, transformer_options)
369+
356370
h = torch.cat([h, hsp], dim=1)
357371
del hsp
358372
if len(hs) > 0:

modules/settings.py

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ def load_settings():
6565
settings['lora_4_weight'] = modules.path.default_lora_weight
6666
settings['lora_5_model'] = 'None'
6767
settings['lora_5_weight'] = modules.path.default_lora_weight
68+
settings['freeu'] = False
69+
settings['freeu_b1'] = 1.01
70+
settings['freeu_b2'] = 1.02
71+
settings['freeu_s1'] = 0.99
72+
settings['freeu_s2'] = 0.95
6873

6974
if exists('settings.json'):
7075
with open('settings.json') as settings_file:

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ Below things are already inside the software, and **users do not need to do anyt
189189
24. Support for loading models from subfolders (ported from RuinedFooocus).
190190
25. Support for authentication in --share mode (credentials loaded from auth.json - use auth-example.json as a template).
191191
26. Support for wildcards (ported from RuinedFooocus - put them in wildcards folder, then try prompts like `__color__ sports car` with different seeds).
192+
27. Support for [FreeU](https://chenyangsi.top/FreeU/).
192193

193194
## Thanks
194195

settings-example.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,10 @@
5656
"lora_4_model": "None",
5757
"lora_4_weight": 0.5,
5858
"lora_5_model": "None",
59-
"lora_5_weight": 0.5
59+
"lora_5_weight": 0.5,
60+
"freeu": false,
61+
"freeu_b1": 1.01,
62+
"freeu_b2": 1.02,
63+
"freeu_s1": 0.99,
64+
"freeu_s2": 0.95
6065
}

update_log_mre.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### 2.0.78.2 MRE
2+
3+
* Added support for FreeU.
4+
* Updated Comfy.
5+
16
### 2.0.78.1 MRE
27

38
* Fixed reading paths from paths.json (broken in 2.0.73 MRE).

webui.py

+28-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def metadata_to_ctrls(metadata, ctrls):
9292
# image_number
9393
if 'seed' in metadata:
9494
ctrls[6] = metadata['seed']
95-
ctrls[55] = False
95+
ctrls[60] = False
9696
if 'sharpness' in metadata:
9797
ctrls[7] = metadata['sharpness']
9898
if 'sampler_name' in metadata:
@@ -210,6 +210,16 @@ def metadata_to_ctrls(metadata, ctrls):
210210
ctrls[54] = metadata['prompt_expansion']
211211
elif 'software' in metadata and metadata['software'].startswith('Fooocus 1.'):
212212
ctrls[54] = False
213+
if 'freeu' in metadata:
214+
ctrls[55] = metadata['freeu']
215+
if 'freeu_b1' in metadata:
216+
ctrls[56] = metadata['freeu_b1']
217+
if 'freeu_b2' in metadata:
218+
ctrls[57] = metadata['freeu_b2']
219+
if 'freeu_s1' in metadata:
220+
ctrls[58] = metadata['freeu_s1']
221+
if 'freeu_s2' in metadata:
222+
ctrls[59] = metadata['freeu_s2']
213223
# seed_random
214224
return ctrls
215225

@@ -513,6 +523,16 @@ def depth_changed(value):
513523
scheduler = gr.Dropdown(label='Scheduler', choices=['karras', 'exponential', 'sgm_uniform', 'simple', 'ddim_uniform'], value=settings['scheduler'])
514524
sharpness = gr.Slider(label='Sampling Sharpness', minimum=0.0, maximum=30.0, step=0.01, value=settings['sharpness'])
515525

526+
freeu_enabled = gr.Checkbox(label='FreeU', value=settings['freeu'])
527+
freeu_b1 = gr.Slider(label='Backbone Scaling Factor 1', minimum=0, maximum=2, step=0.01,
528+
value=settings['freeu_b1'], visible=settings['freeu'])
529+
freeu_b2 = gr.Slider(label='Backbone Scaling Factor 2', minimum=0, maximum=2, step=0.01,
530+
value=settings['freeu_b2'], visible=settings['freeu'])
531+
freeu_s1 = gr.Slider(label='Skip Scaling Factor 1', minimum=0, maximum=2, step=0.01,
532+
value=settings['freeu_s1'], visible=settings['freeu'])
533+
freeu_s2 = gr.Slider(label='Skip Scaling Factor 2', minimum=0, maximum=2, step=0.01,
534+
value=settings['freeu_s2'], visible=settings['freeu'])
535+
516536
def model_refresh_clicked():
517537
modules.path.update_all_model_names()
518538
results = []
@@ -523,6 +543,12 @@ def model_refresh_clicked():
523543

524544
model_refresh.click(model_refresh_clicked, [], [base_model, refiner_model] + lora_ctrls, queue=False)
525545

546+
def freeu_changed(value):
547+
return gr.update(visible=value == True), gr.update(visible=value == True), gr.update(visible=value == True), gr.update(visible=value == True)
548+
549+
freeu_enabled.change(fn=freeu_changed, inputs=[freeu_enabled], outputs=[freeu_b1, freeu_b2, freeu_s1, freeu_s2])
550+
551+
freeu_ctrls = [freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2]
526552

527553
with gr.Tab(label='Misc'):
528554
output_format = gr.Radio(label='Output Format', choices=['png', 'jpg'], value=settings['output_format'])
@@ -561,7 +587,7 @@ def verify_input(img2img, canny, depth, gallery_in, gallery_rev, gallery_out):
561587
]
562588
ctrls += [base_model, refiner_model, base_clip_skip, refiner_clip_skip] + lora_ctrls
563589
ctrls += [save_metadata_json, save_metadata_image] + img2img_ctrls + [same_seed_for_all, output_format]
564-
ctrls += canny_ctrls + depth_ctrls + [prompt_expansion]
590+
ctrls += canny_ctrls + depth_ctrls + [prompt_expansion] + freeu_ctrls
565591
load_prompt_button.upload(fn=load_prompt_handler, inputs=[load_prompt_button] + ctrls + [seed_random], outputs=ctrls + [seed_random])
566592
ctrls += [input_image_checkbox, current_tab]
567593
ctrls += [uov_method, uov_input_image]

0 commit comments

Comments
 (0)