作者 | 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文件,这些是常见的:
- Turicreate:它是python库,简化了自定义机器学习模型的开发,更重要的是,可以将模型导出到可由Xcode解析的.mlmodel文件中。
- 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