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:
lllyasviel 2023-08-12 23:14:54 -07:00 committed by GitHub
parent 158afe088d
commit 8720e435f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 214 additions and 110 deletions

View File

@ -1 +1 @@
version = '1.0.19'
version = '1.0.20'

77
modules/async_worker.py Normal file
View 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()

View File

@ -1,6 +1,5 @@
import os
import random
import cv2
import einops
import torch
import numpy as np
@ -8,12 +7,11 @@ import numpy as np
import comfy.model_management
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 comfy.sample import prepare_mask, broadcast_cond, load_additional_models, cleanup_additional_models
from modules.samplers_advanced import KSampler, KSamplerWithRefiner
from modules.adm_patch import patch_negative_adm
from modules.cv2win32 import show_preview
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 = einops.rearrange(x_sample, 'b c h w -> b h w c')
x_sample = x_sample.cpu().numpy().clip(0, 255).astype(np.uint8)
for i, s in enumerate(x_sample):
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}')
return x_sample[0]
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)
def callback(step, x0, x, total_steps):
if callback_function is not None:
callback_function(step, x0, x, total_steps)
y = None
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)
sigmas = None
@ -197,10 +192,11 @@ def ksampler_with_refiner(model, positive, negative, refiner, refiner_positive,
pbar = comfy.utils.ProgressBar(steps)
def callback(step, x0, x, total_steps):
if callback_function is not None:
callback_function(step, x0, x, total_steps)
y = None
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)
sigmas = None

View File

@ -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
View 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)

View File

@ -8,8 +8,8 @@ Pillow==9.2.0
scipy==1.9.3
tqdm==4.64.1
psutil==5.9.5
opencv-python==4.7.0.72
numpy==1.23.5
pytorch_lightning==1.9.4
omegaconf==2.2.3
gradio==3.39.0
pygit2==1.12.2

View File

@ -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
* Unlock to allow changing model.

View File

@ -1,65 +1,49 @@
import gradio as gr
import sys
import time
import modules.path
import random
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.cv2win32 import close_all_preview, save_image
from modules.util import generate_temp_filename
from modules.sdxl_styles import style_keys, aspect_ratios
def generate_clicked(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, progress=gr.Progress()):
def generate_clicked(*args):
yield gr.update(interactive=False), \
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)
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):
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
while not finished:
time.sleep(0.01)
if len(worker.outputs) > 0:
flag, product = worker.outputs.pop(0)
if flag == 'preview':
percentage, title, image = product
yield gr.update(interactive=False), \
gr.update(visible=True, value=modules.html.make_progress_html(percentage, title)), \
gr.update(visible=True, value=image) if image is not None else gr.update(), \
gr.update(visible=False)
if flag == 'results':
yield gr.update(interactive=True), \
gr.update(visible=False), \
gr.update(visible=False), \
gr.update(visible=True, value=product)
finished = True
return
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 gr.Row():
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.Column(scale=0.85):
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
]
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)