Segmentazione dei selfie con ML Kit su iOS

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

ML Kit fornisce un SDK ottimizzato per la segmentazione dei selfie. Gli asset di Selfie Segmenter sono collegati in modo statico alla tua app al momento della creazione. Questo aumenterà le dimensioni della tua app fino a 24 MB e la latenza dell'API può variare da ~7 ms a ~12 ms a seconda delle dimensioni dell'immagine di input, misurate su iPhone X.

Prima di iniziare

  1. Includi le seguenti librerie di ML Kit nel tuo podfile:

    pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
    
  2. Dopo aver installato o aggiornato i pod del progetto, apri il progetto Xcode utilizzando l'estensione .xcworkspace. ML Kit è supportato in Xcode versione 13.2.1 o successive.

1. Crea un'istanza di Segmenter

Per eseguire la segmentazione su un'immagine selfie, per prima cosa crea un'istanza di Segmenter con SelfieSegmenterOptions e, se vuoi, specifica le impostazioni di segmentazione.

Opzioni per la segmentazione

Modalità Segmenter

Segmenter funziona in due modalità. Assicurati di scegliere quello più adatto al tuo caso d'uso.

STREAM_MODE (default)

Questa modalità è progettata per lo streaming di fotogrammi da video o videocamere. In questa modalità, il segmentatore utilizzerà i risultati dei frame precedenti per restituire risultati di segmentazione più fluidi.

SINGLE_IMAGE_MODE (default)

Questa modalità è progettata per immagini singole non correlate. In questa modalità, il segmentatore elaborerà ogni immagine in modo indipendente, senza alcun livellamento sui frame.

Attiva maschera delle dimensioni non elaborate

Chiedi al segmenter di restituire la maschera delle dimensioni non elaborate che corrisponde alle dimensioni di output del modello.

La dimensione della maschera non elaborata (ad es. 256 x 256) è generalmente inferiore alle dimensioni dell'immagine di input.

Senza specificare questa opzione, il segmento ridimensiona la maschera non elaborata per adattarsi alle dimensioni dell'immagine di input. Valuta la possibilità di utilizzare questa opzione se vuoi applicare una logica di scalabilità personalizzata o se non è necessario ripetere il processo per il tuo caso d'uso.

Specifica le opzioni di segmentazione:

Swift

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

Objective-C

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

Infine, ottieni un'istanza di Segmenter. Passa le opzioni specificate:

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. Prepara l'immagine di input

Per segmentare i selfie, segui le istruzioni riportate di seguito per ogni immagine o frame di video. Se hai abilitato la modalità flusso, devi creare oggetti VisionImage da CMSampleBuffer s.

Crea un oggetto VisionImage utilizzando un UIImage o un CMSampleBuffer.

Se utilizzi un UIImage, segui questi passaggi:

  • Crea un oggetto VisionImage con il UIImage. Assicurati di specificare il .orientation corretto.

    Swift

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

    Objective-C

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

Se utilizzi un CMSampleBuffer, segui questi passaggi:

  • Specifica l'orientamento dei dati dell'immagine contenuti in CMSampleBuffer.

    Per ottenere l'orientamento dell'immagine:

    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;
      }
    }
          
  • Crea un oggetto VisionImage utilizzando l'oggetto e l'orientamento 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. Elabora immagine

Passa l'oggetto VisionImage a uno dei metodi di elaborazione delle immagini di Segmenter. Puoi utilizzare il metodo process(image:) asincrono o il metodo sincrono di results(in:).

Per eseguire in modo sincrono la segmentazione di un'immagine di un selfie:

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.

Per eseguire la segmentazione su un'immagine selfie in modo asincrono:

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. Ottieni la maschera di segmentazione

Puoi ottenere il risultato della segmentazione nel seguente modo:

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

Per un esempio completo di come utilizzare i risultati della segmentazione, consulta l'esempio di guida rapida di ML Kit.

Suggerimenti per migliorare il rendimento

La qualità dei risultati dipende dalla qualità dell'immagine di input:

  • Affinché ML Kit possa ottenere un risultato di segmentazione accurato, l'immagine deve essere di almeno 256x256 pixel.
  • Se esegui la segmentazione dei selfie in un'applicazione in tempo reale, puoi anche prendere in considerazione le dimensioni complessive delle immagini di input. Le immagini più piccole possono essere elaborate più rapidamente, quindi per ridurre la latenza, acquisisci immagini a risoluzioni inferiori, ma tieni presente i requisiti di risoluzione riportati sopra e assicurati che il soggetto occupi la maggiore quantità possibile di immagini.
  • Una messa a fuoco dell'immagine scadente può anche influire sulla precisione. Se non ottieni risultati accettabili, chiedi all'utente di riprendere l'immagine.

Se vuoi utilizzare la segmentazione in un'applicazione in tempo reale, segui queste linee guida per ottenere le migliori frequenze di fotogrammi:

  • Utilizza la modalità di segmentazione stream.
  • Potresti acquisire immagini a una risoluzione inferiore. Tuttavia, tieni presente i requisiti per le dimensioni delle immagini di questa API.
  • Per l'elaborazione dei frame video, utilizza l'API sincrona results(in:) dell'utilità di segmentazione. Chiama questo metodo dalla funzione AV CaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, DidOutput:from:) per ottenere in modo sincrono i risultati dal frame video specificato. Mantieni AiKaLaLatVideo sempre attivo di AV CaptureVideoDataOutput per limitare le chiamate al segmenter. Se è disponibile un nuovo frame video mentre il segmento è in esecuzione, verrà eliminato.
  • Se utilizzi l'output del segmentatore per sovrapporre la grafica all'immagine di input, ottieni innanzitutto il risultato di ML Kit, quindi visualizza l'immagine e l'overlay in un solo passaggio. In questo modo, il rendering viene visualizzato nella superficie di visualizzazione solo una volta per ogni frame di input elaborato. Per un esempio, consulta le classi previewOverlayView e CameraViewController nell'esempio di guida rapida di ML Kit.