ML Kit を使用してオブジェクトを検出して追跡する(iOS)

ML Kit を使用すると、連続する動画フレームのオブジェクトを検出して追跡できます。

ML Kit に画像を渡すと、画像内の最大 5 つのオブジェクトと、画像内の各オブジェクトの位置が検出されます。動画ストリーム内のオブジェクトを検出する場合、各オブジェクトに一意の ID が割り当てられます。この ID を使用して、オブジェクトをフレーム間でトラッキングできます。必要に応じて、大まかなオブジェクト分類を有効にして、広範なカテゴリの説明でオブジェクトにラベル付けをします。

試してみる

始める前に

  1. Podfile に次の ML Kit Pod を含めます。
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. プロジェクトの Pod をインストールまたは更新したら、.xcworkspace を使用して Xcode プロジェクトを開きます。ML Kit は Xcode バージョン 12.4 以降でサポートされています。

1. オブジェクト検出を構成する

オブジェクトを検出して追跡するには、まず ObjectDetector のインスタンスを作成し、必要に応じてデフォルトから変更する検出設定を指定します。

  1. ObjectDetectorOptions オブジェクトを使用して、ユースケースのオブジェクト検出を構成します。次の設定を変更できます。

    オブジェクト検出の設定
    検出モード .stream(デフォルト)| .singleImage

    ストリーム モード(デフォルト)では、オブジェクト検出は非常に低いレイテンシで実行されますが、最初の数回の検出呼び出しで不完全な結果(未指定の境界ボックスやカテゴリなど)が生成されることがあります。また、ストリーム モードでは、検出機能はオブジェクトにトラッキング ID を割り当てます。これにより、フレームをまたいでオブジェクトをトラッキングできます。このモードは、オブジェクトを追跡する場合、または動画ストリームをリアルタイムで処理する場合など、低レイテンシが重要な場合に使用します。

    シングル画像モードでは、オブジェクトの境界ボックスが決定された後に、オブジェクト検出の結果が返されます。分類も有効にすると、境界ボックスとカテゴリラベルの両方が使用可能になった後に結果が返されます。その結果、検出のレイテンシが長くなる可能性があります。また、シングル画像モードでは、トラッキング ID は割り当てられません。レイテンシが重要ではなく、部分的な結果を処理しない場合は、このモードを使用します。

    複数のオブジェクトを検出して追跡する false(デフォルト)| true

    最大 5 つのオブジェクトを検出して追跡するか、最も視認性の高いオブジェクトのみを追跡するか(デフォルト)。

    オブジェクトを分類する false(デフォルト)| true

    検出されたオブジェクトを大まかなカテゴリに分類するかどうか。有効にすると、オブジェクト検出機能はファッション アイテム、食品、家庭用品、場所、植物のカテゴリにオブジェクトを分類します。

    オブジェクト検出とトラッキングの API は、次の 2 つの主要なユースケース向けに最適化されています。

    • カメラのビューファインダー内で最も目立つオブジェクトのライブ検出とトラッキング。
    • 静止画像内の複数のオブジェクトの検出。

    このようなユースケース向けに API を構成するには:

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. 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. 入力画像を準備する

オブジェクトを検出して追跡するには、画像または動画フレームごとに次の操作を行います。ストリーム モードを有効にした場合は、CMSampleBuffer から VisionImage オブジェクトを作成する必要があります。

UIImage または CMSampleBuffer を使用して VisionImage オブジェクトを作成します。

UIImage を使用する場合は、次の操作を行います。

  • UIImage を使用して VisionImage オブジェクトを作成します。正しい .orientation を指定してください。

    Swift

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

    Objective-C

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

CMSampleBuffer を使用する場合は、次の操作を行います。

  • CMSampleBuffer に含まれる画像データの向きを指定します。

    画像の向きを取得するには:

    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;
      }
    }
          
  • CMSampleBuffer オブジェクトと画面の向きを使用して、VisionImage オブジェクトを作成します。

    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. 画像を処理する

VisionImage をオブジェクト検出のいずれかの画像処理メソッドに渡します。非同期の process(image:) メソッドまたは同期 results() メソッドを使用できます。

オブジェクトを非同期で検出するには:

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

オブジェクトを同期的に検出するには:

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. 検出されたオブジェクトに関する情報を取得する

画像プロセッサの呼び出しが成功すると、非同期メソッドと同期メソッドのどちらを呼び出したかに応じて、Object のリストが完了ハンドラに渡されるか、リストが返されます。

Object には次のプロパティが含まれています。

frame 画像内のオブジェクトの位置を示す CGRect
trackingID 複数の画像でオブジェクトを識別する整数。シングル イメージ モードでは「nil」を指定します。
labels 検出機能によって返されたオブジェクトを記述するラベルの配列。検出オプション shouldEnableClassificationfalse に設定されている場合、プロパティは空です。

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

ユーザビリティとパフォーマンスの向上

最適なユーザー エクスペリエンスを実現するには、アプリで次のガイドラインを遵守してください。

  • オブジェクト検出が成功するかどうかは、オブジェクトの視覚的な複雑さによって異なります。これが検出されるには、視覚的特徴が少ないオブジェクトが画像の大部分を占めている必要があります。検出するオブジェクトの種類に適した入力をキャプチャするためのガイダンスをユーザーに提供する必要があります。
  • 分類を使用するときに、サポートされているカテゴリに分類されないオブジェクトを検出するには、未知のオブジェクトに対する特別な処理を実装します。

また、マテリアル デザインの機械学習を利用した機能のパターン コレクションもご覧ください。

リアルタイムのアプリケーションでストリーミング モードを使用する場合は、最適なフレームレートを得るために次のガイドラインに従ってください。

  • ストリーミング モードで複数オブジェクト検出を使用しないでください。ほとんどのデバイスは適切なフレームレートを生成できません。
  • 分類が不要な場合は無効にしてください。
  • 動画フレームの処理には、検出機能の results(in:) 同期 API を使用します。このメソッドを AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) 関数から呼び出して、指定された動画フレームから同期的に結果を取得します。 AVCaptureVideoDataOutput alwaysDiscardsLateVideoFramestrue のままにして、検出機能の呼び出しをスロットリングします。検出機能の実行中に新しい動画フレームが使用可能になると、そのフレームは破棄されます。
  • 検出機能の出力を使用して入力画像にグラフィックをオーバーレイする場合は、まず ML Kit から結果を取得してから、画像とオーバーレイを 1 つのステップでレンダリングします。これにより、ディスプレイ サーフェスへのレンダリングは、処理された入力フレームごとに 1 回だけ行います。例については、ML Kit クイックスタート サンプルの updatePreviewOverlayViewWithLastFrame をご覧ください。