ML Kit を使用して顔を検出する(iOS)

ML Kit を使用すると、画像や動画内の顔を検出できます。

試してみる

  • サンプルアプリを試してみると、この API の使用例を確認できます。
  • Codelab を使用してコードを自分で試します。

始める前に

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

入力画像に関するガイドライン

顔認識には、480x360 ピクセル以上の画像を使用する必要があります。ML Kit で顔を正確に検出するには、入力画像に十分なピクセルデータで表される顔が含まれている必要があります。一般に、画像内で検出する顔は少なくとも 100x100 ピクセルである必要があります。顔の輪郭を検出する場合、ML Kit にはより高い解像度の入力が必要です。つまり、各顔は少なくとも 200x200 ピクセルである必要があります。

リアルタイム アプリケーションで顔を検出する場合は、入力画像全体のサイズも考慮する必要があります。サイズが小さいほど処理が速くなるため、レイテンシを短縮するために画像をキャプチャします。低い解像度で画像をキャプチャします。ただし、上記の精度要件に留意し、画像の中で被写体の顔ができるだけ大きく写るようにしてください。リアルタイムのパフォーマンスを改善するためのヒントもご覧ください。

画像のフォーカスが弱い場合、精度にも影響することがあります。満足のいく結果が得られない場合は、ユーザーに画像を再度キャプチャするよう依頼してください。

カメラに対する顔の向きも、ML Kit が検出する顔の特徴に影響することがあります。顔検出のコンセプトをご覧ください。

1. 顔検出機能を構成する

顔検出を画像に適用する前に、顔検出機能のデフォルト設定を変更するには、FaceDetectorOptions オブジェクトを使用して設定を指定します。次の設定を変更できます。

設定
performanceMode fast(デフォルト)| accurate

顔を検出するときに速度または精度を優先します。

landmarkMode none(デフォルト)| all

検出されたすべての顔の「ランドマーク」(目、耳、鼻、頬、口)を検出するかどうか。

contourMode none(デフォルト)| all

顔の特徴の輪郭を検出するかどうかを指定します。輪郭は画像内で最も目立つ顔についてのみ検出されます。

classificationMode none(デフォルト)| all

顔を「笑顔」や「目を開いているか」などのカテゴリに分類するかどうか。

minFaceSize CGFloat(デフォルト: 0.1

顔の最小サイズを設定します。画像の幅に対する頭の幅の比率で表されます。

isTrackingEnabled false(デフォルト)| true

顔に ID(画像間で顔をトラッキングするために使用できるもの)を割り当てるかどうか。

輪郭検出を有効にすると、顔が 1 つだけ検出されるため、顔トラッキングで有用な結果が得られません。そのため、検出速度を向上させるため、輪郭検出と顔トラッキングの両方を有効にしないでください。

たとえば、次のいずれかの例のように FaceDetectorOptions オブジェクトを作成します。

Swift

// High-accuracy landmark detection and face classification
let options = FaceDetectorOptions()
options.performanceMode = .accurate
options.landmarkMode = .all
options.classificationMode = .all

// Real-time contour detection of multiple faces
// options.contourMode = .all

Objective-C

// High-accuracy landmark detection and face classification
MLKFaceDetectorOptions *options = [[MLKFaceDetectorOptions alloc] init];
options.performanceMode = MLKFaceDetectorPerformanceModeAccurate;
options.landmarkMode = MLKFaceDetectorLandmarkModeAll;
options.classificationMode = MLKFaceDetectorClassificationModeAll;

// Real-time contour detection of multiple faces
// options.contourMode = MLKFaceDetectorContourModeAll;

2. 入力画像を準備する

画像内の顔を検出するには、process(_:completion:) または results(in:) メソッドを使用して、画像を UIImage または CMSampleBufferRef として FaceDetector に渡します。

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. FaceDetector のインスタンスを取得する

FaceDetector のインスタンスを取得します。

Swift

let faceDetector = FaceDetector.faceDetector(options: options)

Objective-C

MLKFaceDetector *faceDetector = [MLKFaceDetector faceDetectorWithOptions:options];
      

4. 画像を処理する

次に、画像を process() メソッドに渡します。

Swift

weak var weakSelf = self
faceDetector.process(visionImage) { faces, error in
  guard let strongSelf = weakSelf else {
    print("Self is nil!")
    return
  }
  guard error == nil, let faces = faces, !faces.isEmpty else {
    // ...
    return
  }

  // Faces detected
  // ...
}

Objective-C

[faceDetector processImage:image
                completion:^(NSArray<MLKFace *> *faces,
                             NSError *error) {
  if (error != nil) {
    return;
  }
  if (faces.count > 0) {
    // Recognized faces
  }
}];

5. 検出された顔に関する情報を取得する

顔検出オペレーションが成功すると、顔検出機能は Face オブジェクトの配列を完了ハンドラに渡します。各 Face オブジェクトは画像内で検出された顔を表します。顔ごとに、入力画像の境界座標と、検出するように顔検出器に構成した情報を取得できます。例:

Swift

for face in faces {
  let frame = face.frame
  if face.hasHeadEulerAngleX {
    let rotX = face.headEulerAngleX  // Head is rotated to the uptoward rotX degrees
  }
  if face.hasHeadEulerAngleY {
    let rotY = face.headEulerAngleY  // Head is rotated to the right rotY degrees
  }
  if face.hasHeadEulerAngleZ {
    let rotZ = face.headEulerAngleZ  // Head is tilted sideways rotZ degrees
  }

  // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
  // nose available):
  if let leftEye = face.landmark(ofType: .leftEye) {
    let leftEyePosition = leftEye.position
  }

  // If contour detection was enabled:
  if let leftEyeContour = face.contour(ofType: .leftEye) {
    let leftEyePoints = leftEyeContour.points
  }
  if let upperLipBottomContour = face.contour(ofType: .upperLipBottom) {
    let upperLipBottomPoints = upperLipBottomContour.points
  }

  // If classification was enabled:
  if face.hasSmilingProbability {
    let smileProb = face.smilingProbability
  }
  if face.hasRightEyeOpenProbability {
    let rightEyeOpenProb = face.rightEyeOpenProbability
  }

  // If face tracking was enabled:
  if face.hasTrackingID {
    let trackingId = face.trackingID
  }
}

Objective-C

for (MLKFace *face in faces) {
  // Boundaries of face in image
  CGRect frame = face.frame;
  if (face.hasHeadEulerAngleX) {
    CGFloat rotX = face.headEulerAngleX;  // Head is rotated to the upward rotX degrees
  }
  if (face.hasHeadEulerAngleY) {
    CGFloat rotY = face.headEulerAngleY;  // Head is rotated to the right rotY degrees
  }
  if (face.hasHeadEulerAngleZ) {
    CGFloat rotZ = face.headEulerAngleZ;  // Head is tilted sideways rotZ degrees
  }

  // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
  // nose available):
  MLKFaceLandmark *leftEar = [face landmarkOfType:FIRFaceLandmarkTypeLeftEar];
  if (leftEar != nil) {
    MLKVisionPoint *leftEarPosition = leftEar.position;
  }

  // If contour detection was enabled:
  MLKFaceContour *upperLipBottomContour = [face contourOfType:FIRFaceContourTypeUpperLipBottom];
  if (upperLipBottomContour != nil) {
    NSArray<MLKVisionPoint *> *upperLipBottomPoints = upperLipBottomContour.points;
    if (upperLipBottomPoints.count > 0) {
      NSLog("Detected the bottom contour of the subject's upper lip.")
    }
  }

  // If classification was enabled:
  if (face.hasSmilingProbability) {
    CGFloat smileProb = face.smilingProbability;
  }
  if (face.hasRightEyeOpenProbability) {
    CGFloat rightEyeOpenProb = face.rightEyeOpenProbability;
  }

  // If face tracking was enabled:
  if (face.hasTrackingID) {
    NSInteger trackingID = face.trackingID;
  }
}

顔の輪郭の例

顔の輪郭検出を有効にすると、検出された顔の特徴のポイントのリストが表示されます。これらの点は対象物の形状を表します。輪郭の表現方法について詳しくは、顔検出のコンセプトをご覧ください。

次の画像は、これらの点が顔にどのようにマッピングされるかを示しています。画像をクリックすると拡大されます。

検出された顔の輪郭メッシュの例

リアルタイムの顔検出

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

  • 顔の輪郭検出または分類とランドマーク検出のいずれか一方のみを使用するように、顔検出器を構成します。

    輪郭検出
    ランドマーク検出
    分類
    ランドマーク検出と分類
    輪郭検出とランドマーク検出
    輪郭検出と分類
    輪郭検出、ランドマーク検出、分類

  • fast モードを有効にします(デフォルトで有効)。

  • 低解像度で画像をキャプチャすることを検討してください。ただし、この API の画像サイズの要件にも注意してください。

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