人脸检测(Face Detection)是利用计算机视觉处理技术子啊数字图像或视频中自动定位人脸的过程,人脸检测不仅检测人脸在图像或视频中的位置,还能检测出其大小与方向(姿态)。人脸检测是欧冠人脸图像分析应用的基础,包括人脸识别和验证、监控场合的人脸跟踪、面部表情解析、面部属性识别(性别、年龄、表情)、面部光照调整和变形、面部形状重建、图像视频检索等。人脸识别(Face Recognition)是判定两张人脸是否为同一个人,人脸识别技术是人脸检测技术的拓展应用。目前ARKit仅提供人脸检测而不提供人脸识别。人脸跟踪(Face Tracking)是指将人脸检测拓展到视频序列,跟踪同一张脸在视频序列中的位置,人脸跟踪不包含人脸识别功能,它是根据视频序列中人脸的位置和运动推断不同视频帧中的人脸是否为同一个人的技术。
人脸网格
除了人脸姿态,ARKit还提供了每个已检测到的人脸网格(ARFaceGeometry),该网格包含了1220个顶点,网格数据包含了顶点、索引、三角形数量、纹理坐标等相关信息,利用人脸网格开发者就可以渲染出人脸形状,或者对人脸网络进行自定义贴图等。根据官方demo通过Scenekit实现面部网格定位,主要涉及到的类有ARFaceAnchor、ARFaceGeometry和ARSCNFaceGeometry。其中ARFaceAnchor继承自ARAnchor,是专门用于锚定人脸的锚点,其transform属性置顶相对于世界坐标系的人脸位置与方向,利用它就可以生成人脸网格;ARSCNFaceGeometry包含ARKit生成的人脸网格信息,包括顶点、索引、UV坐标等信息;ARSCNFaceGeometry则是利用ARFaceGeometry网格数据生成SCNGeometry,可以直接作为Scenekit场景中的节点。代码如下:
通用的代理回调方法:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else { return }
DispatchQueue.main.async {
let contentController = self.selectedVirtualContent.makeController()
// 通过协议方法进行节点初始化
if node.childNodes.isEmpty, let contentNode = contentController.renderer(renderer, nodeFor: faceAnchor) {
node.addChildNode(contentNode)
self.faceAnchorsAndContentControllers[faceAnchor] = contentController
}
}
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
guard let sceneView = renderer as? ARSCNView, anchor is ARFaceAnchor else {
return nil
}
let faceGeometry = ARSCNFaceGeometry(device: sceneView.device!)!
let material = faceGeometry.firstMaterial!
// 设置贴图
material.diffuse.contents = UIImage.init(named:"wireframeTexture")
material.lightingModel = .physicallyBased
contentNode = SCNNode(geometry: faceGeometry)
return contentNode
}
// 刷新贴图
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceGeometry = node.geometry as? ARSCNFaceGeometry,
let faceAnchor = anchor as? ARFaceAnchor else {
return
}
faceGeometry.update(from: faceAnchor.geometry)
}
挂载虚拟元素
挂载虚拟元素可以通过Scenekit也可以通过RealityKit,通过Scenekit挂在元素时需要像贴图一样获取面部锚点的transform然后实时刷新节点的位置即可,代码如下:
class FaceOcclusionOverlay: NSObject, VirtualContentController {
var contentNode: SCNNode?
var occlusionNode: SCNNode!
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
guard let sceneView = renderer as? ARSCNView, anchor is ARFaceAnchor else {
return nil
}
// 获取面部的形状
let faceGeometry = ARSCNFaceGeometry(device: sceneView.device!)!
faceGeometry.firstMaterial!.colorBufferWriteMask = []
occlusionNode = SCNNode(geometry: faceGeometry)
occlusionNode.renderingOrder = -1
let faceOverlayContent = SCNReferenceNode(named: "overlayModel")
let material = SCNMaterial.materialWithColor(anchor.identifier.toRandomColor())
faceOverlayContent.childNode(withName: "text", recursively: true)?.geometry?.materials = [material]
contentNode = SCNNode()
contentNode!.addChildNode(occlusionNode)
contentNode!.addChildNode(faceOverlayContent)
return contentNode
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceGeometry = occlusionNode.geometry as? ARSCNFaceGeometry,
let faceAnchor = anchor as? ARFaceAnchor else { return }
faceGeometry.update(from: faceAnchor.geometry)
}
}
通过RealityKit实现时可以通过Reality Composer来进行实现,其打开方式为Xcode->Open Developer Tool->Reality Composer,在Reality Composer选择人脸模式就可以在里面添加虚拟物体了,并且可以添加动画及触发条件,具体使用步骤可以查看苹果的文档。在配置好config为ARFaceTrackingConfiguration的情况下代码中使用及其简单,导入composer project后会生成相应的调用代码,如下:
// 场景1即为我创建的面部AR场景
let newAnchor = try! Composer01.load场景1()
arView.scene.anchors.append(newAnchor)
// 需要切换场景时可以直接调用
let g = try! Composer01.load场景()
self.scene.anchors.removeAll()
self.scene.addAnchor(g)


BlendShapes
iPhone X之后机型上增加了深度相机,利用深度相机可以更加精确的捕获用户的面部表情,提供更详细的面部特征点信息。当利用前置摄像头捕获到用户面部信息后,ARKit通过一种叫做BlendShapes的方式对面部表情进行描述,BlendShapes在3d Max中也叫变形器,这个概念原本用于描述通过参数控制模型网格的位移,苹果将这一技术用在ARkit用于表示通过人脸表情因子驱动模型的技术。BlendShapes在技术上是一组存储了用户面部表情特征运动因子的字典,共包含了52组特征运动数据,ARkit会根据摄像机采集的用户表情特征值实时地设置对应的运动因子,运用这些运动因子可以驱动2D或者3D人脸模型,使这些模型呈现与用户一致的表情。这52组因子中,包括7组左眼运动因子、7组右眼运动因子、27组嘴巴与下巴运动因子、10组眉毛脸颊鼻子运动因子和一组舌头运动因子数据,我们可以全部使用也可以只使用一部分。
每组运动因子表示一个ARKit识别的人脸表情特征,其包含一个表示人脸特定表情的定位符与一个表示表情程度的浮点类型值,表情程度值的范围为[0, 1],其中0表示没有表情,1表示完全表情。ARKit会实时捕捉这些运动因子并返回表情值,然后调用Scenekit中的SCNMorpher.SetWeight()方法进行网格融合,其方法为
setWeight(_ weight: CGFloat, forTargetNamed targetName: Int);
其中froTargetNamed参数为需要融合的网格变形器名,即在模型中设置的BlendShapeLocation名称,weight为0~1的权重值(表情值)。
使用ARKit的BlendShapes功能需要满足两个条件:1.有一个备有深度相机或者A12及以上处理器的移动设备;2.有一个BlendShapes已经定义好的模型。usdz以及Reality格式不支持BlendShapes,可以使用Scenekit使用BlendShapes。如下图分别是设计好的模型以及其设置好的BlendShapeLocation名称:

使用方法也很简单,在面部跟踪状态下将节点添加到面部节点下并且在帧更新的代理方法下进行实时更新即可
func modelSetup() {
if let filePath = Bundle.main.path(forResource: "BlendShapeFace", ofType: "scn") {
// 同步加载face模型
let referenceURL = URL(fileURLWithPath: filePath)
self.contentNode = SCNReferenceNode(url: referenceURL)
self.contentNode?.load()
self.head.morpher?.unifiesNormals = true
self.contentNode?.scale = SCNVector3(0.01,0.01,0.01)
self.contentNode?.position.y += 0.02
}
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let _ = anchor as? ARFaceAnchor else { return }
modelSetup()
node.addChildNode(self.contentNode!)
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode,for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else { return }
DispatchQueue.main.async {
for (key, value) in faceAnchor.blendShapes {
print("\(key.rawValue)----\(value)")
if let fValue = value as? Float {
self.head.morpher?.setWeight(CGFloat(fValue), forTargetNamed: key.rawValue)
}
}
}
}
效果如下:

ARKit下同时开启前后摄像头
拥有前置深度相机或A12及以上处理器硬件的iPhone/iPad,再运行iOS13以上系统时,可以同时开启设备前后摄像头,即同时进行人脸检测是世界跟踪。在RealityKit下同时开启前后摄像头需要使用ARFaceTranckingConfiguration配置或者ARWorldTrackingConfiguration。使用ARFaceTranckingConfiguration时将其supportWorldTracking设置为true,使用ARWorldTrackingConfiguration时将其userFaceTrackingEnabled属性设置为true都可以支持人脸检测的设备上同时开启前后摄像头。然后就可以根据BlendShapes的流程进行模型的BlendShapeLocation参数设置。
// 判断是否支持开启双摄像头
if ARWorldTrackingConfiguration.supportsUserFaceTracking {
let config = ARWorldTrackingConfiguration()
// 在世界跟踪模式下开启面部跟踪
config.userFaceTrackingEnabled = true
config.planeDetection = .horizontal
config.worldAlignment = .gravity
arView.automaticallyConfigureSession = false
arView.session.run(config,options: [])
arView.session.delegate = arView
arView.createRobotHead()
}
效果图如下:

网友评论