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() | |