Segmentasi selfie dengan ML Kit di iOS

ML Kit menyediakan SDK yang dioptimalkan untuk segmentasi selfie. Aset Selfie Segmenter ditautkan secara statis ke aplikasi Anda pada waktu build. Ini akan meningkatkan ukuran aplikasi Anda hingga 24 MB dan latensi API dapat bervariasi dari ~7 md hingga ~12 md bergantung pada ukuran gambar input, seperti yang diukur pada iPhone X.

Cobalah

Sebelum memulai

  1. Sertakan library ML Kit berikut di Podfile Anda:

    pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
    
  2. Setelah menginstal atau mengupdate Pod project, buka project Xcode menggunakan file .xcworkspace. ML Kit didukung di Xcode versi 13.2.1 atau yang lebih baru.

1. Membuat instance Segmenter

Untuk melakukan segmentasi pada gambar selfie, pertama-tama buat instance Segmenter dengan SelfieSegmenterOptions dan secara opsional tentukan setelan segmentasi.

Opsi segmentasi

Mode Segmentasi

Segmenter beroperasi dalam dua mode. Pastikan Anda memilih yang sesuai dengan kasus penggunaan Anda.

STREAM_MODE (default)

Mode ini dirancang untuk streaming frame dari video atau kamera. Dalam mode ini, segmener akan memanfaatkan hasil dari frame sebelumnya untuk menampilkan hasil segmentasi yang lebih halus.

SINGLE_IMAGE_MODE (default)

Mode ini didesain untuk gambar tunggal yang tidak terkait. Dalam mode ini, segmener akan memproses setiap gambar secara independen, tanpa memperhalus frame.

Aktifkan mask ukuran mentah

Meminta segmener untuk menampilkan mask ukuran mentah yang cocok dengan ukuran output model.

Ukuran mask mentah (mis. 256x256) biasanya lebih kecil daripada ukuran gambar input.

Tanpa menentukan opsi ini, segmener akan menskalakan ulang mask mentah agar sesuai dengan ukuran gambar input. Pertimbangkan untuk menggunakan opsi ini jika Anda ingin menerapkan logika penskalaan ulang yang disesuaikan atau penskalaan ulang tidak diperlukan untuk kasus penggunaan Anda.

Tentukan opsi segmentasi:

Swift

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

Objective-C

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

Terakhir, dapatkan instance Segmenter. Teruskan opsi yang Anda tentukan:

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. Siapkan gambar input

Untuk menyegmentasikan selfie, lakukan hal berikut untuk setiap gambar atau frame video. Jika mengaktifkan mode streaming, Anda harus membuat objek VisionImage dari CMSampleBuffer.

Buat objek VisionImage menggunakan UIImage atau CMSampleBuffer.

Jika Anda menggunakan UIImage, ikuti langkah-langkah berikut:

  • Buat objek VisionImage dengan UIImage. Pastikan untuk menentukan .orientation yang benar.

    Swift

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

    Objective-C

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

Jika Anda menggunakan CMSampleBuffer, ikuti langkah-langkah berikut:

  • Tentukan orientasi data gambar yang terdapat dalam CMSampleBuffer.

    Untuk mendapatkan orientasi gambar:

    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;
      }
    }
          
  • Buat objek VisionImage menggunakan objek dan orientasi 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. Memproses gambar

Teruskan objek VisionImage ke salah satu metode pemrosesan gambar Segmenter. Anda dapat menggunakan metode process(image:) asinkron atau metode results(in:) sinkron.

Untuk melakukan segmentasi pada gambar selfie secara sinkron:

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.

Untuk melakukan segmentasi pada gambar selfie secara asinkron:

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. Dapatkan mask segmentasi

Anda bisa mendapatkan hasil segmentasi sebagai berikut:

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

Untuk contoh lengkap cara menggunakan hasil segmentasi, lihat contoh panduan memulai ML Kit.

Tips untuk meningkatkan performa

Kualitas hasil bergantung pada kualitas gambar input:

  • Agar ML Kit bisa mendapatkan hasil segmentasi yang akurat, gambar harus berukuran minimal 256x256 piksel.
  • Jika Anda melakukan segmentasi selfie dalam aplikasi real-time, sebaiknya pertimbangkan juga dimensi keseluruhan gambar input. Gambar yang lebih kecil dapat diproses lebih cepat. Jadi, untuk mengurangi latensi, ambil gambar dengan resolusi lebih rendah. Namun, perhatikan persyaratan resolusi di atas dan pastikan subjek menempati gambar sebanyak mungkin.
  • Fokus gambar yang buruk juga dapat memengaruhi akurasi. Jika Anda tidak mendapatkan hasil yang dapat diterima, minta pengguna untuk mengambil ulang gambar.

Jika Anda ingin menggunakan segmentasi dalam aplikasi real-time, ikuti panduan ini untuk mencapai kecepatan frame terbaik:

  • Gunakan mode segmener stream.
  • Pertimbangkan untuk mengambil gambar dengan resolusi lebih rendah. Namun, perhatikan juga persyaratan dimensi gambar API ini.
  • Untuk memproses frame video, gunakan API sinkron results(in:) dari segmener. Panggil metode ini dari fungsi captureOutput(_, didOutput:from:) AVCaptureVideoDataOutputSampleBufferDelegate untuk mendapatkan hasil dari frame video tertentu secara sinkron. Pertahankan setelan alwaysDiscardsLateVideoFrames pada AVCaptureVideoDataOutput untuk membatasi panggilan ke Segmenter. Jika frame video baru tersedia saat segmener sedang berjalan, frame tersebut akan dihapus.
  • Jika Anda menggunakan output dari segmener untuk menempatkan grafis pada gambar input, pertama-tama dapatkan hasilnya dari ML Kit, lalu render gambar dan tempatkan grafis dalam satu langkah. Dengan demikian, Anda hanya merender ke permukaan tampilan sekali untuk setiap frame input yang diproses. Lihat class previewOverlayView dan CameraViewController dalam contoh panduan memulai ML Kit sebagai contoh.