Spaces:
Runtime error
Runtime error
import threading | |
import gc | |
import torch | |
import math | |
import time | |
import pathlib | |
from pathlib import Path | |
buffer = [] | |
outputs = [] | |
results = [] | |
metadatastrings = [] | |
current_task = 0 | |
interrupt_ruined_processing = False | |
def worker(): | |
global buffer, outputs | |
import json | |
import os | |
import shared | |
import random | |
from modules.prompt_processing import process_metadata, process_prompt, parse_loras | |
from PIL import Image | |
from PIL.PngImagePlugin import PngInfo | |
from modules.util import generate_temp_filename, TimeIt, get_checkpoint_hashes, get_lora_hashes | |
import modules.pipelines | |
from shared import settings | |
pipeline = modules.pipelines.update( | |
{"base_model_name": settings.default_settings.get("base_model")} | |
) | |
if not pipeline == None: | |
pipeline.load_base_model(settings.default_settings.get("base_model")) | |
def job_start(gen_data): | |
shared.state["preview_grid"] = None | |
shared.state["preview_total"] = max(gen_data["image_total"], 1) | |
shared.state["preview_count"] = 0 | |
def job_stop(): | |
shared.state["preview_grid"] = None | |
shared.state["preview_total"] = 0 | |
shared.state["preview_count"] = 0 | |
def _process(gen_data): | |
global results, metadatastrings | |
gen_data = process_metadata(gen_data) | |
pipeline = modules.pipelines.update(gen_data) | |
if pipeline == None: | |
print(f"ERROR: No pipeline") | |
return | |
try: | |
# See if pipeline wants to pre-parse gen_data | |
gen_data = pipeline.parse_gen_data(gen_data) | |
except: | |
pass | |
image_number = gen_data["image_number"] | |
loras = [] | |
for lora_data in gen_data["loras"] if gen_data["loras"] is not None else []: | |
w, l = lora_data[1].split(" - ", 1) | |
loras.append((l, float(w))) | |
parsed_loras, pos_stripped, neg_stripped = parse_loras( | |
gen_data["prompt"], gen_data["negative"] | |
) | |
loras.extend(parsed_loras) | |
if "silent" not in gen_data: | |
outputs.append( | |
[ | |
gen_data["task_id"], | |
"preview", | |
(-1, f"Loading base model: {gen_data['base_model_name']}", None), | |
] | |
) | |
gen_data["modelhash"] = pipeline.load_base_model(gen_data["base_model_name"]) | |
if "silent" not in gen_data: | |
outputs.append([gen_data["task_id"], "preview", (-1, f"Loading LoRA models ...", None)]) | |
pipeline.load_loras(loras) | |
# FIXME move this into get_perf_options? | |
if ( | |
gen_data["performance_selection"] | |
== shared.performance_settings.CUSTOM_PERFORMANCE | |
): | |
steps = gen_data["custom_steps"] | |
else: | |
perf_options = shared.performance_settings.get_perf_options( | |
gen_data["performance_selection"] | |
).copy() | |
perf_options.update(gen_data) | |
gen_data = perf_options | |
steps = gen_data["custom_steps"] | |
gen_data["steps"] = steps | |
if ( | |
gen_data["aspect_ratios_selection"] | |
== shared.resolution_settings.CUSTOM_RESOLUTION | |
): | |
width, height = (gen_data["custom_width"], gen_data["custom_height"]) | |
else: | |
width, height = shared.resolution_settings.aspect_ratios[ | |
gen_data["aspect_ratios_selection"] | |
] | |
if "width" in gen_data: | |
width = gen_data["width"] | |
else: | |
gen_data["width"] = width | |
if "height" in gen_data: | |
height = gen_data["height"] | |
else: | |
gen_data["height"] = height | |
if gen_data["cn_selection"] == "Img2Img" or gen_data["cn_type"] == "Img2img": | |
if gen_data["input_image"]: | |
width = gen_data["input_image"].width | |
height = gen_data["input_image"].height | |
else: | |
print(f"WARNING: CheatCode selected but no Input image selected. Ignoring PowerUp!") | |
gen_data["cn_selection"] = "None" | |
gen_data["cn_type"] = "None" | |
seed = gen_data["seed"] | |
max_seed = 2**32 | |
if not isinstance(seed, int) or seed < 0: | |
seed = random.randint(0, max_seed) | |
seed = seed % max_seed | |
all_steps = steps * max(image_number, 1) | |
with open("render.txt") as f: | |
lines = f.readlines() | |
status = random.choice(lines) | |
status = f"{status}" | |
class InterruptProcessingException(Exception): | |
pass | |
def callback(step, x0, x, total_steps, y): | |
global status, interrupt_ruined_processing | |
if interrupt_ruined_processing: | |
shared.state["interrupted"] = True | |
interrupt_ruined_processing = False | |
raise InterruptProcessingException() | |
# If we only generate 1 image, skip the last preview | |
if ( | |
(not gen_data["generate_forever"]) | |
and shared.state["preview_total"] == 1 | |
and steps == step | |
): | |
return | |
done_steps = i * steps + step | |
try: | |
status | |
except NameError: | |
status = None | |
if step % 10 == 0 or status == None: | |
status = random.choice(lines) | |
grid_xsize = math.ceil(math.sqrt(shared.state["preview_total"])) | |
grid_ysize = math.ceil(shared.state["preview_total"] / grid_xsize) | |
grid_max = max(grid_xsize, grid_ysize) | |
pwidth = int(width * grid_xsize / grid_max) | |
pheight = int(height * grid_ysize / grid_max) | |
if shared.state["preview_grid"] is None: | |
shared.state["preview_grid"] = Image.new("RGB", (pwidth, pheight)) | |
if y is not None: | |
if isinstance(y, Image.Image): | |
image = y | |
elif isinstance(y, str): | |
image = Image.open(y) | |
else: | |
image = Image.fromarray(y) | |
grid_xpos = int( | |
(shared.state["preview_count"] % grid_xsize) * (pwidth / grid_xsize) | |
) | |
grid_ypos = int( | |
math.floor(shared.state["preview_count"] / grid_xsize) | |
* (pheight / grid_ysize) | |
) | |
image = image.resize((int(width / grid_max), int(height / grid_max))) | |
shared.state["preview_grid"].paste(image, (grid_xpos, grid_ypos)) | |
preview = shared.path_manager.model_paths["temp_preview_path"] | |
else: | |
preview = None | |
shared.state["preview_grid"].save( | |
shared.path_manager.model_paths["temp_preview_path"], | |
optimize=True, | |
quality=35 if step < total_steps else 70, | |
) | |
outputs.append( | |
[ | |
gen_data["task_id"], | |
"preview", | |
( | |
int( | |
100 | |
* (gen_data["index"][0] + done_steps / all_steps) | |
/ max(gen_data["index"][1], 1) | |
), | |
f"{status} - {step}/{total_steps}", | |
preview, | |
), | |
] | |
) | |
# TODO: this should be an "inital ok gen_data" at the beginning of the function | |
if "input_image" not in gen_data: | |
gen_data["input_image"] = None | |
if "main_view" not in gen_data: | |
gen_data["main_view"] = None | |
stop_batch = False | |
for i in range(max(image_number, 1)): | |
p_txt, n_txt = process_prompt( | |
gen_data["style_selection"], pos_stripped, neg_stripped, gen_data | |
) | |
gen_data["positive_prompt"] = p_txt | |
gen_data["negative_prompt"] = n_txt | |
gen_data["seed"] = seed # Update seed | |
start_step = 0 | |
denoise = None | |
with TimeIt("Pipeline process"): | |
try: | |
imgs = pipeline.process( | |
gen_data=gen_data, | |
callback=callback if "silent" not in gen_data else None, | |
) | |
except InterruptProcessingException as iex: | |
stop_batch = True | |
imgs = [] | |
for x in imgs: | |
folder=shared.path_manager.model_paths["temp_outputs_path"] | |
local_temp_filename = generate_temp_filename( | |
folder=folder, | |
extension="png", | |
) | |
dir_path = Path(local_temp_filename).parent | |
dir_path.mkdir(parents=True, exist_ok=True) | |
metadata = None | |
prompt = { | |
"Prompt": p_txt, | |
"Negative": n_txt, | |
"steps": steps, | |
"cfg": gen_data["cfg"], | |
"width": width, | |
"height": height, | |
"seed": seed, | |
"sampler_name": gen_data["sampler_name"], | |
"scheduler": gen_data["scheduler"], | |
"base_model_name": gen_data["base_model_name"], | |
"base_model_hash": get_checkpoint_hashes(gen_data["base_model_name"])['SHA256'], | |
"loras": [[f"{get_lora_hashes(lora[0])['SHA256']}", f"{lora[1]} - {lora[0]}"] for lora in loras], | |
"start_step": start_step, | |
"denoise": denoise, | |
"clip_skip": gen_data["clip_skip"], | |
"software": "RuinedFooocus", | |
} | |
metadata = PngInfo() | |
# if True: | |
# def handle_whitespace(string: str): | |
# return ( | |
# string.strip() | |
# .replace("\n", " ") | |
# .replace("\r", " ") | |
# .replace("\t", " ") | |
# ) | |
# comment = f"{handle_whitespace(p_txt)}\nNegative prompt: {handle_whitespace(n_txt)}\nSteps: {round(steps, 1)}, Sampler: {gen_data['sampler_name']} {gen_data['scheduler']}, CFG Scale: {float(gen_data['cfg'])}, Seed: {seed}, Size: {width}x{height}, Model hash: {model_hash(Path(shared.path_manager.model_paths['modelfile_path']) / gen_data['base_model_name'])}, Model: {gen_data['base_model_name']}, Version: RuinedFooocus" | |
# metadata.add_text("parameters", comment) | |
# else: | |
metadata.add_text("parameters", json.dumps(prompt)) | |
if "preview_count" not in shared.state: | |
shared.state["preview_count"] = 0 | |
shared.state["preview_count"] += 1 | |
if isinstance(x, str) or isinstance( | |
x, (pathlib.WindowsPath, pathlib.PosixPath) | |
): | |
local_temp_filename = x | |
else: | |
if not isinstance(x, Image.Image): | |
x = Image.fromarray(x) | |
x.save(local_temp_filename, pnginfo=metadata) | |
try: | |
metadata = { | |
"parameters": json.dumps(prompt), | |
"file_path": str(Path(local_temp_filename).relative_to(folder)) | |
} | |
if "browser" in shared.shared_cache: | |
shared.shared_cache["browser"].add_image( | |
local_temp_filename, | |
Path(local_temp_filename).relative_to(folder), | |
metadata, | |
commit=True | |
) | |
except: | |
pass | |
results.append(local_temp_filename) | |
metadatastrings.append(json.dumps(prompt)) | |
shared.state["last_image"] = local_temp_filename | |
seed += 1 | |
if stop_batch: | |
break | |
return | |
def reset_preview(): | |
shared.state["preview_grid"] = None | |
shared.state["preview_count"] = 0 | |
def process(gen_data): | |
global results, metadatastrings | |
# Check some needed items | |
if not "image_total" in gen_data: | |
gen_data["image_total"] = 1 | |
if not "generate_forever" in gen_data: | |
gen_data["generate_forever"] = False | |
shared.state["preview_total"] = max(gen_data["image_total"], 1) | |
while True: | |
reset_preview() | |
results = [] | |
gen_data["index"] = (0, (gen_data["image_total"])) | |
if isinstance(gen_data["prompt"], list): | |
tmp_data = gen_data.copy() | |
for prompt in gen_data["prompt"]: | |
tmp_data["prompt"] = prompt | |
if gen_data["generate_forever"]: | |
reset_preview() | |
_process(tmp_data) | |
if shared.state["interrupted"]: | |
break | |
tmp_data["index"] = (tmp_data["index"][0] + 1, tmp_data["index"][1]) | |
else: | |
gen_data["index"] = (0, 1) | |
_process(gen_data) | |
metadatastrings = [] | |
if not (gen_data["generate_forever"] and shared.state["interrupted"] == False): | |
break | |
# Prepend preview-grid (maybe) | |
if ( | |
"preview_grid" in shared.state and | |
shared.state["preview_grid"] is not None | |
and shared.state["preview_total"] > 1 | |
and ("show_preview" not in gen_data or gen_data["show_preview"] == True) | |
and not gen_data["generate_forever"] | |
): | |
results = [ | |
shared.path_manager.model_paths["temp_preview_path"] | |
] + results | |
outputs.append([gen_data["task_id"], "results", results]) | |
def txt2txt_process(gen_data): | |
pipeline = modules.pipelines.update(gen_data) | |
if pipeline == None: | |
print(f"ERROR: No pipeline") | |
return | |
try: | |
# See if pipeline wants to pre-parse gen_data | |
gen_data = pipeline.parse_gen_data(gen_data) | |
except: | |
pass | |
results = pipeline.process(gen_data) | |
outputs.append([gen_data["task_id"], "results", results]) | |
def handler(gen_data): | |
match gen_data["task_type"]: | |
case "process": | |
process(gen_data) | |
case "api_process": | |
gen_data["silent"] = True | |
process(gen_data) | |
case "llama": | |
txt2txt_process(gen_data) | |
case _: | |
print(f"WARN: Unknown task_type: {gen_data['task_type']}") | |
while True: | |
time.sleep(0.01) | |
if len(buffer) > 0: | |
task = buffer.pop(0) | |
handler(task) | |
gc.collect() | |
if torch.cuda.is_available(): | |
torch.cuda.empty_cache() | |
torch.cuda.ipc_collect() | |
# Use this to add a task, then use task_result() to get data from the pipeline | |
def add_task(gen_data): | |
global current_task, buffer | |
current_task += 1 | |
task_id = current_task | |
gen_data["task_id"] = task_id | |
buffer.append(gen_data.copy()) | |
return task_id | |
# Pipelines use this to add results | |
def add_result(task_id, flag, product): | |
global outputs | |
outputs.append([task_id, flag, product]) | |
# Use the task_id from add_task() to wait for data | |
def task_result(task_id): | |
global outputs | |
while True: | |
if not outputs: | |
time.sleep(0.1) | |
continue | |
if outputs[0][0] == task_id: | |
id, flag, product = outputs.pop(0) | |
break | |
return (flag, product) | |
threading.Thread(target=worker, daemon=True).start() | |