iOS 向け画像埋め込みガイド

MediaPipe Image Embedder タスクを使用すると、画像データを数値表現に変換して、2 つの画像の類似性の比較など、ML 関連の画像処理タスクを実行できます。

これらの手順で説明するコードサンプルは GitHub で入手できます。このタスクの動作を確認するには、こちらのウェブデモをご覧ください。このタスクの機能、モデル、構成オプションの詳細については、概要をご覧ください。

サンプルコード

MediaPipe Tasks のサンプルコードは、iOS 用の画像埋め込みアプリの基本的な実装です。この例では、物理 iOS デバイスのカメラを使用して画像を継続的に埋め込みます。また、デバイス ギャラリーの画像ファイルに対して埋め込みを実行することもできます。

このアプリは、独自の iOS アプリの開始点として使用できます。また、既存のアプリを変更する際にも参照できます。Image Embedder のサンプルコードは GitHub でホストされています。

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

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

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

  1. 次のコマンドを使用して Git リポジトリのクローンを作成します。

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. 必要に応じて、スパース チェックアウトを使用するように Git インスタンスを構成して、Image Embedder サンプルアプリのファイルのみを取得します。

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

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

主要コンポーネント

次のファイルには、画像埋め込みのサンプル アプリケーションに不可欠なコードが含まれています。

セットアップ

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

依存関係

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

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

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

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

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

モデル

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

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

BaseOptions.modelAssetPath プロパティを使用して、App Bundle 内のモデルのパスを指定します。

タスクを作成する

画像埋め込みタスクを作成するには、そのイニシャライザのいずれかを呼び出します。ImageEmbedder(options:) イニシャライザは、構成オプションの値を受け入れます。

カスタマイズした構成オプションで初期化された画像埋め込み機能を必要としない場合は、ImageEmbedder(modelPath:) イニシャライザを使用して、デフォルトのオプションを指定して画像埋め込みツールを作成できます。構成オプションの詳細については、構成の概要をご覧ください。

Image Embedder タスクは、静止画像、動画ファイル、ライブ動画ストリーミングの 3 つの入力データ型をサポートしています。デフォルトでは、ImageEmbedder(modelPath:) は静止画像のタスクを初期化します。動画ファイルまたはライブ動画ストリームを処理するようにタスクを初期化する場合は、ImageEmbedder(options:) を使用して動画またはライブ配信の実行モードを指定します。ライブ配信モードでは、Image Embedder が画像埋め込み結果を非同期でデリゲートに配信できるように、imageEmbedderLiveStreamDelegate 構成オプションも追加する必要があります。

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

Swift

画像

import MediaPipeTasksVision

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageEmbedderOptions()
options.baseOptions.modelAssetPath = modelPath
options.quantize = true
options.l2Normalize = true

let imageEmbedder = try ImageEmbedder(options: options)
    

動画

import MediaPipeTasksVision

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageEmbedderOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.quantize = true
options.l2Normalize = true

let imageEmbedder = try ImageEmbedder(options: options)
    

ライブ配信

import MediaPipeTasksVision

// Class that conforms to the `ImageEmbedderLiveStreamDelegate` protocol and
// implements the method that the image embedder calls once it finishes
// embedding each input frame.
class ImageEmbedderResultProcessor: NSObject, ImageEmbedderLiveStreamDelegate {

  func imageEmbedder(
    _ imageEmbedder: ImageEmbedder,
    didFinishEmbedding result: ImageEmbedderResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the image embedder result or errors here.

  }
}

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageEmbedderOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.quantize = true
options.l2Normalize = true

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

let imageEmbedder = try ImageEmbedder(options: options)
    

Objective-C

画像

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageEmbedderOptions *options = [[MPPImageEmbedderOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.quantize = YES;
options.l2Normalize = YES;

MPPImageEmbedder *imageEmbedder =
  [[MPPImageEmbedder alloc] initWithOptions:options error:nil];
    

動画

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageEmbedderOptions *options = [[MPPImageEmbedderOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.quantize = YES;
options.l2Normalize = YES;

MPPImageEmbedder *imageEmbedder =
  [[MPPImageEmbedder alloc] initWithOptions:options error:nil];
    

ライブ配信

@import MediaPipeTasksVision;

// Class that conforms to the `MPPImageEmbedderLiveStreamDelegate` protocol
// and implements the method that the image embedder calls once it finishes
// embedding each input frame.
@interface APPImageEmbedderResultProcessor : NSObject 

@end

@implementation APPImageEmbedderResultProcessor

-   (void)imageEmbedder:(MPPImageEmbedder *)imageEmbedder
    didFinishEmbeddingWithResult:(MPPImageEmbedderResult *)imageEmbedderResult
         timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                           error:(NSError *)error {

    // Process the image embedder result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageEmbedderOptions *options = [[MPPImageEmbedderOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.quantize = YES;
options.l2Normalize = YES;

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

MPPImageEmbedder *imageEmbedder =
  [[MPPImageEmbedder alloc] initWithOptions:options error:nil];
    

設定オプション

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

オプション名 説明 値の範囲 デフォルト値
runningMode タスクの実行モードを設定します。画像埋め込みには、次の 3 つのモードがあります。

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

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

LIVE_STREAM: カメラなどからの入力データのライブ配信モード。 このモードでは、画像フレームの埋め込みの結果を非同期で受信するには、ImageEmbedderLiveStreamDelegate を実装するクラスのインスタンスに imageEmbedderLiveStreamDelegate を設定する必要があります。
{RunningMode.image, RunningMode.video, RunningMode.liveStream} {RunningMode.image}
l2Normalize 返された特徴ベクトルを L2 ノルムで正規化するかどうか。このオプションは、モデルにネイティブの L2_NORMALIZATION TFLite Op がまだ含まれていない場合にのみ使用します。ほとんどの場合、すでにこのオプションが設定されており、TFLite 推論によって L2 正規化が実行されるため、このオプションは必要ありません。 Bool false
quantize 返されたエンベディングをスカラー量子化によりバイトに量子化するかどうか。エンベディングは暗黙的に単位正規化されていると想定されるため、すべてのディメンションの値は [-1.0、1.0] 内に収まります。そうでない場合は、l2Normalize オプションを使用します。 Bool false

実行モードがライブ ストリームに設定されている場合、画像エンベディングには追加の imageEmbedderLiveStreamDelegate 構成オプションが必要です。これにより、画像エンベディングは画像のエンベディング結果を非同期で配信できます。デリゲートには imageEmbedder(_:didFinishEmbedding:timestampInMilliseconds:error:) メソッドを実装する必要があります。このメソッドは、Image Embedder が各入力画像フレームのエンベディング結果を処理した後に呼び出されます。

オプション名 説明 値の範囲 デフォルト値
imageEmbedderLiveStreamDelegate 画像埋め込みツールが、ライブ配信モードで画像の埋め込み結果を非同期で受信できるようにしました。このプロパティにインスタンスが設定されているクラスは、imageEmbedder(_:didFinishEmbedding:timestampInMilliseconds:error:) メソッドを実装する必要があります。 該当なし 未設定

データの準備

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

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

CVPixelBuffer

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

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

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

  • 動画: 動画フレームは、処理のために CVPixelBuffer 形式に変換してから、動画モードで Image Embedder に送信できます。

  • ライブストリーム: 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 デベロッパー ドキュメントをご覧ください。

タスクを実行する

Image Embedder を実行するには、割り当てられた実行モードに固有の embed() メソッドを使用します。

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

次のコードサンプルは、さまざまな実行モードで画像埋め込みを実行する基本的な例を示しています。

Swift

画像

let result = try imageEmbedder.embed(image: image)
    

動画

let result = try imageEmbedder.embed(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

ライブ配信

try imageEmbedder.embedAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

画像

MPPImageEmbedderResult *result =
  [imageEmbedder embedImage:image error:nil];
    

動画

MPPImageEmbedderResult *result =
  [imageEmbedder embedVideoFrame:image
           timestampInMilliseconds:timestamp
                             error:nil];
    

ライブ配信

BOOL success =
  [imageEmbedder embedAsyncImage:image
           timestampInMilliseconds:timestamp
                             error:nil];
    

Image Embedder のコード例では、これらのモード(embed(image:)embed(videoFrame:timestampInMilliseconds:)embedAsync(image:timestampInMilliseconds:))の実装について詳しく説明しています。サンプルコードでは、ユーザーが処理モードを切り替えることができますが、ユースケースで必要でない場合があります。

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

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

  • 画像モードまたは動画モードで実行している場合、画像エンベディング タスクは、入力画像またはフレームの処理が完了するまで現在のスレッドをブロックします。現在のスレッドをブロックしないようにするには、iOS の Dispatch または NSOperation フレームワークを使用して、バックグラウンド スレッドで処理を実行します。アプリが Swift を使用して作成されている場合は、バックグラウンド スレッドの実行に Swift の同時実行を使用することもできます。

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

結果を処理して表示する

推論を実行すると、Image Embedder は、入力画像のエンベディング(浮動小数点またはスカラー量子化)のリストを含む ImageEmbedderResult オブジェクトを返します。

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

ImageEmbedderResult:
  Embedding #0 (sole embedding head):
    float_embedding: {0.0, 0.0, ..., 0.0, 1.0, 0.0, 0.0, 2.0}
    head_index: 0

この結果は、次の画像をエンベディングすることで得られました。

2 つのエンベディングの類似性を比較するには、ImageEmbedder.cosineSimilarity 関数を使用します。

Swift

let similarity = try ImageEmbedder.cosineSimilarity(
  embedding1: result.embeddingResult.embeddings[0],
  embedding2: otherResult.embeddingResult.embeddings[0])
    

Objective-C

NSNumber *similarity = [MPPImageEmbedder
      cosineSimilarityBetweenEmbedding1:result.embeddingResult.embeddings[0]
                          andEmbedding2:otherResult.embeddingResult.embeddings[0]
                                  error:nil];