تقسیم‌بندی سلفی با کیت ML در iOS

ML Kit یک SDK بهینه‌سازی شده برای تقسیم‌بندی سلفی فراهم می‌کند. دارایی های Selfie Segmenter به صورت ایستا به برنامه شما در زمان ساخت مرتبط هستند. این اندازه برنامه شما را تا 24 مگابایت افزایش می‌دهد و تأخیر API می‌تواند بسته به اندازه تصویر ورودی، همانطور که در iPhone X اندازه‌گیری می‌شود، از 7 میلی‌ثانیه تا 12 میلی‌ثانیه متغیر باشد.

آن را امتحان کنید

قبل از اینکه شروع کنی

  1. کتابخانه های ML Kit زیر را در فایل پادفایل خود قرار دهید:

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

1. یک نمونه از Segmenter ایجاد کنید

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

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

حالت بخش بندی

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

STREAM_MODE (default)

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

SINGLE_IMAGE_MODE (default)

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

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

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

اندازه ماسک خام (به عنوان مثال 256x256) معمولاً کوچکتر از اندازه تصویر ورودی است.

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

گزینه های قطعه ساز را مشخص کنید:

سریع

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

هدف-C

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

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

سریع

let segmenter = Segmenter.segmenter(options: options)

هدف-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

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

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

با استفاده از UIImage یا CMSampleBuffer VisionImage .

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

  • با VisionImage یک شی UIImage کنید. مطمئن شوید که جهت .orientation را مشخص کرده اید.

    سریع

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

    هدف-C

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

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

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

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

    سریع

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

    هدف-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;
      }
    }
          
  • یک شی VisionImage با استفاده از شی CMSampleBuffer و جهت گیری ایجاد کنید:

    سریع

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

    هدف-C

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

3. تصویر را پردازش کنید

شی VisionImage را به یکی از روش های پردازش تصویر Segmenter ارسال کنید. می توانید از process(image:) یا روش results(in:) استفاده کنید.

برای انجام بخش بندی روی یک تصویر سلفی به طور همزمان:

سریع

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.

هدف-C

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

// Success. Get a segmentation mask here.

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

سریع

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

هدف-C

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

4. ماسک تقسیم بندی را دریافت کنید

شما می توانید نتیجه تقسیم بندی را به صورت زیر بدست آورید:

سریع

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
}

هدف-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);
}

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

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

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

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

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

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