lllyasviel commited on
Commit
796cb1f
1 Parent(s): 2f3af37

1.0.20 (#35)

Browse files

Re-write UI to use async codes: (1) for faster start, and (2) for better live preview.
Removed opencv dependency
Plan to support Linux soon

fooocus_version.py CHANGED
@@ -1 +1 @@
1
- version = '1.0.19'
 
1
+ version = '1.0.20'
modules/async_worker.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+
4
+ buffer = []
5
+ outputs = []
6
+
7
+
8
+ def worker():
9
+ global buffer, outputs
10
+
11
+ import time
12
+ import random
13
+ import modules.default_pipeline as pipeline
14
+ import modules.path
15
+
16
+ from PIL import Image
17
+ from modules.sdxl_styles import apply_style, aspect_ratios
18
+ from modules.util import generate_temp_filename
19
+
20
+ def handler(task):
21
+ prompt, negative_prompt, style_selction, performance_selction, \
22
+ aspect_ratios_selction, image_number, image_seed, base_model_name, refiner_model_name, \
23
+ l1, w1, l2, w2, l3, w3, l4, w4, l5, w5 = task
24
+
25
+ loras = [(l1, w1), (l2, w2), (l3, w3), (l4, w4), (l5, w5)]
26
+
27
+ pipeline.refresh_base_model(base_model_name)
28
+ pipeline.refresh_refiner_model(refiner_model_name)
29
+ pipeline.refresh_loras(loras)
30
+
31
+ p_txt, n_txt = apply_style(style_selction, prompt, negative_prompt)
32
+
33
+ if performance_selction == 'Speed':
34
+ steps = 30
35
+ switch = 20
36
+ else:
37
+ steps = 60
38
+ switch = 40
39
+
40
+ width, height = aspect_ratios[aspect_ratios_selction]
41
+
42
+ results = []
43
+ seed = image_seed
44
+ if not isinstance(seed, int) or seed < 0 or seed > 65535:
45
+ seed = random.randint(1, 65535)
46
+
47
+ all_steps = steps * image_number
48
+
49
+ def callback(step, x0, x, total_steps, y):
50
+ done_steps = i * steps + step
51
+ outputs.append(['preview', (
52
+ int(100.0 * float(done_steps) / float(all_steps)),
53
+ f'Step {step}/{total_steps} in the {i}-th Sampling',
54
+ y)])
55
+
56
+ for i in range(image_number):
57
+ imgs = pipeline.process(p_txt, n_txt, steps, switch, width, height, seed, callback=callback)
58
+
59
+ for x in imgs:
60
+ local_temp_filename = generate_temp_filename(folder=modules.path.temp_outputs_path, extension='png')
61
+ Image.fromarray(x).save(local_temp_filename)
62
+
63
+ seed += 1
64
+ results += imgs
65
+
66
+ outputs.append(['results', results])
67
+ return
68
+
69
+ while True:
70
+ time.sleep(0.01)
71
+ if len(buffer) > 0:
72
+ task = buffer.pop(0)
73
+ handler(task)
74
+ pass
75
+
76
+
77
+ threading.Thread(target=worker, daemon=True).start()
modules/core.py CHANGED
@@ -1,6 +1,5 @@
1
  import os
2
  import random
3
- import cv2
4
  import einops
5
  import torch
6
  import numpy as np
@@ -8,12 +7,11 @@ import numpy as np
8
  import comfy.model_management
9
  import comfy.utils
10
 
11
- from comfy.sd import load_checkpoint_guess_config, load_lora_for_models
12
  from nodes import VAEDecode, EmptyLatentImage, CLIPTextEncode
13
  from comfy.sample import prepare_mask, broadcast_cond, load_additional_models, cleanup_additional_models
14
  from modules.samplers_advanced import KSampler, KSamplerWithRefiner
15
  from modules.adm_patch import patch_negative_adm
16
- from modules.cv2win32 import show_preview
17
 
18
 
19
  patch_negative_adm()
@@ -86,11 +84,7 @@ def get_previewer(device, latent_format):
86
  x_sample = taesd.decoder(torch.nn.functional.avg_pool2d(x0, kernel_size=(2, 2))).detach() * 255.0
87
  x_sample = einops.rearrange(x_sample, 'b c h w -> b h w c')
88
  x_sample = x_sample.cpu().numpy().clip(0, 255).astype(np.uint8)
89
- for i, s in enumerate(x_sample):
90
- if i > 0:
91
- show_preview(f'cv2_preview_{i}', s, title=f'Preview Image {i}, step = [{step}/{total_steps}')
92
- else:
93
- show_preview(f'cv2_preview_{i}', s, title=f'Preview Image, step = {step}/{total_steps}')
94
 
95
  taesd.preview = preview_function
96
 
@@ -126,10 +120,11 @@ def ksampler(model, positive, negative, latent, seed=None, steps=30, cfg=7.0, sa
126
  pbar = comfy.utils.ProgressBar(steps)
127
 
128
  def callback(step, x0, x, total_steps):
129
- if callback_function is not None:
130
- callback_function(step, x0, x, total_steps)
131
  if previewer and step % 3 == 0:
132
- previewer.preview(x0, step, total_steps)
 
 
133
  pbar.update_absolute(step + 1, total_steps, None)
134
 
135
  sigmas = None
@@ -197,10 +192,11 @@ def ksampler_with_refiner(model, positive, negative, refiner, refiner_positive,
197
  pbar = comfy.utils.ProgressBar(steps)
198
 
199
  def callback(step, x0, x, total_steps):
200
- if callback_function is not None:
201
- callback_function(step, x0, x, total_steps)
202
  if previewer and step % 3 == 0:
203
- previewer.preview(x0, step, total_steps)
 
 
204
  pbar.update_absolute(step + 1, total_steps, None)
205
 
206
  sigmas = None
 
1
  import os
2
  import random
 
3
  import einops
4
  import torch
5
  import numpy as np
 
7
  import comfy.model_management
8
  import comfy.utils
9
 
10
+ from comfy.sd import load_checkpoint_guess_config
11
  from nodes import VAEDecode, EmptyLatentImage, CLIPTextEncode
12
  from comfy.sample import prepare_mask, broadcast_cond, load_additional_models, cleanup_additional_models
13
  from modules.samplers_advanced import KSampler, KSamplerWithRefiner
14
  from modules.adm_patch import patch_negative_adm
 
15
 
16
 
17
  patch_negative_adm()
 
84
  x_sample = taesd.decoder(torch.nn.functional.avg_pool2d(x0, kernel_size=(2, 2))).detach() * 255.0
85
  x_sample = einops.rearrange(x_sample, 'b c h w -> b h w c')
86
  x_sample = x_sample.cpu().numpy().clip(0, 255).astype(np.uint8)
87
+ return x_sample[0]
 
 
 
 
88
 
89
  taesd.preview = preview_function
90
 
 
120
  pbar = comfy.utils.ProgressBar(steps)
121
 
122
  def callback(step, x0, x, total_steps):
123
+ y = None
 
124
  if previewer and step % 3 == 0:
125
+ y = previewer.preview(x0, step, total_steps)
126
+ if callback_function is not None:
127
+ callback_function(step, x0, x, total_steps, y)
128
  pbar.update_absolute(step + 1, total_steps, None)
129
 
130
  sigmas = None
 
192
  pbar = comfy.utils.ProgressBar(steps)
193
 
194
  def callback(step, x0, x, total_steps):
195
+ y = None
 
196
  if previewer and step % 3 == 0:
197
+ y = previewer.preview(x0, step, total_steps)
198
+ if callback_function is not None:
199
+ callback_function(step, x0, x, total_steps, y)
200
  pbar.update_absolute(step + 1, total_steps, None)
201
 
202
  sigmas = None
modules/cv2win32.py DELETED
@@ -1,43 +0,0 @@
1
- import threading
2
- import cv2
3
- import os
4
-
5
-
6
- buffer = []
7
-
8
-
9
- def worker():
10
- global buffer
11
- try:
12
- while True:
13
- cv2.waitKey(50)
14
- if len(buffer) > 0:
15
- task = buffer.pop(0)
16
- if task is None:
17
- cv2.destroyAllWindows()
18
- else:
19
- flag, img, title = task
20
- cv2.imshow(flag, img)
21
- cv2.setWindowTitle(flag, title)
22
- cv2.setWindowProperty(flag, cv2.WND_PROP_TOPMOST, 1)
23
- except Exception as e:
24
- print('Failed to open preview window. You are not using a local device with GUI support.')
25
- print(e)
26
- pass
27
-
28
-
29
- def save_image(path, img):
30
- os.makedirs(os.path.dirname(path), exist_ok=True)
31
- cv2.imwrite(path, img[..., ::-1].copy())
32
- print(f'Image saved: {path}')
33
-
34
-
35
- def show_preview(flag, img, title='preview'):
36
- buffer.append((flag, img[..., ::-1].copy(), title))
37
-
38
-
39
- def close_all_preview():
40
- buffer.append(None)
41
-
42
-
43
- threading.Thread(target=worker, daemon=True).start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/html.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ css = '''
2
+ .loader-container {
3
+ display: flex; /* Use flex to align items horizontally */
4
+ align-items: center; /* Center items vertically within the container */
5
+ white-space: nowrap; /* Prevent line breaks within the container */
6
+ }
7
+
8
+ .loader {
9
+ border: 8px solid #f3f3f3; /* Light grey */
10
+ border-top: 8px solid #3498db; /* Blue */
11
+ border-radius: 50%;
12
+ width: 30px;
13
+ height: 30px;
14
+ animation: spin 2s linear infinite;
15
+ }
16
+
17
+ @keyframes spin {
18
+ 0% { transform: rotate(0deg); }
19
+ 100% { transform: rotate(360deg); }
20
+ }
21
+
22
+ /* Style the progress bar */
23
+ progress {
24
+ appearance: none; /* Remove default styling */
25
+ height: 20px; /* Set the height of the progress bar */
26
+ border-radius: 5px; /* Round the corners of the progress bar */
27
+ background-color: #f3f3f3; /* Light grey background */
28
+ width: 100%;
29
+ }
30
+
31
+ /* Style the progress bar container */
32
+ .progress-container {
33
+ margin-left: 20px;
34
+ margin-right: 20px;
35
+ flex-grow: 1; /* Allow the progress container to take up remaining space */
36
+ }
37
+
38
+ /* Set the color of the progress bar fill */
39
+ progress::-webkit-progress-value {
40
+ background-color: #3498db; /* Blue color for the fill */
41
+ }
42
+
43
+ progress::-moz-progress-bar {
44
+ background-color: #3498db; /* Blue color for the fill in Firefox */
45
+ }
46
+
47
+ /* Style the text on the progress bar */
48
+ progress::after {
49
+ content: attr(value '%'); /* Display the progress value followed by '%' */
50
+ position: absolute;
51
+ top: 50%;
52
+ left: 50%;
53
+ transform: translate(-50%, -50%);
54
+ color: white; /* Set text color */
55
+ font-size: 14px; /* Set font size */
56
+ }
57
+
58
+ /* Style other texts */
59
+ .loader-container > span {
60
+ margin-left: 5px; /* Add spacing between the progress bar and the text */
61
+ }
62
+
63
+ .progress-bar > .generating {
64
+ display: none !important;
65
+ }
66
+
67
+ .progress-bar{
68
+ height: 30px !important;
69
+ }
70
+
71
+ '''
72
+ progress_html = '''
73
+ <div class="loader-container">
74
+ <div class="loader"></div>
75
+ <div class="progress-container">
76
+ <progress value="*number*" max="100"></progress>
77
+ </div>
78
+ <span>*text*</span>
79
+ </div>
80
+ '''
81
+
82
+
83
+ def make_progress_html(number, text):
84
+ return progress_html.replace('*number*', str(number)).replace('*text*', text)
requirements_versions.txt CHANGED
@@ -8,8 +8,8 @@ Pillow==9.2.0
8
  scipy==1.9.3
9
  tqdm==4.64.1
10
  psutil==5.9.5
11
- opencv-python==4.7.0.72
12
  numpy==1.23.5
13
  pytorch_lightning==1.9.4
14
  omegaconf==2.2.3
15
  gradio==3.39.0
 
 
8
  scipy==1.9.3
9
  tqdm==4.64.1
10
  psutil==5.9.5
 
11
  numpy==1.23.5
12
  pytorch_lightning==1.9.4
13
  omegaconf==2.2.3
14
  gradio==3.39.0
15
+ pygit2==1.12.2
update_log.md CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  ### 1.0.19
2
 
3
  * Unlock to allow changing model.
 
1
+ ### 1.0.20
2
+
3
+ * Re-write UI to use async codes: (1) for faster start, and (2) for better live preview.
4
+ * Removed opencv dependency
5
+ * Plan to support Linux soon
6
+
7
  ### 1.0.19
8
 
9
  * Unlock to allow changing model.
webui.py CHANGED
@@ -1,65 +1,49 @@
1
  import gradio as gr
 
 
2
  import modules.path
3
- import random
4
  import fooocus_version
5
- import modules.default_pipeline as pipeline
6
-
7
- from modules.sdxl_styles import apply_style, style_keys, aspect_ratios
8
- from modules.cv2win32 import close_all_preview, save_image
9
- from modules.util import generate_temp_filename
10
-
11
-
12
- def generate_clicked(prompt, negative_prompt, style_selction, performance_selction,
13
- aspect_ratios_selction, image_number, image_seed, base_model_name, refiner_model_name,
14
- l1, w1, l2, w2, l3, w3, l4, w4, l5, w5, progress=gr.Progress()):
15
-
16
- loras = [(l1, w1), (l2, w2), (l3, w3), (l4, w4), (l5, w5)]
17
-
18
- pipeline.refresh_base_model(base_model_name)
19
- pipeline.refresh_refiner_model(refiner_model_name)
20
- pipeline.refresh_loras(loras)
21
-
22
- p_txt, n_txt = apply_style(style_selction, prompt, negative_prompt)
23
-
24
- if performance_selction == 'Speed':
25
- steps = 30
26
- switch = 20
27
- else:
28
- steps = 60
29
- switch = 40
30
-
31
- width, height = aspect_ratios[aspect_ratios_selction]
32
-
33
- results = []
34
- seed = image_seed
35
- if not isinstance(seed, int) or seed < 0 or seed > 65535:
36
- seed = random.randint(1, 65535)
37
-
38
- all_steps = steps * image_number
39
-
40
- def callback(step, x0, x, total_steps):
41
- done_steps = i * steps + step
42
- progress(float(done_steps) / float(all_steps), f'Step {step}/{total_steps} in the {i}-th Sampling')
43
-
44
- for i in range(image_number):
45
- imgs = pipeline.process(p_txt, n_txt, steps, switch, width, height, seed, callback=callback)
46
-
47
- for x in imgs:
48
- local_temp_filename = generate_temp_filename(folder=modules.path.temp_outputs_path, extension='png')
49
- save_image(local_temp_filename, x)
50
-
51
- seed += 1
52
- results += imgs
53
-
54
- close_all_preview()
55
- return results
56
-
57
-
58
- block = gr.Blocks(title='Fooocus ' + fooocus_version.version).queue()
59
  with block:
60
  with gr.Row():
61
  with gr.Column():
62
- gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', height=720)
 
 
63
  with gr.Row():
64
  with gr.Column(scale=0.85):
65
  prompt = gr.Textbox(show_label=False, placeholder="Type prompt here.", container=False, autofocus=True)
@@ -107,6 +91,6 @@ with block:
107
  performance_selction, aspect_ratios_selction, image_number, image_seed
108
  ]
109
  ctrls += [base_model, refiner_model] + lora_ctrls
110
- run_button.click(fn=generate_clicked, inputs=ctrls, outputs=[gallery])
111
 
112
- block.launch(inbrowser=True)
 
1
  import gradio as gr
2
+ import sys
3
+ import time
4
  import modules.path
 
5
  import fooocus_version
6
+ import modules.html
7
+ import modules.async_worker as worker
8
+
9
+ from modules.sdxl_styles import style_keys, aspect_ratios
10
+
11
+
12
+ def generate_clicked(*args):
13
+ yield gr.update(interactive=False), \
14
+ gr.update(visible=True, value=modules.html.make_progress_html(1, 'Processing text encoding ...')), \
15
+ gr.update(visible=True, value=None), \
16
+ gr.update(visible=False)
17
+
18
+ worker.buffer.append(list(args))
19
+ finished = False
20
+
21
+ while not finished:
22
+ time.sleep(0.01)
23
+ if len(worker.outputs) > 0:
24
+ flag, product = worker.outputs.pop(0)
25
+ if flag == 'preview':
26
+ percentage, title, image = product
27
+ yield gr.update(interactive=False), \
28
+ gr.update(visible=True, value=modules.html.make_progress_html(percentage, title)), \
29
+ gr.update(visible=True, value=image) if image is not None else gr.update(), \
30
+ gr.update(visible=False)
31
+ if flag == 'results':
32
+ yield gr.update(interactive=True), \
33
+ gr.update(visible=False), \
34
+ gr.update(visible=False), \
35
+ gr.update(visible=True, value=product)
36
+ finished = True
37
+ return
38
+
39
+
40
+ block = gr.Blocks(title='Fooocus ' + fooocus_version.version, css=modules.html.css).queue()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  with block:
42
  with gr.Row():
43
  with gr.Column():
44
+ progress_window = gr.Image(label='Preview', show_label=True, height=640, visible=False)
45
+ progress_html = gr.HTML(value=modules.html.make_progress_html(32, 'Progress 32%'), visible=False, elem_id='progress-bar', elem_classes='progress-bar')
46
+ gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', height=720, visible=True)
47
  with gr.Row():
48
  with gr.Column(scale=0.85):
49
  prompt = gr.Textbox(show_label=False, placeholder="Type prompt here.", container=False, autofocus=True)
 
91
  performance_selction, aspect_ratios_selction, image_number, image_seed
92
  ]
93
  ctrls += [base_model, refiner_model] + lora_ctrls
94
+ run_button.click(fn=generate_clicked, inputs=ctrls, outputs=[run_button, progress_html, progress_window, gallery])
95
 
96
+ block.launch(inbrowser=True, server_name='0.0.0.0' if '--listen' in sys.argv else None)