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

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

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

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

قبل ان تبدأ

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

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

    pod 'GoogleMLKit/ImageLabelingCustom'
    

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

    pod 'GoogleMLKit/ImageLabelingCustom'
    pod 'GoogleMLKit/LinkFirebase'
    
  2. بعد تثبيت أو تحديث Pods لمشروعك، افتح مشروع Xcode الخاص بك باستخدام .xcworkspace . يتم دعم ML Kit في إصدار Xcode 12.2 أو أعلى.

  3. إذا كنت تريد تنزيل نموذج ، فتأكد من إضافة Firebase إلى مشروع Android الخاص بك ، إذا لم تكن قد قمت بذلك بالفعل. هذا غير مطلوب عند تجميع النموذج.

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

تكوين مصدر نموذج محلي

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

  1. قم باستخراج النموذج وبياناته التعريفية من الأرشيف المضغوط الذي قمت بتنزيله من وحدة تحكم Firebase إلى مجلد:

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

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

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

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

    سويفت

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

    ج موضوعية

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

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

لاستخدام النموذج المستضاف عن بعد، قم بإنشاء كائن CustomRemoteModel ، مع تحديد الاسم الذي قمت بتعيينه للنموذج عند نشره:

سويفت

// 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)

ج موضوعية

// 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:

سويفت

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

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

ج موضوعية

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

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

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

قم بإنشاء ملصق صورة من النموذج الخاص بك

بعد تكوين مصادر النموذج الخاص بك، قم بإنشاء كائن ImageLabeler من أحد هذه المصادر.

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

سويفت

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)

ج موضوعية

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 : أنشئ أداة تسمية من النموذج البعيد إذا كان تم تحميلها، ومن النموذج المحلي خلاف ذلك.

سويفت

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)

ج موضوعية

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 في كتلة المراقب، نظرًا لأن التنزيلات قد تستغرق بعض الوقت، ويمكن تحرير الكائن الأصلي عند انتهاء التنزيل. على سبيل المثال:

سويفت

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]
    // ...
}

ج موضوعية

__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 الصحيح.

    سويفت

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

    ج موضوعية

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

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

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

    للحصول على اتجاه الصورة:

    سويفت

    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 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 واتجاهه:

    سويفت

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

3. قم بتشغيل أداة تسمية الصورة

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

سويفت

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<MLKImageLabel *> *_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<MLKImageLabel *> *labels =
    [imageLabeler resultsInImage:image error:&error];
// Show results or handle the error.

4. احصل على معلومات حول الكائنات ذات العلامات

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

سويفت

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

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

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

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