Refine Poisson blending mask generation.
- Updated `face_swapper.py` to prioritize using the face bounding box (bbox) for creating the Poisson blending mask. This provides a tighter mask around the swapped face area, aiming to reduce artifacts near ears/hairline. - Implemented fallback to landmark-based convex hull if bbox is invalid or unavailable. - Added logging for mask generation issues and skipping blending if no valid mask can be created.pull/1380/head
parent
2b61ac140a
commit
b260aa27ca
|
@ -138,26 +138,39 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
|
|||
|
||||
face_mask_for_blending = np.zeros(temp_frame.shape[:2], dtype=np.uint8)
|
||||
|
||||
# Attempt to get landmarks; use bbox if landmarks are not available or suitable
|
||||
landmarks = target_face.landmark_2d_106 if hasattr(target_face, 'landmark_2d_106') else None
|
||||
|
||||
if landmarks is not None and len(landmarks) > 0:
|
||||
try:
|
||||
# Use a convex hull of the landmarks as the mask
|
||||
hull_points = cv2.convexHull(landmarks.astype(np.int32))
|
||||
cv2.fillConvexPoly(face_mask_for_blending, hull_points, 255)
|
||||
except Exception as e:
|
||||
logging.warning(f"Could not form convex hull for Poisson mask: {e}. Falling back to bbox.")
|
||||
# Fallback to bbox if convex hull fails
|
||||
x1, y1, x2, y2 = target_face.bbox.astype(int)
|
||||
x1, y1 = max(0, x1), max(0, y1)
|
||||
x2, y2 = min(temp_frame.shape[1], x2), min(temp_frame.shape[0], y2)
|
||||
face_mask_for_blending[y1:y2, x1:x2] = 255
|
||||
elif hasattr(target_face, 'bbox'): # Fallback to bbox if no landmarks
|
||||
# Prioritize using the bounding box for a tighter mask
|
||||
if hasattr(target_face, 'bbox'):
|
||||
x1, y1, x2, y2 = target_face.bbox.astype(int)
|
||||
x1, y1 = max(0, x1), max(0, y1)
|
||||
x2, y2 = min(temp_frame.shape[1], x2), min(temp_frame.shape[0], y2)
|
||||
face_mask_for_blending[y1:y2, x1:x2] = 255
|
||||
# Ensure coordinates are within frame bounds
|
||||
x1_b, y1_b = max(0, x1), max(0, y1) # Use different var names to avoid conflict with center calculation
|
||||
x2_b, y2_b = min(temp_frame.shape[1], x2), min(temp_frame.shape[0], y2)
|
||||
|
||||
# Create a rectangular mask based on the bounding box
|
||||
if x1_b < x2_b and y1_b < y2_b:
|
||||
face_mask_for_blending[y1_b:y2_b, x1_b:x2_b] = 255
|
||||
else:
|
||||
logging.warning("Invalid bounding box for Poisson mask. Attempting landmark-based mask.")
|
||||
# Fallback to landmark-based convex hull if bbox is invalid
|
||||
landmarks = target_face.landmark_2d_106 if hasattr(target_face, 'landmark_2d_106') else None
|
||||
if landmarks is not None and len(landmarks) > 0:
|
||||
try:
|
||||
hull_points = cv2.convexHull(landmarks.astype(np.int32))
|
||||
cv2.fillConvexPoly(face_mask_for_blending, hull_points, 255)
|
||||
except Exception as e:
|
||||
logging.error(f"Could not form convex hull for Poisson mask from landmarks: {e}. Blending will be skipped.")
|
||||
else:
|
||||
logging.error("No valid bbox or landmarks for Poisson mask. Blending will be skipped.")
|
||||
else:
|
||||
# Fallback to landmark-based convex hull if no bbox attribute
|
||||
landmarks = target_face.landmark_2d_106 if hasattr(target_face, 'landmark_2d_106') else None
|
||||
if landmarks is not None and len(landmarks) > 0:
|
||||
try:
|
||||
hull_points = cv2.convexHull(landmarks.astype(np.int32))
|
||||
cv2.fillConvexPoly(face_mask_for_blending, hull_points, 255)
|
||||
except Exception as e:
|
||||
logging.error(f"Could not form convex hull for Poisson mask from landmarks (no bbox): {e}. Blending will be skipped.")
|
||||
else:
|
||||
logging.error("No bbox or landmarks available for Poisson mask. Blending will be skipped.")
|
||||
|
||||
# Feather the mask to smooth edges for Poisson blending
|
||||
feather_amount = modules.globals.poisson_blending_feather_amount
|
||||
|
|
Loading…
Reference in New Issue