כשמעבירים תמונה ל-ML Kit, הוא מזהה עד חמישה אובייקטים בתמונה, וגם את המיקום של כל אובייקט בתמונה. כשמזהים אובייקטים בסטרימינג של סרטונים, לכל אובייקט יש מזהה ייחודי שאפשר להשתמש בו כדי לעקוב אחרי האובייקט מפריים לפריים.
אתם יכולים להשתמש במודל מותאם אישית לסיווג תמונות כדי לסווג את האובייקטים שמזוהים. במאמר מודלים בהתאמה אישית עם ML Kit מוסברות הדרישות לתאימות מודלים, איפה אפשר למצוא מודלים שאומנו מראש ואיך לאמן מודלים משלכם.
יש שתי דרכים לשלב מודל בהתאמה אישית. אפשר לארוז את המודל על ידי הוספתו לתיקיית הנכסים של האפליקציה, או להוריד אותו באופן דינמי מ-Cloud Storage. בטבלה הבאה מוצגת השוואה בין שתי האפשרויות.
| מודל בחבילה | מודל מתארח |
|---|---|
המודל הוא חלק מקובץ .ipa של האפליקציה, ולכן הוא מגדיל את הגודל שלה. |
המודל לא מופיע בקובץ .ipa של האפליקציה. הוא מתארח על ידי העלאה ל-Cloud Storage. מומלץ להשתמש ב-Cloud Storage for Firebase. |
| המודל זמין באופן מיידי, גם כשמכשיר Android אופליין | האפליקציה צריכה לכלול קוד להורדת המודל לפי דרישה |
| אין צורך בפרויקט Firebase | נדרש פרויקט Firebase (אם משתמשים ב-Cloud Storage for Firebase). |
| כדי לעדכן את המודל, צריך לפרסם מחדש את האפליקציה | עדכון המודל בלי לפרסם מחדש את האפליקציה |
| אין בדיקות A/B מובנות | בדיקות A/B עם הגדרת תצורה מרחוק ב-Firebase |
רוצה לנסות?
- אפשר לראות דוגמה לשימוש במודל החבילה באפליקציית המדריך למתחילים בנושא Vision, ודוגמה לשימוש במודל המתארח באפליקציית המדריך למתחילים בנושא AutoML.
- כדי לראות הטמעה מקצה לקצה של ה-API הזה, אפשר לעיין באפליקציית הדוגמה של Material Design.
לפני שמתחילים
מוסיפים את ספריות ML Kit ל-Podfile:
pod 'GoogleMLKit/ObjectDetectionCustom', '8.0.0'אחרי שמתקינים או מעדכנים את ה-Pods של הפרויקט, פותחים את פרויקט Xcode באמצעות
.xcworkspace. ML Kit נתמך ב-Xcode בגרסה 13.2.1 ומעלה.אם רוצים להוריד מודל באמצעות Cloud Storage for Firebase, צריך לוודא שהוספתם את Firebase לפרויקט iOS, אם עדיין לא עשיתם זאת. זה לא נדרש כשמצרפים את המודל לחבילה.
1. טעינת המודל
הגדרת מקור מודל מקומי
כדי לארוז את המודל עם האפליקציה:
מעתיקים את קובץ המודל (בדרך כלל מסתיים ב-
.tfliteאו ב-.lite) לפרויקט Xcode, ומוודאים לבחור באפשרותCopy bundle resourcesכשעושים זאת. קובץ המודל ייכלל בקובץ AAB ויהיה זמין ל-ML Kit.יוצרים אובייקט
LocalModelומציינים את הנתיב לקובץ המודל:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
הגדרת מקור מודל שמתארח מרחוק
כדי להשתמש במודל שמתארח מרחוק, צריך להוריד את קובץ המודל לאחסון המקומי של המכשיר באמצעות הלוגיקה של האפליקציה, ואז לטעון אותו כמודל מקומי. מומלץ להשתמש ב-Cloud Storage for 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]; }
ממשק ה-API לזיהוי אובייקטים ולמעקב מותאם לשני תרחישי השימוש העיקריים הבאים:
- זיהוי ומעקב בזמן אמת של האובייקט הבולט ביותר בעינית המצלמה.
- זיהוי של כמה אובייקטים בתמונה סטטית.
כדי להגדיר את ה-API לתרחישי השימוש האלה:
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. קבלת מידע על אובייקטים עם תוויות
אם הקריאה למעבד התמונות מצליחה, הוא מעביר רשימה של Objects ל-completion handler או מחזיר את הרשימה, בהתאם לשיטה האסינכרונית או הסינכרונית שהפעלתם.
כל 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]; } }
איך מבטיחים חוויית משתמש מעולה
כדי לספק את חוויית המשתמש הכי טובה, צריך לפעול לפי ההנחיות הבאות באפליקציה:
- הצלחת זיהוי האובייקט תלויה במורכבות החזותית שלו. כדי שאובייקטים עם מספר קטן של מאפיינים חזותיים יזוהו, יכול להיות שהם יצטרכו לתפוס חלק גדול יותר מהתמונה. חשוב לספק למשתמשים הנחיות לגבי צילום קלט שמתאים לסוג האובייקטים שרוצים לזהות.
- כשמשתמשים בסיווג, אם רוצים לזהות אובייקטים שלא נכנסים בצורה ברורה לקטגוריות הנתמכות, צריך להטמיע טיפול מיוחד באובייקטים לא ידועים.
כדאי גם לעיין באפליקציית התצוגה של ML Kit Material Design ובאוסף הדפוסים של Material Design לתכונות מבוססות למידת מכונה.
Improving performance
אם רוצים להשתמש בזיהוי אובייקטים באפליקציה בזמן אמת, כדאי לפעול לפי ההנחיות הבאות כדי להשיג את קצב הפריימים הטוב ביותר:כשמשתמשים במצב סטרימינג באפליקציה בזמן אמת, לא מומלץ להשתמש בכמה זיהוי אובייקטים, כי רוב המכשירים לא יוכלו להפיק קצב פריימים מספיק.
- כדי לעבד פריימים של סרטונים, משתמשים ב-API הסינכרוני של הגלאי
results(in:). קוראים לפונקציה הזו מהפונקציהAVCaptureVideoDataOutputSampleBufferDelegate'scaptureOutput(_, didOutput:from:)כדי לקבל תוצאות באופן סינכרוני מפריים נתון של סרטון. שומרים עלAVCaptureVideoDataOutputalwaysDiscardsLateVideoFramesבתורtrueכדי להגביל את השיחות לגלאי. אם פריים חדש של סרטון יהיה זמין בזמן שהגלאי פועל, הוא ייפסל. - אם משתמשים בפלט של הגלאי כדי להוסיף גרפיקה לתמונת הקלט, צריך קודם לקבל את התוצאה מ-ML Kit, ואז לעבד את התמונה ולהוסיף את הגרפיקה בשלב אחד. כך, הרינדור מתבצע רק פעם אחת לכל פריים קלט שעבר עיבוד. דוגמה מופיעה ב-updatePreviewOverlayViewWithLastFrame במדריך לתחילת העבודה עם ML Kit.