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

MediaPipe Gesture Recognizer タスクを使用すると、手ジェスチャーをリアルタイムで認識し、認識された手ジェスチャーの結果と検出された手のランドマークを取得できます。以下では、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 の使用をご覧ください。

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

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

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

モデル

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

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

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

タスクを作成する

ジェスチャー認識タスクは、いずれかの初期化子を呼び出すことで作成できます。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: 単一画像入力のモード。

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

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"]
です。
  • Display Name locale: 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 カスタム ジェスチャー分類システムの動作を構成するためのオプション。
  • Display Name locale: 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 形式は、次の実行モードに適しています。

    • 画像: 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];
        

    この例では、デフォルトの UIImage.Orientation.Up の向きで MPImage を初期化しています。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 には、画像座標のハンド ランドマーク、ワールド座標のハンド ランドマーク、利き手(左手/右手)、検出された手のハンド ジェスチャー カテゴリが含まれます。

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

    結果の 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)
    

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