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

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

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

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

قبل البدء

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

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

    pod 'GoogleMLKit/ImageLabelingCustom'
    

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

    pod 'GoogleMLKit/ImageLabelingCustom'
    pod 'GoogleMLKit/LinkFirebase'
    
  2. بعد تثبيت لوحات مشروعك أو تحديثها، افتح مشروعك في Xcode باستخدام .xcworkspace. يتوافق ML Kit مع الإصدار 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];

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

يمكنك الحصول على حالة تنزيل النموذج من خلال إرفاق عناصر متابعة في "مركز الإشعارات" التلقائي. احرِص على استخدام إشارة ضعيفة إلى 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 في نموذج تطبيق العرض