使用CoreML和ARKit进行人脸检测和识别

2019-08-05 11:47:12 浏览数 (1)

作者 | Omar M'Haimdat

来源 | Medium

编辑 | 代码医生团队

创建单视图应用程序

首先,需要使用单个视图应用创建一个iOS项目:

创建一个视图应用程序

现在已经拥有了自己的项目,并且因为不喜欢使用故事板,所以应用程序以编程方式完成,这意味着没有按钮或开关切换,只需要纯粹的代码。

必须删除main.storyboard并设置您的AppDelegate.swift文件,如下所示:

代码语言:javascript复制
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.makeKeyAndVisible()
        let controller = ViewController()
        window?.rootViewController = controller
        
        return true
    }

确保从部署信息中删除故事板“Main”。

创建场景并将其添加到子视图

只有一个ViewController,它将是应用程序的主要入口点。

在这个阶段,需要导入ARKit并实例化一个ARSCNView自动渲染来自设备相机的实时视频作为场景背景。它还会自动移动其SceneKit摄像头以匹配设备的真实世界移动,这意味着不需要锚点来跟踪我们添加到场景中的对象的位置。

需要给它屏幕边界,以便摄像机会话占据整个屏幕:

代码语言:javascript复制
let sceneView = ARSCNView(frame: UIScreen.main.bounds)

在该ViewDidLoad方法中,将设置一些内容,例如委托,还需要查看帧统计信息以监视帧丢弃:

代码语言:javascript复制
self.view.addSubview(sceneView) // add the scene to the subview
sceneView.delegate = self // Setting the delegate for our view controller
sceneView.showsStatistics = true // Show statistics

开始一个ARFaceTrackingConfiguration会话

现在需要开始一个会话ARFaceTrackingConfiguration,这个配置让可以访问仅适用于iPhone X,Xs和Xr的前置TrueDepth摄像头。以下是Apple文档中的更详细说明:

面部跟踪配置会根据设备的前置摄像头检测用户的脸部。运行此配置时,AR会话将检测用户的面部(如果在前置摄像头图像中可见),并在其锚点列表中添加表示面部的ARFaceAnchor对象。每个面部锚点提供有关面部位置和方向,其拓扑以及描述面部表情的特征的信息。

ViewDidLoad方法应如下所示:

代码语言:javascript复制
override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(sceneView)
        sceneView.delegate = self
        sceneView.showsStatistics = true
        guard ARFaceTrackingConfiguration.isSupported else { return }
        let configuration = ARFaceTrackingConfiguration()
        configuration.isLightEstimationEnabled = true
        sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
    }

训练人脸识别模型

有多种方法可以创建与CoreML兼容的.mlmodel文件,这些是常见的:

  1. Turicreate:它是python库,简化了自定义机器学习模型的开发,更重要的是,可以将模型导出到可由Xcode解析的.mlmodel文件中。
  2. MLImageClassifierBuilder():它是一个内置的解决方案,提供Xcode开箱即用,可以访问几乎一个拖放界面来训练一个相对简单的模型。

https://pypi.org/project/turicreate/?source=post_page---------------------------

MLImageClassifierBuilder

已经创建了多个模型来测试这两个解决方案,因为没有大数据集,决定使用MLImageClassifierBuilder()和一组67个图像,这些图像是'Omar MHAIMDAT '和一组261 在unsplash上找到的'未知'的面孔。

https://unsplash.com/

打开游乐场并编写此代码:

代码语言:javascript复制
import CreateMLUI
 
let builder = MLImageClassifierBuilder()
builder.showInLiveView()

建议将最大迭代次数设置为20并添加裁剪增强,这将为每个图像添加4个裁剪图像实例。

捕获相机帧并将其注入模型

需要使用场景委托来扩展ViewController ARSCNViewDelegate。需要两个委托方法,一个用于设置面部检测,另一个用于在检测到面部时更新场景:

人脸检测:

代码语言:javascript复制
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        
        guard let device = sceneView.device else {
            return nil
        }
        
        let faceGeometry = ARSCNFaceGeometry(device: device)
        
        let node = SCNNode(geometry: faceGeometry)
        
        node.geometry?.firstMaterial?.fillMode = .lines
        
        return node
    }

不幸的是,当睁开眼睛或嘴巴时,场景不会更新。在这种情况下,需要相应地更新场景。

更新场景:

代码语言:javascript复制
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        
        guard let faceAnchor = anchor as? ARFaceAnchor,
            let faceGeometry = node.geometry as? ARSCNFaceGeometry else {
                return
        }
        
        faceGeometry.update(from: faceAnchor.geometry)
}

采用整个面几何和映射,并更新节点。

获取相机框架:

这很有趣,因为ARSCNView继承自AVCaptureSession,意味着可以得到一个cvPixelFuffer可以提供模型。

这是从sceneView属性中获取它的简单方法:

代码语言:javascript复制
guard let pixelBuffer = self.sceneView.session.currentFrame?.capturedImage else { return }

将相机框架注入模型:

现在可以检测到面部并拥有每个相机框架,已准备好为模型提供一些内容:

代码语言:javascript复制
guard let model = try? VNCoreMLModel(for: FaceRecognition3().model) else {
            fatalError("Unable to load model")
        }
        
        let coreMlRequest = VNCoreMLRequest(model: model) {[weak self] request, error in
            guard let results = request.results as? [VNClassificationObservation],
                let topResult = results.first
                else {
                    fatalError("Unexpected results")
            }
 
            DispatchQueue.main.async {[weak self] in
                print(topResult.identifier)
            }
        }
        
        guard let pixelBuffer = self.sceneView.session.currentFrame?.capturedImage else { return }
        
        
        let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
        DispatchQueue.global().async {
            do {
                try handler.perform([coreMlRequest])
            } catch {
                print(error)
            }
        }

在识别的脸部上方显示名称

最后也可能是最令人沮丧的部分是在识别出的脸部上方投射3D文本。如果考虑一下,配置就不如ARWorldTrackingConfiguration能够访问众多方法和类的功能那样强大。使用的是前置摄像头,可以实现的功能很少。

尽管如此,仍然可以在屏幕上投影3D文本,但它不会跟踪面部运动并相应地进行更改。

代码语言:javascript复制
let text = SCNText(string: "", extrusionDepth: 2)
let font = UIFont(name: "Avenir-Heavy", size: 18)
text.font = font
let material = SCNMaterial()
material.diffuse.contents = UIColor.black
text.materials = [material]
text.firstMaterial?.isDoubleSided = true
        
let textNode = SCNNode(geometry: faceGeometry)
textNode.position = SCNVector3(-0.1, -0.01, -0.5)
textNode.scale = SCNVector3(0.002, 0.002, 0.002)
textNode.geometry = text

现在有了SCNText对象,需要使用相应的face更新它并将其添加到rootNode:

代码语言:javascript复制
      let coreMlRequest = VNCoreMLRequest(model: model) {[weak self] request, error in
            guard let results = request.results as? [VNClassificationObservation],
                let topResult = results.first
                else {
                    fatalError("Unexpected results")
            }
 
            DispatchQueue.main.async {[weak self] in
                print(topResult.identifier)
                if topResult.identifier != "Unknown" {
                    text.string = topResult.identifier
                    self!.sceneView.scene.rootNode.addChildNode(textNode)
                    self!.sceneView.autoenablesDefaultLighting = true
                }
            }
        }

最后结果:

这是面部检测和识别的最终结果。

该项目可从Github帐户下载

https://github.com/omarmhaimdat/WhoAreYou

0 人点赞