ML Kit は、自撮り写真のセグメンテーション向けに最適化された SDK を提供します。自撮り写真セグメンテーションのアセットは、ビルド時にアプリに静的にリンクされます。これにより、アプリのサイズが最大 24 MB 増加します。iPhone X で測定した場合、API レイテンシは入力画像のサイズに応じて最大 7 ミリ秒から最大 12 ミリ秒まで変動する可能性があります。
試してみる
- サンプルアプリを試して、この API の使用例を確認します。
始める前に
Podfile に次の ML Kit ライブラリを含めます。
pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
プロジェクトの Pod をインストールまたは更新したら、
xcworkspace
を使用して Xcode プロジェクトを開きます。ML Kit は Xcode バージョン 13.2.1 以降でサポートされています。
1. Segmenter のインスタンスを作成する
自撮り画像でセグメンテーションを行うには、まず SelfieSegmenterOptions
で Segmenter
のインスタンスを作成し、必要に応じてセグメンテーション設定を指定します。
セグメント化ツールのオプション
セグメンタ モード
Segmenter
は 2 つのモードで動作します。ユースケースに合ったものを選択してください。
STREAM_MODE (default)
このモードは、動画やカメラからのフレームのストリーミング用に設計されています。このモードでは、セグメント化ツールは前のフレームからの結果を利用して、より滑らかなセグメンテーション結果を返します。
SINGLE_IMAGE_MODE (default)
このモードは、関連性のない 1 つの画像向けに設計されています。このモードでは、セグメンタは各画像を個別に処理し、フレームを平滑化しません。
未加工サイズのマスクを有効にする
モデルの出力サイズに一致する未加工サイズのマスクを返すよう、セグメンタに指示します。
未加工のマスクサイズ(256x256 など)は通常、入力画像サイズよりも小さくなります。
このオプションを指定しなかった場合、セグメント化ツールによって、入力画像のサイズに合わせて未加工のマスクが再スケーリングされます。カスタマイズされた再スケーリング ロジックを適用する場合や、ユースケースで再スケーリングが不要な場合は、このオプションの使用を検討してください。
セグメンタ オプションを指定します。
Swift
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
Objective-C
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
最後に、Segmenter
のインスタンスを取得します。指定したオプションを渡します。
Swift
let segmenter = Segmenter.segmenter(options: options)
Objective-C
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. 入力画像を準備する
自撮り写真を分割するには、画像または動画フレームごとに次の操作を行います。
ストリーム モードを有効にした場合は、CMSampleBuffer
から VisionImage
オブジェクトを作成する必要があります。
UIImage
または CMSampleBuffer
を使用して VisionImage
オブジェクトを作成します。
UIImage
を使用する場合の手順は次のとおりです。
UIImage
を使用してVisionImage
オブジェクトを作成します。正しい.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; } }
CMSampleBuffer
オブジェクトと画面の向きを使用して、VisionImage
オブジェクトを作成します。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.画像を処理する
VisionImage
オブジェクトを Segmenter
のいずれかの画像処理メソッドに渡します。非同期 process(image:)
メソッドまたは同期 results(in:)
メソッドを使用できます。
自撮り画像に対して同期的にセグメンテーションを実行するには:
Swift
var mask: [SegmentationMask] do { mask = try segmenter.results(in: image) } catch let error { print("Failed to perform segmentation with error: \(error.localizedDescription).") return } // Success. Get a segmentation mask here.
Objective-C
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
自撮り画像のセグメンテーションを非同期的に実行するには:
Swift
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
Objective-C
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. セグメンテーション マスクを取得する
セグメンテーションの結果は次のように取得できます。
Swift
let maskWidth = CVPixelBufferGetWidth(mask.buffer) let maskHeight = CVPixelBufferGetHeight(mask.buffer) CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) var maskAddress = CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory( to: Float32.self, capacity: maskBytesPerRow * maskHeight) for _ in 0...(maskHeight - 1) { for col in 0...(maskWidth - 1) { // Gets the confidence of the pixel in the mask being in the foreground. let foregroundConfidence: Float32 = maskAddress[col] } maskAddress += maskBytesPerRow / MemoryLayout<Float32>.size }
Objective-C
size_t width = CVPixelBufferGetWidth(mask.buffer); size_t height = CVPixelBufferGetHeight(mask.buffer); CVPixelBufferLockBaseAddress(mask.buffer, kCVPixelBufferLock_ReadOnly); size_t maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer); float *maskAddress = (float *)CVPixelBufferGetBaseAddress(mask.buffer); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { // Gets the confidence of the pixel in the mask being in the foreground. float foregroundConfidence = maskAddress[col]; } maskAddress += maskBytesPerRow / sizeof(float); }
セグメンテーション結果の使用方法の完全な例については、ML Kit クイックスタート サンプルをご覧ください。
パフォーマンス改善のヒント
結果の品質は入力画像の品質によって異なります。
- ML Kit で正確なセグメンテーション結果を得るには、画像を 256 x 256 ピクセル以上にする必要があります。
- リアルタイム アプリケーションで自撮り写真のセグメンテーションを実行する場合は、入力画像の全体的なサイズも考慮する必要があります。サイズが小さいほど処理が速くなるため、遅延を短縮するために低解像度で画像をキャプチャします。ただし、上記の解像度要件に留意し、被写体が画像の大部分を占めるようにしてください。
- 画像のフォーカスが不適切であることも精度に影響することがあります。満足のいく結果が得られない場合は、画像をキャプチャし直すようユーザーに依頼します。
リアルタイムのアプリケーションでセグメンテーションを使用する場合は、最適なフレームレートを得るために次のガイドラインに従ってください。
stream
セグメンタ モードを使用します。- 低解像度で画像をキャプチャすることを検討してください。ただし、この API の画像サイズの要件にも注意してください。
- 動画フレームの処理には、セグメンタの
results(in:)
同期 API を使用します。このメソッドを AVCaptureVideoDataOutputSampleBufferDelegate の captureOutput(_, didOutput:from:) 関数から呼び出して、指定された動画フレームから結果を同期的に取得します。セグメンタへの呼び出しをスロットリングするために、AVCaptureVideoDataOutput の alwaysDiscardsLateVideoFrames を true のままにします。セグメント化ツールの実行中に新しい動画フレームが利用可能になると、そのフレームは破棄されます。 - セグメンタの出力を使用して入力画像にグラフィックスをオーバーレイする場合は、まず ML Kit から結果を取得してから、画像とオーバーレイを 1 つのステップでレンダリングします。これにより、処理された入力フレームごとに、ディスプレイ サーフェスに 1 回だけレンダリングされます。例については、ML Kit クイックスタート サンプルの previewOverlayView クラスと CameraViewController クラスをご覧ください。