Python+OpenCV+MediaPipe面部关键点三角图像叠加位置异常求助
问题解决:人脸三角区图片叠加的位置匹配与透明化处理
核心问题分析
你遇到的两个问题本质是:
- 位置不匹配:目标图片的三角坐标和摄像头人脸关键点坐标没有做尺寸映射+透视变换,MediaPipe输出的是相对画面的归一化坐标,直接使用会错位;另外人脸三角会随面部姿态变化角度,目标图片的三角是固定形状,必须做透视变形匹配人脸姿态。
- 无透明度方形:叠加时没处理Alpha通道或蒙版,直接把整个图片区域贴上去,没有抠出三角的透明背景。
分步解决方案
1. 预处理目标图片:提取三角区蒙版
先从目标图片中抠出三角区域的蒙版,确保叠加时只有三角内容显示:
import cv2 import numpy as np import mediapipe as mp # 加载目标图片和定义目标三角区(替换成你自己的图片路径和实际提取的坐标) target_img = cv2.imread("your_target_image.png") # 示例:目标图片上的三个三角顶点像素坐标 target_tri = np.array([[100, 150], [300, 150], [200, 350]], dtype=np.float32) # 创建目标三角的蒙版 mask = np.zeros(target_img.shape[:2], dtype=np.uint8) cv2.fillConvexPoly(mask, target_tri.astype(np.int32), 255) # 提取带蒙版的目标三角ROI target_roi = cv2.bitwise_and(target_img, target_img, mask=mask)
2. 摄像头人脸关键点坐标转换
MediaPipe输出的face_landmarks.landmark是相对画面的归一化坐标(x/y范围0-1),需要转换成摄像头画面的像素坐标:
mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False, max_num_faces=1, min_detection_confidence=0.5) cap = cv2.VideoCapture(0) while cap.isOpened(): success, frame = cap.read() if not success: continue # 镜像翻转画面(可选,符合日常视觉习惯) frame = cv2.flip(frame, 1) h, w, _ = frame.shape rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_mesh.process(rgb_frame) if results.multi_face_landmarks: for face_landmarks in results.multi_face_landmarks: # 替换成你要匹配的人脸三角关键点索引(参考MediaPipe关键点图) # 示例:左眼周围的三个关键点索引 landmark_indices = [159, 145, 133] face_tri = [] for idx in landmark_indices: x = int(face_landmarks.landmark[idx].x * w) y = int(face_landmarks.landmark[idx].y * h) face_tri.append([x, y]) face_tri = np.array(face_tri, dtype=np.float32)
3. 透视变换匹配人脸三角姿态
计算目标三角到人脸三角的透视变换矩阵,把目标ROI变形到人脸三角的形状:
# 计算仿射变换矩阵(针对三角区域用仿射足够) M = cv2.getAffineTransform(target_tri, face_tri) # 对目标ROI和蒙版做透视变换 warped_target = cv2.warpAffine(target_roi, M, (w, h)) warped_mask = cv2.warpAffine(mask, M, (w, h))
4. Alpha混合叠加到摄像头画面
用蒙版做Alpha混合,避免出现方形背景:
# 将单通道蒙版转为3通道,方便和彩色画面混合 warped_mask_3ch = cv2.cvtColor(warped_mask, cv2.COLOR_GRAY2BGR) / 255.0 # 混合计算:目标区域*蒙版权重 + 原画面*(1-蒙版权重) frame = (frame * (1 - warped_mask_3ch) + warped_target * warped_mask_3ch).astype(np.uint8)
完整代码整合
import cv2 import numpy as np import mediapipe as mp # 初始化MediaPipe人脸网格 mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False, max_num_faces=1, min_detection_confidence=0.5) # 加载目标图片并定义目标三角区(替换成你的实际内容) target_img = cv2.imread("target_tri_image.png") target_tri = np.array([[50, 50], [250, 50], [150, 200]], dtype=np.float32) # 创建目标三角蒙版 mask = np.zeros(target_img.shape[:2], dtype=np.uint8) cv2.fillConvexPoly(mask, target_tri.astype(np.int32), 255) target_roi = cv2.bitwise_and(target_img, target_img, mask=mask) # 开启摄像头 cap = cv2.VideoCapture(0) while cap.isOpened(): success, frame = cap.read() if not success: print("摄像头读取失败") continue frame = cv2.flip(frame, 1) h, w, _ = frame.shape rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_mesh.process(rgb_frame) if results.multi_face_landmarks: for face_landmarks in results.multi_face_landmarks: # 替换成你需要的人脸三角关键点索引 landmark_indices = [159, 145, 133] face_tri = [] for idx in landmark_indices: x = int(face_landmarks.landmark[idx].x * w) y = int(face_landmarks.landmark[idx].y * h) face_tri.append([x, y]) face_tri = np.array(face_tri, dtype=np.float32) # 透视变换与叠加 M = cv2.getAffineTransform(target_tri, face_tri) warped_target = cv2.warpAffine(target_roi, M, (w, h)) warped_mask = cv2.warpAffine(mask, M, (w, h)) warped_mask_3ch = cv2.cvtColor(warped_mask, cv2.COLOR_GRAY2BGR) / 255.0 frame = (frame * (1 - warped_mask_3ch) + warped_target * warped_mask_3ch).astype(np.uint8) cv2.imshow("Face Triangle Overlay", frame) if cv2.waitKey(5) & 0xFF == 27: break cap.release() cv2.destroyAllWindows()
关键注意事项
- 关键点索引对应:一定要根据你的需求,从MediaPipe人脸关键点图中选对三个匹配的顶点,选不对必然位置错位。
- 目标三角坐标:确保你提取的目标图片三角坐标是正确的像素坐标,而非归一化坐标。
- Alpha通道优化:如果你的目标图片本身是带透明背景的PNG,可以直接用
target_img[:, :, 3]作为蒙版,无需手动创建。
内容的提问来源于stack exchange,提问作者LUser
相关产品推荐
相关产品推荐

