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)
このモードは、関連性のない単一の画像用に設計されています。このモードでは、セグメンタは各画像を個別に処理し、フレームでの平滑化は行いません。
未加工サイズのマスクを有効にする
モデルの出力サイズと一致する未加工のサイズのマスクを返すようセグメンタに指示します。
未加工のマスクサイズ(例: 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. 入力画像を準備する
自撮り写真を分割するには、動画の画像またはフレームごとに次の操作を行います。
ストリーム モードを有効にした場合は、VisionImage
オブジェクトを作成する必要があります。
CMSampleBuffer
秒。
VisionImage
オブジェクトを作成するには、UIImage
または
CMSampleBuffer
。
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; } }
- 次のコマンドを使用して、
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. 画像を処理する
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 クラスをご覧ください。