美文网首页程序员iOS音视频开发
AVFoundation开发秘籍笔记-07高级捕捉功能之人脸识别

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

作者: 竹与豆 | 来源:发表于2018-06-06 14:17 被阅读9次

    一、概述

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/xlubsftx.html