Selfiesegmentierung mit ML Kit für iOS

<ph type="x-smartling-placeholder">

ML Kit bietet ein optimiertes SDK für die Segmentierung von Selfies. Die Assets des Selfie-Segmenters sind bei der Erstellung statisch mit Ihrer App verknüpft. Dadurch wird die Größe Ihrer App um bis zu 24 MB erhöht und die API-Latenz kann abhängig von der Größe des Eingabebildes (bei der Messung auf dem iPhone X) zwischen ~7 ms und 12 ms variieren.

Jetzt ausprobieren

  • Probieren Sie die Beispiel-App aus, um sehen Sie sich ein Anwendungsbeispiel für diese API an.

Hinweis

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

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

1. Instanz von Segmenter 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.

Segmentierungsoptionen

Segmentierungsmodus

Der Segmenter wird in zwei Modi ausgeführt. Wählen Sie die Option aus, die zu Ihrem Anwendungsfall passt.

STREAM_MODE (default)

Dieser Modus ist zum Streamen von Frames über ein Video oder eine Kamera vorgesehen. In diesem Modus verwendet der Segmentierungsoperator Ergebnisse aus vorherigen Frames, um flüssigere Segmentierungsergebnisse zurückzugeben.

SINGLE_IMAGE_MODE (default)

Dieser Modus ist für einzelne Bilder vorgesehen, die keinen Bezug haben. In diesem Modus verarbeitet der Segmenter jedes Bild unabhängig und ohne Glättung über die Frames.

Maske für Rohgröße aktivieren

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

Die Größe der Rohmaske (z.B. 256 x 256) ist normalerweise kleiner als die Größe des Eingabebilds.

Ohne Angabe dieser Option skaliert der Segmenter die Rohmaske neu, damit sie der Größe des eingegebenen Bilds entspricht. Verwenden Sie diese Option, wenn Sie eine benutzerdefinierte Neuskalierungslogik anwenden möchten oder für Ihren Anwendungsfall keine Neuskalierung 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;

Schließlich rufen Sie eine Instanz von Segmenter ab. Übergeben Sie die angegebenen Optionen:

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. Eingabebild vorbereiten

So segmentieren Sie Selfies für jedes Bild oder jeden Frame des Videos: Wenn Sie den Streammodus aktiviert haben, müssen Sie VisionImage-Objekte erstellen aus CMSampleBuffer.

Erstellen Sie ein VisionImage-Objekt mithilfe von UIImage oder einem CMSampleBuffer

Wenn du ein UIImage verwendest, gehe so vor:

  • Erstellen Sie ein VisionImage-Objekt mit der UIImage. Geben Sie die richtige .orientation an.

    Swift

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

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Wenn du ein CMSampleBuffer verwendest, gehe so vor:

  • Geben Sie die Ausrichtung der Bilddaten an, die in der CMSampleBuffer

    So ermitteln Sie die Bildausrichtung:

    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 mithilfe der CMSampleBuffer-Objekt und 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 segmentieren Sie ein Selfie synchron:

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 Selfies 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

Sie können das Segmentierungsergebnis so erhalten:

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 in der ML Kit-Schnellstartbeispiel.

Tipps zur Verbesserung der Leistung

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

  • Damit ML Kit ein genaues Segmentierungsergebnis erhalten kann, sollte das Bild mindestens 256 x 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, nehmen Sie Bilder mit einer niedrigeren Auflösung auf. Beachten Sie jedoch die oben genannten Anforderungen an die Auflösung und achten Sie darauf, dass die Person so viel wie möglich auf dem Bild einnimmt.
  • Ein schlechter Bildfokus kann auch die Genauigkeit beeinträchtigen. Wenn Sie keine akzeptablen Ergebnisse erhalten, bitten Sie den Nutzer, das Bild erneut aufzunehmen.

Wenn Sie die Segmentierung in einer Echtzeitanwendung verwenden möchten, beachten Sie die folgenden Richtlinien, 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 dieser API an die Bildabmessungen.
  • Verwende zur Verarbeitung von Videoframes die synchrone results(in:) API des Segmenters. Rufen Sie diese Methode aus der Funktion captureOutput(_, didOutput:from:) von AVCaptureVideoDataOutputSampleBufferDelegate auf, um synchron Ergebnisse aus dem angegebenen Videoframe abzurufen. Behalten Sie für AVCaptureVideoDataOutput die Einstellung alwaysDiscardsLateVideoFrames auf „true“ bei, um Aufrufe an den Segmenter zu drosseln. Wenn ein neuer Videoframe verfügbar wird, während die Segmentierung ausgeführt wird, wird dieser entfernt.
  • Wenn Sie die Ausgabe des Segmenters verwenden, um Grafiken über das Eingabebild zu blenden, rufen Sie zuerst das Ergebnis aus ML Kit ab und rendern Sie dann das Bild und das Overlay in einem Schritt. Auf diese Weise rendern Sie auf der Anzeigeoberfläche nur einmal für jeden verarbeiteten Eingabe-Frame. Ein Beispiel finden Sie in den Klassen previewOverlayView und CameraViewController im Beispiel für ML Kit-Kurzanleitung.