iOS'te ML Kiti ile selfie segmentasyonu

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

Deneyin

Başlamadan önce

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

    pod 'GoogleMLKit/SegmentationSelfie', '8.0.0'
    
  2. Projenizin Pod'larını yükledikten veya güncelledikten sonra .xcworkspace kullanarak Xcode projenizi açın. ML Kiti, Xcode 13.2.1 veya sonraki sürümlerde desteklenir.

1. Segmenter örneği oluşturma

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

Segmentasyon aracı 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ışı yapmak için tasarlanmıştır. Bu modda segmenter, daha akıcı segmentasyon sonuçları döndürmek için önceki karelerden elde edilen sonuçlardan yararlanır.

SINGLE_IMAGE_MODE (default)

Bu mod, birbiriyle ilişkili olmayan tekli resimler için tasarlanmıştır. Bu modda segmenter, her resmi bağımsız olarak işler ve kareler üzerinde yumuşatma yapmaz.

Ham boyut maskesini etkinleştirme

Segmentasyon aracından, model çıkış boyutuyla eşleşen ham boyut maskesini döndürmesini ister.

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

Bu seçenek belirtilmezse segmenter, ham maskeyi giriş resmi boyutuna uyacak ş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 resim veya video karesinde aşağıdaki işlemleri yapın. Akış modunu etkinleştirdiyseniz CMSampleBuffer'lerden VisionImage nesneleri oluşturmanız gerekir.

UIImage veya CMSampleBuffer kullanarak bir VisionImage nesnesi oluşturun.

UIImage kullanıyorsanız aşağıdaki 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 aşağıdaki adımları uygulayın:

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

    Resim 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;
      }
    }
          
  • CMSampleBuffer nesnesini ve yö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şleme

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

Selfie resminde 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.

Selfie resminde 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 Kiti hızlı başlangıç örneğine bakın.

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

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

  • ML Kit'in doğru bir segmentasyon sonucu elde edebilmesi için resmin en az 256x256 piksel olması gerekir.
  • Selfie segmentasyonunu gerçek zamanlı bir uygulamada gerçekleştiriyorsanız giriş resimlerinin genel boyutlarını da göz önünde bulundurmanız önerilir. Daha küçük resimler daha hızlı işlenebilir. Bu nedenle, gecikmeyi azaltmak için resimleri daha düşük çözünürlüklerde çekin ancak yukarıdaki çözünürlük şartlarını göz önünde bulundurun ve öznenin resmin mümkün olduğunca büyük bir kısmını kapladığından emin olun.
  • Resim odağının kötü olması da doğruluğu 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 aşağıdaki yönergeleri uygulayın:

  • stream segmenter modunu kullanın.
  • Görüntüleri daha düşük çözünürlükte yakalamayı deneyin. Ancak bu API'nin resim boyutu koşullarını da göz önünde bulundurun.
  • Video karelerini işlemek için segmenter'ın results(in:) eşzamanlı API'sini kullanın. Belirli bir video karesinden eşzamanlı olarak sonuç almak için bu yöntemi AVCaptureVideoDataOutputSampleBufferDelegate'in captureOutput(_, didOutput:from:) işlevinden çağırın. Segmenter çağrılarını kısıtlamak için AVCaptureVideoDataOutput'un alwaysDiscardsLateVideoFrames değerini doğru olarak ayarlayın. Segmenter çalışırken yeni bir video karesi kullanılabilir hale gelirse bu kare silinir.
  • Segmentasyon aracının çıkışını, giriş görüntüsünün üzerine grafik yerleştirmek için kullanıyorsanız önce ML Kit'ten sonucu alın, ardından görüntüyü ve yer paylaşımını tek bir adımda oluşturun. Böylece, işlenen her giriş karesi için görüntüleme yüzeyine yalnızca bir kez oluşturma işlemi yaparsınız. Örnek için ML Kit hızlı başlangıç örneğindeki previewOverlayView ve CameraViewController sınıflarına bakın.