From 9d64ea448b59fd75d3a9595beeea2169be9d57be Mon Sep 17 00:00:00 2001 From: yolain Date: Thu, 28 Mar 2024 23:50:13 +0800 Subject: [PATCH] add:easy preMaskDetailerFix --- README.en.md | 2 + README.md | 2 + py/easyNodes.py | 248 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 215 insertions(+), 37 deletions(-) diff --git a/README.en.md b/README.en.md index ee0cbae..07855dc 100644 --- a/README.en.md +++ b/README.en.md @@ -37,6 +37,8 @@ PS: Please update [ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAd
- Added `easy ipadapterApply` - Added `easy ipadapterApplyADV` +- Added `easy preMaskDetailerFix` +- Fixed `easy stylesSelector` is change the prompt when not select the style (4c25580) diff --git a/README.md b/README.md index ab191e1..852b59e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ PS: 请更新至最新版v2的 [ComfyUI_IPAdapter_plus](https://github.com/cubiq
- 增加 `easy ipadapterApply` - 增加 `easy ipadapterApplyADV` +- 增加 `easy preMaskDetailerFix` +- 修复 `easy stylesSelector` 当未选择样式时,原有提示词发生了变化 (4c25580) diff --git a/py/easyNodes.py b/py/easyNodes.py index fc12b32..0991b89 100644 --- a/py/easyNodes.py +++ b/py/easyNodes.py @@ -194,6 +194,9 @@ class stylesPromptSelector: values = prompt[my_unique_id]["inputs"]['select_styles'].split(',') has_prompt = False + if len(values) == 0: + return (positive, negative) + for index, val in enumerate(values): if 'prompt' in all_styles[val]: if "{prompt}" in all_styles[val]['prompt'] and has_prompt == False: @@ -4156,6 +4159,150 @@ class preDetailerFix: return (new_pipe,) +# 预遮罩细节修复 +class preMaskDetailerFix: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "pipe": ("PIPE_LINE",), + "mask": ("MASK",), + + "guide_size": ("FLOAT", {"default": 384, "min": 64, "max": MAX_RESOLUTION, "step": 8}), + "guide_size_for": ("BOOLEAN", {"default": True, "label_on": "bbox", "label_off": "crop_region"}), + "max_size": ("FLOAT", {"default": 1024, "min": 64, "max": MAX_RESOLUTION, "step": 8}), + "mask_mode": ("BOOLEAN", {"default": True, "label_on": "masked only", "label_off": "whole"}), + + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), + "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}), + "sampler_name": (comfy.samplers.KSampler.SAMPLERS,), + "scheduler": (comfy.samplers.KSampler.SCHEDULERS,), + "denoise": ("FLOAT", {"default": 0.5, "min": 0.0001, "max": 1.0, "step": 0.01}), + + "feather": ("INT", {"default": 5, "min": 0, "max": 100, "step": 1}), + "crop_factor": ("FLOAT", {"default": 3.0, "min": 1.0, "max": 10, "step": 0.1}), + "drop_size": ("INT", {"min": 1, "max": MAX_RESOLUTION, "step": 1, "default": 10}), + "refiner_ratio": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0}), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 100}), + "cycle": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1}), + }, + "optional": { + # "patch": ("INPAINT_PATCH",), + "optional_image": ("IMAGE",), + "inpaint_model": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}), + "noise_mask_feather": ("INT", {"default": 20, "min": 0, "max": 100, "step": 1}), + }, + } + + RETURN_TYPES = ("PIPE_LINE",) + RETURN_NAMES = ("pipe",) + OUTPUT_IS_LIST = (False,) + FUNCTION = "doit" + + CATEGORY = "EasyUse/Fix" + + def doit(self, pipe, mask, guide_size, guide_size_for, max_size, mask_mode, seed, steps, cfg, sampler_name, scheduler, denoise, feather, crop_factor, drop_size,refiner_ratio, batch_size, cycle, optional_image=None, inpaint_model=False, noise_mask_feather=20): + + model = pipe["model"] if "model" in pipe else None + if model is None: + raise Exception(f"[ERROR] pipe['model'] is missing") + clip = pipe["clip"] if"clip" in pipe else None + if clip is None: + raise Exception(f"[ERROR] pipe['clip'] is missing") + vae = pipe["vae"] if "vae" in pipe else None + if vae is None: + raise Exception(f"[ERROR] pipe['vae'] is missing") + if optional_image is not None: + images = optional_image + else: + images = pipe["images"] if "images" in pipe else None + if images is None: + raise Exception(f"[ERROR] pipe['image'] is missing") + positive = pipe["positive"] if "positive" in pipe else None + if positive is None: + raise Exception(f"[ERROR] pipe['positive'] is missing") + negative = pipe["negative"] if "negative" in pipe else None + if negative is None: + raise Exception(f"[ERROR] pipe['negative'] is missing") + latent = pipe["samples"] if "samples" in pipe else None + if latent is None: + raise Exception(f"[ERROR] pipe['samples'] is missing") + + if 'noise_mask' not in latent: + if images is None: + raise Exception("No Images found") + if vae is None: + raise Exception("No VAE found") + x = (images.shape[1] // 8) * 8 + y = (images.shape[2] // 8) * 8 + mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), + size=(images.shape[1], images.shape[2]), mode="bilinear") + + pixels = images.clone() + if pixels.shape[1] != x or pixels.shape[2] != y: + x_offset = (pixels.shape[1] % 8) // 2 + y_offset = (pixels.shape[2] % 8) // 2 + pixels = pixels[:, x_offset:x + x_offset, y_offset:y + y_offset, :] + mask = mask[:, :, x_offset:x + x_offset, y_offset:y + y_offset] + + mask_erosion = mask + + m = (1.0 - mask.round()).squeeze(1) + for i in range(3): + pixels[:, :, :, i] -= 0.5 + pixels[:, :, :, i] *= m + pixels[:, :, :, i] += 0.5 + t = vae.encode(pixels) + + latent = {"samples": t, "noise_mask": (mask_erosion[:, :, :x, :y].round())} + # when patch was linked + # if patch is not None: + # worker = InpaintWorker(node_name="easy kSamplerInpainting") + # model, = worker.patch(model, latent, patch) + + loader_settings = pipe["loader_settings"] if "loader_settings" in pipe else {} + + new_pipe = { + "images": images, + "model": model, + "clip": clip, + "vae": vae, + "positive": positive, + "negative": negative, + "seed": seed, + "mask": mask, + + "loader_settings": loader_settings, + + "detail_fix_settings": { + "guide_size": guide_size, + "guide_size_for": guide_size_for, + "max_size": max_size, + "seed": seed, + "steps": steps, + "cfg": cfg, + "sampler_name": sampler_name, + "scheduler": scheduler, + "denoise": denoise, + "feather": feather, + "crop_factor": crop_factor, + "drop_size": drop_size, + "refiner_ratio": refiner_ratio, + "batch_size": batch_size, + "cycle": cycle + }, + + "mask_settings": { + "mask_mode": mask_mode, + "inpaint_model": inpaint_model, + "noise_mask_feather": noise_mask_feather + } + } + + del pipe + + return (new_pipe,) + # 细节修复 class detailerFix: @classmethod @@ -4172,10 +4319,10 @@ class detailerFix: "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID", } } - RETURN_TYPES = ("PIPE_LINE", "IMAGE",) - RETURN_NAMES = ("pipe", "image") + RETURN_TYPES = ("PIPE_LINE", "IMAGE", "IMAGE", "IMAGE") + RETURN_NAMES = ("pipe", "image", "cropped_refined", "cropped_enhanced_alpha") OUTPUT_NODE = True - OUTPUT_IS_LIST = (False, False) + OUTPUT_IS_LIST = (False, False, True, True) FUNCTION = "doit" CATEGORY = "EasyUse/Fix" @@ -4192,19 +4339,12 @@ class detailerFix: if model is None: raise Exception(f"[ERROR] model or pipe['model'] is missing") - bbox_segm_pipe = pipe["bbox_segm_pipe"] if pipe and "bbox_segm_pipe" in pipe else None - if bbox_segm_pipe is None: - raise Exception(f"[ERROR] bbox_segm_pipe or pipe['bbox_segm_pipe'] is missing") - sam_pipe = pipe["sam_pipe"] if "sam_pipe" in pipe else None - if sam_pipe is None: - raise Exception(f"[ERROR] sam_pipe or pipe['sam_pipe'] is missing") - bbox_detector_opt, bbox_threshold, bbox_dilation, bbox_crop_factor, segm_detector_opt = bbox_segm_pipe - sam_model_opt, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative = sam_pipe - detail_fix_settings = pipe["detail_fix_settings"] if "detail_fix_settings" in pipe else None if detail_fix_settings is None: raise Exception(f"[ERROR] detail_fix_settings or pipe['detail_fix_settings'] is missing") + mask = pipe["mask"] if "mask" in pipe else None + image = pipe["images"] clip = pipe["clip"] vae = pipe["vae"] @@ -4212,36 +4352,65 @@ class detailerFix: positive = pipe["positive"] negative = pipe["negative"] loader_settings = pipe["loader_settings"] if "loader_settings" in pipe else {} - guide_size = pipe["detail_fix_settings"]["guide_size"] - guide_size_for = pipe["detail_fix_settings"]["guide_size_for"] - max_size = pipe["detail_fix_settings"]["max_size"] - steps = pipe["detail_fix_settings"]["steps"] - cfg = pipe["detail_fix_settings"]["cfg"] - sampler_name = pipe["detail_fix_settings"]["sampler_name"] - scheduler = pipe["detail_fix_settings"]["scheduler"] - denoise = pipe["detail_fix_settings"]["denoise"] - feather = pipe["detail_fix_settings"]["feather"] - noise_mask = pipe["detail_fix_settings"]["noise_mask"] - force_inpaint = pipe["detail_fix_settings"]["force_inpaint"] - drop_size = pipe["detail_fix_settings"]["drop_size"] - wildcard = pipe["detail_fix_settings"]["wildcard"] - cycle = pipe["detail_fix_settings"]["cycle"] + guide_size = pipe["detail_fix_settings"]["guide_size"] if "guide_size" in pipe["detail_fix_settings"] else 256 + guide_size_for = pipe["detail_fix_settings"]["guide_size_for"] if "guide_size_for" in pipe[ + "detail_fix_settings"] else True + max_size = pipe["detail_fix_settings"]["max_size"] if "max_size" in pipe["detail_fix_settings"] else 768 + steps = pipe["detail_fix_settings"]["steps"] if "steps" in pipe["detail_fix_settings"] else 20 + cfg = pipe["detail_fix_settings"]["cfg"] if "cfg" in pipe["detail_fix_settings"] else 1.0 + sampler_name = pipe["detail_fix_settings"]["sampler_name"] if "sampler_name" in pipe[ + "detail_fix_settings"] else None + scheduler = pipe["detail_fix_settings"]["scheduler"] if "scheduler" in pipe["detail_fix_settings"] else None + denoise = pipe["detail_fix_settings"]["denoise"] if "denoise" in pipe["detail_fix_settings"] else 0.5 + feather = pipe["detail_fix_settings"]["feather"] if "feather" in pipe["detail_fix_settings"] else 5 + crop_factor = pipe["detail_fix_settings"]["crop_factor"] if "crop_factor" in pipe["detail_fix_settings"] else 3.0 + drop_size = pipe["detail_fix_settings"]["drop_size"] if "drop_size" in pipe["detail_fix_settings"] else 10 + refiner_ratio = pipe["detail_fix_settings"]["refiner_ratio"] if "refiner_ratio" in pipe else 0.2 + batch_size = pipe["detail_fix_settings"]["batch_size"] if "batch_size" in pipe["detail_fix_settings"] else 1 + noise_mask = pipe["detail_fix_settings"]["noise_mask"] if "noise_mask" in pipe["detail_fix_settings"] else None + force_inpaint = pipe["detail_fix_settings"]["force_inpaint"] if "force_inpaint" in pipe["detail_fix_settings"] else False + wildcard = pipe["detail_fix_settings"]["wildcard"] if "wildcard" in pipe["detail_fix_settings"] else "" + cycle = pipe["detail_fix_settings"]["cycle"] if "cycle" in pipe["detail_fix_settings"] else 1 - del pipe + bbox_segm_pipe = pipe["bbox_segm_pipe"] if pipe and "bbox_segm_pipe" in pipe else None + sam_pipe = pipe["sam_pipe"] if "sam_pipe" in pipe else None # 细节修复初始时间 start_time = int(time.time() * 1000) + if "mask_settings" in pipe: + mask_mode = pipe['mask_settings']["mask_mode"] if "inpaint_model" in pipe['mask_settings'] else True + inpaint_model = pipe['mask_settings']["inpaint_model"] if "inpaint_model" in pipe['mask_settings'] else False + noise_mask_feather = pipe['mask_settings']["noise_mask_feather"] if "noise_mask_feather" in pipe['mask_settings'] else 20 + cls = ALL_NODE_CLASS_MAPPINGS["MaskDetailerPipe"] + if "MaskDetailerPipe" not in ALL_NODE_CLASS_MAPPINGS: + raise Exception(f"[ERROR] To use MaskDetailerPipe, you need to install 'Impact Pack'") + basic_pipe = (model, clip, vae, positive, negative) + result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, basic_pipe, refiner_basic_pipe_opt = cls().doit(image, mask, basic_pipe, guide_size, guide_size_for, max_size, mask_mode, + seed, steps, cfg, sampler_name, scheduler, denoise, + feather, crop_factor, drop_size, refiner_ratio, batch_size, cycle=1, + refiner_basic_pipe_opt=None, detailer_hook=None, inpaint_model=inpaint_model, noise_mask_feather=noise_mask_feather) + result_mask = mask + result_cnet_images = () + else: + if bbox_segm_pipe is None: + raise Exception(f"[ERROR] bbox_segm_pipe or pipe['bbox_segm_pipe'] is missing") + if sam_pipe is None: + raise Exception(f"[ERROR] sam_pipe or pipe['sam_pipe'] is missing") + bbox_detector_opt, bbox_threshold, bbox_dilation, bbox_crop_factor, segm_detector_opt = bbox_segm_pipe + sam_model_opt, sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, sam_mask_hint_use_negative = sam_pipe + if "FaceDetailer" not in ALL_NODE_CLASS_MAPPINGS: + raise Exception(f"[ERROR] To use FaceDetailer, you need to install 'Impact Pack'") + cls = ALL_NODE_CLASS_MAPPINGS["FaceDetailer"] - cls = ALL_NODE_CLASS_MAPPINGS["FaceDetailer"] - - result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, result_mask, pipe, result_cnet_images = cls().doit( - image, model, clip, vae, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, - scheduler, - positive, negative, denoise, feather, noise_mask, force_inpaint, - bbox_threshold, bbox_dilation, bbox_crop_factor, - sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, - sam_mask_hint_use_negative, drop_size, bbox_detector_opt, wildcard, cycle, sam_model_opt, segm_detector_opt, - detailer_hook=None) + result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, result_mask, pipe, result_cnet_images = cls().doit( + image, model, clip, vae, guide_size, guide_size_for, max_size, seed, steps, cfg, sampler_name, + scheduler, + positive, negative, denoise, feather, noise_mask, force_inpaint, + bbox_threshold, bbox_dilation, bbox_crop_factor, + sam_detection_hint, sam_dilation, sam_threshold, sam_bbox_expansion, sam_mask_hint_threshold, + sam_mask_hint_use_negative, drop_size, bbox_detector_opt, wildcard, cycle, sam_model_opt, + segm_detector_opt, + detailer_hook=None) # 细节修复结束时间 end_time = int(time.time() * 1000) @@ -4273,11 +4442,14 @@ class detailerFix: }, "detail_fix_settings": detail_fix_settings } + if "mask_settings" in pipe: + new_pipe["mask_settings"] = pipe["mask_settings"] sampler.update_value_by_id("pipe_line", my_unique_id, new_pipe) del bbox_segm_pipe del sam_pipe + del pipe if image_output in ("Hide", "Hide/Save"): return {"ui": {}, @@ -5721,6 +5893,7 @@ NODE_CLASS_MAPPINGS = { # fix 修复相关 "easy hiresFix": hiresFix, "easy preDetailerFix": preDetailerFix, + "easy preMaskDetailerFix": preMaskDetailerFix, "easy ultralyticsDetectorPipe": ultralyticsDetectorForDetailerFix, "easy samLoaderPipe": samLoaderForDetailerFix, "easy detailerFix": detailerFix, @@ -5809,6 +5982,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { # fix 修复相关 "easy hiresFix": "HiresFix", "easy preDetailerFix": "PreDetailerFix", + "easy preMaskDetailerFix": "preMaskDetailerFix", "easy ultralyticsDetectorPipe": "UltralyticsDetector (Pipe)", "easy samLoaderPipe": "SAMLoader (Pipe)", "easy detailerFix": "DetailerFix",