عند تمرير صورة إلى "حزمة تعلّم الآلة"، ترصد الحزمة ما يصل إلى خمسة عناصر في الصورة بالإضافة إلى موضع كل عنصر فيها. عند رصد العناصر في بث الفيديو، يكون لكل عنصر معرّف فريد يمكنك استخدامه لتتبُّع العنصر من إطار إلى آخر.
يمكنك استخدام نموذج مخصّص لتصنيف الصور من أجل تصنيف العناصر التي يتم رصدها. يُرجى الرجوع إلى المقالة النماذج المخصّصة باستخدام "حزمة تعلّم الآلة" للحصول على إرشادات حول متطلبات توافق النماذج، ومكان العثور على النماذج المدرَّبة مسبقًا، وكيفية تدريب نماذجك الخاصة.
هناك طريقتان لدمج نموذج مخصّص. يمكنك تجميع النموذج من خلال وضعه داخل مجلد مواد العرض في تطبيقك، أو يمكنك تنزيله ديناميكيًا من Cloud Storage. يقارن الجدول التالي بين الخيارَين.
| النموذج المُجمَّع | النموذج المُستضاف |
|---|---|
النموذج جزء من ملف .ipa الخاص بتطبيقك، ما
يزيد من حجمه. |
النموذج ليس جزءًا من ملف .ipa الخاص بتطبيقك. يتم استضافته من خلال تحميله إلى Cloud Storage. ننصحك باستخدام
Cloud Storage لبرنامج
Firebase. |
| النموذج متاح على الفور، حتى عندما يكون جهاز Android غير متصل بالإنترنت | يجب أن يتضمّن تطبيقك رمزًا لتنزيل النموذج عند الطلب |
| لا حاجة إلى مشروع Firebase | يتطلب مشروع Firebase (في حال استخدام مساحة تخزين سحابية لـ Firebase). |
| يجب إعادة نشر تطبيقك لتعديل النموذج | يمكنك إرسال تعديلات النموذج بدون إعادة نشر تطبيقك |
| ما مِن اختبار A/B مضمّن | اختبار A/B باستخدام ميزة "الإعداد عن بُعد عبر Firebase" |
للتجربة:
- يمكنك الاطّلاع على تطبيق التشغيل السريع للرؤية للحصول على مثال على استخدام النموذج المُجمَّع، و تطبيق التشغيل السريع لـ AutoML للحصول على مثال على استخدام النموذج المُستضاف.
- يمكنك الاطّلاع على تطبيق Material Design showcase app للحصول على عملية تنفيذ شاملة لواجهة برمجة التطبيقات هذه.
قبل البدء
أضِف مكتبات "حزمة تعلّم الآلة" إلى ملف Podfile:
pod 'GoogleMLKit/ObjectDetectionCustom', '8.0.0'بعد تثبيت حزم Pods في مشروعك أو تعديلها، افتح مشروع Xcode باستخدام ملف
.xcworkspaceالخاص به. تتوافق "حزمة تعلّم الآلة" مع الإصدار 13.2.1 من Xcode أو الإصدارات الأحدث.إذا كنت تريد تنزيل نموذج باستخدام مساحة تخزين سحابية لـ Firebase، تأكَّد من إضافة Firebase إلى مشروعك على iOS، إذا لم يسبق لك إجراء ذلك. لا يكون ذلك مطلوبًا عند تجميع النموذج.
1- تحميل النموذج
إعداد مصدر نموذج محلي
لتجميع النموذج مع تطبيقك، اتّبِع الخطوات التالية:
انسخ ملف النموذج (الذي ينتهي عادةً باللاحقة
.tfliteأو .lite) إلى مشروع Xcode، مع الحرص على اختيارCopy bundle resourcesعند إجراء ذلك. سيتم تضمين ملف النموذج في حزمة التطبيق وسيكون متاحًا لـ "حزمة تعلّم الآلة".أنشئ الكائن
LocalModelمع تحديد مسار ملف النموذج:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
إعداد مصدر نموذج مُستضاف عن بُعد
لاستخدام النموذج المُستضاف عن بُعد، عليك تنزيل ملف النموذج إلى وحدة التخزين المحلية للجهاز باستخدام منطق تطبيقك الخاص، ثم تحميله كنموذج محلي. ننصحك باستخدام Cloud Storage لبرنامج Firebase لاستضافة نموذج. للحصول على تفاصيل التنفيذ، يُرجى الاطّلاع على دليل نقل البيانات من Firebase ML إلى Cloud Storage.
2- إعداد أداة رصد العناصر
بعد إعداد مصادر النموذج، اضبط أداة رصد العناصر لحالة الاستخدام الخاصة بك باستخدام كائن CustomObjectDetectorOptions. يمكنك تغيير الإعدادات التالية:
| إعدادات أداة رصد العناصر | |
|---|---|
| وضع الرصد |
STREAM_MODE (تلقائي) | SINGLE_IMAGE_MODE
في في |
| رصد عناصر متعدّدة وتتبُّعها |
false (تلقائي) | true
يحدد هذا الخيار ما إذا كان سيتم رصد ما يصل إلى خمسة عناصر وتتبُّعها أو رصد العنصر الأبرز فقط (تلقائي). |
| تصنيف العناصر |
false (تلقائي) | true
يحدد هذا الخيار ما إذا كان سيتم تصنيف العناصر التي تم رصدها باستخدام نموذج التصنيف المخصّص المقدَّم
أم لا. لاستخدام نموذج التصنيف المخصّص
، عليك ضبط هذا الخيار على |
| الحد الأدنى لمستوى الموثوقية في التصنيف |
الحد الأدنى لنتيجة الدقة في التصنيفات التي تم رصدها إذا لم يتم ضبط هذا الخيار، سيتم استخدام أي حد أدنى للدقة في أداة التصنيف تحدّده بيانات النموذج الوصفية. إذا لم يكن النموذج يحتوي على أي بيانات وصفية أو إذا لم تحدّد البيانات الوصفية حدًا أدنى للدقة في أداة التصنيف، سيتم استخدام حد أدنى تلقائي للدقة يبلغ 0.0. |
| الحد الأقصى للتصنيفات لكل عنصر |
الحد الأقصى لعدد التصنيفات لكل عنصر التي ستعرضها أداة الرصد إذا لم يتم ضبط هذا الخيار، سيتم استخدام القيمة التلقائية 10. |
إذا كان لديك نموذج مُجمَّع محليًا فقط، ما عليك سوى إنشاء أداة رصد عناصر من كائن LocalModel:
Swift
let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3
Objective-C
MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3;
إذا كان لديك نموذج مُستضاف عن بُعد، عليك التأكّد من تنزيله قبل تشغيله.
على الرغم من أنّه عليك تأكيد ذلك قبل تشغيل أداة رصد العناصر فقط، إذا كان لديك نموذج مُستضاف عن بُعد ونموذج مُجمَّع محليًا، قد يكون من المنطقي إجراء هذا التحقّق عند إنشاء مثيل ObjectDetector: أنشئ أداة رصد من النموذج المُستضاف عن بُعد إذا تم تنزيله، ومن النموذج المحلي بخلاف ذلك.
Swift
// Path where your download logic saves the model let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") let model: LocalModel if FileManager.default.fileExists(atPath: localModelURL.path) { // Use the downloaded model model = LocalModel(path: localModelURL.path) } else { // Fall back to bundled model guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return } model = LocalModel(path: bundledModelPath) } let options = CustomObjectDetectorOptions(localModel: model) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3 let objectDetector = ObjectDetector.objectDetector(options: options)
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; MLKLocalModel *model; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Use the downloaded model model = [[MLKLocalModel alloc] initWithPath:localModelPath]; } else { // Fall back to bundled model NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"]; model = [[MLKLocalModel alloc] initWithPath:bundledModelPath]; } MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:model]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3; MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
إذا كان لديك نموذج مُستضاف عن بُعد فقط، عليك إيقاف الوظائف المتعلقة بالنموذج، مثلاً تعتيم جزء من واجهة المستخدم أو إخفاؤه، إلى أن تؤكّد تنزيل النموذج.
Swift
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") if FileManager.default.fileExists(atPath: localModelURL.path) { // Model is already cached, initialize immediately self.initializeDetector(with: localModelURL) } else { // Model is not yet available, show loading UI and start download self.showLoadingUI() let storage = Storage.storage() let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite") modelRef.write(toFile: localModelURL) { url, error in self.hideLoadingUI() if let error = error { // Handle download error self.showErrorUI() } else if let modelURL = url { // Download success, initialize detector self.initializeDetector(with: modelURL) } } } func initializeDetector(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true self.objectDetector = ObjectDetector.objectDetector(options: options) // Enable ML features in UI self.enableMLFeatures() }
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath]; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Model is already cached, initialize immediately [self initializeDetectorWithURL:localModelURL]; } else { // Model is not yet available, show loading UI and start download [self showLoadingUI]; FIRStorage *storage = [FIRStorage storage]; FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"]; [modelRef writeToFile:localModelURL completion:^(NSURL * _Nullable URL, NSError * _Nullable error) { [self hideLoadingUI]; if (error != nil) { // Handle download error [self showErrorUI]; } else { // Download success, initialize detector [self initializeDetectorWithURL:URL]; } }]; } - (void)initializeDetectorWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; self.objectDetector = [MLKObjectDetector objectDetectorWithOptions:options]; // Enable ML features in UI [self enableMLFeatures]; }
تم تحسين واجهة برمجة التطبيقات لرصد العناصر وتتبُّعها لحالتَي الاستخدام الأساسيتَين التاليتَين:
- الرصد والتتبُّع المباشرَين للعنصر الأبرز في عدسة الكاميرا
- رصد عناصر متعدّدة من صورة ثابتة
لإعداد واجهة برمجة التطبيقات لحالتَي الاستخدام هاتَين، اتّبِع الخطوات التالية:
Swift
// Live detection and tracking let options = CustomObjectDetectorOptions(localModel: localModel) options.shouldEnableClassification = true options.maxPerObjectLabelCount = 3 // Multiple object detection in static images let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableMultipleObjects = true options.shouldEnableClassification = true options.maxPerObjectLabelCount = 3
Objective-C
// Live detection and tracking MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.shouldEnableClassification = YES; options.maxPerObjectLabelCount = 3; // Multiple object detection in static images MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableMultipleObjects = YES; options.shouldEnableClassification = YES; options.maxPerObjectLabelCount = 3;
3- تجهيز الصورة المُدخَلة
أنشئ كائن 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];
4- إنشاء أداة رصد العناصر وتشغيلها
أنشئ أداة رصد عناصر جديدة:
Swift
let objectDetector = ObjectDetector.objectDetector(options: options)
Objective-C
MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
بعد ذلك، استخدِم أداة الرصد:
بشكل غير متزامن:
Swift
objectDetector.process(image) { objects, error in guard error == nil, let objects = objects, !objects.isEmpty else { // Handle the error. return } // Show results. }
Objective-C
[objectDetector processImage:image completion:^(NSArray
*_Nullable objects, NSError *_Nullable error) { if (objects.count == 0) { // Handle the error. return; } // Show results. }]; بشكل متزامن:
Swift
var objects: [Object] do { objects = try objectDetector.results(in: image) } catch let error { // Handle the error. return } // Show results.
Objective-C
NSError *error; NSArray
*objects = [objectDetector resultsInImage:image error:&error]; // Show results or handle the error.
5- الحصول على معلومات عن العناصر التي تم تصنيفها
إذا نجحت عملية استدعاء معالج الصور، فإنّها إما تمرِّر قائمة Object إلى معالج الإكمال أو تعرض القائمة، وذلك حسب ما إذا كنت قد استدعيت الطريقة غير المتزامنة أو المتزامنة.
يحتوي كل Object على الخصائص التالية:
frame |
CGRect يشير إلى موضع العنصر في الـ
صورة. |
||||||
trackingID |
عدد صحيح يحدّد العنصر في جميع الصور، أو `nil` في `SINGLE_IMAGE_MODE`. | ||||||
labels |
|
Swift
// objects contains one item if multiple object detection wasn't enabled. for object in objects { let frame = object.frame let trackingID = object.trackingID let description = object.labels.enumerated().map { (index, label) in "Label \(index): \(label.text), \(label.confidence), \(label.index)" }.joined(separator: "\n") }
Objective-C
// The list of detected objects contains one item if multiple object detection // wasn't enabled. for (MLKObject *object in objects) { CGRect frame = object.frame; NSNumber *trackingID = object.trackingID; for (MLKObjectLabel *label in object.labels) { NSString *labelString = [NSString stringWithFormat:@"%@, %f, %lu", label.text, label.confidence, (unsigned long)label.index]; } }
ضمان تجربة رائعة للمستخدم
للحصول على أفضل تجربة مستخدم، اتّبِع الإرشادات التالية في تطبيقك:
- يعتمد نجاح رصد العناصر على درجة وضوحها البصرية. لكي يتم رصد العناصر التي تحتوي على عدد قليل من الميزات المرئية، قد تحتاج إلى أن تشغل جزءًا أكبر من الصورة. عليك تزويد المستخدمين بإرشادات حول التقاط الإدخالات التي تعمل بشكل جيد مع نوع العناصر التي تريد رصدها.
- عند استخدام التصنيف، إذا كنت تريد رصد العناصر التي لا تندرج بشكل واضح ضمن الفئات المتوافقة، نفِّذ معالجة خاصة للعناصر غير المعروفة.
يمكنك أيضًا الاطّلاع على تطبيق Material Design showcase في "حزمة تعلّم الآلة" و مجموعة أنماط Material Design للميزات المستندة إلى تعلّم الآلة.
تحسين الأداء
إذا كنت تريد استخدام ميزة رصد العناصر في تطبيق في الوقت الفعلي، اتّبِع الإرشادات التالية للحصول على أفضل معدلات الإطارات:عند استخدام وضع البث في تطبيق في الوقت الفعلي، لا تستخدِم ميزة رصد عناصر متعدّدة، لأنّ معظم الأجهزة لن تتمكّن من إنتاج معدلات إطارات مناسبة.
- لمعالجة إطارات الفيديو، استخدِم واجهة برمجة التطبيقات المتزامنة
results(in:)لأداة الرصد. استدعِ هذه الطريقة من الـAVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:)للحصول على النتائج بشكل متزامن من إطار الفيديو المحدّد. احتفِظ بقيمةAVCaptureVideoDataOutputفيalwaysDiscardsLateVideoFramesعلىtrueللحدّ من عمليات استدعاء أداة الرصد. إذا أصبح إطار فيديو جديد متاحًا أثناء تشغيل أداة الرصد، سيتم إسقاطه. - إذا كنت تستخدم ناتج أداة الرصد لتراكب الرسومات على الصورة المُدخَلة، احصل أولاً على النتيجة من "حزمة تعلّم الآلة"، ثم اعرض الصورة والتراكب في خطوة واحدة. من خلال إجراء ذلك، يمكنك العرض على سطح العرض مرة واحدة فقط لكل إطار إدخال تتم معالجته. يمكنك الاطّلاع على updatePreviewOverlayViewWithLastFrame في نموذج التشغيل السريع في "حزمة تعلّم الآلة" للحصول على مثال.