تصنيف الصور باستخدام نموذج مخصص على نظام التشغيل iOS

يمكنك استخدام أدوات تعلّم الآلة للتعرّف على الكيانات في صورة وتصنيفها. تتوافق واجهة برمجة التطبيقات هذه مع مجموعة كبيرة من نماذج تصنيف الصور المخصّصة. يُرجى الرجوع إلى النماذج المخصّصة باستخدام حزمة تعلّم الآلة للحصول على إرشادات حول متطلبات توافق النماذج، ومعرفة كيفية العثور على نماذج مدرّبة مسبقًا، وكيفية تدريب نماذجك الخاصة.

هناك طريقتان لدمج النموذج المخصّص. يمكنك تجميع النموذج من خلال وضعه في مجلد مواد العرض في التطبيق أو تنزيله ديناميكيًا من Firebase. يقارن الجدول التالي بين الخيارين.

النموذج المجمّع النموذج المستضاف
النموذج هو جزء من حزمة APK لتطبيقك، ما يزيد من حجمه. النموذج ليس جزءًا من APK. وتتم استضافته عن طريق تحميله إلى أداة تعلُّم الآلة من Firebase.
يتوفر الطراز على الفور، حتى عندما يكون جهاز Android غير متصل بالإنترنت. يتم تنزيل النموذج عند الطلب
عدم الحاجة إلى مشروع Firebase يجب توفّر مشروع في Firebase.
يجب إعادة نشر تطبيقك لتحديث النموذج. إرسال تحديثات النموذج بدون إعادة نشر تطبيقك
بدون اختبار A/B مدمج إجراء اختبار A/B بسهولة باستخدام ميزة الإعداد عن بُعد في Firebase

تجربة السمات والبيانات

قبل البدء

  1. تضمين مكتبات ML Kit في ملف Podfile:

    لتجميع نموذج مع تطبيقك:

    pod 'GoogleMLKit/ImageLabelingCustom', '3.2.0'
    

    لتنزيل نموذج بشكل ديناميكي من Firebase، أضِف التبعية LinkFirebase:

    pod 'GoogleMLKit/ImageLabelingCustom', '3.2.0'
    pod 'GoogleMLKit/LinkFirebase', '3.2.0'
    
  2. بعد تثبيت أو تحديث مجموعات اللوحات لمشروعك، افتح مشروع Xcode باستخدام .xcworkspace الخاص به. تتوفّر هذه الأداة في الإصدار 13.2.1 من Xcode أو الإصدارات الأحدث.

  3. إذا أردت تنزيل نموذج، تأكّد من إضافة Firebase إلى مشروع iOS، إذا لم يسبق لك إجراء ذلك، فهذا ليس مطلوبًا عند تجميع النموذج.

1- تحميل النموذج

ضبط مصدر نموذج محلي

لتجميع النموذج مع تطبيقك:

  1. انسخ ملف النموذج (الذي ينتهي عادةً بالأرقام .tflite أو .lite) إلى مشروع Xcode الخاص بك، مع الحرص على اختيار Copy bundle resources عند إجراء ذلك. سيتم تضمين ملف النموذج في حزمة التطبيق وسيكون متاحًا لبرنامج ML Kit.

  2. إنشاء كائن LocalModel، مع تحديد المسار إلى ملف النموذج:

    Swift

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

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

ضبط مصدر نموذج مستضاف على Firebase

لاستخدام النموذج الذي تتم استضافته عن بُعد، أنشِئ عنصر RemoteModel مع تحديد الاسم الذي حدّدته للنموذج عند نشره:

Swift

let firebaseModelSource = FirebaseModelSource(
    name: "your_remote_model") // The name you assigned in
                               // the Firebase console.
let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource)

Objective-C

MLKFirebaseModelSource *firebaseModelSource =
    [[MLKFirebaseModelSource alloc]
        initWithName:@"your_remote_model"]; // The name you assigned in
                                            // the Firebase console.
MLKCustomRemoteModel *remoteModel =
    [[MLKCustomRemoteModel alloc]
        initWithRemoteModelSource:firebaseModelSource];

بعد ذلك، ابدأ مهمة تنزيل النموذج، مع تحديد الشروط التي تريد السماح بالتنزيل بموجبها. إذا لم يكن النموذج مثبَّتًا على الجهاز أو في حال توفُّر إصدار أحدث من النموذج، سيتم تنزيل النموذج من Firebase بشكل غير متزامن:

Swift

let downloadConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)

let downloadProgress = ModelManager.modelManager().download(
  remoteModel,
  conditions: downloadConditions
)

Objective-C

MLKModelDownloadConditions *downloadConditions =
    [[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];

NSProgress *downloadProgress =
    [[MLKModelManager modelManager] downloadModel:remoteModel
                                       conditions:downloadConditions];

تبدأ العديد من التطبيقات مهمّة التنزيل في رمز الإعداد الخاص بها، ويمكنك تنفيذ ذلك في أي وقت قبل أن تحتاج إلى استخدام النموذج.

ضبط أداة تصنيف الصور

بعد ضبط مصادر النماذج، يمكنك إنشاء كائن ImageLabeler من أحدها.

تتوفّر الخيارات التالية:

الخيارات
confidenceThreshold

الحد الأدنى لدرجة الثقة للتصنيفات التي تم رصدها. وفي حال عدم ضبط هذه السياسة، سيتم استخدام أي حدّ أدنى للمصنِّف تحدِّده البيانات الوصفية للنموذج. إذا كان النموذج لا يحتوي على أي بيانات وصفية أو إذا لم تحدّد البيانات الوصفية حدًا للمصنِّف، سيتم استخدام حد أقصى تلقائي هو 0.0.

maxResultCount

الحد الأقصى لعدد التصنيفات المطلوب عرضها. وفي حال ترك هذه السياسة بدون ضبط، سيتم استخدام القيمة التلقائية التي تبلغ 10.

إذا كان لديك فقط نموذج مجمَّع محليًا، ما عليك سوى إنشاء تصنيف من كائن LocalModel:

Swift

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

Objective-C

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

إذا كان لديك نموذج مُستضاف عن بُعد، عليك التأكّد ممّا إذا تمّ تنزيله قبل تشغيله. ويمكنك التحقّق من حالة مهمة تنزيل النموذج باستخدام طريقة isModelDownloaded(remoteModel:) لدى مدير النماذج.

عليك تأكيد ذلك فقط قبل تشغيل المصنِّف، ولكن إذا كان لديك نموذج مستضاف عن بُعد ونموذج مجمَّع محليًا، قد يكون من الأفضل إجراء هذا الفحص عند إنشاء مثيل ImageLabeler، إذ عليك إنشاء مصنِّف من النموذج البعيد إذا كان قد تم تنزيله، ومن النموذج المحلي بطريقة أخرى.

Swift

var options: CustomImageLabelerOptions!
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = CustomImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = CustomImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = NSNumber(value: 0.0)
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKCustomImageLabelerOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKCustomImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = @(0.0);
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

إذا لم يكن لديك سوى نموذج مُستضاف عن بُعد، يجب إيقاف الوظائف ذات الصلة بالنموذج، على سبيل المثال، غير متاح أو إخفاء جزء من واجهة المستخدم، حتى تتأكّد من تنزيل النموذج.

يمكنك معرفة حالة تنزيل النموذج من خلال إرفاق المراقبين بمركز الإشعارات التلقائي. يجب استخدام إشارة ضعيفة إلى self في قسم المراقب، وذلك لأنّ عمليات التنزيل قد تستغرق بعض الوقت، ويمكن أن تتم إزالة العنصر الأصلي عند انتهاء عملية التنزيل. مثال:

Swift

NotificationCenter.default.addObserver(
    forName: .mlkitModelDownloadDidSucceed,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel,
        model.name == "your_remote_model"
        else { return }
    // The model was downloaded and is available on the device
}

NotificationCenter.default.addObserver(
    forName: .mlkitModelDownloadDidFail,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel
        else { return }
    let error = userInfo[ModelDownloadUserInfoKey.error.rawValue]
    // ...
}

Objective-C

__weak typeof(self) weakSelf = self;

[NSNotificationCenter.defaultCenter
    addObserverForName:MLKModelDownloadDidSucceedNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              MLKRemoteModel *model = note.userInfo[MLKModelDownloadUserInfoKeyRemoteModel];
              if ([model.name isEqualToString:@"your_remote_model"]) {
                // The model was downloaded and is available on the device
              }
            }];

[NSNotificationCenter.defaultCenter
    addObserverForName:MLKModelDownloadDidFailNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              NSError *error = note.userInfo[MLKModelDownloadUserInfoKeyError];
            }];

2. تحضير صورة الإدخال

أنشِئ عنصر VisionImage باستخدام UIImage أو CMSampleBuffer.

في حال استخدام "UIImage"، يُرجى اتّباع الخطوات التالية:

  • يمكنك إنشاء عنصر VisionImage باستخدام UIImage. تأكَّد من تحديد سمة .orientation الصحيحة.

    Swift

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

    Objective-C

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

    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;
      }
    }
          
  • ويمكنك إنشاء كائن VisionImage باستخدام كائن 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. تشغيل برنامج تصنيف الصور

لتصنيف الكائنات في صورة، مرِّر الكائن image إلى طريقة process() في ImageLabeler.

بشكل غير متزامن:

Swift

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

Objective-C

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

في الوقت نفسه:

Swift

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

Objective-C

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

4. الحصول على معلومات عن الكيانات المصنّفة

إذا نجحت عملية تصنيف الصور، سيتم عرض الصفيف ImageLabel. يمثل كل ImageLabel شيئًا تم تصنيفه في الصورة. يمكنك الحصول على الوصف النصي لكل تصنيف (إذا كان متاحًا في البيانات الوصفية لملف نموذج TensorFlow Lite) ونتيجة الثقة والفهرس. مثال:

Swift

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

Objective-C

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

نصائح لتحسين الأداء في الوقت الفعلي

إذا أردت تصنيف الصور في تطبيق في الوقت الفعلي، اتّبِع هذه الإرشادات لتحقيق أفضل معدلات عرض الإطارات:

  • لمعالجة إطارات الفيديو، استخدِم واجهة برمجة التطبيقات المتزامنة results(in:) لأداة الرصد. ويمكنك استدعاء هذه الطريقة من وظيفة captureOutput(_, didOutput:from:) في AVCaptureVideoDataOutputSampleBufferDelegate للحصول على نتائج متزامنة من إطار الفيديو المحدّد. الاحتفاظ بـ AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames true لتقليل المكالمات الواردة إلى جهاز الرصد. في حال توفّر إطار فيديو جديد أثناء تشغيل أداة الرصد، سيتم تجاهله.
  • إذا كنت تستخدم نتيجة أداة الرصد لإضافة رسومات على الصورة التي تم إدخالها، احصل أولاً على النتيجة من أدوات تعلّم الآلة، ثم اعرض الصورة والتراكب في خطوة واحدة. بإجراء ذلك، يتم عرضك على سطح الشاشة مرة واحدة فقط لكل إطار إدخال تمت معالجته. للاطّلاع على مثال، يمكن الاطّلاع على updatePreviewOverlayViewWithLastFrame في نموذج البدء السريع في ML Kit.