Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d09d46f16 | ||
|
|
bac4d69fba | ||
|
|
0c06d3e3bf | ||
|
|
7861f289c5 | ||
|
|
fd5f4737fe | ||
|
|
fb2a1e3283 | ||
|
|
75cb3303a9 | ||
|
|
2763361b55 | ||
|
|
3f70c7eb6b | ||
|
|
5beb93db0a | ||
|
|
98d3b8ac13 | ||
|
|
c871825137 | ||
|
|
26245524ea | ||
|
|
2292031e5f | ||
|
|
017dcee81b | ||
|
|
6d6eb679d5 | ||
|
|
2abacd7fde | ||
|
|
6a2d356ed7 | ||
|
|
97c93eff3d | ||
|
|
2598a6efe4 | ||
|
|
dedd32d29e | ||
|
|
a839966e9f | ||
|
|
c8e3535869 | ||
|
|
359e72cd2e | ||
|
|
a11d54294f | ||
|
|
6013eb426c | ||
|
|
0b005db217 | ||
|
|
658f057ccd | ||
|
|
5b77e1cf3d | ||
|
|
a6dbede699 | ||
|
|
dc01281d85 | ||
|
|
948b679e70 | ||
|
|
6335f3c3d4 | ||
|
|
3744780d0b | ||
|
|
425924bcbb | ||
|
|
d364f2874a | ||
|
|
e75c9a402a | ||
|
|
e45b89d111 | ||
|
|
3c61c01ba1 | ||
|
|
a41dfa668c | ||
|
|
e54e9ea579 |
@@ -1,2 +1,3 @@
|
||||
__pycache__
|
||||
.idea
|
||||
*.DS_Store
|
||||
|
||||
@@ -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
|
||||
@@ -131,6 +132,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
-1
@@ -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 增加模糊选项
|
||||
@@ -117,6 +118,8 @@ Easyphoto工作位置: [./workflow/easyphoto.json](./workflows/easyphoto.json )
|
||||
* GetImageInfo PM: 提取图片的宽高
|
||||
* FaceShapMatchPM: 扩散后的图片和原图片进行一定的融合,减少脸旁边的差异
|
||||
* MakeUpTransferPM: 使用gan网络模型对妆容进行一定的迁移
|
||||
* SuperMakeUpTransferPM:(多节点的整合)融合两张图片的装扮
|
||||
* SuperColorTransferPM:(多节点的整合)迁移两张图片的颜色
|
||||
|
||||
## 贡献
|
||||
|
||||
|
||||
+5
-29
@@ -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
Binary file not shown.
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 679 KiB |
Binary file not shown.
+2
-2
@@ -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"),
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import sys
|
||||
import subprocess
|
||||
import threading
|
||||
import locale
|
||||
import traceback
|
||||
import re
|
||||
from portrait.config import *
|
||||
|
||||
windows_not_install = ['mmcv_full\n']
|
||||
|
||||
def log(msg, end=None, file=None):
|
||||
print('Portrait Maker ==============', 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"[Portrait Maker] 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"[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
||||
return set()
|
||||
|
||||
return pip_list
|
||||
|
||||
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("### ComfyUI-Portrait-Maker: 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']
|
||||
else:
|
||||
pip_install = [sys.executable, '-m', 'pip', 'install', '-q']
|
||||
mim_install = [sys.executable, '-m', 'mim', 'install']
|
||||
|
||||
subpack_req = os.path.join(root_path, "requirements.txt")
|
||||
check_and_install_requirements(subpack_req)
|
||||
if platform.system() != "Windows" :
|
||||
process_wrap(pip_install + ['mmcv_full'], cwd=root_path)
|
||||
if sys.argv[0] == 'install.py':
|
||||
sys.path.append('.') # for portable version
|
||||
|
||||
except Exception as e:
|
||||
log("[ERROR] ComfyUI-Impact-Pack: Dependency installation has failed. Please install manually.")
|
||||
traceback.print_exc()
|
||||
@@ -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
|
||||
|
||||
|
||||
+80
-21
@@ -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,6 +46,14 @@ 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)
|
||||
@@ -55,12 +63,24 @@ class FaceFusionPM:
|
||||
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)
|
||||
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 +97,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 +156,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 +184,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 +202,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 +243,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 +295,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 +328,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 +347,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 +369,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),)
|
||||
|
||||
@@ -407,10 +429,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 +445,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 +470,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 +484,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 is "":
|
||||
result = str(round(score, 2))
|
||||
else:
|
||||
result = f"{result_prefix}_{round(score, 2)}"
|
||||
return (result,)
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user