ML Kit を使用すると、画像内のエンティティを認識してラベルを付けることができます。 この API は、幅広いカスタム画像分類モデルをサポートしています。モデルの互換性要件、事前トレーニング済みモデルの入手先、独自のモデルをトレーニングする方法については、ML Kit でのカスタムモデルをご覧ください。
カスタムモデルを統合する方法は 2 つあります。モデルをアプリのアセット フォルダに配置してバンドルする方法と、Cloud Storage から動的にダウンロードする方法があります。次の表に、2 つのオプションを比較します。
| バンドルモデル | ホストされているモデル |
|---|---|
| モデルはアプリの APK の一部であるため、サイズが大きくなります。 | モデルは APK の一部ではありません。Cloud Storage にアップロードすることでホストされます。Cloud Storage for Firebase を使用することをおすすめします。 |
| このモデルは、Android デバイスがオフラインのときでもすぐに利用可能 | アプリには、オンデマンドでモデルをダウンロードするコードを含める必要があります。 |
| Firebase プロジェクトは不要 | Firebase プロジェクトが必要(Cloud Storage for Firebase を使用する場合)。 |
| モデルを更新するにはアプリを再公開する必要がある | アプリを再公開することなくモデルの更新を push できる |
| 組み込みの A/B テストはない | Firebase Remote Config を使用した A/B テスト |
試してみる
- バンドルモデルの使用例についてはVision クイックスタート アプリ、ホストされているモデルの使用例についてはAutoML クイックスタート アプリをご覧ください。
始める前に
Podfile に ML Kit ライブラリを含めます。
pod 'GoogleMLKit/ImageLabelingCustom', '8.0.0'プロジェクトの Pod をインストールまたは更新した後に、
.xcworkspaceを使用して Xcode プロジェクトを開きます。ML Kit は Xcode バージョン 13.2.1 以降でサポートされています。Cloud Storage for Firebase を使用してモデルをダウンロードする場合は、 まだ行っていない場合は Firebase を iOS プロジェクトに追加します。 これは、モデルをバンドルする場合には必要ありません。
1. モデルを読み込む
ローカル モデルソースを構成する
モデルをアプリにバンドルするには:
モデルファイル(拡張子は通常
.tfliteまたは.lite)を Xcode プロジェクトにコピーします。その際、Copy bundle resourcesを選択してください。モデルファイルは App Bundle に含められ、ML Kit から使用できます。LocalModelオブジェクトを作成し、モデルファイルのパスを指定します。Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
リモートでホストされるモデルソースを構成する
リモートでホストされるモデルを使用するには、独自のアプリロジックを使用してモデルファイルをデバイスのローカル ストレージにダウンロードし、ローカルモデルとして読み込む必要があります。モデルをホストするには、Cloud Storage for Firebase を使用することをおすすめします。実装の詳細については、 Firebase ML から Cloud Storage への移行ガイドをご覧ください。
画像ラベラーを構成する
モデルソースを構成したら、そのいずれかから ImageLabeler オブジェクトを作成します。
以下のオプションを使用できます。
| オプション | |
|---|---|
confidenceThreshold
|
検出されたラベルの最小信頼スコア。設定されていない場合は、モデルのメタデータで指定された分類子のしきい値が使用されます。モデルにメタデータが含まれていない場合、またはメタデータで 分類子のしきい値が指定されていない場合は、デフォルトのしきい値 0.0 が 使用されます。 |
maxResultCount
|
返されるラベルの最大数。設定されていない場合は、デフォルト値の 10 が使用されます。 |
ローカル バンドルモデルのみがある場合は、LocalModel オブジェクトからラベラーを作成するだけで済みます。
Swift
let options = CustomImageLabelerOptions(localModel: localModel) options.confidenceThreshold = NSNumber(value: 0.0) let imageLabeler = ImageLabeler.imageLabeler(options: options)
Objective-C
MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; options.confidenceThreshold = @(0.0); MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
リモートでホストされるモデルがある場合は、そのモデルを実行する前にダウンロード済みであることを確認する必要があります。
ダウンロードのステータスはラベラーを実行する前に確認するだけで済みますが、リモートでホストされるモデルとローカル バンドルモデルの両方がある場合は、ImageLabeler をインスタンス化する、つまりラベラーを作成する(リモートモデルをダウンロード済みの場合はリモートモデルから、ダウンロードされていない場合はローカルモデルから作成する)ときに確認しても問題ありません。
Swift
// Path where your download logic saves the model let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") let model: LocalModel if FileManager.default.fileExists(atPath: localModelURL.path) { // Use the downloaded model model = LocalModel(path: localModelURL.path) } else { // Fall back to bundled model guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return } model = LocalModel(path: bundledModelPath) } let options = CustomImageLabelerOptions(localModel: model) let imageLabeler = ImageLabeler.imageLabeler(options: options)
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; MLKLocalModel *model; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Use the downloaded model model = [[MLKLocalModel alloc] initWithPath:localModelPath]; } else { // Fall back to bundled model NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"]; model = [[MLKLocalModel alloc] initWithPath:bundledModelPath]; } MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:model]; MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
リモートでホストされるモデルのみがある場合は、モデルがダウンロード済みであることを確認するまで、モデルに関連する機能(UI の一部をグレー表示または非表示にするなど)を無効にする必要があります。
Swift
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") if FileManager.default.fileExists(atPath: localModelURL.path) { // Model is already cached, initialize immediately self.initializeLabeler(with: localModelURL) } else { // Model is not yet available, show loading UI and start download self.showLoadingUI() let storage = Storage.storage() let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite") modelRef.write(toFile: localModelURL) { url, error in self.hideLoadingUI() if let error = error { // Handle download error self.showErrorUI() } else if let modelURL = url { // Download success, initialize labeler self.initializeLabeler(with: modelURL) } } } func initializeLabeler(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomImageLabelerOptions(localModel: localModel) self.imageLabeler = ImageLabeler.imageLabeler(options: options) // Enable ML-related UI features here self.enableMLFeatures() }
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath]; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Model is already cached, initialize immediately [self initializeLabelerWithURL:localModelURL]; } else { // Model is not yet available, show loading UI and start download [self showLoadingUI]; FIRStorage *storage = [FIRStorage storage]; FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"]; [modelRef writeToFile:localModelURL completion:^(NSURL * _Nullable URL, NSError * _Nullable error) { [self hideLoadingUI]; if (error != nil) { // Handle download error [self showErrorUI]; } else { // Download success, initialize labeler [self initializeLabelerWithURL:URL]; } }]; } - (void)initializeLabelerWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; self.imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options]; // Enable ML-related UI features here [self enableMLFeatures]; }
2. 入力画像を準備する
UIImage または
CMSampleBuffer を使用して VisionImage オブジェクトを作成します。
UIImage を使用する場合は、次の手順を行います。
VisionImageオブジェクトをUIImageで作成します。正しい.orientationを指定してください。Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
CMSampleBuffer を使用する場合は、次の手順を行います。
-
に含まれる画像データの向きを指定します。
CMSampleBuffer画像の向きは次のように取得します。
Swift
func imageOrientation( deviceOrientation: UIDeviceOrientation, cameraPosition: AVCaptureDevice.Position ) -> UIImage.Orientation { switch deviceOrientation { case .portrait: return cameraPosition == .front ? .leftMirrored : .right case .landscapeLeft: return cameraPosition == .front ? .downMirrored : .up case .portraitUpsideDown: return cameraPosition == .front ? .rightMirrored : .left case .landscapeRight: return cameraPosition == .front ? .upMirrored : .down case .faceDown, .faceUp, .unknown: return .up } }
Objective-C
- (UIImageOrientation) imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation cameraPosition:(AVCaptureDevicePosition)cameraPosition { switch (deviceOrientation) { case UIDeviceOrientationPortrait: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored : UIImageOrientationRight; case UIDeviceOrientationLandscapeLeft: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored : UIImageOrientationUp; case UIDeviceOrientationPortraitUpsideDown: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored : UIImageOrientationLeft; case UIDeviceOrientationLandscapeRight: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored : UIImageOrientationDown; case UIDeviceOrientationUnknown: case UIDeviceOrientationFaceUp: case UIDeviceOrientationFaceDown: return UIImageOrientationUp; } }
VisionImageオブジェクトと向きを使用してCMSampleBufferオブジェクトを作成します:Swift
let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
Objective-C
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. 画像ラベラーを実行する
画像内のオブジェクトにラベルを付けるには、image オブジェクトを ImageLabeler の process() メソッドに渡します。
非同期:
Swift
imageLabeler.process(image) { labels, error in guard error == nil, let labels = labels, !labels.isEmpty else { // Handle the error. return } // Show results. }
Objective-C
[imageLabeler processImage:image completion:^(NSArray*_Nullable labels, NSError *_Nullable error) { if (label.count == 0) { // Handle the error. return; } // Show results. }];
同期:
Swift
var labels: [ImageLabel] do { labels = try imageLabeler.results(in: image) } catch let error { // Handle the error. return } // Show results.
Objective-C
NSError *error; NSArray*labels = [imageLabeler resultsInImage:image error:&error]; // Show results or handle the error.
4. ラベル付きエンティティに関する情報を取得する
画像のラベル付けオペレーションが成功すると、ImageLabel の配列が返されます。各 ImageLabel は画像内でラベル付けされたものを表します。各ラベルのテキストの説明(LiteRT モデルファイルのメタデータで使用できる場合)、信頼スコア、インデックスを取得できます。次に例を示します。
Swift
for label in labels { let labelText = label.text let confidence = label.confidence let index = label.index }
Objective-C
for (MLKImageLabel *label in labels) { NSString *labelText = label.text; float confidence = label.confidence; NSInteger index = label.index; }
リアルタイムのパフォーマンスを改善するためのヒント
リアルタイムのアプリケーションで画像にラベルを付ける場合は、次の ガイドラインに従って適切なフレームレートを得てください。
- 動画フレームを処理するには、検出機能の
results(in:)同期 API を使用します。このメソッドをAVCaptureVideoDataOutputSampleBufferDelegateのcaptureOutput(_, didOutput:from:)関数から呼び出して、指定された動画 フレームから結果を同期的に取得します。検出器の呼び出しをスロットルするために、AVCaptureVideoDataOutputのalwaysDiscardsLateVideoFramesをtrueのままにします。検出器の実行中に新しい 動画フレームが使用可能になった場合は、そのフレームはドロップされます。 - 検出器の出力を使用して入力画像の上にグラフィックスをオーバーレイする場合は、まず ML Kit から検出結果を取得し、画像とオーバーレイを 1 つのステップでレンダリングします。これにより、ディスプレイ サーフェスへのレンダリングは 処理済みの入力フレームごとに 1 回で済みます。例については、ML Kit クイックスタート サンプルの updatePreviewOverlayViewWithLastFrame をご覧ください。