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

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

כשמעבירים תמונות 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. מגדירים את מזהה האובייקטים לתרחיש לדוגמה באמצעות מאפיין אובייקט 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 מכיל את המאפיינים הבאים:

    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-link]{: .external } ובאוסף דוגמאות לתכונות מבוססות-למידת מכונה של Material Design.

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

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

  • משביתים את הסיווג אם אין צורך בו.

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