بخش‌بندی سلفی با ML Kit در iOS

‫ML Kit یک کیت توسعه نرم‌افزار بهینه‌سازی‌شده برای بخش‌بندی سلفی ارائه می‌دهد. دارایی‌های «بخش‌بندی سلفی» در زمان ساخت به‌صورت ایستا به برنامه شما پیوند داده می‌شوند. این کار اندازه برنامه شما را تا ۲۴ مگابایت افزایش می‌دهد و تأخیر در میانای برنامه‌سازی کاربردی می‌تواند بسته به اندازه تصویر ورودی از حدود ۷ میلی‌ثانیه تا حدود ۱۲ میلی‌ثانیه متغیر باشد، همان‌گونه که در iPhone X اندازه‌گیری شده است.

امتحان کنید

  • با برنامه نمونه بازی کنید تا نمونه‌ای از استفاده از این API را ببینید.

قبل‌از شروع

  1. کتابخانه‌های ML Kit زیر را در Podfile خود اضافه کنید:

    pod 'GoogleMLKit/SegmentationSelfie', '8.0.0'
    
  2. پس‌از نصب یا به‌روزرسانی Pods پروژه، پروژه Xcode را بااستفاده از .xcworkspace آن باز کنید. ML Kit در Xcode نسخه ۱۳.۲.۱ یا بالاتر پشتیبانی می‌شود.

۱. ایجاد نمونه‌ای از «بخش‌بندی‌کننده»

برای انجام تقسیم‌بندی روی تصویر سلفی، ابتدا نمونه‌ای از Segmenter با SelfieSegmenterOptions ایجاد کنید و درصورت تمایل تنظیمات تقسیم‌بندی را مشخص کنید.

گزینه‌های بخش‌بندی

حالت بخش‌بندی

‫Segmenter در دو حالت کار می‌کند. حتماً موردی را انتخاب کنید که با مورد استفاده شما مطابقت دارد.

STREAM_MODE (default)

این حالت برای جاری‌سازی قاب‌های ویدیو یا دوربین طراحی شده است. در این حالت، بخش‌بندی‌کننده از نتایج فریم‌های قبلی برای برگرداندن نتایج بخش‌بندی هموارتر استفاده می‌کند.

SINGLE_IMAGE_MODE (default)

این حالت برای تصاویر تکی که به هم مرتبط نیستند طراحی شده است. در این حالت، بخش‌بندی‌کننده هر تصویر را به‌طور مستقل پردازش می‌کند و هیچ هموارسازی‌ای روی قاب‌ها انجام نمی‌شود.

فعال کردن پوشش اندازه خام

از بخش‌بندی‌کننده می‌خواهد که ماسک اندازه خام را که با اندازه برونداد مدل مطابقت دارد برگرداند.

اندازه ماسک خام (مثلاً ۲۵۶x۲۵۶) معمولاً کوچک‌تر از اندازه تصویر ورودی است.

بدون مشخص کردن این گزینه، بخش‌بندی‌کننده ماسک خام را تغییر مقیاس می‌دهد تا با اندازه تصویر ورودی مطابقت داشته باشد. اگر می‌خواهید منطق تغییر مقیاس سفارشی‌شده اعمال کنید یا تغییر مقیاس برای مورد استفاده شما لازم نیست، از این گزینه استفاده کنید.

گزینه‌های بخش‌بندی را مشخص کنید:

Swift

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

آبجکتیو-سی

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

در آخر، نمونه‌ای از Segmenter را دریافت کنید. گزینه‌هایی را که مشخص کرده‌اید ارسال کنید:

Swift

let segmenter = Segmenter.segmenter(options: options)

آبجکتیو-سی

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

۲. آماده کردن تصویر ورودی

برای بخش‌بندی کردن سلفی‌ها، برای هر تصویر یا قاب ویدیو، مراحل زیر را انجام دهید. اگر حالت جاری‌سازی را فعال کرده‌اید، باید VisionImage شیء از CMSampleBuffer ایجاد کنید.

بااستفاده از UIImage یا CMSampleBuffer، یک شیء VisionImage ایجاد کنید.

اگر از UIImage استفاده می‌کنید، این مراحل را دنبال کنید:

  • با UIImage، شیء VisionImage ایجاد کنید. حتماً .orientation صحیح را مشخص کنید.

    Swift

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

    آبجکتیو-سی

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

اگر از CMSampleBuffer استفاده می‌کنید، این مراحل را دنبال کنید:

  • جهت داده‌های تصویر موجود در CMSampleBuffer را مشخص کنید.

    برای دریافت جهت تصویر:

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

    آبجکتیو-سی

    - (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 و جهت‌گیری، شیء VisionImage ایجاد کنید:

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    آبجکتیو-سی

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

۳. پردازش تصویر

شیء VisionImage را به یکی از روش‌های پردازش تصویر Segmenter منتقل کنید. می‌توانید از روش ناهمزمان process(image:) یا روش همزمان results(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.

آبجکتیو-سی

NSError *error;
MLKSegmentationMask *mask =
    [segmenter resultsInImage:image error:&error];
if (error != nil) {
  // Error.
  return;
}

// Success. Get a segmentation mask here.

برای انجام دادن تقسیم‌بندی روی تصویر سلفی به‌صورت ناهمزمان:

Swift

segmenter.process(image) { mask, error in
  guard error == nil else {
    // Error.
    return
  }
  // Success. Get a segmentation mask here.

آبجکتیو-سی

[segmenter processImage:image
             completion:^(MLKSegmentationMask * _Nullable mask,
                          NSError * _Nullable error) {
               if (error != nil) {
                 // Error.
                 return;
               }
               // Success. Get a segmentation mask here.
             }];

۴. دریافت ماسک بخش‌بندی

می‌توانید نتیجه تقسیم‌بندی را به‌صورت زیر دریافت کنید:

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
}

آبجکتیو-سی

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

برای مشاهده نمونه کامل نحوه استفاده از نتایج تقسیم‌بندی، لطفاً نمونه راه‌اندازی سریع ML Kit را ببینید.

نکته‌هایی برای بهبود عملکرد

کیفیت نتایج شما به کیفیت تصویر ورودی بستگی دارد:

  • برای اینکه ML Kit نتیجه بخش‌بندی دقیقی دریافت کند، تصویر باید حداقل ۲۵۶×۲۵۶ پیکسل باشد.
  • اگر بخش‌بندی سلفی را در برنامه‌ای هم‌زمان انجام می‌دهید، شاید بخواهید ابعاد کلی تصاویر ورودی را نیز درنظر بگیرید. تصویرهای کوچک‌تر را می‌توان سریع‌تر پردازش کرد، بنابراین برای کاهش تأخیر، تصویرها را با وضوح پایین‌تر ضبط کنید، اما الزامات وضوح بالا را درنظر داشته باشید و مطمئن شوید که سوژه تا حد امکان بخش زیادی از تصویر را اشغال کند.
  • فوکوس ضعیف تصویر نیز می‌تواند بر دقت تأثیر بگذارد. اگر نتایج قابل‌قبول نبود، از کاربر بخواهید تصویر را دوباره بگیرد.

اگر می‌خواهید از تقسیم‌بندی در یک برنامه هم‌زمان استفاده کنید، این دستورالعمل‌ها را برای دستیابی به بهترین نرخ فریم دنبال کنید:

  • از حالت بخش‌بندی stream استفاده کنید.
  • تصاویر را با وضوح پایین‌تری بگیرید. بااین‌حال، الزامات ابعاد تصویر این API را نیز درنظر داشته باشید.
  • برای پردازش قاب‌های ویدیو، از results(in:) هم‌زمان میانای برنامه‌سازی کاربردی بخش‌بندی‌کننده استفاده کنید. این روش را از تابع AVCaptureVideoDataOutputSampleBufferDelegate's captureOutput(_, didOutput:from:) فراخوانی کنید تا نتایج را به‌صورت هم‌زمان از قاب ویدیو داده‌شده دریافت کنید. AVCaptureVideoDataOutput را در alwaysDiscardsLateVideoFrames روی درست نگه دارید تا تماس‌های بخش‌بندی‌کننده را محدود کنید. اگر فریم ویدیو جدیدی درحین اجرای بخش‌بندی‌کننده دردسترس قرار گیرد، حذف خواهد شد.
  • اگر از خروجی بخش‌بندی‌کننده برای هم‌پوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از ML Kit دریافت کنید، سپس تصویر و هم‌پوشانی را در یک مرحله ارائه دهید. با انجام این کار، برای هر قاب ورودی پردازش‌شده، فقط یک‌بار به سطح نمایشگر رندر می‌کنید. برای نمونه، کلاس‌های previewOverlayView و CameraViewController را در نمونه آغاز سریع ML Kit ببینید.