本章的内容主要是围绕AV Foundation中的相机捕获高级功能,主要内容如下:
- 人脸检测
- 机器码识别
人脸检测
AV Foundation内部提供了人脸识别的功能,很多人可能都不知道有这一功能。人脸的API隐藏在了AVCaptureMetadataOutput里。
先回顾下AV Foundation的媒体捕获结构图,通过图片,可以知道相机捕获数据是通过一些列的Output输出给开发者使用的,有负责拍照的PhotoOutput、有负责视频录制的MovieFileOutput等:
苹果提供了AVCaptureMetadataOutput,用于对捕获到的视频元数据进行输出,其中的人脸数据也封装在其中。
使用方式
AVCaptureMetadataOutput的使用和其他Output没什么太多区别,也是十分的容易,主要步骤如下:
- 创建一个AVCaptureMetadataOutput对象,并使用session的addOutput加到输出列表中
- AVCaptureMetadataOutput提供了一个metadataObjectTypes属性,该属性的作用是是声明需要捕获何种类型的元数据,因为除了人脸数据外,还有机器码等数据(后文会介绍),需要捕获人脸数据,将类型设置为.face
- 实现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。