אפשר להוסיף תוויות לתמונות באמצעות מודל שעבר אימון של AutoML ב-iOS

אחרי שמאמנים את המודל באמצעות AutoML Vision Edge, אפשר להשתמש בו באפליקציה כדי להוסיף תוויות לתמונות.

יש שתי דרכים לשלב מודלים שהוכשרו מ-AutoML Vision Edge. אפשר לקבץ את המודל על ידי העתקת קובצי המודל לפרויקט ה-Xcode, או להוריד אותו באופן דינמי מ-Firebase.

אפשרויות של קיבוץ דגמים
מקובצות באפליקציה שלך
  • המודל הוא חלק מהחבילה
  • המודל זמין באופן מיידי, גם כשמכשיר ה-iOS במצב אופליין
  • אין צורך בפרויקט Firebase
אירוח באמצעות Firebase
  • מארחים את המודל על ידי העלאתו ללמידת מכונה ב-Firebase
  • הקטנת הגודל של App Bundle
  • הורדת המודל מתבצעת לפי דרישה
  • דחיפת עדכוני מודל ללא פרסום האפליקציה מחדש
  • מבצעים בדיקות A/B בקלות בעזרת הגדרת תצורה מרחוק ב-Firebase
  • נדרש פרויקט Firebase

אני רוצה לנסות

לפני שמתחילים

1. כוללים את ספריות ML Kit ב-Podfile:

כדי לקבץ מודל עם האפליקציה:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    
כדי להוריד מודל באופן דינמי מ-Firebase, מוסיפים את התלות LinkFirebase:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    pod 'GoogleMLKit/LinkFirebase'
    
2. אחרי שמתקינים או מעדכנים את ה-Pods של הפרויקט, צריך לפתוח את פרויקט ה-Xcode באמצעות .xcworkspacecode> שלו. ערכת ML נתמכת בגרסה 13.2.1 ואילך של Xcode. 3. אם רוצים להוריד מודל, חשוב להוסיף את Firebase לפרויקט iOS, אם עדיין לא עשיתם זאת. לא חייבים לעשות זאת כשמקבצים את המודל.

1. טעינת המודל

הגדרת מקור למודל מקומי

כדי להוסיף את המודל לאפליקציה:

1. מחלצים את המודל ואת המטא-נתונים שלו מארכיון ה-ZIP שהורדתם ממסוף Firebase לתיקייה:
    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    
כל שלושת הקבצים צריכים להיות באותה תיקייה. מומלץ להשתמש בקבצים כפי שהורדתם אותם, בלי לשנות אותם (כולל שמות הקבצים).

2. מעתיקים את התיקייה לפרויקט Xcode ומקפידים לבחור באפשרות יצירת הפניות לתיקיות כשעושים זאת. קובץ המודל והמטא-נתונים ייכללו ב-App Bundle ויהיו זמינים ל-ML Kit.

3. יוצרים אובייקט AutoMLImageLabelerLocalModel ומציינים את הנתיב לקובץ המניפסט של המודל:

Swift

guard let manifestPath = Bundle.main.path(
    forResource: "manifest",
    ofType: "json",
    inDirectory: "your_model_directory"
) else { return }
let localModel = AutoMLImageLabelerLocalModel(manifestPath: manifestPath)

Objective-C

NSString *manifestPath =
    [NSBundle.mainBundle pathForResource:@"manifest"
                                  ofType:@"json"
                             inDirectory:@"your_model_directory"];
MLKAutoMLImageLabelerLocalModel *localModel =
    [[MLKAutoMLImageLabelerLocalModel alloc] initWithManifestPath:manifestPath];

הגדרת מקור מודל שמתארח ב-Firebase

כדי להשתמש במודל באירוח מרוחק, יוצרים אובייקט AutoMLImageLabelerRemoteModel ומציינים את השם שנתתם למודל כשפרסמתם אותו:

Swift

let remoteModel = AutoMLImageLabelerRemoteModel(
    name: "your_remote_model"  // The name you assigned in
                               // the Firebase console.
)

Objective-C

MLKAutoMLImageLabelerRemoteModel *remoteModel =
    [[MLKAutoMLImageLabelerRemoteModel alloc]
        initWithName:@"your_remote_model"];  // The name you assigned in
                                             // the Firebase console.

לאחר מכן, מתחילים את המשימה של הורדת המודל, ומציינים את התנאים שבהם רוצים לאפשר הורדה. אם המודל לא נמצא במכשיר, או אם יש גרסה חדשה יותר שלו, תתבצע במשימה הורדה אסינכרונית של המודל מ-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 מאחד מהם.

אם יש לכם רק מודל בחבילה מקומית, כל מה שצריך לעשות הוא ליצור מתייג מהאובייקט AutoMLImageLabelerLocalModel ולהגדיר את ערך הסף של דירוג המהימנות הרצוי (קראו את המאמר הערכת המצב:

Swift

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)

Objective-C

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: יוצרים מתייג מהמודל המרוחק אם הוא הורד, ומהמודל המקומי אחרת.

Swift

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)

Objective-C

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 בבלוק של הצופה, כי יכול להיות שהורדות עשויות להימשך זמן מה, והאובייקט המקורי יכול להיות משוחרר עד לסיום ההורדה. למשל:

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. הרצה של מתייג התמונות

באופן אסינכרוני:

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 (labels.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.