一、引言

在计算机视觉领域,人体关键点检测(Human Pose Estimation,HPE)一直是研究和应用的热点方向之一。随着深度学习与实时图像处理技术的发展,人体姿势估计已经从传统的 2D 检测走向了 3D 空间建模,并在 运动分析、动作识别、虚拟现实、游戏交互、医疗康复、智能健身 等众多场景中得到了广泛应用。

然而,仅仅检测人体骨架往往无法满足实际需求。人机交互过程中,手部动作面部表情同样具有重要意义:

  • 手势识别 可以实现更自然的人机控制,例如手势控制智能家居、无人机;

  • 人脸关键点检测 则是表情识别、虚拟形象驱动、情感计算的重要基础;

  • 姿势检测 则是全身动态捕捉、运动姿态评估的核心。

因此,一个融合 姿势(Pose)、手部(Hand)、人脸(Face) 三大关键点检测任务的多模态分析系统,不仅能提供更完整的人体建模,还能大幅提升交互体验。

本文将详细解析基于 Google Mediapipe Tasks API 封装的 HumanMultiLandmarker 类。它能够同时调用三个模型:人体骨架检测、手部关键点检测、人脸关键点检测,并在同一张画面上输出三种结果。同时,它还能将结果组织成三屏对比图(原始画面 / 骨架点线图 / 骨架叠加图),帮助开发者更直观地理解和调试模型效果。


二、技术背景

2.1 Mediapipe 简介

Mediapipe 是 Google 开源的一套跨平台、可扩展的机器学习应用框架,专注于 实时多模态感知。其核心优势在于:

  1. 丰富的预训练模型:包含人脸检测、手部跟踪、姿势估计、物体检测等;

  2. 跨平台支持:支持 Android、iOS、Web、Python、C++ 等;

  3. 高效实时性能:能够在 CPU、GPU 上流畅运行;

  4. 可组合性强:多个模块可以像“积木”一样拼装组合。

其中,人体相关的关键模型包括:

  • PoseLandmarker(人体骨架关键点检测);

  • HandLandmarker(手部 21 个关键点检测);

  • FaceLandmarker(人脸 478 个关键点检测)。

2.2 多模态人体建模的意义

单一的人体姿态估计只能提供人体整体运动的基本结构信息。但在交互系统中,手部与面部的信息同样至关重要:

  • 全身:判断运动姿势,适用于健身指导、动作分析;

  • 手势:识别指令,如挥手、点赞、数字手势等;

  • 表情:分析情绪,驱动虚拟人物动画。

三者结合,才能形成完整的多模态建模系统。


三、代码结构概述

下面我们逐步解析 HumanMultiLandmarker 类。

class HumanMultiLandmarker:
    def __init__(...):
        # 初始化三个模型(姿势、手部、人脸)

    def _draw_landmarks(...):
        # 通用绘制函数,支持点和线

    def do(...):
        # 执行一次检测,并返回三屏对比结果

该类由 初始化方法绘制方法检测方法 三个部分组成。


四、初始化部分详解

self.pose_detector = vision.PoseLandmarker.create_from_options(...)
self.hand_detector = vision.HandLandmarker.create_from_options(...)
self.face_detector = vision.FaceLandmarker.create_from_options(...)

__init__ 方法中,分别加载了三个 .task 模型文件:

  • pose_landmarker_heavy.task:人体骨架检测(33 个关键点,支持全身姿态);

  • hand_landmarker.task:手部检测(单手 21 个关键点,支持双手检测);

  • face_landmarker.task:人脸检测(478 个关键点,涵盖面部轮廓、眼睛、嘴巴、鼻子等)。

每个 Landmarker 都通过 BaseOptions 指定模型路径,再由 create_from_options 生成检测器实例。
运行模式选择 RunningMode.IMAGE,表示输入的是单帧图像,而不是视频流或实时流。


五、绘制部分详解

绘制采用 OpenCV 的 cv2.circlecv2.line

def _draw_landmarks(self, frame, landmarks, connections=None, color=(0, 255, 0)):
    h, w, _ = frame.shape
    for lm in landmarks:
        cx, cy = int(lm.x * w), int(lm.y * h)
        cv2.circle(frame, (cx, cy), self.point_size, color, -1)

    if connections:
        for start, end in connections:
            x1, y1 = int(landmarks[start].x * w), int(landmarks[start].y * h)
            x2, y2 = int(landmarks[end].x * w), int(landmarks[end].y * h)
            cv2.line(frame, (x1, y1), (x2, y2), color, self.line_thickness)

关键点绘制

  • 将归一化坐标 (lm.x, lm.y) 转换为图像像素坐标 (cx, cy)

  • 通过 cv2.circle 绘制点,支持自定义颜色和大小。

连接绘制

  • 如果传入了 connections,则按照索引连接关键点;

  • 姿势骨架采用 solutions.pose.POSE_CONNECTIONS

  • 手部骨架采用 solutions.hands.HAND_CONNECTIONS

  • 人脸不绘制连线,避免过于密集。


六、检测与结果拼接

skeleton_only = np.zeros_like(frame) skeleton_overlay = frame.copy()

系统生成三张图像:

  1. 原始图像(未修改);

  2. 骨架图像(黑底,只绘制点和线);

  3. 叠加图像(原始帧上绘制骨架)。

检测过程:

pose_res = self.pose_detector.detect(mp_image) hand_res = self.hand_detector.detect(mp_image) face_res = self.face_detector.detect(mp_image)

随后分别绘制:

  • Pose → 白色线条;

  • Hand → 黄色线条;

  • Face → 红色点。

最后通过 np.concatenate 拼接为三屏对比图:

triple_frame = np.concatenate([frame, skeleton_only, skeleton_overlay], axis=1)

七、运行效果

运行后的视频帧将展示三屏效果:

  • 左屏:原始摄像头画面;

  • 中屏:骨架点与线条(无背景干扰);

  • 右屏:骨架叠加在原始画面上。

import cv2
import numpy as np
import mediapipe as mp
from mediapipe import solutions
from mediapipe.tasks import python
from mediapipe.tasks.python import vision

class HumanMultiLandmarker:
    def __init__(self,
                 pose_model="文件地址/pose_landmarker_heavy.task",
                 hand_model="文件地址/hand_landmarker.task",
                 face_model="文件地址/face_landmarker.task",
                 point_size=5,
                 line_thickness=2):
        """加载姿势、手部、人脸三个模型"""
        base_pose = python.BaseOptions(model_asset_path=pose_model)
        base_hand = python.BaseOptions(model_asset_path=hand_model)
        base_face = python.BaseOptions(model_asset_path=face_model)

        self.pose_detector = vision.PoseLandmarker.create_from_options(
            vision.PoseLandmarkerOptions(
                base_options=base_pose,
                num_poses=1,
                running_mode=vision.RunningMode.IMAGE
            )
        )

        self.hand_detector = vision.HandLandmarker.create_from_options(
            vision.HandLandmarkerOptions(
                base_options=base_hand,
                num_hands=2,
                running_mode=vision.RunningMode.IMAGE
            )
        )

        self.face_detector = vision.FaceLandmarker.create_from_options(
            vision.FaceLandmarkerOptions(
                base_options=base_face,
                running_mode=vision.RunningMode.IMAGE
            )
        )

        # 绘图参数
        self.point_size = point_size
        self.line_thickness = line_thickness
        self.pose_connections = solutions.pose.POSE_CONNECTIONS
        self.hand_connections = solutions.hands.HAND_CONNECTIONS

    def _draw_landmarks(self, frame, landmarks, connections=None, color=(0, 255, 0)):
        """通用绘制函数"""
        h, w, _ = frame.shape
        for lm in landmarks:
            cx, cy = int(lm.x * w), int(lm.y * h)
            cv2.circle(frame, (cx, cy), self.point_size, color, -1)

        if connections:
            for start, end in connections:
                x1, y1 = int(landmarks[start].x * w), int(landmarks[start].y * h)
                x2, y2 = int(landmarks[end].x * w), int(landmarks[end].y * h)
                cv2.line(frame, (x1, y1), (x2, y2), color, self.line_thickness)

    def do(self, frame,device):
        """三模型检测 + 三屏展示"""
        if frame is None:
            return None
        mp_image = mp.Image(
            image_format=mp.ImageFormat.SRGB,
            data=cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        )

        # 检测
        pose_res = self.pose_detector.detect(mp_image)
        hand_res = self.hand_detector.detect(mp_image)
        face_res = self.face_detector.detect(mp_image)
        skeleton_only = np.zeros_like(frame)
        skeleton_overlay = frame.copy()

        # 姿态绘制
        if pose_res.pose_landmarks:
            for pose_landmarks in pose_res.pose_landmarks:
                self._draw_landmarks(skeleton_only, pose_landmarks,
                                     self.pose_connections, (255, 255, 255))
                self._draw_landmarks(skeleton_overlay, pose_landmarks,
                                     self.pose_connections, (255, 255, 255))

        # 手部绘制
        if hand_res.hand_landmarks:
            for hand_landmarks in hand_res.hand_landmarks:
                self._draw_landmarks(skeleton_only, hand_landmarks,

                                     self.hand_connections, (0, 255, 255))
                self._draw_landmarks(skeleton_overlay, hand_landmarks,

                                     self.hand_connections, (0, 255, 255))


        # 人脸绘制(只画点,避免太密集)
        if face_res.face_landmarks:
            for face_landmarks in face_res.face_landmarks:
                self._draw_landmarks(skeleton_only, face_landmarks, None, (0, 0, 255))
                self._draw_landmarks(skeleton_overlay, face_landmarks, None, (0, 0, 255))

        # 拼接三屏
        triple_frame = np.concatenate([frame, skeleton_only, skeleton_overlay], axis=1)
        return triple_frame

这种可视化方式非常适合:

  1. 模型调试:对比原始图像与检测结果;

  2. 效果展示:直观展示算法检测效果;

  3. 数据采集:骨架单独图像可用于后续训练。


八、应用场景

  1. 智能健身

    • 检测用户姿态是否正确;

    • 对比标准动作与实际动作;

    • 实时反馈给用户。

  2. 人机交互

    • 通过手势实现自然交互;

    • 表情捕捉用于虚拟角色驱动。

  3. 虚拟现实与元宇宙

    • 捕捉全身骨骼与面部表情;

    • 驱动虚拟形象进行动作和情感表达。

  4. 医疗康复

    • 姿势检测用于康复训练指导;

    • 手势与表情用于病患康复进度跟踪。

   对 PiscTrace or PiscCode感兴趣?更多精彩内容请移步官网看看~🔗 PiscTrace

Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐