51 Commits
Author SHA1 Message Date
toto fde067c9f5 update node exception 2024-03-07 14:45:08 +08:00
toto f233cae900 Auto update 2024-03-07 14:37:13 +08:00
tototianhao 94dcf5b2d9 Auto update 2024-01-12 01:46:22 +08:00
tototianhao db728d3b70 Auto update 2024-01-12 01:41:04 +08:00
toto 9a7cdbd756 update linux install error 2023-12-29 14:38:03 +08:00
toto 74dce3296d update roop 2023-12-21 12:39:38 +08:00
toto e30f28d20e update ali 2023-12-21 12:35:35 +08:00
TaylorGoulding baa3724ae2 Update README_zh-CN.md 2023-12-18 19:00:42 +08:00
TaylorGoulding 9453492f02 Update README.md 2023-12-18 19:00:24 +08:00
toto b2d44fb139 add mmcv 2023-12-18 16:14:54 +08:00
toto 1d09d46f16 update sim 2023-12-18 16:08:37 +08:00
toto bac4d69fba update sim 2023-12-18 14:40:09 +08:00
toto 0c06d3e3bf update socre 2023-12-18 14:29:24 +08:00
toto 7861f289c5 update sim 2023-12-18 11:58:05 +08:00
toto fd5f4737fe update log 2023-12-17 07:19:19 +08:00
toto fb2a1e3283 update 2023-12-17 07:08:29 +08:00
toto 75cb3303a9 not download base model 2023-12-17 06:54:14 +08:00
toto 2763361b55 update 2023-12-17 06:53:25 +08:00
toto 3f70c7eb6b update insightface; 2023-12-17 00:36:09 +08:00
toto 5beb93db0a update insightface 2023-12-17 00:35:44 +08:00
toto 98d3b8ac13 fix log error 2023-12-16 22:44:29 +08:00
toto c871825137 update pip install 2023-12-16 22:41:11 +08:00
toto 26245524ea update import 2023-12-16 22:27:08 +08:00
toto 2292031e5f add log 2023-12-16 22:25:46 +08:00
toto 017dcee81b update root path 2023-12-16 22:22:56 +08:00
toto 6d6eb679d5 update 2023-12-16 22:13:48 +08:00
toto 2abacd7fde update pip 2023-12-16 21:43:12 +08:00
toto 6a2d356ed7 add some pack 2023-12-16 21:36:21 +08:00
toto 97c93eff3d window 2023-12-16 20:13:00 +08:00
toto 2598a6efe4 update depen 2023-12-16 19:54:39 +08:00
toto dedd32d29e update 2023-12-16 19:29:37 +08:00
toto a839966e9f update 2023-12-16 19:15:10 +08:00
toto c8e3535869 update nodes 2023-12-16 18:54:17 +08:00
tototianhao 359e72cd2e update plugin 2023-12-16 18:46:32 +08:00
tototianhao a11d54294f update depen 2023-12-15 17:52:12 +08:00
TaylorGoulding 6013eb426c Update requirements.txt 2023-12-15 17:45:26 +08:00
TaylorGoulding 0b005db217 Update requirements.txt 2023-12-15 17:45:12 +08:00
TaylorGoulding 658f057ccd Update __init__.py 2023-12-15 17:42:31 +08:00
TaylorGoulding 5b77e1cf3d Update requirements.txt 2023-12-15 17:32:57 +08:00
toto a6dbede699 update image size 2023-12-12 14:42:24 +08:00
toto dc01281d85 update jpg 2023-12-12 14:40:59 +08:00
toto 948b679e70 udpate 2023-12-12 13:19:41 +08:00
toto 6335f3c3d4 update 2023-12-12 13:07:40 +08:00
toto 3744780d0b add prcode 2023-12-12 13:07:08 +08:00
tototianhao 425924bcbb update wechat image 2023-11-12 00:17:11 +08:00
toto d364f2874a update wechat png 2023-11-02 10:54:36 +08:00
toto e75c9a402a update 2023-11-01 14:46:51 +08:00
toto e45b89d111 bugfix makeup transfer error 2023-10-30 20:56:51 +08:00
toto 3c61c01ba1 bugfix face fusion roop model can't detect face 2023-10-25 22:45:56 +08:00
toto a41dfa668c bugfix 2023-10-25 21:45:38 +08:00
toto e54e9ea579 add workflow readme 2023-10-25 21:21:20 +08:00
13 changed files with 4709 additions and 72 deletions
+1
View File
@@ -1,2 +1,3 @@
__pycache__
.idea
*.DS_Store
+4 -5
View File
@@ -11,11 +11,12 @@ If you have any questions or suggestions, you can reach us through:
- Email: tototianhao@gmail.com
- telegram: https://t.me/+JoFE2vqHU4phZjg1
- QQ Group: 10419777
- WeChat Group: <img src="./images/wechat.jpg" width="200">
- WeChat Group: <img src="./images/wechat.jpg" width="300">
## 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
3. Add v1.2.0 workflow
## V1.1.0 Update
@@ -40,10 +41,6 @@ If you have any questions or suggestions, you can reach us through:
## Installation
**Note: When you start the plugin for the first time, it will download all the models required by EasyPhoto. You can see the download progress in the terminal. Please do not interrupt the download (no hash verification for startup speed). If the download is interrupted, you need to manually delete the files downloaded halfway last time and download them again.**
### For Windows users
If you are using a project extracted from a zip package while using ComfyUI, you won't be able to use this plugin. This project relies on ModelScope, but the virtual environment provided in the official ComfyUI zip package cannot install ModelScope. Furthermore, the ComfyUI author has responded, stating that this issue cannot be resolved.[aliyunsdkcor error](https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/223) If Windows users wish to use this plugin for analyzing and composing ComfyUI workflows, they will need to create their own virtual environment. (I am using Python 3.10.6.).Of course, if you know a solution, feel free to submit a pull request (PR).
### install step
1. First, install ComfyUI.
@@ -131,6 +128,8 @@ Click "Load" in the right panel of ComfyUI and select the ./workflow/easyphoto_w
* Face Shape Match PM: Apply a certain level of fusion between the diffused image and the original image to reduce differences around the face.
* Makeup Transfer PM: Use a GAN network model to perform makeup transfer.
* SuperMakeUpTransfer PM:(Multi-node integration) makeup by merging two pictures
* SuperColorTransfer PM:(Multi-node integration) transfer the colors of two pictures
## Contribution
If you find any issues or have suggestions for improvement, feel free to contribute. Follow these steps:
+4 -5
View File
@@ -13,11 +13,12 @@ English | [简体中文](./README_zh-CN.md)
- 电子邮件:tototianhao@gmail.com
- telegram: https://t.me/+JoFE2vqHU4phZjg1
- QQ 群:10419777
- 微信群: <img src="./images/wechat.jpg" width="200">
- 微信群: <img src="./images/wechat.jpg" width="300">
## V1.2.0 Update
1. 增加PM_SuperColorTransfer 节点,简化了颜色迁移的流程
2. 增加PM_SuperMakeUpTransfer 节点,简化了进行装扮迁移的流程
3. 增加v1.2.0 workflow
## v1.1.0 更新
1. faceskin 增加模糊选项
@@ -43,10 +44,6 @@ English | [简体中文](./README_zh-CN.md)
**注意:初次启动插件的时候会下载EasyPhoto所需要的所有模型,在terminal中可以看到下载进度,请不要中断下载,(为了启动速度,没有做hash校验),如果中断下载,需要手动删除上次下载一半的文件,重新下载。**
### windows用户
如果在使用ComfyUI的时候使用zip包解压后的项目,是无法使用本插件的,本项目依赖modelscope,但是ComfyUI官方zip包中的虚拟环境无法安装modelscope,并且ComfyUI作者已经回复了表示无法解决此问题[aliyunsdkcor error](https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/223)如果windows用户想使用本插件来分析、组合ComfyUI的流程,请自己创建虚拟环境。(我使用的是python3.10.6),当然如果您知道解决的方法,欢迎提交pr
### 步骤
1. 首先安装ComfyUI
@@ -117,6 +114,8 @@ Easyphoto工作位置: [./workflow/easyphoto.json](./workflows/easyphoto.json )
* GetImageInfo PM: 提取图片的宽高
* FaceShapMatchPM: 扩散后的图片和原图片进行一定的融合,减少脸旁边的差异
* MakeUpTransferPM: 使用gan网络模型对妆容进行一定的迁移
* SuperMakeUpTransferPM:(多节点的整合)融合两张图片的装扮
* SuperColorTransferPM:(多节点的整合)迁移两张图片的颜色
## 贡献
+5 -29
View File
@@ -1,12 +1,9 @@
import sys
import os
import os, sys
main_path = os.path.dirname(__file__)
sys.path.append(main_path)
import subprocess
import threading
import portrait.install
import requests
from tqdm import tqdm
from portrait.nodes import *
@@ -14,30 +11,6 @@ from portrait.nodes import *
# import pydevd_pycharm
# pydevd_pycharm.settrace('49.7.62.197', port=10090, stdoutToServer=True, stderrToServer=True)
def handle_stream(stream, prefix):
for line in stream:
print(prefix, line, end="")
def run_script(cmd, cwd='.'):
process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)
stdout_thread = threading.Thread(target=handle_stream, args=(process.stdout, ""))
stderr_thread = threading.Thread(target=handle_stream, args=(process.stderr, "[!]"))
stdout_thread.start()
stderr_thread.start()
stdout_thread.join()
stderr_thread.join()
return process.wait()
print("## installing dependencies")
requirements_path = os.path.join(main_path, "requirements.txt")
run_script([sys.executable, '-s', '-m', 'pip', 'install', '-q', '-r', requirements_path])
def urldownload_progressbar(url, file_path):
response = requests.get(url, stream=True)
total_size = int(response.headers.get('content-length', 0))
@@ -79,6 +52,7 @@ NODE_CLASS_MAPPINGS = {
"PM_FaceShapMatch": FaceShapMatchPM,
"PM_SuperColorTransfer": SuperColorTransferPM,
"PM_SuperMakeUpTransfer": SuperMakeUpTransferPM,
"PM_Similarity": SimilarityPM,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PM_RetinaFace": "RetinaFace PM",
@@ -100,6 +74,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"PM_FaceShapMatch": "FaceShapMatch PM",
"PM_SuperColorTransfer": "SuperColorTransfer PM",
"PM_SuperMakeUpTransfer": "SuperMakeUpTransfer PM",
"PM_Similarity": "Similarity PM",
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
Regular → Executable
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 679 KiB

Binary file not shown.
+2 -2
View File
@@ -6,7 +6,7 @@ utils_path = os.path.join(root_path, "utils")
models_path = os.path.join(root_path, "models")
# save_dirs
urls = [
"https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/webui/ChilloutMix-ni-fp16.safetensors",
# "https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/webui/ChilloutMix-ni-fp16.safetensors",
"https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/webui/control_v11p_sd15_openpose.pth",
"https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/webui/control_v11p_sd15_canny.pth",
"https://pai-aigc-photog.oss-cn-hangzhou.aliyuncs.com/webui/control_v11f1e_sd15_tile.pth",
@@ -23,7 +23,7 @@ urls = [
]
filenames = [
os.path.join(folder_names_and_paths['checkpoints'][0][0], "Chilloutmix-Ni-pruned-fp16-fix.safetensors"),
# os.path.join(folder_names_and_paths['checkpoints'][0][0], "Chilloutmix-Ni-pruned-fp16-fix.safetensors"),
os.path.join(folder_names_and_paths['controlnet'][0][0], "control_v11p_sd15_openpose.pth"),
os.path.join(folder_names_and_paths['controlnet'][0][0], "control_v11p_sd15_canny.pth"),
os.path.join(folder_names_and_paths['controlnet'][0][0], "control_v11f1e_sd15_tile.pth"),
+114
View File
@@ -0,0 +1,114 @@
import os
import sys
import subprocess
import threading
import locale
import traceback
import re
from .config import root_path
plugin_name = os.path.basename(root_path)
windows_not_install = ['mmcv_full\n']
def log(msg, end=None, file=None):
print(f'{plugin_name} :', msg, end=end, file=file)
def handle_stream(stream, is_stdout):
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
for msg in stream:
if is_stdout:
log(msg, end="", file=sys.stdout)
else:
log(msg, end="", file=sys.stderr)
def process_wrap(cmd_str, cwd=None, handler=None):
log(f"EXECUTE: {cmd_str} in '{cwd}'")
process = subprocess.Popen(cmd_str, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)
if handler is None:
handler = handle_stream
stdout_thread = threading.Thread(target=handler, args=(process.stdout, True))
stderr_thread = threading.Thread(target=handler, args=(process.stderr, False))
stdout_thread.start()
stderr_thread.start()
stdout_thread.join()
stderr_thread.join()
return process.wait()
# ---
pip_list = None
def get_installed_packages():
global pip_list
if pip_list is None:
try:
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
pip_list = set([line.split()[0].lower() for line in result.split('\n') if line.strip()])
except subprocess.CalledProcessError as e:
log(f"Failed to retrieve the information of installed pip packages.")
return set()
return pip_list
def mmcv_install():
process_wrap(pip_install + ['-U', 'openmim'], cwd=root_path)
process_wrap(mim_install + ['mmcv-full'], cwd=root_path)
pass
def is_installed(name):
name = name.strip()
pattern = r'([^<>!=]+)([<>!=]=?)'
match = re.search(pattern, name)
if match:
name = match.group(1)
result = name.lower() in get_installed_packages()
return result
def check_and_install_requirements(file_path):
log(file_path)
version = sys.version_info[:2]
if os.path.exists(file_path):
with open(file_path, 'r') as file:
lines = file.readlines()
for line in lines:
log(line)
if not is_installed(line):
if platform.system() == "Windows" and version[1] == 11 and 'insightface' in line:
process_wrap(pip_install + ['insightface-0.7.3-cp311-cp311-win_amd64.whl'], cwd=root_path)
continue
if platform.system() == "Windows" and line in windows_not_install:
log(f"windows skip {line}")
continue
log(f"install {line}")
process_wrap(pip_install + [line], cwd=root_path)
return False
return True
try:
import platform
log("### : Check dependencies")
if "python_embed" in sys.executable or "python_embedded" in sys.executable:
pip_install = [sys.executable, '-s', '-m', 'pip', 'install', '-q']
mim_install = [sys.executable, '-s', '-m', 'mim', 'install', '-q']
else:
pip_install = [sys.executable, '-m', 'pip', 'install', '-q']
mim_install = [sys.executable, '-m', 'mim', 'install', '-q']
subpack_req = os.path.join(root_path, "requirements.txt")
# mmcv_install()
check_and_install_requirements(subpack_req)
if sys.argv[0] == 'install.py':
sys.path.append('..') # for portable version
except Exception as e:
log("Dependency installation has failed. Please install manually.")
traceback.print_exc()
+8
View File
@@ -16,6 +16,7 @@ skin_retouching = None
portrait_enhancement = None
psgan_interface = None
real_gan_sr = None
face_recognition = None
def get_retinaface_detection():
global retinaface_detection
@@ -72,3 +73,10 @@ def get_pagan_interface():
makeup_transfer_model_path = os.path.join(models_path, "makeup_transfer.pth")
psgan_interface = PSGAN_Inference("cuda", makeup_transfer_model_path, get_retinaface_detection(), get_face_skin(), face_landmarks_model_path)
return psgan_interface
def get_face_recognition():
global face_recognition
if face_recognition is None:
face_recognition = pipeline("face_recognition", model="bubbliiiing/cv_retinafce_recognition", model_revision="v1.0.3")
return face_recognition
+92 -28
View File
@@ -6,9 +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
@@ -46,21 +46,46 @@ class FaceFusionPM:
CATEGORY = "protrait/model"
def resize(self, tensor):
image = tensor_to_img(tensor)
short_side = max(image.width, image.height)
resize = float(short_side / 640)
new_size = (int(image.width // resize), int(image.height // resize))
resize_image = image.resize(new_size, Image.Resampling.LANCZOS)
return img_to_np(resize_image)
def img_face_fusion(self, source_image, swap_image, mode):
if mode == "ali":
source_image = tensor_to_img(source_image)
swap_image = tensor_to_img(swap_image)
fusion_image = get_image_face_fusion()(dict(template=source_image, user=swap_image))[
source_image_pil = tensor_to_img(source_image)
swap_image_pil = tensor_to_img(swap_image)
fusion_image = get_image_face_fusion()(dict(template=source_image_pil, user=swap_image_pil))[
OutputKeys.OUTPUT_IMG]
result_image = Image.fromarray(cv2.cvtColor(fusion_image, cv2.COLOR_BGR2RGB))
return (img_to_tensor(result_image),)
else:
width, height = source_image.shape[2], source_image.shape[1]
need_resize = False
source_np = tensor_to_np(source_image)
swap_np = tensor_to_np(swap_image)
if source_image.shape[2] > 640 or source_image.shape[1] > 640:
source_np = self.resize(source_image)
need_resize = True
if swap_image.shape[2] > 640 or swap_image.shape[1] > 640:
swap_np = self.resize(swap_image)
get_face_analysis().prepare(ctx_id=0, det_size=(640, 640))
source_image = tensor_to_np(source_image)
faces = get_face_analysis().get(source_image)
swap_image = tensor_to_np(swap_image)
swap_face = get_face_analysis().get(swap_image)
result_image = get_roop().get(source_image, faces[0], swap_face[0], paste_back=True)
faces = get_face_analysis().get(source_np)
swap_faces = get_face_analysis().get(swap_np)
if len(faces) == 0:
raise RuntimeError("No face was recognized in the source image / source image 没有识别到人脸")
if len(swap_faces) == 0:
print("No face was recognized in the swap faces / swap faces没有识别到人脸, 用原脸替换!!!!!!!!!")
return (source_image,)
result_image = get_roop().get(source_np, faces[0], swap_faces[0], paste_back=True)
if need_resize:
image = Image.fromarray(result_image)
new_size = width, height
result_image = image.resize(new_size, Image.Resampling.LANCZOS)
result_image = img_to_np(result_image)
return (np_to_tensor(result_image),)
class RatioMerge2ImagePM:
@@ -77,7 +102,7 @@ class RatioMerge2ImagePM:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "image_ratio_merge"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def image_ratio_merge(self, image1, image2, fusion_rate):
rate_fusion_image = image1 * (1 - fusion_rate) + image2 * fusion_rate
@@ -136,7 +161,7 @@ class ExpandMaskFaceWidthPM:
RETURN_TYPES = ("MASK", "BOX")
FUNCTION = "expand_mask_face_width"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def expand_mask_face_width(self, mask, box, expand_width):
h, w = mask.shape[1], mask.shape[2]
@@ -164,7 +189,7 @@ class BoxCropImagePM:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("crop_image",)
FUNCTION = "box_crop_image"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def box_crop_image(self, image, box):
image = image[:, box[1]:box[3], box[0]:box[2], :]
@@ -182,7 +207,7 @@ class ColorTransferPM:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "color_transfer"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def color_transfer(self, transfer_from, transfer_to):
transfer_result = color_transfer(tensor_to_np(transfer_from), tensor_to_np(transfer_to)) # 进行颜色迁移
@@ -223,7 +248,7 @@ class MaskDilateErodePM:
RETURN_TYPES = ("MASK",)
FUNCTION = "mask_dilate_erode"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def mask_dilate_erode(self, mask):
out_mask = Image.fromarray(np.uint8(cv2.dilate(tensor_to_np(mask), np.ones((96, 96), np.uint8), iterations=1) - cv2.erode(tensor_to_np(mask), np.ones((48, 48), np.uint8), iterations=1)))
@@ -275,13 +300,13 @@ class ImageScaleShortPM:
return {"required": {
"image": ("IMAGE",),
"size": ("INT", {"default": 512, "min": 0, "max": 2048, "step": 1}),
"crop_face": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}),
"crop_face": ("BOOLEAN", {"default": False}),
}}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "image_scale_short"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def image_scale_short(self, image, size, crop_face):
input_image = tensor_to_img(image)
@@ -308,7 +333,7 @@ class ImageResizeTargetPM:
FUNCTION = "image_resize_target"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def image_resize_target(self, image, width, height):
imagepi = tensor_to_img(image)
@@ -327,7 +352,7 @@ class GetImageInfoPM:
FUNCTION = "get_image_info"
CATEGORY = "protrait/model"
CATEGORY = "protrait/other"
def get_image_info(self, image):
width = image.shape[2]
@@ -349,6 +374,8 @@ class MakeUpTransferPM:
CATEGORY = "protrait/model"
def makeup_transfer(self, source_image, makeup_image):
source_image = tensor_to_img(source_image)
makeup_image = tensor_to_img(makeup_image)
result = get_pagan_interface().transfer(source_image, makeup_image)
return (img_to_tensor(result),)
@@ -370,9 +397,9 @@ class FaceShapMatchPM:
def faceshap_match(self, source_image, match_image, face_box):
# detect face area
source_image = tensor_to_img(source_image)
match_image = tensor_to_img(match_image)
face_skin_mask = get_face_skin()(source_image, get_retinaface_detection(), needs_index=[[1, 2, 3, 4, 5, 7, 8, 10, 11, 12, 13]])[0]
source_image_copy = tensor_to_img(source_image)
match_image_copy = tensor_to_img(match_image)
face_skin_mask = get_face_skin()(source_image_copy, get_retinaface_detection(), needs_index=[[1, 2, 3, 4, 5, 7, 8, 10, 11, 12, 13]])[0]
face_width = face_box[2] - face_box[0]
kernel_size = np.ones((int(face_width // 10), int(face_width // 10)), np.uint8)
@@ -385,7 +412,7 @@ class FaceShapMatchPM:
# paste back to photo, Using I2I generation controlled solely by OpenPose, even with a very small denoise amplitude,
# still carries the risk of introducing NSFW and global incoherence.!!! important!!!
input_image_uint8 = np.array(source_image) * face_skin_mask + np.array(match_image) * (1 - face_skin_mask)
input_image_uint8 = np.array(source_image_copy) * face_skin_mask + np.array(match_image_copy) * (1 - face_skin_mask)
return (np_to_tensor(input_image_uint8),)
@@ -407,10 +434,11 @@ class SuperColorTransferPM:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "super_color_transfer"
CATEGORY = "protrait/model"
CATEGORY = "protrait/super"
def super_color_transfer(self, main_image, transfer_image, avatar_box=None):
origin_np = tensor_to_np(main_image)
result_np = None
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], :]
@@ -422,10 +450,13 @@ class SuperColorTransferPM:
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
result_np = masked_img_np
origin_np[avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] = masked_img_np
if avatar_box is not None:
origin_np[avatar_box[1]:avatar_box[3], avatar_box[0]:avatar_box[2], :] = masked_img_np
result_np = origin_np
return (np_to_tensor(origin_np),)
return (np_to_tensor(result_np),)
class SuperMakeUpTransferPM:
@@ -444,7 +475,7 @@ class SuperMakeUpTransferPM:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "super_makeup_transfer"
CATEGORY = "protrait/model"
CATEGORY = "protrait/super"
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]
@@ -458,3 +489,36 @@ class SuperMakeUpTransferPM:
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),)
class SimilarityPM:
@classmethod
def INPUT_TYPES(s):
return \
{
"required": {
"main_image": ("IMAGE",),
"compare_image": ("IMAGE",),
"model": (["sim"],),
"result_prefix": ("STRING", {"default": ""}),
},
}
RETURN_TYPES = ("STRING",)
FUNCTION = "similarity_compare"
CATEGORY = "protrait/model"
def similarity_compare(self, main_image, compare_image, model, result_prefix):
main_image_copy = tensor_to_img(main_image)
compare_image_copy = tensor_to_img(compare_image)
score = None
result = None
if model == "sim":
root_embedding = get_face_recognition()(dict(user=Image.fromarray(np.uint8(main_image_copy))))[OutputKeys.IMG_EMBEDDING]
compare_embedding = get_face_recognition()(dict(user=Image.fromarray(np.uint8(compare_image_copy))))[OutputKeys.IMG_EMBEDDING]
score = float(np.dot(root_embedding, np.transpose(compare_embedding))[0][0])
if result_prefix == "":
result = str(round(score, 2))
else:
result = f"{result_prefix}_{round(score, 2)}"
return (result,)
+2 -2
View File
@@ -456,7 +456,7 @@ class PreProcess:
lms = lms[:, ::-1]
mask, diff = self.process(mask, lms, device=self.device)
image = image.resize((self.img_size, self.img_size), Image.ANTIALIAS)
image = image.resize((self.img_size, self.img_size), Image.Resampling.NEAREST)
image = self.transform(image)
real = to_var(image.unsqueeze(0))
return [real, mask, diff], face_on_image, crop_face
@@ -830,7 +830,7 @@ class PostProcess:
height, width = source.shape[:2]
small_source = cv2.resize(source, (self.img_size, self.img_size))
laplacian_diff = source.astype(np.float) - cv2.resize(small_source, (width, height)).astype(np.float)
laplacian_diff = source.astype(np.float64) - cv2.resize(small_source, (width, height)).astype(np.float64)
result = (cv2.resize(result, (width, height)) + laplacian_diff).round().clip(0, 255).astype(np.uint8)
if self.denoise:
result = cv2.fastNlMeansDenoisingColored(result)
+3 -1
View File
@@ -1,3 +1,4 @@
aliyun-python-sdk-core-v3==2.13.10
opencv-python
tensorflow-cpu
tensorflow
@@ -7,4 +8,5 @@ modelscope
scikit-image
matplotlib
insightface
diffusers==0.18.2
diffusers==0.18.2
sentencepiece
File diff suppressed because it is too large Load Diff