diff --git a/README.en.md b/README.en.md index 33fe116..6db7933 100644 --- a/README.en.md +++ b/README.en.md @@ -45,6 +45,7 @@ Double-click install.bat to install the required dependencies **v1.2.0** +- Added `easy pulIDApply` and `easy pulIDApplyADV` - Added `easy huanyuanDiTLoader` and `easy pixArtLoader` - Added **easy sliderControl** - Slider control node, which can currently be used to control the parameters of ipadapterMS (double-click the slider to reset to default) - Added **layer_weights** in `easy ipadapterApplyADV` @@ -385,6 +386,8 @@ Disclaimer: Opened source was not easy. I have a lot of respect for the contribu [ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID) - Face migration +[ComfyUI_PuLID](https://github.com/cubiq/PuLID_ComfyUI) - Face migration + [ComfyUI-Custom-Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) - pyssss🐍 [cg-image-picker](https://github.com/chrisgoringe/cg-image-picker) - Image Preview Chooser diff --git a/README.md b/README.md index 1fd5ea8..635a480 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ git clone https://github.com/yolain/ComfyUI-Easy-Use **v1.2.0** +- 增加 `easy pulIDApply` 和 `easy pulIDApplyADV` - 增加 `easy huanyuanDiTLoader` 和 `easy pixArtLoader` - 当新菜单的位置在上或者下时增加上 crystools 的显示,推荐开两个就好(如果后续crystools有更新UI适配我可能会删除掉) - 增加 **easy sliderControl** - 滑块控制节点,当前可用于控制ipadapterMS的参数 (双击滑块可重置为默认值) @@ -399,6 +400,8 @@ git clone https://github.com/yolain/ComfyUI-Easy-Use [ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID) - 人脸迁移 +[ComfyUI_PuLID](https://github.com/cubiq/PuLID_ComfyUI) - 人脸迁移 + [ComfyUI-Custom-Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) - pyssss 小蛇🐍脚本 [cg-image-picker](https://github.com/chrisgoringe/cg-image-picker) - 图片选择器 diff --git a/prestartup_script.py b/prestartup_script.py index 600b0c9..55e83fb 100644 --- a/prestartup_script.py +++ b/prestartup_script.py @@ -22,6 +22,7 @@ add_folder_path_and_extensions("mmdets", [os.path.join(model_path, "mmdets")], f add_folder_path_and_extensions("sams", [os.path.join(model_path, "sams")], folder_paths.supported_pt_extensions) add_folder_path_and_extensions("onnx", [os.path.join(model_path, "onnx")], {'.onnx'}) add_folder_path_and_extensions("instantid", [os.path.join(model_path, "instantid")], folder_paths.supported_pt_extensions) +add_folder_path_and_extensions("pulid", [os.path.join(model_path, "pulid")], folder_paths.supported_pt_extensions) add_folder_path_and_extensions("layer_model", [os.path.join(model_path, "layer_model")], folder_paths.supported_pt_extensions) add_folder_path_and_extensions("rembg", [os.path.join(model_path, "rembg")], folder_paths.supported_pt_extensions) add_folder_path_and_extensions("ipadapter", [os.path.join(model_path, "ipadapter")], folder_paths.supported_pt_extensions) diff --git a/py/easyNodes.py b/py/easyNodes.py index e3cc0c4..873ac16 100644 --- a/py/easyNodes.py +++ b/py/easyNodes.py @@ -3581,6 +3581,108 @@ class instantIDApplyAdvanced(instantID): return self.run(pipe, image, instantid_file, insightface, control_net_name, cn_strength, cn_soft_weights, weight, start_at, end_at, noise, image_kps, mask, control_net, positive, negative, prompt, extra_pnginfo, my_unique_id) +class applyPulID: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "pulid_file": (folder_paths.get_filename_list("pulid"),), + "insightface": (["CPU", "CUDA", "ROCM"],), + "image": ("IMAGE",), + "method": (["fidelity", "style", "neutral"],), + "weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 5.0, "step": 0.05}), + "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), + "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), + }, + "optional": { + "attn_mask": ("MASK",), + }, + } + + RETURN_TYPES = ("MODEL",) + RETURN_NAMES = ("model",) + + FUNCTION = "run" + CATEGORY = "EasyUse/Adapter" + + def error(self): + raise Exception(f"[ERROR] To use pulIDApply, you need to install 'ComfyUI_PulID'") + + def run(self, model, image, pulid_file, insightface, weight, start_at, end_at, method=None, noise=0.0, fidelity=None, projection=None, attn_mask=None, prompt=None, extra_pnginfo=None, my_unique_id=None): + pulid_model, insightface_model, eva_clip = None, None, None + # Load PulID + cache_key = 'pulID' + if cache_key in backend_cache.cache: + log_node_info("easy pulIDApply","Using InstantIDModel Cached") + _, pulid_model = backend_cache.cache[cache_key][1] + if "PulidModelLoader" in ALL_NODE_CLASS_MAPPINGS: + load_pulid_cls = ALL_NODE_CLASS_MAPPINGS["PulidModelLoader"] + pulid_model, = load_pulid_cls().load_model(pulid_file) + backend_cache.update_cache(cache_key, 'pulid', (False, pulid_model)) + else: + self.error() + # Load Insightface + icache_key = 'insightface-' + insightface + if icache_key in backend_cache.cache: + log_node_info("easy pulIDApply", f"Using InsightFaceModel {insightface} Cached") + _, insightface_model = backend_cache.cache[icache_key][1] + elif "PulidInsightFaceLoader" in ALL_NODE_CLASS_MAPPINGS: + load_insightface_cls = ALL_NODE_CLASS_MAPPINGS["InstantIDFaceAnalysis"] + insightface_model, = load_insightface_cls().load_insight_face(insightface) + backend_cache.update_cache(icache_key, 'insightface', (False, insightface_model)) + else: + self.error() + # Load Eva clip + ecache_key = 'eva_clip' + if ecache_key in backend_cache.cache: + log_node_info("easy pulIDApply", f"Using EVAClipModel Cached") + _, eva_clip = backend_cache.cache[ecache_key][1] + elif "PulidEvaClipLoader" in ALL_NODE_CLASS_MAPPINGS: + load_evaclip_cls = ALL_NODE_CLASS_MAPPINGS["PulidEvaClipLoader"] + eva_clip, = load_evaclip_cls().load_eva_clip() + backend_cache.update_cache(ecache_key, 'eva_clip', (False, eva_clip)) + else: + self.error() + + # Apply PulID + if method is not None: + if "ApplyPulid" in ALL_NODE_CLASS_MAPPINGS: + cls = ALL_NODE_CLASS_MAPPINGS['ApplyPulid'] + model, = cls().apply_pulid(model, pulid=pulid_model, eva_clip=eva_clip, face_analysis=insightface_model, image=image, weight=weight, method=method, start_at=start_at, end_at=end_at, attn_mask=attn_mask) + else: + self.error() + else: + if "ApplyPulidAdvanced" in ALL_NODE_CLASS_MAPPINGS: + cls = ALL_NODE_CLASS_MAPPINGS['ApplyPulidAdvanced'] + model, = cls().apply_pulid(model, pulid=pulid_model, eva_clip=eva_clip, face_analysis=insightface_model, image=image, weight=weight, projection=projection, fidelity=fidelity, noise=noise, start_at=start_at, end_at=end_at, attn_mask=attn_mask) + else: + self.error() + + return (model,) + +class applyPulIDADV(applyPulID): + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "pulid_file": (folder_paths.get_filename_list("pulid"),), + "insightface": (["CPU", "CUDA", "ROCM"],), + "image": ("IMAGE",), + "weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 5.0, "step": 0.05}), + "projection": (["ortho_v2", "ortho", "none"], {"default":"ortho_v2"}), + "fidelity": ("INT", {"default": 8, "min": 0, "max": 32, "step": 1}), + "noise": ("FLOAT", {"default": 0.0, "min": -1.0, "max": 1.0, "step": 0.1}), + "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), + "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), + }, + "optional": { + "attn_mask": ("MASK",), + }, + } + # ---------------------------------------------------------------适配器 结束----------------------------------------------------------------------# #---------------------------------------------------------------预采样 开始----------------------------------------------------------------------# @@ -7230,6 +7332,8 @@ NODE_CLASS_MAPPINGS = { "easy ipadapterStyleComposition": ipadapterStyleComposition, "easy instantIDApply": instantIDApply, "easy instantIDApplyADV": instantIDApplyAdvanced, + "easy pulIDApply": applyPulID, + "easy pulIDApplyADV": applyPulIDADV, "easy styleAlignedBatchAlign": styleAlignedBatchAlign, "easy icLightApply": icLightApply, # Inpaint 内补 @@ -7345,6 +7449,8 @@ NODE_DISPLAY_NAME_MAPPINGS = { "easy ipadapterApplyFromParams": "Easy Apply IPAdapter (From Params)", "easy instantIDApply": "Easy Apply InstantID", "easy instantIDApplyADV": "Easy Apply InstantID (Advanced)", + "easy pulIDApply": "Easy Apply PuLID", + "easy pulIDApplyADV": "Easy Apply PuLID (Advanced)", "easy styleAlignedBatchAlign": "Easy Apply StyleAlign", "easy icLightApply": "Easy Apply ICLight", # Inpaint 内补 diff --git a/web/js/easy/easyExtraMenu.js b/web/js/easy/easyExtraMenu.js index 96b309d..c9366f0 100644 --- a/web/js/easy/easyExtraMenu.js +++ b/web/js/easy/easyExtraMenu.js @@ -6,7 +6,7 @@ const loaders = ['easy fullLoader', 'easy a1111Loader', 'easy comfyLoader', 'eas const preSampling = ['easy preSampling', 'easy preSamplingAdvanced', 'easy preSamplingDynamicCFG', 'easy preSamplingNoiseIn', 'easy preSamplingCustom', 'easy preSamplingLayerDiffusion', 'easy fullkSampler'] const kSampler = ['easy kSampler', 'easy kSamplerTiled', 'easy kSamplerInpainting', 'easy kSamplerDownscaleUnet', 'easy kSamplerLayerDiffusion'] const controlnet = ['easy controlnetLoader', 'easy controlnetLoaderADV', 'easy instantIDApply', 'easy instantIDApplyADV'] -const ipadapter = ['easy ipadapterApply', 'easy ipadapterApplyADV', 'easy ipadapterStyleComposition', 'easy ipadapterApplyFromParams'] +const ipadapter = ['easy ipadapterApply', 'easy ipadapterApplyADV', 'easy ipadapterStyleComposition', 'easy ipadapterApplyFromParams', 'easy pulIDApply', 'easy pulIDApplyADV'] const positive_prompt = ['easy positive', 'easy wildcards'] const imageNode = ['easy loadImageBase64', 'LoadImage', 'LoadImageMask'] const inpaint = ['easy applyBrushNet', 'easy applyPowerPaint', 'easy applyInpaint'] @@ -60,6 +60,8 @@ const widgetMapping = { "end_at": "end_at", "cache_mode": "cache_mode", "use_tiled": "use_tiled", + "insightface": "insightface", + "pulid_file": "pulid_file" }, "load_image":{ "image":"image", @@ -576,7 +578,7 @@ app.registerExtension({ } // Swap IPAdapater if (ipadapter.includes(nodeData.name)) { - addMenu("↪️ Swap EasyIPAdapater", 'ipadapter', ipadapter, nodeType) + addMenu("↪️ Swap EasyAdapater", 'ipadapter', ipadapter, nodeType) } // Swap Image if (imageNode.includes(nodeData.name)) {