iOS 向け姿勢ランドマーク検出ガイド

ポーズ ランドマーク タスクを使用すると、画像または動画内の人体のランドマークを検出できます。このタスクを使用すると、体の主要な位置を特定し、姿勢を分析して、動きを分類できます。このタスクでは、単一の画像または動画を処理する ML モデルを使用します。このタスクは、身体ポーズのランドマークを画像座標と 3 次元の世界座標で出力します。

以下の手順では、iOS アプリでポーズ ランドマークを使用する方法について説明します。この手順で説明するコードサンプルは GitHub で入手できます。

このタスクの動作を確認するには、こちらのウェブデモをご覧ください。このタスクの機能、モデル、構成オプションの詳細については、概要をご覧ください。

サンプルコード

MediaPipe Tasks のサンプルコードは、iOS 向けのポーズ ランドマークアプリの基本的な実装です。この例では、物理的な iOS デバイスのカメラを使用して、連続した動画ストリームでポーズを検出します。デバイスのギャラリーにある画像や動画のポーズも検出できます。

このアプリは、独自の iOS アプリの開始点として使用できます。また、既存のアプリを変更する際にも参照できます。Pose Landmarker の例コードは 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 のセットアップ ガイドをご覧ください。

主要コンポーネント

次のファイルには、Pose Landmarker サンプル アプリケーションの重要なコードが含まれています。

  • PoseLandmarkerService.swift: ランドマークを初期化し、モデル選択を処理し、入力データに対して推論を実行します。
  • CameraViewController: ライブカメラ フィード入力モードの UI を実装し、ランドマークを可視化します。
  • MediaLibraryViewController.swift: 静止画像ファイルと動画ファイル入力モードの UI を実装し、ランドマークを可視化します。

セットアップ

このセクションでは、Pose Landmarker を使用するように開発環境とコード プロジェクトを設定する主な手順について説明します。プラットフォーム バージョンの要件など、MediaPipe タスクを使用する開発環境の設定に関する一般的な情報については、iOS 用セットアップ ガイドをご覧ください。

依存関係

Pose Landmarker は MediaPipeTasksVision ライブラリを使用します。このライブラリは CocoaPods を使用してインストールする必要があります。このライブラリは Swift アプリと Objective-C アプリの両方に対応しており、言語固有の追加設定は必要ありません。

macOS に CocoaPods をインストールする手順については、CocoaPods インストール ガイドをご覧ください。アプリに必要な Pod を使用して Podfile を作成する方法については、CocoaPods の使用をご覧ください。

次のコードを使用して、MediaPipeTasksVision Pod を Podfile に追加します。

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

アプリに単体テスト ターゲットが含まれている場合は、Podfile の設定について詳しくは、iOS 用セットアップ ガイドをご覧ください。

モデル

MediaPipe Pose Landmarker タスクには、このタスクと互換性のあるトレーニング済みのバンドルが必要です。Pose Landmarker で使用可能なトレーニング済みモデルの詳細については、タスクの概要のモデルセクションをご覧ください。

download_models.sh スクリプトを使用してモデルをダウンロードし、Xcode を使用してプロジェクト ディレクトリに追加します。Xcode プロジェクトにファイルを追加する手順については、Xcode プロジェクトでのファイルとフォルダの管理をご覧ください。

BaseOptions.modelAssetPath プロパティを使用して、アプリバンドルのモデルのパスを指定します。コード例については、次のセクションをご覧ください。

タスクを作成する

Pose Landmarker タスクを作成するには、いずれかの初期化子を呼び出します。PoseLandmarker(options:) イニシャライザは、構成オプションの値を受け入れます。

カスタマイズされた構成オプションで初期化されたポーズ ランドマークが不要な場合は、PoseLandmarker(modelPath:) イニシャライザを使用して、デフォルト オプションでポーズ ランドマークを作成できます。構成オプションの詳細については、構成の概要をご覧ください。

Pose Landmarker タスクは、静止画像、動画ファイル、ライブ動画ストリームの 3 種類の入力データに対応しています。デフォルトでは、PoseLandmarker(modelPath:) は静止画像のタスクを初期化します。動画ファイルまたはライブ動画ストリームを処理するようにタスクを初期化する場合は、PoseLandmarker(options:) を使用して動画またはライブ配信の実行モードを指定します。ライブ配信モードでは、Pose Landmarker がポーズ ランドマーク検出結果をデリゲータに非同期で配信できるように、追加の 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 Landmarker はトラッキングを使用して、フレームごとに手のひら検出モデルをトリガーしないようにします。これにより、レイテンシを短縮できます。

設定オプション

このタスクには、iOS アプリ用の次の構成オプションがあります。

オプション名 説明 値の範囲 デフォルト値
running_mode タスクの実行モードを設定します。モードは次の 3 つです。

IMAGE: 単一画像入力のモード。

動画: 動画のデコードされたフレームのモード。

LIVE_STREAM: カメラなどからの入力データのライブ配信モード。 このモードでは、ポーズ ランドマーク検出を非同期で実行した結果を受け取るために、poseLandmarkerLiveStreamDelegatePoseLandmarkerLiveStreamDelegate を実装するクラスのインスタンスに設定する必要があります。
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
num_poses Pose Landmarker で検出できるポーズの最大数。 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

ライブ配信の設定

ランニング モードが [Livestream] に設定されている場合、姿勢マーカーには追加の poseLandmarkerLiveStreamDelegate 設定オプションが必要です。これにより、姿勢マーカーがランドマーク検出の結果を非同期で配信できるようになります。デリゲートには poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) メソッドを実装する必要があります。このメソッドは、Pose Landmarker が各フレームでポーズ ランドマーク検出を実行した結果を処理した後に呼び出されます。

オプション名 説明 値の範囲 デフォルト値
poseLandmarkerLiveStreamDelegate Pose Landmarker がライブ配信モードでポーズ ランドマーク検出を非同期的に実行した結果を受け取れるようにします。このプロパティにインスタンスが設定されているクラスは、poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) メソッドを実装する必要があります。 該当なし 未設定

データの準備

入力画像またはフレームを Pose Landmarker に渡す前に、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 を初期化しています。MPImage は、サポートされている UIImage.Orientation 値のいずれかで初期化できます。Pose Landmarker は、.upMirrored.downMirrored.leftMirrored.rightMirrored などのミラーリングされた向きをサポートしていません。

UIImage の詳細については、UIImage Apple デベロッパー ドキュメントをご覧ください。

CVPixelBuffer

CVPixelBuffer 形式は、フレームを生成し、iOS の CoreImage フレームワークを使用して処理するアプリに適しています。

CVPixelBuffer 形式は、次の実行モードに適しています。

  • 画像: iOS の CoreImage フレームワークを使用して処理を行った後に CVPixelBuffer 画像を生成するアプリは、画像実行モードで Pose Landmarker に送信できます。

  • 動画: 動画フレームは処理用に CVPixelBuffer 形式に変換し、動画モードで Pose Landmarker に送信できます。

  • ライブストリーム: 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 の AVCaptureVideoDataOutput によって CMSampleBuffer 形式で非同期的に配信されます。

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 デベロッパー ドキュメントをご覧ください。

タスクを実行する

Pose Landmarker を実行するには、割り当てられた実行モードに固有の 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];
    

Pose Landmarker のコードサンプルでは、これらのモード(detect(image:)detect(videoFrame:timestampInMilliseconds:)detectAsync(image:timestampInMilliseconds:))の各実装について詳しく説明しています。このサンプルコードを使用すると、ユースケースでは不要な処理モードをユーザーが切り替えることができます。

次の点にご留意ください。

  • 動画モードまたはライブ配信モードで実行する場合は、入力フレームのタイムスタンプを [Pose Landmarker] タスクに指定する必要があります。

  • 画像モードまたは動画モードで実行する場合、Pose Landmarker タスクは、入力画像またはフレームの処理が完了するまで現在のスレッドをブロックします。現在のスレッドをブロックしないようにするには、iOS の Dispatch または NSOperation フレームワークを使用して、バックグラウンド スレッドで処理を実行します。

  • ライブ配信モードで実行すると、Pose Landmarker タスクはすぐに返され、現在のスレッドはブロックされません。各入力フレームを処理した後、ポーズ ランドマークの結果を使用して poseLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) メソッドを呼び出します。Pose Landmarker は、専用のシリアル ディスパッチ キューでこのメソッドを非同期的に呼び出します。結果をユーザー インターフェースに表示するには、結果を処理した後に結果をメインキューにディスパッチします。Pose Landmarker タスクが別のフレームの処理でビジー状態になっているときに detectAsync 関数が呼び出されると、Pose Landmarker は新しい入力フレームを無視します。

結果を処理して表示する

推論を実行すると、Pose Landmarker タスクは、各ポーズ ランドマークの座標を含む 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: ヒップの中間点を原点とする、メートル単位の現実世界の 3 次元座標。

  • visibility: 画像内にランドマークが見える可能性。

次の画像は、タスク出力の可視化を示しています。

セグメンテーション マスク(省略可)は、検出された人物に属する各ピクセルの可能性を表します。次の画像は、タスク出力のセグメンテーション マスクです。

位置マーカーのサンプルコードでは、位置マーカーの結果を表示する方法を示しています。