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

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

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

רוצה לנסות?

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

  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

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

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

    זיהוי של מספר אובייקטים ומעקב אחריהם 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 מכיל את המאפיינים (properties) הבאים:

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 אוסף תבניות לתכונות מבוססות-למידת מכונה.

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

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