相机捕获高级功能

1,172 阅读3分钟

本章的内容主要是围绕AV Foundation中的相机捕获高级功能,主要内容如下:

  1. 人脸检测
  2. 机器码识别

人脸检测

AV Foundation内部提供了人脸识别的功能,很多人可能都不知道有这一功能。人脸的API隐藏在了AVCaptureMetadataOutput里。

先回顾下AV Foundation的媒体捕获结构图,通过图片,可以知道相机捕获数据是通过一些列的Output输出给开发者使用的,有负责拍照的PhotoOutput、有负责视频录制的MovieFileOutput等:

苹果提供了AVCaptureMetadataOutput,用于对捕获到的视频元数据进行输出,其中的人脸数据也封装在其中。

使用方式

AVCaptureMetadataOutput的使用和其他Output没什么太多区别,也是十分的容易,主要步骤如下:

  1. 创建一个AVCaptureMetadataOutput对象,并使用session的addOutput加到输出列表中
  2. AVCaptureMetadataOutput提供了一个metadataObjectTypes属性,该属性的作用是是声明需要捕获何种类型的元数据,因为除了人脸数据外,还有机器码等数据(后文会介绍),需要捕获人脸数据,将类型设置为.face
  3. 实现AVCaptureMetadataOutputObjectsDelegate协议的 metadataOutput(_:didOutput:from:)方法,并且设置AVCaptureMetadataOutput的delegate

通过上面三步,若相机捕获到相关元数据,会通过metadataOutput(_:didOutput:from:)方法回调数据,配置的代码如下:

// 创建metaOutput
private let metaOutput = AVCaptureMetadataOutput()

// 加入到sesson的Output列表中
if session.canAddOutput(metaOutput) {
    session.addOutput(metaOutput)
    // 声明数据类型
    metaOutput.metadataObjectTypes = [.face]
    metaOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
}

// 捕获到的元数据回调
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
    // 处理人脸数据
}

人脸数据获取

可以发现,metadataOutput(_:didOutput:from:)返回的metadataObjects是一个AVMetadataObject数组,AVMetadataObject是一个基类,对于获取人脸数据,需要将它转成它的子类AVMetadataFaceObject,可以看下AVMetadataFaceObject的结构:

open class AVMetadataFaceObject : AVMetadataObject, NSCopying {
    open var faceID: Int { get }
    open var hasRollAngle: Bool { get }
    open var rollAngle: CGFloat { get }
    open var hasYawAngle: Bool { get }
    open var yawAngle: CGFloat { get }
    open var bounds: CGRect { get }
}

通过AVMetadataFaceObject的结构,我们可以拿到以下数据:

  • faceID:人脸追踪的trackId
  • rollAngle、yawAngle:人脸的欧拉角数据,rollAngle是沿着z轴旋转的人脸角度,yawAngle是沿着y轴旋转的人脸角度
  • bounds:人脸的区域,其实是基类才提供的属性

由于相机有自己的坐标系,和视图坐标系不一致,所以还需要将AVMetadataFaceObject的bounds和angle转换,如果相机的预览是使用AVVideoPreviewLayer,它提供了转换的API给开发者使用,使用方式如下:

// 将设备坐标的数据转换成视图坐标系
private func transformedObjectFromPreview(faces: [AVMetadataObject]) -> [AVMetadataObject] {
    return faces.map { (object) -> AVMetadataObject in
        return previewView.videoPreviewLayer.transformedMetadataObject(for: object)!
    }
}

if let faces = tranfromedObjects as? [AVMetadataFaceObject] {
    for face in faces {
        // 获取faceId
        let faceId = face.faceID
        debugPrint("Face detected with ID: \(face.faceID)")
                debugPrint("Face bounds:\(face.bounds)")
    }
}

通过上面的数据,我们便可以很轻易的在相机上实现人脸识别和追踪,效果如图所示:

机器码识别

机器码的识别和人脸识别的使用方式差不多,在配置过程主要是需要将metadataObjectType设置成qr等机器码类型,代码示例如下:

metaOutput.metadataObjectTypes = [.qr]

除了qr(二维码)、还有aztec(条形码)等,如果需要可以一并添加到metadataObjectTypes中去,对于机器码,AV Foundation提供了另一个子类AVMetadataMachineReadableCodeObject来处理:

open class AVMetadataMachineReadableCodeObject : AVMetadataObject {
    open var stringValue: String? { get }
    open var bounds: CGRect { get }
}
  • stringValue:表示机器码的字符含义
  • bounds:表示机器码的范围

同样机器码的bound也需要进行从设备坐标系转换到视图坐标系上,之后获取其stringValue和bounds,代码示例如下:

if let codes = tranfromedObjects as? [AVMetadataMachineReadableCodeObject]{
    var lostKeys = Array(codeLayers.keys)
    for object in codes {
        guard let value = object.stringValue else { return }
    }
}

通过上面的使用,也可以识别到相机中的二维码,效果如图所示:

总结

今天的相机高级功能主要分享了两个方面,一个是人脸识别,一个是机器码识别,希望对读者能提供一定的帮助,文中若有什么描述错误,欢迎指正,文中涉及的示例代码和效果,已上传至Github