Skip to content

Commit 6c26938

Browse files
committed
updated the MaskFromFaceModel node.
1 parent 5b1df56 commit 6c26938

File tree

4 files changed

+46
-9
lines changed

4 files changed

+46
-9
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ This node splits one mask into two masks of the same size according to the area
4949
This node is designed for growing masks quickly. When using the official or other mask growth nodes, the speed slows down significantly with large grow values, such as above 20. In contrast, this node maintains consistent speed regardless of the grow value.
5050

5151
## MaskFromFaceModel
52-
Generates a mask from the face model of the Reactor face swap node output. The mask covers the facial area below the eyes, excluding the forehead. If the forehead is crucial for your application, it's recommended to use a different mask or adjust the generated mask accordingly.
52+
Generates a mask from the face model of the Reactor face swap node. The mask covers the facial area below the eyes, excluding the forehead. Enabling add_bbox_upper_points provides a rough approximation but lacks precision. If the forehead is essential for your application, consider using a different mask or adjusting the generated mask as needed.
53+
54+
<img src="assets/maskFromFacemodel.png" width="100%"/>
5355

5456
## MaskAutoSelector
5557
Check the three input masks. If any are available, return the first. If none are available, raise an exception.

assets/maskFromFacemodel.png

229 KB
Loading

py/nodes.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from comfy_extras.nodes_upscale_model import ImageUpscaleWithModel
2323
from math import dist
2424
import folder_paths
25+
from .utils import tensor2np,np2tensor
2526

2627
config_dir = os.path.join(folder_paths.base_path, "config")
2728
if not os.path.exists(config_dir):
@@ -532,22 +533,31 @@ class MaskFromFaceModel:
532533

533534
@classmethod
534535
def INPUT_TYPES(self):
535-
536536
return {
537-
"required": {
538-
"face_model": ("FACE_MODEL", ),
539-
"size_as": ("IMAGE",),
537+
"required": {
538+
"image": ("IMAGE",),
540539
"max_face_number": ("INT", {"default": -1, "min": -1, "max": 99, "step": 1}),
540+
"add_bbox_upper_points": ("BOOLEAN", {"default": False}), # 新增参数
541541
},
542542
"optional": {
543+
"faceanalysis": ("FACEANALYSIS", ),
544+
"face_model": ("FACE_MODEL", ),
543545
}
544546
}
545547

546548
RETURN_TYPES = ("MASK",)
547549
FUNCTION = 'mask_get'
548550
CATEGORY = 'utils/mask'
549551

550-
def mask_get(self, face_model, size_as, max_face_number):
552+
def mask_get(self, image, max_face_number, add_bbox_upper_points, faceanalysis=None, face_model=None):
553+
if faceanalysis is None and face_model is None:
554+
raise Exception("both faceanalysis and face_model are none!")
555+
556+
if face_model is None:
557+
image_np = tensor2np(image)
558+
image_np = image_np[0] if isinstance(image_np, list) else image_np
559+
face_model = self.analyze_faces(faceanalysis, image_np)
560+
551561
if not isinstance(face_model,list):
552562
face_models = [face_model]
553563
else:
@@ -556,11 +566,28 @@ def mask_get(self, face_model, size_as, max_face_number):
556566
if max_face_number !=-1 and len(face_model) > max_face_number:
557567
face_models = self.remove_unavaible_face_models(face_models=face_models,max_people_number=max_face_number)
558568

559-
h, w = size_as.shape[-3:-1]
569+
h, w = image.shape[-3:-1]
560570

561571
result = np.zeros((h, w), dtype=np.uint8)
562572
for face in face_models:
563573
points = face.landmark_2d_106.astype(np.int32) # Convert landmarks to integer format
574+
575+
if add_bbox_upper_points:
576+
# 获取bbox的坐标
577+
x1, y1 = face.bbox[0:2]
578+
x2, y2 = face.bbox[2:4]
579+
580+
# 计算上边的1/4和3/4位置的点
581+
width = x2 - x1
582+
left_quarter_x = x1 + width // 4
583+
right_quarter_x = x2 - width // 4
584+
585+
# 创建两个新点
586+
left_quarter_point = np.array([left_quarter_x, y1], dtype=np.int32)
587+
right_quarter_point = np.array([right_quarter_x, y1], dtype=np.int32)
588+
589+
# 将两个点添加到landmarks中
590+
points = np.vstack((points, left_quarter_point, right_quarter_point))
564591

565592
points = points.reshape((-1, 1, 2)) # Reshape for cv2.drawContours
566593

@@ -603,7 +630,15 @@ def get_max_distance(self, keypoints):
603630

604631
return max_distance
605632

606-
633+
def analyze_faces(self, insightface, img_data: np.ndarray):
634+
for size in [(size, size) for size in range(640, 320, -320)]:
635+
insightface.det_model.input_size = size
636+
face = insightface.get(img_data)
637+
if face:
638+
if 640 not in size:
639+
print(f"\033[33mINFO: InsightFace detection resolution lowered to {size}.\033[0m")
640+
break
641+
return face
607642
class MaskCoverFourCorners:
608643

609644
@classmethod

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "comfyui-utils-nodes"
33
description = "Nodes:LoadImageWithSwitch, ImageBatchOneOrMore, ModifyTextGender, GenderControlOutput, ImageCompositeMaskedWithSwitch, ImageCompositeMaskedOneByOne, ColorCorrectOfUtils, SplitMask, MaskFastGrow, CheckpointLoaderSimpleWithSwitch, ImageResizeTo8x, MatchImageRatioToPreset, UpscaleImageWithModelIfNeed, MaskFromFaceModel, MaskCoverFourCorners, DetectorForNSFW, DeepfaceAnalyzeFaceAttributes etc."
4-
version = "1.1.8"
4+
version = "1.1.9"
55
license = { file = "LICENSE" }
66
dependencies = []
77

0 commit comments

Comments
 (0)