אתם יכולים להשתמש ב-ML Kit כדי לזהות ישויות בתמונה ולהוסיף להן תוויות. ה-API הזה תומך במגוון רחב של מודלים מותאמים אישית לסיווג תמונות. במאמר מודלים בהתאמה אישית עם ML Kit מוסבר על דרישות התאימות של המודלים, איפה אפשר למצוא מודלים מאומנים מראש ואיך לאמן מודלים משלכם.
יש שתי דרכים לשלב מודל בהתאמה אישית. אפשר לארוז את המודל על ידי הוספתו לתיקיית הנכסים של האפליקציה, או להוריד אותו באופן דינמי מ-Cloud Storage. בטבלה הבאה מוצגת השוואה בין שתי האפשרויות.
| מודל בחבילה | מודל מתארח |
|---|---|
| המודל הוא חלק מחבילת ה-APK של האפליקציה, ולכן הוא מגדיל את הגודל שלה. | המודל לא נכלל ב-APK. הוא מתארח על ידי העלאה ל-Cloud Storage. מומלץ להשתמש ב-Cloud Storage for Firebase. |
| המודל זמין באופן מיידי, גם כשמכשיר Android אופליין | האפליקציה צריכה לכלול קוד להורדת המודל לפי דרישה |
| אין צורך בפרויקט Firebase | נדרש פרויקט Firebase (אם משתמשים ב-Cloud Storage for Firebase). |
| כדי לעדכן את המודל, צריך לפרסם מחדש את האפליקציה | עדכון המודל בלי לפרסם מחדש את האפליקציה |
| אין בדיקות A/B מובנות | בדיקות A/B עם הגדרת תצורה מרחוק ב-Firebase |
רוצה לנסות?
- אפשר לראות דוגמה לשימוש במודל החבילה באפליקציית המדריך למתחילים בנושא Vision, ודוגמה לשימוש במודל המתארח באפליקציית המדריך למתחילים בנושא AutoML.
לפני שמתחילים
מוסיפים את ספריות ML Kit ל-Podfile:
pod 'GoogleMLKit/ImageLabelingCustom', '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.
הגדרת הכלי לתווית תמונות
אחרי שמגדירים את מקורות המודל, יוצרים אובייקט 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];
אם יש לכם מודל שמתארח מרחוק, תצטרכו לוודא שהוא הורד לפני שתפעילו פתרונות חכמים.
למרות שצריך לאשר את זה רק לפני שמריצים את הכלי לסימון תוויות, אם יש לכם גם מודל שמתארח מרחוק וגם מודל שמאוגד באופן מקומי, כדאי לבצע את הבדיקה הזו כשיוצרים מופע של ImageLabeler: ליצור כלי לסימון תוויות מהמודל המרוחק אם הוא הורד, ומהמודל המקומי אחרת.
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 = CustomImageLabelerOptions(localModel: model) let imageLabeler = ImageLabeler.imageLabeler(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]; } MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:model]; MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions: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.initializeLabeler(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 labeler self.initializeLabeler(with: modelURL) } } } func initializeLabeler(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomImageLabelerOptions(localModel: localModel) self.imageLabeler = ImageLabeler.imageLabeler(options: options) // Enable ML-related UI features here 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 initializeLabelerWithURL: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 labeler [self initializeLabelerWithURL:URL]; } }]; } - (void)initializeLabelerWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; self.imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options]; // Enable ML-related UI features here [self enableMLFeatures]; }
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 האובייקט לשיטה 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 מייצג משהו שסומן בתמונה. אפשר לקבל את תיאור הטקסט של כל תווית (אם הוא זמין במטא-נתונים של קובץ מודל LiteRT), ציון מהימנות ואינדקס. לדוגמה:
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:). קוראים לפונקציה הזו מהפונקציהAVCaptureVideoDataOutputSampleBufferDelegate'scaptureOutput(_, didOutput:from:)כדי לקבל תוצאות באופן סינכרוני מפריים נתון של סרטון. שומרים עלAVCaptureVideoDataOutputalwaysDiscardsLateVideoFramesבתורtrueכדי להגביל את השיחות לגלאי. אם פריים חדש של סרטון יהיה זמין בזמן שהגלאי פועל, הוא ייפסל. - אם משתמשים בפלט של הגלאי כדי להוסיף גרפיקה לתמונת הקלט, צריך קודם לקבל את התוצאה מ-ML Kit, ואז לעבד את התמונה ולהוסיף את הגרפיקה בשלב אחד. כך, הרינדור מתבצע רק פעם אחת לכל פריים קלט שעבר עיבוד. דוגמה מופיעה ב-updatePreviewOverlayViewWithLastFrame במדריך לתחילת העבודה עם ML Kit.