一、概述
这里的人脸检测是通过AVFoundation实现的实时人脸检测功能,会在检测到人脸自动建立相应的焦点。
AVFoundation中通过特定的AVCaptureOutput类型的AVCaptureMetadataOutput实现这个功能。它的输出同之前类似,输出的不是静态图片或影片,而是元数据。定义了用来处理多种元数据类型的接口,当使用人脸检测时,会输出一个具体子类类型AVMetadataFaceObject
。
AVMetadataFaceObject
几个重要属性:
-
rollAngle
:倾斜角,表示人的头部向肩膀方向的侧倾角度。 -
yawAngle
:偏转角,表示人脸绕y轴旋转的角度。 -
bounds
:边界,对应的是设备坐标。
人脸识别的整个流程与之前用到的静态图片和视频捕捉是一样的,不同的是一些配置的不同,以及对获取到的脸部数据对象的处理。
二、实现流程
基本功能
- 1、创建会话,并配置输入输出
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
AVCaptureDevice *videoDevice =
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *videoInput =
[AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
if (videoInput) {
if ([self.captureSession canAddInput:videoInput]) {
[self.captureSession addInput:videoInput];
self.activeVideoInput = videoInput;
} else {
if (error) {
}
}
}
// Setup the still image output
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
//self.imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
if ([self.captureSession canAddOutput:self.imageOutput]) {
[self.captureSession addOutput:self.imageOutput];
} else {
if (error) {
}
}
// 添加元数据输出捕捉
self.metadataOutput = [[AVCaptureMetadataOutput alloc] init];
if ([self.captureSession canAddOutput:self.metadataOutput]) {
[self.captureSession addOutput:self.metadataOutput];
// 添加新的捕捉会话输出
NSArray *metadataObjectTypes = @[AVMetadataObjectTypeFace];
self.metadataOutput.metadataObjectTypes = metadataObjectTypes;
//指定输出的元数据类型。
dispatch_queue_t mainqueue = dispatch_get_main_queue();
[self.metadataOutput setMetadataObjectsDelegate:self queue:mainqueue];
//有新的元数据被检测到时,会都回调代理AVCaptureMetadataOutputObjectsDelegate中的方法
//可以自定义系列的调度队列,不过由于人脸检测用到硬件加速,而且许多人物都要在主线程中执行,所以需要为这个参数指定主队列。
- 2、设置回调代理方法
#pragma -- mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection {
//metadataObjects 就是人脸检测结果的元数据,
//包含多个人脸数据信息,可以做相应处理,
// 比如将要实现的,在人脸上画框标记。
}
- 3、开始会话和结束会话
- (void)startSession {
if (![self.captureSession isRunning]) {
dispatch_async(self.videoQueue, ^{
[self.captureSession startRunning];
});
}
}
- (void)stopSession {
if ([self.captureSession isRunning]) {
dispatch_async(self.videoQueue, ^{
[self.captureSession stopRunning];
});
}
}
-
4、设置必要的预览层
视频预览层,和将要标记人脸的数据集合以及标记人脸方框的父layer。
self.faceLayers = [NSMutableDictionary dictionary];
// 存放人脸数据:@{faceId:layer}
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.overlayLayer = [CALayer layer];
self.overlayLayer.frame = self.bounds;
self.overlayLayer.sublayerTransform = THMakePerspectiveTransform(10000);
//设置sublayerTransform属性为CATransform3D,可以对所有子层应用视角转换。
[self.previewLayer addSublayer:self.overlayLayer];
static CATransform3D THMakePerspectiveTransform(CGFloat eyePosition) {
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0/eyePosition;
return transform;
// CoreAnimation中所使用的transformation matrix类型,用于进行缩放和旋转等转换。
// 设置m34可以应用视角转换,即让子层绕Y轴旋转。
}
- 5、元数据处理
NSArray *transformedFaces = [self transformedFacesFromFaces:faces];
// Listing 7.11
NSMutableArray *lostFaces = [self.faceLayers.allKeys mutableCopy];
for (AVMetadataFaceObject *face in transformedFaces) {
NSNumber *faceId = @(face.faceID);
[lostFaces removeObject:faceId];
// 如果对应faceId还在,将它从要删除视图的数组中移除。
CALayer *layer = self.faceLayers[faceId];
// 查找faceId对应的Layer
if (!layer) {
//如果没有对应layer,说明是新加入的faceId,需要新建对应来layer
layer = [self makeFaceLayer];
[self.overlayLayer addSublayer:layer];
self.faceLayers[faceId] = layer;
}
layer.transform = CATransform3DIdentity;
//对每个人脸图层,先将他的tansform属性设置为CATransform3DIdentity
//然后重新设置之前的用过的变换
layer.frame = face.bounds;
}
// 删除已经移除人脸对应的图层
for (NSNumber *faceId in lostFaces) {
CALayer *layer = self.faceLayers[faceId];
[layer removeFromSuperlayer];
[self.faceLayers removeObjectForKey:faceId];
}
将取得的人脸元数据的坐标做相应转换。
- (NSArray *)transformedFacesFromFaces:(NSArray *)faces {
// Listing 7.11
NSMutableArray *transformedFaces = [[NSMutableArray alloc] init];
for (AVMetadataObject *face in faces) {
AVMetadataObject *transformedFace = [self.previewLayer transformedMetadataObjectForMetadataObject:face];
//将设备坐标空间的人脸对象转化为视图空间对象集合
[transformedFaces addObject:transformedFace];
//得到一个由AVMetadataFaceObject实例组成的集合,其中有创建用户界面所需要的坐标点
}
return transformedFaces;
}
// 创建标记人脸的方框
- (CALayer *)makeFaceLayer {
CALayer *layer= [CALayer layer];
layer.borderWidth = 5.0f;
layer.borderColor = [UIColor colorWithRed:0.188 green:0.517 blue:0.877 alpha:1.0].CGColor;
return layer;
}
这样基本已经实现了人脸识别,以及标记功能。本书作者还对这个功能做了拓展,实现角度检测的变换。相对比较复杂。
拓展
这里的拓展,增加了对标记人脸方框的角度变换实现,它会随着人脸的转动和倾斜,方框也发生相应的变换。
- 1、在上述元数据处理方法中加入方框角度变换的是实现方法即可
添加在for循环之中,创建重置完layer.transform之后。
if (face.hasRollAngle) {
//检测人脸是否具有有效的倾斜角,如果没有获取属性会有异常。
//如果有rollAngle,则获取相应的CATransform3D
//将它与标识变换关联在一起,并设置图层的transform属性
CATransform3D t = [self transformForRollAngle:face.rollAngle];
layer.transform = CATransform3DConcat(layer.transform, t);
}
if (face.hasYawAngle) {
//检测人脸是否具有有效的偏转角,如果没有获取属性会有异常。
//如果有hasYawAngle,则获取相应的CATransform3D
//将它与标识变换关联在一起,并设置图层的transform属性
CATransform3D t = [self transformForYawAngle:face.hasYawAngle];
layer.transform = CATransform3DConcat(layer.transform, t);
}
- 2、Z轴的角度
// Rotate around Z-axis
- (CATransform3D)transformForRollAngle:(CGFloat)rollAngleInDegrees {
CGFloat rollAngleInRadians = THDegreesToRadians(rollAngleInDegrees);
//从对象得到rollAngle的单位是度,需要转换为弧度制。
//将转换结果赋值给CATransform3DMakeRotation函数
//x,y,z轴对应参数分别以0,0,1,得到的就是绕Z轴的倾斜角旋转转换、
return CATransform3DMakeRotation(rollAngleInRadians, 0.f, 0.f, 1.f);
}
- 3、Y轴的角度
// Rotate around Y-axis
- (CATransform3D)transformForYawAngle:(CGFloat)yawAngleInDegrees {
// Listing 7.13
//从对象得到hasYawAngle的单位是度,需要转换为弧度制。
//将转换结果赋值给CATransform3DMakeRotation函数
//x,y,z轴对应参数分别以0,-1,0,得到的就是绕Y轴的倾斜角旋转转换、
CGFloat yawAngleInRadians = THDegreesToRadians(yawAngleInDegrees);
CATransform3D yawAngleTransform = CATransform3DMakeRotation(yawAngleInRadians, 0.f, -1.f, 0.f);
//由于overlayer需要应用sublayerTransform,图层hi投影到Z轴
//人脸从一次移动到另一侧时就会出现3D效果
return CATransform3DConcat(yawAngleTransform, [self orientationTransform]);
//应用程序用户界面固定为垂直方向,不过需要为设备方向计算一个相应的旋转变换。
//如果不这样做,会导致人脸图层的便宜效果不正确,这一转换会同其他变换关联。
}
- 4、设备方向的调整
- (CATransform3D)orientationTransform {
// Listing 7.13
CGFloat angle = 0.0;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIDeviceOrientationLandscapeRight:
angle = -M_PI;
break;
case UIDeviceOrientationLandscapeLeft:
angle = M_PI;
break;
case UIDeviceOrientationPortrait:
angle = 0;
break;
default:
break;
}
return CATransform3DMakeRotation(angle, 0.f, 0.f, 1.f);
// return CATransform3DIdentity;
}
角度变换:
static CGFloat THDegreesToRadians(CGFloat degrees) {
// Listing 7.13
return degrees * M_PI / 180;
}
三、总结
只是实现简单地人脸识别,如果要实现更多的功能,相关的还有CoreAnimation以及Quartz框架的知识需要了解学习,还在努力中~~~