适用于 iOS 的人脸检测指南

通过人脸检测器任务,您可以检测图片或视频中的人脸。您可以使用此任务在帧中定位人脸和面部特征。此任务使用可处理单张图片或连续图片流的机器学习 (ML) 模型。该任务会输出人脸位置以及以下面部关键点:左眼、右眼、鼻尖、嘴巴、左眼悲剧和右眼悲剧。

GitHub 上提供了这些说明中介绍的代码示例。 您可以观看此 Web 演示,了解此任务的实际效果。如需详细了解此任务的功能、模型和配置选项,请参阅概览

代码示例

MediaPipe Tasks 示例代码是适用于 iOS 的人脸检测器应用的简单实现。该示例使用实体 Android 设备上的摄像头来检测连续视频流中的人脸。应用还可以检测设备图库中的图片和视频中的人脸。

您可以使用该应用作为基础来开发自己的 iOS 应用,也可以在修改现有应用时参考该应用。人脸检测器示例代码托管在 GitHub 上。

下载代码

以下说明介绍了如何使用 git 命令行工具创建示例代码的本地副本。

如需下载示例代码,请执行以下操作:

  1. 使用以下命令克隆 Git 代码库:

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. (可选)将您的 Git 实例配置为使用稀疏检出,以便只有 Face Detector 示例应用的文件:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/face_detector/ios/
    

创建示例代码的本地版本后,您可以安装 MediaPipe 任务库,使用 Xcode 打开项目并运行应用。有关说明,请参阅 iOS 设置指南

关键组件

以下文件包含人脸检测器示例应用的关键代码:

初始设置

本部分介绍了为使用人脸检测器而设置开发环境和代码项目的关键步骤。如需了解有关为使用 MediaPipe 任务设置开发环境的一般信息(包括平台版本要求),请参阅 iOS 设置指南

依赖项

人脸检测器使用 MediaPipeTasksVision 库,必须使用 CocoaPods 进行安装。该库与 Swift 和 Objective-C 应用兼容,不需要任何其他针对特定语言的设置。

如需了解如何在 macOS 上安装 CocoaPods,请参阅 CocoaPods 安装指南。 如需了解如何创建包含应用必要 Pod 的 Podfile,请参阅使用 CocoaPods

使用以下代码在 Podfile 中添加 MediaPipeTasksVision pod:

target 'MyFaceDetectorApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

如果您的应用包含单元测试目标,请参阅 iOS 设置指南,详细了解如何设置 Podfile

模型

MediaPipe 人脸检测器任务需要使用与此任务兼容的经过训练的模型。如需详细了解人脸检测器的可用经过训练的模型,请参阅任务概览“模型”部分

选择并下载模型,然后使用 Xcode 将模型添加到您的项目目录中。如需了解如何将文件添加到 Xcode 项目,请参阅管理 Xcode 项目中的文件和文件夹

使用 BaseOptions.modelAssetPath 属性指定 app bundle 中模型的路径。如需查看代码示例,请参阅下一部分。

创建任务

您可以通过调用人脸检测器任务之一来创建该任务。FaceDetector(options:) 初始化程序接受配置选项的值。

如果您不需要使用自定义配置选项初始化的人脸检测器,则可以使用 FaceDetector(modelPath:) 初始化程序使用默认选项创建人脸检测器。如需详细了解配置选项,请参阅配置概览

人脸检测器任务支持 3 种输入数据类型:静态图片、视频文件和直播视频流。默认情况下,FaceDetector(modelPath:) 会初始化静态图片的任务。如果您希望将任务初始化为处理视频文件或直播视频流,请使用 FaceDetector(options:) 指定视频或直播的运行模式。直播模式还需要一个额外的 faceDetectorLiveStreamDelegate 配置选项,以便人脸检测器能够将人脸检测结果异步传送给代理。

选择与运行模式对应的标签页,了解如何创建任务并运行推断。

Swift

映像

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = FaceDetectorOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image

let faceDetector = try FaceDetector(options: options)
    

视频

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = FaceDetectorOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video

let faceDetector = try FaceDetector(options: options)
    

直播

import MediaPipeTasksVision

// Class that conforms to the `FaceDetectorLiveStreamDelegate` protocol and
// implements the method that the face detector calls once it finishes
// detecting faces in each input frame.
class FaceDetectorResultProcessor: NSObject, FaceDetectorLiveStreamDelegate {

  func faceDetector(
    _ faceDetector: FaceDetector,
    didFinishDetection result: FaceDetectorResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the face detection result or errors here.

  }
}

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = FaceDetectorOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream

// Assign an object of the class to the `faceDetectorLiveStreamDelegate`
// property.
let processor = FaceDetectorResultProcessor()
options.faceDetectorLiveStreamDelegate = processor

let faceDetector = try FaceDetector(options: options)
    

Objective-C

映像

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPFaceDetectorOptions *options = [[MPPFaceDetectorOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;

MPPFaceDetector *faceDetector =
      [[MPPFaceDetector alloc] initWithOptions:options error:nil];
    

视频

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPFaceDetectorOptions *options = [[MPPFaceDetectorOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;

MPPFaceDetector *faceDetector =
      [[MPPFaceDetector alloc] initWithOptions:options error:nil];
    

直播

@import MediaPipeTasksVision;

// Class that conforms to the `MPPFaceDetectorLiveStreamDelegate` protocol
// and implements the method that the face detector calls once it finishes
// detecting faces in each input frame.

@interface APPFaceDetectorResultProcessor : NSObject 

@end

@implementation APPFaceDetectorResultProcessor

-   (void)faceDetector:(MPPFaceDetector *)faceDetector
    didFinishDetectionWithResult:(MPPFaceDetectorResult *)faceDetectorResult
         timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                           error:(NSError *)error {

    // Process the face detector result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPFaceDetectorOptions *options = [[MPPFaceDetectorOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;

// Assign an object of the class to the `faceDetectorLiveStreamDelegate`
// property.
APPFaceDetectorResultProcessor *processor = [APPFaceDetectorResultProcessor new];
options.faceDetectorLiveStreamDelegate = processor;

MPPFaceDetector *faceDetector =
      [[MPPFaceDetector alloc] initWithOptions:options error:nil];
    

注意:如果您使用的是视频模式或直播模式,则人脸检测器会使用跟踪来避免在每一帧上触发检测模型,这有助于缩短延迟时间。

配置选项

此任务针对 iOS 应用提供以下配置选项:

选项名称 说明 值范围 默认值
runningMode 设置任务的运行模式。有三种模式:

IMAGE:单张图片输入的模式。

VIDEO:视频的已解码帧的模式。

LIVE_STREAM:输入数据(例如来自摄像头)的直播的模式。在此模式下,必须调用 resultListener,以设置用于异步接收结果的监听器。
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
minDetectionConfidence 人脸检测被视为成功的最低置信度分数。 Float [0,1] 0.5
minSuppressionThreshold 将人脸检测视为重叠的最小非最大抑制阈值。 Float [0,1] 0.3

直播配置

当运行模式设置为直播时,人脸检测器需要额外的 faceDetectorLiveStreamDelegate 配置选项,以便人脸检测器能够异步传送检测结果。该代理会实现 faceDetector(_:didFinishDetection:timestampInMilliseconds:error:) 方法,人脸检测器会在处理完每一帧的人脸检测结果后调用该方法。

选项名称 说明 值范围 默认值
faceDetectorLiveStreamDelegate 启用人脸检测器,以便在直播模式下异步接收人脸检测结果。将此属性设置为此属性的类必须实现 faceDetector(_:didFinishDetection:timestampInMilliseconds:error:) 方法。 不适用 未设置

准备数据

您需要先将输入图片或帧转换为 MPImage 对象,然后再将其传递给人脸检测器。MPImage 支持不同类型的 iOS 图片格式,可以在任何运行模式下使用它们进行推理。如需详细了解 MPImage,请参阅 MPImage API

根据您的用例和应用所需的运行模式选择 iOS 图片格式。MPImage 接受 UIImageCVPixelBufferCMSampleBuffer iOS 图片格式。

UIImage

UIImage 格式非常适合以下运行模式:

  • 图片:可将 app bundle、用户图库或文件系统中的图片转换为 UIImage 图片格式,并将其转换为 MPImage 对象。

  • 视频:使用 AVAssetImageGenerator 将视频帧提取为 CGImage 格式,然后将它们转换为 UIImage 图片。

Swift

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(uiImage: image)
    

Objective-C

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

该示例使用默认的 UIImage.Orientation.Up 方向初始化 MPImage。您可以使用任何受支持的 UIImage.Orientation 值初始化 MPImage。人脸检测器不支持 .upMirrored.downMirrored.leftMirrored.rightMirrored 等镜像方向。

如需详细了解 UIImage,请参阅 UIImage Apple 开发者文档

CVPixelBuffer

CVPixelBuffer 格式非常适合生成帧并使用 iOS CoreImage 框架进行处理的应用。

CVPixelBuffer 格式非常适合以下运行模式:

  • 图片:使用 iOS 的 CoreImage 框架进行一些处理后生成 CVPixelBuffer 图片的应用可发送到处于图片运行模式下的人脸检测器。

  • 视频:将视频帧转换为 CVPixelBuffer 格式进行处理,然后在视频模式下发送到人脸检测器。

  • Livestream:对于使用 iOS 相机生成帧的应用,您可以先转换为 CVPixelBuffer 格式以进行处理,然后再以直播模式发送到人脸检测器。

Swift

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(pixelBuffer: pixelBuffer)
    

Objective-C

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

如需详细了解 CVPixelBuffer,请参阅 CVPixelBuffer Apple 开发者文档

CMSampleBuffer

CMSampleBuffer 格式可存储统一媒体类型的媒体样本,非常适合直播运行模式。来自 iOS 相机的实时帧由 iOS AVCaptureVideoDataOutputCMSampleBuffer 格式异步传送。

Swift

// Obtain a CMSampleBuffer.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(sampleBuffer: sampleBuffer)
    

Objective-C

// Obtain a `CMSampleBuffer`.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
    

如需详细了解 CMSampleBuffer,请参阅 CMSampleBuffer Apple 开发者文档

运行任务

如需运行人脸检测器,请使用特定于指定的运行模式的 detect() 方法:

  • 静态图片:detect(image:)
  • 视频:detect(videoFrame:timestampInMilliseconds:)
  • 直播:detectAsync(image:timestampInMilliseconds:)

人脸检测器会返回在输入图片或帧中检测到的人脸。

以下代码示例展示了如何在不同的运行模式下运行人脸检测器:

Swift

映像

let result = try faceDetector.detect(image: image)
    

视频

let result = try faceDetector.detect(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

直播

try faceDetector.detectAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

映像

MPPFaceDetectorResult *result = [faceDetector detectInImage:image
                                                      error:nil];
    

视频

MPPFaceDetectorResult *result = [faceDetector detectInVideoFrame:image
                                         timestampInMilliseconds:timestamp
                                                           error:nil];
    

直播

BOOL success = [faceDetector detectAsyncInImage:image
                        timestampInMilliseconds:timestamp
                                          error:nil];
    

人脸检测器代码示例中更详细地展示了其中每种模式的实现 detect(image:)detect(videoFrame:timestampInMilliseconds:)detectAsync(image:timestampInMilliseconds:)。示例代码允许用户在处理模式之间切换,而您的用例可能不需要这些模式。

请注意以下几点:

  • 在视频模式或直播模式下运行时,您还必须向人脸检测器任务提供输入帧的时间戳。

  • 在图片或视频模式下运行时,人脸检测器任务会阻塞当前线程,直到处理完输入图片或帧。为避免阻塞当前线程,请使用 iOS DispatchNSOperation 框架在后台线程中执行处理。

  • 在直播模式下运行时,人脸检测器任务会立即返回,并且不会阻塞当前线程。它会在处理每个输入帧后调用 faceDetector(_:didFinishDetection:timestampInMilliseconds:error:) 方法并返回人脸检测结果。人脸检测器会在专用的串行调度队列中异步调用此方法。如需在界面上显示结果,请在处理结果后将结果分派到主队列。如果在人脸检测器任务正忙于处理另一帧时调用 detectAsync 函数,人脸检测器会忽略新的输入帧。

处理和显示结果

运行推断后,人脸检测器任务会返回一个 FaceDetectorResult 对象,其中包含已检测到的人脸的边界框和每个检测到的人脸的置信度分数。

下面显示了此任务的输出数据示例:

FaceDetectionResult:
  Detections:
    Detection #0:
      BoundingBox:
        origin_x: 126
        origin_y: 100
        width: 463
        height: 463
      Categories:
        Category #0:
          index: 0
          score: 0.9729152917861938
      NormalizedKeypoints:
        NormalizedKeypoint #0:
          x: 0.18298381567001343
          y: 0.2961040139198303
        NormalizedKeypoint #1:
          x: 0.3302789330482483
          y: 0.29289937019348145
        ... (6 keypoints for each face)
    Detection #1:
      BoundingBox:
        origin_x: 616
        origin_y: 193
        width: 430
        height: 430
      Categories:
        Category #0:
          index: 0
          score: 0.9251380562782288
      NormalizedKeypoints:
        NormalizedKeypoint #0:
          x: 0.6151331663131714
          y: 0.3713381886482239
        NormalizedKeypoint #1:
          x: 0.7460576295852661
          y: 0.38825345039367676
        ... (6 keypoints for each face)

下图显示了任务输出的可视化效果:

对于没有边界框的图片,请参阅原始图片

人脸检测器示例代码演示了如何显示结果。如需了解详情,请参阅代码示例