From f1e9a5c42639e2d3b63847dd114a8f86e5cbcc88 Mon Sep 17 00:00:00 2001 From: ChrisColeTech Date: Sat, 17 Aug 2024 20:08:43 -0400 Subject: [PATCH] Batch File upload/ Folder upload. Bulk Enhance -Immediately enhance each image generated -Added Bulk Enhance -Added 2 smaller resolutions: 512x512 and 704x704 for faster image generation -Added default enhance prompts for pre-selections 'face', 'eye', 'mouth', 'hair', 'hand', 'body' -Added more error handling --- .gitignore | 1 + modules/async_worker.py | 1220 ++++++++++++++++++++++--------- modules/bulk_enhance_helpers.py | 155 ++++ modules/config.py | 177 +++-- modules/flags.py | 26 +- webui.py | 738 ++++++++++++------- 6 files changed, 1630 insertions(+), 687 deletions(-) create mode 100644 modules/bulk_enhance_helpers.py diff --git a/.gitignore b/.gitignore index 5bf633a8..9216f23a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ user_path_config-deprecated.txt /.coverage* /auth.json .DS_Store +/.venv diff --git a/modules/async_worker.py b/modules/async_worker.py index a0b96a54..e31ab074 100644 --- a/modules/async_worker.py +++ b/modules/async_worker.py @@ -1,13 +1,15 @@ import threading - +import gradio as gr from extras.inpaint_mask import generate_mask_from_image, SAMOptions from modules.patch import PatchSettings, patch_settings, patch_all import modules.config - +from PIL import Image, ImageOps +import modules.gradio_hijack as grh patch_all() class AsyncTask: + def __init__(self, args): from modules.flags import Performance, MetadataScheme, ip_list, disabled from modules.util import get_enabled_loras @@ -95,8 +97,10 @@ class AsyncTask: self.inpaint_advanced_masking_checkbox = args.pop() self.invert_mask_checkbox = args.pop() self.inpaint_erode_or_dilate = args.pop() - self.save_final_enhanced_image_only = args.pop() if not args_manager.args.disable_image_log else False - self.save_metadata_to_images = args.pop() if not args_manager.args.disable_metadata else False + self.save_final_enhanced_image_only = args.pop( + ) if not args_manager.args.disable_image_log else False + self.save_metadata_to_images = args.pop( + ) if not args_manager.args.disable_metadata else False self.metadata_scheme = MetadataScheme( args.pop()) if not args_manager.args.disable_metadata else MetadataScheme.FOOOCUS @@ -154,11 +158,24 @@ class AsyncTask: enhance_inpaint_erode_or_dilate, enhance_mask_invert ]) - self.should_enhance = self.enhance_checkbox and (self.enhance_uov_method != disabled.casefold() or len(self.enhance_ctrls) > 0) + self.should_enhance = self.enhance_checkbox and ( + self.enhance_uov_method != disabled.casefold() or len(self.enhance_ctrls) > 0) self.images_to_enhance_count = 0 self.enhance_stats = {} + self.bulk_enhance_enabled = args.pop() + self.bulk_enhance_data_type = args.pop() + self.bulk_enhance_file_explorer = args.pop() + self.bulk_enhance_input_path = args.pop() + async_tasks = [] +# Define global variables +current_task_id = 0 +current_progress = None +preparation_steps = None +all_steps = None +total_count = None +current_async_task = None class EarlyReturnException(BaseException): @@ -210,8 +227,21 @@ def worker(): if async_gradio_app.share: flag += f''' or {async_gradio_app.share_url}''' print(flag) + + except FileNotFoundError: + # Handle the case where the file is not found + gr.Error( + "\n\nπŸ’₯The specified file was not found. Please check the file path and try again. πŸ“\n\n ") + + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️ A value error occurred: {ve}. Please check the input values. ⚠️\n\n ") + except Exception as e: - print(e) + # Handle any other unforeseen errors + gr.Error( + f"\n\nπŸ’₯ An unexpected error occurred: {e} πŸ’₯\n\n ") def progressbar(async_task, number, text): print(f'[Fooocus] {text}') @@ -222,7 +252,8 @@ def worker(): imgs = [imgs] if censor and (modules.config.default_black_out_nsfw or black_out_nsfw): - progressbar(async_task, progressbar_index, 'Checking for NSFW content ...') + progressbar(async_task, progressbar_index, + 'Checking for NSFW content ...') imgs = default_censor(imgs) async_task.results = async_task.results + imgs @@ -310,15 +341,19 @@ def worker(): refiner_swap_method=async_task.refiner_swap_method, disable_preview=async_task.disable_preview ) - del positive_cond, negative_cond # Save memory + # del positive_cond, negative_cond # Save memory if inpaint_worker.current_task is not None: imgs = [inpaint_worker.current_task.post_process(x) for x in imgs] - current_progress = int(base_progress + (100 - preparation_steps) / float(all_steps) * steps) + current_progress = int( + base_progress + (100 - preparation_steps) / float(all_steps) * steps) if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: - progressbar(async_task, current_progress, 'Checking for NSFW content ...') + progressbar(async_task, current_progress, + 'Checking for NSFW content ...') imgs = default_censor(imgs) - progressbar(async_task, current_progress, f'Saving image {current_task_id + 1}/{total_count} to system ...') - img_paths = save_and_log(async_task, height, imgs, task, use_expansion, width, loras, persist_image) + progressbar(async_task, current_progress, + f'Saving image {current_task_id + 1}/{total_count} to system ...') + img_paths = save_and_log( + async_task, height, imgs, task, use_expansion, width, loras, persist_image) yield_result(async_task, img_paths, current_progress, async_task.black_out_nsfw, False, do_not_show_finished_images=not show_intermediate_results or async_task.disable_intermediate_results) @@ -338,11 +373,14 @@ def worker(): img_paths = [] for x in imgs: d = [('Prompt', 'prompt', task['log_positive_prompt']), - ('Negative Prompt', 'negative_prompt', task['log_negative_prompt']), - ('Fooocus V2 Expansion', 'prompt_expansion', task['expansion']), + ('Negative Prompt', 'negative_prompt', + task['log_negative_prompt']), + ('Fooocus V2 Expansion', + 'prompt_expansion', task['expansion']), ('Styles', 'styles', str(task['styles'] if not use_expansion else [fooocus_expansion] + task['styles'])), - ('Performance', 'performance', async_task.performance_selection.value), + ('Performance', 'performance', + async_task.performance_selection.value), ('Steps', 'steps', async_task.steps), ('Resolution', 'resolution', str((width, height))), ('Guidance Scale', 'guidance_scale', async_task.cfg_scale), @@ -357,9 +395,11 @@ def worker(): if async_task.refiner_model_name != 'None': if async_task.overwrite_switch > 0: - d.append(('Overwrite Switch', 'overwrite_switch', async_task.overwrite_switch)) + d.append(('Overwrite Switch', 'overwrite_switch', + async_task.overwrite_switch)) if async_task.refiner_swap_method != flags.refiner_swap_method: - d.append(('Refiner Swap Method', 'refiner_swap_method', async_task.refiner_swap_method)) + d.append( + ('Refiner Swap Method', 'refiner_swap_method', async_task.refiner_swap_method)) if modules.patch.patch_settings[pid].adaptive_cfg != modules.config.default_cfg_tsnr: d.append( ('CFG Mimicking from TSNR', 'adaptive_cfg', modules.patch.patch_settings[pid].adaptive_cfg)) @@ -377,11 +417,13 @@ def worker(): for li, (n, w) in enumerate(loras): if n != 'None': - d.append((f'LoRA {li + 1}', f'lora_combined_{li + 1}', f'{n} : {w}')) + d.append( + (f'LoRA {li + 1}', f'lora_combined_{li + 1}', f'{n} : {w}')) metadata_parser = None if async_task.save_metadata_to_images: - metadata_parser = modules.meta_parser.get_metadata_parser(async_task.metadata_scheme) + metadata_parser = modules.meta_parser.get_metadata_parser( + async_task.metadata_scheme) metadata_parser.set_data(task['log_positive_prompt'], task['positive'], task['log_negative_prompt'], task['negative'], async_task.steps, async_task.base_model_name, async_task.refiner_model_name, @@ -389,7 +431,8 @@ def worker(): d.append(('Metadata Scheme', 'metadata_scheme', async_task.metadata_scheme.value if async_task.save_metadata_to_images else async_task.save_metadata_to_images)) d.append(('Version', 'version', 'Fooocus v' + fooocus_version.version)) - img_paths.append(log(x, d, metadata_parser, async_task.output_format, task, persist_image)) + img_paths.append( + log(x, d, metadata_parser, async_task.output_format, task, persist_image)) return img_paths @@ -405,7 +448,8 @@ def worker(): cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) for task in async_task.cn_tasks[flags.cn_cpds]: cn_img, cn_stop, cn_weight = task cn_img = resize_image(HWC3(cn_img), width=width, height=height) @@ -416,7 +460,8 @@ def worker(): cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) for task in async_task.cn_tasks[flags.cn_ip]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -424,9 +469,11 @@ def worker(): # 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_path) + task[0] = ip_adapter.preprocess( + cn_img, ip_adapter_path=ip_adapter_path) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) for task in async_task.cn_tasks[flags.cn_ip_face]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -437,12 +484,16 @@ def worker(): # 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) + task[0] = ip_adapter.preprocess( + cn_img, ip_adapter_path=ip_adapter_face_path) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) - all_ip_tasks = async_task.cn_tasks[flags.cn_ip] + async_task.cn_tasks[flags.cn_ip_face] + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) + all_ip_tasks = async_task.cn_tasks[flags.cn_ip] + \ + async_task.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) + pipeline.final_unet = ip_adapter.patch_model( + pipeline.final_unet, all_ip_tasks) def apply_vary(async_task, uov_method, denoising_strength, uov_input_image, switch, current_progress, advance_progress=False): if 'subtle' in uov_method: @@ -469,7 +520,8 @@ def worker(): denoise=denoising_strength, refiner_swap_method=async_task.refiner_swap_method ) - initial_latent = core.encode_vae(vae=candidate_vae, pixels=initial_pixels) + 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 @@ -481,7 +533,8 @@ def worker(): inpaint_disable_initial_latent, current_progress, skip_apply_outpaint=False, advance_progress=False): if not skip_apply_outpaint: - inpaint_image, inpaint_mask = apply_outpaint(async_task, inpaint_image, inpaint_mask) + inpaint_image, inpaint_mask = apply_outpaint( + async_task, inpaint_image, inpaint_mask) inpaint_worker.current_task = inpaint_worker.InpaintWorker( image=inpaint_image, @@ -497,9 +550,12 @@ def worker(): if advance_progress: current_progress += 1 progressbar(async_task, current_progress, '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) + 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=async_task.steps, switch=switch, @@ -538,7 +594,8 @@ def worker(): B, C, H, W = latent_fill.shape height, width = H * 8, W * 8 final_height, final_width = inpaint_worker.current_task.image.shape[:2] - print(f'Final resolution is {str((final_width, final_height))}, latent is {str((width, height))}.') + print( + f'Final resolution is {str((final_width, final_height))}, latent is {str((width, height))}.') return denoising_strength, initial_latent, width, height, current_progress @@ -546,21 +603,25 @@ def worker(): if len(async_task.outpaint_selections) > 0: H, W, C = inpaint_image.shape if 'top' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[int(H * 0.3), 0], [0, 0], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[int(H * 0.3), 0], [0, 0], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[int(H * 0.3), 0], [0, 0]], mode='constant', constant_values=255) if 'bottom' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[0, int(H * 0.3)], [0, 0], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[0, int(H * 0.3)], [0, 0], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[0, int(H * 0.3)], [0, 0]], mode='constant', constant_values=255) H, W, C = inpaint_image.shape if 'left' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[0, 0], [int(W * 0.3), 0], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[0, 0], [int(W * 0.3), 0], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[0, 0], [int(W * 0.3), 0]], mode='constant', constant_values=255) if 'right' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[0, 0], [0, int(W * 0.3)], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[0, 0], [0, int(W * 0.3)], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[0, 0], [0, int(W * 0.3)]], mode='constant', constant_values=255) @@ -574,7 +635,8 @@ def worker(): H, W, C = uov_input_image.shape if advance_progress: current_progress += 1 - progressbar(async_task, current_progress, f'Upscaling image from {str((W, H))} ...') + progressbar(async_task, current_progress, + f'Upscaling image from {str((W, H))} ...') uov_input_image = perform_upscale(uov_input_image) print(f'Image upscaled.') if '1.5x' in uov_method: @@ -589,7 +651,8 @@ def worker(): uov_input_image = set_image_shape_ceil(uov_input_image, 1024) shape_ceil = 1024 else: - uov_input_image = resample_image(uov_input_image, width=W * f, height=H * f) + uov_input_image = resample_image( + uov_input_image, width=W * f, height=H * f) image_is_super_large = shape_ceil > 2800 if 'fast' in uov_method: direct_return = True @@ -640,15 +703,18 @@ def worker(): def process_prompt(async_task, prompt, negative_prompt, base_model_additional_loras, image_number, disable_seed_increment, use_expansion, use_style, use_synthetic_refiner, current_progress, advance_progress=False): - prompts = remove_empty_str([safe_str(p) for p in prompt.splitlines()], default='') - negative_prompts = remove_empty_str([safe_str(p) for p in negative_prompt.splitlines()], default='') + prompts = remove_empty_str([safe_str(p) + for p in prompt.splitlines()], default='') + negative_prompts = remove_empty_str( + [safe_str(p) for p in negative_prompt.splitlines()], default='') prompt = prompts[0] negative_prompt = negative_prompts[0] if prompt == '': # disable expansion when empty since it is not meaningful and influences image prompt use_expansion = False extra_positive_prompts = prompts[1:] if len(prompts) > 1 else [] - extra_negative_prompts = negative_prompts[1:] if len(negative_prompts) > 1 else [] + extra_negative_prompts = negative_prompts[1:] if len( + negative_prompts) > 1 else [] if advance_progress: current_progress += 1 progressbar(async_task, current_progress, 'Loading models ...') @@ -671,12 +737,16 @@ def worker(): if disable_seed_increment: task_seed = async_task.seed % (constants.MAX_SEED + 1) else: - task_seed = (async_task.seed + i) % (constants.MAX_SEED + 1) # randint is inclusive, % is not + # randint is inclusive, % is not + task_seed = (async_task.seed + i) % (constants.MAX_SEED + 1) - task_rng = random.Random(task_seed) # may bind to inpaint noise in the future - task_prompt = apply_wildcards(prompt, task_rng, i, async_task.read_wildcards_in_order) + # may bind to inpaint noise in the future + task_rng = random.Random(task_seed) + task_prompt = apply_wildcards( + prompt, task_rng, i, async_task.read_wildcards_in_order) task_prompt = apply_arrays(task_prompt, i) - task_negative_prompt = apply_wildcards(negative_prompt, task_rng, i, async_task.read_wildcards_in_order) + task_negative_prompt = apply_wildcards( + negative_prompt, task_rng, i, async_task.read_wildcards_in_order) task_extra_positive_prompts = [apply_wildcards(pmt, task_rng, i, async_task.read_wildcards_in_order) for pmt in extra_positive_prompts] @@ -695,24 +765,29 @@ def worker(): if s == random_style_name: s = get_random_style(task_rng) task_styles[j] = s - p, n, style_has_placeholder = apply_style(s, positive=task_prompt) + p, n, style_has_placeholder = apply_style( + s, positive=task_prompt) if style_has_placeholder: placeholder_replaced = True positive_basic_workloads = positive_basic_workloads + p negative_basic_workloads = negative_basic_workloads + n if not placeholder_replaced: - positive_basic_workloads = [task_prompt] + positive_basic_workloads + positive_basic_workloads = [ + task_prompt] + positive_basic_workloads else: positive_basic_workloads.append(task_prompt) - negative_basic_workloads.append(task_negative_prompt) # Always use independent workload for negative. + # Always use independent workload for negative. + negative_basic_workloads.append(task_negative_prompt) positive_basic_workloads = positive_basic_workloads + task_extra_positive_prompts negative_basic_workloads = negative_basic_workloads + task_extra_negative_prompts - positive_basic_workloads = remove_empty_str(positive_basic_workloads, default=task_prompt) - negative_basic_workloads = remove_empty_str(negative_basic_workloads, default=task_negative_prompt) + positive_basic_workloads = remove_empty_str( + positive_basic_workloads, default=task_prompt) + negative_basic_workloads = remove_empty_str( + negative_basic_workloads, default=task_negative_prompt) tasks.append(dict( task_seed=task_seed, @@ -725,8 +800,10 @@ def worker(): 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='\n'.join( + [task_prompt] + task_extra_positive_prompts), + log_negative_prompt='\n'.join( + [task_negative_prompt] + task_extra_negative_prompts), styles=task_styles )) if use_expansion: @@ -734,24 +811,31 @@ def worker(): current_progress += 1 for i, t in enumerate(tasks): - progressbar(async_task, current_progress, f'Preparing Fooocus text #{i + 1} ...') - expansion = pipeline.final_expansion(t['task_prompt'], t['task_seed']) + progressbar(async_task, current_progress, + 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. + # Deep copy. + t['positive'] = copy.deepcopy(t['positive']) + [expansion] if advance_progress: current_progress += 1 for i, t in enumerate(tasks): - progressbar(async_task, current_progress, f'Encoding positive #{i + 1} ...') - t['c'] = pipeline.clip_encode(texts=t['positive'], pool_top_k=t['positive_top_k']) + progressbar(async_task, current_progress, + f'Encoding positive #{i + 1} ...') + t['c'] = pipeline.clip_encode( + texts=t['positive'], pool_top_k=t['positive_top_k']) if advance_progress: current_progress += 1 for i, t in enumerate(tasks): if abs(float(async_task.cfg_scale) - 1.0) < 1e-4: t['uc'] = pipeline.clone_cond(t['c']) else: - progressbar(async_task, current_progress, f'Encoding negative #{i + 1} ...') - t['uc'] = pipeline.clip_encode(texts=t['negative'], pool_top_k=t['negative_top_k']) + progressbar(async_task, current_progress, + f'Encoding negative #{i + 1} ...') + t['uc'] = pipeline.clip_encode( + texts=t['negative'], pool_top_k=t['negative_top_k']) return tasks, use_expansion, loras, current_progress def apply_freeu(async_task): @@ -776,16 +860,20 @@ def worker(): if async_task.scheduler_name in ['lcm', 'tcd']: final_scheduler_name = 'sgm_uniform' if pipeline.final_unet is not None: - pipeline.final_unet = patch_discrete(pipeline.final_unet, async_task.scheduler_name) + pipeline.final_unet = patch_discrete( + pipeline.final_unet, async_task.scheduler_name) if pipeline.final_refiner_unet is not None: - pipeline.final_refiner_unet = patch_discrete(pipeline.final_refiner_unet, async_task.scheduler_name) + pipeline.final_refiner_unet = patch_discrete( + pipeline.final_refiner_unet, async_task.scheduler_name) elif async_task.scheduler_name == 'edm_playground_v2.5': final_scheduler_name = 'karras' if pipeline.final_unet is not None: - pipeline.final_unet = patch_edm(pipeline.final_unet, async_task.scheduler_name) + pipeline.final_unet = patch_edm( + pipeline.final_unet, async_task.scheduler_name) if pipeline.final_refiner_unet is not None: - pipeline.final_refiner_unet = patch_edm(pipeline.final_refiner_unet, async_task.scheduler_name) + pipeline.final_refiner_unet = patch_edm( + pipeline.final_refiner_unet, async_task.scheduler_name) return final_scheduler_name @@ -793,8 +881,10 @@ def worker(): print('Enter Hyper-SD mode.') if advance_progress: current_progress += 1 - progressbar(async_task, current_progress, 'Downloading Hyper-SD components ...') - async_task.performance_loras += [(modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)] + progressbar(async_task, current_progress, + 'Downloading Hyper-SD components ...') + async_task.performance_loras += [ + (modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)] if async_task.refiner_model_name != 'None': print(f'Refiner disabled in Hyper-SD mode.') async_task.refiner_model_name = 'None' @@ -814,7 +904,8 @@ def worker(): if advance_progress: current_progress += 1 progressbar(async_task, 1, 'Downloading Lightning components ...') - async_task.performance_loras += [(modules.config.downloading_sdxl_lightning_lora(), 1.0)] + async_task.performance_loras += [ + (modules.config.downloading_sdxl_lightning_lora(), 1.0)] if async_task.refiner_model_name != 'None': print(f'Refiner disabled in Lightning mode.') async_task.refiner_model_name = 'None' @@ -834,7 +925,8 @@ def worker(): if advance_progress: current_progress += 1 progressbar(async_task, 1, 'Downloading LCM components ...') - async_task.performance_loras += [(modules.config.downloading_sdxl_lcm_lora(), 1.0)] + async_task.performance_loras += [ + (modules.config.downloading_sdxl_lcm_lora(), 1.0)] if async_task.refiner_model_name != 'None': print(f'Refiner disabled in LCM mode.') async_task.refiner_model_name = 'None' @@ -878,13 +970,16 @@ def worker(): H, W, C = inpaint_image.shape async_task.inpaint_mask_image_upload = resample_image(async_task.inpaint_mask_image_upload, width=W, height=H) - async_task.inpaint_mask_image_upload = np.mean(async_task.inpaint_mask_image_upload, axis=2) + async_task.inpaint_mask_image_upload = np.mean( + async_task.inpaint_mask_image_upload, axis=2) async_task.inpaint_mask_image_upload = (async_task.inpaint_mask_image_upload > 127).astype( np.uint8) * 255 - inpaint_mask = np.maximum(inpaint_mask, async_task.inpaint_mask_image_upload) + inpaint_mask = np.maximum( + inpaint_mask, async_task.inpaint_mask_image_upload) if int(async_task.inpaint_erode_or_dilate) != 0: - inpaint_mask = erode_or_dilate(inpaint_mask, async_task.inpaint_erode_or_dilate) + inpaint_mask = erode_or_dilate( + inpaint_mask, async_task.inpaint_erode_or_dilate) if async_task.invert_mask_checkbox: inpaint_mask = 255 - inpaint_mask @@ -898,8 +993,10 @@ def worker(): progressbar(async_task, 1, 'Downloading inpainter ...') inpaint_head_model_path, inpaint_patch_model_path = modules.config.downloading_inpaint_models( async_task.inpaint_engine) - base_model_additional_loras += [(inpaint_patch_model_path, 1.0)] - print(f'[Inpaint] Current inpaint model is {inpaint_patch_model_path}') + base_model_additional_loras += [ + (inpaint_patch_model_path, 1.0)] + print( + f'[Inpaint] Current inpaint model is {inpaint_patch_model_path}') if async_task.refiner_model_name == 'None': use_synthetic_refiner = True async_task.refiner_switch = 0.8 @@ -922,14 +1019,26 @@ def worker(): if len(async_task.cn_tasks[flags.cn_cpds]) > 0: controlnet_cpds_path = modules.config.downloading_controlnet_cpds() if len(async_task.cn_tasks[flags.cn_ip]) > 0: - clip_vision_path, ip_negative_path, ip_adapter_path = modules.config.downloading_ip_adapters('ip') + clip_vision_path, ip_negative_path, ip_adapter_path = modules.config.downloading_ip_adapters( + 'ip') if len(async_task.cn_tasks[flags.cn_ip_face]) > 0: clip_vision_path, ip_negative_path, ip_adapter_face_path = modules.config.downloading_ip_adapters( 'face') if async_task.current_tab == 'enhance' and async_task.enhance_input_image is not None: goals.append('enhance') skip_prompt_processing = True - async_task.enhance_input_image = HWC3(async_task.enhance_input_image) + async_task.enhance_input_image = HWC3( + async_task.enhance_input_image) + + if async_task.bulk_enhance_enabled and async_task.bulk_enhance_data_type == 'Files' and len(async_task.bulk_enhance_file_explorer) > 0: + goals.append('bulk_enhance_files') + + skip_prompt_processing = True + + if async_task.bulk_enhance_enabled and async_task.bulk_enhance_data_type == 'Folder' and async_task.bulk_enhance_input_path: + goals.append('bulk_enhance_folder') + skip_prompt_processing = True + return base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, inpaint_head_model_path, inpaint_image, inpaint_mask, ip_adapter_face_path, ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner def prepare_upscale(async_task, goals, uov_input_image, uov_method, performance, steps, current_progress, @@ -947,7 +1056,8 @@ def worker(): if advance_progress: current_progress += 1 - progressbar(async_task, current_progress, 'Downloading upscale models ...') + progressbar(async_task, current_progress, + 'Downloading upscale models ...') modules.config.downloading_upscale_model() return uov_input_image, skip_prompt_processing, steps @@ -970,11 +1080,13 @@ def worker(): use_synthetic_refiner, width, show_intermediate_results=True, persist_image=True): base_model_additional_loras = [] inpaint_head_model_path = None - inpaint_parameterized = inpaint_engine != 'None' # inpaint_engine = None, improve detail + # inpaint_engine = None, improve detail + inpaint_parameterized = inpaint_engine != 'None' initial_latent = None prompt = prepare_enhance_prompt(prompt, async_task.prompt) - negative_prompt = prepare_enhance_prompt(negative_prompt, async_task.negative_prompt) + negative_prompt = prepare_enhance_prompt( + negative_prompt, async_task.negative_prompt) if 'vary' in goals: img, denoising_strength, initial_latent, width, height, current_progress = apply_vary( @@ -985,21 +1097,27 @@ def worker(): if direct_return: d = [('Upscale (Fast)', 'upscale_fast', '2x')] if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: - progressbar(async_task, current_progress, 'Checking for NSFW content ...') + progressbar(async_task, current_progress, + 'Checking for NSFW content ...') img = default_censor(img) - progressbar(async_task, current_progress, f'Saving image {current_task_id + 1}/{total_count} to system ...') - uov_image_path = log(img, d, output_format=async_task.output_format, persist_image=persist_image) + progressbar(async_task, current_progress, + f'Saving image {current_task_id + 1}/{total_count} to system ...') + uov_image_path = log( + img, d, output_format=async_task.output_format, persist_image=persist_image) yield_result(async_task, uov_image_path, current_progress, async_task.black_out_nsfw, False, do_not_show_finished_images=not show_intermediate_results or async_task.disable_intermediate_results) return current_progress, img, prompt, negative_prompt if 'inpaint' in goals and inpaint_parameterized: - progressbar(async_task, current_progress, 'Downloading inpainter ...') + progressbar(async_task, current_progress, + 'Downloading inpainter ...') inpaint_head_model_path, inpaint_patch_model_path = modules.config.downloading_inpaint_models( inpaint_engine) if inpaint_patch_model_path not in base_model_additional_loras: - base_model_additional_loras += [(inpaint_patch_model_path, 1.0)] - progressbar(async_task, current_progress, 'Preparing enhance prompts ...') + base_model_additional_loras += [ + (inpaint_patch_model_path, 1.0)] + progressbar(async_task, current_progress, + 'Preparing enhance prompts ...') # positive and negative conditioning aren't available here anymore, process prompt again tasks_enhance, use_expansion, loras, current_progress = process_prompt( async_task, prompt, negative_prompt, base_model_additional_loras, 1, True, @@ -1025,7 +1143,7 @@ def worker(): preparation_steps, total_count, show_intermediate_results, persist_image) - del task_enhance['c'], task_enhance['uc'] # Save memory + # del task_enhance['c'], task_enhance['uc'] # Save memory return current_progress, imgs[0], prompt, negative_prompt def enhance_upscale(all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, @@ -1035,7 +1153,8 @@ def worker(): # reset inpaint worker to prevent tensor size issues and not mix upscale and inpainting inpaint_worker.current_task = None - current_progress = int(base_progress + (100 - preparation_steps) / float(all_steps) * (done_steps_upscaling + done_steps_inpainting)) + current_progress = int(base_progress + (100 - preparation_steps) / + float(all_steps) * (done_steps_upscaling + done_steps_inpainting)) goals_enhance = [] img, skip_prompt_processing, steps = prepare_upscale( async_task, goals_enhance, img, async_task.enhance_uov_method, async_task.performance_selection, @@ -1057,263 +1176,58 @@ def worker(): async_task.last_stop = False # also skip all enhance steps for this image, but add the steps to the progress bar if async_task.enhance_uov_processing_order == flags.enhancement_uov_before: - done_steps_inpainting += len(async_task.enhance_ctrls) * enhance_steps + done_steps_inpainting += len( + async_task.enhance_ctrls) * enhance_steps exception_result = 'continue' else: print('User stopped') - exception_result = 'break' + gr.Error( + "\n\n πŸ’₯ Processing was interrupted by the user. Please try again. πŸ’₯\n\n ") + + except FileNotFoundError: + # Handle the case where the file is not found + gr.Error( + "\n\n πŸ’₯ The specified file was not found. Please check the file path and try again. πŸ“\n\n ") + + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️ A value error occurred: {ve}. Please check the input values. ⚠️\n\n ") + + except Exception as e: + # Handle any other unforeseen errors + gr.Error( + f"\n\n πŸ’₯ An unexpected error occurred: {e} πŸ’₯\n\n ") finally: done_steps_upscaling += steps return current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result - @torch.no_grad() - @torch.inference_mode() - def handler(async_task: AsyncTask): - preparation_start_time = time.perf_counter() - async_task.processing = True + def enhance_images( + imgs, + async_task, + current_task_id, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + current_task_number, + persist_image=True + ): - async_task.outpaint_selections = [o.lower() for o in async_task.outpaint_selections] - base_model_additional_loras = [] - async_task.uov_method = async_task.uov_method.casefold() - async_task.enhance_uov_method = async_task.enhance_uov_method.casefold() - - if fooocus_expansion in async_task.style_selections: - use_expansion = True - async_task.style_selections.remove(fooocus_expansion) - else: - use_expansion = False - - use_style = len(async_task.style_selections) > 0 - - if async_task.base_model_name == async_task.refiner_model_name: - print(f'Refiner disabled because base model and refiner are same.') - async_task.refiner_model_name = 'None' - - current_progress = 0 - if async_task.performance_selection == Performance.EXTREME_SPEED: - set_lcm_defaults(async_task, current_progress, advance_progress=True) - elif async_task.performance_selection == Performance.LIGHTNING: - set_lightning_defaults(async_task, current_progress, advance_progress=True) - elif async_task.performance_selection == Performance.HYPER_SD: - set_hyper_sd_defaults(async_task, current_progress, advance_progress=True) - - print(f'[Parameters] Adaptive CFG = {async_task.adaptive_cfg}') - print(f'[Parameters] CLIP Skip = {async_task.clip_skip}') - print(f'[Parameters] Sharpness = {async_task.sharpness}') - print(f'[Parameters] ControlNet Softness = {async_task.controlnet_softness}') - print(f'[Parameters] ADM Scale = ' - f'{async_task.adm_scaler_positive} : ' - f'{async_task.adm_scaler_negative} : ' - f'{async_task.adm_scaler_end}') - print(f'[Parameters] Seed = {async_task.seed}') - - apply_patch_settings(async_task) - - print(f'[Parameters] CFG = {async_task.cfg_scale}') - - initial_latent = None - denoising_strength = 1.0 - tiled = False - - width, height = async_task.aspect_ratios_selection.replace('Γ—', ' ').split(' ')[:2] - width, height = int(width), int(height) - - skip_prompt_processing = False - - inpaint_worker.current_task = None - inpaint_parameterized = async_task.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 - - goals = [] - tasks = [] - current_progress = 1 - - if async_task.input_image_checkbox: - base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, inpaint_head_model_path, inpaint_image, inpaint_mask, ip_adapter_face_path, ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner = apply_image_input( - async_task, base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, - goals, inpaint_head_model_path, inpaint_image, inpaint_mask, inpaint_parameterized, ip_adapter_face_path, - ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner) - - # Load or unload CNs - progressbar(async_task, current_progress, 'Loading control models ...') - 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) - - async_task.steps, switch, width, height = apply_overrides(async_task, async_task.steps, height, width) - - print(f'[Parameters] Sampler = {async_task.sampler_name} - {async_task.scheduler_name}') - print(f'[Parameters] Steps = {async_task.steps} - {switch}') - - progressbar(async_task, current_progress, 'Initializing ...') - - loras = async_task.loras - if not skip_prompt_processing: - tasks, use_expansion, loras, current_progress = process_prompt(async_task, async_task.prompt, async_task.negative_prompt, - base_model_additional_loras, async_task.image_number, - async_task.disable_seed_increment, use_expansion, use_style, - use_synthetic_refiner, current_progress, advance_progress=True) - - if len(goals) > 0: - current_progress += 1 - progressbar(async_task, current_progress, 'Image processing ...') - - should_enhance = async_task.enhance_checkbox and (async_task.enhance_uov_method != flags.disabled.casefold() or len(async_task.enhance_ctrls) > 0) - - if 'vary' in goals: - async_task.uov_input_image, denoising_strength, initial_latent, width, height, current_progress = apply_vary( - async_task, async_task.uov_method, denoising_strength, async_task.uov_input_image, switch, - current_progress) - - if 'upscale' in goals: - direct_return, async_task.uov_input_image, denoising_strength, initial_latent, tiled, width, height, current_progress = apply_upscale( - async_task, async_task.uov_input_image, async_task.uov_method, switch, current_progress, - advance_progress=True) - if direct_return: - d = [('Upscale (Fast)', 'upscale_fast', '2x')] - if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: - progressbar(async_task, 100, 'Checking for NSFW content ...') - async_task.uov_input_image = default_censor(async_task.uov_input_image) - progressbar(async_task, 100, 'Saving image to system ...') - uov_input_image_path = log(async_task.uov_input_image, d, output_format=async_task.output_format) - yield_result(async_task, uov_input_image_path, 100, async_task.black_out_nsfw, False, - do_not_show_finished_images=True) - return - - if 'inpaint' in goals: - try: - denoising_strength, initial_latent, width, height, current_progress = apply_inpaint(async_task, - initial_latent, - inpaint_head_model_path, - inpaint_image, - inpaint_mask, - inpaint_parameterized, - async_task.inpaint_strength, - async_task.inpaint_respective_field, - switch, - async_task.inpaint_disable_initial_latent, - current_progress, - advance_progress=True) - except EarlyReturnException: - return - - if 'cn' in goals: - apply_control_nets(async_task, height, ip_adapter_face_path, ip_adapter_path, width, current_progress) - if async_task.debugging_cn_preprocessor: - return - - if async_task.freeu_enabled: - apply_freeu(async_task) - - # async_task.steps can have value of uov steps here when upscale has been applied - steps, _, _, _ = apply_overrides(async_task, async_task.steps, height, width) - - images_to_enhance = [] - if 'enhance' in goals: - async_task.image_number = 1 - images_to_enhance += [async_task.enhance_input_image] - height, width, _ = async_task.enhance_input_image.shape - # input image already provided, processing is skipped - steps = 0 - yield_result(async_task, async_task.enhance_input_image, current_progress, async_task.black_out_nsfw, False, - async_task.disable_intermediate_results) - - all_steps = steps * async_task.image_number - - if async_task.enhance_checkbox and async_task.enhance_uov_method != flags.disabled.casefold(): - enhance_upscale_steps = async_task.performance_selection.steps() - if 'upscale' in async_task.enhance_uov_method: - if 'fast' in async_task.enhance_uov_method: - enhance_upscale_steps = 0 - else: - enhance_upscale_steps = async_task.performance_selection.steps_uov() - enhance_upscale_steps, _, _, _ = apply_overrides(async_task, enhance_upscale_steps, height, width) - enhance_upscale_steps_total = async_task.image_number * enhance_upscale_steps - all_steps += enhance_upscale_steps_total - - if async_task.enhance_checkbox and len(async_task.enhance_ctrls) != 0: - enhance_steps, _, _, _ = apply_overrides(async_task, async_task.original_steps, height, width) - all_steps += async_task.image_number * len(async_task.enhance_ctrls) * enhance_steps - - all_steps = max(all_steps, 1) - - 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() - preparation_start_time - print(f'Preparation time: {preparation_time:.2f} seconds') - - final_scheduler_name = patch_samplers(async_task) - print(f'Using {final_scheduler_name} scheduler.') - - async_task.yields.append(['preview', (current_progress, 'Moving model to GPU ...', None)]) - - processing_start_time = time.perf_counter() - - preparation_steps = current_progress - total_count = async_task.image_number - - def callback(step, x0, x, total_steps, y): - if step == 0: - async_task.callback_steps = 0 - async_task.callback_steps += (100 - preparation_steps) / float(all_steps) - async_task.yields.append(['preview', ( - int(current_progress + async_task.callback_steps), - f'Sampling step {step + 1}/{total_steps}, image {current_task_id + 1}/{total_count} ...', y)]) - - show_intermediate_results = len(tasks) > 1 or async_task.should_enhance - persist_image = not async_task.should_enhance or not async_task.save_final_enhanced_image_only - - for current_task_id, task in enumerate(tasks): - progressbar(async_task, current_progress, f'Preparing task {current_task_id + 1}/{async_task.image_number} ...') - execution_start_time = time.perf_counter() - - try: - imgs, img_paths, current_progress = process_task(all_steps, async_task, callback, controlnet_canny_path, - controlnet_cpds_path, current_task_id, - denoising_strength, final_scheduler_name, goals, - initial_latent, async_task.steps, switch, task['c'], - task['uc'], task, loras, tiled, use_expansion, width, - height, current_progress, preparation_steps, - async_task.image_number, show_intermediate_results, - persist_image) - - current_progress = int(preparation_steps + (100 - preparation_steps) / float(all_steps) * async_task.steps * (current_task_id + 1)) - images_to_enhance += imgs - - except ldm_patched.modules.model_management.InterruptProcessingException: - if async_task.last_stop == 'skip': - print('User skipped') - async_task.last_stop = False - continue - else: - print('User stopped') - break - - del task['c'], task['uc'] # Save memory - execution_time = time.perf_counter() - execution_start_time - print(f'Generating and saving time: {execution_time:.2f} seconds') - - if not async_task.should_enhance: - print(f'[Enhance] Skipping, preconditions aren\'t met') - stop_processing(async_task, processing_start_time) - return - - progressbar(async_task, current_progress, 'Processing enhance ...') + index = current_task_id + progressbar(async_task, current_progress, + 'Processing enhance ...') active_enhance_tabs = len(async_task.enhance_ctrls) should_process_enhance_uov = async_task.enhance_uov_method != flags.disabled.casefold() @@ -1323,49 +1237,65 @@ def worker(): active_enhance_tabs += 1 enhance_uov_before = async_task.enhance_uov_processing_order == flags.enhancement_uov_before enhance_uov_after = async_task.enhance_uov_processing_order == flags.enhancement_uov_after - total_count = len(images_to_enhance) * active_enhance_tabs - async_task.images_to_enhance_count = len(images_to_enhance) + total_count = 1 * active_enhance_tabs + async_task.images_to_enhance_count = 1 base_progress = current_progress current_task_id = -1 done_steps_upscaling = 0 done_steps_inpainting = 0 - enhance_steps, _, _, _ = apply_overrides(async_task, async_task.original_steps, height, width) + enhance_steps, _, _, _ = apply_overrides( + async_task, async_task.original_steps, height, width) exception_result = None - for index, img in enumerate(images_to_enhance): - async_task.enhance_stats[index] = 0 - enhancement_image_start_time = time.perf_counter() + # Ensure the dictionary is initialized + if not hasattr(async_task, 'enhance_stats'): + async_task.enhance_stats = {} + # Initialize the key if it's missing + async_task.enhance_stats.setdefault(index, 0) + # Initialize the key -1 with a default value of 0 if it's missing + async_task.enhance_stats.setdefault(-1, 0) + for img in imgs: + # Update enhance stats for each image + enhancement_image_start_time = time.perf_counter() last_enhance_prompt = async_task.prompt last_enhance_negative_prompt = async_task.negative_prompt + # Include the enhancement process here for each image + # This logic should be similar to what's in the loop enhancing images_to_enhance + if enhance_uov_before: - current_task_id += 1 - persist_image = not async_task.save_final_enhanced_image_only or active_enhance_tabs == 0 + # Enhance the image using the enhance_upscale function current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result = enhance_upscale( all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, current_task_id, denoising_strength, done_steps_inpainting, done_steps_upscaling, enhance_steps, async_task.prompt, async_task.negative_prompt, final_scheduler_name, height, img, preparation_steps, switch, tiled, total_count, use_expansion, use_style, use_synthetic_refiner, width, persist_image) - async_task.enhance_stats[index] += 1 if exception_result == 'continue': continue elif exception_result == 'break': break + # Continue with other enhancements as needed + # This involves the code for inpainting, masking, etc. + # For example, if you have another enhancement function like process_enhance, it should go here. # inpaint for all other tabs for enhance_mask_dino_prompt_text, enhance_prompt, enhance_negative_prompt, enhance_mask_model, enhance_mask_cloth_category, enhance_mask_sam_model, enhance_mask_text_threshold, enhance_mask_box_threshold, enhance_mask_sam_max_detections, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, enhance_inpaint_strength, enhance_inpaint_respective_field, enhance_inpaint_erode_or_dilate, enhance_mask_invert in async_task.enhance_ctrls: current_task_id += 1 - current_progress = int(base_progress + (100 - preparation_steps) / float(all_steps) * (done_steps_upscaling + done_steps_inpainting)) - progressbar(async_task, current_progress, f'Preparing enhancement {current_task_id + 1}/{total_count} ...') + current_progress = int(base_progress + (100 - preparation_steps) / float( + all_steps) * (done_steps_upscaling + done_steps_inpainting)) + progressbar(async_task, current_progress, + f'Preparing enhancement {current_task_number}/{total_count} ...') enhancement_task_start_time = time.perf_counter() - is_last_enhance_for_image = (current_task_id + 1) % active_enhance_tabs == 0 and not enhance_uov_after + is_last_enhance_for_image = ( + current_task_number) % active_enhance_tabs == 0 and not enhance_uov_after persist_image = not async_task.save_final_enhanced_image_only or is_last_enhance_for_image extras = {} if enhance_mask_model == 'sam': - print(f'[Enhance] Searching for "{enhance_mask_dino_prompt_text}"') + print( + f'[Enhance] Searching for "{enhance_mask_dino_prompt_text}"') elif enhance_mask_model == 'u2net_cloth_seg': extras['cloth_category'] = enhance_mask_cloth_category @@ -1383,23 +1313,29 @@ def worker(): mask = mask[:, :, 0] if int(enhance_inpaint_erode_or_dilate) != 0: - mask = erode_or_dilate(mask, enhance_inpaint_erode_or_dilate) + mask = erode_or_dilate( + mask, enhance_inpaint_erode_or_dilate) if enhance_mask_invert: mask = 255 - mask if async_task.debugging_enhance_masks_checkbox: - async_task.yields.append(['preview', (current_progress, 'Loading ...', mask)]) + async_task.yields.append( + ['preview', (current_progress, 'Loading ...', mask)]) yield_result(async_task, mask, current_progress, async_task.black_out_nsfw, False, async_task.disable_intermediate_results) async_task.enhance_stats[index] += 1 - print(f'[Enhance] {dino_detection_count} boxes detected') - print(f'[Enhance] {sam_detection_count} segments detected in boxes') - print(f'[Enhance] {sam_detection_on_mask_count} segments applied to mask') + print( + f'[Enhance] {dino_detection_count} boxes detected') + print( + f'[Enhance] {sam_detection_count} segments detected in boxes') + print( + f'[Enhance] {sam_detection_on_mask_count} segments applied to mask') if enhance_mask_model == 'sam' and (dino_detection_count == 0 or not async_task.debugging_dino and sam_detection_on_mask_count == 0): - print(f'[Enhance] No "{enhance_mask_dino_prompt_text}" detected, skipping') + print( + f'[Enhance] No "{enhance_mask_dino_prompt_text}" detected, skipping') continue goals_enhance = ['inpaint'] @@ -1420,7 +1356,6 @@ def worker(): last_enhance_prompt = enhance_prompt_processed if enhance_negative_prompt_processed != '': last_enhance_negative_prompt = enhance_negative_prompt_processed - except ldm_patched.modules.model_management.InterruptProcessingException: if async_task.last_stop == 'skip': print('User skipped') @@ -1428,13 +1363,29 @@ def worker(): continue else: print('User stopped') - exception_result = 'break' - break + gr.Error( + "\n\nπŸ’₯ Processing was interrupted by the user. Please try again. πŸ’₯ \n\n ") + + except FileNotFoundError: + # Handle the case where the file is not found + gr.Error( + "\n\nπŸ’₯ The specified file was not found. Please check the file path and try again. πŸ“\n\n ") + + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️ A value error occurred: {ve}. Please check the input values. ⚠️\n\n ") + + except Exception as e: + # Handle any other unforeseen errors + gr.Error( + f"\n\nπŸ’₯ An unexpected error occurred: {e} πŸ’₯\n\n") finally: done_steps_inpainting += enhance_steps enhancement_task_time = time.perf_counter() - enhancement_task_start_time - print(f'Enhancement time: {enhancement_task_time:.2f} seconds') + print( + f'Enhancement time: {enhancement_task_time:.2f} seconds') if exception_result == 'break': break @@ -1450,18 +1401,562 @@ def worker(): preparation_steps, switch, tiled, total_count, use_expansion, use_style, use_synthetic_refiner, width, persist_image) async_task.enhance_stats[index] += 1 - + if exception_result == 'continue': continue elif exception_result == 'break': break enhancement_image_time = time.perf_counter() - enhancement_image_start_time - print(f'Enhancement image time: {enhancement_image_time:.2f} seconds') + print( + f'Enhancement image time: {enhancement_image_time:.2f} seconds') + async_task.enhance_stats[-1] += 1 + + def callback(step, x0, x, total_steps, y): + global current_async_task + global current_task_id + global current_progress + global preparation_steps + global all_steps + global total_count + + # Check if current_async_task is not None to avoid AttributeError + if current_async_task is None: + raise ValueError("current_async_task is not set") + + if step == 0: + current_async_task.callback_steps = 0 + + # Calculate callback steps + current_async_task.callback_steps += (100 - + preparation_steps) / float(all_steps) + + # Append to yields + current_async_task.yields.append([ + 'preview', ( + int(current_progress + current_async_task.callback_steps), + f'Sampling step {step + 1}/{total_steps}, image {current_task_id + 1}/{total_count} ...', + y + ) + ]) + + @torch.no_grad() + @torch.inference_mode() + def handler(async_task: AsyncTask): + global current_async_task + global current_task_id + global current_progress + global preparation_steps + global all_steps + global total_count + current_async_task = async_task + + preparation_start_time = time.perf_counter() + async_task.processing = True + + async_task.outpaint_selections = [ + o.lower() for o in async_task.outpaint_selections] + base_model_additional_loras = [] + async_task.uov_method = async_task.uov_method.casefold() + async_task.enhance_uov_method = async_task.enhance_uov_method.casefold() + + if fooocus_expansion in async_task.style_selections: + use_expansion = True + async_task.style_selections.remove(fooocus_expansion) + else: + use_expansion = False + + use_style = len(async_task.style_selections) > 0 + + if async_task.base_model_name == async_task.refiner_model_name: + print(f'Refiner disabled because base model and refiner are same.') + async_task.refiner_model_name = 'None' + + current_progress = 0 + if async_task.performance_selection == Performance.EXTREME_SPEED: + set_lcm_defaults(async_task, current_progress, + advance_progress=True) + elif async_task.performance_selection == Performance.LIGHTNING: + set_lightning_defaults( + async_task, current_progress, advance_progress=True) + elif async_task.performance_selection == Performance.HYPER_SD: + set_hyper_sd_defaults( + async_task, current_progress, advance_progress=True) + + print(f'[Parameters] Adaptive CFG = {async_task.adaptive_cfg}') + print(f'[Parameters] CLIP Skip = {async_task.clip_skip}') + print(f'[Parameters] Sharpness = {async_task.sharpness}') + print( + f'[Parameters] ControlNet Softness = {async_task.controlnet_softness}') + print(f'[Parameters] ADM Scale = ' + f'{async_task.adm_scaler_positive} : ' + f'{async_task.adm_scaler_negative} : ' + f'{async_task.adm_scaler_end}') + print(f'[Parameters] Seed = {async_task.seed}') + + apply_patch_settings(async_task) + + print(f'[Parameters] CFG = {async_task.cfg_scale}') + + initial_latent = None + denoising_strength = 1.0 + tiled = False + + width, height = async_task.aspect_ratios_selection.replace('Γ—', ' ').split(' ')[ + :2] + width, height = int(width), int(height) + + skip_prompt_processing = False + + inpaint_worker.current_task = None + inpaint_parameterized = async_task.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 + + goals = [] + tasks = [] + current_progress = 1 + + if async_task.input_image_checkbox or async_task.bulk_enhance_enabled: + base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, inpaint_head_model_path, inpaint_image, inpaint_mask, ip_adapter_face_path, ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner = apply_image_input( + async_task, base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, + goals, inpaint_head_model_path, inpaint_image, inpaint_mask, inpaint_parameterized, ip_adapter_face_path, + ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner) + + # Load or unload CNs + progressbar(async_task, current_progress, 'Loading control models ...') + 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) + + async_task.steps, switch, width, height = apply_overrides( + async_task, async_task.steps, height, width) + + print( + f'[Parameters] Sampler = {async_task.sampler_name} - {async_task.scheduler_name}') + print(f'[Parameters] Steps = {async_task.steps} - {switch}') + + progressbar(async_task, current_progress, 'Initializing ...') + + loras = async_task.loras + if not skip_prompt_processing: + tasks, use_expansion, loras, current_progress = process_prompt(async_task, async_task.prompt, async_task.negative_prompt, + base_model_additional_loras, async_task.image_number, + async_task.disable_seed_increment, use_expansion, use_style, + use_synthetic_refiner, current_progress, advance_progress=True) + + if len(goals) > 0: + current_progress += 1 + progressbar(async_task, current_progress, 'Image processing ...') + + should_enhance = async_task.enhance_checkbox and ( + async_task.enhance_uov_method != flags.disabled.casefold() or len(async_task.enhance_ctrls) > 0) + + if 'vary' in goals: + async_task.uov_input_image, denoising_strength, initial_latent, width, height, current_progress = apply_vary( + async_task, async_task.uov_method, denoising_strength, async_task.uov_input_image, switch, + current_progress) + + if 'upscale' in goals: + direct_return, async_task.uov_input_image, denoising_strength, initial_latent, tiled, width, height, current_progress = apply_upscale( + async_task, async_task.uov_input_image, async_task.uov_method, switch, current_progress, + advance_progress=True) + if direct_return: + d = [('Upscale (Fast)', 'upscale_fast', '2x')] + if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: + progressbar(async_task, 100, + 'Checking for NSFW content ...') + async_task.uov_input_image = default_censor( + async_task.uov_input_image) + progressbar(async_task, 100, 'Saving image to system ...') + uov_input_image_path = log( + async_task.uov_input_image, d, output_format=async_task.output_format) + yield_result(async_task, uov_input_image_path, 100, async_task.black_out_nsfw, False, + do_not_show_finished_images=True) + return + + if 'inpaint' in goals: + try: + denoising_strength, initial_latent, width, height, current_progress = apply_inpaint(async_task, + initial_latent, + inpaint_head_model_path, + inpaint_image, + inpaint_mask, + inpaint_parameterized, + async_task.inpaint_strength, + async_task.inpaint_respective_field, + switch, + async_task.inpaint_disable_initial_latent, + current_progress, + advance_progress=True) + except EarlyReturnException: + return + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️ A value error occurred: {ve}. Please check the input values. ⚠️\n\n") + + except Exception as e: + # Handle any other unforeseen errors + gr.Error( + f" \n\nπŸ’₯ An unexpected error occurred: {e} πŸ’₯ \n\n") + + if 'cn' in goals: + apply_control_nets(async_task, height, ip_adapter_face_path, + ip_adapter_path, width, current_progress) + if async_task.debugging_cn_preprocessor: + return + + if async_task.freeu_enabled: + apply_freeu(async_task) + + # async_task.steps can have value of uov steps here when upscale has been applied + steps, _, _, _ = apply_overrides( + async_task, async_task.steps, height, width) + all_steps = steps * async_task.image_number + image_enhance = async_task.enhance_checkbox or async_task.bulk_enhance_enabled + if image_enhance and async_task.enhance_uov_method != flags.disabled.casefold(): + enhance_upscale_steps = async_task.performance_selection.steps() + if 'upscale' in async_task.enhance_uov_method: + if 'fast' in async_task.enhance_uov_method: + enhance_upscale_steps = 0 + else: + enhance_upscale_steps = async_task.performance_selection.steps_uov() + enhance_upscale_steps, _, _, _ = apply_overrides( + async_task, enhance_upscale_steps, height, width) + enhance_upscale_steps_total = async_task.image_number * enhance_upscale_steps + all_steps += enhance_upscale_steps_total + + if image_enhance and len(async_task.enhance_ctrls) != 0: + enhance_steps, _, _, _ = apply_overrides( + async_task, async_task.original_steps, height, width) + all_steps += async_task.image_number * \ + len(async_task.enhance_ctrls) * enhance_steps + + if image_enhance and len(async_task.enhance_ctrls) == 0 and async_task.enhance_uov_method == flags.disabled.casefold(): + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️ Warning - Enhancements will be skipped. ⚠️ \n\nNo Enhancements were selected. \n\n Please check the input values. \n\n") + + all_steps = max(all_steps, 1) + + 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() - preparation_start_time + print(f'Preparation time: {preparation_time:.2f} seconds') + + final_scheduler_name = patch_samplers(async_task) + print(f'Using {final_scheduler_name} scheduler.') + + async_task.yields.append( + ['preview', (current_progress, 'Moving model to GPU ...', None)]) + + processing_start_time = time.perf_counter() + + preparation_steps = current_progress + total_count = async_task.image_number + + show_intermediate_results = len(tasks) > 1 or async_task.should_enhance + persist_image = not async_task.should_enhance or not async_task.save_final_enhanced_image_only +# ENHANCEMENTS # + images_to_enhance = [] + if 'enhance' in goals: + async_task.image_number = 1 + images_to_enhance = [async_task.enhance_input_image] + height, width, _ = async_task.enhance_input_image.shape + # input image already provided, processing is skipped + steps = 0 + yield_result(async_task, async_task.enhance_input_image, current_progress, async_task.black_out_nsfw, False, + async_task.disable_intermediate_results) + enhance_images( + images_to_enhance, + async_task, + 0, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + 0 + ) + + if 'bulk_enhance_files' in goals: + for file in async_task.bulk_enhance_file_explorer: + try: + + # Open and preprocess the image + image = grh.Image(type='numpy')._format_image(Image.open( + file.orig_name)) + image = HWC3(image) + + # Add the image to the list + images_to_enhance = [image] + + _, height, width = image.shape # Unpack the shape into C, H, W + # input image already provided, processing is skipped + steps = 0 + yield_result(async_task, image, current_progress, async_task.black_out_nsfw, False, + async_task.disable_intermediate_results) + enhance_images( + images_to_enhance, + async_task, + 0, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + 0 + ) + except ldm_patched.modules.model_management.InterruptProcessingException: + if async_task.last_stop == 'skip': + print('User skipped') + async_task.last_stop = False + continue + else: + print('User stopped') + gr.Error( + "\n\nπŸ’₯Processing was interrupted by the user. Please try again.πŸ’₯ \n\n") + + except FileNotFoundError: + # Handle the case where the file is not found + gr.Error( + "\n\nπŸ’₯The specified file was not found. Please check the file path and try again. πŸ“ \n\n") + + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️A value error occurred: {ve}. Please check the input values. ⚠️\n\n") + + except Exception as e: + # Handle any other unforeseen errors + gr.Error( + f"\n\nπŸ’₯An unexpected error occurred: {e} πŸ’₯\n\n") + + if 'bulk_enhance_folder' in goals: + # Walk through the directory tree + for root, dirs, files_in_dir in os.walk(async_task.bulk_enhance_input_path): + try: + + for file_name in files_in_dir: + # Build full path to the file + full_file_path = os.path.join(root, file_name) + image = grh.Image(type='numpy')._format_image(Image.open( + full_file_path)) + image = HWC3(image) + + # Add the image to the list + images_to_enhance = [image] + + _, height, width = image.shape # Unpack the shape into C, H, W + + steps = 0 + yield_result(async_task, image, current_progress, async_task.black_out_nsfw, False, + async_task.disable_intermediate_results) + enhance_images( + images_to_enhance, + async_task, + 0, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + 0 + ) + except ldm_patched.modules.model_management.InterruptProcessingException: + if async_task.last_stop == 'skip': + print('User skipped') + async_task.last_stop = False + continue + else: + print('User stopped') + gr.Error( + "\n\nπŸ’₯Processing was interrupted by the user. Please try again.πŸ’₯\n\n") + + except FileNotFoundError: + # Handle the case where the file is not found + gr.Error( + "\n\nπŸ’₯The specified file was not found. Please check the file path and try again. πŸ“\n\n") + + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\n⚠️A value error occurred: {ve}. Please check the input values. ⚠️\n\n") + + except Exception as e: + # Handle any other unforeseen errors + gr.Error( + f"\n\nπŸ’₯An unexpected error occurred: {e} πŸ’₯\n\n") + +# MAIN GENERATION QUEUE # + for current_task_id, task in enumerate(tasks): + current_task_number = current_task_id + 1 + progressbar(async_task, current_progress, + f'Preparing task {current_task_number}/{async_task.image_number} ...') + + setup(async_task, current_task_number) + + execution_start_time = time.perf_counter() + + try: + imgs, img_paths, current_progress = process_task(all_steps, async_task, callback, controlnet_canny_path, + controlnet_cpds_path, current_task_id, + denoising_strength, final_scheduler_name, goals, + initial_latent, async_task.steps, switch, task[ + 'c'], + task['uc'], task, loras, tiled, use_expansion, width, + height, current_progress, preparation_steps, + async_task.image_number, show_intermediate_results, + persist_image) + + current_progress = int(preparation_steps + (100 - preparation_steps) / float( + all_steps) * async_task.steps * (current_task_id + 1)) + # images_to_enhance += imgs + if not async_task.should_enhance: + print(f'[Enhance] Skipping, preconditions aren\'t met') + stop_processing(async_task, processing_start_time) + return + + # Immediately enhance each image generated + + enhance_images( + imgs, + async_task, + current_task_id, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + current_task_number + ) + + except ldm_patched.modules.model_management.InterruptProcessingException: + if async_task.last_stop == 'skip': + print('User skipped') + async_task.last_stop = False + continue + else: + print('User stopped') + gr.Error( + "\n\nπŸ’₯Processing was interrupted by the user. Please try again.πŸ’₯\n\n") + + except FileNotFoundError: + # Handle the case where the file is not found + gr.Error( + "\n\nπŸ’₯The specified file was not found. Please check the file path and try again. πŸ“\n\n") + + except ValueError as ve: + # Handle value errors (e.g., invalid parameters) + gr.Warning( + f"\n\nπŸ’₯A value error occurred: {ve}. Please check the input values. ⚠️\n\n") + + except Exception as e: + # Handle any other unforeseen errors + gr.Error( + f"\n\nπŸ’₯An unexpected error occurred: {e} πŸ’₯\n\n") + + # del task['c'], task['uc'] # Save memory + execution_time = time.perf_counter() - execution_start_time + print(f'Generating and saving time: {execution_time:.2f} seconds') stop_processing(async_task, processing_start_time) return + def setup(async_task: AsyncTask, current_task_number): + if async_task.performance_selection == Performance.EXTREME_SPEED: + set_lcm_defaults(async_task, current_progress, + advance_progress=True) + elif async_task.performance_selection == Performance.LIGHTNING: + set_lightning_defaults( + async_task, current_progress, advance_progress=True) + elif async_task.performance_selection == Performance.HYPER_SD: + set_hyper_sd_defaults( + async_task, current_progress, advance_progress=True) + width, height = async_task.aspect_ratios_selection.replace('Γ—', ' ').split(' ')[ + :2] + width, height = int(width), int(height) + inpaint_worker.current_task = None + + 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 + + goals = [] + current_progress = current_task_number + # Load or unload CNs + progressbar(async_task, current_progress, 'Loading control models ...') + 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) + + async_task.steps, switch, width, height = apply_overrides( + async_task, async_task.steps, height, width) + progressbar(async_task, current_progress, 'Initializing ...') + + patch_samplers(async_task) + while True: time.sleep(0.01) if len(async_tasks) > 0: @@ -1476,6 +1971,9 @@ def worker(): except: traceback.print_exc() task.yields.append(['finish', task.results]) + + gr.Error( + f"\n\nπŸ’₯An unexpected error occurred: Please try again. πŸ’₯ \n\n") finally: if pid in modules.patch.patch_settings: del modules.patch.patch_settings[pid] diff --git a/modules/bulk_enhance_helpers.py b/modules/bulk_enhance_helpers.py new file mode 100644 index 00000000..6192defb --- /dev/null +++ b/modules/bulk_enhance_helpers.py @@ -0,0 +1,155 @@ +import gradio as gr +import random +import os +import json +import time +import shared +import modules.config +import fooocus_version +import modules.html +import modules.async_worker as worker +import modules.constants as constants +import modules.flags as flags +import modules.gradio_hijack as grh +import modules.style_sorter as style_sorter +import modules.meta_parser +import args_manager +import copy +import launch +from extras.inpaint_mask import SAMOptions + +from modules.sdxl_styles import legal_style_names +from modules.private_logger import get_current_html_path +from modules.ui_gradio_extensions import reload_javascript +from modules.auth import auth_enabled, check_auth +from modules.util import is_json +from tkinter import Tk, filedialog + + +def process_directories(directory_paths): + if not directory_paths: + return "No directories selected." + + results = [] + for directory in directory_paths: + # List files in the directory + files = os.listdir(directory) + results.append(f"Contents of {directory}:\n" + "\n".join(files)) + + return "\n\n".join(results) + + +def update_visibility(x): + # Add more updates for other components + return [gr.update(visible=x), gr.update(visible=x)] + + +def list_to_string(filenames): + # Join the filenames list into a comma-separated string + file_list = ', '.join(filenames) + return file_list + + +def on_browse(data_type): + root = Tk() + root.attributes("-topmost", True) + root.withdraw() + if data_type == "Files": + filenames = filedialog.askopenfilenames() + if len(filenames) > 0: + root.destroy() + file_list = list_to_string(filenames) + return file_list + else: + filename = "Files not seleceted" + root.destroy() + return None + + elif data_type == "Folder": + filename = filedialog.askdirectory() + if filename: + if os.path.isdir(filename): + root.destroy() + return str(filename) + else: + root.destroy() + return str(filename) + else: + filename = "Folder not seleceted" + root.destroy() + return None + + +def on_file_change(files, data_type): + if files and data_type == "Files": + return gr.update(visible=True), gr.update(), gr.update(value=True) + + # If no files are selected, hide file explorer and clear input_path + if not files and data_type == "Files": + return gr.update(visible=False), gr.update(value=""), gr.update(value=False) + + if data_type == "Folder": + return gr.update(visible=False), gr.update(), gr.update(value=True) + + return gr.update(visible=False), gr.update(), gr.update(value=False) + + +def on_input_change(input_path, file_explorer): + if os.path.isdir(input_path): + # Return an empty list if input_path is a directory or empty + return None, gr.update(visible=True), gr.update(value=True) + + if not input_path: + # Return an empty list if input_path is a directory or empty + return None, gr.update(visible=False), gr.update(value=False) + + # Initialize a dictionary to track unique file names and their paths + unique_file_paths = {} + + # Process the input_path string + if input_path: + # Clean up the input path string and split it into a list of file paths + file_paths_list = input_path.strip("()").replace("'", "").split(", ") + # Extract file names and ensure uniqueness + for path in file_paths_list: + file_name = os.path.basename(path) + unique_file_paths[file_name] = path + + # Process file_explorer items if provided + if file_explorer: + # Extract 'orig_name' from each file_explorer object and ensure uniqueness + for item in file_explorer: + file_name = os.path.basename(item.orig_name) + # Store the path, replacing any existing path with the same file name + unique_file_paths[file_name] = item.orig_name + + # Convert the dictionary values back to a list of unique file paths + if len(unique_file_paths.values()) > 0: + return list(unique_file_paths.values()), gr.update(visible=False), gr.update(value=True) + else: + return None, gr.update(visible=False), gr.update(value=False) + + +def on_click_clear(): + return None, None, gr.update(visible=False), gr.update(visible=False) + +# Function to set prompts based on the selected type + + +def update_prompts(selected_type): + # Ensure selected_type is a valid key and exists in the dictionary + if selected_type in modules.config.default_enhance_prompts: + positive_prompt = modules.config.default_enhance_prompts[selected_type]['positive'] + negative_prompt = modules.config.default_enhance_prompts[selected_type]['negative'] + return positive_prompt, negative_prompt + else: + # Returning default or empty values + return "Default positive prompt", "Default negative prompt" + + +def on_selection_change(selected_type): + # Get prompts based on selected_type + positive_prompt, negative_prompt = update_prompts(selected_type[0]) + + # Return the prompts + return positive_prompt, negative_prompt diff --git a/modules/config.py b/modules/config.py index 8609b415..02037125 100644 --- a/modules/config.py +++ b/modules/config.py @@ -7,7 +7,6 @@ import args_manager import tempfile import modules.flags import modules.sdxl_styles - from modules.model_loader import load_file_from_url from modules.extra_utils import makedirs_with_log, get_files_from_folder, try_eval_env_var from modules.flags import OutputFormat, Performance, MetadataScheme @@ -21,9 +20,11 @@ def get_config_path(key, default_value): else: return os.path.abspath(default_value) + wildcards_max_bfs_depth = 64 config_path = get_config_path('config_path', "./config.txt") -config_example_path = get_config_path('config_example_path', "config_modification_tutorial.txt") +config_example_path = get_config_path( + 'config_example_path', "config_modification_tutorial.txt") config_dict = {} always_save_keys = [] visited_keys = [] @@ -41,9 +42,11 @@ try: config_dict.update(json.load(json_file)) always_save_keys = list(config_dict.keys()) except Exception as e: - print(f'Failed to load config file "{config_path}" . The reason is: {str(e)}') + print( + f'Failed to load config file "{config_path}" . The reason is: {str(e)}') print('Please make sure that:') - print(f'1. The file "{config_path}" is a valid text file, and you have access to read it.') + print( + f'1. The file "{config_path}" is a valid text file, and you have access to read it.') print('2. Use "\\\\" instead of "\\" when describing paths.') print('3. There is no "," before the last "}".') print('4. All key/value formats are correct.') @@ -56,7 +59,8 @@ def try_load_deprecated_user_path_config(): return try: - deprecated_config_dict = json.load(open('user_path_config.txt', "r", encoding="utf-8")) + deprecated_config_dict = json.load( + open('user_path_config.txt', "r", encoding="utf-8")) def replace_config(old_key, new_key): if old_key in deprecated_config_dict: @@ -75,7 +79,8 @@ def try_load_deprecated_user_path_config(): replace_config('temp_outputs_path', 'path_outputs') if deprecated_config_dict.get("default_model", None) == 'juggernautXL_version6Rundiffusion.safetensors': - os.replace('user_path_config.txt', 'user_path_config-deprecated.txt') + os.replace('user_path_config.txt', + 'user_path_config-deprecated.txt') print('Config updated successfully in silence. ' 'A backup of previous config is written to "user_path_config-deprecated.txt".') return @@ -86,7 +91,8 @@ def try_load_deprecated_user_path_config(): print('Loading using deprecated old models and deprecated old configs.') return else: - os.replace('user_path_config.txt', 'user_path_config-deprecated.txt') + os.replace('user_path_config.txt', + 'user_path_config-deprecated.txt') print('Config updated successfully by user. ' 'A backup of previous config is written to "user_path_config-deprecated.txt".') return @@ -98,6 +104,7 @@ def try_load_deprecated_user_path_config(): try_load_deprecated_user_path_config() + def get_presets(): preset_folder = 'presets' presets = ['initial'] @@ -107,10 +114,12 @@ def get_presets(): return presets + [f[:f.index(".json")] for f in os.listdir(preset_folder) if f.endswith('.json')] + def update_presets(): global available_presets available_presets = get_presets() + def try_get_preset_content(preset): if isinstance(preset, str): preset_path = os.path.abspath(f'./presets/{preset}.json') @@ -127,18 +136,22 @@ def try_get_preset_content(preset): print(e) return {} + available_presets = get_presets() preset = args_manager.args.preset config_dict.update(try_get_preset_content(preset)) + def get_path_output() -> str: """ Checking output path argument and overriding default path. """ global config_dict - path_output = get_dir_or_set_default('path_outputs', '../outputs/', make_directory=True) + path_output = get_dir_or_set_default( + 'path_outputs', '../outputs/', make_directory=True) if args_manager.args.output_path: - print(f'Overriding config value path_outputs with {args_manager.args.output_path}') + print( + f'Overriding config value path_outputs with {args_manager.args.output_path}') config_dict['path_outputs'] = path_output = args_manager.args.output_path return path_output @@ -172,15 +185,18 @@ def get_dir_or_set_default(key, default_value, as_array=False, make_directory=Fa return v if v is not None: - print(f'Failed to load config key: {json.dumps({key:v})} is invalid or does not exist; will use {json.dumps({key:default_value})} instead.') + print( + f'Failed to load config key: {json.dumps({key:v})} is invalid or does not exist; will use {json.dumps({key:default_value})} instead.') if isinstance(default_value, list): dp = [] for path in default_value: - abs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), path)) + abs_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), path)) dp.append(abs_path) os.makedirs(abs_path, exist_ok=True) else: - dp = os.path.abspath(os.path.join(os.path.dirname(__file__), default_value)) + dp = os.path.abspath(os.path.join( + os.path.dirname(__file__), default_value)) os.makedirs(dp, exist_ok=True) if as_array: dp = [dp] @@ -188,18 +204,26 @@ def get_dir_or_set_default(key, default_value, as_array=False, make_directory=Fa return dp -paths_checkpoints = get_dir_or_set_default('path_checkpoints', ['../models/checkpoints/'], True) +paths_checkpoints = get_dir_or_set_default( + 'path_checkpoints', ['../models/checkpoints/'], True) paths_loras = get_dir_or_set_default('path_loras', ['../models/loras/'], True) -path_embeddings = get_dir_or_set_default('path_embeddings', '../models/embeddings/') -path_vae_approx = get_dir_or_set_default('path_vae_approx', '../models/vae_approx/') +path_embeddings = get_dir_or_set_default( + 'path_embeddings', '../models/embeddings/') +path_vae_approx = get_dir_or_set_default( + 'path_vae_approx', '../models/vae_approx/') path_vae = get_dir_or_set_default('path_vae', '../models/vae/') -path_upscale_models = get_dir_or_set_default('path_upscale_models', '../models/upscale_models/') +path_upscale_models = get_dir_or_set_default( + 'path_upscale_models', '../models/upscale_models/') path_inpaint = get_dir_or_set_default('path_inpaint', '../models/inpaint/') -path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlnet/') -path_clip_vision = get_dir_or_set_default('path_clip_vision', '../models/clip_vision/') -path_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion') +path_controlnet = get_dir_or_set_default( + 'path_controlnet', '../models/controlnet/') +path_clip_vision = get_dir_or_set_default( + 'path_clip_vision', '../models/clip_vision/') +path_fooocus_expansion = get_dir_or_set_default( + 'path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion') path_wildcards = get_dir_or_set_default('path_wildcards', '../wildcards/') -path_safety_checker = get_dir_or_set_default('path_safety_checker', '../models/safety_checker/') +path_safety_checker = get_dir_or_set_default( + 'path_safety_checker', '../models/safety_checker/') path_sam = get_dir_or_set_default('path_sam', '../models/sam/') path_outputs = get_path_output() @@ -209,7 +233,7 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_ if key not in visited_keys: visited_keys.append(key) - + v = os.getenv(key) if v is not None: v = try_eval_env_var(v, expected_type) @@ -228,7 +252,8 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_ return v else: if v is not None: - print(f'Failed to load config key: {json.dumps({key:v})} is invalid; will use {json.dumps({key:default_value})} instead.') + print( + f'Failed to load config key: {json.dumps({key:v})} is invalid; will use {json.dumps({key:default_value})} instead.') config_dict[key] = default_value return default_value @@ -274,7 +299,8 @@ default_base_model_name = default_model = get_config_item_or_set_default( previous_default_models = get_config_item_or_set_default( key='previous_default_models', default_value=[], - validator=lambda x: isinstance(x, list) and all(isinstance(k, str) for k in x), + validator=lambda x: isinstance(x, list) and all( + isinstance(k, str) for k in x), expected_type=list ) default_refiner_model_name = default_refiner = get_config_item_or_set_default( @@ -331,15 +357,18 @@ default_loras = get_config_item_or_set_default( ] ], validator=lambda x: isinstance(x, list) and all( - len(y) == 3 and isinstance(y[0], bool) and isinstance(y[1], str) and isinstance(y[2], numbers.Number) + len(y) == 3 and isinstance(y[0], bool) and isinstance( + y[1], str) and isinstance(y[2], numbers.Number) or len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number) for y in x), expected_type=list ) -default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else (True, y[0], y[1]) for y in default_loras] +default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else ( + True, y[0], y[1]) for y in default_loras] default_max_lora_number = get_config_item_or_set_default( key='default_max_lora_number', - default_value=len(default_loras) if isinstance(default_loras, list) and len(default_loras) > 0 else 5, + default_value=len(default_loras) if isinstance( + default_loras, list) and len(default_loras) > 0 else 5, validator=lambda x: isinstance(x, int) and x >= 1, expected_type=int ) @@ -380,7 +409,8 @@ default_styles = get_config_item_or_set_default( "Fooocus Enhance", "Fooocus Sharp" ], - validator=lambda x: isinstance(x, list) and all(y in modules.sdxl_styles.legal_style_names for y in x), + validator=lambda x: isinstance(x, list) and all( + y in modules.sdxl_styles.legal_style_names for y in x), expected_type=list ) default_prompt_negative = get_config_item_or_set_default( @@ -448,37 +478,43 @@ default_output_format = get_config_item_or_set_default( default_image_number = get_config_item_or_set_default( key='default_image_number', default_value=2, - validator=lambda x: isinstance(x, int) and 1 <= x <= default_max_image_number, + validator=lambda x: isinstance( + x, int) and 1 <= x <= default_max_image_number, expected_type=int ) checkpoint_downloads = get_config_item_or_set_default( key='checkpoint_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) lora_downloads = get_config_item_or_set_default( key='lora_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) embeddings_downloads = get_config_item_or_set_default( key='embeddings_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) vae_downloads = get_config_item_or_set_default( key='vae_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) available_aspect_ratios = get_config_item_or_set_default( key='available_aspect_ratios', default_value=modules.flags.sdxl_aspect_ratios, - validator=lambda x: isinstance(x, list) and all('*' in v for v in x) and len(x) > 1, + validator=lambda x: isinstance(x, list) and all( + '*' in v for v in x) and len(x) > 1, expected_type=list ) default_aspect_ratio = get_config_item_or_set_default( @@ -521,7 +557,8 @@ for image_count in range(default_controlnet_image_count): default_ip_images[image_count] = get_config_item_or_set_default( key=f'default_ip_image_{image_count}', default_value='None', - validator=lambda x: x == 'None' or isinstance(x, str) and os.path.exists(x), + validator=lambda x: x == 'None' or isinstance( + x, str) and os.path.exists(x), expected_type=str ) @@ -571,7 +608,8 @@ default_cfg_tsnr = get_config_item_or_set_default( default_clip_skip = get_config_item_or_set_default( key='default_clip_skip', default_value=2, - validator=lambda x: isinstance(x, int) and 1 <= x <= modules.flags.clip_skip_max, + validator=lambda x: isinstance( + x, int) and 1 <= x <= modules.flags.clip_skip_max, expected_type=int ) default_overwrite_step = get_config_item_or_set_default( @@ -596,7 +634,8 @@ example_inpaint_prompts = get_config_item_or_set_default( default_value=[ 'highly detailed face', 'detailed girl face', 'detailed man face', 'detailed hand', 'beautiful eyes' ], - validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x), + validator=lambda x: isinstance(x, list) and all( + isinstance(v, str) for v in x), expected_type=list ) example_enhance_detection_prompts = get_config_item_or_set_default( @@ -604,9 +643,38 @@ example_enhance_detection_prompts = get_config_item_or_set_default( default_value=[ 'face', 'eye', 'mouth', 'hair', 'hand', 'body' ], - validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x), + validator=lambda x: isinstance(x, list) and all( + isinstance(v, str) for v in x), expected_type=list ) + +default_enhance_prompts = { + 'face': { + 'positive': "Enhance the face to ensure clear and detailed features. The face should have a well-defined structure with smooth skin, natural contours, and a balanced complexion. Make sure the expression is natural and engaging.", + 'negative': "Avoid any blurriness or distortions in the face. Do not include uneven skin tones, unnatural facial expressions, or any missing facial features. Ensure there are no artifacts or unnatural smoothing that might distort the face's natural appearance." + }, + 'eye': { + 'positive': "Enhance the eyes to be clear, sharp, and vividly detailed. The eyes should have natural reflections and a realistic appearance. Ensure the irises and pupils are distinct, and there are no shadows or blurs affecting the eyes.", + 'negative': "Exclude any blurring, distortions, or unnatural reflections in the eyes. Avoid asymmetrical or misaligned eyes, and ensure there are no unnatural colors or artifacts that could detract from a realistic appearance." + }, + 'mouth': { + 'positive': "Enhance the mouth to appear natural and symmetrical. The lips should be smooth and well-defined, with no abnormalities. Ensure the mouth reflects a realistic expression and that teeth are visible only if naturally exposed.", + 'negative': "Avoid any distortions, asymmetry, or unnatural shapes in the mouth. Do not include missing or extra teeth, and ensure there are no anomalies or artifacts affecting the mouth's appearance." + }, + 'hair': { + 'positive': "Enhance the hair to look full, natural, and well-styled. The texture should be realistic, with clear individual strands or locks and natural shine. Ensure the color and style match the intended look without any unnatural effects.", + 'negative': "Exclude any unnatural textures, blurs, or artifacts in the hair. Avoid colors that look artificial or inconsistent, and ensure there are no missing or irregular sections of hair that could disrupt the natural appearance." + }, + 'hand': { + 'positive': "Enhance the hands to ensure all fingers are clearly visible and well-defined. The hands should have realistic textures and proportions, with no missing or distorted fingers. The overall appearance should be natural and proportional.", + 'negative': "Avoid any distortions or missing fingers in the hands. Do not include unnatural shapes or proportions, and ensure there are no anomalies or artifacts that affect the realistic appearance of the hands." + }, + 'body': { + 'positive': "Enhance the body to ensure a complete and natural appearance with all limbs properly defined. The body should reflect realistic proportions and posture, with no missing or distorted body parts. Ensure the overall shape and anatomy are natural and well-balanced.", + 'negative': "Exclude any missing limbs, distortions, or unrealistic body shapes. Avoid anomalies in body posture or proportions, and ensure there are no artifacts or inconsistencies that could affect the natural appearance of the body." + } +} + default_enhance_tabs = get_config_item_or_set_default( key='default_enhance_tabs', default_value=3, @@ -658,7 +726,8 @@ default_save_metadata_to_images = get_config_item_or_set_default( default_metadata_scheme = get_config_item_or_set_default( key='default_metadata_scheme', default_value=MetadataScheme.FOOOCUS.value, - validator=lambda x: x in [y[1] for y in modules.flags.metadata_scheme if y[1] == x], + validator=lambda x: x in [y[1] + for y in modules.flags.metadata_scheme if y[1] == x], expected_type=str ) metadata_created_by = get_config_item_or_set_default( @@ -669,7 +738,8 @@ metadata_created_by = get_config_item_or_set_default( ) example_inpaint_prompts = [[x] for x in example_inpaint_prompts] -example_enhance_detection_prompts = [[x] for x in example_enhance_detection_prompts] +example_enhance_detection_prompts = [[x] + for x in example_enhance_detection_prompts] default_invert_mask_checkbox = get_config_item_or_set_default( key='default_invert_mask_checkbox', @@ -719,7 +789,9 @@ default_describe_content_type = get_config_item_or_set_default( expected_type=list ) -config_dict["default_loras"] = default_loras = default_loras[:default_max_lora_number] + [[True, 'None', 1.0] for _ in range(default_max_lora_number - len(default_loras))] +config_dict["default_loras"] = default_loras = default_loras[:default_max_lora_number] + \ + [[True, 'None', 1.0] + for _ in range(default_max_lora_number - len(default_loras))] # mapping config to meta parameter possible_preset_keys = { @@ -759,7 +831,8 @@ REWRITE_PRESET = False if REWRITE_PRESET and isinstance(args_manager.args.preset, str): save_path = 'presets/' + args_manager.args.preset + '.json' with open(save_path, "w", encoding="utf-8") as json_file: - json.dump({k: config_dict[k] for k in possible_preset_keys}, json_file, indent=4) + json.dump({k: config_dict[k] + for k in possible_preset_keys}, json_file, indent=4) print(f'Preset saved to {save_path}. Exiting ...') exit(0) @@ -772,13 +845,15 @@ def add_ratio(x): default_aspect_ratio = add_ratio(default_aspect_ratio) -available_aspect_ratios_labels = [add_ratio(x) for x in available_aspect_ratios] +available_aspect_ratios_labels = [ + add_ratio(x) for x in available_aspect_ratios] # Only write config in the first launch. if not os.path.exists(config_path): with open(config_path, "w", encoding="utf-8") as json_file: - json.dump({k: config_dict[k] for k in always_save_keys}, json_file, indent=4) + json.dump({k: config_dict[k] + for k in always_save_keys}, json_file, indent=4) # Always write tutorials. @@ -799,7 +874,8 @@ wildcard_filenames = [] def get_model_filenames(folder_paths, extensions=None, name_filter=None): if extensions is None: - extensions = ['.pth', '.ckpt', '.bin', '.safetensors', '.fooocus.patch'] + extensions = ['.pth', '.ckpt', '.bin', + '.safetensors', '.fooocus.patch'] files = [] if not isinstance(folder_paths, list): @@ -913,14 +989,16 @@ def downloading_ip_adapters(v): model_dir=path_clip_vision, file_name='clip_vision_vit_h.safetensors' ) - results += [os.path.join(path_clip_vision, 'clip_vision_vit_h.safetensors')] + results += [os.path.join(path_clip_vision, + 'clip_vision_vit_h.safetensors')] load_file_from_url( url='https://huggingface.co/lllyasviel/misc/resolve/main/fooocus_ip_negative.safetensors', model_dir=path_controlnet, file_name='fooocus_ip_negative.safetensors' ) - results += [os.path.join(path_controlnet, 'fooocus_ip_negative.safetensors')] + results += [os.path.join(path_controlnet, + 'fooocus_ip_negative.safetensors')] if v == 'ip': load_file_from_url( @@ -928,7 +1006,8 @@ def downloading_ip_adapters(v): model_dir=path_controlnet, file_name='ip-adapter-plus_sdxl_vit-h.bin' ) - results += [os.path.join(path_controlnet, 'ip-adapter-plus_sdxl_vit-h.bin')] + results += [os.path.join(path_controlnet, + 'ip-adapter-plus_sdxl_vit-h.bin')] if v == 'face': load_file_from_url( @@ -936,7 +1015,8 @@ def downloading_ip_adapters(v): model_dir=path_controlnet, file_name='ip-adapter-plus-face_sdxl_vit-h.bin' ) - results += [os.path.join(path_controlnet, 'ip-adapter-plus-face_sdxl_vit-h.bin')] + results += [os.path.join(path_controlnet, + 'ip-adapter-plus-face_sdxl_vit-h.bin')] return results @@ -949,6 +1029,7 @@ def downloading_upscale_model(): ) return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin') + def downloading_safety_checker_model(): load_file_from_url( url='https://huggingface.co/mashb1t/misc/resolve/main/stable-diffusion-safety-checker.bin', diff --git a/modules/flags.py b/modules/flags.py index 05c29a23..a7425800 100644 --- a/modules/flags.py +++ b/modules/flags.py @@ -8,17 +8,21 @@ upscale_15 = 'Upscale (1.5x)' upscale_2 = 'Upscale (2x)' upscale_fast = 'Upscale (Fast 2x)' -uov_list = [disabled, subtle_variation, strong_variation, upscale_15, upscale_2, upscale_fast] +uov_list = [disabled, subtle_variation, strong_variation, + upscale_15, upscale_2, upscale_fast] enhancement_uov_before = "Before First Enhancement" enhancement_uov_after = "After Last Enhancement" -enhancement_uov_processing_order = [enhancement_uov_before, enhancement_uov_after] +enhancement_uov_processing_order = [ + enhancement_uov_before, enhancement_uov_after] enhancement_uov_prompt_type_original = 'Original Prompts' enhancement_uov_prompt_type_last_filled = 'Last Filled Enhancement Prompts' -enhancement_uov_prompt_types = [enhancement_uov_prompt_type_original, enhancement_uov_prompt_type_last_filled] +enhancement_uov_prompt_types = [ + enhancement_uov_prompt_type_original, enhancement_uov_prompt_type_last_filled] -CIVITAI_NO_KARRAS = ["euler", "euler_ancestral", "heun", "dpm_fast", "dpm_adaptive", "ddim", "uni_pc"] +CIVITAI_NO_KARRAS = ["euler", "euler_ancestral", "heun", + "dpm_fast", "dpm_adaptive", "ddim", "uni_pc"] # fooocus: a1111 (Civitai) KSAMPLER = { @@ -55,7 +59,8 @@ SAMPLERS = KSAMPLER | SAMPLER_EXTRA KSAMPLER_NAMES = list(KSAMPLER.keys()) -SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "lcm", "turbo", "align_your_steps", "tcd", "edm_playground_v2.5"] +SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", + "ddim_uniform", "lcm", "turbo", "align_your_steps", "tcd", "edm_playground_v2.5"] SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys()) sampler_list = SAMPLER_NAMES @@ -68,7 +73,8 @@ default_vae = 'Default (model)' refiner_swap_method = 'joint' default_input_image_tab = 'uov_tab' -input_image_tab_ids = ['uov_tab', 'ip_tab', 'inpaint_tab', 'describe_tab', 'enhance_tab', 'metadata_tab'] +input_image_tab_ids = ['uov_tab', 'ip_tab', 'inpaint_tab', + 'describe_tab', 'enhance_tab', 'metadata_tab'] cn_ip = "ImagePrompt" cn_ip_face = "FaceSwap" @@ -84,7 +90,8 @@ default_parameters = { output_formats = ['png', 'jpeg', 'webp'] -inpaint_mask_models = ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam'] +inpaint_mask_models = ['u2net', 'u2netp', 'u2net_human_seg', + 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam'] inpaint_mask_cloth_category = ['full', 'upper', 'lower'] inpaint_mask_sam_model = ['vit_b', 'vit_l', 'vit_h'] @@ -92,14 +99,15 @@ inpaint_engine_versions = ['None', 'v1', 'v2.5', 'v2.6'] inpaint_option_default = 'Inpaint or Outpaint (default)' inpaint_option_detail = 'Improve Detail (face, hand, eyes, etc.)' inpaint_option_modify = 'Modify Content (add objects, change background, etc.)' -inpaint_options = [inpaint_option_default, inpaint_option_detail, inpaint_option_modify] +inpaint_options = [inpaint_option_default, + inpaint_option_detail, inpaint_option_modify] describe_type_photo = 'Photograph' describe_type_anime = 'Art/Anime' describe_types = [describe_type_photo, describe_type_anime] sdxl_aspect_ratios = [ - '704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152', + '512*512', '704*704', '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', diff --git a/webui.py b/webui.py index b8159d85..1fa58e4b 100644 --- a/webui.py +++ b/webui.py @@ -23,6 +23,9 @@ from modules.private_logger import get_current_html_path from modules.ui_gradio_extensions import reload_javascript from modules.auth import auth_enabled, check_auth from modules.util import is_json +from tkinter import Tk, filedialog +from modules.bulk_enhance_helpers import * + def get_task(*args): args = list(args) @@ -30,6 +33,7 @@ def get_task(*args): return worker.AsyncTask(args=args) + def generate_clicked(task: worker.AsyncTask): import ldm_patched.modules.model_management as model_management @@ -58,7 +62,8 @@ def generate_clicked(task: worker.AsyncTask): # help bad internet connection by skipping duplicated preview if len(task.yields) > 0: # if we have the next item - if task.yields[0][0] == 'preview': # if the next item is also a preview + # if the next item is also a preview + if task.yields[0][0] == 'preview': # print('Skipped one preview for better internet connection.') continue @@ -73,8 +78,6 @@ def generate_clicked(task: worker.AsyncTask): gr.update(visible=True, value=product), \ gr.update(visible=False) if flag == 'finish': - if not args_manager.args.disable_enhance_output_sorting: - product = sort_enhance_images(product, task) yield gr.update(visible=False), \ gr.update(visible=False), \ @@ -122,7 +125,8 @@ def inpaint_mode_change(mode, inpaint_engine_version): if mode == modules.flags.inpaint_option_detail: return [ gr.update(visible=True), gr.update(visible=False, value=[]), - gr.Dataset.update(visible=True, samples=modules.config.example_inpaint_prompts), + gr.Dataset.update( + visible=True, samples=modules.config.example_inpaint_prompts), False, 'None', 0.5, 0.0 ] @@ -132,19 +136,22 @@ def inpaint_mode_change(mode, inpaint_engine_version): if mode == modules.flags.inpaint_option_modify: return [ gr.update(visible=True), gr.update(visible=False, value=[]), - gr.Dataset.update(visible=False, samples=modules.config.example_inpaint_prompts), + gr.Dataset.update( + visible=False, samples=modules.config.example_inpaint_prompts), True, inpaint_engine_version, 1.0, 0.0 ] return [ gr.update(visible=False, value=''), gr.update(visible=True), - gr.Dataset.update(visible=False, samples=modules.config.example_inpaint_prompts), + gr.Dataset.update( + visible=False, samples=modules.config.example_inpaint_prompts), False, inpaint_engine_version, 1.0, 0.618 ] reload_javascript() + title = f'Fooocus {fooocus_version.version}' if isinstance(args_manager.args.preset, str): @@ -165,7 +172,8 @@ with shared.gradio_root: progress_html = gr.HTML(value=modules.html.make_progress_html(32, 'Progress 32%'), visible=False, elem_id='progress-bar', elem_classes='progress-bar') gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', visible=True, height=768, - elem_classes=['resizable_area', 'main_view', 'final_gallery', 'image_gallery'], + elem_classes=[ + 'resizable_area', 'main_view', 'final_gallery', 'image_gallery'], elem_id='final_gallery') with gr.Row(): with gr.Column(scale=17): @@ -174,14 +182,20 @@ with shared.gradio_root: default_prompt = modules.config.default_prompt if isinstance(default_prompt, str) and default_prompt != '': - shared.gradio_root.load(lambda: default_prompt, outputs=prompt) + shared.gradio_root.load( + lambda: default_prompt, outputs=prompt) with gr.Column(scale=3, min_width=0): - generate_button = gr.Button(label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True) - reset_button = gr.Button(label="Reconnect", value="Reconnect", elem_classes='type_row', elem_id='reset_button', visible=False) - load_parameter_button = gr.Button(label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False) - skip_button = gr.Button(label="Skip", value="Skip", elem_classes='type_row_half', elem_id='skip_button', visible=False) - stop_button = gr.Button(label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False) + generate_button = gr.Button( + label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True) + reset_button = gr.Button(label="Reconnect", value="Reconnect", + elem_classes='type_row', elem_id='reset_button', visible=False) + load_parameter_button = gr.Button( + label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False) + skip_button = gr.Button( + label="Skip", value="Skip", elem_classes='type_row_half', elem_id='skip_button', visible=False) + stop_button = gr.Button( + label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False) def stop_clicked(currentTask): import ldm_patched.modules.model_management as model_management @@ -197,21 +211,31 @@ with shared.gradio_root: model_management.interrupt_current_processing() return currentTask - stop_button.click(stop_clicked, inputs=currentTask, outputs=currentTask, queue=False, show_progress=False, _js='cancelGenerateForever') - skip_button.click(skip_clicked, inputs=currentTask, outputs=currentTask, queue=False, show_progress=False) + stop_button.click(stop_clicked, inputs=currentTask, outputs=currentTask, + queue=False, show_progress=False, _js='cancelGenerateForever') + skip_button.click(skip_clicked, inputs=currentTask, + outputs=currentTask, queue=False, show_progress=False) with gr.Row(elem_classes='advanced_check_row'): - input_image_checkbox = gr.Checkbox(label='Input Image', value=modules.config.default_image_prompt_checkbox, container=False, elem_classes='min_check') - enhance_checkbox = gr.Checkbox(label='Enhance', value=modules.config.default_enhance_checkbox, container=False, elem_classes='min_check') - advanced_checkbox = gr.Checkbox(label='Advanced', value=modules.config.default_advanced_checkbox, container=False, elem_classes='min_check') + input_image_checkbox = gr.Checkbox( + label='Input Image', value=modules.config.default_image_prompt_checkbox, container=False, elem_classes='min_check') + enhance_checkbox = gr.Checkbox( + label='Enhance', value=modules.config.default_enhance_checkbox, container=False, elem_classes='min_check') + advanced_checkbox = gr.Checkbox( + label='Advanced', value=modules.config.default_advanced_checkbox, container=False, elem_classes='min_check') +# TABS + with gr.Row(visible=modules.config.default_image_prompt_checkbox) as image_input_panel: with gr.Tabs(selected=modules.config.default_selected_image_input_tab_id): with gr.Tab(label='Upscale or Variation', id='uov_tab') as uov_tab: with gr.Row(): with gr.Column(): - uov_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False) + uov_input_image = grh.Image( + label='Image', source='upload', type='numpy', show_label=False) with gr.Column(): - uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, value=modules.config.default_uov_method) - gr.HTML('\U0001F4D4 Documentation') + uov_method = gr.Radio( + label='Upscale or Variation:', choices=flags.uov_list, value=modules.config.default_uov_method) + gr.HTML( + '\U0001F4D4 Documentation') with gr.Tab(label='Image Prompt', id='ip_tab') as ip_tab: with gr.Row(): ip_images = [] @@ -223,33 +247,40 @@ with shared.gradio_root: for image_count in range(modules.config.default_controlnet_image_count): image_count += 1 with gr.Column(): - ip_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False, height=300, value=modules.config.default_ip_images[image_count]) + ip_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False, + height=300, value=modules.config.default_ip_images[image_count]) ip_images.append(ip_image) ip_ctrls.append(ip_image) with gr.Column(visible=modules.config.default_image_prompt_advanced_checkbox) as ad_col: with gr.Row(): - ip_stop = gr.Slider(label='Stop At', minimum=0.0, maximum=1.0, step=0.001, value=modules.config.default_ip_stop_ats[image_count]) + ip_stop = gr.Slider( + label='Stop At', minimum=0.0, maximum=1.0, step=0.001, value=modules.config.default_ip_stop_ats[image_count]) ip_stops.append(ip_stop) ip_ctrls.append(ip_stop) - ip_weight = gr.Slider(label='Weight', minimum=0.0, maximum=2.0, step=0.001, value=modules.config.default_ip_weights[image_count]) + ip_weight = gr.Slider( + label='Weight', minimum=0.0, maximum=2.0, step=0.001, value=modules.config.default_ip_weights[image_count]) ip_weights.append(ip_weight) ip_ctrls.append(ip_weight) - ip_type = gr.Radio(label='Type', choices=flags.ip_list, value=modules.config.default_ip_types[image_count], container=False) + ip_type = gr.Radio( + label='Type', choices=flags.ip_list, value=modules.config.default_ip_types[image_count], container=False) ip_types.append(ip_type) ip_ctrls.append(ip_type) - ip_type.change(lambda x: flags.default_parameters[x], inputs=[ip_type], outputs=[ip_stop, ip_weight], queue=False, show_progress=False) + ip_type.change(lambda x: flags.default_parameters[x], inputs=[ip_type], outputs=[ + ip_stop, ip_weight], queue=False, show_progress=False) ip_ad_cols.append(ad_col) - ip_advanced = gr.Checkbox(label='Advanced', value=modules.config.default_image_prompt_advanced_checkbox, container=False) + ip_advanced = gr.Checkbox( + label='Advanced', value=modules.config.default_image_prompt_advanced_checkbox, container=False) gr.HTML('* \"Image Prompt\" is powered by Fooocus Image Mixture Engine (v1.0.1). \U0001F4D4 Documentation') def ip_advance_checked(x): return [gr.update(visible=x)] * len(ip_ad_cols) + \ [flags.default_ip] * len(ip_types) + \ [flags.default_parameters[flags.default_ip][0]] * len(ip_stops) + \ - [flags.default_parameters[flags.default_ip][1]] * len(ip_weights) + [flags.default_parameters[flags.default_ip] + [1]] * len(ip_weights) ip_advanced.change(ip_advance_checked, inputs=ip_advanced, outputs=ip_ad_cols + ip_types + ip_stops + ip_weights, @@ -258,29 +289,40 @@ with shared.gradio_root: with gr.Tab(label='Inpaint or Outpaint', id='inpaint_tab') as inpaint_tab: with gr.Row(): with gr.Column(): - inpaint_input_image = grh.Image(label='Image', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas', show_label=False) - inpaint_advanced_masking_checkbox = gr.Checkbox(label='Enable Advanced Masking Features', value=modules.config.default_inpaint_advanced_masking_checkbox) - inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, value=modules.config.default_inpaint_method, label='Method') - inpaint_additional_prompt = gr.Textbox(placeholder="Describe what you want to inpaint.", elem_id='inpaint_additional_prompt', label='Inpaint Additional Prompt', visible=False) - outpaint_selections = gr.CheckboxGroup(choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint Direction') + inpaint_input_image = grh.Image(label='Image', source='upload', type='numpy', tool='sketch', + height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas', show_label=False) + inpaint_advanced_masking_checkbox = gr.Checkbox( + label='Enable Advanced Masking Features', value=modules.config.default_inpaint_advanced_masking_checkbox) + inpaint_mode = gr.Dropdown( + choices=modules.flags.inpaint_options, value=modules.config.default_inpaint_method, label='Method') + inpaint_additional_prompt = gr.Textbox( + placeholder="Describe what you want to inpaint.", elem_id='inpaint_additional_prompt', label='Inpaint Additional Prompt', visible=False) + outpaint_selections = gr.CheckboxGroup( + choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint Direction') example_inpaint_prompts = gr.Dataset(samples=modules.config.example_inpaint_prompts, label='Additional Prompt Quick List', - components=[inpaint_additional_prompt], + components=[ + inpaint_additional_prompt], visible=False) - gr.HTML('* Powered by Fooocus Inpaint Engine \U0001F4D4 Documentation') - example_inpaint_prompts.click(lambda x: x[0], inputs=example_inpaint_prompts, outputs=inpaint_additional_prompt, show_progress=False, queue=False) + gr.HTML( + '* Powered by Fooocus Inpaint Engine \U0001F4D4 Documentation') + example_inpaint_prompts.click( + lambda x: x[0], inputs=example_inpaint_prompts, outputs=inpaint_additional_prompt, show_progress=False, queue=False) with gr.Column(visible=modules.config.default_inpaint_advanced_masking_checkbox) as inpaint_mask_generation_col: - inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", mask_opacity=1, elem_id='inpaint_mask_canvas') - invert_mask_checkbox = gr.Checkbox(label='Invert Mask When Generating', value=modules.config.default_invert_mask_checkbox) + inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', tool='sketch', + height=500, brush_color="#FFFFFF", mask_opacity=1, elem_id='inpaint_mask_canvas') + invert_mask_checkbox = gr.Checkbox( + label='Invert Mask When Generating', value=modules.config.default_invert_mask_checkbox) inpaint_mask_model = gr.Dropdown(label='Mask generation model', choices=flags.inpaint_mask_models, value=modules.config.default_inpaint_mask_model) inpaint_mask_cloth_category = gr.Dropdown(label='Cloth category', - choices=flags.inpaint_mask_cloth_category, - value=modules.config.default_inpaint_mask_cloth_category, - visible=False) - inpaint_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', value='', visible=False, info='Use singular whenever possible', placeholder='Describe what you want to detect.') + choices=flags.inpaint_mask_cloth_category, + value=modules.config.default_inpaint_mask_cloth_category, + visible=False) + inpaint_mask_dino_prompt_text = gr.Textbox( + label='Detection prompt', value='', visible=False, info='Use singular whenever possible', placeholder='Describe what you want to detect.') example_inpaint_mask_dino_prompt_text = gr.Dataset( samples=modules.config.example_enhance_detection_prompts, label='Detection Prompt Quick List', @@ -292,11 +334,16 @@ with shared.gradio_root: show_progress=False, queue=False) with gr.Accordion("Advanced options", visible=False, open=False) as inpaint_mask_advanced_options: - inpaint_mask_sam_model = gr.Dropdown(label='SAM model', choices=flags.inpaint_mask_sam_model, value=modules.config.default_inpaint_mask_sam_model) - inpaint_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, maximum=1.0, value=0.3, step=0.05) - inpaint_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, maximum=1.0, value=0.25, step=0.05) - inpaint_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", info="Set to 0 to detect all", minimum=0, maximum=10, value=modules.config.default_sam_max_detections, step=1, interactive=True) - generate_mask_button = gr.Button(value='Generate mask from image') + inpaint_mask_sam_model = gr.Dropdown( + label='SAM model', choices=flags.inpaint_mask_sam_model, value=modules.config.default_inpaint_mask_sam_model) + inpaint_mask_box_threshold = gr.Slider( + label="Box Threshold", minimum=0.0, maximum=1.0, value=0.3, step=0.05) + inpaint_mask_text_threshold = gr.Slider( + label="Text Threshold", minimum=0.0, maximum=1.0, value=0.25, step=0.05) + inpaint_mask_sam_max_detections = gr.Slider( + label="Maximum number of detections", info="Set to 0 to detect all", minimum=0, maximum=10, value=modules.config.default_sam_max_detections, step=1, interactive=True) + generate_mask_button = gr.Button( + value='Generate mask from image') def generate_mask(image, mask_model, cloth_category, dino_prompt_text, sam_model, box_threshold, text_threshold, sam_max_detections, dino_erode_or_dilate, dino_debug): from extras.inpaint_mask import generate_mask_from_image @@ -316,11 +363,11 @@ with shared.gradio_root: model_type=sam_model ) - mask, _, _, _ = generate_mask_from_image(image, mask_model, extras, sam_options) + mask, _, _, _ = generate_mask_from_image( + image, mask_model, extras, sam_options) return mask - inpaint_mask_model.change(lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] + [gr.update(visible=x == 'sam')] * 2 + [gr.Dataset.update(visible=x == 'sam', @@ -335,19 +382,25 @@ with shared.gradio_root: with gr.Tab(label='Describe', id='describe_tab') as describe_tab: with gr.Row(): with gr.Column(): - describe_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False) + describe_input_image = grh.Image( + label='Image', source='upload', type='numpy', show_label=False) with gr.Column(): describe_methods = gr.CheckboxGroup( label='Content Type', choices=flags.describe_types, value=modules.config.default_describe_content_type) - describe_apply_styles = gr.Checkbox(label='Apply Styles', value=modules.config.default_describe_apply_prompts_checkbox) - describe_btn = gr.Button(value='Describe this Image into Prompt') - describe_image_size = gr.Textbox(label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False) - gr.HTML('\U0001F4D4 Documentation') + describe_apply_styles = gr.Checkbox( + label='Apply Styles', value=modules.config.default_describe_apply_prompts_checkbox) + describe_btn = gr.Button( + value='Describe this Image into Prompt') + describe_image_size = gr.Textbox( + label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False) + gr.HTML( + '\U0001F4D4 Documentation') def trigger_show_image_properties(image): - value = modules.util.get_image_size_info(image, modules.flags.sdxl_aspect_ratios) + value = modules.util.get_image_size_info( + image, modules.flags.sdxl_aspect_ratios) return gr.update(value=value, visible=True) describe_input_image.upload(trigger_show_image_properties, inputs=describe_input_image, @@ -356,17 +409,22 @@ with shared.gradio_root: with gr.Tab(label='Enhance', id='enhance_tab') as enhance_tab: with gr.Row(): with gr.Column(): - enhance_input_image = grh.Image(label='Use with Enhance, skips image generation', source='upload', type='numpy') - gr.HTML('\U0001F4D4 Documentation') + enhance_input_image = grh.Image( + label='Use with Enhance, skips image generation', source='upload', type='numpy') + gr.HTML( + '\U0001F4D4 Documentation') with gr.Tab(label='Metadata', id='metadata_tab') as metadata_tab: with gr.Column(): - metadata_input_image = grh.Image(label='For images created by Fooocus', source='upload', type='pil') + metadata_input_image = grh.Image( + label='For images created by Fooocus', source='upload', type='pil') metadata_json = gr.JSON(label='Metadata') - metadata_import_button = gr.Button(value='Apply Metadata') + metadata_import_button = gr.Button( + value='Apply Metadata') def trigger_metadata_preview(file): - parameters, metadata_scheme = modules.meta_parser.read_info_from_image(file) + parameters, metadata_scheme = modules.meta_parser.read_info_from_image( + file) results = {} if parameters is not None: @@ -380,180 +438,270 @@ with shared.gradio_root: metadata_input_image.upload(trigger_metadata_preview, inputs=metadata_input_image, outputs=metadata_json, queue=False, show_progress=True) +# BULK ENHANCE # + bulk_enhance_ctrls = [] with gr.Row(visible=modules.config.default_enhance_checkbox) as enhance_input_panel: with gr.Tabs(): - with gr.Tab(label='Upscale or Variation'): + with gr.Tab(label='Bulk Enhance'): + bulk_enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check', + container=False, visible=False) + # Create a FileExplorer component + with gr.Row(): + bulk_enhance_data_type = gr.Radio( + choices=["Files", "Folder"], value="Files", label="Select Files or Folder:") + + with gr.Row(elem_id="file_row", visible=False) as bulk_enhance_file_row: + bulk_enhance_file_explorer = gr.File( + label="Selected Files", + file_count="multiple", # or "single" for single file selection + root_dir=".", # Specify root directory if needed + show_label=True, + elem_id="file_explorer", + name="file_explorer" + ) + + with gr.Row(elem_id="folder_row", visible=False) as bulk_enhance_folder_row: + + bulk_enhance_input_path = gr.Textbox( + label="Selected Folder", max_lines=1, show_label=True, scale=5, interactive=False) with gr.Row(): with gr.Column(): - enhance_uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, - value=modules.config.default_enhance_uov_method) - enhance_uov_processing_order = gr.Radio(label='Order of Processing', - info='Use before to enhance small details and after to enhance large areas.', - choices=flags.enhancement_uov_processing_order, - value=modules.config.default_enhance_uov_processing_order) - enhance_uov_prompt_type = gr.Radio(label='Prompt', - info='Choose which prompt to use for Upscale or Variation.', - choices=flags.enhancement_uov_prompt_types, - value=modules.config.default_enhance_uov_prompt_type, - visible=modules.config.default_enhance_uov_processing_order == flags.enhancement_uov_after) + pass - enhance_uov_processing_order.change(lambda x: gr.update(visible=x == flags.enhancement_uov_after), - inputs=enhance_uov_processing_order, - outputs=enhance_uov_prompt_type, - queue=False, show_progress=False) - gr.HTML('\U0001F4D4 Documentation') - enhance_ctrls = [] - enhance_inpaint_mode_ctrls = [] - enhance_inpaint_engine_ctrls = [] - enhance_inpaint_update_ctrls = [] - for index in range(modules.config.default_enhance_tabs): - with gr.Tab(label=f'#{index + 1}') as enhance_tab_item: - enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check', - container=False) + with gr.Column(): + with gr.Blocks(): + with gr.Row(): + bulk_enhance_image_browse_btn = gr.Button( + "Browse", elem_id="browse-button", size="sm") - enhance_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', - info='Use singular whenever possible', - placeholder='Describe what you want to detect.', - interactive=True, - visible=modules.config.default_enhance_inpaint_mask_model == 'sam') - example_enhance_mask_dino_prompt_text = gr.Dataset( - samples=modules.config.example_enhance_detection_prompts, - label='Detection Prompt Quick List', - components=[enhance_mask_dino_prompt_text], - visible=modules.config.default_enhance_inpaint_mask_model == 'sam') - example_enhance_mask_dino_prompt_text.click(lambda x: x[0], - inputs=example_enhance_mask_dino_prompt_text, - outputs=enhance_mask_dino_prompt_text, - show_progress=False, queue=False) + bulk_enhance_clear_button = gr.ClearButton(elem_id="clear-button", + size="sm") - enhance_prompt = gr.Textbox(label="Enhancement positive prompt", - placeholder="Uses original prompt instead if empty.", - elem_id='enhance_prompt') - enhance_negative_prompt = gr.Textbox(label="Enhancement negative prompt", - placeholder="Uses original negative prompt instead if empty.", - elem_id='enhance_negative_prompt') + bulk_enhance_clear_button.click(on_click_clear, outputs=[ + bulk_enhance_file_explorer, bulk_enhance_input_path, bulk_enhance_file_row, bulk_enhance_folder_row]) + with gr.Column(): + pass - with gr.Accordion("Detection", open=False): - enhance_mask_model = gr.Dropdown(label='Mask generation model', - choices=flags.inpaint_mask_models, - value=modules.config.default_enhance_inpaint_mask_model) - enhance_mask_cloth_category = gr.Dropdown(label='Cloth category', - choices=flags.inpaint_mask_cloth_category, - value=modules.config.default_inpaint_mask_cloth_category, - visible=modules.config.default_enhance_inpaint_mask_model == 'u2net_cloth_seg', - interactive=True) + bulk_enhance_ctrls += [ + bulk_enhance_enabled, + bulk_enhance_data_type, + bulk_enhance_file_explorer, + bulk_enhance_input_path + ] + with gr.Row(visible=modules.config.default_enhance_checkbox) as enhance_input_panel2: - with gr.Accordion("SAM Options", - visible=modules.config.default_enhance_inpaint_mask_model == 'sam', - open=False) as sam_options: - enhance_mask_sam_model = gr.Dropdown(label='SAM model', - choices=flags.inpaint_mask_sam_model, - value=modules.config.default_inpaint_mask_sam_model, - interactive=True) - enhance_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, - maximum=1.0, value=0.3, step=0.05, - interactive=True) - enhance_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, - maximum=1.0, value=0.25, step=0.05, - interactive=True) - enhance_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", - info="Set to 0 to detect all", - minimum=0, maximum=10, - value=modules.config.default_sam_max_detections, - step=1, interactive=True) + with gr.Row(): + with gr.Tabs(): + with gr.Tab(label='Upscale or Variation'): + with gr.Row(): + with gr.Column(): + enhance_uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, + value=modules.config.default_enhance_uov_method) + enhance_uov_processing_order = gr.Radio(label='Order of Processing', + info='Use before to enhance small details and after to enhance large areas.', + choices=flags.enhancement_uov_processing_order, + value=modules.config.default_enhance_uov_processing_order) + enhance_uov_prompt_type = gr.Radio(label='Prompt', + info='Choose which prompt to use for Upscale or Variation.', + choices=flags.enhancement_uov_prompt_types, + value=modules.config.default_enhance_uov_prompt_type, + visible=modules.config.default_enhance_uov_processing_order == flags.enhancement_uov_after) - with gr.Accordion("Inpaint", visible=True, open=False): - enhance_inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, - value=modules.config.default_inpaint_method, - label='Method', interactive=True) - enhance_inpaint_disable_initial_latent = gr.Checkbox( - label='Disable initial latent in inpaint', value=False) - enhance_inpaint_engine = gr.Dropdown(label='Inpaint Engine', - value=modules.config.default_inpaint_engine_version, - choices=flags.inpaint_engine_versions, - info='Version of Fooocus inpaint model. If set, use performance Quality or Speed (no performance LoRAs) for best results.') - enhance_inpaint_strength = gr.Slider(label='Inpaint Denoising Strength', - minimum=0.0, maximum=1.0, step=0.001, - value=1.0, - info='Same as the denoising strength in A1111 inpaint. ' - 'Only used in inpaint, not used in outpaint. ' - '(Outpaint always use 1.0)') - enhance_inpaint_respective_field = gr.Slider(label='Inpaint Respective Field', - minimum=0.0, maximum=1.0, step=0.001, - value=0.618, - info='The area to inpaint. ' - 'Value 0 is same as "Only Masked" in A1111. ' - 'Value 1 is same as "Whole Image" in A1111. ' - 'Only used in inpaint, not used in outpaint. ' - '(Outpaint always use 1.0)') - enhance_inpaint_erode_or_dilate = gr.Slider(label='Mask Erode or Dilate', - minimum=-64, maximum=64, step=1, value=0, - info='Positive value will make white area in the mask larger, ' - 'negative value will make white area smaller. ' - '(default is 0, always processed before any mask invert)') - enhance_mask_invert = gr.Checkbox(label='Invert Mask', value=False) + enhance_uov_processing_order.change(lambda x: gr.update(visible=x == flags.enhancement_uov_after), + inputs=enhance_uov_processing_order, + outputs=enhance_uov_prompt_type, + queue=False, show_progress=False) + gr.HTML( + '\U0001F4D4 Documentation') + enhance_ctrls = [] + enhance_inpaint_mode_ctrls = [] + enhance_inpaint_engine_ctrls = [] + enhance_inpaint_update_ctrls = [] + for index in range(modules.config.default_enhance_tabs): + with gr.Tab(label=f'#{index + 1}') as enhance_tab_item: + enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check', + container=False) - gr.HTML('\U0001F4D4 Documentation') + enhance_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', + info='Use singular whenever possible', + placeholder='Describe what you want to detect.', + interactive=True, + visible=modules.config.default_enhance_inpaint_mask_model == 'sam') + example_enhance_mask_dino_prompt_text = gr.Dataset( + samples=modules.config.example_enhance_detection_prompts, + label='Detection Prompt Quick List', + components=[enhance_mask_dino_prompt_text], + visible=modules.config.default_enhance_inpaint_mask_model == 'sam') + example_enhance_mask_dino_prompt_text.click(lambda x: x[0], + inputs=example_enhance_mask_dino_prompt_text, + outputs=enhance_mask_dino_prompt_text, + show_progress=False, queue=False) - enhance_ctrls += [ - enhance_enabled, - enhance_mask_dino_prompt_text, - enhance_prompt, - enhance_negative_prompt, - enhance_mask_model, - enhance_mask_cloth_category, - enhance_mask_sam_model, - enhance_mask_text_threshold, - enhance_mask_box_threshold, - enhance_mask_sam_max_detections, - enhance_inpaint_disable_initial_latent, - enhance_inpaint_engine, - enhance_inpaint_strength, - enhance_inpaint_respective_field, - enhance_inpaint_erode_or_dilate, - enhance_mask_invert - ] + enhance_prompt = gr.Textbox(label="Enhancement positive prompt", + placeholder="Uses original prompt instead if empty.", + elem_id='enhance_prompt') + enhance_negative_prompt = gr.Textbox(label="Enhancement negative prompt", + placeholder="Uses original negative prompt instead if empty.", + elem_id='enhance_negative_prompt') - enhance_inpaint_mode_ctrls += [enhance_inpaint_mode] - enhance_inpaint_engine_ctrls += [enhance_inpaint_engine] + # Bind the callback to the selection change + example_enhance_mask_dino_prompt_text.select( + on_selection_change, + inputs=example_enhance_mask_dino_prompt_text, + outputs=[enhance_prompt, + enhance_negative_prompt] + ) + with gr.Accordion("Detection", open=False): + enhance_mask_model = gr.Dropdown(label='Mask generation model', + choices=flags.inpaint_mask_models, + value=modules.config.default_enhance_inpaint_mask_model) + enhance_mask_cloth_category = gr.Dropdown(label='Cloth category', + choices=flags.inpaint_mask_cloth_category, + value=modules.config.default_inpaint_mask_cloth_category, + visible=modules.config.default_enhance_inpaint_mask_model == 'u2net_cloth_seg', + interactive=True) - enhance_inpaint_update_ctrls += [[ - enhance_inpaint_mode, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, - enhance_inpaint_strength, enhance_inpaint_respective_field - ]] + with gr.Accordion("SAM Options", + visible=modules.config.default_enhance_inpaint_mask_model == 'sam', + open=False) as sam_options: + enhance_mask_sam_model = gr.Dropdown(label='SAM model', + choices=flags.inpaint_mask_sam_model, + value=modules.config.default_inpaint_mask_sam_model, + interactive=True) + enhance_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, + maximum=1.0, value=0.3, step=0.05, + interactive=True) + enhance_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, + maximum=1.0, value=0.25, step=0.05, + interactive=True) + enhance_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", + info="Set to 0 to detect all", + minimum=0, maximum=10, + value=modules.config.default_sam_max_detections, + step=1, interactive=True) - enhance_inpaint_mode.change(inpaint_mode_change, inputs=[enhance_inpaint_mode, inpaint_engine_state], outputs=[ - inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, - enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, - enhance_inpaint_strength, enhance_inpaint_respective_field - ], show_progress=False, queue=False) + with gr.Accordion("Inpaint", visible=True, open=False): + enhance_inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, + value=modules.config.default_inpaint_method, + label='Method', interactive=True) + enhance_inpaint_disable_initial_latent = gr.Checkbox( + label='Disable initial latent in inpaint', value=False) + enhance_inpaint_engine = gr.Dropdown(label='Inpaint Engine', + value=modules.config.default_inpaint_engine_version, + choices=flags.inpaint_engine_versions, + info='Version of Fooocus inpaint model. If set, use performance Quality or Speed (no performance LoRAs) for best results.') + enhance_inpaint_strength = gr.Slider(label='Inpaint Denoising Strength', + minimum=0.0, maximum=1.0, step=0.001, + value=1.0, + info='Same as the denoising strength in A1111 inpaint. ' + 'Only used in inpaint, not used in outpaint. ' + '(Outpaint always use 1.0)') + enhance_inpaint_respective_field = gr.Slider(label='Inpaint Respective Field', + minimum=0.0, maximum=1.0, step=0.001, + value=0.618, + info='The area to inpaint. ' + 'Value 0 is same as "Only Masked" in A1111. ' + 'Value 1 is same as "Whole Image" in A1111. ' + 'Only used in inpaint, not used in outpaint. ' + '(Outpaint always use 1.0)') + enhance_inpaint_erode_or_dilate = gr.Slider(label='Mask Erode or Dilate', + minimum=-64, maximum=64, step=1, value=0, + info='Positive value will make white area in the mask larger, ' + 'negative value will make white area smaller. ' + '(default is 0, always processed before any mask invert)') + enhance_mask_invert = gr.Checkbox( + label='Invert Mask', value=False) - enhance_mask_model.change( - lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] + - [gr.update(visible=x == 'sam')] * 2 + - [gr.Dataset.update(visible=x == 'sam', - samples=modules.config.example_enhance_detection_prompts)], - inputs=enhance_mask_model, - outputs=[enhance_mask_cloth_category, enhance_mask_dino_prompt_text, sam_options, - example_enhance_mask_dino_prompt_text], - queue=False, show_progress=False) + gr.HTML( + '\U0001F4D4 Documentation') + + enhance_ctrls += [ + enhance_enabled, + enhance_mask_dino_prompt_text, + enhance_prompt, + enhance_negative_prompt, + enhance_mask_model, + enhance_mask_cloth_category, + enhance_mask_sam_model, + enhance_mask_text_threshold, + enhance_mask_box_threshold, + enhance_mask_sam_max_detections, + enhance_inpaint_disable_initial_latent, + enhance_inpaint_engine, + enhance_inpaint_strength, + enhance_inpaint_respective_field, + enhance_inpaint_erode_or_dilate, + enhance_mask_invert + ] + + enhance_inpaint_mode_ctrls += [ + enhance_inpaint_mode] + enhance_inpaint_engine_ctrls += [ + enhance_inpaint_engine] + + enhance_inpaint_update_ctrls += [[ + enhance_inpaint_mode, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, + enhance_inpaint_strength, enhance_inpaint_respective_field + ]] + + enhance_inpaint_mode.change(inpaint_mode_change, inputs=[enhance_inpaint_mode, inpaint_engine_state], outputs=[ + inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, + enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, + enhance_inpaint_strength, enhance_inpaint_respective_field + ], show_progress=False, queue=False) + + enhance_mask_model.change( + lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] + + [gr.update(visible=x == 'sam')] * 2 + + [gr.Dataset.update(visible=x == 'sam', + samples=modules.config.example_enhance_detection_prompts)], + inputs=enhance_mask_model, + outputs=[enhance_mask_cloth_category, enhance_mask_dino_prompt_text, sam_options, + example_enhance_mask_dino_prompt_text], + queue=False, show_progress=False) switch_js = "(x) => {if(x){viewer_to_bottom(100);viewer_to_bottom(500);}else{viewer_to_top();} return x;}" down_js = "() => {viewer_to_bottom();}" + +# EVENT HANDLERS input_image_checkbox.change(lambda x: gr.update(visible=x), inputs=input_image_checkbox, outputs=image_input_panel, queue=False, show_progress=False, _js=switch_js) - ip_advanced.change(lambda: None, queue=False, show_progress=False, _js=down_js) - current_tab = gr.Textbox(value='uov', visible=False) - uov_tab.select(lambda: 'uov', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - ip_tab.select(lambda: 'ip', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - describe_tab.select(lambda: 'desc', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - enhance_tab.select(lambda: 'enhance', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - metadata_tab.select(lambda: 'metadata', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - enhance_checkbox.change(lambda x: gr.update(visible=x), inputs=enhance_checkbox, - outputs=enhance_input_panel, queue=False, show_progress=False, _js=switch_js) + ip_advanced.change(lambda: None, queue=False, + show_progress=False, _js=down_js) + + current_tab = gr.Textbox(value='uov', visible=False) + uov_tab.select(lambda: 'uov', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + ip_tab.select(lambda: 'ip', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + describe_tab.select(lambda: 'desc', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + enhance_tab.select(lambda: 'enhance', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + metadata_tab.select(lambda: 'metadata', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + enhance_checkbox.change(fn=update_visibility, inputs=enhance_checkbox, + outputs=[enhance_input_panel, enhance_input_panel2], queue=False, show_progress=False, _js=switch_js) + bulk_enhance_image_browse_btn.click( + on_browse, inputs=bulk_enhance_data_type, outputs=bulk_enhance_input_path, show_progress="hidden") + bulk_enhance_file_explorer.change( + on_file_change, + inputs=[bulk_enhance_file_explorer, bulk_enhance_data_type], + # Note: output both file_row and input_path + outputs=[bulk_enhance_file_row, + bulk_enhance_input_path, bulk_enhance_enabled] + ) + bulk_enhance_input_path.change( + on_input_change, + inputs=[bulk_enhance_input_path, bulk_enhance_file_explorer], + # Update file_explorer based on input_path + outputs=[bulk_enhance_file_explorer, + bulk_enhance_folder_row, bulk_enhance_enabled] + ) with gr.Column(scale=1, visible=modules.config.default_advanced_checkbox) as advanced_column: with gr.Tab(label='Settings'): @@ -575,10 +723,13 @@ with shared.gradio_root: info='width Γ— height', elem_classes='aspect_ratios') - aspect_ratios_selection.change(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') - shared.gradio_root.load(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') + aspect_ratios_selection.change(lambda x: None, inputs=aspect_ratios_selection, + queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') + shared.gradio_root.load(lambda x: None, inputs=aspect_ratios_selection, queue=False, + show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') - image_number = gr.Slider(label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number) + image_number = gr.Slider( + label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number) output_format = gr.Radio(label='Output Format', choices=flags.OutputFormat.list(), @@ -589,7 +740,9 @@ with shared.gradio_root: elem_id='negative_prompt', value=modules.config.default_prompt_negative) seed_random = gr.Checkbox(label='Random', value=True) - image_seed = gr.Textbox(label='Seed', value=0, max_lines=1, visible=False) # workaround for https://github.com/gradio-app/gradio/issues/5354 + # workaround for https://github.com/gradio-app/gradio/issues/5354 + image_seed = gr.Textbox( + label='Seed', value=0, max_lines=1, visible=False) def random_checked(r): return gr.update(visible=not r) @@ -616,7 +769,8 @@ with shared.gradio_root: return gr.update(value=f'\U0001F4DA History Log') history_link = gr.HTML() - shared.gradio_root.load(update_history_link, outputs=history_link, queue=False, show_progress=False) + shared.gradio_root.load( + update_history_link, outputs=history_link, queue=False, show_progress=False) with gr.Tab(label='Styles', elem_classes=['style_selections_tab']): style_sorter.try_load_sorted_styles( @@ -628,17 +782,21 @@ with shared.gradio_root: value="", label='Search Styles') style_selections = gr.CheckboxGroup(show_label=False, container=False, - choices=copy.deepcopy(style_sorter.all_styles), - value=copy.deepcopy(modules.config.default_styles), + choices=copy.deepcopy( + style_sorter.all_styles), + value=copy.deepcopy( + modules.config.default_styles), label='Selected Styles', elem_classes=['style_selections']) - gradio_receiver_style_selections = gr.Textbox(elem_id='gradio_receiver_style_selections', visible=False) + gradio_receiver_style_selections = gr.Textbox( + elem_id='gradio_receiver_style_selections', visible=False) shared.gradio_root.load(lambda: gr.update(choices=copy.deepcopy(style_sorter.all_styles)), outputs=style_selections) style_search_bar.change(style_sorter.search_styles, - inputs=[style_selections, style_search_bar], + inputs=[style_selections, + style_search_bar], outputs=style_selections, queue=False, show_progress=False).then( @@ -654,8 +812,10 @@ with shared.gradio_root: with gr.Tab(label='Models'): with gr.Group(): with gr.Row(): - base_model = gr.Dropdown(label='Base Model (SDXL only)', choices=modules.config.model_filenames, value=modules.config.default_base_model_name, show_label=True) - refiner_model = gr.Dropdown(label='Refiner (SDXL or SD 1.5)', choices=['None'] + modules.config.model_filenames, value=modules.config.default_refiner_model_name, show_label=True) + base_model = gr.Dropdown(label='Base Model (SDXL only)', choices=modules.config.model_filenames, + value=modules.config.default_base_model_name, show_label=True) + refiner_model = gr.Dropdown(label='Refiner (SDXL or SD 1.5)', choices=[ + 'None'] + modules.config.model_filenames, value=modules.config.default_refiner_model_name, show_label=True) refiner_switch = gr.Slider(label='Refiner Switch At', minimum=0.1, maximum=1.0, step=0.0001, info='Use 0.4 for SD1.5 realistic models; ' @@ -681,10 +841,12 @@ with shared.gradio_root: lora_weight = gr.Slider(label='Weight', minimum=modules.config.default_loras_min_weight, maximum=modules.config.default_loras_max_weight, step=0.01, value=weight, elem_classes='lora_weight', scale=5) - lora_ctrls += [lora_enabled, lora_model, lora_weight] + lora_ctrls += [lora_enabled, + lora_model, lora_weight] with gr.Row(): - refresh_files = gr.Button(label='Refresh', value='\U0001f504 Refresh All Files', variant='secondary', elem_classes='refresh_button') + refresh_files = gr.Button( + label='Refresh', value='\U0001f504 Refresh All Files', variant='secondary', elem_classes='refresh_button') with gr.Tab(label='Advanced'): guidance_scale = gr.Slider(label='Guidance Scale', minimum=1.0, maximum=30.0, step=0.01, value=modules.config.default_cfg_scale, @@ -692,8 +854,10 @@ with shared.gradio_root: sharpness = gr.Slider(label='Image Sharpness', minimum=0.0, maximum=30.0, step=0.001, value=modules.config.default_sample_sharpness, info='Higher value means image and texture are sharper.') - gr.HTML('\U0001F4D4 Documentation') - dev_mode = gr.Checkbox(label='Developer Debug Mode', value=modules.config.default_developer_debug_mode_checkbox, container=False) + gr.HTML( + '\U0001F4D4 Documentation') + dev_mode = gr.Checkbox( + label='Developer Debug Mode', value=modules.config.default_developer_debug_mode_checkbox, container=False) with gr.Column(visible=modules.config.default_developer_debug_mode_checkbox) as dev_tools: with gr.Tab(label='Debug Tools'): @@ -713,8 +877,8 @@ with shared.gradio_root: info='Enabling Fooocus\'s implementation of CFG mimicking for TSNR ' '(effective when real CFG > mimicked CFG).') clip_skip = gr.Slider(label='CLIP Skip', minimum=1, maximum=flags.clip_skip_max, step=1, - value=modules.config.default_clip_skip, - info='Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).') + value=modules.config.default_clip_skip, + info='Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).') sampler_name = gr.Dropdown(label='Sampler', choices=flags.sampler_list, value=modules.config.default_sampler) scheduler_name = gr.Dropdown(label='Scheduler', choices=flags.scheduler_list, @@ -754,13 +918,15 @@ with shared.gradio_root: interactive=not modules.config.default_black_out_nsfw, info='Disable preview during generation.') disable_intermediate_results = gr.Checkbox(label='Disable Intermediate Results', - value=flags.Performance.has_restricted_features(modules.config.default_performance), - info='Disable intermediate results during generation, only show final gallery.') + value=flags.Performance.has_restricted_features( + modules.config.default_performance), + info='Disable intermediate results during generation, only show final gallery.') disable_seed_increment = gr.Checkbox(label='Disable seed increment', info='Disable automatic seed increment when image number is > 1.', value=False) - read_wildcards_in_order = gr.Checkbox(label="Read wildcards in order", value=False) + read_wildcards_in_order = gr.Checkbox( + label="Read wildcards in order", value=False) black_out_nsfw = gr.Checkbox(label='Black Out NSFW', value=modules.config.default_black_out_nsfw, interactive=not modules.config.default_black_out_nsfw, @@ -806,12 +972,14 @@ with shared.gradio_root: step=1, value=128) with gr.Tab(label='Inpaint'): - debugging_inpaint_preprocessor = gr.Checkbox(label='Debug Inpaint Preprocessing', value=False) + debugging_inpaint_preprocessor = gr.Checkbox( + label='Debug Inpaint Preprocessing', value=False) debugging_enhance_masks_checkbox = gr.Checkbox(label='Debug Enhance Masks', value=False, info='Show enhance masks in preview and final results') debugging_dino = gr.Checkbox(label='Debug GroundingDINO', value=False, info='Use GroundingDINO boxes instead of more detailed SAM masks') - inpaint_disable_initial_latent = gr.Checkbox(label='Disable initial latent in inpaint', value=False) + inpaint_disable_initial_latent = gr.Checkbox( + label='Disable initial latent in inpaint', value=False) inpaint_engine = gr.Dropdown(label='Inpaint Engine', value=modules.config.default_inpaint_engine_version, choices=flags.inpaint_engine_versions, @@ -839,7 +1007,8 @@ with shared.gradio_root: 'negative value will make white area smaller. ' '(default is 0, processed before SAM)') - inpaint_mask_color = gr.ColorPicker(label='Inpaint brush color', value='#FFFFFF', elem_id='inpaint_brush_color') + inpaint_mask_color = gr.ColorPicker( + label='Inpaint brush color', value='#FFFFFF', elem_id='inpaint_brush_color') inpaint_ctrls = [debugging_inpaint_preprocessor, inpaint_disable_initial_latent, inpaint_engine, inpaint_strength, inpaint_respective_field, @@ -847,7 +1016,8 @@ with shared.gradio_root: inpaint_advanced_masking_checkbox.change(lambda x: [gr.update(visible=x)] * 2, inputs=inpaint_advanced_masking_checkbox, - outputs=[inpaint_mask_image, inpaint_mask_generation_col], + outputs=[ + inpaint_mask_image, inpaint_mask_generation_col], queue=False, show_progress=False) inpaint_mask_color.change(lambda x: gr.update(brush_color=x), inputs=inpaint_mask_color, @@ -855,12 +1025,18 @@ with shared.gradio_root: queue=False, show_progress=False) with gr.Tab(label='FreeU'): - freeu_enabled = gr.Checkbox(label='Enabled', value=False) - freeu_b1 = gr.Slider(label='B1', minimum=0, maximum=2, step=0.01, value=1.01) - freeu_b2 = gr.Slider(label='B2', minimum=0, maximum=2, step=0.01, value=1.02) - freeu_s1 = gr.Slider(label='S1', minimum=0, maximum=4, step=0.01, value=0.99) - freeu_s2 = gr.Slider(label='S2', minimum=0, maximum=4, step=0.01, value=0.95) - freeu_ctrls = [freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2] + freeu_enabled = gr.Checkbox( + label='Enabled', value=False) + freeu_b1 = gr.Slider( + label='B1', minimum=0, maximum=2, step=0.01, value=1.01) + freeu_b2 = gr.Slider( + label='B2', minimum=0, maximum=2, step=0.01, value=1.02) + freeu_s1 = gr.Slider( + label='S1', minimum=0, maximum=4, step=0.01, value=0.99) + freeu_s2 = gr.Slider( + label='S2', minimum=0, maximum=4, step=0.01, value=0.95) + freeu_ctrls = [freeu_enabled, freeu_b1, + freeu_b2, freeu_s1, freeu_s2] def dev_mode_checked(r): return gr.update(visible=r) @@ -870,9 +1046,12 @@ with shared.gradio_root: def refresh_files_clicked(): modules.config.update_files() - results = [gr.update(choices=modules.config.model_filenames)] - results += [gr.update(choices=['None'] + modules.config.model_filenames)] - results += [gr.update(choices=[flags.default_vae] + modules.config.vae_filenames)] + results = [ + gr.update(choices=modules.config.model_filenames)] + results += [gr.update(choices=['None'] + + modules.config.model_filenames)] + results += [gr.update(choices=[flags.default_vae] + + modules.config.vae_filenames)] if not args_manager.args.disable_preset_selection: results += [gr.update(choices=modules.config.available_presets)] for i in range(modules.config.default_max_lora_number): @@ -895,17 +1074,22 @@ with shared.gradio_root: base_model, refiner_model, refiner_switch, sampler_name, scheduler_name, vae_name, seed_random, image_seed, inpaint_engine, inpaint_engine_state, inpaint_mode] + enhance_inpaint_mode_ctrls + [generate_button, - load_parameter_button] + freeu_ctrls + lora_ctrls + load_parameter_button] + freeu_ctrls + lora_ctrls if not args_manager.args.disable_preset_selection: def preset_selection_change(preset, is_generating, inpaint_mode): - preset_content = modules.config.try_get_preset_content(preset) if preset != 'initial' else {} - preset_prepared = modules.meta_parser.parse_meta_from_preset(preset_content) + preset_content = modules.config.try_get_preset_content( + preset) if preset != 'initial' else {} + preset_prepared = modules.meta_parser.parse_meta_from_preset( + preset_content) default_model = preset_prepared.get('base_model') - previous_default_models = preset_prepared.get('previous_default_models', []) - checkpoint_downloads = preset_prepared.get('checkpoint_downloads', {}) - embeddings_downloads = preset_prepared.get('embeddings_downloads', {}) + previous_default_models = preset_prepared.get( + 'previous_default_models', []) + checkpoint_downloads = preset_prepared.get( + 'checkpoint_downloads', {}) + embeddings_downloads = preset_prepared.get( + 'embeddings_downloads', {}) lora_downloads = preset_prepared.get('lora_downloads', {}) vae_downloads = preset_prepared.get('vae_downloads', {}) @@ -918,7 +1102,6 @@ with shared.gradio_root: return modules.meta_parser.load_parameter_button_click(json.dumps(preset_prepared), is_generating, inpaint_mode) - def inpaint_engine_state_change(inpaint_engine_version, *args): if inpaint_engine_version == 'empty': inpaint_engine_version = modules.config.default_inpaint_engine_version @@ -939,15 +1122,17 @@ with shared.gradio_root: performance_selection.change(lambda x: [gr.update(interactive=not flags.Performance.has_restricted_features(x))] * 11 + [gr.update(visible=not flags.Performance.has_restricted_features(x))] * 1 + - [gr.update(value=flags.Performance.has_restricted_features(x))] * 1, + [gr.update( + value=flags.Performance.has_restricted_features(x))] * 1, inputs=performance_selection, outputs=[ guidance_scale, sharpness, adm_scaler_end, adm_scaler_positive, adm_scaler_negative, refiner_switch, refiner_model, sampler_name, scheduler_name, adaptive_cfg, refiner_swap_method, negative_prompt, disable_intermediate_results - ], queue=False, show_progress=False) + ], queue=False, show_progress=False) - output_format.input(lambda x: gr.update(output_format=x), inputs=output_format) + output_format.input(lambda x: gr.update( + output_format=x), inputs=output_format) advanced_checkbox.change(lambda x: gr.update(visible=x), advanced_checkbox, advanced_column, queue=False, show_progress=False) \ @@ -960,7 +1145,8 @@ with shared.gradio_root: ], show_progress=False, queue=False) # load configured default_inpaint_method - default_inpaint_ctrls = [inpaint_mode, inpaint_disable_initial_latent, inpaint_engine, inpaint_strength, inpaint_respective_field] + default_inpaint_ctrls = [inpaint_mode, inpaint_disable_initial_latent, + inpaint_engine, inpaint_strength, inpaint_respective_field] for mode, disable_initial_latent, engine, strength, respective_field in [default_inpaint_ctrls] + enhance_inpaint_update_ctrls: shared.gradio_root.load(inpaint_mode_change, inputs=[mode, inpaint_engine_state], outputs=[ inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, disable_initial_latent, @@ -984,13 +1170,19 @@ with shared.gradio_root: ctrls += [base_model, refiner_model, refiner_switch] + lora_ctrls ctrls += [input_image_checkbox, current_tab] ctrls += [uov_method, uov_input_image] - ctrls += [outpaint_selections, inpaint_input_image, inpaint_additional_prompt, inpaint_mask_image] - ctrls += [disable_preview, disable_intermediate_results, disable_seed_increment, black_out_nsfw] - ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg, clip_skip] + ctrls += [outpaint_selections, inpaint_input_image, + inpaint_additional_prompt, inpaint_mask_image] + ctrls += [disable_preview, disable_intermediate_results, + disable_seed_increment, black_out_nsfw] + ctrls += [adm_scaler_positive, adm_scaler_negative, + adm_scaler_end, adaptive_cfg, clip_skip] ctrls += [sampler_name, scheduler_name, vae_name] - ctrls += [overwrite_step, overwrite_switch, overwrite_width, overwrite_height, overwrite_vary_strength] - ctrls += [overwrite_upscale_strength, mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint] - ctrls += [debugging_cn_preprocessor, skipping_cn_preprocessor, canny_low_threshold, canny_high_threshold] + ctrls += [overwrite_step, overwrite_switch, overwrite_width, + overwrite_height, overwrite_vary_strength] + ctrls += [overwrite_upscale_strength, + mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint] + ctrls += [debugging_cn_preprocessor, skipping_cn_preprocessor, + canny_low_threshold, canny_high_threshold] ctrls += [refiner_swap_method, controlnet_softness] ctrls += freeu_ctrls ctrls += inpaint_ctrls @@ -1006,6 +1198,7 @@ with shared.gradio_root: enhance_input_image, enhance_checkbox, enhance_uov_method, enhance_uov_processing_order, enhance_uov_prompt_type] ctrls += enhance_ctrls + ctrls += bulk_enhance_ctrls def parse_meta(raw_prompt_txt, is_generating): loaded_json = None @@ -1020,17 +1213,21 @@ with shared.gradio_root: return json.dumps(loaded_json), gr.update(visible=False), gr.update(visible=True) - prompt.input(parse_meta, inputs=[prompt, state_is_generating], outputs=[prompt, generate_button, load_parameter_button], queue=False, show_progress=False) + prompt.input(parse_meta, inputs=[prompt, state_is_generating], outputs=[ + prompt, generate_button, load_parameter_button], queue=False, show_progress=False) - load_parameter_button.click(modules.meta_parser.load_parameter_button_click, inputs=[prompt, state_is_generating, inpaint_mode], outputs=load_data_outputs, queue=False, show_progress=False) + load_parameter_button.click(modules.meta_parser.load_parameter_button_click, inputs=[ + prompt, state_is_generating, inpaint_mode], outputs=load_data_outputs, queue=False, show_progress=False) def trigger_metadata_import(file, state_is_generating): - parameters, metadata_scheme = modules.meta_parser.read_info_from_image(file) + parameters, metadata_scheme = modules.meta_parser.read_info_from_image( + file) if parameters is None: print('Could not find metadata in the image!') parsed_parameters = {} else: - metadata_parser = modules.meta_parser.get_metadata_parser(metadata_scheme) + metadata_parser = modules.meta_parser.get_metadata_parser( + metadata_scheme) parsed_parameters = metadata_parser.to_json(parameters) return modules.meta_parser.load_parameter_button_click(parsed_parameters, state_is_generating, inpaint_mode) @@ -1058,7 +1255,8 @@ with shared.gradio_root: for notification_file in ['notification.ogg', 'notification.mp3']: if os.path.exists(notification_file): - gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False) + gr.Audio(interactive=False, value=notification_file, + elem_id='audio_notification', visible=False) break def trigger_describe(modes, img, apply_styles): @@ -1068,7 +1266,8 @@ with shared.gradio_root: if flags.describe_type_photo in modes: from extras.interrogate import default_interrogator as default_interrogator_photo describe_prompts.append(default_interrogator_photo(img)) - styles.update(["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"]) + styles.update( + ["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"]) if flags.describe_type_anime in modes: from extras.wd14tagger import default_interrogator as default_interrogator_anime @@ -1098,7 +1297,6 @@ with shared.gradio_root: if prompt == '': return trigger_describe(mode, img, apply_styles) return gr.update(), gr.update() - uov_input_image.upload(trigger_auto_describe, inputs=[describe_methods, uov_input_image, prompt, describe_apply_styles], outputs=[prompt, style_selections], show_progress=True, queue=True) \ .then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \ @@ -1110,6 +1308,7 @@ with shared.gradio_root: .then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \ .then(lambda: None, _js='()=>{refresh_style_localization();}') + def dump_default_english_config(): from modules.localization import dump_english_config dump_english_config(grh.all_components) @@ -1122,7 +1321,8 @@ shared.gradio_root.launch( server_name=args_manager.args.listen, server_port=args_manager.args.port, share=args_manager.args.share, - auth=check_auth if (args_manager.args.share or args_manager.args.listen) and auth_enabled else None, + auth=check_auth if ( + args_manager.args.share or args_manager.args.listen) and auth_enabled else None, allowed_paths=[modules.config.path_outputs], blocked_paths=[constants.AUTH_FILENAME] )