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 di iPhone X.

Cobalah

Sebelum memulai

  1. Sertakan library ML Kit berikut di Podfile Anda:

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

1. Membuat instance Segmenter

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

Opsi segmentasi

Mode Segmentasi

Segmenter beroperasi dalam dua mode. Pastikan Anda memilih salah satu yang cocok dengan kasus penggunaan Anda.

STREAM_MODE (default)

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

SINGLE_IMAGE_MODE (default)

Mode ini didesain untuk gambar tunggal yang tidak terkait. Dalam mode ini, Segmenter akan memproses setiap gambar secara independen, tanpa ada penyesuaian pada frame.

Aktifkan mask ukuran mentah

Meminta pengklasifikasi untuk menampilkan mask ukuran mentah yang sesuai dengan ukuran output model.

Ukuran mask mentah (misalnya, 256x256) biasanya lebih kecil dari ukuran gambar input.

Tanpa menentukan opsi ini, pengelompok 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 pembagi segmen:

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. Menyiapkan gambar input

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

Buat objek VisionImage menggunakan UIImage atau objek 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 penyamaran 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 tentang cara menggunakan hasil segmentasi, silakan lihat Contoh panduan memulai ML Kit.

Tips untuk meningkatkan performa

Kualitas hasil bergantung pada kualitas gambar input:

  • Agar ML Kit mendapatkan hasil segmentasi yang akurat, gambar harus berukuran minimal 256x256 piksel.
  • Jika Anda melakukan segmentasi selfie dalam aplikasi real-time, Anda mungkin juga ingin mempertimbangkan 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 berikut untuk mencapai kecepatan frame terbaik:

  • Gunakan mode pengelompok stream.
  • Sebaiknya ambil gambar dengan resolusi yang lebih rendah. Namun, perhatikan juga persyaratan dimensi gambar API ini.
  • Untuk memproses frame video, gunakan API sinkron results(in:) dari pembagi. Panggil metode ini dari fungsi captureOutput(_, didOutput:from:) dari AVCaptureVideoDataOutputSampleBufferDelegate untuk mendapatkan hasil dari frame video yang diberikan secara sinkron. Pertahankan setelan alwaysDiscardsLateVideoFrames dari AVCaptureVideoDataOutput sebagai benar untuk membatasi panggilan ke pemroses iklan. Jika frame video baru tersedia saat pemroses segmen 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 untuk mengetahui contohnya.