iOS 適用的位置地標偵測指南

姿勢地標工作可讓您在圖片或影片中偵測人體地標。您可以使用這項工作來找出身體的重要位置、分析姿勢,以及分類動作。這項工作會使用支援單一圖片或影片的機器學習 (ML) 模型這項工作會以圖片座標和 3D 世界座標輸出人體姿勢地標。

以下指示將說明如何搭配使用 Pose 地標和 iOS 應用程式。您可以在 GitHub 上找到這些操作說明中所述的程式碼範例。

您可以查看這個網頁示範,瞭解這項工作的實際運作情形。如要進一步瞭解這項工作的功能、模型和設定選項,請參閱總覽

程式碼範例

MediaPipe Tasks 範例程式碼是 iOS 專用姿勢地標應用程式的基本實作方式。本範例會使用實體 iOS 裝置的相機,在持續的影片串流中偵測姿勢。應用程式也可以偵測裝置相片庫中的圖片和影片姿勢。

您可以使用這個應用程式做為自有 iOS 應用程式的起點,或是在修改現有應用程式時參考。姿勢地標程式碼範例已託管至 GitHub

下載程式碼

以下操作說明說明如何使用 git 指令列工具,建立範例程式碼的本機副本。

下載程式碼範例:

  1. 使用下列指令複製 Git 存放區:

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. 您可以選擇將 Git 例項設定為使用稀疏檢查,這樣您就只會取得 Pose Landmarker 範例應用程式的檔案:

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

建立範例程式碼的本機版本後,您可以安裝 MediaPipe 工作程式庫、使用 Xcode 開啟專案,然後執行應用程式。如需操作說明,請參閱 iOS 設定指南

重要元件

以下檔案包含姿勢地標範例應用程式的關鍵程式碼:

設定

本節將說明設定開發環境和程式碼專案以使用姿勢地標的主要步驟。如要進一步瞭解如何設定開發環境,以便使用 MediaPipe 工作,包括平台版本需求,請參閱 iOS 專用設定指南

依附元件

Pose Lander 會使用 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 Pose Landmarker 工作需要經過訓練且與這項工作相容。如要進一步瞭解可用於姿勢標記器的訓練模型,請參閱任務總覽的「模型」一節。

使用 download_models.sh 指令碼下載模型,然後使用 Xcode 將模型新增至專案目錄。如需在 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];
    

注意:如果您使用影片模式或直播模式,Pose Lander 會使用追蹤功能,避免在每個影格上觸發手掌偵測模型,進而縮短延遲時間。

設定選項

此工作包含下列 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 設定結果事件監聽器,以便在 Pose 地標 er 處於直播模式時,以非同步方式接收地標結果。 只有在執行模式設為「LIVE_STREAM」時才能使用 ResultListener N/A

直播設定

當執行模式設為直播時,Pose Landmarker 需要額外的 poseLandmarkerLiveStreamDelegate 設定選項,才能讓 Pose Landmarker 以非同步方式提供姿勢地標偵測結果。委派程式必須實作 poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) 方法,Pose Landmarker 會在處理對每個影格執行姿勢地標偵測的結果後,呼叫該方法。

選項名稱 說明 值範圍 預設值
poseLandmarkerLiveStreamDelegate 讓姿勢地標偵測器在直播模式下,以非同步方式接收執行姿勢地標偵測的結果。將例項設為此屬性的類別,必須實作 poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) 方法。 不適用 未設定

準備資料

您必須先將輸入圖片或影格轉換為 MPImage 物件,才能將其傳遞至姿勢標記器。MPImage 支援不同類型的 iOS 圖片格式,可在任何執行中模式使用來推論。如要進一步瞭解 MPImage,請參閱 MPImage API

請根據用途和應用程式所需的執行模式,選擇 iOS 圖片格式。MPImage 接受 UIImageCVPixelBufferCMSampleBuffer iOS 圖片格式。

UIImage

UIImage 格式非常適合下列執行模式:

  • 圖片:應用程式套件、使用者圖庫或檔案系統中的圖片 (格式為 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];
    

本範例使用預設的 UIImage.Orientation.Up 方向初始化 MPImage。您可以使用任何支援的 UIImage.Orientation 值初始化 MPImage。Pose Landmarker 不支援鏡像方向,例如 .upMirrored.downMirrored.leftMirrored.rightMirrored

如要進一步瞭解 UIImage,請參閱 UIImage Apple 開發人員說明文件

CVPixelBuffer

CVPixelBuffer 格式非常適合用於產生影格,並使用 iOS CoreImage 架構進行處理的應用程式。

CVPixelBuffer 格式非常適合下列執行模式:

  • 圖片:如果應用程式在使用 iOS 的 CoreImage 架構進行部分處理後,產生 CVPixelBuffer 圖片,則可在圖片執行模式下傳送至姿勢地標。

  • 影片:影片影格可以轉換成 CVPixelBuffer 格式進行處理,然後在影片模式中傳送至 Pose 地標。

  • 直播:使用 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 AVCaptureVideoDataOutput 會以 CMSampleBuffer 格式,以非同步方式傳送 iOS 攝影機的即時影格。

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:)。程式碼範例可讓使用者切換不同處理模式,但某些處理模式可能不適用於您的用途。

注意事項:

  • 在影片模式或直播模式下執行時,您也必須為 Pose 地標 er 工作提供輸入影格的時間戳記。

  • 在圖片或影片模式下執行時,姿勢地標器工作會阻斷目前的執行緒,直到處理完輸入圖片或影格為止。為避免封鎖目前的執行緒,請使用 iOS DispatchNSOperation 架構在背景執行緒中執行處理作業。

  • 在直播模式下執行時,姿勢地標作業會立即傳回,且不會封鎖目前的執行緒。在處理每個輸入影格後,會使用姿勢標記器結果呼叫 poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) 方法。Pose Landmarker 會在專屬的序列調度佇列上以非同步方式叫用這個方法。如要在使用者介面上顯示結果,請在處理結果後將結果調度至主佇列。如果在姿勢地標工作忙於處理其他影格時呼叫 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) 正規化為 0.0 到 1.0。

  • z:地標深度,與河流中點的深度是起點。值越小,地標離相機越近。z 的大小會使用與 x 大致相同的比例。

  • visibility:地標在圖片中可見的可能性。

輸出內容包含下列世界座標 (WorldLandmarks):

  • xyz:以公尺為單位的實際 3D 座標,以臀部中點做為原點。

  • visibility:地標在圖片中顯示的可能性。

下圖是工作輸出內容的視覺化呈現:

選用的分割遮罩代表每個像素屬於偵測到的人物的可能性。以下圖片是工作輸出的區隔遮罩:

姿勢定位標記範例程式碼示範如何顯示姿勢定位標記結果。