ML Kit bietet ein optimiertes SDK für die Segmentierung von Selfies. Die Selfie Segmenter-Assets werden bei der Buildzeit statisch mit Ihrer App verknüpft. Dadurch erhöht sich die App-Größe um bis zu 24 MB und die API-Latenz kann je nach Größe des Eingabebilds zwischen etwa 7 ms und 12 ms liegen, gemessen auf dem iPhone X.
Jetzt ausprobieren
- In der Beispielanwendung sehen Sie ein Beispiel für die Verwendung dieser API.
Hinweis
Fügen Sie Ihrer Podfile-Datei die folgenden ML Kit-Bibliotheken hinzu:
pod 'GoogleMLKit/SegmentationSelfie', '7.0.0'
Nachdem Sie die Pods Ihres Projekts installiert oder aktualisiert haben, öffnen Sie Ihr Xcode-Projekt mit der
xcworkspace
-Datei. ML Kit wird in Xcode Version 13.2.1 oder höher unterstützt.
1. Instanz von Segmenter erstellen
Wenn Sie eine Segmentierung für ein Selfie-Bild ausführen möchten, erstellen Sie zuerst eine Instanz von Segmenter
mit SelfieSegmenterOptions
und geben Sie optional die Segmentierungseinstellungen an.
Segmentierungsoptionen
Segmentiermodus
Der Segmenter
kann in zwei Modi betrieben werden. Achten Sie darauf, die Option auszuwählen, die zu Ihrem Anwendungsfall passt.
STREAM_MODE (default)
Dieser Modus ist für das Streaming von Frames aus einem Video oder einer Kamera konzipiert. In diesem Modus nutzt der Segmentierungsalgorithmus Ergebnisse aus vorherigen Frames, um glattere Segmentierungsergebnisse zu liefern.
SINGLE_IMAGE_MODE (default)
Dieser Modus ist für einzelne Bilder gedacht, die nicht zusammenhängen. In diesem Modus verarbeitet der Segmentierungsalgorithmus jedes Bild unabhängig voneinander, ohne Glättung über Frames hinweg.
Maske für Rohgröße aktivieren
Der Segmentierer wird aufgefordert, die Rohgrößenmaske zurückzugeben, die der Modellausgabegröße entspricht.
Die Größe der Rohmaske (z.B. 256 × 256) ist in der Regel kleiner als die Größe des Eingabebilds.
Wenn Sie diese Option nicht angeben, skaliert der Segmentierungsalgorithmus die Rohmaske so, dass sie der Größe des Eingabebilds entspricht. Verwenden Sie diese Option, wenn Sie eine benutzerdefinierte Logik für die Neuberechnung anwenden möchten oder die Neuberechnung für Ihren Anwendungsfall nicht erforderlich ist.
Geben Sie die Segmentierungsoptionen an:
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Rufen Sie abschließend eine Instanz von Segmenter
ab. Übergeben Sie die von Ihnen angegebenen Optionen:
let segmenter = Segmenter.segmenter(options: options)
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Eingabebild vorbereiten
Wenn Sie Selfies segmentieren möchten, gehen Sie für jedes Bild oder jeden Videoframe so vor:
Wenn Sie den Stream-Modus aktiviert haben, müssen Sie VisionImage
-Objekte aus CMSampleBuffer
-Objekten erstellen.
Erstellen Sie ein VisionImage
-Objekt mit einem UIImage
oder einem CMSampleBuffer
.
Wenn Sie ein UIImage
verwenden, gehen Sie so vor:
- Erstellen Sie ein
VisionImage
-Objekt mit demUIImage
. Achten Sie darauf, die richtige.orientation
anzugeben.let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Wenn Sie ein CMSampleBuffer
verwenden, gehen Sie so vor:
-
Geben Sie die Ausrichtung der Bilddaten an, die in
CMSampleBuffer
enthalten sind.So rufen Sie die Bildausrichtung auf:
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 } }
- (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; } }
- Erstellen Sie ein
VisionImage
-Objekt mit demCMSampleBuffer
-Objekt und der Ausrichtung:let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. Bild verarbeiten
Übergeben Sie das VisionImage
-Objekt an eine der Bildverarbeitungsmethoden von Segmenter
. Sie können entweder die asynchrone Methode process(image:)
oder die synchrone Methode results(in:)
verwenden.
So führen Sie eine synchrone Segmentierung für ein Selfie durch:
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.
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
So führen Sie eine asynchrone Segmentierung eines Selfies durch:
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. Segmentierungsmaske abrufen
So rufen Sie das Segmentierungsergebnis ab:
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 }
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); }
Ein vollständiges Beispiel für die Verwendung der Segmentierungsergebnisse finden Sie im Beispiel für die ML Kit-Kurzanleitung.
Tipps zur Leistungsverbesserung
Die Qualität der Ergebnisse hängt von der Qualität des Eingabebilds ab:
- Damit ML Kit ein genaues Segmentierungsergebnis liefern kann, muss das Bild mindestens 256 × 256 Pixel groß sein.
- Wenn Sie die Selfie-Segmentierung in einer Echtzeitanwendung ausführen, sollten Sie auch die Gesamtabmessungen der Eingabebilder berücksichtigen. Kleinere Bilder können schneller verarbeitet werden. Um die Latenz zu verringern, sollten Sie Bilder mit niedrigerer Auflösung aufnehmen. Beachten Sie dabei jedoch die oben genannten Auflösungsanforderungen und achten Sie darauf, dass das Motiv möglichst viel Platz im Bild einnimmt.
- Auch ein unscharfer Bildfokus kann sich auf die Genauigkeit auswirken. Wenn Sie keine zufriedenstellenden Ergebnisse erhalten, bitten Sie den Nutzer, das Bild noch einmal aufzunehmen.
Wenn Sie die Segmentierung in einer Echtzeitanwendung verwenden möchten, beachten Sie die folgenden Richtlinien, um die besten Frameraten zu erzielen:
- Verwenden Sie den Segmentierungsmodus
stream
. - Sie können auch Bilder mit niedrigerer Auflösung aufnehmen. Beachten Sie jedoch auch die Anforderungen an die Bildabmessungen dieser API.
- Verwende für die Verarbeitung von Videoframes die synchrone
results(in:)
API des Segmentierungstools. Rufen Sie diese Methode über die Funktion captureOutput(_, didOutput:from:) des AVCaptureVideoDataOutputSampleBufferDelegate auf, um synchron Ergebnisse aus dem angegebenen Videoframe abzurufen. Lassen Sie alwaysDiscardsLateVideoFrames von AVCaptureVideoDataOutput auf „true“ gesetzt, um Aufrufe an den Segmenter zu drosseln. Wenn während der Ausführung des Segmentierungstools ein neuer Videoframe verfügbar wird, wird er verworfen. - Wenn Sie die Ausgabe des Segmentierungstools verwenden, um Grafiken auf das Eingabebild zu legen, rufen Sie zuerst das Ergebnis aus ML Kit ab und rendern Sie dann das Bild und das Overlay in einem einzigen Schritt. So wird für jeden verarbeiteten Eingabeframe nur einmal auf die Displayoberfläche gerendert. Ein Beispiel finden Sie in den Klassen previewOverlayView und CameraViewController im ML Kit-Beispiel für den Schnellstart.