תיוג תמונות באמצעות מודל שאומן באמצעות AutoML ב-iOS
אחרי שמאמנים מודל משלכם באמצעות AutoML Vision Edge, אפשר להשתמש בו באפליקציה כדי לתייג תמונות.
יש שתי דרכים לשלב מודלים שהוכשרו באמצעות AutoML Vision Edge. אפשר לארוז את המודל על ידי העתקת הקבצים שלו לפרויקט Xcode, או להוריד אותו באופן דינמי מ-Firebase.
אפשרויות של חבילות מודלים | |
---|---|
חבילה באפליקציה |
|
אירוח ב-Firebase |
|
רוצה לנסות?
- כדאי לנסות את האפליקציה לדוגמה כדי לראות דוגמה לשימוש ב-API הזה.
לפני שמתחילים
1. מוסיפים את ספריות ML Kit ל-Podfile:כדי לארוז מודל עם האפליקציה:
pod 'GoogleMLKit/ImageLabelingAutoML'
LinkFirebase
:
pod 'GoogleMLKit/ImageLabelingAutoML' pod 'GoogleMLKit/LinkFirebase'
.xcworkspace
code>. ML Kit נתמך ב-Xcode בגרסה 13.2.1 ואילך.
3. אם רוצים להוריד מודל, צריך לוודא שמוסיפים את Firebase לפרויקט ב-iOS, אם עדיין לא עשיתם זאת. לא צריך לעשות זאת כשמקבצים את המודל.
1. טעינת המודל
הגדרת מקור מודל מקומי
כדי לצרף את המודל לאפליקציה:1. מחלצים את המודל ואת המטא-נתונים שלו מהארכיון בפורמט zip שהורדתם ממסוף Firebase לתיקייה:
your_model_directory |____dict.txt |____manifest.json |____model.tflite
2. מעתיקים את התיקייה לפרויקט Xcode, תוך הקפדה על בחירה באפשרות Create folder references. קובץ המודל והמטא-נתונים ייכללו בחבילת האפליקציה ויהיה אפשר לגשת אליהם דרך ML Kit.
3. יוצרים אובייקט
AutoMLImageLabelerLocalModel
ומציינים את הנתיב לקובץ המניפסט של המודל:
guard let manifestPath = Bundle.main.path( forResource: "manifest", ofType: "json", inDirectory: "your_model_directory" ) else { return } let localModel = AutoMLImageLabelerLocalModel(manifestPath: manifestPath)
NSString *manifestPath = [NSBundle.mainBundle pathForResource:@"manifest" ofType:@"json" inDirectory:@"your_model_directory"]; MLKAutoMLImageLabelerLocalModel *localModel = [[MLKAutoMLImageLabelerLocalModel alloc] initWithManifestPath:manifestPath];
הגדרת מקור מודל שמתארח ב-Firebase
כדי להשתמש במודל שמתארח מרחוק, יוצרים אובייקט AutoMLImageLabelerRemoteModel
ומציינים את השם שהקציתם למודל כשפרסמתם אותו:
let remoteModel = AutoMLImageLabelerRemoteModel( name: "your_remote_model" // The name you assigned in // the Firebase console. )
MLKAutoMLImageLabelerRemoteModel *remoteModel = [[MLKAutoMLImageLabelerRemoteModel alloc] initWithName:@"your_remote_model"]; // The name you assigned in // the Firebase console.
לאחר מכן, מפעילים את המשימה של הורדת המודל ומציינים את התנאים שבהם רוצים לאפשר הורדה. אם המודל לא נמצא במכשיר או אם יש גרסה חדשה יותר של המודל, המשימה תוריד את המודל מ-Firebase באופן אסינכררוני:
let downloadConditions = ModelDownloadConditions( allowsCellularAccess: true, allowsBackgroundDownloading: true ) let downloadProgress = ModelManager.modelManager().download( remoteModel, conditions: downloadConditions )
MLKModelDownloadConditions *downloadConditions = [[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES allowsBackgroundDownloading:YES]; NSProgress *downloadProgress = [[MLKModelManager modelManager] downloadModel:remoteModel conditions:downloadConditions];
באפליקציות רבות, משימה ההורדה מתחילה בקוד האיניציאליזציה, אבל אפשר לעשות זאת בכל שלב לפני שמשתמשים במודל.
יצירת כלי לתיוג תמונות מהמודל
אחרי שמגדירים את מקורות המודלים, יוצרים אובייקט ImageLabeler
מאחד מהם.
אם יש לכם רק מודל בחבילה מקומית, פשוט יוצרים מכשיר לתיוג מהאובייקט AutoMLImageLabelerLocalModel
ומגדירים את סף ציון האמון הנדרש (ראו בדיקת המצב:
let options = AutoMLImageLabelerOptions(localModel: localModel) options.confidenceThreshold = NSNumber(value: 0.0) // Evaluate your model in the Firebase console // to determine an appropriate value. let imageLabeler = ImageLabeler.imageLabeler(options: options)
MLKAutoMLImageLabelerOptions *options = [[MLKAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel]; options.confidenceThreshold = @(0.0); // Evaluate your model in the Firebase console // to determine an appropriate value. MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
אם יש לכם מודל שמתארח מרחוק, תצטרכו לוודא שהוא הורדה לפני שתפעילו אותו. אפשר לבדוק את סטטוס המשימה של הורדת המודל באמצעות השיטה isModelDownloaded
(remoteModel:) של מנהל המודל.
צריך לאשר את זה רק לפני שמפעילים את הלייבלר, אבל אם יש לכם גם מודל שמתארח מרחוק וגם מודל שמקובץ באופן מקומי, כדאי לבצע את הבדיקה הזו כשיוצרים את המופע של ImageLabeler
: יוצרים לייבלר מהמודל המרוחק אם הוא הועלה, וממודל מקומי אחרת.
var options: AutoMLImageLabelerOptions! if (ModelManager.modelManager().isModelDownloaded(remoteModel)) { options = AutoMLImageLabelerOptions(remoteModel: remoteModel) } else { options = AutoMLImageLabelerOptions(localModel: localModel) } options.confidenceThreshold = NSNumber(value: 0.0) // Evaluate your model in the Firebase console // to determine an appropriate value. let imageLabeler = ImageLabeler.imageLabeler(options: options)
MLKAutoMLImageLabelerOptions *options; if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) { options = [[MLKAutoMLImageLabelerOptions alloc] initWithRemoteModel:remoteModel]; } else { options = [[MLKAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel]; } options.confidenceThreshold = @(0.0); // Evaluate your model in the Firebase console // to determine an appropriate value. MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
אם יש לכם רק מודל שמתארח מרחוק, עליכם להשבית את הפונקציונליות שקשורה למודל – לדוגמה, להפוך חלק מממשק המשתמש לאפור או להסתיר אותו – עד שתאשרו שהמודל הוריד.
כדי לקבל את סטטוס ההורדה של המודל, צריך לצרף משגיחים למרכז ההתראות שמוגדר כברירת מחדל. חשוב להשתמש בהפניה חלשה ל-self
בבלוק הצופה, כי ההורדות עשויות להימשך זמן מה, והאובייקט המקור יכול להשתחרר עד שההורדה תסתיים. לדוגמה:
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] // ... }
__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
.let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
אם אתם משתמשים ב-CMSampleBuffer
, עליכם לפעול לפי השלבים הבאים:
-
מציינים את הכיוון של נתוני התמונה שמכיל השדה
CMSampleBuffer
.כדי לקבל את כיוון התמונה:
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 } }
- (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
והכיוון:let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. הפעלת הכלי לתיוג תמונות
באופן אסינכרוני:
imageLabeler.process(image) { labels, error in guard error == nil, let labels = labels, !labels.isEmpty else { // Handle the error. return } // Show results. }
[imageLabeler processImage:image completion:^(NSArray*_Nullable labels, NSError *_Nullable error) { if (labels.count == 0) { // Handle the error. return; } // Show results. }];
באופן סינכרוני:
var labels: [ImageLabel] do { labels = try imageLabeler.results(in: image) } catch let error { // Handle the error. return } // Show results.
NSError *error; NSArray*labels = [imageLabeler resultsInImage:image error:&error]; // Show results or handle the error.
4. אחזור מידע על אובייקטים מתויגים
אם פעולת התיוג של התמונה תצליח, היא תחזיר מערך שלImageLabel
. כל ImageLabel
מייצג משהו שסומן בתמונה. אפשר לקבל את תיאור הטקסט של כל תווית (אם הוא זמין במטא-נתונים של קובץ המודל של TensorFlow Lite), את ציון האמון ואת האינדקס שלה.
לדוגמה:
for label in labels { let labelText = label.text let confidence = label.confidence let index = label.index }
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 Kit.