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 trong các khung hình video liên tiếp.

Khi bạn chuyển một hình ảnh vào Bộ công cụ học máy, bộ công cụ này sẽ phát hiện tối đa 5 đối tượng trong hình ảnh cùng với vị trí của từng đối tượng trong hình ảnh. Khi phát hiện đối tượng trong luồng video, mỗi đối tượng có một mã nhận dạng duy nhất mà bạn có thể dùng để theo dõi đối tượng qua từng khung hình. Bạn cũng có thể tuỳ ý bật tính năng phân loại đối tượng thô. 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.

Dùng thử

Trước khi bắt đầu

  1. Đưa các nhóm Bộ công cụ học máy sau đây vào Podfile của bạn:
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. Sau khi cài đặt hoặc cập nhật Nhóm của dự án, hãy mở dự án Xcode của bạn bằng .xcworkspace của dự án. Bộ công cụ học máy được hỗ trợ trong Xcode phiên bản 12.4 trở lên.

1. Định cấu hình trình phát hiện vật thể

Để phát hiện và theo dõi các đối tượng, trước tiên, hãy tạo một thực thể của ObjectDetector và tuỳ ý chỉ định bất kỳ chế độ cài đặt nào của trình phát hiện mà bạn muốn thay đổi so với chế độ 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 ObjectDetectorOptions. 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ể tạo ra kết quả không đầy đủ (chẳng hạn như danh mục hoặc hộp giới hạn không được chỉ định) trong một vài 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. Bạn có thể dùng mã này để theo dõi các đối tượng trên các khung. Hãy sử dụng chế độ này khi bạn muốn theo dõi các đối tượng hoặc khi cần có độ trễ thấp, chẳng hạn như khi xử lý luồng video theo thời gian thực.

    Ở chế độ hình ảnh đơn lẻ, trình phát hiện đối tượng sẽ trả về kết quả sau khi hộp giới hạn của đối tượng được xác định. Nếu bạn bật tính năng phân loại, thì kết quả sẽ được trả về sau khi cả nhãn hộp giới hạn và nhãn danh mục đều có sẵn. Do đó, độ trễ phát hiện có thể cao hơn. Ngoài ra, ở chế độ hình ảnh đơn, 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ý một phần kết quả.

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

    Phát hiện và theo dõi tối đa 5 đối tượng hay chỉ một đố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 đã phát hiện thành các danh mục thô hay không. Khi được bật, trình phát hiện đối tượng 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 và cây cối.

    API theo dõi và phát hiện đối tượng được tối ưu hoá cho 2 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 một ảnh tĩnh.

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

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. Tạo một thực thể của 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. Chuẩn bị hình ảnh đầu vào

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

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

Nếu bạn sử dụng UIImage, hãy làm theo các bước sau:

  • Tạo đối tượng VisionImage bằng UIImage. Hãy nhớ chỉ định đúng .orientation.

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Nếu bạn sử dụng CMSampleBuffer, hãy làm theo các bước sau:

  • Chỉ định hướng của dữ liệu hình ảnh có trong CMSampleBuffer.

    Cách tải hướng của hình ảnh:

    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;
      }
    }
          
  • Tạo đối tượng VisionImage bằng cách sử dụng đối tượng và hướng 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. Xử lý hình ảnh

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ộ.

Để phát hiện đối tượng một cách không đồng bộ:

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

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

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. Xem thông tin về các đối tượng phát hiện được

Nếu lệnh gọi đến trình xử lý hình ảnh thành công, thì trình xử lý hình ảnh sẽ chuyển danh sách Object đế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 Object 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 Một số nguyên xác định đối tượng trên các hình ảnh, hoặc giá trị "nil" ở chế độ hình ảnh đơn.
labels Một mảng nhãn mô tả đối tượng mà trình phát hiện trả về. Thuộc tính này sẽ trống nếu bạn đặt tuỳ chọn trình phát hiện shouldEnableClassification thành 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];
    ...
  }
}

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 đối tượng có thành công hay không phụ thuộc vào độ phức tạp về hình ảnh của đối tượng. Để được phát hiện, các đối tượng có ít tính năng hình ảnh có thể cần chiếm phần lớn hơn hình ảnh. 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 phù hợp 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 các danh mục được hỗ trợ, 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 tham khảo bộ sưu tập Mẫu Material Design cho các tính năng sử dụng công nghệ học máy.

Khi bạn sử dụng chế độ truyền trực tuyến trong ứ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 sử dụng tính năng phát hiện nhiều đối tượng ở chế độ truyền trực tuyến, vì hầu hết thiết bị sẽ không tạo được tốc độ khung hình thích hợp.
  • Hãy tắt chế độ phân loại nếu bạn không cần.
  • Để xử lý khung video, hãy sử dụng API đồng bộ results(in:) của trình phát hiện. Hãy gọi phương thức này từ hàm captureOutput(_, didOutput:from:) của AVCaptureVideoDataOutputSampleBufferDelegate để nhận kết quả đồng bộ từ khung hình video đã cho. Giữ alwaysDiscardsLateVideoFrames của AVCaptureVideoDataOutput ở dạng true để đ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, thì khung hình đó sẽ bị loại bỏ.
  • Nếu bạn sử 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ủ chỉ qua một bước. Bằng cách này, bạn chỉ kết xuất trên giao diện màn hình một lần cho mỗi khung đầu vào được xử lý. Hãy xem ví dụ về updatePreviewOverlayViewWithLastFrame trong mẫu bắt đầu nhanh của Bộ công cụ học máy.