diff --git a/README.md b/README.md index 7606c23..d4b5224 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.820 +Currently loaded Fooocus version: 2.1.822 ### Run with Replicate Now you can use Fooocus-API by Replicate, the model is in [konieshadow/fooocus-api](https://replicate.com/konieshadow/fooocus-api). diff --git a/docs/openapi.json b/docs/openapi.json index 65e83c7..e0874d5 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -707,12 +707,6 @@ "description": "Canny High Threshold", "default": 128 }, - "inpaint_engine": { - "type": "string", - "title": "Inpaint Engine", - "description": "Inpaint Engine", - "default": "v1" - }, "freeu_enabled": { "type": "boolean", "title": "Freeu Enabled", @@ -742,6 +736,40 @@ "title": "Freeu S2", "description": "FreeU B4", "default": 0.95 + }, + "debugging_inpaint_preprocessor": { + "type": "boolean", + "title": "Debugging Inpaint Preprocessor", + "description": "Debug Inpaint Preprocessing", + "default": false + }, + "inpaint_disable_initial_latent": { + "type": "boolean", + "title": "Inpaint Disable Initial Latent", + "description": "Disable initial latent in inpaint", + "default": false + }, + "inpaint_engine": { + "type": "string", + "title": "Inpaint Engine", + "description": "Inpaint Engine", + "default": "v1" + }, + "inpaint_strength": { + "type": "number", + "maximum": 1, + "minimum": 0, + "title": "Inpaint Strength", + "description": "Inpaint Denoising Strength", + "default": 1 + }, + "inpaint_respective_field": { + "type": "number", + "maximum": 1, + "minimum": 0, + "title": "Inpaint Respective Field", + "description": "Inpaint Respective Field", + "default": 1 } }, "type": "object", @@ -874,6 +902,18 @@ "title": "Input Mask", "description": "Inpaint or outpaint mask" }, + "inpaint_additional_prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Inpaint Additional Prompt", + "description": "Describe what you want to inpaint" + }, "outpaint_selections": { "items": { "type": "string" @@ -902,7 +942,8 @@ "description": "Fooocus style selections, seperated by comma", "default": [ "Fooocus V2", - "Fooocus Cinematic" + "Fooocus Enhance", + "Fooocus Sharp" ] }, "performance_selection": { @@ -944,17 +985,17 @@ "maximum": 30, "minimum": 1, "title": "Guidance Scale", - "default": 7 + "default": 4 }, "base_model_name": { "type": "string", "title": "Base Model Name", - "default": "sd_xl_base_1.0_0.9vae.safetensors" + "default": "juggernautXL_version6Rundiffusion.safetensors" }, "refiner_model_name": { "type": "string", "title": "Refiner Model Name", - "default": "sd_xl_refiner_1.0_0.9vae.safetensors" + "default": "None" }, "refiner_switch": { "type": "number", @@ -962,7 +1003,7 @@ "minimum": 0.1, "title": "Refiner Switch", "description": "Refiner Switch At", - "default": 0.667 + "default": 0.5 }, "loras": { "anyOf": [ @@ -975,7 +1016,7 @@ ], "title": "Loras", "description": "Lora config in JSON. Format as [{\"model_name\": \"sd_xl_offset_example-lora_1.0.safetensors\", \"weight\": 0.5}]", - "default": "[{\"model_name\":\"sd_xl_offset_example-lora_1.0.safetensors\",\"weight\":0.5}]" + "default": "[{\"model_name\":\"sd_xl_offset_example-lora_1.0.safetensors\",\"weight\":0.1}]" }, "advanced_params": { "anyOf": [ @@ -1201,7 +1242,8 @@ "description": "Fooocus style selections, seperated by comma", "default": [ "Fooocus V2", - "Fooocus Cinematic" + "Fooocus Enhance", + "Fooocus Sharp" ] }, "performance_selection": { @@ -1243,17 +1285,17 @@ "maximum": 30, "minimum": 1, "title": "Guidance Scale", - "default": 7 + "default": 4 }, "base_model_name": { "type": "string", "title": "Base Model Name", - "default": "sd_xl_base_1.0_0.9vae.safetensors" + "default": "juggernautXL_version6Rundiffusion.safetensors" }, "refiner_model_name": { "type": "string", "title": "Refiner Model Name", - "default": "sd_xl_refiner_1.0_0.9vae.safetensors" + "default": "None" }, "refiner_switch": { "type": "number", @@ -1261,7 +1303,7 @@ "minimum": 0.1, "title": "Refiner Switch", "description": "Refiner Switch At", - "default": 0.667 + "default": 0.5 }, "loras": { "anyOf": [ @@ -1274,7 +1316,7 @@ ], "title": "Loras", "description": "Lora config in JSON. Format as [{\"model_name\": \"sd_xl_offset_example-lora_1.0.safetensors\", \"weight\": 0.5}]", - "default": "[{\"model_name\":\"sd_xl_offset_example-lora_1.0.safetensors\",\"weight\":0.5}]" + "default": "[{\"model_name\":\"sd_xl_offset_example-lora_1.0.safetensors\",\"weight\":0.1}]" }, "advanced_params": { "anyOf": [ @@ -1334,7 +1376,8 @@ "description": "Fooocus style selections, seperated by comma", "default": [ "Fooocus V2", - "Fooocus Cinematic" + "Fooocus Enhance", + "Fooocus Sharp" ] }, "performance_selection": { @@ -1376,17 +1419,17 @@ "maximum": 30, "minimum": 1, "title": "Guidance Scale", - "default": 7 + "default": 4 }, "base_model_name": { "type": "string", "title": "Base Model Name", - "default": "sd_xl_base_1.0_0.9vae.safetensors" + "default": "juggernautXL_version6Rundiffusion.safetensors" }, "refiner_model_name": { "type": "string", "title": "Refiner Model Name", - "default": "sd_xl_refiner_1.0_0.9vae.safetensors" + "default": "None" }, "refiner_switch": { "type": "number", @@ -1394,7 +1437,7 @@ "minimum": 0.1, "title": "Refiner Switch", "description": "Refiner Switch At", - "default": 0.667 + "default": 0.5 }, "loras": { "anyOf": [ @@ -1407,7 +1450,7 @@ ], "title": "Loras", "description": "Lora config in JSON. Format as [{\"model_name\": \"sd_xl_offset_example-lora_1.0.safetensors\", \"weight\": 0.5}]", - "default": "[{\"model_name\":\"sd_xl_offset_example-lora_1.0.safetensors\",\"weight\":0.5}]" + "default": "[{\"model_name\":\"sd_xl_offset_example-lora_1.0.safetensors\",\"weight\":0.1}]" }, "advanced_params": { "anyOf": [ @@ -1616,7 +1659,8 @@ "title": "Style Selections", "default": [ "Fooocus V2", - "Fooocus Cinematic" + "Fooocus Enhance", + "Fooocus Sharp" ] }, "performance_selection": { @@ -1658,17 +1702,17 @@ "maximum": 30, "minimum": 1, "title": "Guidance Scale", - "default": 7 + "default": 4 }, "base_model_name": { "type": "string", "title": "Base Model Name", - "default": "sd_xl_base_1.0_0.9vae.safetensors" + "default": "juggernautXL_version6Rundiffusion.safetensors" }, "refiner_model_name": { "type": "string", "title": "Refiner Model Name", - "default": "sd_xl_refiner_1.0_0.9vae.safetensors" + "default": "None" }, "refiner_switch": { "type": "number", @@ -1676,7 +1720,7 @@ "minimum": 0.1, "title": "Refiner Switch", "description": "Refiner Switch At", - "default": 0.667 + "default": 0.5 }, "loras": { "items": { @@ -1687,7 +1731,7 @@ "default": [ { "model_name": "sd_xl_offset_example-lora_1.0.safetensors", - "weight": 0.5 + "weight": 0.1 } ] }, diff --git a/fooocus_api_version.py b/fooocus_api_version.py index 6f142de..7601492 100644 --- a/fooocus_api_version.py +++ b/fooocus_api_version.py @@ -1 +1 @@ -version = '0.3.16' +version = '0.3.17' diff --git a/fooocusapi/api_utils.py b/fooocusapi/api_utils.py index a6c9071..23a7fe7 100644 --- a/fooocusapi/api_utils.py +++ b/fooocusapi/api_utils.py @@ -58,7 +58,9 @@ def req_to_params(req: Text2ImgRequest) -> ImageGenerationParams: refiner_model_name = 'None' inpaint_input_image = None + inpaint_additional_prompt = None if isinstance(req, ImgInpaintOrOutpaintRequest): + inpaint_additional_prompt = req.inpaint_additional_prompt input_image = read_input_image(req.input_image) input_mask = None if req.input_mask is not None: @@ -96,12 +98,16 @@ def req_to_params(req: Text2ImgRequest) -> ImageGenerationParams: print(f"[Warning] Wrong inpaint_engine input: {adp.inpaint_engine}, using default") adp.inpaint_engine = flags.default_inpaint_engine_version - advanced_params = [adp.disable_preview, 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.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] + advanced_params = [ + adp.disable_preview, 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.skipping_cn_preprocessor, adp.controlnet_softness, adp.canny_low_threshold, adp.canny_high_threshold, \ + adp.refiner_swap_method, \ + adp.freeu_enabled, adp.freeu_b1, adp.freeu_b2, adp.freeu_s1, adp.freeu_s2, \ + adp.debugging_inpaint_preprocessor, adp.inpaint_disable_initial_latent, adp.inpaint_engine, adp.inpaint_strength, adp.inpaint_respective_field + ] return ImageGenerationParams(prompt=prompt, negative_prompt=negative_prompt, @@ -120,6 +126,7 @@ def req_to_params(req: Text2ImgRequest) -> ImageGenerationParams: uov_method=uov_method, outpaint_selections=outpaint_selections, inpaint_input_image=inpaint_input_image, + inpaint_additional_prompt=inpaint_additional_prompt, image_prompts=image_prompts, advanced_params=advanced_params, ) diff --git a/fooocusapi/models.py b/fooocusapi/models.py index aed5f14..9af835a 100644 --- a/fooocusapi/models.py +++ b/fooocusapi/models.py @@ -85,12 +85,16 @@ class AdvancedParams(BaseModel): 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) - inpaint_engine: str = Field('v1', description="Inpaint Engine") freeu_enabled: bool = Field(False, description="FreeU enabled") freeu_b1: float = Field(1.01, description="FreeU B1") freeu_b2: float = Field(1.02, description="FreeU B2") freeu_s1: float = Field(0.99, description="FreeU B3") freeu_s2: float = Field(0.95, description="FreeU B4") + debugging_inpaint_preprocessor: bool = Field(False, description="Debug Inpaint Preprocessing") + inpaint_disable_initial_latent: bool = Field(False, description="Disable initial latent in inpaint") + inpaint_engine: str = Field('v1', description="Inpaint Engine") + inpaint_strength: float = Field(1.0, description="Inpaint Denoising Strength", ge=0.0, le=1.0) + inpaint_respective_field: float = Field(1.0, description="Inpaint Respective Field", ge=0.0, le=1.0) class Text2ImgRequest(BaseModel): @@ -172,12 +176,14 @@ def as_form(cls, input_image: UploadFile = Form(description="Init image for upsa class ImgInpaintOrOutpaintRequest(Text2ImgRequest): input_image: UploadFile input_mask: UploadFile | None + inpaint_additional_prompt: str | None outpaint_selections: List[OutpaintExpansion] @classmethod def as_form(cls, input_image: UploadFile = Form(description="Init image for inpaint or outpaint"), input_mask: UploadFile = Form( File(None), description="Inpaint or outpaint mask"), + inpaint_additional_prompt: str | None = Form(None, description="Describe what you want to inpaint"), outpaint_selections: List[str] = Form( [], description="Outpaint expansion selections, literal 'Left', 'Right', 'Top', 'Bottom' seperated by comma"), prompt: str = Form(''), @@ -238,7 +244,7 @@ def as_form(cls, input_image: UploadFile = Form(description="Init image for inpa errs = ve.errors() raise RequestValidationError(errors=[errs]) - return cls(input_image=input_image, input_mask=input_mask, outpaint_selections=outpaint_selections_arr, prompt=prompt, negative_prompt=negative_prompt, style_selections=style_selection_arr, + return cls(input_image=input_image, input_mask=input_mask, inpaint_additional_prompt=inpaint_additional_prompt, outpaint_selections=outpaint_selections_arr, prompt=prompt, negative_prompt=negative_prompt, style_selections=style_selection_arr, performance_selection=performance_selection, aspect_ratios_selection=aspect_ratios_selection, image_number=image_number, image_seed=image_seed, sharpness=sharpness, guidance_scale=guidance_scale, base_model_name=base_model_name, refiner_model_name=refiner_model_name, refiner_switch=refiner_switch, diff --git a/fooocusapi/parameters.py b/fooocusapi/parameters.py index cae0e28..df32d60 100644 --- a/fooocusapi/parameters.py +++ b/fooocusapi/parameters.py @@ -97,6 +97,7 @@ def __init__(self, prompt: str, uov_method: str, outpaint_selections: List[str], inpaint_input_image: Dict[str, np.ndarray] | None, + inpaint_additional_prompt: str | None, image_prompts: List[Tuple[np.ndarray, float, float, str]], advanced_params: List[any] | None): self.prompt = prompt @@ -116,7 +117,9 @@ def __init__(self, prompt: str, self.uov_method = uov_method self.outpaint_selections = outpaint_selections self.inpaint_input_image = inpaint_input_image + self.inpaint_additional_prompt = inpaint_additional_prompt self.image_prompts = image_prompts + if advanced_params is None: disable_preview = False adm_scaler_positive = 1.5 @@ -139,15 +142,24 @@ def __init__(self, prompt: str, controlnet_softness = 0.25 canny_low_threshold = 64 canny_high_threshold = 128 - inpaint_engine = default_inpaint_engine_version refiner_swap_method = 'joint' freeu_enabled = False freeu_b1, freeu_b2, freeu_s1, freeu_s2 = [None] * 4 - self.advanced_params = [disable_preview, adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg, sampler_name, - 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, 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] + debugging_inpaint_preprocessor = False + inpaint_disable_initial_latent = False + inpaint_engine = default_inpaint_engine_version + inpaint_strength = 1.0 + inpaint_respective_field = 0.618 + + self.advanced_params = [ + disable_preview, adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg, sampler_name, \ + 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, skipping_cn_preprocessor, controlnet_softness, canny_low_threshold, canny_high_threshold, \ + refiner_swap_method, \ + freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2, \ + debugging_inpaint_preprocessor, inpaint_disable_initial_latent, inpaint_engine, inpaint_strength, inpaint_respective_field + ] else: self.advanced_params = advanced_params diff --git a/fooocusapi/repositories_versions.py b/fooocusapi/repositories_versions.py index 0b4dab0..6e849d2 100644 --- a/fooocusapi/repositories_versions.py +++ b/fooocusapi/repositories_versions.py @@ -1,5 +1,5 @@ import os -fooocus_version = '2.1.820' +fooocus_version = '2.1.822' fooocus_commit_hash = os.environ.get( - 'FOOOCUS_COMMIT_HASH', "3b97e49dd814a43fc75a26b2495603e38b8b2f62") + 'FOOOCUS_COMMIT_HASH', "dececbd06000304ef9cd6a43fcd0e685eab147e9") diff --git a/fooocusapi/worker.py b/fooocusapi/worker.py index 3398d68..f404908 100644 --- a/fooocusapi/worker.py +++ b/fooocusapi/worker.py @@ -8,7 +8,7 @@ from fooocusapi.parameters import default_inpaint_engine_version, GenerationFinishReason, ImageGenerationParams, ImageGenerationResult from fooocusapi.task_queue import QueueTask, TaskQueue, TaskOutputs -save_log = True + task_queue = TaskQueue(queue_size=3, hisotry_size=6) @@ -16,17 +16,18 @@ def process_top(): import fcbh.model_management fcbh.model_management.interrupt_current_processing() + @torch.no_grad() @torch.inference_mode() -def process_generate(queue_task: QueueTask, params: ImageGenerationParams) -> List[ImageGenerationResult]: +def process_generate(async_task: QueueTask, params: ImageGenerationParams) -> List[ImageGenerationResult]: try: import modules.default_pipeline as pipeline except Exception as e: print('Import default pipeline error:', e) - if not queue_task.is_finished: - task_queue.finish_task(queue_task.seq) - queue_task.set_result([], True, str(e)) - print(f"[Task Queue] Finish task with error, seq={queue_task.seq}") + if not async_task.is_finished: + task_queue.finish_task(async_task.seq) + async_task.set_result([], True, str(e)) + print(f"[Task Queue] Finish task with error, seq={async_task.seq}") return [] import modules.patch as patch @@ -46,7 +47,8 @@ def process_generate(queue_task: QueueTask, params: ImageGenerationParams) -> Li from modules.expansion import safe_str from modules.sdxl_styles import apply_style, fooocus_expansion, apply_wildcards - outputs = TaskOutputs(queue_task) + outputs = TaskOutputs(async_task) + results = [] def refresh_seed(r, seed_string): if r: @@ -59,13 +61,12 @@ def refresh_seed(r, seed_string): except ValueError: pass return random.randint(constants.MIN_SEED, constants.MAX_SEED) - - def progressbar(number, text): + + def progressbar(_, number, text): print(f'[Fooocus] {text}') outputs.append(['preview', (number, text, None)]) - queue_task.set_progress(number, text) - def yield_result(imgs, tasks): + def yield_result(_, imgs, tasks): if not isinstance(imgs, list): imgs = [imgs] @@ -74,9 +75,9 @@ def yield_result(imgs, tasks): 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}") + async_task.set_result(results, False) + task_queue.finish_task(async_task.seq) + print(f"[Task Queue] Finish task, seq={async_task.seq}") outputs.append(['results', imgs]) pipeline.prepare_text_encoder(async_call=True) @@ -85,21 +86,21 @@ def yield_result(imgs, tasks): try: waiting_sleep_steps: int = 0 waiting_start_time = time.perf_counter() - while not task_queue.is_task_ready_to_start(queue_task.seq): + while not task_queue.is_task_ready_to_start(async_task.seq): if waiting_sleep_steps == 0: print( - f"[Task Queue] Waiting for task queue become free, seq={queue_task.seq}") + f"[Task Queue] Waiting for task queue become free, seq={async_task.seq}") delay = 0.1 time.sleep(delay) waiting_sleep_steps += 1 if waiting_sleep_steps % int(10 / delay) == 0: waiting_time = time.perf_counter() - waiting_start_time print( - f"[Task Queue] Already waiting for {waiting_time}S, seq={queue_task.seq}") + f"[Task Queue] Already waiting for {waiting_time}S, seq={async_task.seq}") - print(f"[Task Queue] Task queue is free, start task, seq={queue_task.seq}") + print(f"[Task Queue] Task queue is free, start task, seq={async_task.seq}") - task_queue.start_task(queue_task.seq) + task_queue.start_task(async_task.seq) execution_start_time = time.perf_counter() @@ -123,6 +124,9 @@ def yield_result(imgs, tasks): uov_input_image = params.uov_input_image outpaint_selections = params.outpaint_selections inpaint_input_image = params.inpaint_input_image + inpaint_additional_prompt = params.inpaint_additional_prompt + if inpaint_additional_prompt is None: + inpaint_additional_prompt = '' if inpaint_input_image is not None and inpaint_input_image['image'] is not None: if inpaint_input_image['mask'] is None: @@ -173,8 +177,8 @@ def yield_result(imgs, tasks): 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)] + progressbar(async_task, 1, 'Downloading LCM components ...') + loras += [(config.downloading_sdxl_lcm_lora(), 1.0)] if refiner_model_name != 'None': print(f'Refiner disabled in LCM mode.') @@ -200,7 +204,10 @@ def yield_result(imgs, tasks): patch.positive_adm_scale = advanced_parameters.adm_scaler_positive patch.negative_adm_scale = advanced_parameters.adm_scaler_negative patch.adm_scaler_end = advanced_parameters.adm_scaler_end - print(f'[Parameters] ADM Scale = {patch.positive_adm_scale} : {patch.negative_adm_scale} : {patch.adm_scaler_end}') + print(f'[Parameters] ADM Scale = ' + f'{patch.positive_adm_scale} : ' + f'{patch.negative_adm_scale} : ' + f'{patch.adm_scaler_end}') cfg_scale = float(guidance_scale) print(f'[Parameters] CFG = {cfg_scale}') @@ -208,17 +215,21 @@ def yield_result(imgs, tasks): initial_latent = None denoising_strength = 1.0 tiled = False - inpaint_worker.current_task = None - - width, height = aspect_ratios_selection.split('×') + + width, height = aspect_ratios_selection.replace('×', ' ').split(' ')[:2] width, height = int(width), int(height) skip_prompt_processing = False refiner_swap_method = advanced_parameters.refiner_swap_method + inpaint_worker.current_task = None + inpaint_parameterized = advanced_parameters.inpaint_engine != 'None' inpaint_image = None inpaint_mask = None inpaint_head_model_path = None + + use_synthetic_refiner = False + controlnet_canny_path = None controlnet_cpds_path = None clip_vision_path, ip_negative_path, ip_adapter_path, ip_adapter_face_path = None, None, None, None @@ -233,7 +244,8 @@ def yield_result(imgs, tasks): tasks = [] if input_image_checkbox: - if (current_tab == 'uov' or (current_tab == 'ip' and advanced_parameters.mixing_image_prompt_and_vary_upscale)) \ + if (current_tab == 'uov' or ( + current_tab == 'ip' and advanced_parameters.mixing_image_prompt_and_vary_upscale)) \ and uov_method != flags.disabled and uov_input_image is not None: uov_input_image = HWC3(uov_input_image) if 'vary' in uov_method: @@ -254,25 +266,40 @@ def yield_result(imgs, tasks): if performance_selection == 'Extreme Speed': steps = 8 - progressbar(1, 'Downloading upscale models ...') + progressbar(async_task, 1, 'Downloading upscale models ...') config.downloading_upscale_model() - if (current_tab == 'inpaint' or (current_tab == 'ip' and advanced_parameters.mixing_image_prompt_and_inpaint))\ + 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'] inpaint_mask = inpaint_input_image['mask'][:, :, 0] inpaint_image = HWC3(inpaint_image) 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 = 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}') + if inpaint_parameterized: + progressbar(async_task, 1, 'Downloading inpainter ...') + config.downloading_upscale_model() + 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}') + if refiner_model_name == 'None': + use_synthetic_refiner = True + refiner_switch = 0.5 + else: + inpaint_head_model_path, inpaint_patch_model_path = None, None + print(f'[Inpaint] Parameterized inpaint is disabled.') + if inpaint_additional_prompt != '': + if prompt == '': + prompt = inpaint_additional_prompt + else: + prompt = inpaint_additional_prompt + '\n' + prompt goals.append('inpaint') if current_tab == 'ip' or \ advanced_parameters.mixing_image_prompt_and_inpaint or \ advanced_parameters.mixing_image_prompt_and_vary_upscale: goals.append('cn') - progressbar(1, 'Downloading control models ...') + progressbar(async_task, 1, 'Downloading control models ...') if len(cn_tasks[flags.cn_canny]) > 0: controlnet_canny_path = config.downloading_controlnet_canny() if len(cn_tasks[flags.cn_cpds]) > 0: @@ -280,8 +307,9 @@ def yield_result(imgs, tasks): if len(cn_tasks[flags.cn_ip]) > 0: 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 ...') + clip_vision_path, ip_negative_path, ip_adapter_face_path = config.downloading_ip_adapters( + 'face') + progressbar(async_task, 1, 'Loading control models ...') # Load or unload CNs pipeline.refresh_controlnets([controlnet_canny_path, controlnet_cpds_path]) @@ -305,7 +333,7 @@ def yield_result(imgs, tasks): print(f'[Parameters] Sampler = {sampler_name} - {scheduler_name}') print(f'[Parameters] Steps = {steps} - {switch}') - progressbar(1, 'Initializing ...') + progressbar(async_task, 1, 'Initializing ...') if not skip_prompt_processing: @@ -322,11 +350,12 @@ def yield_result(imgs, tasks): extra_positive_prompts = prompts[1:] if len(prompts) > 1 else [] extra_negative_prompts = negative_prompts[1:] if len(negative_prompts) > 1 else [] - progressbar(3, 'Loading models ...') + progressbar(async_task, 3, 'Loading models ...') 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) + loras=loras, base_model_additional_loras=base_model_additional_loras, + use_synthetic_refiner=use_synthetic_refiner) - progressbar(3, 'Processing prompts ...') + progressbar(async_task, 3, 'Processing prompts ...') tasks = [] for i in range(image_number): task_seed = (seed + i) % (constants.MAX_SEED + 1) # randint is inclusive, % is not @@ -367,32 +396,31 @@ def yield_result(imgs, tasks): uc=None, positive_top_k=len(positive_basic_workloads), negative_top_k=len(negative_basic_workloads), - log_positive_prompt='\n'.join([task_prompt] + task_extra_positive_prompts), - log_negative_prompt='\n'.join([task_negative_prompt] + task_extra_negative_prompts), + log_positive_prompt='; '.join([task_prompt] + task_extra_positive_prompts), + log_negative_prompt='; '.join([task_negative_prompt] + task_extra_negative_prompts), )) if use_expansion: for i, t in enumerate(tasks): - progressbar(5, f'Preparing Fooocus text #{i + 1} ...') + progressbar(async_task, 5, f'Preparing Fooocus text #{i + 1} ...') expansion = pipeline.final_expansion(t['task_prompt'], t['task_seed']) print(f'[Prompt Expansion] {expansion}') t['expansion'] = expansion t['positive'] = copy.deepcopy(t['positive']) + [expansion] # Deep copy. for i, t in enumerate(tasks): - progressbar(7, f'Encoding positive #{i + 1} ...') + progressbar(async_task, 7, f'Encoding positive #{i + 1} ...') t['c'] = pipeline.clip_encode(texts=t['positive'], pool_top_k=t['positive_top_k']) for i, t in enumerate(tasks): 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} ...') + progressbar(async_task, 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 ...') + progressbar(async_task, 13, 'Image processing ...') if 'vary' in goals: if 'subtle' in uov_method: @@ -413,8 +441,16 @@ def yield_result(imgs, tasks): uov_input_image = set_image_shape_ceil(uov_input_image, shape_ceil) initial_pixels = core.numpy_to_pytorch(uov_input_image) - progressbar(13, 'VAE encoding ...') - initial_latent = core.encode_vae(vae=pipeline.final_vae, pixels=initial_pixels) + progressbar(async_task, 13, 'VAE encoding ...') + + candidate_vae, _ = pipeline.get_candidate_vae( + steps=steps, + switch=switch, + denoise=denoising_strength, + refiner_swap_method=refiner_swap_method + ) + + initial_latent = core.encode_vae(vae=candidate_vae, pixels=initial_pixels) B, C, H, W = initial_latent['samples'].shape width = W * 8 height = H * 8 @@ -422,11 +458,8 @@ def yield_result(imgs, tasks): if 'upscale' in goals: H, W, C = uov_input_image.shape - progressbar(13, f'Upscaling image from {str((H, W))} ...') - - uov_input_image = core.numpy_to_pytorch(uov_input_image) + progressbar(async_task, 13, f'Upscaling image from {str((H, W))} ...') uov_input_image = perform_upscale(uov_input_image) - uov_input_image = core.pytorch_to_numpy(uov_input_image)[0] print(f'Image upscaled.') if '1.5x' in uov_method: @@ -459,9 +492,8 @@ def yield_result(imgs, tasks): if direct_return: d = [('Upscale (Fast)', '2x')] - if save_log: - log(uov_input_image, d, single_line_number=1) - return yield_result(uov_input_image, tasks) + log(uov_input_image, d, single_line_number=1) + return yield_result(async_task, uov_input_image, tasks) tiled = True denoising_strength = 0.382 @@ -470,16 +502,22 @@ def yield_result(imgs, tasks): denoising_strength = advanced_parameters.overwrite_upscale_strength initial_pixels = core.numpy_to_pytorch(uov_input_image) - progressbar(13, 'VAE encoding ...') + progressbar(async_task, 13, 'VAE encoding ...') + + candidate_vae, _ = pipeline.get_candidate_vae( + steps=steps, + switch=switch, + denoise=denoising_strength, + refiner_swap_method=refiner_swap_method + ) initial_latent = core.encode_vae( - vae=pipeline.final_vae if pipeline.final_refiner_vae is None else pipeline.final_refiner_vae, + vae=candidate_vae, pixels=initial_pixels, tiled=True) B, C, H, W = initial_latent['samples'].shape width = W * 8 height = H * 8 print(f'Final resolution is {str((height, width))}.') - refiner_swap_method = 'upscale' if 'inpaint' in goals: if len(outpaint_selections) > 0: @@ -505,48 +543,69 @@ def yield_result(imgs, tasks): inpaint_image = np.ascontiguousarray(inpaint_image.copy()) inpaint_mask = np.ascontiguousarray(inpaint_mask.copy()) + advanced_parameters.inpaint_strength = 1.0 + advanced_parameters.inpaint_respective_field = 1.0 - inpaint_worker.current_task = inpaint_worker.InpaintWorker(image=inpaint_image, mask=inpaint_mask, - is_outpaint=len(outpaint_selections) > 0) + denoising_strength = advanced_parameters.inpaint_strength - pipeline.final_unet.model.diffusion_model.in_inpaint = True + inpaint_worker.current_task = inpaint_worker.InpaintWorker( + image=inpaint_image, + mask=inpaint_mask, + use_fill=denoising_strength > 0.99, + k=advanced_parameters.inpaint_respective_field + ) - if advanced_parameters.debugging_cn_preprocessor: - return yield_result(inpaint_worker.current_task.visualize_mask_processing(), tasks) + if advanced_parameters.debugging_inpaint_preprocessor: + return yield_result(async_task, inpaint_worker.current_task.visualize_mask_processing(), + do_not_show_finished_images=True) - progressbar(13, 'VAE Inpaint encoding ...') + progressbar(async_task, 13, 'VAE Inpaint encoding ...') inpaint_pixel_fill = core.numpy_to_pytorch(inpaint_worker.current_task.interested_fill) inpaint_pixel_image = core.numpy_to_pytorch(inpaint_worker.current_task.interested_image) inpaint_pixel_mask = core.numpy_to_pytorch(inpaint_worker.current_task.interested_mask) + candidate_vae, candidate_vae_swap = pipeline.get_candidate_vae( + steps=steps, + switch=switch, + denoise=denoising_strength, + refiner_swap_method=refiner_swap_method + ) + latent_inpaint, latent_mask = core.encode_vae_inpaint( mask=inpaint_pixel_mask, - vae=pipeline.final_vae, + vae=candidate_vae, pixels=inpaint_pixel_image) latent_swap = None - if pipeline.final_refiner_vae is not None: - progressbar(13, 'VAE Inpaint SD15 encoding ...') + if candidate_vae_swap is not None: + progressbar(async_task, 13, 'VAE SD15 encoding ...') latent_swap = core.encode_vae( - vae=pipeline.final_refiner_vae, + vae=candidate_vae_swap, pixels=inpaint_pixel_fill)['samples'] - progressbar(13, 'VAE encoding ...') + progressbar(async_task, 13, 'VAE encoding ...') latent_fill = core.encode_vae( - vae=pipeline.final_vae, + vae=candidate_vae, pixels=inpaint_pixel_fill)['samples'] - inpaint_worker.current_task.load_latent(latent_fill=latent_fill, - latent_inpaint=latent_inpaint, - latent_mask=latent_mask, - latent_swap=latent_swap, - inpaint_head_model_path=inpaint_head_model_path) + inpaint_worker.current_task.load_latent( + latent_fill=latent_fill, latent_mask=latent_mask, latent_swap=latent_swap) + + if inpaint_parameterized: + pipeline.final_unet = inpaint_worker.current_task.patch( + inpaint_head_model_path=inpaint_head_model_path, + inpaint_latent=latent_inpaint, + inpaint_latent_mask=latent_mask, + model=pipeline.final_unet + ) + + if not advanced_parameters.inpaint_disable_initial_latent: + initial_latent = {'samples': latent_fill} B, C, H, W = latent_fill.shape height, width = H * 8, W * 8 final_height, final_width = inpaint_worker.current_task.image.shape[:2] - initial_latent = {'samples': latent_fill} print(f'Final resolution is {str((final_height, final_width))}, latent is {str((height, width))}.') if 'cn' in goals: @@ -560,7 +619,7 @@ def yield_result(imgs, tasks): cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if advanced_parameters.debugging_cn_preprocessor: - return yield_result(cn_img, tasks) + return yield_result(async_task, 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) @@ -571,7 +630,7 @@ def yield_result(imgs, tasks): cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if advanced_parameters.debugging_cn_preprocessor: - return yield_result(cn_img, tasks) + return yield_result(async_task, cn_img, tasks) for task in cn_tasks[flags.cn_ip]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -581,7 +640,7 @@ def yield_result(imgs, tasks): 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) + return yield_result(async_task, cn_img, tasks) for task in cn_tasks[flags.cn_ip_face]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -594,7 +653,7 @@ def yield_result(imgs, tasks): task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_face_path) if advanced_parameters.debugging_cn_preprocessor: - return yield_result(cn_img, tasks) + return yield_result(async_task, cn_img, tasks) all_ip_tasks = cn_tasks[flags.cn_ip] + cn_tasks[flags.cn_ip_face] @@ -611,9 +670,17 @@ def yield_result(imgs, tasks): advanced_parameters.freeu_s2 ) - results = [] all_steps = steps * image_number + print(f'[Parameters] Denoising Strength = {denoising_strength}') + + if isinstance(initial_latent, dict) and 'samples' in initial_latent: + log_shape = initial_latent['samples'].shape + else: + log_shape = f'Image Space {(height, width)}' + + print(f'[Parameters] Initial Latent shape: {log_shape}') + preparation_time = time.perf_counter() - execution_start_time print(f'Preparation time: {preparation_time:.2f} seconds') @@ -706,8 +773,7 @@ def callback(step, x0, x, total_steps, y): 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) + log(x, d, single_line_number=3) # Fooocus async_worker.py code end @@ -716,26 +782,26 @@ def callback(step, x0, x, total_steps, y): print("User stopped") results.append(ImageGenerationResult( im=None, seed=task['task_seed'], finish_reason=GenerationFinishReason.user_cancel)) - queue_task.set_result(results, True, str(e)) + async_task.set_result(results, True, str(e)) break except Exception as e: print('Process error:', e) results.append(ImageGenerationResult( im=None, seed=task['task_seed'], finish_reason=GenerationFinishReason.error)) - queue_task.set_result(results, True, str(e)) + async_task.set_result(results, True, str(e)) break execution_time = time.perf_counter() - execution_start_time print(f'Generating and saving time: {execution_time:.2f} seconds') - if queue_task.finish_with_error: - task_queue.finish_task(queue_task.seq) - return queue_task.task_result - return yield_result(results, tasks) + if async_task.finish_with_error: + task_queue.finish_task(async_task.seq) + return async_task.task_result + return yield_result(None, results, tasks) except Exception as e: print('Worker error:', e) - if not queue_task.is_finished: - task_queue.finish_task(queue_task.seq) - queue_task.set_result([], True, str(e)) - print(f"[Task Queue] Finish task with error, seq={queue_task.seq}") + if not async_task.is_finished: + task_queue.finish_task(async_task.seq) + async_task.set_result([], True, str(e)) + print(f"[Task Queue] Finish task with error, seq={async_task.seq}") return [] diff --git a/main.py b/main.py index 4e8b1c4..aafe13e 100644 --- a/main.py +++ b/main.py @@ -252,9 +252,6 @@ def prepare_environments(args) -> bool: worker.task_queue.queue_size = args.queue_size worker.task_queue.history_size = args.queue_history - if args.disable_private_log: - worker.save_log = False - if args.base_url is None or len(args.base_url.strip()) == 0: host = args.host if host == '0.0.0.0': @@ -268,9 +265,13 @@ def prepare_environments(args) -> bool: backend_path = os.path.join(fooocus_path, 'backend', 'headless') if backend_path not in sys.path: sys.path.append(backend_path) - os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" + os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" sys.argv = [sys.argv[0]] + + if args.disable_private_log: + sys.argv.append('--disable-image-log') + if args.preset is not None: # Remove and copy preset folder origin_preset_folder = os.path.abspath(os.path.join(script_path, dir_repos, fooocus_name, 'presets'))