iOS'te ML Kiti ile selfie segmentasyonu

ML Kit, selfie segmentasyonu için optimize edilmiş bir SDK sunar. Selfie Segmenter öğeleri, derleme sırasında statik olarak uygulamanıza bağlıdır. Bu işlem, uygulamanızın boyutunu 24 MB'a kadar artırır. iPhone X'te yapılan ölçüme göre giriş görüntüsü boyutuna bağlı olarak API gecikmesi yaklaşık 7 ms ile ~12 ms. arasında değişebilir.

Deneyin

Başlamadan önce

  1. Aşağıdaki ML Kit kitaplıklarını Podfile dosyanıza ekleyin:

    pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
    
  2. Projenizin Kapsüllerini yükledikten veya güncelledikten sonra Xcode projenizi .xcworkspace ile açın. ML Kit, Xcode sürüm 13.2.1 veya üzeri sürümlerde desteklenir.

1. Segmenter örneği oluşturma

Selfie görüntüsü üzerinde segmentasyon yapmak için önce SelfieSegmenterOptions ile bir Segmenter örneği oluşturun ve isteğe bağlı olarak segmentasyon ayarlarını belirtin.

Segmenter seçenekleri

Segmenter Modu

Segmenter iki modda çalışır. Kullanım alanınıza uygun olanı seçtiğinizden emin olun.

STREAM_MODE (default)

Bu mod, video veya kameradan kare akışı gerçekleştirmek için tasarlanmıştır. Segmenter, bu modda daha düzgün segmentasyon sonuçları döndürmek için önceki karelerden elde edilen sonuçlardan yararlanır.

SINGLE_IMAGE_MODE (default)

Bu mod, birbiriyle alakalı olmayan tek resimler için tasarlanmıştır. Bu modda segmentleyici, kareler üzerinde yumuşatma olmadan her resmi bağımsız olarak işler.

Ham boyut maskesini etkinleştir

Segmentleyiciden, model çıktı boyutuyla eşleşen ham boyut maskesini döndürmesini ister.

İşlenmemiş maske boyutu (ör. 256x256) genellikle giriş resmi boyutundan daha küçüktür.

Bu seçeneği belirtmezseniz segmentleyici, ham maskeyi giriş resmi boyutuyla eşleşecek şekilde yeniden ölçeklendirir. Özelleştirilmiş yeniden ölçeklendirme mantığı uygulamak istiyorsanız veya kullanım alanınız için yeniden ölçeklendirme gerekmiyorsa bu seçeneği kullanabilirsiniz.

Segmenter seçeneklerini belirtin:

Swift

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

Objective-C

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

Son olarak, Segmenter örneği alın. Belirttiğiniz seçenekleri iletin:

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. Giriş görüntüsünü hazırlama

Selfie'leri segmentlere ayırmak için her bir resim veya video karesinde aşağıdakileri yapın. Akış modunu etkinleştirdiyseniz CMSampleBuffer kaynağından VisionImage nesne oluşturmanız gerekir.

UIImage veya CMSampleBuffer kullanarak VisionImage nesnesi oluşturun.

UIImage kullanıyorsanız şu adımları uygulayın:

  • UIImage ile bir VisionImage nesnesi oluşturun. Doğru .orientation değerini belirttiğinizden emin olun.

    Swift

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

    Objective-C

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

CMSampleBuffer kullanıyorsanız şu adımları uygulayın:

  • CMSampleBuffer içinde yer alan resim verilerinin yönünü belirtin.

    Resmin yönünü belirlemek için:

    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;
      }
    }
          
  • CMSampleBuffer nesnesini ve yönünü kullanarak bir VisionImage nesnesi oluşturun:

    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. Resmi işle

VisionImage nesnesini, Segmenter öğesinin resim işleme yöntemlerinden birine geçirin. Eşzamansız process(image:) yöntemini veya eşzamanlı results(in:) yöntemini kullanabilirsiniz.

Bir selfie görüntüsü üzerinde eşzamanlı olarak segmentasyon yapmak iç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.

Bir selfie görüntüsü üzerinde eşzamansız olarak segmentasyon yapmak için:

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. Segmentasyon maskesini alın

Segmentasyon sonucunu aşağıdaki şekilde alabilirsiniz:

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

Segmentasyon sonuçlarının nasıl kullanılacağına dair tam bir örnek için lütfen ML Kit hızlı başlangıç örneğine bakın.

Performansı artırmaya yönelik ipuçları

Sonuçlarınızın kalitesi, giriş görüntüsünün kalitesine bağlıdır:

  • ML Kit'in doğru bir segmentasyon sonucu alabilmesi için görüntü en az 256x256 piksel olmalıdır.
  • Selfie segmentasyonunu gerçek zamanlı bir uygulamada gerçekleştiriyorsanız, giriş resimlerinin genel boyutlarını da göz önünde bulundurmak isteyebilirsiniz. Küçük resimler daha hızlı işlenebilir, bu nedenle gecikmeyi azaltmak için düşük çözünürlüklerde görüntü yakalayın ancak yukarıdaki çözünürlük gereksinimlerini göz önünde bulundurun ve öznenin, resmi olabildiğince çok kapladığından emin olun.
  • Kötü resim odağı, doğruluğu da etkileyebilir. Kabul edilebilir sonuçlar alamazsanız kullanıcıdan resmi yeniden çekmesini isteyin.

Segmentasyonu gerçek zamanlı bir uygulamada kullanmak istiyorsanız en iyi kare hızlarını elde etmek için şu yönergeleri uygulayın:

  • stream segmenter modunu kullanın.
  • Daha düşük çözünürlükte resimler çekmeyi deneyin. Ancak, bu API'nin resim boyutu koşullarını da göz önünde bulundurun.
  • Video karelerini işlemek için segmentleyicinin results(in:) eşzamanlı API'sini kullanın. Belirli bir video karesinden eşzamanlı olarak sonuçlar almak için AVCaptureVideoDataOutputSampleBufferDelegate'in captureOutput(_, didOutput:from:) işlevinden bu yöntemi çağırın. Segmente çağrılarını kısıtlamak için AVCaptureVideoDataOutput'ın alwaysDiscardsLateVideoFrames öğelerini doğru tutun. Segmenter çalışırken yeni bir video karesi kullanılabilir hale gelirse atlanır.
  • Segmentörün çıktısını giriş görüntüsündeki grafikleri bindirmek için kullanıyorsanız önce ML Kit'ten sonucu alın, ardından görüntüyü ve bindirmeyi tek bir adımda oluşturun. Böylece, işlenen her giriş çerçevesi için görüntü yüzeyinde yalnızca bir kez görüntü oluşturursunuz. Örnek için ML Kit hızlı başlangıç örneğindeki previewOverlayView ve CameraViewController sınıflarına bakın.