זיהוי של אובייקטים ומעקב אחריהם באמצעות ערכת למידת מכונה ב-iOS

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

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

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

  1. אם עדיין לא הוספתם את Firebase לאפליקציה, תוכלו לפעול לפי השלבים שמפורטים במדריך לתחילת העבודה.
  2. כוללים את ספריות ML Kit ב-Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    אחרי שמתקינים או מעדכנים את קבוצות ה-Pod של הפרויקט, חשוב לפתוח את ה-Xcode באמצעות .xcworkspace של הפרויקט שלו.
  3. מייבאים את Firebase לאפליקציה:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

1. הגדרת הכלי לזיהוי אובייקטים

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

  1. מגדירים את ה-Object Detector לתרחיש לדוגמה באמצעות אובייקט VisionObjectDetectorOptions. אפשר לשנות את ההגדרות הבאות:

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

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

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

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

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

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

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

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

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

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

    Swift

    // Live detection and tracking
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .stream
    options.shouldEnableMultipleObjects = false
    options.shouldEnableClassification = true  // Optional
    
    // Multiple object detection in static images
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .singleImage
    options.shouldEnableMultipleObjects = true
    options.shouldEnableClassification = true  // Optional
    

    Objective-C

    // Live detection and tracking
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeStream;
    options.shouldEnableMultipleObjects = NO;
    options.shouldEnableClassification = YES;  // Optional
    
    // Multiple object detection in static images
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeSingleImage;
    options.shouldEnableMultipleObjects = YES;
    options.shouldEnableClassification = YES;  // Optional
    
  2. אחזור מופע של FirebaseVisionObjectDetector:

    Swift

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

    Objective-C

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

2. הפעלת מזהה האובייקטים

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

  1. יצירת אובייקט VisionImage באמצעות UIImage או CMSampleBufferRef

    כדי להשתמש ב-UIImage:

    1. במקרה הצורך, מסובבים את התמונה כך ש-imageOrientation הוא .up.
    2. יצירת אובייקט VisionImage באמצעות סיבוב נכון UIImage. אל תציינו מטא-נתונים של סבב, ברירת המחדל יש להשתמש בערך .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    כדי להשתמש ב-CMSampleBufferRef:

    1. יוצרים אובייקט VisionImageMetadata שמציין את הכיוון של נתוני התמונה שמכיל מאגר ה-CMSampleBufferRef.

      כדי לקבל את כיוון התמונה:

      Swift

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      לאחר מכן, יוצרים את אובייקט המטא-נתונים:

      Swift

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. יוצרים אובייקט VisionImage באמצעות אובייקט CMSampleBufferRef והמטא-נתונים של הסבב:

      Swift

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. מעבירים את VisionImage לאחד מהשיטות של עיבוד התמונה של גלאי האובייקטים. אפשר להשתמש בשיטה process(image:) האסינכרונית או ב- שיטת results() סינכרונית.

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

    Swift

    objectDetector.process(image) { detectedObjects, error in
      guard error == nil else {
        // Error.
        return
      }
      guard let detectedObjects = detectedObjects, !detectedObjects.isEmpty else {
        // No objects detected.
        return
      }
    
      // Success. Get object info here.
      // ...
    }
    

    Objective-C

    [objectDetector processImage:image
                      completion:^(NSArray<FIRVisionObject *> * _Nullable objects,
                                   NSError * _Nullable error) {
                        if (error == nil) {
                          return;
                        }
                        if (objects == nil | objects.count == 0) {
                          // No objects detected.
                          return;
                        }
    
                        // Success. Get object info here.
                        // ...
                      }];
    

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

    Swift

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

    Objective-C

    NSError *error;
    NSArray<FIRVisionObject *> *objects = [objectDetector resultsInImage:image
                                                                   error:&error];
    if (error == nil) {
      return;
    }
    if (objects == nil | objects.count == 0) {
      // No objects detected.
      return;
    }
    
    // Success. Get object info here.
    // ...
    
  3. אם הקריאה למעבד התמונות מצליחה, היא מעבירה רשימה של VisionObject לטיפול בהשלמה או מחזירה את הרשימה, בהתאם לקריאה לשיטה האסינכרונית או לקריאה לשיטה הסינכרונית.

    כל VisionObject מכיל את המאפיינים (properties) הבאים:

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

    Swift

    // detectedObjects contains one item if multiple object detection wasn't enabled.
    for obj in detectedObjects {
      let bounds = obj.frame
      let id = obj.trackingID
    
      // If classification was enabled:
      let category = obj.classificationCategory
      let confidence = obj.confidence
    }
    

    Objective-C

    // The list of detected objects contains one item if multiple
    // object detection wasn't enabled.
    for (FIRVisionObject *obj in objects) {
      CGRect bounds = obj.frame;
      if (obj.trackingID) {
        NSInteger id = obj.trackingID.integerValue;
      }
    
      // If classification was enabled:
      FIRVisionObjectCategory category = obj.classificationCategory;
      float confidence = obj.confidence.floatValue;
    }
    

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

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

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

בנוסף, כדאי לעיין [ML Kit Material Design Showcase][showcase-link]{: .external } וגם עיצוב חומר אוסף תבניות לתכונות מבוססות-למידת מכונה.

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

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

  • אפשר להשבית את הסיווג אם לא צריך אותו.

  • צמצום מספר הקריאות למזהה. אם מסגרת וידאו חדשה זמינה בזמן שהגלאי פועל, צריך להסיר את המסגרת.
  • אם אתם משתמשים בפלט של הגלאי כדי להוסיף שכבת-על של גרפיקה לתמונה הקלט, קודם צריך לקבל את התוצאה מ-ML Kit, ואז לבצע עיבוד (רנדור) של התמונה ושל שכבת-העל בשלב אחד. כך תוכלו להציג את משטח המסך פעם אחת בלבד לכל מסגרת קלט. אפשר לעיין ב-previewOverlayView ו-FIRDetectionOverlayView באפליקציה לדוגמה של Showcase.