转到控制台

在 iOS 上使用机器学习套件检测人脸

您可以使用机器学习套件检测图片和视频中的人脸。

如需了解此 API 的实际应用示例,请查看 GitHub 上的机器学习套件快速入门示例

准备工作

  1. 如果您尚未将 Firebase 添加到自己的应用中,请按照入门指南中的步骤执行此操作。
  2. 在 Podfile 中添加机器学习套件库:
    pod 'Firebase/Core'
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionFaceModel'
    
    安装或更新项目的 Pod 之后,请务必使用 Xcode 项目的 .xcworkspace 来打开项目。
  3. 在您的应用中导入 Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

输入图片指南

要使机器学习套件准确检测人脸,输入图片必须包含由足够像素数据表示的人脸。通常,要在图片中检测的每个人脸应至少为 100x100 像素。如果要检测人脸轮廓,机器学习套件需要更高的分辨率输入:每个人脸应至少为 200x200 像素。

如果您是在实时应用中检测人脸,则可能还需要考虑输入图片的整体尺寸。较小图片的处理速度相对较快,因此,为了减少延迟时间,请以较低的分辨率捕获图片(牢记上述准确性要求),并确保主体的面部尽可能占据图片的尺寸。另请参阅提高实时性能的相关提示

图片聚焦不良会影响准确性。如果您获得的结果不可接受,请尝试让用户重新捕获图片。

人脸相对于相机的方向也会影响机器学习套件检测的面部特征。请参阅人脸检测概念

1. 配置面部检测器

在对图片进行人脸检测之前,如果要更改人脸检测器的任何默认设置,请使用 VisionFaceDetectorOptions 对象指定这些设置。您可以更改以下设置:

设置
performanceMode fast(默认值)| accurate

在检测人脸时更注重速度还是准确性。

landmarkMode none(默认值)| all

是否尝试识别检测到的所有人脸的“特征点”:眼睛、耳朵、鼻子、脸颊、嘴巴。

contourMode none(默认值)| all

是否检测面部特征的轮廓。仅检测图片中最突出的人脸的轮廓。

classificationMode none(默认值)| all

是否将人脸分为不同类别(例如“微笑”和“睁眼”)。

minFaceSize CGFloat(默认值:0.1

要检测的人脸的大小下限(相对于图片)。

isTrackingEnabled false(默认值)| true

是否为人脸分配 ID,以用于跨图片跟踪人脸。

请注意,启用轮廓检测后,仅检测一个人脸,因此人脸跟踪不会产生有用的结果。为此,若要加快检测速度,请勿同时启用轮廓检测和人脸跟踪。

例如,如以下其中一个示例所示,构建 VisionFaceDetectorOptions 对象:

Swift

// 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 的一个实例:

    Swift

    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

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    要使用 CMSampleBufferRef,请按以下步骤操作:

    1. 创建一个 VisionImageMetadata 对象,用其指定 CMSampleBufferRef 缓冲区中所含图片数据的方向。

      例如,如果您使用的是从设备的后置摄像头捕获的图片数据:

      Swift

      let metadata = VisionImageMetadata()
      
      // Using back-facing camera
      let devicePosition: AVCaptureDevice.Position = .back
      
      let deviceOrientation = UIDevice.current.orientation
      switch deviceOrientation {
      case .portrait:
        metadata.orientation = devicePosition == .front ? .leftTop : .rightTop
      case .landscapeLeft:
        metadata.orientation = devicePosition == .front ? .bottomLeft : .topLeft
      case .portraitUpsideDown:
        metadata.orientation = devicePosition == .front ? .rightBottom : .leftBottom
      case .landscapeRight:
        metadata.orientation = devicePosition == .front ? .topRight : .bottomRight
      case .faceDown, .faceUp, .unknown:
        metadata.orientation = .leftTop
      }
      

      Objective-C

      // Calculate the image orientation
      FIRVisionDetectorImageOrientation orientation;
      
      // Using front-facing camera
      AVCaptureDevicePosition devicePosition = AVCaptureDevicePositionFront;
      
      UIDeviceOrientation deviceOrientation = UIDevice.currentDevice.orientation;
      switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
              if (devicePosition == AVCaptureDevicePositionFront) {
                  orientation = FIRVisionDetectorImageOrientationLeftTop;
              } else {
                  orientation = FIRVisionDetectorImageOrientationRightTop;
              }
              break;
          case UIDeviceOrientationLandscapeLeft:
              if (devicePosition == AVCaptureDevicePositionFront) {
                  orientation = FIRVisionDetectorImageOrientationBottomLeft;
              } else {
                  orientation = FIRVisionDetectorImageOrientationTopLeft;
              }
              break;
          case UIDeviceOrientationPortraitUpsideDown:
              if (devicePosition == AVCaptureDevicePositionFront) {
                  orientation = FIRVisionDetectorImageOrientationRightBottom;
              } else {
                  orientation = FIRVisionDetectorImageOrientationLeftBottom;
              }
              break;
          case UIDeviceOrientationLandscapeRight:
              if (devicePosition == AVCaptureDevicePositionFront) {
                  orientation = FIRVisionDetectorImageOrientationTopRight;
              } else {
                  orientation = FIRVisionDetectorImageOrientationBottomRight;
              }
              break;
          default:
              orientation = FIRVisionDetectorImageOrientationTopLeft;
              break;
      }
      
      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      metadata.orientation = orientation;
      
    2. 使用 CMSampleBufferRef 对象和旋转元数据创建一个 VisionImage 对象:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:buffer];
      image.metadata = metadata;
      
  3. 然后,将图片传递给 detect(in:) 方法:

    Swift

    faceDetector.process(visionImage) { features, error in
      guard error == nil, let features = features, !features.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 对象代表一张在图片中检测到的面孔。对于每张面孔,您可以获取它在输入图片中的边界坐标,以及您已配置面部检测器查找的任何其他信息。例如:

Swift

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 的图片尺寸要求。

  • 限制检测器的调用次数。如果在检测器运行时有新视频帧可用,请丢弃该帧。