iOS'te ML Kiti ile selfie segmentasyonu

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

Deneyin

Başlamadan önce

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

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

1. Segmenter örneği oluşturma

Bir selfie resminde segmentasyon gerçekleştirmek için önce Segmenter ile SelfieSegmenterOptions örneğini 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, videodan veya kameradan kareler yayınlamak için tasarlanmıştır. Bu modda, segmentleyici daha sorunsuz segmentasyon sonuçları döndürmek için önceki karelerdeki sonuçlardan yararlanır.

SINGLE_IMAGE_MODE (default)

Bu mod, birbiriyle ilişkili olmayan tek resimler için tasarlanmıştır. Bu modda segmentleyici, her resmi bağımsız olarak işler ve kareler arasında yumuşatma yapmaz.

Ham boyut maskesini etkinleştirme

Segmenter'dan, model çıkış boyutuyla eşleşen ham boyut maskesini döndürmesini ister.

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

Bu seçenek belirtilmediğinde, segmentleyici 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.

Segmentleyici 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ğ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 görüntü veya video karesi için aşağıdakileri yapın. Akış modunu etkinleştirdiyseniz CMSampleBuffer öğelerinden VisionImage nesneleri oluşturmanız gerekir.

UIImage veya CMSampleBuffer kullanarak VisionImage nesnesi oluşturun.

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

  • UIImage ile VisionImage nesnesi oluşturun. Doğru .orientation 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.

    Resim yönünü almak 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 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'nin resim 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 segmentasyonu eşzamanlı olarak gerçekleştirmek 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 segmentasyonu eşzamansız olarak gerçekleştirmek 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 ş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ş resminin kalitesine bağlıdır:

  • ML Kit'in doğru bir segmentasyon sonucu elde etmesi için resim en az 256x256 piksel olmalıdır.
  • Gerçek zamanlı bir uygulamada özne segmentasyonu yapıyorsanız giriş resimlerinin genel boyutlarını da göz önünde bulundurmanız gerekebilir. Daha küçük resimler daha hızlı işlenebilir. Bu nedenle, gecikmeyi azaltmak için daha düşük çözünürlüklerde resim ç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 bölümünü kapladığından emin olun.
  • Resmin iyi odaklanmaması da doğruluğu etkileyebilir. Kabul edilebilir sonuçlar almazsanı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 segmentleyici modunu kullanın.
  • Görüntüleri daha düşük çözünürlükte çekmeyi 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:) senkron API'sini kullanın. Belirli bir video karesinden sonuçları senkron olarak almak için bu yöntemi AVCaptureVideoDataOutputSampleBufferDelegate'in captureOutput(_, didOutput:from:) işlevinden çağırın. Segmentleyiciye yapılan çağrıları sınırlamak için AVCaptureVideoDataOutput'un alwaysDiscardsLateVideoFrames özelliğini true olarak tutun. Segmentleyici çalışırken yeni bir video karesi kullanıma sunulursa bu kare bırakılır.
  • Giriş resmine grafik yerleştirmek için segmenter'ın çıkışını kullanıyorsanız önce ML Kit'ten sonucu alın, ardından resmi ve yerleşimi tek adımda oluşturun. Bunu yaparak, işlenen her giriş karesi için yalnızca bir kez görüntüleme yüzeyine 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.