资讯专栏INFORMATION COLUMN

MediaPipe基础(2)人脸网格

darkbug / 1844人阅读

摘要:机器学习流水线我们的管道由两个协同工作的实时深度神经网络模型组成一个对完整图像进行操作并计算人脸位置的检测器,以及一个对这些位置进行操作并通过回归预测近似表面几何形状的人脸地标模型。

1.摘要

MediaPipe Face Mesh 是一种面部几何解决方案,即使在移动设备上也能实时估计 468 个 3D 面部标志。它采用机器学习 (ML) 来推断 3D 表面几何形状,只需要一个摄像头输入,无需专用深度传感器。该解决方案在整个管道中利用轻量级模型架构和 GPU 加速,提供对实时体验至关重要的实时性能。

此外,该解决方案与人脸几何模块捆绑在一起,弥合了人脸地标估计和有用的实时增强现实 (AR) 应用程序之间的差距。它建立了一个可度量的3D空间,并使用面部地标屏幕位置来估计该空间内的面部几何形状。人脸几何数据由常见的三维几何基元组成,包括人脸姿态变换矩阵和三角化人脸网格。在幕后,使用一种被称为普鲁克分析的轻量级的统计分析方法,用来驱动一个健壮的、性能好的、可移植的逻辑。该分析在CPU上运行,并且在ML模型推理的基础上具有最小的速度/内存占用。

2.机器学习流水线

我们的 ML 管道由两个协同工作的实时深度神经网络模型组成:一个对完整图像进行操作并计算人脸位置的检测器,以及一个对这些位置进行操作并通过回归预测近似表面几何形状的 3D 人脸地标模型。准确裁剪人脸大大减少了对常见数据增强的需求,例如由旋转、平移和缩放组成的仿射变换。相反,它允许网络将其大部分资源用于坐标预测精度。此外,在我们的管道中,还可以根据前一帧中识别出的人脸地标生成裁剪图,并且只有当地标模型无法再识别人脸时,才会调用人脸检测器来重新定位人脸。此策略类似于我们的 MediaPipe Hands 解决方案中采用的策略,后者使用手掌检测器和手部标志模型。

3.模型

  • 人脸检测模型
    该人脸检测器与MediaPipe人脸检测中使用的BlazeFace模型相同。
  • 人脸地标模型
    对于 3D 人脸地标,我们采用迁移学习训练了一个具有多个目标的网络:网络同时预测合成渲染数据上的 3D 地标坐标和带注释的真实世界数据上的 2D 语义轮廓。由此产生的网络为我们提供了合理的 3D 地标预测,不仅针对合成数据,还针对真实世界数据。

3D地标网络接收剪裁好的视频帧作为输入,不需要额外的深度输入。该模型输出3D点的位置,以及人脸在输入中存在和合理对齐的概率。一种常见的替代方法是预测每个地标的2D热图,但它不适合深度预测,而且对这么多点有很高的计算成本。我们通过迭代引导和改进预测进一步提高模型的准确性和鲁棒性。这样我们就可以将数据集扩展到越来越具有挑战性的情况,例如鬼脸、斜角和遮挡。

4.人脸几何模块

Face Landmark Model在屏幕坐标空间中进行单摄像头人脸地标检测:X、Y坐标为归一化屏幕坐标,而Z坐标为相对坐标,在弱透视投影摄像机模型下缩放为X坐标。这种格式非常适合一些应用程序,但它不能直接实现增强现实(AR)的全部功能,如将虚拟3D对象与检测到的人脸对齐。

人脸几何模块从屏幕坐标空间移向可度量的3D空间,并提供必要的基元来将检测到的人脸作为常规3D对象处理。通过设计,您将能够使用透视相机将最终的3D场景投影回屏幕坐标空间,并保证面部地标位置没有改变。
关键概念

4.1METRIC 3D SPACE

Face Geometry 模块中建立的 Metric 3D 空间是右手正交的 metric 3D 坐标空间。在空间内,有一个位于空间原点并指向Z轴负方向的虚拟透视相机。在当前的流程中,假设输入的相机帧正是由这个虚拟相机观察到的,因此它的参数稍后用于将屏幕地标坐标转换回Metric 3D空间。虚拟相机参数可以自由设置,但为了获得更好的效果,建议尽可能接近真实的物理相机参数。

4.2规范化的人脸模型

规范化的人脸模型(Canonical Face Model )是人脸的静态 3D 模型,它遵循 Face Landmark Model 的 468 个 3D 人脸地标拓扑。该模型具有两个重要功能:

  • 定义了公制单位:规范化的人脸模型的比例定义了Metric 3D 空间的公制单位。默认规范化的人脸模型使用的公制单位是厘米;
  • 静态空间和运行时空间之间的桥梁:人脸姿态变换矩阵实际上是从规范化的人脸模型到在每一帧上估计的运行时人脸地标集的线性映射。通过这种方式,围绕规范化的人脸模型建模的虚拟 3D 资产可以通过对它们应用人脸姿势变换矩阵来与跟踪的人脸对齐。

5.组件

5.1几何管道

几何管道是一个关键组件,它负责在Metric 3D空间内估计人脸几何对象。在每一帧中,按照给定的顺序执行以下步骤:

  • 人脸地标屏幕坐标转换为三维空间坐标;
  • 人脸姿态变换矩阵被估计为从规范化的人脸landmark 集到运行时人脸landmark 集的刚性线性映射,以最小化两者之间的差异;
  • 使用运行时的人脸landmark作为顶点位置(XYZ)创建了一个人脸网格,而顶点纹理坐标(UV)和三角形拓扑都继承自规范化的人脸模型。

几何管道被实现为MediaPipe计算器。为了方便起见,人脸几何管道计算器与相应的元数据捆绑在一起,形成统一的MediaPipe子图。人脸几何格式被定义为协议缓冲区消息。

5.2效果渲染器

效果渲染器是一个组件,它作为一个面部效果渲染器的工作示例。它以OpenGL ES 2.0 API为目标,在移动设备上启用实时性能,并支持以下渲染模式:

  • 3D物体渲染模式:虚拟对象与检测到的人脸对齐,以模拟附着在人脸上的对象(例如:眼镜);
  • 人脸网格渲染模式:在面部网格表面上拉伸纹理以模拟面部绘画技术。

在这两种渲染模式中,面部网格首先被渲染为直接进入深度缓冲区的遮挡物。此步骤有助于通过隐藏面部表面后面的不可见元素来创建更可信的效果。

6.解决方案的API

6.1参数配置

  • STATIC_IMAGE_MODE:如果设置为 false,该解决方案会将输入图像视为视频流。它将尝试在第一张输入图像中检测人脸,并在成功检测后进一步定位人脸地标。在随后的图像中,一旦检测到所有 max_num_faces 人脸并定位了相应的人脸地标,它就会简单地跟踪这些地标,而不会调用另一个检测,直到它失去对任何人脸的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 true,人脸检测会在每个输入图像上运行,非常适合处理一批静态的、可能不相关的图像。默认为false。
  • MAX_NUM_FACES:要检测的最大人脸数。默认为 1。
  • MIN_DETECTION_CONFIDENCE:来自人脸检测模型的最小置信值 ([0.0, 1.0]),以便将检测视为成功。默认为 0.5。
  • MIN_TRACKING_CONFIDENCE:来自地标跟踪模型的最小置信值 ([0.0, 1.0]),用于将被视为成功跟踪的人脸地标,否则将在下一个输入图像上自动调用人脸检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 static_image_mode 为真,则忽略这个参数,人脸检测将在每个图像上运行。默认为 0.5。

6.2输出

  • MULTI_FACE_LANDMARKS:检测/跟踪人脸的集合,其中每个人脸表示为 468 个人脸地标的列表,每个地标由 x、y 和 z 组成。 x 和 y 分别通过图像宽度和高度归一化为 [0.0, 1.0]。 z 表示地标深度,以头部中心的深度为原点,值越小,地标离相机越近。 z 的大小使用与 x 大致相同的比例。

7.Python API解决方案

支持配置选项:

  • static_image_mode
  • max_num_faces
  • min_detection_confidence
  • min_tracking_confidence
    (1)基础版本
# 图像import cv2import mediapipe as mpmp_drawing = mp.solutions.drawing_utilsmp_drawing_styles = mp.solutions.drawing_utils.DrawingSpecmp_face_mesh = mp.solutions.face_mesh# 静态图片:IMAGE_FILES = ["trump.jpg"]drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)with mp_face_mesh.FaceMesh(    static_image_mode=True,    max_num_faces=1,    min_detection_confidence=0.5) as face_mesh:  for idx, file in enumerate(IMAGE_FILES):    image = cv2.imread(file)    # Convert the BGR image to RGB before processing.    results = face_mesh.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))    # Print and draw face mesh landmarks on the image.    if not results.multi_face_landmarks:      continue    annotated_image = image.copy()    for face_landmarks in results.multi_face_landmarks:      print("face_landmarks:", face_landmarks)      mp_drawing.draw_landmarks(          image=annotated_image,          landmark_list=face_landmarks,          connections=mp_face_mesh.FACE_CONNECTIONS,)      mp_drawing.draw_landmarks(          image=annotated_image,          landmark_list=face_landmarks,          connections=mp_face_mesh.FACE_CONNECTIONS,)    cv2.imwrite("annotated_image" + str(idx) + ".png", annotated_image)
# 视频#!/usr/bin/python3# -*- encoding: utf-8 -*-import cv2import mediapipe as mpimport timecap = cv2.VideoCapture("1.mp4")pTime = 0mpDraw = mp.solutions.drawing_utilsmpFaceMesh = mp.solutions.face_meshfaceMesh = mpFaceMesh.FaceMesh(max_num_faces=2)drawSpec = mpDraw.DrawingSpec(thickness=1, circle_radius=2)while True:    success, img = cap.read()    if not success:        break    imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)    results = faceMesh.process(imgRGB)    if results.multi_face_landmarks:        for faceLms in results.multi_face_landmarks:            mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACE_CONNECTIONS, drawSpec, drawSpec)            # mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACE_CONNECTIONS)            for id, lm in enumerate(faceLms.landmark):                print(lm)                ih, iw, ic = img.shape                x, y = int(lm.x * iw), int(lm.y * ih)                print(id, x, y)    cTime = time.time()    fps = 1 / (cTime - pTime)    pTime = cTime    cv2.putText(img, f"FPS: {int(fps)}", (20, 70), cv2.FONT_HERSHEY_PLAIN,                3, (255, 0, 0), 3)    cv2.imshow("Image", img)    cv2.waitKey(1)

(2)加强版

#!/usr/bin/python3# -*- encoding: utf-8 -*-"""@File    : FaceMeshModule.py.py@Time    : 2021/9/28 10:33@Author  : David@Software: PyCharm"""import cv2import mediapipe as mpimport timeclass FaceMeshDetector():    def __init__(self, staticMode=False, maxFaces=2, minDetectionCon=0.5, minTrackCon=0.5):        self.staticMode = staticMode        self.maxFaces = maxFaces        self.minDetectionCon = minDetectionCon        self.minTrackCon = minTrackCon        self.mpDraw = mp.solutions.drawing_utils        self.mpFaceMesh = mp.solutions.face_mesh        self.faceMesh = self.mpFaceMesh.FaceMesh(self.staticMode, self.maxFaces,                                                 self.minDetectionCon, self.minTrackCon)        self.drawSpec = self.mpDraw.DrawingSpec(thickness=1, circle_radius=2)    def findFaceMesh(self, img, draw=True):        self.imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)        self.results = self.faceMesh.process(self.imgRGB)        faces = []        if self.results.multi_face_landmarks:            for faceLms in self.results.multi_face_landmarks:                if draw:                    self.mpDraw.draw_landmarks(img, faceLms, self.mpFaceMesh.FACEMESH_CONTOURS,                                               self.drawSpec, self.drawSpec)                face = []                for id, lm in enumerate(faceLms.landmark):                    # print(lm)                    ih, iw, ic = img.shape                    x, y = int(lm.x * iw), int(lm.y * ih)                    # cv2.putText(img, str(id), (x, y), cv2.FONT_HERSHEY_PLAIN,                    #           0.7, (0, 255, 0), 1)                    # print(id,x,y)                    face.append([x, y])                faces.append(face)        return img, facesdef main():    cap = cv2.VideoCapture("1.mp4")    pTime = 0    detector = FaceMeshDetector(maxFaces=2)    while True:        success, img = cap.read()        if not success:            break        img, faces = detector.findFaceMesh(img)        if len(faces) != 0:            print(faces[0])        cTime = time.time()        fps = 1 / (cTime - pTime)        pTime = cTime        cv2.putText(img, f"FPS: {int(fps)}", (20, 70), cv2.FONT_HERSHEY_PLAIN,                    3, (0, 255, 0), 3)        cv2.imshow("Image", img)        cv2.waitKey(1)if __name__ == "__main__":    main()

参考目录

https://google.github.io/mediapipe/solutions/face_mesh.html

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121649.html

相关文章

  • Mediapipe入门——搭建姿态检测模型并实时输出人体关节点3d坐标

    摘要:大有用武之地,可以做物体检测自拍分割头发分割人脸检测手部检测运动追踪,等等。它将尝试在第一张图像中检测最突出的人,并在成功检测后进一步定位姿势和其他地标。默认为来自人员检测模型的最小置信值,用于将检测视为成功。 ...

    不知名网友 评论0 收藏0
  • 看不懂手语怎么办?用Python和Opencv完美复刻出来!这样总看的懂了吧!

    摘要:我们将使用一个模块检测所有面部和手部标志的解决方案。平滑的地标该参数通过对不同输入图像的姿态标志进行滤波,减少预测中的抖动。最小跟踪信心它被用来指定从地标跟踪模型中检测成功的最小置信度值。将映像转换为映像,并使用初始化的整体模型进行预测。   在本文中,我们将使用Python来检测人脸和手部...

    whlong 评论0 收藏0
  • 【机器学习实验四】基于Logistic Regression二分类算法实现手部姿态识别

    摘要:基于的手部关键点检测为了和当前博主正在学习的机器学习算法联系起来,在本篇博客中,我们暂不考虑如何实现手部关键点的回归问题,仅考虑分类问题。因此博主将会基于开源深度学习工具包里的手部姿态检测算法提取手部关键点。 ...

    williamwen1986 评论0 收藏0
  • 基于Opencv的手势识别

    摘要:同时打印出了手上的每个结点的位置变化。解决方案使用国内镜像安装这时候,考虑使用国内镜像安装,会快很多。点击下面的即可。 文章目录 一、 效果(版本2的效果)二、全...

    曹金海 评论0 收藏0
  • YOLO算法的原理与实现

    摘要:近几年来,目标检测算法取得了很大的突破。本文主要讲述算法的原理,特别是算法的训练与预测中详细细节,最后将给出如何使用实现算法。但是结合卷积运算的特点,我们可以使用实现更高效的滑动窗口方法。这其实是算法的思路。下面将详细介绍算法的设计理念。 1、前言当我们谈起计算机视觉时,首先想到的就是图像分类,没错,图像分类是计算机视觉最基本的任务之一,但是在图像分类的基础上,还有更复杂和有意思的任务,如目...

    zhangfaliang 评论0 收藏0

发表评论

0条评论

darkbug

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<