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

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

ML Kit にイメージを渡すと、ML Kit は各イメージについて、最大 5 つの検出されたオブジェクトとそのイメージ内での位置のリストを返します。動画ストリーム内のオブジェクトを検出する場合は、すべてのオブジェクトに ID を割り当てます。この ID を使用して、イメージ全体でオブジェクトをトラックできます。また、大まかなオブジェクト分類を有効にして、オブジェクトに幅広いカテゴリの説明をラベル付けすることもできます。

始める前に

  1. まだアプリに Firebase を追加していない場合は、スタートガイドの手順に沿って追加してください。
  2. Podfile に ML Kit ライブラリを含めます。
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    プロジェクトのポッドをインストールまたは更新した後に、.xcworkspace を使用して Xcode プロジェクトを開くようにしてください。
  3. アプリに Firebase をインポートします。

    Swift

    import Firebase

    Objective-C

    @import Firebase;

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

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

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

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

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

    シングル イメージモードでは、検出されたオブジェクトの境界ボックスと(分類を有効にしている場合)カテゴリが利用可能になるまでオブジェクト検出を待機してから結果を返します。結果として、検出のレイテンシが潜在的に長くなります。また、シングル イメージ モードでは、トラッキング ID が割り当てられません。レイテンシが重要ではなく、部分的な結果を処理しない場合は、このモードを使用します。

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

    最大 5 つのオブジェクトを検出してトラックするか、最も目立つオブジェクトのみをトラックするか(デフォルト)。

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

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

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

    • カメラのファインダー内で最も目立つオブジェクトをライブで検出してトラッキングする
    • 静止画像の複数のオブジェクトを検出する

    これらのユースケースに API を構成するには以下を実行します。

    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. 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. オブジェクト検出を実行する

オブジェクトを検出してトラックするには、各画像または動画フレームに対して次の手順を行います。ストリーム モードを有効にした場合は、CMSampleBufferRef から VisionImage オブジェクトを作成する必要があります。

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

    UIImage を使用するには:

    1. 必要に応じて、imageOrientation プロパティが .up になるように画像を回転させます。
    2. 適切に回転させた UIImage を使用して VisionImage オブジェクトを作成します。回転メタデータにはデフォルト値の .topLeft を使用する必要があるため、値を指定しないでください。

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    CMSampleBufferRef を使用するには:

    1. VisionImageMetadata オブジェクトを作成し、CMSampleBufferRef バッファに格納されている画像データの向きを指定します。

      画像の向きは次のように取得します。

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

      次に、メタデータ オブジェクトを作成します。

      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. VisionImage オブジェクトと回転メタデータを使用して CMSampleBufferRef オブジェクトを作成します。

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. VisionImage をオブジェクト検出のいずれかのイメージ処理メソッドに渡します。非同期 process(image:) メソッドまたは同期 results() メソッドを使用できます。

    オブジェクトを非同期的に検出するには、次の手順を行います。

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

    オブジェクトを同期的に検出するには、次の手順を行います。

    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. イメージ プロセッサへの呼び出しが成功した場合は、非同期メソッドと同期メソッドのどちらを呼び出したかに応じて、VisionObject のリストを完了ハンドラに渡すか、リストを返します。

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

    frame 画像内のオブジェクトの位置を示す CGRect
    trackingID 画像間でオブジェクトを識別する整数。シングル イメージ モードの nil。
    classificationCategory オブジェクトのおおまかなカテゴリ。オブジェクト検出で分類が有効になっていない場合、これは常に .unknown です。
    confidence オブジェクト分類の信頼値。オブジェクト検出で分類が有効になっていない場合、またはオブジェクトが不明と分類されている場合、これは 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;
    }
    

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

最高のユーザー エクスペリエンスを提供するため、次のガイドラインに従ってアプリを作成してください。

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

また、[ML Kit Material Design ショーケース アプリ][showcase-link]{: .external } とマテリアル デザインの Patterns for machine learning-powered features のコレクションも確認してください。

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

  • ストリーミング モードで複数のオブジェクト検出を使用しないでください。ほとんどのデバイスは十分なフレームレートを生成できません。

  • 不要な場合は、分類を無効にします。

  • 検出器の呼び出しのスロットル調整を行います。検出器の実行中に新しい動画フレームが使用可能になった場合は、そのフレームをドロップします。
  • 検出器の出力を使用して入力画像の上にグラフィックスをオーバーレイする場合は、まず ML Kit から検出結果を取得し、画像とオーバーレイを 1 つのステップでレンダリングします。これにより、ディスプレイ サーフェスへのレンダリングは入力フレームごとに 1 回で済みます。例については、ショーケース サンプルアプリの previewOverlayView クラスと FIRDetectionOverlayView クラスをご覧ください。