diff --git a/modules/processors/frame/face_masking.py b/modules/processors/frame/face_masking.py index 3ae963e..d0dfb64 100644 --- a/modules/processors/frame/face_masking.py +++ b/modules/processors/frame/face_masking.py @@ -285,6 +285,65 @@ def create_eyes_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, tuple return mask, eyes_cutout, (min_x, min_y, max_x, max_y), eyes_polygon +def create_curved_eyebrow(points): + if len(points) >= 5: + # Sort points by x-coordinate + sorted_idx = np.argsort(points[:, 0]) + sorted_points = points[sorted_idx] + + # Calculate dimensions + x_min, y_min = np.min(sorted_points, axis=0) + x_max, y_max = np.max(sorted_points, axis=0) + width = x_max - x_min + height = y_max - y_min + + # Create more points for smoother curve + num_points = 50 + x = np.linspace(x_min, x_max, num_points) + + # Fit quadratic curve through points for more natural arch + coeffs = np.polyfit(sorted_points[:, 0], sorted_points[:, 1], 2) # Changed to quadratic + y = np.polyval(coeffs, x) + + # Create points for top and bottom curves with consistent offsets + top_offset = height * 0.3 # Simplified offset for cleaner curve + bottom_offset = height * 0.1 # Thinner bottom curve + + # Create smooth curves + top_curve = y - top_offset + bottom_curve = y + bottom_offset + + # Create curved endpoints with slight taper + end_points = 5 + start_x = np.linspace(x[0] - width * 0.1, x[0], end_points) + end_x = np.linspace(x[-1], x[-1] + width * 0.1, end_points) + + # Create tapered ends + start_curve = np.column_stack(( + start_x, + np.linspace(bottom_curve[0], top_curve[0], end_points) + )) + end_curve = np.column_stack(( + end_x, + np.linspace(bottom_curve[-1], top_curve[-1], end_points) + )) + + # Combine all points to form a smooth contour + contour_points = np.vstack([ + start_curve, + np.column_stack((x, top_curve)), + end_curve, + np.column_stack((x[::-1], bottom_curve[::-1])) + ]) + + # Add slight padding for better coverage + center = np.mean(contour_points, axis=0) + vectors = contour_points - center + padded_points = center + vectors * 1.15 # 15% padding + + return padded_points + return points + def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, tuple, np.ndarray): mask = np.zeros(frame.shape[:2], dtype=np.uint8) eyebrows_cutout = None @@ -335,33 +394,30 @@ def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, t num_points = 50 x = np.linspace(x_min, x_max, num_points) - # Fit cubic curve through points for more natural arch - coeffs = np.polyfit(sorted_points[:, 0], sorted_points[:, 1], 3) + # Fit quadratic curve through points for more natural arch + coeffs = np.polyfit(sorted_points[:, 0], sorted_points[:, 1], 2) # Changed to quadratic y = np.polyval(coeffs, x) - # Create points for top and bottom curves with varying offsets - top_offset = np.linspace(height * 0.4, height * 0.3, num_points) # Varying offset for more natural shape - bottom_offset = np.linspace(height * 0.2, height * 0.15, num_points) - - # Add some randomness to the offsets for more natural look - top_offset += np.random.normal(0, height * 0.02, num_points) - bottom_offset += np.random.normal(0, height * 0.01, num_points) - - # Smooth the offsets - top_offset = cv2.GaussianBlur(top_offset.reshape(-1, 1), (1, 3), 1).reshape(-1) - bottom_offset = cv2.GaussianBlur(bottom_offset.reshape(-1, 1), (1, 3), 1).reshape(-1) + # Create points for top and bottom curves with consistent offsets + top_offset = height * 0.3 # Simplified offset for cleaner curve + bottom_offset = height * 0.1 # Thinner bottom curve + # Create smooth curves top_curve = y - top_offset bottom_curve = y + bottom_offset - # Create curved endpoints + # Create curved endpoints with slight taper end_points = 5 + start_x = np.linspace(x[0] - width * 0.1, x[0], end_points) + end_x = np.linspace(x[-1], x[-1] + width * 0.1, end_points) + + # Create tapered ends start_curve = np.column_stack(( - np.linspace(x[0] - width * 0.05, x[0], end_points), + start_x, np.linspace(bottom_curve[0], top_curve[0], end_points) )) end_curve = np.column_stack(( - np.linspace(x[-1], x[-1] + width * 0.05, end_points), + end_x, np.linspace(bottom_curve[-1], top_curve[-1], end_points) )) @@ -373,13 +429,10 @@ def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, t np.column_stack((x[::-1], bottom_curve[::-1])) ]) - # Add padding and smooth the shape + # Add slight padding for better coverage center = np.mean(contour_points, axis=0) vectors = contour_points - center - padded_points = center + vectors * 1.2 # 20% padding - - # Convert to integer coordinates and draw - cv2.fillPoly(mask_roi, [padded_points.astype(np.int32)], 255) + padded_points = center + vectors * 1.15 # 15% padding return padded_points return points