Add eyebrow scaling/rotation and cheek position controls
- Added eyebrow size controls (left/right, x/y scaling) - Added eyebrow rotation controls (left/right, degrees) - Added cheek position offset controls (left/right, x/y) - Implemented transform order: scale → rotate → translate for eyebrows - Updated settings_list import/export to handle 47 parameters - Updated web extension reset button with new defaults - All tests passed successfully Co-authored-by: EricRorich <86147326+EricRorich@users.noreply.github.com>
This commit is contained in:
co-authored by
EricRorich
parent
17d15b4c60
commit
8f4e2efd2c
+162
-29
@@ -305,7 +305,36 @@ class ComfyUIFaceShaper:
|
||||
"FLOAT",
|
||||
{"default": 1.0, "min": 0.5, "max": 2.0, "step": 0.01},
|
||||
),
|
||||
# Cheeks
|
||||
"cheek_left_pos_x": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.3, "max": 0.3, "step": 0.005},
|
||||
),
|
||||
"cheek_left_pos_y": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.3, "max": 0.3, "step": 0.005},
|
||||
),
|
||||
"cheek_right_pos_x": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.3, "max": 0.3, "step": 0.005},
|
||||
),
|
||||
"cheek_right_pos_y": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.3, "max": 0.3, "step": 0.005},
|
||||
),
|
||||
# Eyebrows
|
||||
"eyebrow_left_size_x": (
|
||||
"FLOAT",
|
||||
{"default": 1.0, "min": 0.5, "max": 2.0, "step": 0.01},
|
||||
),
|
||||
"eyebrow_left_size_y": (
|
||||
"FLOAT",
|
||||
{"default": 1.0, "min": 0.5, "max": 2.0, "step": 0.01},
|
||||
),
|
||||
"eyebrow_left_rotation": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -45.0, "max": 45.0, "step": 0.1},
|
||||
),
|
||||
"eyebrow_left_pos_x": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.5, "max": 0.5, "step": 0.01},
|
||||
@@ -314,6 +343,18 @@ class ComfyUIFaceShaper:
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.5, "max": 0.5, "step": 0.01},
|
||||
),
|
||||
"eyebrow_right_size_x": (
|
||||
"FLOAT",
|
||||
{"default": 1.0, "min": 0.5, "max": 2.0, "step": 0.01},
|
||||
),
|
||||
"eyebrow_right_size_y": (
|
||||
"FLOAT",
|
||||
{"default": 1.0, "min": 0.5, "max": 2.0, "step": 0.01},
|
||||
),
|
||||
"eyebrow_right_rotation": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -45.0, "max": 45.0, "step": 0.1},
|
||||
),
|
||||
"eyebrow_right_pos_x": (
|
||||
"FLOAT",
|
||||
{"default": 0.0, "min": -0.5, "max": 0.5, "step": 0.01},
|
||||
@@ -388,8 +429,18 @@ class ComfyUIFaceShaper:
|
||||
lip_lower_size_y: float,
|
||||
chin_size_x: float,
|
||||
chin_size_y: float,
|
||||
cheek_left_pos_x: float,
|
||||
cheek_left_pos_y: float,
|
||||
cheek_right_pos_x: float,
|
||||
cheek_right_pos_y: float,
|
||||
eyebrow_left_size_x: float,
|
||||
eyebrow_left_size_y: float,
|
||||
eyebrow_left_rotation: float,
|
||||
eyebrow_left_pos_x: float,
|
||||
eyebrow_left_pos_y: float,
|
||||
eyebrow_right_size_x: float,
|
||||
eyebrow_right_size_y: float,
|
||||
eyebrow_right_rotation: float,
|
||||
eyebrow_right_pos_x: float,
|
||||
eyebrow_right_pos_y: float,
|
||||
nose_pos_y: float,
|
||||
@@ -403,7 +454,7 @@ class ComfyUIFaceShaper:
|
||||
):
|
||||
"""Render the facial mask image and return it as a tensor."""
|
||||
# Handle settings_list import: override all adjustable parameters if provided
|
||||
if settings_list is not None and len(settings_list) >= 35:
|
||||
if settings_list is not None and len(settings_list) >= 47:
|
||||
eye_left_size_x = settings_list[0]
|
||||
eye_left_size_y = settings_list[1]
|
||||
eye_left_pos_x = settings_list[2]
|
||||
@@ -428,18 +479,28 @@ class ComfyUIFaceShaper:
|
||||
lip_lower_size_y = settings_list[21]
|
||||
chin_size_x = settings_list[22]
|
||||
chin_size_y = settings_list[23]
|
||||
eyebrow_left_pos_x = settings_list[24]
|
||||
eyebrow_left_pos_y = settings_list[25]
|
||||
eyebrow_right_pos_x = settings_list[26]
|
||||
eyebrow_right_pos_y = settings_list[27]
|
||||
nose_pos_y = settings_list[28]
|
||||
nose_size_x = settings_list[29]
|
||||
nose_size_y = settings_list[30]
|
||||
camera_distance = settings_list[31]
|
||||
camera_pos_x = settings_list[32]
|
||||
camera_pos_y = settings_list[33]
|
||||
line_thickness = settings_list[34]
|
||||
# Note: canvas_width/canvas_height (indices 35-36) are exported but not imported
|
||||
cheek_left_pos_x = settings_list[24]
|
||||
cheek_left_pos_y = settings_list[25]
|
||||
cheek_right_pos_x = settings_list[26]
|
||||
cheek_right_pos_y = settings_list[27]
|
||||
eyebrow_left_size_x = settings_list[28]
|
||||
eyebrow_left_size_y = settings_list[29]
|
||||
eyebrow_left_rotation = settings_list[30]
|
||||
eyebrow_left_pos_x = settings_list[31]
|
||||
eyebrow_left_pos_y = settings_list[32]
|
||||
eyebrow_right_size_x = settings_list[33]
|
||||
eyebrow_right_size_y = settings_list[34]
|
||||
eyebrow_right_rotation = settings_list[35]
|
||||
eyebrow_right_pos_x = settings_list[36]
|
||||
eyebrow_right_pos_y = settings_list[37]
|
||||
nose_pos_y = settings_list[38]
|
||||
nose_size_x = settings_list[39]
|
||||
nose_size_y = settings_list[40]
|
||||
camera_distance = settings_list[41]
|
||||
camera_pos_x = settings_list[42]
|
||||
camera_pos_y = settings_list[43]
|
||||
line_thickness = settings_list[44]
|
||||
# Note: canvas_width/canvas_height (indices 45-46) are exported but not imported
|
||||
# as they are always provided as direct parameters to the method
|
||||
|
||||
face_points = _face_data_for_gender(gender)
|
||||
@@ -488,6 +549,32 @@ class ComfyUIFaceShaper:
|
||||
) -> List[Tuple[float, float]]:
|
||||
return [(rx + offset_x, ry + offset_y) for rx, ry in points]
|
||||
|
||||
# Helper to rotate points around a center by angle in degrees.
|
||||
def rotate_polygon(
|
||||
points: List[Tuple[float, float]],
|
||||
angle_degrees: float,
|
||||
cx: float,
|
||||
cy: float,
|
||||
) -> List[Tuple[float, float]]:
|
||||
"""Rotate points around (cx, cy) by angle_degrees using 2D rotation."""
|
||||
if angle_degrees == 0.0:
|
||||
return points
|
||||
import math
|
||||
angle_rad = math.radians(angle_degrees)
|
||||
cos_a = math.cos(angle_rad)
|
||||
sin_a = math.sin(angle_rad)
|
||||
rotated = []
|
||||
for rx, ry in points:
|
||||
# Translate to origin
|
||||
dx = rx - cx
|
||||
dy = ry - cy
|
||||
# Rotate
|
||||
new_x = dx * cos_a - dy * sin_a
|
||||
new_y = dx * sin_a + dy * cos_a
|
||||
# Translate back
|
||||
rotated.append((cx + new_x, cy + new_y))
|
||||
return rotated
|
||||
|
||||
# Draw outer head outline with per-region horizontal scaling
|
||||
if "outer_head" in face_points:
|
||||
# Calculate centroid for scaling
|
||||
@@ -571,22 +658,48 @@ class ComfyUIFaceShaper:
|
||||
pixel_points = [to_pixel(pt) for pt in chin]
|
||||
draw.line(pixel_points, fill=(0, 0, 0), width=stroke_width)
|
||||
|
||||
# Draw eyebrows with translation
|
||||
# Draw eyebrows with scale → rotation → translation transform order
|
||||
if "eyebrow_left" in face_points:
|
||||
eyebrow_left = translate_polygon(
|
||||
face_points["eyebrow_left"],
|
||||
eyebrow_left_pos_x,
|
||||
eyebrow_left_pos_y,
|
||||
)
|
||||
# Calculate centroid
|
||||
eyebrow_left_points = face_points["eyebrow_left"]
|
||||
cx = sum(px for px, _ in eyebrow_left_points) / len(eyebrow_left_points)
|
||||
cy = sum(py for _, py in eyebrow_left_points) / len(eyebrow_left_points)
|
||||
|
||||
# Step 1: Scale around centroid
|
||||
scaled = []
|
||||
for rx, ry in eyebrow_left_points:
|
||||
dx = (rx - cx) * eyebrow_left_size_x
|
||||
dy = (ry - cy) * eyebrow_left_size_y
|
||||
scaled.append((cx + dx, cy + dy))
|
||||
|
||||
# Step 2: Rotate around centroid
|
||||
rotated = rotate_polygon(scaled, eyebrow_left_rotation, cx, cy)
|
||||
|
||||
# Step 3: Apply positional offset
|
||||
eyebrow_left = translate_polygon(rotated, eyebrow_left_pos_x, eyebrow_left_pos_y)
|
||||
|
||||
pixel_points = [to_pixel(pt) for pt in eyebrow_left]
|
||||
draw.line(pixel_points, fill=(0, 0, 0), width=stroke_width)
|
||||
|
||||
if "eyebrow_right" in face_points:
|
||||
eyebrow_right = translate_polygon(
|
||||
face_points["eyebrow_right"],
|
||||
eyebrow_right_pos_x,
|
||||
eyebrow_right_pos_y,
|
||||
)
|
||||
# Calculate centroid
|
||||
eyebrow_right_points = face_points["eyebrow_right"]
|
||||
cx = sum(px for px, _ in eyebrow_right_points) / len(eyebrow_right_points)
|
||||
cy = sum(py for _, py in eyebrow_right_points) / len(eyebrow_right_points)
|
||||
|
||||
# Step 1: Scale around centroid
|
||||
scaled = []
|
||||
for rx, ry in eyebrow_right_points:
|
||||
dx = (rx - cx) * eyebrow_right_size_x
|
||||
dy = (ry - cy) * eyebrow_right_size_y
|
||||
scaled.append((cx + dx, cy + dy))
|
||||
|
||||
# Step 2: Rotate around centroid
|
||||
rotated = rotate_polygon(scaled, eyebrow_right_rotation, cx, cy)
|
||||
|
||||
# Step 3: Apply positional offset
|
||||
eyebrow_right = translate_polygon(rotated, eyebrow_right_pos_x, eyebrow_right_pos_y)
|
||||
|
||||
pixel_points = [to_pixel(pt) for pt in eyebrow_right]
|
||||
draw.line(pixel_points, fill=(0, 0, 0), width=stroke_width)
|
||||
|
||||
@@ -602,20 +715,30 @@ class ComfyUIFaceShaper:
|
||||
pixel_points = [to_pixel(pt) for pt in nose]
|
||||
draw.line(pixel_points, fill=(0, 0, 0), width=stroke_width)
|
||||
|
||||
# Draw cheeks and connect them to outer head
|
||||
# Draw cheeks with position offsets and connect them to outer head
|
||||
if "cheek_left" in face_points:
|
||||
pixel_points = [to_pixel(pt) for pt in face_points["cheek_left"]]
|
||||
cheek_left = translate_polygon(
|
||||
face_points["cheek_left"],
|
||||
cheek_left_pos_x,
|
||||
cheek_left_pos_y,
|
||||
)
|
||||
pixel_points = [to_pixel(pt) for pt in cheek_left]
|
||||
draw.line(pixel_points, fill=(0, 0, 0), width=stroke_width)
|
||||
# Connect cheek_left last point to outer head point
|
||||
cheek_left_end = to_pixel((0.316923, 0.729073))
|
||||
cheek_left_end = to_pixel((0.316923 + cheek_left_pos_x, 0.729073 + cheek_left_pos_y))
|
||||
outer_head_point = to_pixel((0.320690, 0.854875))
|
||||
draw.line([cheek_left_end, outer_head_point], fill=(0, 0, 0), width=stroke_width)
|
||||
|
||||
if "cheek_right" in face_points:
|
||||
pixel_points = [to_pixel(pt) for pt in face_points["cheek_right"]]
|
||||
cheek_right = translate_polygon(
|
||||
face_points["cheek_right"],
|
||||
cheek_right_pos_x,
|
||||
cheek_right_pos_y,
|
||||
)
|
||||
pixel_points = [to_pixel(pt) for pt in cheek_right]
|
||||
draw.line(pixel_points, fill=(0, 0, 0), width=stroke_width)
|
||||
# Connect cheek_right last point to outer head point
|
||||
cheek_right_end = to_pixel((0.683077, 0.729073))
|
||||
cheek_right_end = to_pixel((0.683077 + cheek_right_pos_x, 0.729073 + cheek_right_pos_y))
|
||||
outer_head_point = to_pixel((0.679310, 0.854875))
|
||||
draw.line([cheek_right_end, outer_head_point], fill=(0, 0, 0), width=stroke_width)
|
||||
|
||||
@@ -693,8 +816,18 @@ class ComfyUIFaceShaper:
|
||||
lip_lower_size_y,
|
||||
chin_size_x,
|
||||
chin_size_y,
|
||||
cheek_left_pos_x,
|
||||
cheek_left_pos_y,
|
||||
cheek_right_pos_x,
|
||||
cheek_right_pos_y,
|
||||
eyebrow_left_size_x,
|
||||
eyebrow_left_size_y,
|
||||
eyebrow_left_rotation,
|
||||
eyebrow_left_pos_x,
|
||||
eyebrow_left_pos_y,
|
||||
eyebrow_right_size_x,
|
||||
eyebrow_right_size_y,
|
||||
eyebrow_right_rotation,
|
||||
eyebrow_right_pos_x,
|
||||
eyebrow_right_pos_y,
|
||||
nose_pos_y,
|
||||
|
||||
@@ -51,8 +51,18 @@ app.registerExtension({
|
||||
"lip_lower_size_y": 1.0,
|
||||
"chin_size_x": 1.0,
|
||||
"chin_size_y": 1.0,
|
||||
"cheek_left_pos_x": 0.0,
|
||||
"cheek_left_pos_y": 0.0,
|
||||
"cheek_right_pos_x": 0.0,
|
||||
"cheek_right_pos_y": 0.0,
|
||||
"eyebrow_left_size_x": 1.0,
|
||||
"eyebrow_left_size_y": 1.0,
|
||||
"eyebrow_left_rotation": 0.0,
|
||||
"eyebrow_left_pos_x": 0.0,
|
||||
"eyebrow_left_pos_y": 0.0,
|
||||
"eyebrow_right_size_x": 1.0,
|
||||
"eyebrow_right_size_y": 1.0,
|
||||
"eyebrow_right_rotation": 0.0,
|
||||
"eyebrow_right_pos_x": 0.0,
|
||||
"eyebrow_right_pos_y": 0.0,
|
||||
"nose_pos_y": 0.0,
|
||||
|
||||
Reference in New Issue
Block a user