1.0.20 (#35)
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
This commit is contained in:
parent
158afe088d
commit
8720e435f5
@ -1 +1 @@
|
|||||||
version = '1.0.19'
|
version = '1.0.20'
|
||||||
|
77
modules/async_worker.py
Normal file
77
modules/async_worker.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
buffer = []
|
||||||
|
outputs = []
|
||||||
|
|
||||||
|
|
||||||
|
def worker():
|
||||||
|
global buffer, outputs
|
||||||
|
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import modules.default_pipeline as pipeline
|
||||||
|
import modules.path
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from modules.sdxl_styles import apply_style, aspect_ratios
|
||||||
|
from modules.util import generate_temp_filename
|
||||||
|
|
||||||
|
def handler(task):
|
||||||
|
prompt, negative_prompt, style_selction, performance_selction, \
|
||||||
|
aspect_ratios_selction, image_number, image_seed, base_model_name, refiner_model_name, \
|
||||||
|
l1, w1, l2, w2, l3, w3, l4, w4, l5, w5 = task
|
||||||
|
|
||||||
|
loras = [(l1, w1), (l2, w2), (l3, w3), (l4, w4), (l5, w5)]
|
||||||
|
|
||||||
|
pipeline.refresh_base_model(base_model_name)
|
||||||
|
pipeline.refresh_refiner_model(refiner_model_name)
|
||||||
|
pipeline.refresh_loras(loras)
|
||||||
|
|
||||||
|
p_txt, n_txt = apply_style(style_selction, prompt, negative_prompt)
|
||||||
|
|
||||||
|
if performance_selction == 'Speed':
|
||||||
|
steps = 30
|
||||||
|
switch = 20
|
||||||
|
else:
|
||||||
|
steps = 60
|
||||||
|
switch = 40
|
||||||
|
|
||||||
|
width, height = aspect_ratios[aspect_ratios_selction]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
seed = image_seed
|
||||||
|
if not isinstance(seed, int) or seed < 0 or seed > 65535:
|
||||||
|
seed = random.randint(1, 65535)
|
||||||
|
|
||||||
|
all_steps = steps * image_number
|
||||||
|
|
||||||
|
def callback(step, x0, x, total_steps, y):
|
||||||
|
done_steps = i * steps + step
|
||||||
|
outputs.append(['preview', (
|
||||||
|
int(100.0 * float(done_steps) / float(all_steps)),
|
||||||
|
f'Step {step}/{total_steps} in the {i}-th Sampling',
|
||||||
|
y)])
|
||||||
|
|
||||||
|
for i in range(image_number):
|
||||||
|
imgs = pipeline.process(p_txt, n_txt, steps, switch, width, height, seed, callback=callback)
|
||||||
|
|
||||||
|
for x in imgs:
|
||||||
|
local_temp_filename = generate_temp_filename(folder=modules.path.temp_outputs_path, extension='png')
|
||||||
|
Image.fromarray(x).save(local_temp_filename)
|
||||||
|
|
||||||
|
seed += 1
|
||||||
|
results += imgs
|
||||||
|
|
||||||
|
outputs.append(['results', results])
|
||||||
|
return
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(0.01)
|
||||||
|
if len(buffer) > 0:
|
||||||
|
task = buffer.pop(0)
|
||||||
|
handler(task)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
threading.Thread(target=worker, daemon=True).start()
|
@ -1,6 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import cv2
|
|
||||||
import einops
|
import einops
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -8,12 +7,11 @@ import numpy as np
|
|||||||
import comfy.model_management
|
import comfy.model_management
|
||||||
import comfy.utils
|
import comfy.utils
|
||||||
|
|
||||||
from comfy.sd import load_checkpoint_guess_config, load_lora_for_models
|
from comfy.sd import load_checkpoint_guess_config
|
||||||
from nodes import VAEDecode, EmptyLatentImage, CLIPTextEncode
|
from nodes import VAEDecode, EmptyLatentImage, CLIPTextEncode
|
||||||
from comfy.sample import prepare_mask, broadcast_cond, load_additional_models, cleanup_additional_models
|
from comfy.sample import prepare_mask, broadcast_cond, load_additional_models, cleanup_additional_models
|
||||||
from modules.samplers_advanced import KSampler, KSamplerWithRefiner
|
from modules.samplers_advanced import KSampler, KSamplerWithRefiner
|
||||||
from modules.adm_patch import patch_negative_adm
|
from modules.adm_patch import patch_negative_adm
|
||||||
from modules.cv2win32 import show_preview
|
|
||||||
|
|
||||||
|
|
||||||
patch_negative_adm()
|
patch_negative_adm()
|
||||||
@ -86,11 +84,7 @@ def get_previewer(device, latent_format):
|
|||||||
x_sample = taesd.decoder(torch.nn.functional.avg_pool2d(x0, kernel_size=(2, 2))).detach() * 255.0
|
x_sample = taesd.decoder(torch.nn.functional.avg_pool2d(x0, kernel_size=(2, 2))).detach() * 255.0
|
||||||
x_sample = einops.rearrange(x_sample, 'b c h w -> b h w c')
|
x_sample = einops.rearrange(x_sample, 'b c h w -> b h w c')
|
||||||
x_sample = x_sample.cpu().numpy().clip(0, 255).astype(np.uint8)
|
x_sample = x_sample.cpu().numpy().clip(0, 255).astype(np.uint8)
|
||||||
for i, s in enumerate(x_sample):
|
return x_sample[0]
|
||||||
if i > 0:
|
|
||||||
show_preview(f'cv2_preview_{i}', s, title=f'Preview Image {i}, step = [{step}/{total_steps}')
|
|
||||||
else:
|
|
||||||
show_preview(f'cv2_preview_{i}', s, title=f'Preview Image, step = {step}/{total_steps}')
|
|
||||||
|
|
||||||
taesd.preview = preview_function
|
taesd.preview = preview_function
|
||||||
|
|
||||||
@ -126,10 +120,11 @@ def ksampler(model, positive, negative, latent, seed=None, steps=30, cfg=7.0, sa
|
|||||||
pbar = comfy.utils.ProgressBar(steps)
|
pbar = comfy.utils.ProgressBar(steps)
|
||||||
|
|
||||||
def callback(step, x0, x, total_steps):
|
def callback(step, x0, x, total_steps):
|
||||||
if callback_function is not None:
|
y = None
|
||||||
callback_function(step, x0, x, total_steps)
|
|
||||||
if previewer and step % 3 == 0:
|
if previewer and step % 3 == 0:
|
||||||
previewer.preview(x0, step, total_steps)
|
y = previewer.preview(x0, step, total_steps)
|
||||||
|
if callback_function is not None:
|
||||||
|
callback_function(step, x0, x, total_steps, y)
|
||||||
pbar.update_absolute(step + 1, total_steps, None)
|
pbar.update_absolute(step + 1, total_steps, None)
|
||||||
|
|
||||||
sigmas = None
|
sigmas = None
|
||||||
@ -197,10 +192,11 @@ def ksampler_with_refiner(model, positive, negative, refiner, refiner_positive,
|
|||||||
pbar = comfy.utils.ProgressBar(steps)
|
pbar = comfy.utils.ProgressBar(steps)
|
||||||
|
|
||||||
def callback(step, x0, x, total_steps):
|
def callback(step, x0, x, total_steps):
|
||||||
if callback_function is not None:
|
y = None
|
||||||
callback_function(step, x0, x, total_steps)
|
|
||||||
if previewer and step % 3 == 0:
|
if previewer and step % 3 == 0:
|
||||||
previewer.preview(x0, step, total_steps)
|
y = previewer.preview(x0, step, total_steps)
|
||||||
|
if callback_function is not None:
|
||||||
|
callback_function(step, x0, x, total_steps, y)
|
||||||
pbar.update_absolute(step + 1, total_steps, None)
|
pbar.update_absolute(step + 1, total_steps, None)
|
||||||
|
|
||||||
sigmas = None
|
sigmas = None
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import threading
|
|
||||||
import cv2
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
buffer = []
|
|
||||||
|
|
||||||
|
|
||||||
def worker():
|
|
||||||
global buffer
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
cv2.waitKey(50)
|
|
||||||
if len(buffer) > 0:
|
|
||||||
task = buffer.pop(0)
|
|
||||||
if task is None:
|
|
||||||
cv2.destroyAllWindows()
|
|
||||||
else:
|
|
||||||
flag, img, title = task
|
|
||||||
cv2.imshow(flag, img)
|
|
||||||
cv2.setWindowTitle(flag, title)
|
|
||||||
cv2.setWindowProperty(flag, cv2.WND_PROP_TOPMOST, 1)
|
|
||||||
except Exception as e:
|
|
||||||
print('Failed to open preview window. You are not using a local device with GUI support.')
|
|
||||||
print(e)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def save_image(path, img):
|
|
||||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
||||||
cv2.imwrite(path, img[..., ::-1].copy())
|
|
||||||
print(f'Image saved: {path}')
|
|
||||||
|
|
||||||
|
|
||||||
def show_preview(flag, img, title='preview'):
|
|
||||||
buffer.append((flag, img[..., ::-1].copy(), title))
|
|
||||||
|
|
||||||
|
|
||||||
def close_all_preview():
|
|
||||||
buffer.append(None)
|
|
||||||
|
|
||||||
|
|
||||||
threading.Thread(target=worker, daemon=True).start()
|
|
84
modules/html.py
Normal file
84
modules/html.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
css = '''
|
||||||
|
.loader-container {
|
||||||
|
display: flex; /* Use flex to align items horizontally */
|
||||||
|
align-items: center; /* Center items vertically within the container */
|
||||||
|
white-space: nowrap; /* Prevent line breaks within the container */
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
border: 8px solid #f3f3f3; /* Light grey */
|
||||||
|
border-top: 8px solid #3498db; /* Blue */
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
animation: spin 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the progress bar */
|
||||||
|
progress {
|
||||||
|
appearance: none; /* Remove default styling */
|
||||||
|
height: 20px; /* Set the height of the progress bar */
|
||||||
|
border-radius: 5px; /* Round the corners of the progress bar */
|
||||||
|
background-color: #f3f3f3; /* Light grey background */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the progress bar container */
|
||||||
|
.progress-container {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
flex-grow: 1; /* Allow the progress container to take up remaining space */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the color of the progress bar fill */
|
||||||
|
progress::-webkit-progress-value {
|
||||||
|
background-color: #3498db; /* Blue color for the fill */
|
||||||
|
}
|
||||||
|
|
||||||
|
progress::-moz-progress-bar {
|
||||||
|
background-color: #3498db; /* Blue color for the fill in Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the text on the progress bar */
|
||||||
|
progress::after {
|
||||||
|
content: attr(value '%'); /* Display the progress value followed by '%' */
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: white; /* Set text color */
|
||||||
|
font-size: 14px; /* Set font size */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style other texts */
|
||||||
|
.loader-container > span {
|
||||||
|
margin-left: 5px; /* Add spacing between the progress bar and the text */
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar > .generating {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar{
|
||||||
|
height: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
'''
|
||||||
|
progress_html = '''
|
||||||
|
<div class="loader-container">
|
||||||
|
<div class="loader"></div>
|
||||||
|
<div class="progress-container">
|
||||||
|
<progress value="*number*" max="100"></progress>
|
||||||
|
</div>
|
||||||
|
<span>*text*</span>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def make_progress_html(number, text):
|
||||||
|
return progress_html.replace('*number*', str(number)).replace('*text*', text)
|
@ -8,8 +8,8 @@ Pillow==9.2.0
|
|||||||
scipy==1.9.3
|
scipy==1.9.3
|
||||||
tqdm==4.64.1
|
tqdm==4.64.1
|
||||||
psutil==5.9.5
|
psutil==5.9.5
|
||||||
opencv-python==4.7.0.72
|
|
||||||
numpy==1.23.5
|
numpy==1.23.5
|
||||||
pytorch_lightning==1.9.4
|
pytorch_lightning==1.9.4
|
||||||
omegaconf==2.2.3
|
omegaconf==2.2.3
|
||||||
gradio==3.39.0
|
gradio==3.39.0
|
||||||
|
pygit2==1.12.2
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
### 1.0.20
|
||||||
|
|
||||||
|
* 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
|
||||||
|
|
||||||
### 1.0.19
|
### 1.0.19
|
||||||
|
|
||||||
* Unlock to allow changing model.
|
* Unlock to allow changing model.
|
||||||
|
86
webui.py
86
webui.py
@ -1,65 +1,49 @@
|
|||||||
import gradio as gr
|
import gradio as gr
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
import modules.path
|
import modules.path
|
||||||
import random
|
|
||||||
import fooocus_version
|
import fooocus_version
|
||||||
import modules.default_pipeline as pipeline
|
import modules.html
|
||||||
|
import modules.async_worker as worker
|
||||||
|
|
||||||
from modules.sdxl_styles import apply_style, style_keys, aspect_ratios
|
from modules.sdxl_styles import style_keys, aspect_ratios
|
||||||
from modules.cv2win32 import close_all_preview, save_image
|
|
||||||
from modules.util import generate_temp_filename
|
|
||||||
|
|
||||||
|
|
||||||
def generate_clicked(prompt, negative_prompt, style_selction, performance_selction,
|
def generate_clicked(*args):
|
||||||
aspect_ratios_selction, image_number, image_seed, base_model_name, refiner_model_name,
|
yield gr.update(interactive=False), \
|
||||||
l1, w1, l2, w2, l3, w3, l4, w4, l5, w5, progress=gr.Progress()):
|
gr.update(visible=True, value=modules.html.make_progress_html(1, 'Processing text encoding ...')), \
|
||||||
|
gr.update(visible=True, value=None), \
|
||||||
|
gr.update(visible=False)
|
||||||
|
|
||||||
loras = [(l1, w1), (l2, w2), (l3, w3), (l4, w4), (l5, w5)]
|
worker.buffer.append(list(args))
|
||||||
|
finished = False
|
||||||
|
|
||||||
pipeline.refresh_base_model(base_model_name)
|
while not finished:
|
||||||
pipeline.refresh_refiner_model(refiner_model_name)
|
time.sleep(0.01)
|
||||||
pipeline.refresh_loras(loras)
|
if len(worker.outputs) > 0:
|
||||||
|
flag, product = worker.outputs.pop(0)
|
||||||
p_txt, n_txt = apply_style(style_selction, prompt, negative_prompt)
|
if flag == 'preview':
|
||||||
|
percentage, title, image = product
|
||||||
if performance_selction == 'Speed':
|
yield gr.update(interactive=False), \
|
||||||
steps = 30
|
gr.update(visible=True, value=modules.html.make_progress_html(percentage, title)), \
|
||||||
switch = 20
|
gr.update(visible=True, value=image) if image is not None else gr.update(), \
|
||||||
else:
|
gr.update(visible=False)
|
||||||
steps = 60
|
if flag == 'results':
|
||||||
switch = 40
|
yield gr.update(interactive=True), \
|
||||||
|
gr.update(visible=False), \
|
||||||
width, height = aspect_ratios[aspect_ratios_selction]
|
gr.update(visible=False), \
|
||||||
|
gr.update(visible=True, value=product)
|
||||||
results = []
|
finished = True
|
||||||
seed = image_seed
|
return
|
||||||
if not isinstance(seed, int) or seed < 0 or seed > 65535:
|
|
||||||
seed = random.randint(1, 65535)
|
|
||||||
|
|
||||||
all_steps = steps * image_number
|
|
||||||
|
|
||||||
def callback(step, x0, x, total_steps):
|
|
||||||
done_steps = i * steps + step
|
|
||||||
progress(float(done_steps) / float(all_steps), f'Step {step}/{total_steps} in the {i}-th Sampling')
|
|
||||||
|
|
||||||
for i in range(image_number):
|
|
||||||
imgs = pipeline.process(p_txt, n_txt, steps, switch, width, height, seed, callback=callback)
|
|
||||||
|
|
||||||
for x in imgs:
|
|
||||||
local_temp_filename = generate_temp_filename(folder=modules.path.temp_outputs_path, extension='png')
|
|
||||||
save_image(local_temp_filename, x)
|
|
||||||
|
|
||||||
seed += 1
|
|
||||||
results += imgs
|
|
||||||
|
|
||||||
close_all_preview()
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
block = gr.Blocks(title='Fooocus ' + fooocus_version.version).queue()
|
block = gr.Blocks(title='Fooocus ' + fooocus_version.version, css=modules.html.css).queue()
|
||||||
with block:
|
with block:
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
with gr.Column():
|
with gr.Column():
|
||||||
gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', height=720)
|
progress_window = gr.Image(label='Preview', show_label=True, height=640, visible=False)
|
||||||
|
progress_html = gr.HTML(value=modules.html.make_progress_html(32, 'Progress 32%'), visible=False, elem_id='progress-bar', elem_classes='progress-bar')
|
||||||
|
gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', height=720, visible=True)
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
with gr.Column(scale=0.85):
|
with gr.Column(scale=0.85):
|
||||||
prompt = gr.Textbox(show_label=False, placeholder="Type prompt here.", container=False, autofocus=True)
|
prompt = gr.Textbox(show_label=False, placeholder="Type prompt here.", container=False, autofocus=True)
|
||||||
@ -107,6 +91,6 @@ with block:
|
|||||||
performance_selction, aspect_ratios_selction, image_number, image_seed
|
performance_selction, aspect_ratios_selction, image_number, image_seed
|
||||||
]
|
]
|
||||||
ctrls += [base_model, refiner_model] + lora_ctrls
|
ctrls += [base_model, refiner_model] + lora_ctrls
|
||||||
run_button.click(fn=generate_clicked, inputs=ctrls, outputs=[gallery])
|
run_button.click(fn=generate_clicked, inputs=ctrls, outputs=[run_button, progress_html, progress_window, gallery])
|
||||||
|
|
||||||
block.launch(inbrowser=True)
|
block.launch(inbrowser=True, server_name='0.0.0.0' if '--listen' in sys.argv else None)
|
||||||
|
Loading…
Reference in New Issue
Block a user