Core ML 模型训练

iOS黑科技之(AVFoundation)动态人脸识别(二)

2017-11-22  本文已影响1188人  TitanCoder

iOS黑科技之(AVFoundation)动态人脸识别(二)

上一篇介绍了Core Image实现的静态人脸识别, 这里介绍AVFoundation的强大功能之一的动态人脸识别

一. 首先介绍一些人脸识别的方式

1. CoreImage静态人脸识别, 可识别照片, 图像等

2. Face++

3. OpenCV

4. Vision

5. AVFoundation

二. 对关键类的简单介绍

1. AVCaptureDevice:代表硬件设备

//4. 移除旧输入,添加新输入
//4.1 设备加锁
session.beginConfiguration()
//4.2. 移除旧设备
session.removeInput(deviceIn)
//4.3 添加新设备
session.addInput(newVideoInput)
//4.4 设备解锁
session.commitConfiguration()

2. AVCaptureDeviceInput:设备输入数据管理对象

3. AVCaptureOutput: 代表输出数据

4. AVCaptureSession: 媒体(音、视频)捕捉会话

5. AVCaptureVideoPreviewLayer: 图片预览层

好了, 上面吧啦吧啦的说了那么多废话, 那么我们的人脸识别究竟是怎样实现的呢? 下面干货来了

三. 添加扫描设备

1. 输出设备

//3.创建原数据的输出对象
let metadataOutput = AVCaptureMetadataOutput()
        
//4.设置代理监听输出对象输出的数据,在主线程中刷新
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)

//7.告诉输出对象要输出什么样的数据,识别人脸, 最多可识别10张人脸
metadataOutput.metadataObjectTypes = [.face]

主要代码如下:

fileprivate func addScaningVideo(){
    //1.获取输入设备(摄像头)
    guard let device = AVCaptureDevice.default(for: .video) else { return }
    
    //2.根据输入设备创建输入对象
    guard let deviceIn = try? AVCaptureDeviceInput(device: device) else { return }
    deviceInput = deviceIn
    
    //3.创建原数据的输出对象
    let metadataOutput = AVCaptureMetadataOutput()
    
    //4.设置代理监听输出对象输出的数据,在主线程中刷新
    metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
    //4.2 设置输出代理
    faceDelegate = previewView
    
    //5.设置输出质量(高像素输出)
    session.sessionPreset = .high
    
    //6.添加输入和输出到会话
    if session.canAddInput(deviceInput!) {
        session.addInput(deviceInput!)
    }
    if session.canAddOutput(metadataOutput) {
        session.addOutput(metadataOutput)
    }
    
    //7.告诉输出对象要输出什么样的数据,识别人脸, 最多可识别10张人脸
    metadataOutput.metadataObjectTypes = [.face]
    
    //8.创建预览图层
    previewLayer = AVCaptureVideoPreviewLayer(session: session)
    previewLayer.videoGravity = .resizeAspectFill
    previewLayer.frame = view.bounds
    previewView.layer.insertSublayer(previewLayer, at: 0)
    
    //9.设置有效扫描区域(默认整个屏幕区域)(每个取值0~1, 以屏幕右上角为坐标原点)
    metadataOutput.rectOfInterest = previewView.bounds
    
    //10. 开始扫描
    if !session.isRunning {
        DispatchQueue.global().async {
            self.session.startRunning()
        }
    }
}

2. 切换摄像头

@IBAction func switchCameraAction(_ sender: Any) {
    //1. 执行转场动画
    let anima = CATransition()
    anima.type = "oglFlip"
    anima.subtype = "fromLeft"
    anima.duration = 0.5
    view.layer.add(anima, forKey: nil)
    
    //2. 获取当前摄像头
    guard let deviceIn = deviceInput else { return }
    let position: AVCaptureDevice.Position = deviceIn.device.position == .back ? .front : .back
    
    //3. 创建新的input
    let deviceSession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position)
    guard let newDevice = deviceSession.devices.filter({ $0.position == position }).first else { return }
    guard let newVideoInput = try? AVCaptureDeviceInput(device: newDevice) else { return }
    
    //4. 移除旧输入,添加新输入
    //4.1 设备加锁
    session.beginConfiguration()
    //4.2. 移除旧设备
    session.removeInput(deviceIn)
    //4.3 添加新设备
    session.addInput(newVideoInput)
    //4.4 设备解锁
    session.commitConfiguration()
    
    //5. 保存最新输入
    deviceInput = newVideoInput
}

3. 处理扫描结果

实现AVCaptureMetadataOutputObjectsDelegate该协议的协议方法(只有一个方法)

//`metadataObjects`就是返回的扫描结果
optional public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)

4. AVMetadataFaceObject介绍

5. 处理扫描结果

5.1 获取预览图层的人脸数组

fileprivate func transformedFaces(faceObjs: [AVMetadataObject]) -> [AVMetadataObject] {
    var faceArr = [AVMetadataObject]()
    for face in faceObjs {
        //将扫描的人脸对象转成在预览图层的人脸对象(主要是坐标的转换)
        if let transFace = previewLayer.transformedMetadataObject(for: face){
            faceArr.append(transFace)
        }
    }
    return faceArr
}

5.2 根据人脸位置添加红框

faceLayer?.frame = face.bounds
    fileprivate func transformDegress(yawAngle: CGFloat) -> CATransform3D {
        let yaw = degreesToRadians(degress: yawAngle)
        //围绕Y轴旋转
        let yawTran = CATransform3DMakeRotation(yaw, 0, -1, 0)
        //红框旋转问题
        return CATransform3DConcat(yawTran, CATransform3DIdentity)
    }
    
    //处理偏转角问题
    fileprivate func transformDegress(rollAngle: CGFloat) -> CATransform3D {
        let roll = degreesToRadians(degress: rollAngle)
        //围绕Z轴旋转
        return CATransform3DMakeRotation(roll, 0, 0, 1)
    }
    
    //角度转换
    fileprivate func degreesToRadians(degress: CGFloat) -> CGFloat{
        return degress * CGFloat(Double.pi) / 180
    }

//3.4 设置偏转角(左右摇头)
if face.hasYawAngle{
    let tranform3D = transformDegress(yawAngle: face.yawAngle)
    
    //矩阵处理
    faceLayer?.transform = CATransform3DConcat(faceLayer!.transform, tranform3D)
}

//3.5 设置倾斜角,侧倾角(左右歪头)
if face.hasRollAngle{
    let tranform3D = transformDegress(rollAngle: face.rollAngle)
    
    //矩阵处理
    faceLayer?.transform = CATransform3DConcat(faceLayer!.transform, tranform3D)
}


GitHub--Demo地址


其他相关文章

上一篇 下一篇

猜你喜欢

热点阅读