首页 > 其他分享 >iOS ARKit --人脸跟踪之挂载虚拟元素

iOS ARKit --人脸跟踪之挂载虚拟元素

时间:2024-01-25 17:00:58浏览次数:33  
标签:func -- iOS scene ARKit let arView 人脸 config

        人脸跟踪(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,并创建一个锚定到人脸的工程。

 (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 中创建场景,每一个场景使用了一个虚拟元素,为方便切换不同的虚拟元素,我们使用了滑动手势控制场实现的效果如下图所示。

 

具体代码地址:https://github.com/duzhaoquan/ARkitDemo.git

标签:func,--,iOS,scene,ARKit,let,arView,人脸,config
From: https://www.cnblogs.com/duzhaoquan/p/17987549

相关文章

  • 一些C++相关的网站
     https://cppinsights.io/ cppinsights.io是一个在线C++代码查看工具,它可以帮助你深入了解C++代码在编译器层面的实际情况。该工具的主要功能是展示C++代码的编译器输出,即展示编译器对代码进行优化、展开模板、内联函数等操作后的实际代码。 https://zh.cpprefere......
  • websocket 包括心跳机制
    https://zhuanlan.zhihu.com/p/6643681141.创建websocket对象,初始化创建对象,开启、onmsg、关闭开启,开始心跳检测发送传递信息(ifwebsocket对象存在,且处于open状态onmsg,接收服务器传送过来的信息,进行业务操作关闭,销毁心跳检测,清除心跳......
  • Spring的事务使用教程
    什么是事务?事务(Transaction)是数据库操作最基本单元,逻辑上一组操作,要么都成功,要么都失败,如果操作之间有一个失败所有操作都失败。事务四个特性(ACID)原子性一组操作要么都成功,要么都失败。一致性一组数据从事务1合法状态转为事务2的另一种合法状态,就是一致。隔离性事务1......
  • 15. 三数之和(中)
    目录题目题解:排序+双指针题目给你一个整数数组nums,判断是否存在三元组[nums[i],nums[j],nums[k]]满足i!=j、i!=k且j!=k,同时还满足nums[i]+nums[j]+nums[k]==0。请你返回所有和为0且不重复的三元组。注意:答案中不可以包含重复的三元组。示例1:......
  • JAVA调用Python脚本执行
    SpringBoot-web环境<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>springboot--启动类@SpringBootApplication(ex......
  • C# Json序列化方案选择
    在C#中,进行JSON序列化和反序列化有多种方案可供选择,常用的是下面俩个System.Text.Json:这是.NETCore和.NET5中内置的JSON序列化和反序列化库,提供了高性能和低内存消耗的JSON处理能力。Newtonsoft.Json:这是一个流行的第三方JSON处理库,广泛用于Framework中的JSON序列化和反序列化......
  • R:PCA(第二版)
    rm(list=ls())library(vegan)library(tidyverse)library(ggalt)library(car)library(ggforce)library(ggpubr)library(patchwork)#2.定义所需的函数。pairwise.adonis1<-function(x,factors,p.adjust.m){#定义了一个名为pairwise.adonis1的函数,该函数......
  • 27派生类的继承过程
    派生类的继承过程派生类如何初始化从基类继承来的成员变量?派生类继承所有可继承的成员变量和方法,除了构造和析构函数通过调用基类的构造函数初始化继承来的成员变量,调用基类的析构函数释放继承来的成员变量通过调用派生类的构造函数初始化新的成员变量,调用派生类的析构函数释......
  • 28重载-隐藏-覆盖
    重载,隐藏,覆盖重载:一组函数要重载,必须处在同一作用域下,而且函数名字相同,参数列表不同。隐藏:继承结构中,派生类的同名成员,将基类的同名成员给隐藏调用了覆盖:派生类中,重写了基类中的虚函数,称为覆盖classBase{public: Base(intdata=10):ma(data){} voidshow(){cout......
  • 29虚函数-静态绑定-动态绑定
    虚函数-静态绑定-动态绑定如果类中定义了虚函数,那么编译阶段,编译器会给这个类类型产生一个唯一的vftable虚函数表,其中主要存储的是RTTI指针和虚函数的地址。程序运行时,每一张虚函数表都会加载到内存的.rodata只读数据区。一个类中定义了虚函数,那么这个类的对象,其运行时,内存中开......