在 iOS 上使用 ML Kit 偵測人臉

您可以使用ML Kit來偵測影像和影片中的人臉。

在你開始之前

  1. 如果您尚未將 Firebase 新增至您的應用程式中,請按照入門指南中的步驟進行操作。
  2. 在 Podfile 中包含 ML Kit 函式庫:
    pod 'Firebase/MLVision', '6.25.0'
    # If you want to detect face contours (landmark detection and classification
    # don't require this additional model):
    pod 'Firebase/MLVisionFaceModel', '6.25.0'
    
    安裝或更新專案的 Pod 後,請務必使用其.xcworkspace開啟 Xcode 專案。
  3. 在您的應用程式中,導入 Firebase:

    迅速

    import Firebase

    Objective-C

    @import Firebase;

輸入影像指南

為了讓 ML Kit 準確地偵測人臉,輸入影像必須包含由足夠的像素資料表示的人臉。一般來說,您想要在影像中偵測的每張臉應至少為 100x100 像素。如果要偵測人臉輪廓,ML Kit 需要更高解析度的輸入:每張人臉至少應為 200x200 像素。

如果您在即時應用程式中偵測人臉,您可能還需要考慮輸入影像的整體尺寸。較小的影像可以更快地處理,因此為了減少延遲,請以較低的解析度擷取影像(記住上述精度要求)並確保主體的臉部盡可能佔據影像。另請參閱提高即時效能的提示

影像聚焦不良會影響準確性。如果您沒有獲得可接受的結果,請嘗試要求使用者重新捕捉影像。

臉部相對於相機的方向也會影響 ML Kit 偵測到的臉部特徵。請參閱人臉偵測概念

1.配置人臉偵測器

在將臉部偵測套用至影像之前,如果要變更任何臉部偵測器的預設設置,請使用VisionFaceDetectorOptions物件指定這些設定。您可以更改以下設定:

設定
performanceMode fast (預設)| accurate

偵測人臉時優先考慮速度或準確性。

landmarkMode none (預設)| all

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

contourMode none (預設)| all

是否偵測臉部特徵輪廓。僅檢測影像中最突出的臉部的輪廓。

classificationMode none (預設)| all

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

minFaceSize CGFloat (預設值: 0.1

要偵測的臉部相對於影像的最小尺寸。

isTrackingEnabled false (預設)| true

是否為人臉分配 ID,可用於跨影像追蹤人臉。

請注意,啟用輪廓偵測後,僅偵測到一張臉部,因此臉部追蹤不會產生有用的結果。因此,為了提高偵測速度,請勿同時啟用輪廓偵測和臉部追蹤。

例如,建立一個VisionFaceDetectorOptions對象,如以下範例之一:

迅速

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

// Real-time contour detection of multiple faces
let options = VisionFaceDetectorOptions()
options.contourMode = .all

Objective-C

// High-accuracy landmark detection and face classification
FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init];
options.performanceMode = FIRVisionFaceDetectorPerformanceModeAccurate;
options.landmarkMode = FIRVisionFaceDetectorLandmarkModeAll;
options.classificationMode = FIRVisionFaceDetectorClassificationModeAll;

// Real-time contour detection of multiple faces
FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init];
options.contourMode = FIRVisionFaceDetectorContourModeAll;

2. 運行人臉偵測器

若要偵測影像中的人臉,請將影像作為UIImageCMSampleBufferRef傳遞給VisionFaceDetectordetect(in:)方法:

  1. 取得VisionFaceDetector的實例:

    迅速

    lazy var vision = Vision.vision()
    
    let faceDetector = vision.faceDetector(options: options)
    

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionFaceDetector *faceDetector = [vision faceDetector];
    // Or, to change the default settings:
    // FIRVisionFaceDetector *faceDetector =
    //     [vision faceDetectorWithOptions:options];
    
  2. 使用UIImageCMSampleBufferRef建立VisionImage物件。

    使用UIImage

    1. 如有必要,旋轉影像,使其imageOrientation屬性為.up
    2. 使用正確旋轉的UIImage建立VisionImage物件。不要指定任何旋轉元資料 - 必須使用預設值.topLeft

      迅速

      let image = VisionImage(image: uiImage)

      Objective-C

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

    使用CMSampleBufferRef

    1. 建立一個VisionImageMetadata對象,該對象指定CMSampleBufferRef緩衝區中包含的圖像資料的方向。

      若要取得影像方向:

      迅速

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

      然後,建立元資料物件:

      迅速

      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. 使用CMSampleBufferRef物件和旋轉元資料建立VisionImage物件:

      迅速

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. 然後,將圖像傳遞給detect(in:)方法:

    迅速

    faceDetector.process(visionImage) { faces, error in
      guard error == nil, let faces = faces, !faces.isEmpty else {
        // ...
        return
      }
    
      // Faces detected
      // ...
    }
    

    Objective-C

    [faceDetector detectInImage:image
                     completion:^(NSArray<FIRVisionFace *> *faces,
                                  NSError *error) {
      if (error != nil) {
        return;
      } else if (faces != nil) {
        // Recognized faces
      }
    }];
    

3. 取得偵測到的人臉資訊

如果臉部偵測操作成功,臉部偵測器會將VisionFace物件陣列傳遞給完成處理程序。每個VisionFace物件代表在影像中偵測到的一張臉。對於每張臉,您都可以獲得其在輸入影像中的邊界座標,以及配置臉部偵測器要尋找的任何其他資訊。例如:

迅速

for face in faces {
  let frame = face.frame
  if face.hasHeadEulerAngleY {
    let rotY = face.headEulerAngleY  // Head is rotated to the right rotY degrees
  }
  if face.hasHeadEulerAngleZ {
    let rotZ = face.headEulerAngleZ  // Head is rotated upward 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 (FIRVisionFace *face in faces) {
  // Boundaries of face in image
  CGRect frame = face.frame;

  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):
  FIRVisionFaceLandmark *leftEar = [face landmarkOfType:FIRFaceLandmarkTypeLeftEar];
  if (leftEar != nil) {
    FIRVisionPoint *leftEarPosition = leftEar.position;
  }

  // If contour detection was enabled:
  FIRVisionFaceContour *upperLipBottomContour = [face contourOfType:FIRFaceContourTypeUpperLipBottom];
  if (upperLipBottomContour != nil) {
    NSArray<FIRVisionPoint *> *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 的圖像尺寸要求。

  • 對檢測器的節流呼叫。如果偵測器運作時有新的視訊幀可用,則丟棄該幀。
  • 如果您使用偵測器的輸出將圖形疊加在輸入影像上,請先從 ML Kit 取得結果,然後一步渲染影像並疊加。透過這樣做,每個輸入幀只需渲染到顯示表面一次。有關範例,請參閱展示範例應用程式中的PreviewOverlayViewFIRDetectionOverlayView類別。