iOS 向けジェスチャー認識ガイド

MediaPipe ジェスチャー認識タスクを使用すると、リアルタイムで手のジェスチャーを認識し、認識された手のジェスチャーの結果と検出された手のランドマークを提供します。ここでは、iOS アプリでジェスチャー認識ツールを使用する方法について説明します。

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

サンプルコード

MediaPipe Tasks のサンプルコードは、iOS 用ジェスチャー認識アプリの基本的な実装です。この例では、物理 iOS デバイスのカメラを使用して、手のジェスチャーを継続的に検出します。また、デバイス ギャラリーの画像や動画を使用して、ジェスチャーを静的に検出することもできます。

このアプリは、独自の iOS アプリの出発点として使用できます。または、既存のアプリを変更するときにアプリを参照できます。ジェスチャー認識ツールのサンプルコードは GitHub でホストされています。

コードをダウンロードする

次の手順では、git コマンドライン ツールを使用してサンプルコードのローカルコピーを作成する方法を示します。

サンプルコードをダウンロードするには:

  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/gesture_recognizer/ios/
    

サンプルコードのローカル バージョンを作成したら、MediaPipe タスク ライブラリをインストールし、Xcode を使用してプロジェクトを開いてアプリを実行できます。手順については、iOS の設定ガイドをご覧ください。

主要コンポーネント

次のファイルには、ジェスチャー認識のサンプル アプリケーションの重要なコードが含まれています。

セットアップ

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

依存関係

ジェスチャー認識ツールは MediaPipeTasksVision ライブラリを使用しますが、このライブラリは CocoaPods を使用してインストールする必要があります。このライブラリは、Swift アプリと Objective-C アプリの両方と互換性があり、言語固有の追加設定は必要ありません。

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

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

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

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

モデル

MediaPipe ジェスチャー認識タスクには、このタスクと互換性のあるトレーニング済みモデルが必要です。ジェスチャー認識ツールで利用可能なトレーニング済みモデルについて詳しくは、タスクの概要のモデルのセクションをご覧ください。

モデルを選択してダウンロードし、Xcode を使用してプロジェクト ディレクトリに追加します。Xcode プロジェクトにファイルを追加する方法については、Xcode プロジェクト内のファイルとフォルダの管理をご覧ください。

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

タスクを作成する

イニシャライザの 1 つを呼び出して、ジェスチャー認識タスクを作成できます。GestureRecognizer(options:) イニシャライザは、構成オプションの値を受け入れます。

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

ジェスチャー認識タスクは、静止画像、動画ファイル、ライブ動画ストリームの 3 つの入力データ型をサポートしています。デフォルトでは、GestureRecognizer(modelPath:) は静止画像のタスクを初期化します。動画ファイルまたはライブ動画ストリームを処理するためにタスクを初期化したい場合は、GestureRecognizer(options:) を使用して動画またはライブ ストリーミングの実行モードを指定します。ライブストリーム モードでは、追加の gestureRecognizerLiveStreamDelegate 構成オプションも必要です。このオプションを使用すると、ジェスチャー認識ツールがジェスチャー認識の結果をデリゲートに非同期で配信できます。

実行モードに対応するタブを選択して、タスクを作成して推論を実行する方法を確認します。

Swift

画像

import MediaPipeTasksVision

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

let options = GestureRecognizerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

let gestureRecognizer = try GestureRecognizer(options: options)
    

動画

import MediaPipeTasksVision

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

let options = GestureRecognizerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

let gestureRecognizer = try GestureRecognizer(options: options)
    

ライブ配信

import MediaPipeTasksVision

// Class that conforms to the `GestureRecognizerLiveStreamDelegate` protocol and
// implements the method that the gesture recognizer calls once it finishes
// performing recognizing hand gestures in each input frame.
class GestureRecognizerResultProcessor: NSObject, GestureRecognizerLiveStreamDelegate {

  func gestureRecognizer(
    _ gestureRecognizer: GestureRecognizer,
    didFinishRecognition result: GestureRecognizerResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the gesture recognizer result or errors here.

  }
}

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

let options = GestureRecognizerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

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

let gestureRecognizer = try GestureRecognizer(options: options)
    

Objective-C

画像

@import MediaPipeTasksVision;

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

MPPGestureRecognizerOptions *options =
  [[MPPGestureRecognizerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

MPPGestureRecognizer *gestureRecognizer =
      [[MPPGestureRecognizer alloc] initWithOptions:options error:nil];
    

動画

@import MediaPipeTasksVision;

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

MPPGestureRecognizerOptions *options =
  [[MPPGestureRecognizerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

MPPGestureRecognizer *gestureRecognizer =
      [[MPPGestureRecognizer alloc] initWithOptions:options error:nil];
    

ライブ配信

@import MediaPipeTasksVision;

// Class that conforms to the `MPPGestureRecognizerLiveStreamDelegate` protocol
// and implements the method that the gesture recognizer calls once it finishes
// performing gesture recognition on each input frame.

@interface APPGestureRecognizerResultProcessor : NSObject 

@end

@implementation APPGestureRecognizerResultProcessor

-   (void)gestureRecognizer:(MPPGestureRecognizer *)gestureRecognizer
    didFinishRecognitionWithResult:(MPPGestureRecognizerResult *)gestureRecognizerResult
           timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                             error:(NSError *)error {

    // Process the gesture recognizer result or errors here.

}

@end

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

MPPGestureRecognizerOptions *options =
  [[MPPGestureRecognizerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

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

MPPGestureRecognizer *gestureRecognizer =
      [[MPPGestureRecognizer alloc] initWithOptions:options error:nil];
    

構成オプション

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

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

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

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

LIVE_STREAM: カメラからのデータなど、入力データのライブストリームのモード。このモードでは、resultListener を呼び出して、結果を非同期で受け取るリスナーをセットアップする必要があります。 このモードでは、GestureRecognizerLiveStreamDelegate を実装するクラスのインスタンスに gestureRecognizerLiveStreamDelegate を設定して、ジェスチャー認識を非同期で実行した結果を受け取る必要があります。
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
num_hands 手の最大数は GestureRecognizer で検出できます。 Any integer > 0 1
min_hand_detection_confidence 手のひら検出モデルで手の検出が成功したとみなすための最小信頼スコア。 0.0 - 1.0 0.5
min_hand_presence_confidence 手のランドマーク検出モデルにおける手の存在スコアの最小信頼スコア。ジェスチャー認識ツールの動画モードとライブ ストリーム モードでは、手のランドマーク モデルからの手の存在の信頼スコアがこのしきい値を下回ると、手のひら検出モデルがトリガーされます。それ以外の場合は、軽量のハンド トラッキング アルゴリズムを使用して、後続のランドマーク検出のための手の位置を決定します。 0.0 - 1.0 0.5
min_tracking_confidence 成功とみなされる、ハンド トラッキングの最小信頼スコア。これは、現在のフレームと最後のフレームの手の間の境界ボックスの IoU しきい値です。ジェスチャー認識ツールの動画モードとストリーム モードでは、トラッキングが失敗すると、ジェスチャー認識は手の検出をトリガーします。それ以外の場合、手の検出はスキップされます。 0.0 - 1.0 0.5
canned_gestures_classifier_options ジェスチャー分類器の動作に関するオプション。返信定型文は ["None", "Closed_Fist", "Open_Palm", "Pointing_Up", "Thumb_Down", "Thumb_Up", "Victory", "ILoveYou"] です。
  • 表示名のロケール: TFLite Model Metadata で指定された表示名に使用するロケール(存在する場合)。
  • 最大結果数: スコアが最上位の分類結果の最大数。0 未満の場合、利用可能なすべての結果が返されます。
  • スコアしきい値: スコアが下回ると、結果が拒否されます。0 に設定すると、利用可能なすべての結果が返されます。
  • カテゴリの許可リスト: カテゴリ名の許可リスト。空でない場合は、このセットにカテゴリが含まれていない分類結果は除外されます。拒否リストとは相互に排他的です。
  • カテゴリ拒否リスト: カテゴリ名の拒否リスト。空でない場合は、カテゴリがこのセットに含まれる分類結果は除外されます。許可リストとは相互に排他的です。
    • 表示名のロケール: any string
    • 最大結果数: any integer
    • スコアしきい値: 0.0-1.0
    • カテゴリの許可リスト: vector of strings
    • カテゴリ拒否リスト: vector of strings
    • 表示名のロケール: "en"
    • 最大結果数: -1
    • スコアしきい値: 0
    • カテゴリの許可リスト: 空
    • カテゴリ拒否リスト: 空
    custom_gestures_classifier_options カスタム ジェスチャー分類器の動作を設定するオプション。
  • 表示名のロケール: TFLite Model Metadata で指定された表示名に使用するロケール(存在する場合)。
  • 最大結果数: スコアが最上位の分類結果の最大数。0 未満の場合、利用可能なすべての結果が返されます。
  • スコアしきい値: スコアが下回ると、結果が拒否されます。0 に設定すると、利用可能なすべての結果が返されます。
  • カテゴリの許可リスト: カテゴリ名の許可リスト。空でない場合は、このセットにカテゴリが含まれていない分類結果は除外されます。拒否リストとは相互に排他的です。
  • カテゴリ拒否リスト: カテゴリ名の拒否リスト。空でない場合は、カテゴリがこのセットに含まれる分類結果は除外されます。許可リストとは相互に排他的です。
    • 表示名のロケール: any string
    • 最大結果数: any integer
    • スコアしきい値: 0.0-1.0
    • カテゴリの許可リスト: vector of strings
    • カテゴリ拒否リスト: vector of strings
    • 表示名のロケール: "en"
    • 最大結果数: -1
    • スコアしきい値: 0
    • カテゴリの許可リスト: 空
    • カテゴリ拒否リスト: 空
    result_listener ジェスチャー認識機能がライブ ストリーム モードのときに分類結果を非同期で受け取るように結果リスナーを設定します。実行モードが LIVE_STREAM に設定されている場合にのみ使用できます ResultListener なし なし

    実行モードがライブストリームに設定されている場合、ジェスチャー認識ツールは追加の gestureRecognizerLiveStreamDelegate 設定オプションを必要とします。これにより、ジェスチャー認識は非同期でジェスチャー認識結果を配信できます。デリゲートは gestureRecognizer(_:didFinishRecognition:timestampInMilliseconds:error:) メソッドを実装する必要があります。各フレームでジェスチャー認識を実行した結果を処理した後に、ジェスチャー認識がこのメソッドを呼び出します。

    オプション名 説明 値の範囲 デフォルト値
    gestureRecognizerLiveStreamDelegate ライブ ストリーム モードでジェスチャー認識の結果を非同期で受け取るために、ジェスチャー認識を有効にします。インスタンスがこのプロパティに設定されているクラスは、gestureRecognizer(_:didFinishRecognition:timestampInMilliseconds:error:) メソッドを実装する必要があります。 該当なし 未設定

    データの準備

    入力の画像またはフレームを操作認識ツールに渡す前に、MPImage オブジェクトに変換する必要があります。MPImage は、さまざまな種類の iOS 画像形式をサポートしており、任意の実行モードで推論に使用できます。MPImage の詳細については、MPImage API をご覧ください。

    ユースケースとアプリケーションに必要な実行モードに基づいて、iOS 画像形式を選択します。MPImage では、UIImageCVPixelBufferCMSampleBuffer の iOS 画像形式を使用できます。

    UIImage

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

    • 画像: UIImage 画像としてフォーマットされた App Bundle、ユーザー ギャラリー、ファイル システムの画像を 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];
        

    この例では、MPImage をデフォルトの UIImage.Orientation.Up の向きで初期化しています。MPImage は、サポートされている任意の UIImage.Orientation 値で初期化できます。ジェスチャー認識ツールは、.upMirrored.downMirrored.leftMirrored.rightMirrored などのミラーリングされた向きをサポートしていません。

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

    CVPixelBuffer

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

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

    • 画像: iOS の CoreImage フレームワークを使用してなんらかの処理を行った後に CVPixelBuffer 画像を生成するアプリは、画像実行モードでジェスチャー認識ツールに送信できます。

    • 動画: 動画フレームを 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 の 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 デベロッパー向けドキュメントをご覧ください。

    タスクを実行する

    ジェスチャー認識機能を実行するには、割り当てた実行モードに固有の recognize() メソッドを使用します。

    • 静止画像: recognize(image:)
    • 動画: recognize(videoFrame:timestampInMilliseconds:)
    • ライブ配信: recognizeAsync(image:timestampInMilliseconds:)

    次のコードサンプルは、さまざまな実行モードでジェスチャー認識を実行する基本的な例を示しています。

    Swift

    画像

    let result = try gestureRecognizer.recognize(image: image)
        

    動画

    let result = try gestureRecognizer.recognize(
      videoFrame: image,
      timestampInMilliseconds: timestamp)
        

    ライブ配信

    try gestureRecognizer.recognizeAsync(
      image: image,
      timestampInMilliseconds: timestamp)
        

    Objective-C

    画像

      MPPGestureRecognizerResult *result =
        [gestureRecognizer recognizeImage:mppImage
                                    error:nil];
        

    動画

    MPPGestureRecognizerResult *result =
      [gestureRecognizer recognizeVideoFrame:image
                     timestampInMilliseconds:timestamp
                                       error:nil];
        

    ライブ配信

    BOOL success =
      [gestureRecognizer recognizeAsyncImage:image
                     timestampInMilliseconds:timestamp
                                       error:nil];
        

    サンプルコードを使用すると、ユースケースでは不要な処理モードを切り替えることができます。

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

    • 動画モードまたはライブストリーム モードで実行する場合は、入力フレームのタイムスタンプもジェスチャー認識タスクに渡す必要があります。

    • 画像モードまたは動画モードで実行する場合、ジェスチャー認識タスクは入力画像または入力フレームの処理を完了するまで、現在のスレッドをブロックします。現在のスレッドのブロックを回避するには、iOS の Dispatch フレームワークまたは NSOperation フレームワークを使用して、バックグラウンド スレッドで処理を実行します。

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

    結果を処理して表示する

    推論を実行すると、ジェスチャー認識タスクは GestureRecognizerResult を返します。これには、画像座標の手のランドマーク、世界座標の手のランドマーク、利き手(左手と右手)、検出された手のジェスチャーのカテゴリが含まれます。

    このタスクからの出力データの例を次に示します。

    結果として得られる GestureRecognizerResult には 4 つのコンポーネントが含まれ、各コンポーネントは配列です。各要素には、検出された 1 つの手の検出結果が含まれます。

    • 利き手

      利き手は、検出された手が左手か右手かを表します。

    • 操作

      検出された手の認識されるジェスチャー カテゴリ。

    • ランドマーク

      21 個の手のランドマークがあり、それぞれ xyz 座標で構成されています。x 座標と y 座標は、それぞれ画像の幅と高さによって [0.0, 1.0] に正規化されます。z 座標はランドマークの深さを表し、手首の深度を原点とします。値が小さいほど、ランドマークがカメラに近くなります。z の大きさは、x とほぼ同じスケールを使用します。

    • 世界の名所

      21 の手のランドマークも世界座標で表示されます。各ランドマークは xyz で構成され、手の幾何学的中心を原点とする実際の 3D 座標をメートル単位で表します。

    GestureRecognizerResult:
      Handedness:
        Categories #0:
          index        : 0
          score        : 0.98396
          categoryName : Left
      Gestures:
        Categories #0:
          score        : 0.76893
          categoryName : Thumb_Up
      Landmarks:
        Landmark #0:
          x            : 0.638852
          y            : 0.671197
          z            : -3.41E-7
        Landmark #1:
          x            : 0.634599
          y            : 0.536441
          z            : -0.06984
        ... (21 landmarks for a hand)
      WorldLandmarks:
        Landmark #0:
          x            : 0.067485
          y            : 0.031084
          z            : 0.055223
        Landmark #1:
          x            : 0.063209
          y            : -0.00382
          z            : 0.020920
        ... (21 world landmarks for a hand)
    

    次の図は、タスク出力を可視化したものです。