برچسب گذاری تصاویر با یک مدل سفارشی در iOS

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

دو راه برای ادغام یک مدل سفارشی وجود دارد. می‌توانید مدل را با قرار دادن آن در پوشه asset برنامه خود، به صورت دسته‌ای (bundle) درآورید، یا می‌توانید آن را به صورت پویا از Cloud Storage دانلود کنید. جدول زیر این دو گزینه را با هم مقایسه می‌کند.

مدل بسته‌ای مدل میزبانی شده
این مدل بخشی از APK برنامه شماست که باعث افزایش حجم آن می‌شود. این مدل بخشی از APK شما نیست. با آپلود در فضای ابری میزبانی می‌شود. توصیه می‌کنیم از فضای ابری برای Firebase استفاده کنید.
این مدل بلافاصله در دسترس است، حتی زمانی که دستگاه اندروید آفلاین باشد برنامه شما باید شامل کدی باشد که مدل را بر اساس تقاضا دانلود کند
نیازی به پروژه Firebase نیست به یک پروژه Firebase نیاز دارد (در صورت استفاده از Cloud Storage برای Firebase).
برای به‌روزرسانی مدل، باید برنامه خود را دوباره منتشر کنید به‌روزرسانی‌های مدل را بدون انتشار مجدد برنامه خود، ارسال کنید
تست A/B داخلی ندارد تست A/B با پیکربندی از راه دور Firebase

امتحانش کن.

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

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

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

  3. اگر می‌خواهید مدلی را با استفاده از فضای ذخیره‌سازی ابری برای فایربیس دانلود کنید ، اگر قبلاً فایربیس را به پروژه iOS خود اضافه نکرده‌اید، حتماً آن را اضافه کنید . این کار هنگام بسته‌بندی مدل لازم نیست.

۱. مدل را بارگذاری کنید

پیکربندی یک منبع مدل محلی

برای اتصال مدل به برنامه خود:

  1. فایل مدل (که معمولاً به .tflite یا .lite ختم می‌شود) را در پروژه Xcode خود کپی کنید، و هنگام انجام این کار، حتماً گزینه Copy bundle resources انتخاب کنید. فایل مدل در بسته برنامه قرار می‌گیرد و در ML Kit در دسترس خواهد بود.

  2. ایجاد شیء LocalModel ، با مشخص کردن مسیر فایل مدل:

    سویفت

    let localModel = LocalModel(path: localModelFilePath)

    هدف-سی

    MLKLocalModel *localModel =
        [[MLKLocalModel alloc] initWithPath:localModelFilePath];

پیکربندی یک منبع مدل میزبانی شده از راه دور

برای استفاده از مدل میزبانی‌شده از راه دور، باید فایل مدل را با استفاده از منطق برنامه خود در حافظه محلی دستگاه دانلود کنید و سپس آن را به عنوان یک مدل محلی بارگذاری کنید. توصیه می‌کنیم برای میزبانی یک مدل از فضای ذخیره‌سازی ابری برای فایربیس استفاده کنید. برای جزئیات پیاده‌سازی، به راهنمای مهاجرت از فایربیس ML به فضای ذخیره‌سازی ابری مراجعه کنید.

پیکربندی برچسب‌گذار تصویر

پس از پیکربندی منابع مدل خود، یک شیء ImageLabeler از یکی از آنها ایجاد کنید.

گزینه‌های زیر موجود است:

گزینه‌ها
confidenceThreshold

حداقل امتیاز اطمینان برچسب‌های شناسایی‌شده. در صورت عدم تنظیم، از هر آستانه طبقه‌بندی‌کننده‌ای که توسط فراداده مدل مشخص شده باشد، استفاده خواهد شد. اگر مدل حاوی هیچ فراداده‌ای نباشد یا فراداده آستانه طبقه‌بندی‌کننده‌ای را مشخص نکند، از آستانه پیش‌فرض ۰.۰ استفاده خواهد شد.

maxResultCount

حداکثر تعداد برچسب‌هایی که باید برگردانده شوند. اگر تنظیم نشود، مقدار پیش‌فرض ۱۰ استفاده خواهد شد.

اگر فقط یک مدلِ بسته‌بندی‌شده‌ی محلی دارید، کافیست یک برچسب‌گذار از شیء LocalModel خود ایجاد کنید:

سویفت

let options = CustomImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0)
let imageLabeler = ImageLabeler.imageLabeler(options: options)

هدف-سی

MLKCustomImageLabelerOptions *options =
    [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0);
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

اگر مدلی دارید که از راه دور میزبانی می‌شود، باید قبل از اجرای آن، بررسی کنید که دانلود شده باشد.

اگرچه شما فقط باید قبل از اجرای برچسب‌گذار این موضوع را تأیید کنید، اما اگر هم یک مدل میزبانی‌شده از راه دور و هم یک مدل بسته‌بندی‌شده محلی دارید، انجام این بررسی هنگام نمونه‌سازی ImageLabeler منطقی است: اگر مدل از راه دور دانلود شده است، یک برچسب‌گذار از آن و در غیر این صورت از مدل محلی ایجاد کنید.

سویفت

// Path where your download logic saves the model
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite")

let model: LocalModel
if FileManager.default.fileExists(atPath: localModelURL.path) {
  // Use the downloaded model
  model = LocalModel(path: localModelURL.path)
} else {
  // Fall back to bundled model
  guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return }
  model = LocalModel(path: bundledModelPath)
}

let options = CustomImageLabelerOptions(localModel: model)
let imageLabeler = ImageLabeler.imageLabeler(options: options)

هدف-سی

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"];

MLKLocalModel *model;
if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) {
  // Use the downloaded model
  model = [[MLKLocalModel alloc] initWithPath:localModelPath];
} else {
  // Fall back to bundled model
  NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"];
  model = [[MLKLocalModel alloc] initWithPath:bundledModelPath];
}

MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:model];
MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];

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

سویفت

let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite")
if FileManager.default.fileExists(atPath: localModelURL.path) {
  // Model is already cached, initialize immediately
  self.initializeLabeler(with: localModelURL)
} else {
  // Model is not yet available, show loading UI and start download
  self.showLoadingUI()
  let storage = Storage.storage()
  let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite")
  modelRef.write(toFile: localModelURL) { url, error in
    self.hideLoadingUI()
    if let error = error {
      // Handle download error
      self.showErrorUI()
    } else if let modelURL = url {
      // Download success, initialize labeler
      self.initializeLabeler(with: modelURL)
    }
  }
}

func initializeLabeler(with modelURL: URL) {
  let localModel = LocalModel(path: modelURL.path)
  let options = CustomImageLabelerOptions(localModel: localModel)
  self.imageLabeler = ImageLabeler.imageLabeler(options: options)
  // Enable ML-related UI features here
  self.enableMLFeatures()
}

هدف-سی

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"];
NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath];

if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) {
  // Model is already cached, initialize immediately
  [self initializeLabelerWithURL:localModelURL];
} else {
  // Model is not yet available, show loading UI and start download
  [self showLoadingUI];

  FIRStorage *storage = [FIRStorage storage];
  FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"];

  [modelRef writeToFile:localModelURL
             completion:^(NSURL * _Nullable URL, NSError * _Nullable error) {
               [self hideLoadingUI];
               if (error != nil) {
                 // Handle download error
                 [self showErrorUI];
               } else {
                 // Download success, initialize labeler
                 [self initializeLabelerWithURL:URL];
               }
             }];
}

- (void)initializeLabelerWithURL:(NSURL *)modelURL {
  MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path];
  MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
  self.imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];

  // Enable ML-related UI features here
  [self enableMLFeatures];
}

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

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

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

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

    سویفت

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

    هدف-سی

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

    هدف-سی

    - (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 ایجاد کنید:

    سویفت

    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];

۳. برچسب‌گذار تصویر را اجرا کنید

برای برچسب‌گذاری اشیاء در یک تصویر، شیء image را به متد process() در ImageLabeler ارسال کنید.

ناهمزمان:

سویفت

imageLabeler.process(image) { labels, error in
    guard error == nil, let labels = labels, !labels.isEmpty else {
        // Handle the error.
        return
    }
    // Show results.
}

هدف-سی

[imageLabeler
    processImage:image
      completion:^(NSArray *_Nullable labels,
                   NSError *_Nullable error) {
        if (label.count == 0) {
            // Handle the error.
            return;
        }
        // Show results.
     }];

همزمان:

سویفت

var labels: [ImageLabel]
do {
    labels = try imageLabeler.results(in: image)
} catch let error {
    // Handle the error.
    return
}
// Show results.

هدف-سی

NSError *error;
NSArray *labels =
    [imageLabeler resultsInImage:image error:&error];
// Show results or handle the error.

۴. دریافت اطلاعات در مورد موجودیت‌های برچسب‌گذاری‌شده

اگر عملیات برچسب‌گذاری تصویر با موفقیت انجام شود، آرایه‌ای از ImageLabel را برمی‌گرداند. هر ImageLabel نشان دهنده چیزی است که در تصویر برچسب‌گذاری شده است. می‌توانید توضیحات متنی هر برچسب (در صورت وجود در فراداده فایل مدل LiteRT)، امتیاز اطمینان و شاخص آن را دریافت کنید. برای مثال:

سویفت

for label in labels {
  let labelText = label.text
  let confidence = label.confidence
  let index = label.index
}

هدف-سی

for (MLKImageLabel *label in labels) {
  NSString *labelText = label.text;
  float confidence = label.confidence;
  NSInteger index = label.index;
}

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

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

  • برای پردازش فریم‌های ویدیویی، از API همزمان results(in:) آشکارساز استفاده کنید. این متد را از تابع captureOutput(_, didOutput:from:) مربوط به AVCaptureVideoDataOutputSampleBufferDelegate فراخوانی کنید تا نتایج از فریم ویدیویی داده شده به صورت همزمان دریافت شوند. alwaysDiscardsLateVideoFrames مربوط به AVCaptureVideoDataOutput را برای فراخوانی‌های throttle به آشکارساز، true نگه دارید. اگر فریم ویدیویی جدیدی در حین اجرای آشکارساز در دسترس قرار گیرد، حذف خواهد شد.
  • اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از کیت ML دریافت کنید، سپس تصویر و همپوشانی را در یک مرحله رندر کنید. با انجام این کار، برای هر فریم ورودی پردازش شده، فقط یک بار روی سطح نمایشگر رندر می‌کنید. برای مثال، به updatePreviewOverlayViewWithLastFrame در نمونه شروع سریع کیت ML مراجعه کنید.