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 uygulamanıza statik olarak bağlıdır. Bu işlem, uygulamanızın boyutunu 24 MB'a kadar artırır. iPhone X'te yapılan ölçümlere göre, giriş resminin boyutuna bağlı olarak API gecikmesi ~7 ms. ile ~12 ms. arasında değişebilir.

Deneyin

Başlamadan önce

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

    pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
    
  2. Projenizin kapsüllerini yükledikten veya güncelledikten sonra .xcworkspace uzantısını kullanarak Xcode projenizi açın. Makine Öğrenimi Kiti, Xcode 13.2.1 veya üzeri sürümlerde desteklenir.

1. Segmenter örneği oluşturma

Bir selfie görüntüsünde segmentasyon yapmak için önce SelfieSegmenterOptions ile 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 seçeneği seçtiğinizden emin olun.

STREAM_MODE (default)

Bu mod, video veya kameradan kare akışı gerçekleştirmek için tasarlanmıştır. Bu modda segmenter, daha yumuşak segmentasyon sonuçları döndürmek için önceki karelerin sonuçlarından yararlanır.

SINGLE_IMAGE_MODE (default)

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

Ham boyut maskesini etkinleştir

Segmenterden, model çıkış boyutuyla eşleşen işlenmemiş boyut maskesini döndürmesini ister.

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

Bu seçenek belirtilmediğinde, 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 öğesinin bir örneğini alın. Belirttiğiniz seçenekleri iletin:

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. Giriş resmini hazırlama

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

Bir VisionImage nesnesi oluşturmak için UIImage veya CMSampleBuffer.

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:

  • Belgenin CMSampleBuffer

    Resmin yönünü öğrenmek 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;
      }
    }
          
  • Şu komutu kullanarak bir VisionImage nesnesi oluşturun: CMSampleBuffer nesne ve yön:

    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şleyin

VisionImage nesnesini, Segmenter öğesinin görüntü işleme yöntemlerinden birine iletin. Eşzamansız process(image:) yöntemini veya eşzamanlı results(in:) yöntemini kullanabilirsiniz.

Bir selfie görüntüsünde 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ünde 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 alma

Segmentasyon sonucunu aşağıdaki gibi 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ği.

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

Sonuçlarınızın kalitesi, giriş resminin kalitesine bağlıdır:

  • Makine Öğrenimi Kiti'nin doğru bir segmentasyon sonucu alabilmesi için görüntünün en az 256x256 piksel boyutunda olması gerekir.
  • Gerçek zamanlı bir uygulamada selfie segmentasyonu yapıyorsanız, giriş görüntülerinin genel boyutlarını da göz önünde bulundurmak isteyebilirsiniz. Daha küçük resimler daha hızlı işlenebilir. Bu nedenle, gecikmeyi azaltmak için düşük çözünürlüklerde fotoğraf çekin. Ancak yukarıdaki çözünürlük gereksinimlerini göz önünde bulundurun ve objenin resmin olabildiğince büyük bir kısmını kapladığından emin olun.
  • Kötü bir resim odağı, doğruluğu da etkileyebilir. Kabul edilebilir sonuçlar almıyorsanız kullanıcıdan resmi yeniden çekmesini isteyin.

Segmentasyonu gerçek zamanlı bir uygulamada kullanmak istiyorsanız en iyi kare hızlarına ulaşmak için aşağıdaki yönergeleri uygulayın:

  • stream segmentleyici modunu kullanın.
  • Görüntüleri daha düşük çözünürlükte çekmeyi düşünün. Ancak bu API'nin resim boyutu şartlarını da göz önünde bulundurun.
  • Video karelerini işlemek için segmenterin results(in:) eşzamanlı API'sini kullanın. Belirli bir video karesinden sonuçları eşzamanlı olarak almak için AVCaptureVideoDataOutputSampleBufferDelegate'in captureOutput(_, didOutput:from:) fonksiyonundan bu yöntemi çağırın. Segmentere yapılan çağrıları kısmak için AVCaptureVideoDataOutput'ın alwaysDiscardsLateVideoFrames değerini doğru olarak bırakın. Segmenter çalışırken yeni bir video karesi kullanılabilir hale gelirse segment atlanır.
  • Giriş görüntüsünün üzerine grafik yerleştirmek için segmenterin çıkışını kullanırsanız önce ML Kit'ten sonucu alın, ardından tek bir adımda görüntüyü oluşturun ve bindirme kullanın. Böylece, işlenen her giriş karesi için görüntü yüzeyinde yalnızca bir kez oluşturma yaparsınız. Örnek için ML Kit hızlı başlangıç örneğindeki previewOverlayView ve CameraViewController sınıflarına bakın.