זיהוי אובייקטים ומעקב אחריהם באמצעות ML Kit ב-iOS

קל לארגן דפים בעזרת אוספים אפשר לשמור ולסווג תוכן על סמך ההעדפות שלך.

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

כשמעבירים תמונה ל-ML Kit, היא מזהה עד חמישה אובייקטים בתמונה יחד עם המיקום של כל אובייקט בתמונה. בעת זיהוי אובייקטים בווידאו בסטרימינג, לכל אובייקט יש מזהה ייחודי שאפשר להשתמש בו כדי לעקוב אחר האובייקט ממסגרת לפריים. אפשר גם להפעיל סיווג של אובייקט גס, שמתייג אובייקטים בתיאורי קטגוריות רחבים.

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

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

1. הגדרה של מזהה האובייקט

כדי לזהות אובייקטים ולעקוב אחריהם, קודם כול יוצרים מופע של ObjectDetector ומגדירים באופן אופציונלי את הגדרות המזהים שרוצים לשנות כברירת מחדל.

  1. צריך להגדיר את מזהה האובייקט לתרחיש לדוגמה באמצעות אובייקט ObjectDetectorOptions. תוכלו לשנות את ההגדרות הבאות:

    הגדרות של מזהה אובייקטים
    מצב זיהוי .stream (ברירת מחדל) | .singleImage

    במצב זרם (ברירת מחדל), מזהה האובייקט פועל עם זמן אחזור קצר מאוד, אבל הוא עשוי להניב תוצאות חלקיות (למשל, תיבות או קטגוריות לא מוגדרות) בהפעלה הראשונה של המזהה. בנוסף, במצב סטרימינג, המזהה מקצה מזהים לצורכי מעקב לאובייקטים. באובייקטים אלה אפשר להשתמש כדי לעקוב אחר אובייקטים במסגרות. יש להשתמש במצב הזה כשרוצים לעקוב אחר אובייקטים או כשחשוב זמן אחזור קצר, למשל בזמן עיבוד זרמי וידאו בזמן אמת.

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

    זיהוי אובייקטים מרובים ומעקב אחריהם false (ברירת מחדל) | true

    האם לזהות עד חמישה אובייקטים או רק את האובייקט הבולט ביותר ולעקוב אחריהם (ברירת מחדל).

    סיווג אובייקטים false (ברירת מחדל) | true

    סיווג של אובייקטים לפי קטגוריות גסות או לא. כשהתכונה מופעלת, מזהה האובייקט מסווג את האובייקטים בקטגוריות הבאות: מוצרי אופנה, מזון, מוצרים לבית, מקומות וצמחים.

    ה-API לזיהוי ולמעקב אחר אובייקטים מותאם לשני התרחישים לדוגמה הבאים:

    • זיהוי בזמן אמת של המעקב אחר האובייקט הבולט ביותר בעינית של המצלמה.
    • זיהוי של מספר אובייקטים בתמונה סטטית.

    כדי להגדיר את ה-API לתרחישים לדוגמה אלה:

Swift

// Live detection and tracking
let options = ObjectDetectorOptions()
options.shouldEnableClassification = true

// Multiple object detection in static images
let options = ObjectDetectorOptions()
options.detectorMode = .singleImage
options.shouldEnableMultipleObjects = true
options.shouldEnableClassification = true

Objective-C

// Live detection and tracking
MLKObjectDetectorOptions *options = [[MLKObjectDetectorOptions alloc] init];
options.shouldEnableClassification = YES;

// Multiple object detection in static images
MLKObjectDetectorOptions *options = [[MLKOptions alloc] init];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableMultipleObjects = YES;
options.shouldEnableClassification = YES;
  1. הצגת מכונה של ObjectDetector:

Swift

let objectDetector = ObjectDetector.objectDetector()

// Or, to change the default settings:
let objectDetector = ObjectDetector.objectDetector(options: options)

Objective-C

MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetector];

// Or, to change the default settings:
MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];

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

כדי לזהות אובייקטים ולעקוב אחריהם, יש לבצע את הפעולות הבאות עבור כל תמונה או מסגרת של סרטון. אם הפעלת את מצב הסטרימינג, עליך ליצור VisionImage אובייקטים מתוך CMSampleBuffer שנ'.

יצירת אובייקט 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. עיבוד התמונה

יש להעביר את ה-VisionImage אל אחת משיטות עיבוד התמונות של מזהה האובייקט. אפשר להשתמש בשיטה האסינכרונית process(image:) או בשיטת results() הסינכרונית.

כדי לזהות אובייקטים באופן אסינכרוני:

Swift

objectDetector.process(image) { objects, error in
  guard error == nil else {
    // Error.
    return
  }
  guard !objects.isEmpty else {
    // No objects detected.
    return
  }

  // Success. Get object info here.
  // ...
}

Objective-C

[objectDetector processImage:image
                  completion:^(NSArray * _Nullable objects,
                               NSError * _Nullable error) {
                    if (error == nil) {
                      return;
                    }
                    if (objects.count == 0) {
                      // No objects detected.
                      return;
                    }

                    // Success. Get object info here.
                  }];

כדי לזהות אובייקטים באופן סינכרוני:

Swift

var objects: [Object]
do {
  objects = try objectDetector.results(in: image)
} catch let error {
  print("Failed to detect object with error: \(error.localizedDescription).")
  return
}
guard !objects.isEmpty else {
  print("Object detector returned no results.")
  return
}

// Success. Get object info here.

Objective-C

NSError *error;
NSArray *objects = [objectDetector resultsInImage:image error:&error];
if (error == nil) {
  return;
}
if (objects.count == 0) {
  // No objects detected.
  return;
}

// Success. Get object info here.

4. קבלת מידע על אובייקטים שזוהו

אם הקריאה למעבד התמונה מצליחה, היא מעבירה רשימה של Object אל ה-handler להשלמה, או מחזירה את הרשימה, בהתאם לקריאה לשיטה האסינכרונית או הסינכרונית.

כל מאפיין Object מכיל את המאפיינים הבאים:

frame CGRect המציין את מיקום האובייקט בתמונה.
trackingID מספר שלם שמזהה את האובייקט בתמונות, או 'nil' במצב תמונה אחת.
labels מערך של תוויות המתארות את האובייקט שהוחזר על ידי המזהה. המאפיין ריק אם אפשרות המזהה shouldEnableClassification מוגדרת לערך false.

Swift

// objects contains one item if multiple object detection wasn't enabled.
for object in objects {
  let frame = object.frame
  let trackingID = object.trackingID

  // If classification was enabled:
  let description = object.labels.enumerated().map { (index, label) in
    "Label \(index): \(label.text), \(label.confidence)"
    }.joined(separator:"\n")

}

Objective-C

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (MLKObject *object in objects) {
  CGRect frame = object.frame;
  NSNumber *trackingID = object.trackingID;
  for (MLKObjectLabel *label in object.labels) {
    NSString *labelString = [NSString stringWithFormat: @"%@, %f, %lu",
      label.text, label.confidence, (unsigned long)label.index];
    ...
  }
}

שיפור נוחות השימוש והביצועים

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

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

מומלץ גם לעיין באוסף תבניות עיצוב של תכונות עיצוב חדשני תלת-ממדי.

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

  • אין להשתמש בזיהוי אובייקטים מרובים במצב סטרימינג, מפני שרוב המכשירים לא יוכלו להפיק קצב פריימים הולם.
  • השבתת הסיווג אם אין צורך בו.
  • לעיבוד מסגרות וידאו, יש להשתמש ב-API הסינכרוני של results(in:) עבור המזהה. התקשר לשיטה הזו מהפונקציה AVCaptureVideoDataOutputSampleBufferDelegate's captureOutput(_, didOutput:from:) כדי לקבל תוצאות באופן יזום ממסגרת הווידאו הנתונה. Keep AVCaptureVideoDataOutput's alwaysDiscardsLateVideoFrames כtrueויסות שיחות למזהה. אם פריים חדש של וידאו הופך לזמין בזמן שהמזהה פועל, הוא יוסר.
  • אם משתמשים בפלט של מזהה-העל כדי להציג גרפיקה על תמונת הקלט, תחילה יש לקבל את התוצאה מ-ML Kit, ולאחר מכן לעבד את התמונה ואת שכבת-העל בפעולה אחת. כך ניתן לעבד את שטח התצוגה פעם אחת בלבד בכל מסגרת של עיבוד נתונים. כדי לראות דוגמה, אפשר לעיין ב-updatePreviewViewViewWithLastFrame בדוגמה למתחילים.