如何判断ARSession中的ARFaceAnchor未更新及人脸移出视野?
这个问题其实是ARFaceTrackingConfiguration的默认行为导致的——人脸移出摄像头视野后,ARSession并不会自动移除对应的ARFaceAnchor,所以session(_:didRemove anchors:)确实不会被触发。而且一旦人脸完全脱离追踪范围,session(_:didUpdate anchors:)也会停止调用,因为没有锚点状态变化了。不过我们可以通过以下几种方式来解决:
核心思路:利用ARFaceAnchor.isTracked属性+帧级检查
ARFaceAnchor有个关键的isTracked属性:当人脸在视野内且被稳定追踪时,它的值是true;当人脸移出视野或者追踪丢失时,会变为false。我们可以结合这个属性,加上对每帧的锚点检查,来判断人脸是否已经离开。
步骤1:保存追踪人脸的标识
首先定义变量来记录当前追踪的人脸锚点UUID和追踪状态:
// 记录当前追踪的人脸锚点UUID var trackedFaceUUID: UUID? // 标记上一帧人脸是否处于被追踪状态 var wasFaceTracked = false // 保存你的人脸锚点副本 var savedFaceAnchor: ARFaceAnchor?
步骤2:在锚点更新时同步状态
在session(_:didUpdate anchors:)里更新锚点副本,并记录追踪状态:
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) { for anchor in anchors { guard let faceAnchor = anchor as? ARFaceAnchor else { continue } // 更新当前追踪的人脸UUID trackedFaceUUID = faceAnchor.uuid // 保存锚点副本 savedFaceAnchor = faceAnchor.copy() as? ARFaceAnchor if faceAnchor.isTracked { wasFaceTracked = true // 这里可以处理人脸正在被追踪的逻辑 } else { // 首次检测到追踪丢失,触发人脸移出逻辑 handleFaceOutOfFrame() wasFaceTracked = false } } }
步骤3:帧级检查确保不遗漏
如果人脸完全移出后,session(_:didUpdate anchors:)可能不再被调用,所以需要在每帧更新时主动检查锚点状态。你可以选择两种回调方式:
方式A:利用ARSession的帧更新回调
实现session(_:didUpdate frame:)方法,在每帧里检查当前会话中的人脸锚点:
func session(_ session: ARSession, didUpdate frame: ARFrame) { guard let trackedUUID = trackedFaceUUID else { return } // 在当前帧的锚点中查找我们追踪的人脸 let currentFaceAnchor = frame.anchors.first { $0.uuid == trackedUUID && $0 is ARFaceAnchor } as? ARFaceAnchor if let faceAnchor = currentFaceAnchor { // 人脸锚点存在但已停止追踪 if !faceAnchor.isTracked && wasFaceTracked { handleFaceOutOfFrame() wasFaceTracked = false } } else { // 人脸锚点已经不存在,确认人脸完全丢失 if wasFaceTracked { handleFaceOutOfFrame() wasFaceTracked = false trackedFaceUUID = nil } } }
方式B:如果用SceneKit,利用渲染器的帧更新回调
如果你用SceneKit渲染AR内容,可以实现renderer(_:updateAtTime:):
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let session = renderer.session, let trackedUUID = trackedFaceUUID else { return } let currentFaceAnchor = session.currentFrame?.anchors.first { $0.uuid == trackedUUID && $0 is ARFaceAnchor } as? ARFaceAnchor // 逻辑和上面的帧更新回调一致 if let faceAnchor = currentFaceAnchor { if !faceAnchor.isTracked && wasFaceTracked { handleFaceOutOfFrame() wasFaceTracked = false } } else { if wasFaceTracked { handleFaceOutOfFrame() wasFaceTracked = false trackedFaceUUID = nil } } }
步骤4:实现人脸移出后的处理逻辑
最后写好handleFaceOutOfFrame()函数,处理人脸移出后的业务:
func handleFaceOutOfFrame() { // 这里写人脸移出视野后的逻辑,比如重置状态、显示提示等 print("人脸已移出摄像头视野") savedFaceAnchor = nil }
补充说明
为什么session(_:didRemove anchors:)不触发?因为ARFaceTrackingConfiguration默认不会自动移除人脸锚点——只有当你设置maximumNumberOfTrackedFaces为大于1的值,且有新的人脸进入视野替换旧的追踪对象,或者ARSession被重置时,才会调用这个方法。所以完全不能依赖它来检测单个人脸的移出。
内容的提问来源于stack exchange,提问作者user14492

