From 961c22c58b3e73a7f00e11cc59dd6d11172897da Mon Sep 17 00:00:00 2001 From: Konie Date: Tue, 14 Nov 2023 15:52:40 +0800 Subject: [PATCH] Merge for Fooocus v2.1.806 Change stop api to POST Preload pipeline when start web app --- README.md | 9 +- config.txt | 12 ++ config_modification_tutorial.txt | 72 ++++++++++ fooocus_api_version.py | 2 +- fooocusapi/api.py | 20 +-- fooocusapi/api_utils.py | 26 ++-- fooocusapi/models.py | 9 +- fooocusapi/parameters.py | 9 +- fooocusapi/repositories_versions.py | 4 +- fooocusapi/worker.py | 208 +++++++++++++++++++--------- main.py | 51 ++++--- predict.py | 2 +- user_path_config-deprecated.txt | 66 +++++++++ 13 files changed, 374 insertions(+), 116 deletions(-) create mode 100644 config.txt create mode 100644 config_modification_tutorial.txt create mode 100644 user_path_config-deprecated.txt diff --git a/README.md b/README.md index 2ce9ede..82cc876 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ FastAPI powered API for [Fooocus](https://github.com/lllyasviel/Fooocus) -Currently loaded Fooocus version: 2.1.781 +Currently loaded Fooocus version: 2.1.806 ### Run with Replicate Now you can use Fooocus-API by Replicate, the model is in [konieshadow/fooocus-api](https://replicate.com/konieshadow/fooocus-api). @@ -16,7 +16,7 @@ With preset: I believe this is the easiest way to generate image with Fooocus's power. ### Reuse model files from Fooocus -You can simple copy `user_path_config.txt` file from your local Fooocus folder to Fooocus-API's root folder. See [Customization](https://github.com/lllyasviel/Fooocus#customization) for details. +You can simple copy `config.txt` file from your local Fooocus folder to Fooocus-API's root folder. See [Customization](https://github.com/lllyasviel/Fooocus#customization) for details. ### Start app Need python version >= 3.10, or use conda to create a new env. @@ -110,6 +110,11 @@ Query async generation request results, return job progress and generation resul Query job queue info, include running job count, finished job count and last job id. +#### Stop Generation task +> POST /v1/generation/stop + +Stop current generation task. + #### Get All Model Names > GET /v1/engines/all-models diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..d178bfd --- /dev/null +++ b/config.txt @@ -0,0 +1,12 @@ +{ + "path_checkpoints": "/home/featurize/work/Fooocus/models/checkpoints", + "path_loras": "/home/featurize/work/Fooocus/models/loras", + "path_embeddings": "/home/featurize/work/Fooocus/models/embeddings", + "path_vae_approx": "/home/featurize/work/Fooocus/models/vae_approx", + "path_upscale_models": "/home/featurize/work/Fooocus/models/upscale_models", + "path_inpaint": "/home/featurize/work/Fooocus/models/inpaint", + "path_controlnet": "/home/featurize/work/Fooocus/models/controlnet", + "path_clip_vision": "/home/featurize/work/Fooocus/models/clip_vision", + "path_fooocus_expansion": "/home/featurize/work/Fooocus/models/prompt_expansion/fooocus_expansion", + "path_outputs": "/home/featurize/work/Fooocus/outputs" +} \ No newline at end of file diff --git a/config_modification_tutorial.txt b/config_modification_tutorial.txt new file mode 100644 index 0000000..1dd5761 --- /dev/null +++ b/config_modification_tutorial.txt @@ -0,0 +1,72 @@ +You can modify your "/home/featurize/work/Fooocus-API/config.txt" using the below keys, formats, and examples. +Do not modify this file. Modifications in this file will not take effect. +This file is a tutorial and example. Please edit "/home/featurize/work/Fooocus-API/config.txt" to really change any settings. +Remember to split the paths with "\\" rather than "\". + + +{ + "path_checkpoints": "/home/featurize/work/Fooocus/models/checkpoints", + "path_loras": "/home/featurize/work/Fooocus/models/loras", + "path_embeddings": "/home/featurize/work/Fooocus/models/embeddings", + "path_vae_approx": "/home/featurize/work/Fooocus/models/vae_approx", + "path_upscale_models": "/home/featurize/work/Fooocus/models/upscale_models", + "path_inpaint": "/home/featurize/work/Fooocus/models/inpaint", + "path_controlnet": "/home/featurize/work/Fooocus/models/controlnet", + "path_clip_vision": "/home/featurize/work/Fooocus/models/clip_vision", + "path_fooocus_expansion": "/home/featurize/work/Fooocus/models/prompt_expansion/fooocus_expansion", + "path_outputs": "/home/featurize/work/Fooocus/outputs", + "default_model": "juggernautXL_version6Rundiffusion.safetensors", + "default_refiner": "None", + "default_refiner_switch": 0.5, + "default_lora": "sd_xl_offset_example-lora_1.0.safetensors", + "default_lora_weight": 0.1, + "default_cfg_scale": 4.0, + "default_sample_sharpness": 2, + "default_sampler": "dpmpp_2m_sde_gpu", + "default_scheduler": "karras", + "default_styles": [ + "Fooocus V2", + "Fooocus Enhance", + "Fooocus Sharp" + ], + "default_prompt_negative": "", + "default_prompt": "", + "default_advanced_checkbox": false, + "default_image_number": 2, + "checkpoint_downloads": { + "juggernautXL_version6Rundiffusion.safetensors": "https://huggingface.co/lllyasviel/fav_models/resolve/main/fav/juggernautXL_version6Rundiffusion.safetensors" + }, + "lora_downloads": { + "sd_xl_offset_example-lora_1.0.safetensors": "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_offset_example-lora_1.0.safetensors" + }, + "embeddings_downloads": {}, + "available_aspect_ratios": [ + "704*1408", + "704*1344", + "768*1344", + "768*1280", + "832*1216", + "832*1152", + "896*1152", + "896*1088", + "960*1088", + "960*1024", + "1024*1024", + "1024*960", + "1088*960", + "1088*896", + "1152*896", + "1152*832", + "1216*832", + "1280*768", + "1344*768", + "1344*704", + "1408*704", + "1472*704", + "1536*640", + "1600*640", + "1664*576", + "1728*576" + ], + "default_aspect_ratio": "1152*896" +} \ No newline at end of file diff --git a/fooocus_api_version.py b/fooocus_api_version.py index 12034da..59775f3 100644 --- a/fooocus_api_version.py +++ b/fooocus_api_version.py @@ -1 +1 @@ -version = '0.3.11' +version = '0.3.12' diff --git a/fooocusapi/api.py b/fooocusapi/api.py index a4e7d72..047b409 100644 --- a/fooocusapi/api.py +++ b/fooocusapi/api.py @@ -160,17 +160,23 @@ def job_queue(): return JobQueueInfo(running_size=len(task_queue.queue), finished_size=len(task_queue.history), last_job_id=task_queue.last_seq) +@app.post("/v1/generation/stop", response_model=StopResponse, description="Job stoping") +def stop(): + stop_worker() + return StopResponse(msg="success") + + @app.get("/v1/engines/all-models", response_model=AllModelNamesResponse, description="Get all filenames of base model and lora") def all_models(): - import modules.path as path - return AllModelNamesResponse(model_filenames=path.model_filenames, lora_filenames=path.lora_filenames) + import modules.config as config + return AllModelNamesResponse(model_filenames=config.model_filenames, lora_filenames=config.lora_filenames) @app.post("/v1/engines/refresh-models", response_model=AllModelNamesResponse, description="Refresh local files and get all filenames of base model and lora") def refresh_models(): - import modules.path as path - path.update_all_model_names() - return AllModelNamesResponse(model_filenames=path.model_filenames, lora_filenames=path.lora_filenames) + import modules.config as config + config.update_all_model_names() + return AllModelNamesResponse(model_filenames=config.model_filenames, lora_filenames=config.lora_filenames) @app.get("/v1/engines/styles", response_model=List[str], description="Get all legal Fooocus styles") @@ -178,10 +184,6 @@ def all_styles(): from modules.sdxl_styles import legal_style_names return legal_style_names -@app.get("/v1/generation/stop", response_model=StopResponse, description="Job stoping") -def stop(): - stop_worker() - return StopResponse(msg="success") app.mount("/files", StaticFiles(directory=file_utils.output_dir), name="files") diff --git a/fooocusapi/api_utils.py b/fooocusapi/api_utils.py index d9a2cc7..79d4961 100644 --- a/fooocusapi/api_utils.py +++ b/fooocusapi/api_utils.py @@ -8,10 +8,10 @@ from PIL import Image from fooocusapi.file_utils import get_file_serve_url, output_file_to_base64img, output_file_to_bytesimg from fooocusapi.models import AsyncJobResponse, AsyncJobStage, GeneratedImageResult, GenerationFinishReason, ImgInpaintOrOutpaintRequest, ImgPromptRequest, ImgUpscaleOrVaryRequest, Text2ImgRequest -from fooocusapi.parameters import ImageGenerationParams, ImageGenerationResult, available_aspect_ratios, default_aspect_ratio, inpaint_model_version, default_sampler, default_scheduler, default_base_model_name, default_refiner_model_name +from fooocusapi.parameters import ImageGenerationParams, ImageGenerationResult, available_aspect_ratios, default_aspect_ratio, default_inpaint_engine_version, default_sampler, default_scheduler, default_base_model_name, default_refiner_model_name from fooocusapi.task_queue import QueueTask import modules.flags as flags -import modules.config as path +import modules.config as config from modules.sdxl_styles import legal_style_names @@ -47,17 +47,17 @@ def read_input_image(input_image: UploadFile) -> np.ndarray: def req_to_params(req: Text2ImgRequest) -> ImageGenerationParams: if req.base_model_name is not None: - if req.base_model_name not in path.model_filenames: + if req.base_model_name not in config.model_filenames: print(f"[Warning] Wrong base_model_name input: {req.base_model_name}, using default") req.base_model_name = default_base_model_name if req.refiner_model_name is not None and req.refiner_model_name != 'None': - if req.refiner_model_name not in path.model_filenames: + if req.refiner_model_name not in config.model_filenames: print(f"[Warning] Wrong refiner_model_name input: {req.refiner_model_name}, using default") req.refiner_model_name = default_refiner_model_name for l in req.loras: - if l.model_name != 'None' and l.model_name not in path.lora_filenames: + if l.model_name != 'None' and l.model_name not in config.lora_filenames: print(f"[Warning] Wrong lora model_name input: {l.model_name}, using 'None'") l.model_name = 'None' @@ -125,15 +125,15 @@ def req_to_params(req: Text2ImgRequest) -> ImageGenerationParams: print(f"[Warning] Wrong scheduler_name input: {adp.scheduler_name}, using default") adp.scheduler_name = default_scheduler - if adp.inpaint_engine not in ['v1', 'v2.5']: + if adp.inpaint_engine not in flags.inpaint_engine_versions: print(f"[Warning] Wrong inpaint_engine input: {adp.inpaint_engine}, using default") - adp.inpaint_engine = inpaint_model_version + adp.inpaint_engine = flags.default_inpaint_engine_version advanced_params = [adp.adm_scaler_positive, adp.adm_scaler_negative, adp.adm_scaler_end, adp.adaptive_cfg, adp.sampler_name, adp.scheduler_name, False, adp.overwrite_step, adp.overwrite_switch, adp.overwrite_width, adp.overwrite_height, adp.overwrite_vary_strength, adp.overwrite_upscale_strength, adp.mixing_image_prompt_and_vary_upscale, adp.mixing_image_prompt_and_inpaint, - adp.debugging_cn_preprocessor, adp.controlnet_softness, adp.canny_low_threshold, adp.canny_high_threshold, adp.inpaint_engine, + adp.debugging_cn_preprocessor, adp.skipping_cn_preprocessor, adp.controlnet_softness, adp.canny_low_threshold, adp.canny_high_threshold, adp.inpaint_engine, adp.refiner_swap_method, adp.freeu_enabled, adp.freeu_b1, adp.freeu_b2, adp.freeu_s1, adp.freeu_s2] return ImageGenerationParams(prompt=prompt, @@ -184,8 +184,16 @@ def generation_output(results: QueueTask | List[ImageGenerationResult], streamin job_result=job_result) if streaming_output: - if len(results) == 0 or results[0].finish_reason != GenerationFinishReason.success: + if len(results) == 0: return Response(status_code=500) + result = results[0] + if result.finish_reason == GenerationFinishReason.queue_is_full: + return Response(status_code=409, content=result.finish_reason.value) + elif result.finish_reason == GenerationFinishReason.user_cancel: + return Response(status_code=400, content=result.finish_reason.value) + elif result.finish_reason == GenerationFinishReason.error: + return Response(status_code=500, content=result.finish_reason.value) + bytes = output_file_to_bytesimg(results[0].im) return Response(bytes, media_type='image/png') else: diff --git a/fooocusapi/models.py b/fooocusapi/models.py index 1909392..3fce1ef 100644 --- a/fooocusapi/models.py +++ b/fooocusapi/models.py @@ -24,6 +24,7 @@ class Lora(BaseModel): class PerfomanceSelection(str, Enum): speed = 'Speed' quality = 'Quality' + extreme_speed = 'Extreme Speed' class UpscaleOrVaryMethod(str, Enum): @@ -42,9 +43,10 @@ class OutpaintExpansion(str, Enum): class ControlNetType(str, Enum): - cn_ip = 'Image Prompt' - cn_canny = 'PyraCanny' - cn_cpds = 'CPDS' + cn_ip = "ImagePrompt" + cn_ip_face = "FaceSwap" + cn_canny = "PyraCanny" + cn_cpds = "CPDS" class ImagePrompt(BaseModel): @@ -72,6 +74,7 @@ class AdvancedParams(BaseModel): mixing_image_prompt_and_vary_upscale: bool = Field(False, description="Mixing Image Prompt and Vary/Upscale") mixing_image_prompt_and_inpaint: bool = Field(False, description="Mixing Image Prompt and Inpaint") debugging_cn_preprocessor: bool = Field(False, description="Debug Preprocessors") + skipping_cn_preprocessor: bool = Field(False, description="Skip Preprocessors") controlnet_softness: float = Field(0.25, description="Softness of ControlNet", ge=0.0, le=1.0) canny_low_threshold: int = Field(64, description="Canny Low Threshold", ge=1, le=255) canny_high_threshold: int = Field(128, description="Canny High Threshold", ge=1, le=255) diff --git a/fooocusapi/parameters.py b/fooocusapi/parameters.py index fa79f0b..cf3264b 100644 --- a/fooocusapi/parameters.py +++ b/fooocusapi/parameters.py @@ -5,7 +5,7 @@ from fooocusapi.task_queue import TaskType -inpaint_model_version = 'v1' +default_inpaint_engine_version = 'v2.6' defualt_styles = ['Fooocus V2', 'Fooocus Enhance', 'Fooocus Sharp'] @@ -78,7 +78,7 @@ class ImageGenerationParams(object): def __init__(self, prompt: str, negative_prompt: str, style_selections: List[str], - performance_selection: List[str], + performance_selection: str, aspect_ratios_selection: str, image_number: int, image_seed: int | None, @@ -129,10 +129,11 @@ def __init__(self, prompt: str, mixing_image_prompt_and_vary_upscale = False mixing_image_prompt_and_inpaint = False debugging_cn_preprocessor = False + skipping_cn_preprocessor = False controlnet_softness = 0.25 canny_low_threshold = 64 canny_high_threshold = 128 - inpaint_engine = inpaint_model_version + inpaint_engine = default_inpaint_engine_version refiner_swap_method = 'joint' freeu_enabled = False freeu_b1, freeu_b2, freeu_s1, freeu_s2 = [None] * 4 @@ -140,7 +141,7 @@ def __init__(self, prompt: str, scheduler_name, generate_image_grid, overwrite_step, overwrite_switch, overwrite_width, overwrite_height, overwrite_vary_strength, overwrite_upscale_strength, mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint, - debugging_cn_preprocessor, controlnet_softness, canny_low_threshold, canny_high_threshold, inpaint_engine, + debugging_cn_preprocessor, skipping_cn_preprocessor, controlnet_softness, canny_low_threshold, canny_high_threshold, inpaint_engine, refiner_swap_method, freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2] else: self.advanced_params = advanced_params diff --git a/fooocusapi/repositories_versions.py b/fooocusapi/repositories_versions.py index a92683d..cf54f8c 100644 --- a/fooocusapi/repositories_versions.py +++ b/fooocusapi/repositories_versions.py @@ -1,5 +1,5 @@ import os -fooocus_version = '2.1.781' +fooocus_version = '2.1.806' fooocus_commit_hash = os.environ.get( - 'FOOOCUS_COMMIT_HASH', "a9bb1079cf44e892c8cabaf7d345db8b649ec5ac") + 'FOOOCUS_COMMIT_HASH', "c9a5e729d91ba3772d421a5db7bf79497011e59f") diff --git a/fooocusapi/worker.py b/fooocusapi/worker.py index d750f35..2bc19ef 100644 --- a/fooocusapi/worker.py +++ b/fooocusapi/worker.py @@ -5,7 +5,7 @@ import torch from typing import List from fooocusapi.file_utils import save_output_file -from fooocusapi.parameters import inpaint_model_version, GenerationFinishReason, ImageGenerationParams, ImageGenerationResult +from fooocusapi.parameters import default_inpaint_engine_version, GenerationFinishReason, ImageGenerationParams, ImageGenerationResult from fooocusapi.task_queue import QueueTask, TaskQueue, TaskOutputs save_log = True @@ -33,11 +33,13 @@ def process_generate(queue_task: QueueTask, params: ImageGenerationParams) -> Li import modules.flags as flags import modules.core as core import modules.inpaint_worker as inpaint_worker - import modules.config as path + import modules.config as config import modules.advanced_parameters as advanced_parameters import modules.constants as constants import fooocus_extras.preprocessors as preprocessors import fooocus_extras.ip_adapter as ip_adapter + import fooocus_extras.face_crop as face_crop + import fcbh.model_management as model_management from modules.util import remove_empty_str, resize_image, HWC3, set_image_shape_ceil, get_image_shape_ceil, get_shape_ceil, resample_image from modules.private_logger import log from modules.upscaler import perform_upscale @@ -63,18 +65,21 @@ def progressbar(number, text): outputs.append(['preview', (number, text, None)]) queue_task.set_progress(number, text) - def make_results_from_outputs(): - results: List[ImageGenerationResult] = [] - for item in outputs.outputs: - seed = -1 if len(item) < 3 else item[2] - if item[0] == 'results': - for im in item[1]: - if isinstance(im, np.ndarray): - img_filename = save_output_file(im) - results.append(ImageGenerationResult(im=img_filename, seed=seed, finish_reason=GenerationFinishReason.success)) + def yield_result(imgs, tasks): + if not isinstance(imgs, list): + imgs = [imgs] + + results = [] + for i, im in enumerate(imgs): + seed = -1 if len(tasks) == 0 else tasks[i]['task_seed'] + img_filename = save_output_file(im) + results.append(ImageGenerationResult(im=img_filename, seed=seed, finish_reason=GenerationFinishReason.success)) queue_task.set_result(results, False) task_queue.finish_task(queue_task.seq) print(f"[Task Queue] Finish task, seq={queue_task.seq}") + + outputs.append(['results', imgs]) + pipeline.prepare_text_encoder(async_call=True) return results try: @@ -121,7 +126,7 @@ def make_results_from_outputs(): image_seed = refresh_seed(image_seed is None, image_seed) - cn_tasks = {flags.cn_ip: [], flags.cn_canny: [], flags.cn_cpds: []} + cn_tasks = {x: [] for x in flags.ip_list} for img_prompt in params.image_prompts: cn_img, cn_stop, cn_weight, cn_type = img_prompt cn_tasks[cn_type].append([cn_img, cn_stop, cn_weight]) @@ -131,7 +136,7 @@ def make_results_from_outputs(): # Fooocus async_worker.py code start outpaint_selections = [o.lower() for o in outpaint_selections] - loras_raw = copy.deepcopy(loras) + base_model_additional_loras = [] raw_style_selections = copy.deepcopy(style_selections) uov_method = uov_method.lower() @@ -143,6 +148,40 @@ def make_results_from_outputs(): use_style = len(style_selections) > 0 + if base_model_name == refiner_model_name: + print(f'Refiner disabled because base model and refiner are same.') + refiner_model_name = 'None' + + assert performance_selection in ['Speed', 'Quality', 'Extreme Speed'] + + steps = 30 + + if performance_selection == 'Speed': + steps = 30 + + if performance_selection == 'Quality': + steps = 60 + + if performance_selection == 'Extreme Speed': + print('Enter LCM mode.') + progressbar(1, 'Downloading LCM components ...') + base_model_additional_loras += [(config.downloading_sdxl_lcm_lora(), 1.0)] + + if refiner_model_name != 'None': + print(f'Refiner disabled in LCM mode.') + + refiner_model_name = 'None' + sampler_name = advanced_parameters.sampler_name = 'lcm' + scheduler_name = advanced_parameters.scheduler_name = 'lcm' + patch.sharpness = sharpness = 0.0 + cfg_scale = guidance_scale = 1.0 + patch.adaptive_cfg = advanced_parameters.adaptive_cfg = 1.0 + refiner_switch = 1.0 + patch.positive_adm_scale = advanced_parameters.adm_scaler_positive = 1.0 + patch.negative_adm_scale = advanced_parameters.adm_scaler_negative = 1.0 + patch.adm_scaler_end = advanced_parameters.adm_scaler_end = 0.0 + steps = 8 + patch.adaptive_cfg = advanced_parameters.adaptive_cfg print(f'[Parameters] Adaptive CFG = {patch.adaptive_cfg}') @@ -173,16 +212,11 @@ def make_results_from_outputs(): inpaint_head_model_path = None controlnet_canny_path = None controlnet_cpds_path = None - clip_vision_path, ip_negative_path, ip_adapter_path = None, None, None + clip_vision_path, ip_negative_path, ip_adapter_path, ip_adapter_face_path = None, None, None, None seed = int(image_seed) print(f'[Parameters] Seed = {seed}') - if performance_selection == 'Speed': - steps = 30 - else: - steps = 60 - sampler_name = advanced_parameters.sampler_name scheduler_name = advanced_parameters.scheduler_name @@ -200,12 +234,19 @@ def make_results_from_outputs(): if 'fast' in uov_method: skip_prompt_processing = True else: + steps = 18 + if performance_selection == 'Speed': steps = 18 - else: + + if performance_selection == 'Quality': steps = 36 + + if performance_selection == 'Extreme Speed': + steps = 8 + progressbar(1, 'Downloading upscale models ...') - path.downloading_upscale_model() + config.downloading_upscale_model() if (current_tab == 'inpaint' or (current_tab == 'ip' and advanced_parameters.mixing_image_prompt_and_inpaint))\ and isinstance(inpaint_input_image, dict): inpaint_image = inpaint_input_image['image'] @@ -214,8 +255,8 @@ def make_results_from_outputs(): if isinstance(inpaint_image, np.ndarray) and isinstance(inpaint_mask, np.ndarray) \ and (np.any(inpaint_mask > 127) or len(outpaint_selections) > 0): progressbar(1, 'Downloading inpainter ...') - inpaint_head_model_path, inpaint_patch_model_path = path.downloading_inpaint_models(advanced_parameters.inpaint_engine) - loras += [(inpaint_patch_model_path, 1.0)] + inpaint_head_model_path, inpaint_patch_model_path = config.downloading_inpaint_models(advanced_parameters.inpaint_engine) + base_model_additional_loras += [(inpaint_patch_model_path, 1.0)] print(f'[Inpaint] Current inpaint model is {inpaint_patch_model_path}') goals.append('inpaint') if current_tab == 'ip' or \ @@ -224,16 +265,19 @@ def make_results_from_outputs(): goals.append('cn') progressbar(1, 'Downloading control models ...') if len(cn_tasks[flags.cn_canny]) > 0: - controlnet_canny_path = path.downloading_controlnet_canny() + controlnet_canny_path = config.downloading_controlnet_canny() if len(cn_tasks[flags.cn_cpds]) > 0: - controlnet_cpds_path = path.downloading_controlnet_cpds() + controlnet_cpds_path = config.downloading_controlnet_cpds() if len(cn_tasks[flags.cn_ip]) > 0: - clip_vision_path, ip_negative_path, ip_adapter_path = path.downloading_ip_adapters() + clip_vision_path, ip_negative_path, ip_adapter_path = config.downloading_ip_adapters('ip') + if len(cn_tasks[flags.cn_ip_face]) > 0: + clip_vision_path, ip_negative_path, ip_adapter_face_path = config.downloading_ip_adapters('face') progressbar(1, 'Loading control models ...') # Load or unload CNs pipeline.refresh_controlnets([controlnet_canny_path, controlnet_cpds_path]) ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path) + ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_face_path) switch = int(round(steps * refiner_switch)) @@ -270,7 +314,8 @@ def make_results_from_outputs(): extra_negative_prompts = negative_prompts[1:] if len(negative_prompts) > 1 else [] progressbar(3, 'Loading models ...') - pipeline.refresh_everything(refiner_model_name=refiner_model_name, base_model_name=base_model_name, loras=loras) + pipeline.refresh_everything(refiner_model_name=refiner_model_name, base_model_name=base_model_name, + loras=loras, base_model_additional_loras=base_model_additional_loras) progressbar(3, 'Processing prompts ...') tasks = [] @@ -330,8 +375,12 @@ def make_results_from_outputs(): t['c'] = pipeline.clip_encode(texts=t['positive'], pool_top_k=t['positive_top_k']) for i, t in enumerate(tasks): - progressbar(10, f'Encoding negative #{i + 1} ...') - t['uc'] = pipeline.clip_encode(texts=t['negative'], pool_top_k=t['negative_top_k']) + if abs(float(cfg_scale) - 1.0) < 1e-4: + progressbar(10, f'Skipped negative #{i + 1} ...') + t['uc'] = pipeline.clone_cond(t['c']) + else: + progressbar(10, f'Encoding negative #{i + 1} ...') + t['uc'] = pipeline.clip_encode(texts=t['negative'], pool_top_k=t['negative_top_k']) if len(goals) > 0: progressbar(13, 'Image processing ...') @@ -403,9 +452,7 @@ def make_results_from_outputs(): d = [('Upscale (Fast)', '2x')] if save_log: log(uov_input_image, d, single_line_number=1) - outputs.append(['results', [uov_input_image], -1 if len(tasks) == 0 else tasks[0]['task_seed']]) - results = make_results_from_outputs() - return results * image_number + return yield_result(uov_input_image, tasks) tiled = True denoising_strength = 0.382 @@ -456,9 +503,7 @@ def make_results_from_outputs(): pipeline.final_unet.model.diffusion_model.in_inpaint = True if advanced_parameters.debugging_cn_preprocessor: - outputs.append(['results', inpaint_worker.current_task.visualize_mask_processing(), -1 if len(tasks) == 0 else tasks[0]['task_seed']]) - results = make_results_from_outputs() - return results + return yield_result(inpaint_worker.current_task.visualize_mask_processing(), tasks) progressbar(13, 'VAE Inpaint encoding ...') @@ -499,23 +544,25 @@ def make_results_from_outputs(): for task in cn_tasks[flags.cn_canny]: cn_img, cn_stop, cn_weight = task cn_img = resize_image(HWC3(cn_img), width=width, height=height) - cn_img = preprocessors.canny_pyramid(cn_img) + + if not advanced_parameters.skipping_cn_preprocessor: + cn_img = preprocessors.canny_pyramid(cn_img) + cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if advanced_parameters.debugging_cn_preprocessor: - outputs.append(['results', [cn_img], task['task_seed']]) - results = make_results_from_outputs() - return results + return yield_result(cn_img, tasks) for task in cn_tasks[flags.cn_cpds]: cn_img, cn_stop, cn_weight = task cn_img = resize_image(HWC3(cn_img), width=width, height=height) - cn_img = preprocessors.cpds(cn_img) + + if not advanced_parameters.skipping_cn_preprocessor: + cn_img = preprocessors.cpds(cn_img) + cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if advanced_parameters.debugging_cn_preprocessor: - outputs.append(['results', [cn_img], task['task_seed']]) - results = make_results_from_outputs() - return results + return yield_result(cn_img, tasks) for task in cn_tasks[flags.cn_ip]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -523,14 +570,27 @@ def make_results_from_outputs(): # https://github.com/tencent-ailab/IP-Adapter/blob/d580c50a291566bbf9fc7ac0f760506607297e6d/README.md?plain=1#L75 cn_img = resize_image(cn_img, width=224, height=224, resize_mode=0) - task[0] = ip_adapter.preprocess(cn_img) + task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_path) + if advanced_parameters.debugging_cn_preprocessor: + return yield_result(cn_img, tasks) + for task in cn_tasks[flags.cn_ip_face]: + cn_img, cn_stop, cn_weight = task + cn_img = HWC3(cn_img) + + if not advanced_parameters.skipping_cn_preprocessor: + cn_img = face_crop.crop_image(cn_img) + + # https://github.com/tencent-ailab/IP-Adapter/blob/d580c50a291566bbf9fc7ac0f760506607297e6d/README.md?plain=1#L75 + cn_img = resize_image(cn_img, width=224, height=224, resize_mode=0) + + task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_face_path) if advanced_parameters.debugging_cn_preprocessor: - outputs.append(['results', [cn_img], task['task_seed']]) - results = make_results_from_outputs() - return results + return yield_result(cn_img, tasks) - if len(cn_tasks[flags.cn_ip]) > 0: - pipeline.final_unet = ip_adapter.patch_model(pipeline.final_unet, cn_tasks[flags.cn_ip]) + all_ip_tasks = cn_tasks[flags.cn_ip] + cn_tasks[flags.cn_ip_face] + + if len(all_ip_tasks) > 0: + pipeline.final_unet = ip_adapter.patch_model(pipeline.final_unet, all_ip_tasks) if advanced_parameters.freeu_enabled: print(f'FreeU is enabled!') @@ -548,6 +608,23 @@ def make_results_from_outputs(): preparation_time = time.perf_counter() - execution_start_time print(f'Preparation time: {preparation_time:.2f} seconds') + final_sampler_name = sampler_name + final_scheduler_name = scheduler_name + + if scheduler_name == 'lcm': + final_scheduler_name = 'sgm_uniform' + if pipeline.final_unet is not None: + pipeline.final_unet = core.opModelSamplingDiscrete.patch( + pipeline.final_unet, + sampling='lcm', + zsnr=False)[0] + if pipeline.final_refiner_unet is not None: + pipeline.final_refiner_unet = core.opModelSamplingDiscrete.patch( + pipeline.final_refiner_unet, + sampling='lcm', + zsnr=False)[0] + print('Using lcm scheduler.') + outputs.append(['preview', (13, 'Moving model to GPU ...', None)]) def callback(step, x0, x, total_steps, y): @@ -582,8 +659,8 @@ def callback(step, x0, x, total_steps, y): height=height, image_seed=task['task_seed'], callback=callback, - sampler_name=sampler_name, - scheduler_name=scheduler_name, + sampler_name=final_sampler_name, + scheduler_name=final_scheduler_name, latent=initial_latent, denoise=denoising_strength, tiled=tiled, @@ -596,7 +673,6 @@ def callback(step, x0, x, total_steps, y): if inpaint_worker.current_task is not None: imgs = [inpaint_worker.current_task.post_process(x) for x in imgs] - img_filenames = [] for x in imgs: d = [ ('Prompt', task['log_positive_prompt']), @@ -607,25 +683,30 @@ def callback(step, x0, x, total_steps, y): ('Resolution', str((width, height))), ('Sharpness', sharpness), ('Guidance Scale', guidance_scale), - ('ADM Guidance', str((patch.positive_adm_scale, patch.negative_adm_scale))), + ('ADM Guidance', str(( + patch.positive_adm_scale, + patch.negative_adm_scale, + patch.adm_scaler_end))), ('Base Model', base_model_name), ('Refiner Model', refiner_model_name), + ('Refiner Switch', refiner_switch), ('Sampler', sampler_name), ('Scheduler', scheduler_name), ('Seed', task['task_seed']) ] - for n, w in loras_raw: + for n, w in loras: if n != 'None': d.append((f'LoRA [{n}] weight', w)) if save_log: log(x, d, single_line_number=3) - img_filename = save_output_file(x) - img_filenames.append(img_filename) # Fooocus async_worker.py code end - + except model_management.InterruptProcessingException as e: + print("User stopped") results.append(ImageGenerationResult( - im=img_filenames[0], seed=task['task_seed'], finish_reason=GenerationFinishReason.success)) + im=None, seed=task['task_seed'], finish_reason=GenerationFinishReason.user_cancel)) + queue_task.set_result(results, True, str(e)) + break except Exception as e: print('Process error:', e) results.append(ImageGenerationResult( @@ -636,13 +717,10 @@ def callback(step, x0, x, total_steps, y): execution_time = time.perf_counter() - execution_start_time print(f'Generating and saving time: {execution_time:.2f} seconds') - pipeline.prepare_text_encoder(async_call=True) - - if not queue_task.finish_with_error: - queue_task.set_result(results, False) - task_queue.finish_task(queue_task.seq) - print(f"[Task Queue] Finish task, seq={queue_task.seq}") - return results + if queue_task.finish_with_error: + task_queue.finish_task(queue_task.seq) + return queue_task.task_result + return yield_result(imgs, tasks) except Exception as e: print('Worker error:', e) if not queue_task.is_finished: diff --git a/main.py b/main.py index da81797..111c9dc 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ import subprocess import sys from importlib.util import find_spec +from threading import Thread from fooocus_api_version import version from fooocusapi.repositories_versions import fooocus_commit_hash @@ -280,26 +281,27 @@ def prepare_environments(args) -> bool: sys.argv.append('--preset') sys.argv.append(args.preset) - import modules.config as path + import modules.config as config + import modules.flags as flags import fooocusapi.parameters as parameters - parameters.defualt_styles = path.default_styles - parameters.default_base_model_name = path.default_base_model_name - parameters.default_refiner_model_name = path.default_refiner_model_name - parameters.default_refiner_switch = path.default_refiner_switch - parameters.default_lora_name = path.default_lora_name - parameters.default_lora_weight = path.default_lora_weight - parameters.default_cfg_scale = path.default_cfg_scale - parameters.default_prompt_negative = path.default_prompt_negative - parameters.default_aspect_ratio = path.default_aspect_ratio.replace('*', '×') - parameters.available_aspect_ratios = [a.replace('*', '×') for a in path.available_aspect_ratios] + parameters.default_inpaint_engine_version = flags.default_inpaint_engine_version + parameters.defualt_styles = config.default_styles + parameters.default_base_model_name = config.default_base_model_name + parameters.default_refiner_model_name = config.default_refiner_model_name + parameters.default_refiner_switch = config.default_refiner_switch + parameters.default_lora_name = config.default_lora_name + parameters.default_lora_weight = config.default_lora_weight + parameters.default_cfg_scale = config.default_cfg_scale + parameters.default_prompt_negative = config.default_prompt_negative + parameters.default_aspect_ratio = config.default_aspect_ratio.replace('*', '×') + parameters.available_aspect_ratios = [a.replace('*', '×') for a in config.available_aspect_ratios] ini_cbh_args() download_models() if args.preload_pipeline: - print("Preload pipeline") - import modules.default_pipeline as _ + preplaod_pipeline() return True @@ -327,13 +329,13 @@ class Args(object): prepare_environments(args) if load_all_models: - import modules.config as path - from fooocusapi.parameters import inpaint_model_version - path.downloading_upscale_model() - path.downloading_inpaint_models(inpaint_model_version) - path.downloading_controlnet_canny() - path.downloading_controlnet_cpds() - path.downloading_ip_adapters() + import modules.config as config + from fooocusapi.parameters import default_inpaint_engine_version + config.downloading_upscale_model() + config.downloading_inpaint_models(default_inpaint_engine_version) + config.downloading_controlnet_canny() + config.downloading_controlnet_cpds() + config.downloading_ip_adapters() print("[Pre Setup] Finished") @@ -343,6 +345,11 @@ def ini_cbh_args(): return args +def preplaod_pipeline(): + print("Preload pipeline") + import modules.default_pipeline as _ + + if __name__ == "__main__": print(f"Python {sys.version}") print(f"Fooocus-API version: {version}") @@ -370,6 +377,10 @@ def ini_cbh_args(): if prepare_environments(args): sys.argv = [sys.argv[0]] + # Load pipeline in new thread + t = Thread(target=preplaod_pipeline) + t.start() + # Start api server from fooocusapi.api import start_app start_app(args) diff --git a/predict.py b/predict.py index 1e7a286..a2fdc45 100644 --- a/predict.py +++ b/predict.py @@ -29,7 +29,7 @@ def predict( style_selections: str = Input(default=','.join(defualt_styles), description="Fooocus styles applied for image generation, seperated by comma"), performance_selection: str = Input( - default='Speed', description="Performance selection", choices=['Speed', 'Quality']), + default='Speed', description="Performance selection", choices=['Speed', 'Quality', 'Extreme Speed']), aspect_ratios_selection: str = Input( default='1152×896', description="The generated image's size", choices=available_aspect_ratios), image_number: int = Input( diff --git a/user_path_config-deprecated.txt b/user_path_config-deprecated.txt new file mode 100644 index 0000000..978d301 --- /dev/null +++ b/user_path_config-deprecated.txt @@ -0,0 +1,66 @@ +{ + "modelfile_path": "/home/featurize/work/Fooocus/models/checkpoints", + "lorafile_path": "/home/featurize/work/Fooocus/models/loras", + "embeddings_path": "/home/featurize/work/Fooocus/models/embeddings", + "vae_approx_path": "/home/featurize/work/Fooocus/models/vae_approx", + "upscale_models_path": "/home/featurize/work/Fooocus/models/upscale_models", + "inpaint_models_path": "/home/featurize/work/Fooocus/models/inpaint", + "controlnet_models_path": "/home/featurize/work/Fooocus/models/controlnet", + "clip_vision_models_path": "/home/featurize/work/Fooocus/models/clip_vision", + "fooocus_expansion_path": "/home/featurize/work/Fooocus/models/prompt_expansion/fooocus_expansion", + "temp_outputs_path": "/home/featurize/work/Fooocus/outputs", + "default_model": "juggernautXL_version6Rundiffusion.safetensors", + "default_refiner": "None", + "default_refiner_switch": 0.8, + "default_lora": "sd_xl_offset_example-lora_1.0.safetensors", + "default_lora_weight": 0.1, + "default_cfg_scale": 4.0, + "default_sample_sharpness": 2, + "default_sampler": "dpmpp_2m_sde_gpu", + "default_scheduler": "karras", + "default_styles": [ + "Fooocus V2", + "Fooocus Enhance", + "Fooocus Sharp" + ], + "default_prompt_negative": "", + "default_prompt": "", + "default_advanced_checkbox": false, + "default_image_number": 2, + "checkpoint_downloads": { + "juggernautXL_version6Rundiffusion.safetensors": "https://huggingface.co/lllyasviel/fav_models/resolve/main/fav/juggernautXL_version6Rundiffusion.safetensors" + }, + "lora_downloads": { + "sd_xl_offset_example-lora_1.0.safetensors": "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_offset_example-lora_1.0.safetensors" + }, + "embeddings_downloads": {}, + "available_aspect_ratios": [ + "704*1408", + "704*1344", + "768*1344", + "768*1280", + "832*1216", + "832*1152", + "896*1152", + "896*1088", + "960*1088", + "960*1024", + "1024*1024", + "1024*960", + "1088*960", + "1088*896", + "1152*896", + "1152*832", + "1216*832", + "1280*768", + "1344*768", + "1344*704", + "1408*704", + "1472*704", + "1536*640", + "1600*640", + "1664*576", + "1728*576" + ], + "default_aspect_ratio": "1152*896" +} \ No newline at end of file