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 を使用します。AVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:) 関数からこのメソッドを呼び出して、指定された動画フレームから同期的に結果を取得します。AVCaptureVideoDataOutputalwaysDiscardsLateVideoFramestrue のままにして、検出機能の呼び出しを絞り込みます。検出器の実行中に新しい動画フレームが使用可能になると、そのフレームは破棄されます。
  • 検出器の出力を使用して入力画像の上にグラフィックスをオーバーレイする場合は、まず ML Kit から検出結果を取得し、画像とオーバーレイを 1 つのステップでレンダリングします。これにより、処理済みの入力フレームごとにディスプレイ サーフェスへのレンダリングが 1 回だけ行われます。例については、ML Kit クイックスタート サンプルの updatePreviewOverlayViewWithLastFrame をご覧ください。