תיוג תמונות באמצעות ML Kit ב-iOS

אפשר להשתמש בלמידת מכונה (ML Kit) כדי לתייג אובייקטים שזוהו בתמונה. דגם ברירת המחדל שכלול ב-ML Kit תומך ביותר מ-400 תוויות שונות.

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

  1. יש לכלול ב-Podfile את מוטות ה-ML Kit הבאים:
    pod 'GoogleMLKit/ImageLabeling', '3.2.0'
    
  2. לאחר ההתקנה או עדכון של ה-Pod של הפרויקט, פותחים את פרויקט Xcode באמצעות .xcworkspace. ערכת ה-ML Kit נתמכת בגרסה Xcode 12.4 ומעלה.

עכשיו אפשר להוסיף תוויות לתמונות.

1. הכנת תמונת הקלט

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

2. הגדרה והפעלה של מתייג התמונות

כדי לסמן אובייקטים בתמונה, מעבירים את האובייקט VisionImage אל השיטה processImage() של ImageLabeler.

  1. תחילה, יש לקבל מכונה של ImageLabeler.

Swift

let labeler = ImageLabeler.imageLabeler()

// Or, to set the minimum confidence required:
// let options = ImageLabelerOptions()
// options.confidenceThreshold = 0.7
// let labeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKImageLabeler *labeler = [MLKImageLabeler imageLabeler];

// Or, to set the minimum confidence required:
// MLKImageLabelerOptions *options =
//         [[MLKImageLabelerOptions alloc] init];
// options.confidenceThreshold = 0.7;
// MLKImageLabeler *labeler =
//         [MLKImageLabeler imageLabelerWithOptions:options];
  1. לאחר מכן, מעבירים את התמונה לשיטה processImage():

Swift

labeler.process(image) { labels, error in
    guard error == nil, let labels = labels else { return }

    // Task succeeded.
    // ...
}

Objective-C

[labeler processImage:image
completion:^(NSArray *_Nullable labels,
            NSError *_Nullable error) {
   if (error != nil) { return; }

   // Task succeeded.
   // ...
}];

3. קבלת מידע על אובייקטים מתויגים

אם יצירת התוויות של התמונות תושלם, ה-handler של ההשלמה יקבל מערך של ImageLabel אובייקטים. כל אובייקט ImageLabel מייצג משהו שתויג בתמונה. מודל הבסיס תומך ביותר מ-400 תוויות שונות. ניתן לקבל את תיאור הטקסט של כל תווית, להוסיף לאינדקס בין כל התוויות הנתמכות לפי המודל ואת ציון המהימנות של ההתאמה. למשל:

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