Phát hiện và theo dõi đối tượng bằng Bộ công cụ học máy trên iOS

Bạn có thể dùng Bộ công cụ học máy để phát hiện và theo dõi các đối tượng trên nhiều khung hình của video.

Khi bạn truyền hình ảnh của Bộ công cụ học máy, Bộ công cụ học máy sẽ trả về một danh sách tối đa 5 đối tượng được phát hiện và vị trí của các đối tượng đó trong hình ảnh cho mỗi hình ảnh. Khi phát hiện các đối tượng trong luồng video, mỗi đối tượng đều có một mã nhận dạng mà bạn có thể dùng để theo dõi đối tượng trên các hình ảnh. Bạn cũng có thể tuỳ ý bật tính năng phân loại đối tượng tương đối. Tính năng này gắn nhãn các đối tượng bằng nội dung mô tả danh mục rộng.

Trước khi bắt đầu

  1. Nếu bạn chưa thêm Firebase vào ứng dụng của mình, hãy thực hiện bằng cách làm theo các bước trong hướng dẫn bắt đầu sử dụng.
  2. Đưa các thư viện Bộ công cụ học máy vào Podfile của bạn:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    Sau khi bạn cài đặt hoặc cập nhật Nhóm của dự án, hãy nhớ mở dự án Xcode bằng cách sử dụng .xcworkspace của dự án đó.
  3. Trong ứng dụng của bạn, hãy nhập Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

1. Định cấu hình trình phát hiện đối tượng

Để bắt đầu phát hiện và theo dõi đối tượng, trước tiên, hãy tạo một thực thể của VisionObjectDetector, tuỳ ý chỉ định bất kỳ chế độ cài đặt trình phát hiện nào bạn muốn thay đổi so với giá trị mặc định.

  1. Định cấu hình trình phát hiện đối tượng cho trường hợp sử dụng của bạn bằng đối tượng VisionObjectDetectorOptions. Bạn có thể thay đổi các chế độ cài đặt sau:

    Cài đặt trình phát hiện đối tượng
    Chế độ phát hiện .stream (mặc định) | .singleImage

    Ở chế độ phát trực tuyến (mặc định), trình phát hiện đối tượng chạy với độ trễ rất thấp nhưng có thể dẫn đến kết quả không đầy đủ (chẳng hạn như các hộp hoặc danh mục giới hạn không xác định) trong một số lệnh gọi đầu tiên của trình phát hiện. Ngoài ra, ở chế độ phát trực tuyến, trình phát hiện sẽ gán mã theo dõi cho các đối tượng và bạn có thể dùng mã này để theo dõi các đối tượng trên nhiều khung hình. Hãy sử dụng chế độ này khi bạn muốn theo dõi các đối tượng hoặc khi độ trễ thấp rất quan trọng, chẳng hạn như khi xử lý luồng video theo thời gian thực.

    Ở chế độ hình ảnh đơn, trình phát hiện đối tượng sẽ đợi cho đến khi hộp giới hạn của đối tượng được phát hiện và danh mục (nếu bạn đã bật tính năng phân loại) xuất hiện trước khi trả về kết quả. Do đó, độ trễ phát hiện có thể cao hơn. Ngoài ra, ở chế độ một hình ảnh, mã theo dõi không được chỉ định. Hãy sử dụng chế độ này nếu độ trễ không quan trọng và bạn không muốn xử lý kết quả một phần.

    Phát hiện và theo dõi nhiều đối tượng false (mặc định) | true

    Xác định xem có phát hiện và theo dõi tối đa 5 đối tượng hay chỉ phát hiện đối tượng nổi bật nhất (mặc định).

    Phân loại đối tượng false (mặc định) | true

    Liệu có phân loại các đối tượng được phát hiện thành các danh mục tương đối hay không. Khi được bật, trình phát hiện vật thể sẽ phân loại các đối tượng thành các danh mục sau: hàng thời trang, thực phẩm, hàng gia dụng, địa điểm, thực vật và không xác định.

    API theo dõi và phát hiện đối tượng được tối ưu hoá cho hai trường hợp sử dụng chính sau:

    • Phát hiện trực tiếp và theo dõi đối tượng nổi bật nhất trong kính ngắm của máy ảnh
    • Phát hiện nhiều đối tượng trong ảnh tĩnh

    Cách định cấu hình API cho các trường hợp sử dụng này:

    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. Nhận một thực thể của 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. Chạy trình phát hiện đối tượng

Để phát hiện và theo dõi các đối tượng, hãy làm như sau cho từng hình ảnh hoặc khung video. Nếu đã bật chế độ phát trực tiếp, bạn phải tạo các đối tượng VisionImage từ các CMSampleBufferRef.

  1. Tạo đối tượng VisionImage bằng UIImage hoặc CMSampleBufferRef.

    Cách sử dụng UIImage:

    1. Nếu cần, hãy xoay hình ảnh để thuộc tính imageOrientation của hình ảnh là .up.
    2. Tạo đối tượng VisionImage bằng UIImage xoay chính xác. Không chỉ định bất kỳ siêu dữ liệu nào về chế độ xoay – bạn phải sử dụng giá trị mặc định, .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Cách sử dụng CMSampleBufferRef:

    1. Tạo một đối tượng VisionImageMetadata chỉ định hướng của dữ liệu hình ảnh có trong vùng đệm CMSampleBufferRef.

      Cách lấy hướng ảnh:

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

      Sau đó, hãy tạo đối tượng siêu dữ liệu:

      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. Tạo đối tượng VisionImage bằng cách sử dụng đối tượng CMSampleBufferRef và siêu dữ liệu xoay:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. Truyền VisionImage đến một trong các phương thức xử lý hình ảnh của trình phát hiện đối tượng. Bạn có thể sử dụng phương thức process(image:) không đồng bộ hoặc phương thức results() đồng bộ.

    Cách phát hiện đối tượng không đồng bộ:

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

    Cách phát hiện đối tượng một cách đồng bộ:

    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. Nếu lệnh gọi đến trình xử lý hình ảnh thành công, lệnh gọi sẽ chuyển danh sách VisionObject đến trình xử lý hoàn thành hoặc trả về danh sách, tuỳ thuộc vào việc bạn đã gọi phương thức không đồng bộ hay đồng bộ.

    Mỗi VisionObject chứa các thuộc tính sau:

    frame CGRect cho biết vị trí của đối tượng trong hình ảnh.
    trackingID Số nguyên xác định đối tượng trên các hình ảnh. Nil ở chế độ một hình ảnh.
    classificationCategory Danh mục tương đối của đối tượng. Nếu trình phát hiện đối tượng chưa bật tính năng phân loại, thì giá trị này luôn là .unknown.
    confidence Giá trị tin cậy của việc phân loại đối tượng. Nếu trình phát hiện đối tượng chưa bật tính năng phân loại hoặc đối tượng được phân loại là không xác định, thì đó là 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;
    }
    

Cải thiện khả năng hữu dụng và hiệu suất

Để có trải nghiệm người dùng tốt nhất, hãy làm theo các nguyên tắc sau trong ứng dụng của bạn:

  • Việc phát hiện được đối tượng có thành công hay không còn phụ thuộc vào độ phức tạp trực quan của đối tượng. Các đối tượng có ít đặc điểm trực quan có thể cần chiếm phần lớn hơn của hình ảnh mới được phát hiện. Bạn nên cung cấp cho người dùng hướng dẫn về cách ghi lại dữ liệu đầu vào hoạt động hiệu quả với loại đối tượng bạn muốn phát hiện.
  • Khi sử dụng tính năng phân loại, nếu bạn muốn phát hiện các đối tượng không thuộc danh mục được hỗ trợ một cách rõ ràng, hãy triển khai cách xử lý đặc biệt cho các đối tượng không xác định.

Ngoài ra, hãy xem bộ sưu tập [ứng dụng giới thiệu Material Design trong Bộ công cụ học máy][showcase-link]{: .external } và bộ sưu tập Mẫu thiết kế cho các tính năng dựa trên công nghệ học máy.

Khi dùng chế độ phát trực tuyến trong một ứng dụng theo thời gian thực, hãy làm theo các nguyên tắc sau để đạt được tốc độ khung hình tốt nhất:

  • Đừng dùng tính năng phát hiện nhiều đối tượng ở chế độ phát trực tuyến, vì hầu hết các thiết bị sẽ không thể tạo ra đủ tốc độ khung hình.

  • Hãy tắt tính năng phân loại nếu bạn không cần.

  • Điều tiết lệnh gọi đến trình phát hiện. Nếu có một khung hình video mới trong khi trình phát hiện đang chạy, hãy bỏ khung hình đó.
  • Nếu bạn đang dùng đầu ra của trình phát hiện để phủ đồ hoạ lên hình ảnh đầu vào, trước tiên hãy lấy kết quả từ Bộ công cụ học máy, sau đó kết xuất hình ảnh và lớp phủ trong một bước duy nhất. Nhờ vậy, bạn chỉ kết xuất lên giao diện hiển thị một lần cho mỗi khung nhập. Hãy xem các lớp previewOverlayViewFIRDetectionOverlayView trong ứng dụng mẫu giới thiệu để làm ví dụ.