MediaPipe FaceMesh vs FaceLandmarker 迁移实操指南
【免费下载链接】mediapipeCross-platform, customizable ML solutions for live and streaming media.项目地址: https://gitcode.com/GitHub_Trending/med/mediapipe
场景:FaceMesh 老方案在直播链路里卡住的两处
修图组把 MediaPipe FaceMesh 接进直播链路,准备换官方面部新方案 FaceLandmarker:老 solution 只回 468 个坐标,唇形对嘴要的 52 维 blendshapes 得自己从点位估;AR 试戴要的 4×4 姿态矩阵,Python 里得另搭 C++ 图去跑 face_geometry 子图。官方文档已注明 FaceMesh 于 2023-05-10 起由 FaceLandmarker 接替,旧 API 停更。
FaceMesh 与 FaceLandmarker 差异对比表
| 对比项 | FaceMesh(旧 solution) | FaceLandmarker(Task API) |
|---|---|---|
| 调用入口 | mp.solutions.face_mesh.FaceMesh.process(),单入口 | detect()/detect_for_video()/detect_async()三种模式 |
| 点位输出 | 默认 468 点,refine_landmarks=True才出 478 点,且要多跑一次注意力模型推理 | 默认 478 点,虹膜 10 点内置 |
| 表情与姿态 | 无 blendshapes、无姿态矩阵,4×4 矩阵需另接 face_geometry 子图 | output_face_blendshapes出 52 维系数,output_facial_transformation_matrixes出 4×4 矩阵 |
| 模型形态 | 打包在 pip wheel 内的face_landmark_front_cpu.binarypb图,不可替换 | 外部face_landmarker.task,约 4.3MB,可自主替换训练产物 |
| 生命周期 | face_mesh 文档 已转为跳转页,2023-05 后无更新 | 属于仍在维护的 Tasks 体系,Python/Java/C++/C/Web 同一套 API |
值得切换的原因很实际:表情参数和姿态矩阵从"自己估"变成"开关打开就有",虹膜点从"多一次推理"变成"默认免费",而且模型文件可替换,给后续换自训模型留了口子。
切换三步走:准备、改造、验证
第一步:准备——下载模型并放入工作目录
pip install "mediapipe>=0.10.0" # Tasks API 起于 0.10 # 下载 face_landmarker.task(约 4.3MB)放到脚本同目录 # 如需本地源码:git clone https://gitcode.com/GitHub_Trending/med/mediapipe旧方案里模型随 wheel 打包,不用管;新方案必须显式给路径,这是迁移中第一个要改的地方。
第二步:改造——把旧 FaceMesh 代码换成 FaceLandmarker
旧 FaceMesh 写法(来自 mediapipe/python/solutions/face_mesh.py):
import cv2 import mediapipe as mp mp_face_mesh = mp.solutions.face_mesh with mp_face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5) as face_mesh: image = cv2.imread('face.jpg') result = face_mesh.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) nose_x = result.multi_face_landmarks[0][1].x * image.shape[1]新 FaceLandmarker 写法(来自 mediapipe/tasks/python/vision/face_landmarker.py):
import cv2 import mediapipe as mp from mediapipe.tasks import python from mediapipe.tasks.python import vision options = vision.FaceLandmarkerOptions( base_options=python.BaseOptions(model_asset_path='face_landmarker.task'), running_mode=vision.RunningMode.IMAGE, num_faces=1, output_face_blendshapes=True, output_facial_transformation_matrixes=True) with vision.FaceLandmarker.create_from_options(options) as lm: image = cv2.imread('face.jpg') mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=np.ascontiguousarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))) result = lm.detect(mp_image) nose_x = result.face_landmarks[0][1].x * image.shape[1]关键变更点:
- 入口从
mp.solutions.face_mesh.FaceMesh(...)换成vision.FaceLandmarker.create_from_options(options),模型从内置 binarypb 变成外部.task文件。 - 入参从裸 numpy RGB 数组换成
mp.Image对象,data 仍是 uint8 RGB 数组,但必须是连续内存。 - 结果字段
multi_face_landmarks改名为face_landmarks,坐标依旧是 [0,1] 归一化,取点方式不变;refine_landmarks参数消失,478 点是默认行为。 - 新增两个输出开关:52 维 blendshapes 和 4×4 面部变换矩阵,默认都是关的。
- 视频场景:
process()逐帧调用改为 VIDEO 模式 +detect_for_video(mp_image, timestamp_ms),时间戳必须单调递增。
第三步:验证——对点位、验输出
import numpy as np first = result.face_landmarks[0] assert len(first) == 478 # 虹膜点是否默认输出 assert len(result.face_blendshapes[0]) == 52 # 表情参数维度 assert result.facial_transformation_matrixes[0].shape == (4, 4) diff = abs(first[1].x * W - nose_x_old) # 鼻尖像素差 print(f'鼻尖偏差: {diff:.1f}px') # 经验值:同设备 <5px验证通过标准:478 点、52 维、4×4 三个断言全过,且鼻尖、眼角(点 33/133)等锚点与旧方案像素差落在个位数。若face_blendshapes是空列表,说明output_face_blendshapes=True没打开——它默认是False。
踩坑实录:三个迁移时真实会撞上的问题 ⚠️
坑 1:running_mode 和 detect 方法不匹配,创建时就抛 ValueError
- 现象:
create_from_options阶段报 running mode 校验错误,进程直接起不来。 - 原因:三种模式和三种调用是绑死的——IMAGE 配
detect(),VIDEO 配detect_for_video(),LIVE_STREAM 配detect_async()且必须提供result_callback,混用即失败。 - 解法:相机实时流就用 LIVE_STREAM;把摄像头逐帧读进来自己算时间戳的场景,用 VIDEO 更可控。
坑 2:视频重播后时间戳回跳,报"timestamp is smaller than what already processed"
- 现象:视频从头再播一遍,或摄像头重启后,
detect_for_video/detect_async抛 ValueError,提示当前时间戳小于已处理的最大时间戳。 - 原因:官方要求相邻调用的
timestamp_ms严格单调递增,直接用"当前墙上时钟"或"帧号×1000//fps"在 seek、重开后都会回跳。 - 解法:时间戳用会话内单调时钟,例如
int((time.monotonic() - t0) * 1000);会话重开就把整个 FaceLandmarker 实例 close 后重建。
坑 3:直接把 cv2.imread 的结果喂给 mp.Image,检不出脸或点位整体漂移
- 现象:同一张图,旧方案 468 点正常,新方案
face_landmarks为空或整张网错位。 - 原因:两处——
cv2.imread返回 BGR 而mp.Image要 RGB;数组若是切片来的,内存不连续,底层绑定会拒收。 - 解法:
np.ascontiguousarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))一步到位,别省这行。
选型与迁移节奏:选哪个规格、什么时候动
- 模型规格:FaceLandmarker 官方只发一个标准版
face_landmarker.task(约 4.3MB),不存在"按设备挑档位"的问题,装进包即可;包体敏感就把文件放资源目录首次启动时拷贝缓存。想换模型可用 Model Maker 训练后导出替换,478 点拓扑和 52 维输出协议不变。 - 模式选型:静态图/离线批处理用 IMAGE;录播视频转码用 VIDEO;摄像头实时流用 LIVE_STREAM,注意官方说明——为压延迟它可能丢帧,不保证每帧都有输出,渲染层要容忍帧间隔抖动。
- 何时动:新项目直接用 FaceLandmarker,别再引入
mp.solutions.face_mesh;存量 FaceMesh 项目里,只要渲染链路碰表情驱动或 AR 贴合,本迭代就该迁;只画 468 点网格、无性能压力的小工具,可以排到下个版本窗口,因为前 468 点拓扑和连线索引(FACEMESH_LIPS、FACEMESH_TESSELATION 等)与旧方案一致,绘制层代码几乎不用动。 - 一个隐藏差异:旧方案虹膜点 468–477 只在
refine_landmarks=True时存在,新方案恒为 478;若旧代码按 478 写了索引而当时没开 refine,迁移后不会炸,但上线前要用真实数据回放一遍点位。
一句话收束:换到 FaceLandmarker,同样的点位白得 52 维表情和 4×4 姿态矩阵,绘制层几乎零改动。下一篇同系列讲把 FaceLandmarker 装进 Android/iOS 端工程并调 GPU 推理管线。
【免费下载链接】mediapipeCross-platform, customizable ML solutions for live and streaming media.项目地址: https://gitcode.com/GitHub_Trending/med/mediapipe
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考