diff --git a/README.md b/README.md index fbe4579..52d5ef7 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,11 @@ If you have any questions or suggestions, you can reach us through: - QQ Group: 10419777 - WeChat Group: +## V1.2.0 Update +1. Add PM_SuperColorTransfer node to simplify the color transfer process +2. Add PM_SuperMakeUpTransfer node to simplify the process of makeup transfer + + ## V1.1.0 Update 1. faceskin adds blur option 2. Add PM_FaceShapMatch node. same as easyphot faceshap match diff --git a/README_zh-CN.md b/README_zh-CN.md index 8709170..5754929 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -15,6 +15,10 @@ English | [简体中文](./README_zh-CN.md) - QQ 群:10419777 - 微信群: +## V1.2.0 Update +1. 增加PM_SuperColorTransfer 节点,简化了颜色迁移的流程 +2. 增加PM_SuperMakeUpTransfer 节点,简化了进行装扮迁移的流程 + ## v1.1.0 更新 1. faceskin 增加模糊选项 2. 增加 PM_FaceShapMatch节点 与easyphoto的FaceshapMatch一致 diff --git a/__init__.py b/__init__.py index 46d0e23..5753945 100644 --- a/__init__.py +++ b/__init__.py @@ -77,6 +77,8 @@ NODE_CLASS_MAPPINGS = { "PM_GetImageInfo": GetImageInfoPM, "PM_MakeUpTransfer": MakeUpTransferPM, "PM_FaceShapMatch": FaceShapMatchPM, + "PM_SuperColorTransfer": SuperColorTransferPM, + "PM_SuperMakeUpTransfer": SuperMakeUpTransferPM, } NODE_DISPLAY_NAME_MAPPINGS = { "PM_RetinaFace": "RetinaFace PM", @@ -95,7 +97,9 @@ NODE_DISPLAY_NAME_MAPPINGS = { "PM_ImageResizeTarget": "ImageResizeTarget PM", "PM_GetImageInfo": "GetImageInfo PM", "PM_MakeUpTransfer": "MakeUpTransfer PM", - "PM_FaceShapMatch":"FaceShapMatch PM" + "PM_FaceShapMatch": "FaceShapMatch PM", + "PM_SuperColorTransfer": "SuperColorTransfer PM", + "PM_SuperMakeUpTransfer": "SuperMakeUpTransfer PM", } __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/images/wechat.jpg b/images/wechat.jpg index 03229da..7bed7b3 100644 Binary files a/images/wechat.jpg and b/images/wechat.jpg differ diff --git a/portrait/nodes.py b/portrait/nodes.py index 4a1c92a..0007818 100644 --- a/portrait/nodes.py +++ b/portrait/nodes.py @@ -6,8 +6,9 @@ from .utils.face_process_utils import call_face_crop, color_transfer, Face_Skin from .utils.img_utils import img_to_tensor, tensor_to_img, tensor_to_np, np_to_tensor, np_to_mask, img_to_mask, img_to_np from .model_holder import * -# import pydevd_pycharm -# pydevd_pycharm.settrace('49.7.62.197', port=10090, stdoutToServer=True, stderrToServer=True) +import pydevd_pycharm + +pydevd_pycharm.settrace('49.7.62.197', port=10090, stdoutToServer=True, stderrToServer=True) class RetinaFacePM: @classmethod @@ -348,8 +349,6 @@ class MakeUpTransferPM: CATEGORY = "protrait/model" def makeup_transfer(self, source_image, makeup_image): - source_image = tensor_to_img(source_image).resize([256, 256]) - makeup_image = tensor_to_img(makeup_image).resize([256, 256]) result = get_pagan_interface().transfer(source_image, makeup_image) return (img_to_tensor(result),) @@ -389,3 +388,73 @@ class FaceShapMatchPM: input_image_uint8 = np.array(source_image) * face_skin_mask + np.array(match_image) * (1 - face_skin_mask) return (np_to_tensor(input_image_uint8),) + +class SuperColorTransferPM: + + @classmethod + def INPUT_TYPES(s): + return \ + { + "required": { + "main_image": ("IMAGE",), + "transfer_image": ("IMAGE",), + }, + "optional": { + "avatar_box": ("BOX",), + }, + } + + RETURN_TYPES = ("IMAGE",) + + FUNCTION = "super_color_transfer" + CATEGORY = "protrait/model" + + def super_color_transfer(self, main_image, transfer_image, avatar_box=None): + origin_np = tensor_to_np(main_image) + if avatar_box is not None: + main_image = main_image[:, avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] + transfer_image = transfer_image[:, avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] + + transfer_result = color_transfer(tensor_to_np(main_image), tensor_to_np(transfer_image)) # 进行颜色迁移 + + face_skin_img = get_face_skin()(Image.fromarray(transfer_result), get_retinaface_detection(), [[1, 2, 3, 4, 5, 10, 12, 13]])[0] + face_skin_np = img_to_np(face_skin_img) + face_skin_np = cv2.blur(face_skin_np, (32, 32)) / 255 + + masked_img_np = tensor_to_np(main_image) * (1 - face_skin_np) + transfer_result * face_skin_np + + origin_np[avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] = masked_img_np + + return (np_to_tensor(origin_np),) + +class SuperMakeUpTransferPM: + + @classmethod + def INPUT_TYPES(s): + return \ + { + "required": { + "main_image": ("IMAGE",), + "makeup_image": ("IMAGE",), + }, + "optional": { + "avatar_box": ("BOX",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "super_makeup_transfer" + CATEGORY = "protrait/model" + + def super_makeup_transfer(self, main_image, makeup_image, avatar_box=None): + box_width, box_height = avatar_box[2] - avatar_box[0], avatar_box[3] - avatar_box[1] + origin_np = tensor_to_np(main_image) + if avatar_box is not None: + main_image = main_image[:, avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] + makeup_image = makeup_image[:, avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] + resize_source_box_image = tensor_to_img(main_image).resize([256, 256]) + resize_makeup_box_image = tensor_to_img(makeup_image).resize([256, 256]) + transfer_image = get_pagan_interface().transfer(resize_source_box_image, resize_makeup_box_image) + box_size_transfer = transfer_image.resize([box_width, box_height], Image.Resampling.LANCZOS) + origin_np[avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] = img_to_np(box_size_transfer) + return (np_to_tensor(origin_np),)