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

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

امتحان کنید

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

قبل‌از شروع

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

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

۱. ایجاد نمونه‌ای از Segmenter

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

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

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

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

STREAM_MODE (default)

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

SINGLE_IMAGE_MODE (default)

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

فعال کردن ماسک اندازه خام

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

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

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

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

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 استفاده می‌کنید، این مراحل را دنبال کنید:

  • یک شیء VisionImage با UIImage ایجاد کن. حتماً .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;
      }
    }
          
  • ایجاد شیء VisionImage بااستفاده از شیء CMSampleBuffer و جهت:

    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:) بخش‌بندی‌کننده استفاده کنید. این روش را از تابع captureOutput(_, didOutput:from:) مربوط به AVCaptureVideoDataOutputSampleBufferDelegate فراخوانی کنید تا نتایج را به‌صورت هم‌زمان از فریم ویدیو داده‌شده دریافت کنید. alwaysDiscardsLateVideoFrames مربوط به AVCaptureVideoDataOutput را روی true (درست) نگه دارید تا فراخوانی‌ها به بخش‌بندی‌کننده محدود شود. اگر فریم ویدیو جدیدی درحین اجرای بخش‌بندی‌کننده دردسترس قرار گیرد، آن فریم حذف خواهد شد.
  • اگر از خروجی بخش‌بندی‌کننده برای هم‌پوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از ML Kit دریافت کنید، سپس تصویر و هم‌پوشانی را در یک مرحله رندر کنید. با انجام این کار، فقط یک بار برای هر فریم ورودی پردازش‌شده به سطح نمایشگر رندر می‌کنید. برای مشاهده نمونه، کلاس‌های previewOverlayView و CameraViewController را در نمونه شروع سریع ML Kit ببینید.