אפשר להשתמש ב-ML Kit כדי לזהות ישויות בתמונה ולתייג אותן. ממשק ה-API הזה תומך במגוון רחב של מודלים לסיווג תמונות בהתאמה אישית. במאמר מודלים בהתאמה אישית עם ML Kit תוכלו למצוא הנחיות לגבי דרישות התאימות למודלים, איפה אפשר למצוא מודלים שעברו אימון מקדים ואיך לאמן מודלים משלכם.
יש שתי דרכים לשלב מודל מותאם אישית. אפשר לארוז את המודל על ידי הוספתו לתיקיית הנכסים של האפליקציה, או להוריד אותו באופן דינמי מ-Firebase. הטבלה הבאה משווה בין שתי האפשרויות.
מודל בחבילה | מודל מתארח |
---|---|
המודל הוא חלק מ-APK של האפליקציה, ולכן הוא גדל. | המודל אינו חלק מה-APK שלך. כדי לארח את המשחק, אתם צריכים להעלות אותו ל-Firebase Machine Learning. |
הדגם זמין באופן מיידי, גם כשמכשיר Android במצב אופליין | המערכת מורידה את המודל על פי דרישה |
אין צורך בפרויקט Firebase | נדרש פרויקט Firebase |
כדי לעדכן את המודל, צריך לפרסם מחדש את האפליקציה | דחיפת עדכוני מודל מבלי לפרסם מחדש את האפליקציה |
אין בדיקות A/B מובנות | בדיקות A/B פשוטות עם הגדרת תצורה מרחוק ב-Firebase |
אני רוצה לנסות
- אפשר לעיין באפליקציית Vision למתחילים כדי לראות שימוש לדוגמה במודל הכלול, ובאפליקציה למתחילים של Automl כדי לראות דוגמה לשימוש במודל המתארח.
לפני שמתחילים
יש לכלול את ספריות ML Kit ב-Podfile:
כדי לקבץ מודל עם האפליקציה:
pod 'GoogleMLKit/ImageLabelingCustom', '3.2.0'
כדי להוריד באופן דינמי מודל מ-Firebase, צריך להוסיף את התלות
LinkFirebase
:pod 'GoogleMLKit/ImageLabelingCustom', '3.2.0' pod 'GoogleMLKit/LinkFirebase', '3.2.0'
אחרי שמתקינים או מעדכנים את ה-Pods של הפרויקט, צריך לפתוח את פרויקט ה-Xcode באמצעות ה-
.xcworkspace
שלו. יש תמיכה ב-ML Kit בגרסה 13.2.1 ואילך של Xcode.אם אתם רוצים להוריד מודל, הקפידו להוסיף את Firebase לפרויקט ה-iOS, אם עדיין לא עשיתם זאת, ולא חייבים לעשות זאת כשמקבצים את המודל.
1. טעינת המודל
הגדרת מקור של מודל מקומי
כדי לקבץ את המודל עם האפליקציה שלך:
מעתיקים את קובץ המודל (שמסתיים בדרך כלל ב-
.tflite
או ב-.lite
) לפרויקט ה-Xcode ומקפידים לבחור ב-Copy bundle resources
כשעושים זאת. קובץ המודל ייכלל ב-App Bundle ויהיה זמין ל-ML Kit.יוצרים אובייקט
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];
אם יש לכם מודל באירוח מרוחק, תצטרכו לבדוק שהוא הורד לפני שמריצים אותו. אפשר לבדוק את הסטטוס של משימת הורדת המודל באמצעות ה-method 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
ל-method process()
של ImageLabeler
.
באופן אסינכרוני:
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; }
טיפים לשיפור הביצועים בזמן אמת
אם רוצים לתייג תמונות באפליקציה בזמן אמת, כדאי לפעול לפי ההנחיות הבאות כדי להשיג את קצבי הפריימים הטובים ביותר:
- לעיבוד פריימים של וידאו, יש להשתמש ב-API הסינכרוני
results(in:)
של המזהה. כדאי להפעיל את השיטה הזו מהפונקציהcaptureOutput(_, didOutput:from:)
שלAVCaptureVideoDataOutputSampleBufferDelegate
כדי לקבל באופן סינכרוני תוצאות מהפריים הנתון של הסרטון. יש לשמור אתalwaysDiscardsLateVideoFrames
שלAVCaptureVideoDataOutput
בתורtrue
כדי לווסת את הקריאות לחיישן. אם בזמן שהמזהה פועל פריים וידאו חדש, הוא יוסר. - אם משתמשים בפלט של המזהה כדי ליצור שכבת-על של גרפיקה בתמונת הקלט, קודם צריך לקבל את התוצאה מ-ML Kit ואז לעבד את התמונה ואת שכבת-העל בשלב אחד. באופן הזה, מתבצע רינדור על פני המסך פעם אחת בלבד לכל מסגרת קלט מעובדת. כדי לראות דוגמה, אפשר לעיין ב-updatePreviewOverlayViewWithLastFrame בדוגמה למתחילים של ערכת ML.