تصنيف الصور باستخدام نموذج مخصص على نظام التشغيل 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 إلى ImageLabeler طريقة process()

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

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:) الخاصة بأداة الرصد. اتصل لهذه الطريقة من AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) للحصول على النتائج من الفيديو المحدّد بشكل متزامن الإطار. إبقاء جهاز AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames كـ true لضبط الاتصالات الموجَّهة إلى أداة الرصد. إذا كانت تجربة يصبح إطار الفيديو متاحًا أثناء تشغيل أداة الكشف، وسيتم إسقاطه.
  • إذا استخدمت مخرجات أداة الكشف لتراكب الرسومات على الصورة المدخلة، والحصول أولاً على النتيجة من ML Kit، ثم عرض الصورة وتراكبها في خطوة واحدة. ومن خلال القيام بذلك، يمكنك العرض على سطح الشاشة مرة واحدة فقط لكل إطار إدخال تمت معالجته. راجع updatePreviewOverlayViewWithLastFrame في عينة البدء السريع لأدوات تعلُّم الآلة كمثال.