AVFoundation开发秘籍笔记-07高级捕捉功能之人脸识别

一、概述

这里的人脸检测是通过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框架的知识需要了解学习,还在努力中~~~

猜你喜欢

转载自blog.csdn.net/fengzhixinfei/article/details/80594269
今日推荐