تصنيف الصور باستخدام نموذج تم تدريبه على AutoML على منصات Apple

بعد تدريب النموذج الخاص بك باستخدام AutoML Vision Edge، يمكنك استخدامه في تطبيقك لتصنيف الصور.

هناك طريقتان لدمج النماذج المدرَّبة من AutoML Vision Edge. يمكنك تجميع النموذج من خلال نسخ ملفات النموذج إلى مشروع Xcode، أو يمكنك تنزيله ديناميكيًا من Firebase.

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

قبل البدء

  1. أدرِج مكتبات ML Kit في ملف Podfile:

    لتضمين نموذج مع تطبيقك، اتّبِع الخطوات التالية:

    pod 'GoogleMLKit/ImageLabelingCustom'
    

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

    pod 'GoogleMLKit/ImageLabelingCustom'
    pod 'GoogleMLKit/LinkFirebase'
    
  2. بعد تثبيت أو تعديل Pods في مشروعك، افتح مشروع Xcode باستخدام .xcworkspace. تتوافق "حزمة تعلُّم الآلة" مع الإصدار 12.2 من Xcode أو الإصدارات الأحدث.

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

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

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

لتضمين النموذج في تطبيقك، اتّبِع الخطوات التالية:

  1. استخرِج النموذج وبياناته الوصفية من ملف zip الذي نزّلته من وحدة تحكّم Firebase إلى مجلد:

    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    

    يجب أن تكون جميع الملفات الثلاثة في المجلد نفسه. ننصحك باستخدام الملفات كما نزّلتها بدون تعديل (بما في ذلك أسماء الملفات).

  2. انسخ المجلد إلى مشروع Xcode، مع الحرص على اختيار إنشاء مراجع للمجلدات عند إجراء ذلك. سيتم تضمين ملف النموذج والبيانات الوصفية في حزمة التطبيق وستكون متاحة لخدمة ML Kit.

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

    Swift

    guard let manifestPath = Bundle.main.path(
        forResource: "manifest",
        ofType: "json",
        inDirectory: "your_model_directory"
    ) else { return true }
    let localModel = LocalModel(manifestPath: manifestPath)
    

    Objective-C

    NSString *manifestPath =
        [NSBundle.mainBundle pathForResource:@"manifest"
                                      ofType:@"json"
                                 inDirectory:@"your_model_directory"];
    MLKLocalModel *localModel =
        [[MLKLocalModel alloc] initWithManifestPath:manifestPath];
    

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

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

Swift

// Initialize the model source with the name you assigned in
// the Firebase console.
let remoteModelSource = FirebaseModelSource(name: "your_remote_model")
let remoteModel = CustomRemoteModel(remoteModelSource: remoteModelSource)

Objective-C

// Initialize the model source with the name you assigned in
// the Firebase console.
MLKFirebaseModelSource *firebaseModelSource =
    [[MLKFirebaseModelSource alloc] initWithName:@"your_remote_model"];
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] downloadRemoteModel:remoteModel
                                             conditions:downloadConditions];

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

إنشاء أداة تصنيف صور من نموذجك

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

إذا كان لديك نموذج مجمّع محليًا فقط، ما عليك سوى إنشاء أداة تصنيف من عنصر LocalModel وضبط الحد الأدنى المطلوب لنتيجة الثقة (راجِع تقييم النموذج):

Swift

let options = CustomImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Cloud console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options)

Objective-C

CustomImageLabelerOptions *options =
    [[CustomImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0f);  // Evaluate your model in the Cloud console
                                        // to determine an appropriate value.
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)  // Evaluate your model in the Firebase console
                                                    // to determine an appropriate value.
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.0f);  // Evaluate your model in the Firebase console
                                        // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

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

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

Swift

NotificationCenter.default.addObserver(
    forName: .mlkitMLModelDownloadDidSucceed,
    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: .mlkitMLModelDownloadDidFail,
    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 أو CMSampleBufferRef.

إذا كنت تستخدم 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;

إذا كنت تستخدم CMSampleBufferRef، اتّبِع الخطوات التالية:

  • تحدّد هذه السمة اتجاه بيانات الصورة الواردة في المخزن المؤقت CMSampleBufferRef.

    للحصول على اتجاه الصورة، اتّبِع الخطوات التالية:

    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 position == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                          : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                          : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                          : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                          : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • أنشئ كائن VisionImage باستخدام الكائن CMSampleBufferRef والاتجاه:

    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- تشغيل أداة تصنيف الصور

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

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<MLKImageLabel *> *_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<MLKImageLabel *> *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;
}

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

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

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