在 iOS 上使用 ML Kit 來偵測和追蹤對象

您可以使用 ML Kit 跨視訊幀檢測和追蹤物件。

當您傳遞 ML Kit 映像時,ML Kit 會為每個影像傳回最多五個偵測到的物件及其在影像中的位置的清單。偵測視訊串流中的物件時,每個物件都有一個 ID,可用於跨影像追蹤物件。您也可以選擇啟用粗略物件分類,這會使用廣泛的類別描述來標記物件。

在你開始之前

  1. 如果您尚未將 Firebase 新增至您的應用程式中,請按照入門指南中的步驟進行操作。
  2. 在 Podfile 中包含 ML Kit 函式庫:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    安裝或更新專案的 Pod 後,請務必使用其.xcworkspace開啟 Xcode 專案。
  3. 在您的應用程式中,導入 Firebase:

    迅速

    import Firebase

    Objective-C

    @import Firebase;

1. 配置物體偵測器

若要開始偵測和追蹤對象,請先建立VisionObjectDetector的實例,可以選擇指定要變更預設設定的任何偵測器設定。

  1. 使用VisionObjectDetectorOptions物件為您的用例配置物件偵測器。您可以更改以下設定:

    物體偵測器設定
    檢測方式.stream (預設)| .singleImage

    在流程模式(預設)下,物件偵測器以非常低的延遲運行,但在偵測器的前幾次呼叫時可能會產生不完整的結果(例如未指定的邊界框或類別)。此外,在串流模式下,偵測器會將追蹤 ID 指派給對象,您可以使用它來跨幀追蹤對象。當您想要追蹤物件或低延遲很重要時(例如即時處理視訊串流時),請使用此模式。

    在單一影像模式下,物件偵測器會等待,直到偵測到的物件的邊界框和(如果啟用了分類)類別可用,然後才傳回結果。因此,檢測延遲可能會更高。此外,在單一影像模式下,不會指派追蹤 ID。如果延遲並不重要且您不想處理部分結果,請使用此模式。

    偵測並追蹤多個物體false (預設)| true

    是否檢測和追蹤最多五個物件或僅檢測和追蹤最突出的物件(預設)。

    將物體分類false (預設)| true

    是否將偵測到的物件分類為粗略類別。啟用後,物件偵測器會將物件分為以下類別:時尚商品、食品、家居用品、地點、植物和未知。

    物件偵測和追蹤 API 針對這兩個核心用例進行了最佳化:

    • 即時偵測並追蹤相機取景器中最突出的物體
    • 偵測靜態影像中的多個對象

    若要為這些用例配置 API:

    迅速

    // 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的實例:

    迅速

    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. 運行物體偵測器

若要偵測和追蹤對象,請對每個影像或視訊畫面執行以下操作。如果啟用了流模式,則必須從CMSampleBufferRef建立VisionImage物件。

  1. 使用UIImageCMSampleBufferRef建立VisionImage物件。

    使用UIImage

    1. 如有必要,旋轉影像,使其imageOrientation屬性為.up
    2. 使用正確旋轉的UIImage建立VisionImage物件。不要指定任何旋轉元資料 - 必須使用預設值.topLeft

      迅速

      let image = VisionImage(image: uiImage)

      Objective-C

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

    使用CMSampleBufferRef

    1. 建立一個VisionImageMetadata對象,該對象指定CMSampleBufferRef緩衝區中包含的圖像資料的方向。

      若要取得影像方向:

      迅速

      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;
        }
      }

      然後,建立元資料物件:

      迅速

      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. 使用CMSampleBufferRef物件和旋轉元資料建立VisionImage物件:

      迅速

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. VisionImage傳遞給物件偵測器的影像處理方法之一。您可以使用非同步process(image:)方法或同步results()方法。

    非同步檢測對象:

    迅速

    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.
                        // ...
                      }];
    

    同步檢測對象:

    迅速

    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

    迅速

    // 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 模式

在即時應用程式中使用串流模式時,請遵循以下準則以獲得最佳幀速率:

  • 不要在串流模式下使用多個物件偵測,因為大多數裝置無法產生足夠的幀速率。

  • 如果不需要,請停用分類。

  • 對檢測器的節流呼叫。如果偵測器運作時有新的視訊幀可用,則丟棄該幀。
  • 如果您使用偵測器的輸出將圖形疊加在輸入影像上,請先從 ML Kit 取得結果,然後一步渲染影像並疊加。透過這樣做,每個輸入幀只需渲染到顯示表面一次。有關範例,請參閱展示範例應用程式中的PreviewOverlayViewFIRDetectionOverlayView類別。