【iOS ARKit】人脸追踪之挂载虚拟元素

        人脸跟踪(Face Tracking)是指将人脸检测扩展到视频序列,跟踪同一张人脸在视频序列中的位置。是论上讲,任何出现在视频中的人险都可以被跟踪,也即是说,在连续视频帧中检测到的人脸可以被识别为同一个人。人脸跟踪不是人脸识别的一种形式,它是根据视频序列中人脸的位置和运动推断不同视频帧中的人脸是否同一人的技术。

  • 挂载虚拟元素

      在iOS Realitykit 中,在检测到的人脸面部挂载虚拟元素的实现方式有两种:一种是通过遵循ARSesionDelegate 协议,执行 session(_ session: ARSession, didAdd anchors: LARAnchor」)方法,在获取的ARPaceAnchor 上挂载虚拟元素;另一种是与 Reality Composer结合使用。在使用第一种方式时,可以利用 ARFaceAnchor 初始化一个 AnchorEntity 类型实例,这样,ARFaceAnehor的姿态信息就可以直接被使用,典型的使用代码如下:

  public func session(_ session: ARSession, didAdd anchors: [ARAnchor]){
           guard let pAnchor = anchors[0] as? ARObjectAnchor else {
              return
            }
            

            let objectName =  pAnchor.referenceObject.name == "jinhua" ? "toy_drummer" : "toy_robot_vintage"
            DispatchQueue.main.async {
                do{
                    let myModeEntity = try Entity.load(named: objectName)
                    let objectEntity = AnchorEntity(anchor: pAnchor)
                    objectEntity.addChild(myModeEntity)
                                                          myModeEntity.playAnimation(myModeEntity.availableAnimations[0].repeat())
                    
                    self.arView?.scene.addAnchor(objectEntity)
                } catch {
                    print("加载失败")
                }
                            
            }
            
        }

      在检测到的人脸上挂载虚拟元素使用 RealityKit 与Reality Composer 结合的方式更方便直观,特别是需要在很多虚拟元素之间进行切换时,可以大大简化代码逻辑。使用第二种方式的操作步骤如下:

 (1) 打开 Reality Composer,并创建一个锚定到人脸的工程(Reality Composer 具体操作参阅第10章),如图5-5所示。

 (2)导入需要挂载的 USDZ 或者 Reality 模型文件并调整到参考人脸理想的位置,然后给场景命名(命名时建议使用英文字母或者英文字母与数字组合,方便在RealityKit 中调用),如图5-6所示。

(3)在Reality Composer 菜单中依次选择“文件”—“保存”(或者使用快捷键 Command+S)保存工程为FaceMask. rcproject 文件(工程名根据需要自行命名)。

(4)使用 RealityKit 加载工程文件到内存,直接获取工程文件中的锚点信息并将其作为 ARAnchor 添加到 ARVeiw.scene 场景中即可。这里需要注意的是,ARKit会在检测到人脸后自动在指定的位置挂载虚拟元素,但 ARKit 并不会自动运行人脸检测的 ARSession,因此,需要手动运行人脸检测的 ARSession 以开启人脸检测功能,典型代码如代码下:

struct FaceMaskContainer : UIViewRepresentable{
    
    
    func makeUIView(context: Context) -> ARView {
        let arView = ARView(frame: .zero)
        
        return arView
    }
    func updateUIView(_ uiView: ARView, context: Context) {
        guard ARFaceTrackingConfiguration.isSupported else {
            return
        }
       
        let config = ARFaceTrackingConfiguration()

        config.isWorldTrackingEnabled = false
        config.providesAudioData = false
        config.maximumNumberOfTrackedFaces =  1
        config.isLightEstimationEnabled = true
//        uiView.session = context.coordinator
        if let faceAnchor = try? FaceMask.loadGlass1() {
            uiView.scene.addAnchor(faceAnchor)
        }
        uiView.session.run(config,options: [.resetTracking, .removeExistingAnchors])
        context.coordinator.arView = uiView
        
        let gesture = UISwipeGestureRecognizer()
        gesture.direction = [.left,.right]
        gesture.addTarget(context.coordinator, action: #selector(context.coordinator.changeGlass(gesture:)))
        uiView.addGestureRecognizer(gesture)
    }
    
    func makeCoordinator() -> FaceMaskContainerCoordinator {
        FaceMaskContainerCoordinator()
    }
    
    class FaceMaskContainerCoordinator: NSObject {
        var arView :ARView?
        var faceMaskCount = 0
        let numberOfMasks = 5
        @MainActor @objc func changeGlass(gesture: UISwipeGestureRecognizer){
            guard let arView = arView else {
                return
            }
            let jian = gesture.direction == .left
            jian ?  (faceMaskCount -= 1) : (faceMaskCount += 1)
            if faceMaskCount < 0 {
                faceMaskCount = 5
            }
            faceMaskCount %= numberOfMasks
            switch faceMaskCount {
            case 0:
                if let g = try? FaceMask.loadGlass2(){
                    arView.scene.anchors.removeAll()
                    arView.scene.addAnchor(g)
                }
                
            case 1:
                if let g = try? FaceMask.loadIndian() {
                    arView.scene.anchors.removeAll()
                    arView.scene.addAnchor(g)
                }
                
            case 2:
                if let g = try? FaceMask.loadRabbit() {
                    arView.scene.anchors.removeAll()
                    arView.scene.addAnchor(g)
                }
                
            case 3:
                if let g = try? FaceMask.loadHelicopterPilot() {
                    arView.scene.anchors.removeAll()
                    arView.scene.addAnchor(g)
                }
                
            case 4:
                if let g = try? FaceMask.loadGlass1() {
                    arView.scene.anchors.removeAll()
                    arView.scene.addAnchor(g)
                }
                
            default:
                break
            }
        }
    }
    
   
}
struct  FaceCheckingContainer: UIViewRepresentable {
    
    @Binding var faceMetre: Bool
    
    func makeUIView(context: Context) -> ARSCNView {
        let arView = ARSCNView(frame: .zero)
        return arView
    }
    
    func updateUIView(_ uiView: ARSCNView, context: Context) {
        guard ARFaceTrackingConfiguration.isSupported else {
            return
        }
        if faceMetre {}
       
        let config = ARFaceTrackingConfiguration()

        config.isWorldTrackingEnabled = false
        config.providesAudioData = false
        config.maximumNumberOfTrackedFaces =  1
        config.isLightEstimationEnabled = true
        uiView.delegate = context.coordinator
        
        uiView.session.run(config,options: [.resetTracking, .removeExistingAnchors])
    }
    
    func makeCoordinator() -> FaceCheckingContainerCoordinator {
        FaceCheckingContainerCoordinator(self)
    }
    
    class FaceCheckingContainerCoordinator: NSObject, ARSessionDelegate,ARSCNViewDelegate {
        
        
        var parent : FaceCheckingContainer
        init(_ parent: FaceCheckingContainer) {
            self.parent = parent
        }
        
        func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
            guard  let device = renderer.device  else {
                return nil
            }
            let faceGeometry = ARSCNFaceGeometry(device: device)
            let node = SCNNode(geometry: faceGeometry)
            
            if parent.faceMetre {
                //显示图片面具
                let matrial = node.geometry?.firstMaterial
                matrial?.diffuse.contents =  "face.scnassets/face.png"
                node.geometry?.firstMaterial?.fillMode = .fill
            }else {
                //显示网格
                node.geometry?.firstMaterial?.fillMode = .lines
            }
          
            return node
        }
        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)
        }
    }
    
    
}

   在代码中,首先检查设备对人脸检测的支持情况,在设备支持时运行人脸检测配置开启测功能,然后加载由 Reality Composer 配置好的虚拟模型。本示例我们在 Reality Composer 中创建场景,每一个场景使用了一个虚拟元素,为方便切换不同的虚拟元素,我们使用了滑动手势控制场实现的效果如下图所示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/348428.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++ 4种类型转换

1. 常量转换(const_cast)。》方便赋值 常量转换用于去掉表达式的const属性&#xff0c;使其变成非常量表达式&#xff0c;方便赋值。常量转换不能改变表达式的类型&#xff0c;只能改变const属性。示例代码&#xff1a; const int a 10; int b const_cast<int&>…

如何在云服务上通过docker部署服务?

如何在云服务上通过docker部署服务&#xff1f; 一、在云服务器上安装Docker1、查看云服务器的OS信息2、[安装Docker并使用&#xff08;Linux&#xff09;](https://help.aliyun.com/zh/ecs/use-cases/deploy-and-use-docker-on-alibaba-cloud-linux-2-instances) 二、通过dock…

基于 Docker 搭建 Uptime-Kuma 一个极简风的应用监控

GitHub&#xff1a;https://github.com/louislam/uptime-kuma 一、uptime-kuma 介绍 Demo&#xff1a;https://uptime.wuhanjiayou.cn/ uptime-kuma 是一款开源的监控工具, 支持 TCP / PING / HTTP 等多种监控方式&#xff0c;可监测网站&#xff0c;数据库&#xff0c;Docker…

nvm安装node 后npm不是内部或外部命令,也不是可运行的程序的处理方法

安装了两个node版本 v10.15.3代表10.15.3版本&#xff0c;打开后 v12.16.2代表node版本12.16.2,打开后 我现在使用10.15.3版本的node&#xff0c;npm可以使用&#xff0c;而当使用12.16.2版本时则不能使用 需要把文件npm, npm.cmd和node_modules三个文件拷贝到v12.16.2文件的…

从Elasticsearch来看分布式系统架构设计

从Elasticsearch来看分布式系统架构设计 - 知乎 分布式系统类型多&#xff0c;涉及面非常广&#xff0c;不同类型的系统有不同的特点&#xff0c;批量计算和实时计算就差别非常大。这篇文章中&#xff0c;重点会讨论下分布式数据系统的设计&#xff0c;比如分布式存储系统&…

论文笔记(四十二)Diff-DOPE: Differentiable Deep Object Pose Estimation

Diff-DOPE: Differentiable Deep Object Pose Estimation 文章概括摘要I. 介绍II. 相关工作III. DIFF-DOPEIV. 实验结果A. 实施细节和性能B. 准确性C. 机器人-摄像机校准 V. 结论VI. 致谢 文章概括 作者&#xff1a;Jonathan Tremblay, Bowen Wen, Valts Blukis, Balakumar Su…

抖捧AI实景自动直播怎么玩

​在如今的全民直播时代&#xff0c;直播已经成为了众多实体店、品牌方所刚需的技能&#xff0c;但是大多数都不具备太多的直播能力 &#xff0c;这个时候实景自动直播就应运而生&#xff0c;但是很多人都没有想清楚&#xff0c;AI实景自动直播&#xff0c;到底适不适合自己用呢…

Linux shell编程学习笔记42:hdparm命令

ChatGPT 和文心一言哪个更好用&#xff1f; 从智能回复、语言准确性、知识库丰富度等方面比较&#xff0c;两大AI助手哪个更胜一筹&#xff1f;快来和我们分享一下你的看法吧~ 0 前言 获取硬盘序列号是信息资产管理和信息安全检测中经常要收集的信息&#xff0c;对于Linux来说…

Java实现医院门诊预约挂号系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

RocketMQ源码阅读-八-定时消息和消息重试

RocketMQ源码阅读-八-定时消息和消息重试 定时消息概念逻辑流程图延迟级别Producer发送定时消息Broker存储定时消息Broker发送定时消息Broker 持久化定时发送进度 消息重试总结 定时消息 概念 官网给出的概念&#xff1a;https://rocketmq.apache.org/zh/docs/featureBehavior…

记录威纶通:HMI

目录 HMI 界面切换​编辑 间接窗口 HMI 界面切换 第一种&#xff1a;选择功能键-切换基本窗口-操作界面-每一个界面都创建切换按钮 第二种&#xff1a;创建一个模板界面-使用功能键按照上面的操作创建两个切换按钮 选择操作界面-点击空白出右击属性-窗口设置底层选择模板界面…

flutter 五点一点四:MaterialApp Theme 给你一堆颜色看看

ColorScheme colorScheme, // 拥有30种颜色(这个数可能过几个版本会变化吧)&#xff0c;可用于配置大多数组件的颜色。 A set of 30 colors based on the[Material spec] that can be used to configure the color properties of most components.Color canvasColor, // Mater…

LeetCode 热题 100 | 子串

目录 1 560. 和为 K 的子数组 2 239. 滑动窗口最大值 3 76. 最小覆盖子串 菜鸟做题第二周&#xff0c;语言是 C 1 560. 和为 K 的子数组 题眼&#xff1a;“子数组是数组中元素的连续非空序列。” 解决本问题的关键就在于如何翻译问题。子数组 s 的和可以看作数组 i 的…

精品基于Uniapp+ssm基于java的赈灾系统App救灾救助捐赠

《[含文档PPT源码等]精品基于Uniappssm基于java的赈灾系统App》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;Java 后台框架&#xff1a;ssm 安卓框架&#xff…

防御第三次作业-防火墙组网实验(3)

目录 实验拓扑图 要求 1 2 针对10.0.2.10设备的安全策略&#xff1a; 针对10.0.2.20设备的安全策略&#xff1a; 3 4 实验拓扑图 各设备ip和接口已配好&#xff0c;均可可ping通防火墙。 要求 1.生产区在工作时间内可以访问dmz区域&#xff0c;仅可以访问http服…

通俗易懂理解FCN全卷积网络模型

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 深度学习笔记&#xff08;二十三&#xff09;Semantic Segmentation(FCN/U-Net/PSPNet/SegNet/U-Net/ICNet/DFANet/Fast-SCNN) 二、FCN相关介绍 1. FCN简介 FCN&#xff08;Fully Convolutional Networks&…

ELK之Grafana添加钉钉告警信息

Grafana版本如下&#xff1a; [roottest data]# grafana-server -v Version 8.4.6 (commit: c53173ff6, branch: HEAD)一、新建钉钉群&#xff0c;并自定义一个机器人 点击右上角设置 ------》 智能群助手 ------》 添加机器人 ------》右侧设置按钮 ------》点击自定义&…

如何快速搭建springboot+前后端分离(vue),多商户客户端实现微信小程序+ios+app使用uniapp(一处编写,处处编译)

kxmalls外卖生鲜多商户&#xff0c;针对中小商户、企业和个人学习者开发。使用Java编码&#xff0c;采用SpringBoot、Mybatis-Plus等易用框架&#xff0c;适合个人学习研究。同时支持单机部署、集群部署&#xff0c;用户与店铺范围动态定位&#xff0c;中小商户企业可根据业务动…

【数据结构与算法】之字符串系列-20240126

这里写目录标题 一、12. 整数转罗马数字二、43. 字符串相乘三、49. 字母异位词分组四、151. 反转字符串中的单词五、179. 最大数 一、12. 整数转罗马数字 中等 罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D …

06.Elasticsearch应用(六)

Elasticsearch应用&#xff08;六&#xff09; 1.什么是分词器 ES文档的数据拆分成一个个有完整含义的关键词&#xff0c;并将关键词与文档对应&#xff0c;这样就可以通过关键词查询文档。要想正确的分词&#xff0c;需要选择合适的分词器 2.ES中的默认分词器 fingerprint…