在 iOS 上使用 ML Kit 偵測臉部

你可以使用 ML Kit 偵測圖片和影片中的臉孔。

立即體驗

事前準備

  1. 在 Podfile 中加入下列 ML Kit pod:
    pod 'GoogleMLKit/FaceDetection', '3.2.0'
    
  2. 安裝或更新專案的 Pod 後,請使用其 .xcworkspace 開啟 Xcode 專案。Xcode 12.4 以上版本支援 ML Kit。

輸入圖片使用規範

如要進行臉部辨識,請使用尺寸至少為 480x360 像素的圖片。為了讓 ML Kit 準確偵測臉孔,輸入圖片必須包含以足夠像素資料表示的臉孔。一般來說,要在圖片中偵測的每個臉孔都至少應為 100x100 像素。如要偵測臉部的輪廓,使用 ML Kit 時需要更高的解析度:每個臉孔至少要有 200 x 200 像素。

如果您在即時應用程式中偵測到臉孔,可能也會考慮輸入圖片的整體尺寸。小型圖片的處理速度可以更快,因此為了縮短延遲時間,以較低解析度拍照,但請留意上方的準確性規定,並確保拍攝主體的臉部盡可能涵蓋最多圖片。另請參閱改善即時效能的訣竅

影像失焦也可能影響準確度。如未取得可接受的結果,請要求使用者重新拍攝圖片。

相對於攝影機的臉部方向,也可能會影響 ML Kit 偵測到的臉部特徵。請參閱「臉部偵測概念」一文。

1. 設定臉部偵測工具

將臉部偵測功能套用至圖片前,如要變更臉孔偵測工具的預設設定,請使用 FaceDetectorOptions 物件指定這些設定。您可以變更下列設定:

設定
performanceMode fast (預設) | accurate

偵測臉孔時,偏好速度或準確度。

landmarkMode none (預設) | all

是否嘗試偵測所有偵測到的臉孔 (眼睛、耳朵、鼻子、臉頰、嘴巴)。

contourMode none (預設) | all

是否偵測臉部特徵的輪廓。系統只會偵測圖片中最顯眼的臉孔。

classificationMode none (預設) | all

是否要將臉孔分類為「微笑」和「眼睛張開」等類別。

minFaceSize CGFloat (預設值:0.1)

設定所需的最小臉孔尺寸,以標題寬度與圖片寬度的比例表示。

isTrackingEnabled false (預設) | true

指定是否指派臉孔 ID,可用於追蹤多張圖片的臉孔。

請注意,如果啟用輪廓偵測功能,系統就只會偵測到一張臉孔,因此臉部追蹤功能不會產生實用的結果。因此,為了加快偵測速度,請勿同時啟用輪廓偵測和臉部追蹤。

例如,您可以像下列其中一個範例一樣建構 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:) 方法,將圖片做為 UIImageCMSampleBufferRef 傳遞至 FaceDetector

使用 UIImageCMSampleBuffer 建立 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 alwaysDiscardsLateVideoFrames 保留為 true,藉此調節對偵測工具的呼叫次數。如果在偵測工具執行期間提供新的影片畫面,該影格將遭到捨棄。
  • 如果您使用偵測工具的輸出內容將輸入圖片上的圖形重疊,請先從 ML Kit 取得結果,然後再在單一步驟算繪影像和重疊。這樣一來,您只會在每個已處理的輸入影格轉譯一次螢幕介面。如需範例,請參閱 ML Kit 快速入門導覽課程範例中的 updatePreviewOverlayViewWithLastFrame