适用于 iOS 的姿势特征点检测指南

姿势特征点器任务可让您检测图片中人体的特征点,或 视频。您可以使用此任务来识别身体的关键位置、分析姿势、 以及对动作进行分类。这项任务使用的机器学习 (ML) 模型 处理单张图片或视频该任务会输出图片中的身体姿势特征点 坐标和三维世界坐标。

以下说明介绍了如何在 iOS 应用中使用姿势特征点标记器。代码 您可在 GitHub

您可以查看此网页 演示。 如需详细了解功能、模型和配置选项 请参阅 概览

代码示例

MediaPipe Tasks 示例代码是姿势特征点的基本实现 。该示例使用 iOS 实体设备上的相机来检测 连续视频串流中的姿势。该应用还可以检测姿势 从设备图库中选择图片和视频

您可以以此为基础来创建自己的 iOS 应用,也可以作为参考 对现有应用进行了修改。姿势特征点器示例代码托管在 GitHub

下载代码

以下说明介绍了如何创建示例的本地副本 使用 git 命令行工具运行 git 代码库。

<ph type="x-smartling-placeholder">

如需下载示例代码,请执行以下操作:

  1. 使用以下命令克隆 git 代码库:

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. (可选)配置您的 git 实例以使用稀疏检出,这样您 仅提供姿势特征符示例应用的文件:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/pose_landmarker/ios/
    

创建示例代码的本地版本后,您可以安装 MediaPipe 任务库,使用 Xcode 打开项目并运行应用。对于 请参阅 iOS 设置指南

关键组件

以下文件包含姿势特征点示例的关键代码 应用:

设置

本部分介绍了设置开发环境和 代码项目使用姿势地标器。有关如何设置 用于使用 MediaPipe 任务(包括平台版本)的开发环境 要求,请参阅 iOS 设置指南

<ph type="x-smartling-placeholder">

依赖项

姿势特征点器使用 MediaPipeTasksVision 库,必须安装该库 使用 CocoaPods 构建容器。该库与 Swift 和 Objective-C 应用兼容 并且不需要任何额外的语言相关设置。

如需了解如何在 macOS 上安装 CocoaPods,请参阅 CocoaPods 安装指南。 如需了解如何创建包含必要 Pod 的 Podfile,请参阅 请参阅使用 CocoaPods

使用以下代码在 Podfile 中添加 MediaPipeTasksVision pod:

target 'MyPoseLandmarkerApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

如果您的应用包含单元测试目标,请参阅应用的设置指南 iOS 设备,详细了解如何设置 您的Podfile

型号

MediaPipe 姿势地标器任务需要一个经过训练的软件包, 此任务。想要详细了解 姿势特征点,请参阅任务概览模型 部分

使用 download_models.sh 脚本 下载模型并使用 Xcode 将其添加到您的项目目录。对于 有关如何向 Xcode 项目添加文件的说明,请参阅管理文件 和文件夹 项目

使用 BaseOptions.modelAssetPath 属性指定模型的路径 。如需查看代码示例,请参阅下一部分。

创建任务

您可以通过调用姿势地标器任务之一来创建该任务。通过 PoseLandmarker(options:) 初始化程序接受配置的值 选项。

如果您不需要使用自定义配置初始化的姿势地标器 可以使用 PoseLandmarker(modelPath:) 初始化程序创建 使用默认选项姿势地标器。如需详细了解配置 选项,请参阅配置概览

姿势地标器任务支持 3 种输入数据类型:静态图片、视频文件 和直播视频流默认情况下,PoseLandmarker(modelPath:) 会初始化 执行任务。如果您希望将任务初始化以处理视频 文件或直播视频串流,请使用 PoseLandmarker(options:) 来指定视频 或实时流式传输模式直播模式还要求 poseLandmarkerLiveStreamDelegate 配置选项,它启用 姿势特征点,以将姿势特征点检测结果发送给代理 异步执行。

选择与运行模式对应的标签页,了解如何创建任务 并进行推理。

Swift

映像

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "pose_landmarker",
                                      ofType: "task")

let options = PoseLandmarkerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image
options.minPoseDetectionConfidence = minPoseDetectionConfidence
options.minPosePresenceConfidence = minPosePresenceConfidence
options.minTrackingConfidence = minTrackingConfidence
options.numPoses = numPoses

let poseLandmarker = try PoseLandmarker(options: options)
    

视频

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "pose_landmarker",
                                      ofType: "task")

let options = PoseLandmarkerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.minPoseDetectionConfidence = minPoseDetectionConfidence
options.minPosePresenceConfidence = minPosePresenceConfidence
options.minTrackingConfidence = minTrackingConfidence
options.numPoses = numPoses

let poseLandmarker = try PoseLandmarker(options: options)
    

直播

import MediaPipeTasksVision

// Class that conforms to the `PoseLandmarkerLiveStreamDelegate` protocol and
// implements the method that the pose landmarker calls once it finishes
// performing pose landmark detection in each input frame.
class PoseLandmarkerResultProcessor: NSObject, PoseLandmarkerLiveStreamDelegate {

  func poseLandmarker(
    _ poseLandmarker: PoseLandmarker,
    didFinishDetection result: PoseLandmarkerResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the pose landmarker result or errors here.

  }
}

let modelPath = Bundle.main.path(forResource: "pose_landmarker",
                                      ofType: "task")

let options = PoseLandmarkerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.minPoseDetectionConfidence = minPoseDetectionConfidence
options.minPosePresenceConfidence = minPosePresenceConfidence
options.minTrackingConfidence = minTrackingConfidence
options.numPoses = numPoses

// Assign an object of the class to the `poseLandmarkerLiveStreamDelegate`
// property.
let processor = PoseLandmarkerResultProcessor()
options.poseLandmarkerLiveStreamDelegate = processor

let poseLandmarker = try PoseLandmarker(options: options)
    

Objective-C

映像

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"pose_landmarker"
                                                      ofType:@"task"];

MPPPoseLandmarkerOptions *options = [[MPPPoseLandmarkerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.minPoseDetectionConfidence = minPoseDetectionConfidence;
options.minPosePresenceConfidence = minPosePresenceConfidence;
options.minTrackingConfidence = minTrackingConfidence;
options.numPoses = numPoses;

MPPPoseLandmarker *poseLandmarker =
  [[MPPPoseLandmarker alloc] initWithOptions:options error:nil];
    

视频

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"pose_landmarker"
                                                      ofType:@"task"];

MPPPoseLandmarkerOptions *options = [[MPPPoseLandmarkerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.minPoseDetectionConfidence = minPoseDetectionConfidence;
options.minPosePresenceConfidence = minPosePresenceConfidence;
options.minTrackingConfidence = minTrackingConfidence;
options.numPoses = numPoses;

MPPPoseLandmarker *poseLandmarker =
  [[MPPPoseLandmarker alloc] initWithOptions:options error:nil];
    

直播

@import MediaPipeTasksVision;

// Class that conforms to the `MPPPoseLandmarkerLiveStreamDelegate` protocol
// and implements the method that the pose landmarker calls once it finishes
// performing pose landmarks= detection in each input frame.

@interface APPPoseLandmarkerResultProcessor : NSObject 

@end

@implementation APPPoseLandmarkerResultProcessor

-   (void)poseLandmarker:(MPPPoseLandmarker *)poseLandmarker
    didFinishDetectionWithResult:(MPPPoseLandmarkerResult *)poseLandmarkerResult
         timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                           error:(NSError *)error {

    // Process the pose landmarker result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"pose_landmarker"
                                                      ofType:@"task"];

MPPPoseLandmarkerOptions *options = [[MPPPoseLandmarkerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.minPoseDetectionConfidence = minPoseDetectionConfidence;
options.minPosePresenceConfidence = minPosePresenceConfidence;
options.minTrackingConfidence = minTrackingConfidence;
options.numPoses = numPoses;

// Assign an object of the class to the `poseLandmarkerLiveStreamDelegate`
// property.
APPPoseLandmarkerResultProcessor *processor =
  [APPPoseLandmarkerResultProcessor new];
options.poseLandmarkerLiveStreamDelegate = processor;

MPPPoseLandmarker *poseLandmarker =
  [[MPPPoseLandmarker alloc] initWithOptions:options error:nil];
    

注意:如果您使用视频模式或直播模式,姿势地标器会使用 以免每一帧都触发手掌检测模型,这有助于 缩短延迟时间

配置选项

此任务具有以下适用于 iOS 应用的配置选项:

选项名称 说明 值范围 默认值
running_mode 设置任务的运行模式。有三个 模式:

IMAGE:单图输入的模式。

VIDEO:视频已解码帧的模式。

LIVE_STREAM:输入流媒体直播模式 例如来自相机的数据。 在此模式下,poseLandmarkerLiveStreamDelegate 必须设置为 实现 PoseLandmarkerLiveStreamDelegate 以接收 以异步方式执行姿势特征点检测。
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
num_poses 通过 姿势特征点。 Integer > 0 1
min_pose_detection_confidence 姿势检测要计算的最低置信度分数 则视为成功 Float [0.0,1.0] 0.5
min_pose_presence_confidence 姿势存在的最低置信度分数 得分。 Float [0.0,1.0] 0.5
min_tracking_confidence 姿势跟踪的最低置信度分数 才会被视为成功 Float [0.0,1.0] 0.5
output_segmentation_masks 姿势特征点是否针对检测到的对象输出分割掩码 姿势。 Boolean False
result_callback 设置结果监听器以接收地标器结果 姿势地标器处于直播模式时异步执行。 仅在跑步模式设为“LIVE_STREAM”时才能使用 ResultListener N/A

直播配置

当跑步模式设置为直播时,姿势地标器需要 额外的 poseLandmarkerLiveStreamDelegate 配置选项 使姿势特征点器能够提供姿势特征点检测结果 异步执行。代理必须实现 poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) 方法、 姿势定位器在处理完姿势结果后调用的新图片 特征点检测。

选项名称 说明 值范围 默认值
poseLandmarkerLiveStreamDelegate 启用姿势特征点器来接收姿势结果 在实时模式下异步进行地标检测。提供 属性,则必须实现 poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) 方法。 不适用 未设置

准备数据

您需要先将输入图片或帧转换为 MPImage 对象,然后才能 传递给姿势特征点。MPImage 支持不同类型的 iOS 图片 并且可在任何运行模式下使用它们进行推理。有关 有关 MPImage 的信息,请参阅 MPImage API

根据您的使用情形和应用的运行模式选择 iOS 图片格式 MPImage 接受 UIImageCVPixelBufferCMSampleBuffer iOS 图片格式。

UIImage

UIImage 格式非常适合以下运行模式:

  • 图片:来自 app bundle、用户图库或文件系统的图片,格式为 UIImage 图片可转换为 MPImage 对象。

  • 视频:使用 AVAssetImageGenerator 将视频帧提取到 CGImage 格式,然后将其转换为 UIImage 图片。

Swift

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(uiImage: image)
    

Objective-C

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

本示例使用默认值初始化 MPImageUIImage.Orientation.Up 屏幕方向。您可以使用任何受支持的MPImage UIImage.Orientation 值。姿势地标器不支持镜像方向,例如.upMirrored.downMirrored.leftMirrored.rightMirrored

有关 UIImage 的详细信息,请参阅 UIImage Apple Developer 文档

CVPixelBuffer

CVPixelBuffer 格式非常适合生成帧的应用 并使用 iOS CoreImage 处理框架

CVPixelBuffer 格式非常适合以下运行模式:

  • 图片:应用会在经过一些处理后生成 CVPixelBuffer 图片 使用 iOS 的 CoreImage 框架发送到 Android Pay 中的姿势特征点 映像运行模式

  • 视频:可将视频帧转换为 CVPixelBuffer 格式,以便 然后发送到视频模式下的姿势特征点标记器。

  • 直播:使用 iOS 相机生成帧的应用可能会被转换 转换为 CVPixelBuffer 格式进行处理,然后再发送到 在直播模式下摆姿势地标。

Swift

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(pixelBuffer: pixelBuffer)
    

Objective-C

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

如需详细了解 CVPixelBuffer,请参阅 CVPixelBuffer Apple 开发者 文档

CMSampleBuffer

CMSampleBuffer 格式用于存储统一媒体类型的媒体样本, 非常适合直播运行模式。来自 iOS 相机的实时帧 由 iOS 以 CMSampleBuffer 格式异步传送 AVCaptureVideoDataOutput

Swift

// Obtain a CMSampleBuffer.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(sampleBuffer: sampleBuffer)
    

Objective-C

// Obtain a `CMSampleBuffer`.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
    

如需详细了解 CMSampleBuffer,请参阅 CMSampleBuffer Apple 开发者 文档

运行任务

如需运行姿势特征点,请使用特定于所分配的姿势特征点的 detect() 方法 跑步模式:

  • 静态图片:detect(image:)
  • 视频:detect(videoFrame:timestampInMilliseconds:)
  • 直播:detectAsync(image:timestampInMilliseconds:)

以下代码示例显示了如何在 不同的跑步模式:

Swift

映像

let result = try poseLandmarker.detect(image: image)
    

视频

let result = try poseLandmarker.detect(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

直播

try poseLandmarker.detectAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

映像

MPPPoseLandmarkerResult *result =
  [poseLandmarker detectImage:image error:nil];
    

视频

MPPPoseLandmarkerResult *result =
  [poseLandmarker detectVideoFrame:image
           timestampInMilliseconds:timestamp
                             error:nil];
    

直播

BOOL success =
  [poseLandmarker detectAsyncImage:image
           timestampInMilliseconds:timestamp
                             error:nil];
    

姿势地标器代码示例展示了以上每种模式的实现 detect(image:)detect(videoFrame:timestampInMilliseconds:)、 和 detectAsync(image:timestampInMilliseconds:)。示例代码允许 让用户可以在您可能并不需要的处理模式之间进行切换 这种情况。

请注意以下几点:

  • 在视频模式或直播模式下运行时,您还必须提供 姿势地标器任务的输入帧的时间戳。

  • 在图片或视频模式下运行时,姿势地标器任务会阻止 当前线程,直到处理完输入图像或帧为止。接收者 避免阻塞当前线程,在后台执行处理 使用 iOS 的会话串 DispatchNSOperation 框架。

  • 在直播模式下运行时,姿势地标器任务会立即返回 并且不会阻塞当前线程。它会调用 poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) 种方式 在处理每个输入帧后使用姿势特征点生成工具结果生成图像。通过 姿势地标器在专用序列上异步调用此方法。 调度队列。要在界面上显示结果,请分派 并在处理后将结果发送到主队列中。如果 detectAsync 函数时,系统会调用姿势地标器任务 姿势特征点器会忽略新的输入帧。

处理和显示结果

运行推理时,姿势特征点器任务会返回一个 PoseLandmarkerResult 其中包含每个姿势特征点的坐标。

以下示例展示了此任务的输出数据:

PoseLandmarkerResult:
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : 0.129959
      visibility   : 0.9999997615814209
      presence     : 0.9999984502792358
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
      visibility   : 0.999909
      presence     : 0.999958
    ... (33 landmarks per pose)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
      visibility   : 0.9999997615814209
      presence     : 0.9999984502792358
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
      visibility   : 0.999976
      presence     : 0.999998
    ... (33 world landmarks per pose)
  SegmentationMasks:
    ... (pictured below)

输出结果同时包含标准化坐标 (Landmarks) 和世界坐标 坐标 (WorldLandmarks)。

输出包含以下标准化坐标 (Landmarks):

  • xy:由 图片宽度 (x) 和高度 (y)。

  • z:地标深度,臀部中点的深度作为 来源。值越小,地标就越靠近镜头。通过 z 的量级使用的比例与 x 大致相同。

  • visibility:地标在图片中可见的可能性。

输出包含以下世界坐标 (WorldLandmarks):

  • xyz:真实的三维坐标(以米为单位), 作为原点。

  • visibility:地标在图片中可见的可能性。

下图直观显示了任务输出:

可选的分割掩码表示每个像素 。下图是 任务输出:

姿势特征点器示例代码演示了如何显示姿势特征点 结果。