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

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

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

רוצה לנסות?

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

  1. יש לכלול את ה-pods הבאים ב-ML Kit ב-Podfile:
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. אחרי ההתקנה או העדכון של ה-Pods של הפרויקט, פותחים את פרויקט Xcode באמצעות .xcworkspace שלו. ערכת ה-ML נתמכת ב-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. קבלת מידע על אובייקטים שזוהו

אם הקריאה למעבד התמונות מצליחה, היא מעבירה רשימה של Objects ל-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];
    ...
  }
}

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

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

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

כדאי גם לבדוק את האוסף Material Design תבניות עבור תכונות מבוססות-למידת מכונה.

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

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