Selfiesegmentierung mit ML Kit für iOS

ML Kit bietet ein optimiertes SDK für die Selfie-Segmentierung. Die Selfie Segmenter-Assets werden zur Build-Zeit statisch mit Ihrer App verknüpft. Dadurch erhöht sich die Größe Ihrer App um bis zu 24 MB und die API-Latenz kann je nach Größe des Eingabebilds zwischen etwa 7 ms und etwa 12 ms variieren (gemessen auf dem iPhone X).

Jetzt ausprobieren

  • Beispiel-App ausprobieren, um ein Beispiel für die Verwendung dieser API zu sehen.

Hinweis

  1. Fügen Sie die folgenden ML Kit-Bibliotheken in Ihre Podfile-Datei ein:

    pod 'GoogleMLKit/SegmentationSelfie', '8.0.0'
    
  2. Nachdem Sie die Pods Ihres Projekts installiert oder aktualisiert haben, öffnen Sie Ihr Xcode-Projekt mit der Datei .xcworkspace. ML Kit wird in Xcode-Version 13.2.1 oder höher unterstützt.

1. Segmenter-Instanz erstellen

Wenn Sie ein Selfie-Bild segmentieren möchten, erstellen Sie zuerst eine Instanz von Segmenter mit SelfieSegmenterOptions und geben Sie optional die Segmentierungseinstellungen an.

Optionen für Segmenter

Segmentierungsmodus

Die Segmenter kann in zwei Modi betrieben werden. Achten Sie darauf, dass Sie die Option auswählen, die Ihrem Anwendungsfall entspricht.

STREAM_MODE (default)

Dieser Modus ist für das Streamen von Frames aus Videos oder Kameras vorgesehen. In diesem Modus nutzt der Segmentierer Ergebnisse aus vorherigen Frames, um glattere Segmentierungsergebnisse zurückzugeben.

SINGLE_IMAGE_MODE (default)

Dieser Modus ist für einzelne Bilder gedacht, die nicht miteinander in Verbindung stehen. In diesem Modus verarbeitet der Segmentierer jedes Bild unabhängig, ohne Glättung über Frames hinweg.

Rohmaske für Größe aktivieren

Fordert den Segmentierer auf, die Rohgrößenmaske zurückzugeben, die der Größe der Modellausgabe entspricht.

Die Größe der Rohmaske (z.B. 256 × 256) ist in der Regel kleiner als die Größe des Eingabebilds.

Ohne diese Option skaliert der Segmentierer die Rohmaske so, dass sie der Größe des Eingabebilds entspricht. Verwenden Sie diese Option, wenn Sie eine benutzerdefinierte Reskalierungslogik anwenden möchten oder für Ihren Anwendungsfall keine Reskalierung erforderlich ist.

Geben Sie die Segmentierungsoptionen an:

Swift

let options = SelfieSegmenterOptions()
options.segmenterMode = .singleImage
options.shouldEnableRawSizeMask = true

Objective-C

MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init];
options.segmenterMode = MLKSegmenterModeSingleImage;
options.shouldEnableRawSizeMask = YES;

Rufen Sie schließlich eine Instanz von Segmenter ab. Übergeben Sie die von Ihnen angegebenen Optionen:

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. Eingabebild vorbereiten

Gehen Sie für jedes Bild oder jeden Videoframes so vor, um Selfies zu segmentieren. Wenn Sie den Streamingmodus aktiviert haben, müssen Sie VisionImage-Objekte aus CMSampleBuffers 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 dem UIImage. Achten Sie darauf, dass Sie die richtige .orientation angeben.

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    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 ab:

    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;
      }
    }
          
  • Erstellen Sie ein VisionImage-Objekt mit dem CMSampleBuffer-Objekt und der Ausrichtung:

    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. 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 die Segmentierung eines Selfie-Bilds synchron aus:

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.

So führen Sie die Segmentierung eines Selfie-Bilds asynchron durch:

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. Segmentierungsmaske abrufen

So erhalten Sie das Segmentierungsergebnis:

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);
}

Ein vollständiges Beispiel für die Verwendung der Segmentierungsergebnisse finden Sie im ML Kit-Kurzanleitungsbeispiel.

Tipps zur Leistungsverbesserung

Die Qualität der Ergebnisse hängt von der Qualität des Eingabebilds ab:

  • Damit ML Kit ein genaues Segmentierungsergebnis erzielen kann, sollte das Bild mindestens 256 × 256 Pixel groß sein.
  • Wenn Sie die Selfie-Segmentierung in einer Echtzeitanwendung durchfü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 niedrigeren Auflösungen aufnehmen. Beachten Sie jedoch die oben genannten Auflösungsanforderungen und sorgen Sie dafür, dass das Motiv so viel wie möglich vom Bild einnimmt.
  • Eine schlechte Bildschärfe kann sich ebenfalls auf die Genauigkeit auswirken. Wenn Sie keine akzeptablen Ergebnisse erhalten, bitten Sie den Nutzer, das Bild noch einmal aufzunehmen.

Wenn Sie die Segmentierung in einer Echtzeitanwendung verwenden möchten, sollten Sie die folgenden Richtlinien beachten, um die besten Framerates zu erzielen:

  • Verwenden Sie den Segmentierungsmodus stream.
  • Nehmen Sie Bilder mit einer niedrigeren Auflösung auf. Beachten Sie jedoch auch die Anforderungen an die Bildabmessungen für diese API.
  • Verwenden Sie für die Verarbeitung von Videoframes die synchrone results(in:) API des Segmentierers. Rufen Sie diese Methode aus der Funktion captureOutput(_, didOutput:from:) des AVCaptureVideoDataOutputSampleBufferDelegate auf, um synchron Ergebnisse aus dem angegebenen Videobild zu erhalten. Lassen Sie AVCaptureVideoDataOutput's alwaysDiscardsLateVideoFrames auf „true“, um Aufrufe des Segmentierers zu drosseln. Wenn ein neuer Videoframes verfügbar wird, während der Segmentierer ausgeführt wird, wird er verworfen.
  • Wenn Sie die Ausgabe des Segmentierers verwenden, um Grafiken auf das Eingabebild zu legen, rufen Sie zuerst das Ergebnis von ML Kit ab und rendern Sie dann das Bild und das Overlay in einem einzigen Schritt. Dadurch wird für jeden verarbeiteten Eingabe-Frame nur einmal auf die Displayoberfläche gerendert. Ein Beispiel finden Sie in den Klassen previewOverlayView und CameraViewController im ML Kit-Schnellstartbeispiel.