全文共字,预计学习时长10分钟
本文将详细介绍一个人脸检测与识别系统的创建过程。
创建单视图应用程序
首先,需要用单视图应用程序创建iOS的项目:
创建单视图应用程序
创建一个新的项目之后,我们计划用纯代码的应用程序来实现,不需要来回切换的按键或开关。
首先要删除main.storyboard,并按如下所示设置AppDelegate.swift文件:
funcapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)-Bool{
//在应用程序启动后覆盖自定义点。
window=UIWindow(frame:UIScreen.main.bounds)
窗口?.makeKeyAndVisible()
让controller=ViewController()
window?.rootViewController=controller
返回true
}
AppDelegate.swift
确保从部署信息中删除主函数故事板。
创建镜头并添加到子视图中
视图控制器只有一个,这是应用程序的主要切入点。
在此阶段,需要引入ARKit并把ARSCNView实例化,自动将摄像机设备的实时视频源变成镜头背景。此外,还能让SceneKit摄像机自动与设备的现实动作相匹配,这意味着不再需要用锚来追踪添加到镜头的物体位置。
提供屏幕边界使摄像机会话占据整个屏幕:
让sceneView=ARSCNView(frame:UIScreen.main.bounds)
ARSCNView实例化
在ViewDidLoad函数中,要建立诸如委托函数之类的东西,还需要观察帧统计信息从而监控是否有丢罗的情况:
self.view.addSubview(sceneView)//将场景添加到子视图中
sceneView.delegate=self//为视图控制器设置委托
sceneView.showsStatistics=true//显示统计信息
在ViewDidLoad函数中设置镜头
开启AR人脸跟踪配置会话
现在需要以AR人脸跟踪配置开启会话,该配置可以让我们使用iPhoneX,Xs,和Xr专用的前置TrueDepth相机。以下是来自苹果公司的详细解释:
人脸跟踪配置通过设备的前置摄像头检测用户面部。运行该配置时,AR会话检测用户面部(如果在前置摄像头画面可见),将代表人脸的AR面锚添加到锚定列表。面锚会提供关于人脸的位置与方向,其拓扑结构以及描述面部表情特征的信息。
ViewDidLoad函数示例:
overridefuncviewDidLoad(){
super.viewDidLoad()
self.view.addSubview(sceneView)
sceneView.delegate=self
sceneView.showsStatistics=true
警卫ARFaceTrackingConfiguration.isSupportedelse{return}
letconfiguration=ARFaceTrackingConfiguration()
configuration.isLightEstimationEnabled=true
sceneView.session.run(配置,选项:[。resetTracking,.removeExistingAnchors])
}
viewDidLoad中()函数
训练人脸识别模型
创建CoreML兼容的.mlmodel文件函数很多,在此介绍使用较为普遍的:
1.Turicreate:简化了自定义机器学习模型研发的python库,更重要的是可以将模型输出到.mlmodel文件,用Xcode进行语法分析。
2.MLImageClassifierBuilder():这是一个内置的可以随时使用的解决方法,通过Xcode连接到大量拖放界面,对相简单的模型进行训练。
MLImageClassifierBuilder
因为没有大型数据库,所以我们创建了大量模型测试这两种解决方法,最后决定使用MLImageClassifierBuilder(),设有67张“OmarMHAIMDAT”的照片(也就是我),还有在unsplash上找到的张陌生面孔。
打开操场上输入以下代码:
导入CreateMLUI
让builder=MLImageClassifierBuilder()
builder.showInLiveView()
MLImageClassifierBuilder
建议最高迭代次数设为20,再进行剪裁增强,每张图像会添加4个剪裁图像实例。
捕获帧并添加到模型中
需要用镜头委托ARSCNViewDelegate来扩展的ViewController,这得用到两种委托函数,一个用于人脸检测设置,另一个在检测到人脸时更新镜头:
人脸检测:
func渲染器(_渲染器:SCNSceneRenderer,nodeForanchor:ARAnchor)-SCNNode?{
guardletdevice=sceneView.deviceelse{
返回零
}
让faceGeometry=ARSCNFaceGeometry(设备:设备)
letnode=SCNNode(geometry:faceGeometry)
node.geometry?.firstMaterial?.fillMode=.lines
返回节点
}
人脸检测
很遗憾,镜头并没有在我们睁眼或者张嘴时更新。这种情况下,就得相应地更新镜头。
更新镜头:
funcrenderer(_renderer:SCNSceneRenderer,didUpdatenode:SCNNode,foranchor:ARAnchor){
警卫让faceAnchor=锚定为?ARFaceAnchor,
让faceGeometry=node.geometry为?ARSCNFaceGeometryelse{
返回
}
faceGeometry.update(来自:faceAnchor.geometry)
提取整个面部的几何结构信息,更新节点。
获取相机的帧:
既然ARSCNView承自AVCaptureSession,那就可以获得cvPixelFuffer填充入模型。
从sceneView属性中获取的简单方法:
guardletpixelBuffer=self.sceneView.session.currentFrame?.capturedImageelse{return}
将相机的帧添加到模型:
到这一步,可以检测到人脸了,相机的帧也已完备,接下来就能为模型填充内容了:
警卫让模特=试试?VNCoreMLModel(for:FaceRecognition3()。model)else{
fatalError(“无法加载模型”)
}
让coreMlRequest=VNCoreMLRequest(model:model){[weakself]request,errorin
警卫让结果=request.results为?[VNClassificationObservation]
让topResult=results.first
其他{
fatalError(“意外结果”)
}
DispatchQueue.main.async{[弱自我]in
打印(topResult.identifier)
}
}
guardletpixelBuffer=self.sceneView.session.currentFrame?.capturedImageelse{return}
lethandler=VNImageRequestHandler(cvPixelBuffer:pixelBuffer,options:[:])
DispatchQueue.global()。async{
做{
尝试handler.perform([coreMlRequest])
}catch{
打印(错误)
}
}
didUpdate渲染器
显示被识别者的姓名
最后一个恼人的部分就是把3D文本投射到识别的人脸上。我们稍加思考就会想到,配置不如ARWorldTrackingConfiguration功能那么强,提供的函数和类并不多。用前置摄像头代替,能实现的功能有限。
尽管不能跟踪面部动作并进行相应的改变,但我们仍能将3D文本投射到屏幕上。
lettext=SCNText(string:“”,extrusionDepth:2)
letfont=UIFont(名称:“Avenir-Heavy”,大小:18)
text.font=font
letmaterial=SCNMaterial()
material.diffuse.contents=UIColor.black
text.materials=[material]
text.firstMaterial?.isDoubleSided=true
lettextNode=SCNNode(geometry:faceGeometry)
textNode.position=SCNVector3(-0.1,-0.01,-0.5)
textNode.scale=SCNVector3(0.,0.,0.)
textNode.geometry=tex
SCNText示例
有了SCNText对象后,需要随着相应的人脸进行更新,然后添加到根节点:
让coreMlRequest=VNCoreMLRequest(model:model){[weakself]request,errorin
警卫让结果=request.results为?[VNClassificationObservation]
让topResult=results.first
其他{
fatalError(“意外结果”)
}
DispatchQueue.main.async{[弱自我]in
打印(topResult.identifier)
iftopResult.identifier!=“Unknown”{
text.string=topResult.identifier
自我!.sceneView.scene.rootNode.addChildNode(textNode)
self!.sceneView.autoenablesDefaultLighting=true
}
}
}
最终结果
人脸检测与识别系统成果展示。
项目