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

אתם יכולים להשתמש ב-ML Kit כדי לזהות ישויות בתמונה ולתייג אותן. ה-API הזה תומך במגוון רחב של מודלים לסיווג תמונות בהתאמה אישית. נשמח אם תוכלו לסמן בכוכב הנחיות לגבי מודלים מותאמים אישית עם ML Kit תאימות למודלים, היכן למצוא מודלים שעברו אימון מראש, ואיך לאמן את המודלים שלכם.

יש שתי דרכים לשלב מודל מותאם אישית. אפשר לקבץ את המודל לפי להוסיף אותו לתיקיית הנכסים של האפליקציה, או שאפשר להוריד אותו באופן דינמי. מ-Firebase. בטבלה הבאה ניתן לראות השוואה בין שתי האפשרויות.

מודל בחבילה מודל מתארח
המודל הוא חלק מה-APK של האפליקציה, ולכן הוא מגדיל את הגודל שלו. המודל אינו חלק מה-APK שלך. כדי לארח את הסרטון, צריך להעלות אותו אל למידת מכונה ב-Firebase.
המודל זמין באופן מיידי, גם כשמכשיר Android במצב אופליין הורדת המודל מתבצעת על פי דרישה
אין צורך בפרויקט Firebase נדרש פרויקט Firebase
צריך לפרסם מחדש את האפליקציה כדי לעדכן את המודל דחיפת עדכוני מודל בלי לפרסם מחדש את האפליקציה
אין בדיקות A/B מובנות לבצע בדיקת A/B קלה ופשוטה באמצעות הגדרת תצורה מרחוק ב-Firebase

רוצה לנסות?

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

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

    לשילוב מודל עם האפליקציה:

    pod 'GoogleMLKit/ImageLabelingCustom', '15.5.0'
    

    כדי להוריד מודל באופן דינמי מ-Firebase, צריך להוסיף את הקוד LinkFirebase של תלות:

    pod 'GoogleMLKit/ImageLabelingCustom', '15.5.0'
    pod 'GoogleMLKit/LinkFirebase', '15.5.0'
    
  2. אחרי שמתקינים או מעדכנים את קבוצות ה-Pod של הפרויקט, פותחים את פרויקט Xcode. באמצעות .xcworkspace שלו. יש תמיכה ב-ML Kit ב-Xcode בגרסה 13.2.1 ומעלה.

  3. אם אתם רוצים להוריד מודל, צריך לוודא מוסיפים את Firebase לפרויקט iOS, אם עדיין לא עשיתם זאת. אין צורך לעשות זאת כשהחבילה של מודל טרנספורמר.

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

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

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

  1. מעתיקים את קובץ המודל (בדרך כלל מסתיים ב-.tflite או ב-.lite) ל-Xcode. תקפיד לבחור באפשרות Copy bundle resources כשתעשו זאת. ייכלל ב-App Bundle, ויהיה זמין ל-ML Kit.

  2. יוצרים אובייקט 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];

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

כדי להוסיף תווית לאובייקטים בתמונה, צריך להעביר את האובייקט imageImageLabeler אמצעי תשלום אחד (process()).

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

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:) של הגלאי. שיחת טלפון שיטה זו מ AVCaptureVideoDataOutputSampleBufferDelegate פונקציה captureOutput(_, didOutput:from:) לקבלת תוצאות בסרטון הנתון באופן סינכרוני מסגרת. שמור את של AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames בתור true כדי לווסת שיחות למזהה. אם תג חדש פריים הווידאו יהפוך לזמין כשהגלאי פועל, הוא יוסר.
  • אם משתמשים בפלט של הגלאי כדי להציג גרפיקה בשכבת-על מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה וליצור שכבת-על בשלב אחד. כך תוכלו להציג את משטח המסך רק פעם אחת לכל מסגרת קלט שעברה עיבוד. אפשר לעיין בתצוגה updatePreviewOverlayViewWithLastFrame בדוגמת המדריך למתחילים ל-ML Kit.