【iOS ARKit】3D人体姿态估计实例

      与2D人体姿态检测一样,在ARKit 中,我们不必关心底层的人体骨骼关节点检测算法,也不必自己去调用这些算法,在运行使用 ARBodyTrackingConfiguration 配置的 ARSession 之后,基于摄像头图像的3D人体姿态估计任务也会启动,我们可以通过 session(_ session: ARSession, didUpdate anchors:[ARAnchor])代理方法直接获取检测到的ARBodyAnchor。

     在 ARKit 中,与检测2D图像或者 3D物体一样,在检测到3D 人体后会生成一个ARBodyAnchor 用于在现实世界和虚拟空间之间建立关联关系,绑定虚拟元素到检测的人体上。在获取 ARBodyAnchor 后,就可以通过 ARBodyAnchor. skeleton. definition. jointNames 获取所有3D人体骨骼关节点名称,通过ARBodyAnchor. skeleton. modelTransform(for:)方法取指定关节点相对 ARBodyAnchor 的位置姿态信息,通过 ARBodyAnchor. skeleton. localTransform(for: ARSkeleton. JointName)方法获取指定关节相对于其父节点的位置姿态信息。示例代码如下代码所示。

 func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {guard let anchor = anchors.first as? ARBodyAnchor else {return}if !isPrinted {isPrinted = true//获取root节点在世界坐标系中的姿态let hipWordPosition = anchor.transformprint("root transform: \(hipWordPosition)")//获取3d骨骼对象let skeleton = anchor.skeleton//获取相对于root节点所有节点的姿态信息数组let jointTranforms = skeleton.jointModelTransforms//获取在世界空间坐标系中所有节点的姿态信息数组let localTransform = skeleton.jointLocalTransforms//遍历姿态信息数字,通过下标遍历for (i, jointTransform) in jointTranforms.enumerated() {let name = anchor.skeleton.definition.jointNames[i]let parentIndex = skeleton.definition.parentIndices[i]guard parentIndex != -1 else {continue}let parentJointTransform = jointTranforms[parentIndex]let parentName = anchor.skeleton.definition.jointNames[parentIndex]print("name: \(name),index: \(i), transform: \(String(describing: jointTransform)), parent name: \(parentName),parent index: \(parentIndex) parent transform: \(String(describing: parentJointTransform))")}//通过名字遍历let jointNames = anchor.skeleton.definition.jointNamesfor name in jointNames {let landmark = anchor.skeleton.modelTransform(for: ARSkeleton.JointName(rawValue: name))let index = anchor.skeleton.definition.index(for: ARSkeleton.JointName(rawValue: name))print("\(name),\(String(describing: landmark)),the index is \(index) parent index is  \(anchor.skeleton.definition.parentIndices[index])")}}}

     代码演示了如何获取 ARKit 生成的 ARBodyAnchor;如何获取3D人体所有骨骼关节点名字集合,以及各关节点及其父节点索引;如何利用关节点名字获取该关节点相对 ARBodyAnchor 的位置信息。捕捉人体3D 姿态信息后除了进行运动姿态分析最重要的用途就是驱动3D 模型,在理解ARKit 提供的3D人体骨骼关节点数据结构信息及关联关系之后,我们就可以利用这些数据实时驱动三维模型,基本思路如下:

    (1)建立一个与关节点表一致,拥有相同人体骨骼关节点的三维模型。

    (2) 开启 3D人体姿态估计功能。

    (3)建立 ARKit 3D 人体姿态估计骨骼关节点与三维模型骨骼关节点的对应关系,并利用3D人体姿态估计骨骼关节点数据驱动三维模型骨骼关节点。

     如前文所述,我们可以从生成的 ARBodyAnchor 中获取所有骨骼关节点的位置信息,利用这些位息,就可以将模型关节点与检测到的人体骨骼关节点关联起来。为了简单起见,下面我们演示利用检的人体 ARBodyAnchor,在人眼处绘制两个球体。代码如下所示。

 func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {guard let anchor = anchors.first as? ARBodyAnchor else {return}let bodyPosition = simd_make_float3(anchor.transform.columns.3) //位置平移信息robotAnchor.position = bodyPosition + robotOffsetrobotAnchor.orientation = Transform(matrix: anchor.transform).rotationif let robotCharacter = robotCharacter,robotCharacter.parent == nil {robotAnchor.addChild(robotCharacter)}//更新眼睛小球位置,guard let leftMatrix = anchor.skeleton.modelTransform(for: ARSkeleton.JointName(rawValue: "left_eye_joint")),let rightMatrix = anchor.skeleton.modelTransform(for: ARSkeleton.JointName(rawValue: "right_eye_joint")) else {return}leftEye.position = simd_make_float3(  leftMatrix.columns.3)rightEye.position = simd_make_float3(rightMatrix.columns.3)//跟节点的位置付值给anchoreyeAnchor.position = simd_make_float3(anchor.transform.columns.3)}

      在代码中,我们首先创建了两个球体,代表人体的左右两只眼睛,然后在 session (: didUipdateanchors:)方法中检查 ARBodyAnchor,利用检测到的3D人体骨骼左右眼关节点(left_eye_joint 和 righ.eye_joint)信息设置并实时更新两个球体的位置及方向。需要注意的是,在实际使用人体骨骼关节点位置信息时,通过 modelTransform(for:)方法获取的关节点位置是相对于 ARBodyAnchor的位置,并不是世界坐标空间中的坐标。在上述代码中,获取某特定关节点位置信息我们使用了 modelTransform(for:)方法,通过关节点名字获取该关节点位置数据,因为关节点的位置数据存储在数组中,使用bodyAnchor.skeleton.jointModelTransforms[index]的方式效率更高,如左眼索引为54,直接将 54作为参数传递即可以获取人体左眼位置数据。上节表列出了所有91 个骨骼关节点的索引值,可以直接使用。运行该示例,在ARKit 检测到人体时,会在人体双眼处放置两个球体,效果如图所示。

      采用同样的方法,可以将获取的所有人体3D骨骼关节点数据绑定到3D模型中的骨骼关节点上,并以此来驱动3D模型的运动,这是以手工的方式绑定检测到的骨骼关节点与模型。在 RealityKit 中,使用了一个名为 BodyTrackedEntity 的实体类描述带骨骼绑定的人体模型,如果模型骨骼关节点命名与相互之间的关系与上节表所示一致,也可以直接通过使用 Body TrackedEntity.joint Transforms [3] = Transform (matrix: body Anchor. skeleton. model Transtorm (for: ARSkeleton. JointName.head)!)语句将检测到的人体关节点位置信息赋给人体模型,从而达到驱动模型的目的。

      ARKit检测到的3D人体骨骼关节点有91个,采用人工绑定骨骼关节点的工作量很大且很容易出错,为此,RealityKit 会自动检测场景中加载的 BodyTrackedEntity 实体对象,并尝试自动执行将检测到的人体骨骼关节点与模型骨骼关节点匹配,如果模型骨骼关节点命名和相互之间的关系与表7-3所示一致,则无须人工手动绑定,RealityKit会自动进行关节点绑定。因此,在模型骨骼完全符合要求的情况下,利用ARKit检测到的3D人体关节点驱动模型变得格外简单,只需要加载模型为 BodyTrackedEntity 实体对象,并添加到 AnchorEntity 中。代码如下所示。

//
//  BodyTracking3DView.swift
//  ARKitDeamo
//
//  Created by zhaoquan du on 2024/2/1.
//import SwiftUIimport SwiftUI
import ARKit
import RealityKit
import Combinestruct BodyTracking3DView: View {var body: some View {BodyTracking3DViewContainer().edgesIgnoringSafeArea(.all).navigationTitle("人体骨架3D检测")}
}struct BodyTracking3DViewContainer:UIViewRepresentable {func makeUIView(context: Context) ->ARView {let arView = ARView(frame: .zero)return arView}func updateUIView(_ uiView: UIViewType, context: Context) {guard ARBodyTrackingConfiguration.isSupported else {return}context.coordinator.arView = uiViewlet config = ARBodyTrackingConfiguration()config.frameSemantics = .bodyDetectionconfig.automaticSkeletonScaleEstimationEnabled = trueuiView.session.delegate = context.coordinatoruiView.session.run(config)}func makeCoordinator() -> Coordinator {Coordinator()}class Coordinator: NSObject,ARSessionDelegate {var arView : ARView? = nilvar isPrinted = falsevar robotCharacter: BodyTrackedEntity?let robotOffset: SIMD3<Float> = [-0.1, 0, 0]let robotAnchor = AnchorEntity()func loadRobot(){var cancellable: AnyCancellable? = nilcancellable = Entity.loadBodyTrackedAsync(named: "robot.usdz").sink { completion inif case let .failure(error) = completion {print("无法加载模型,错误:\(error.localizedDescription)")}cancellable?.cancel()} receiveValue: { body inbody.scale = [1.0,1.0,1.0]self.robotCharacter = bodyself.arView?.scene.addAnchor(self.robotAnchor)cancellable?.cancel()}}func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {guard let anchor = anchors.first as? ARBodyAnchor else {return}
//            createSphere()loadRobot()}func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {guard let anchor = anchors.first as? ARBodyAnchor else {return}let bodyPosition = simd_make_float3(anchor.transform.columns.3) //位置平移信息robotAnchor.position = bodyPosition + robotOffsetrobotAnchor.orientation = Transform(matrix: anchor.transform).rotationif let robotCharacter = robotCharacter,robotCharacter.parent == nil {robotAnchor.addChild(robotCharacter)}}}}

      在代码中,我们首先使用异步的方式加载3D人体模型,并对模型中的骨骼信息进行检查,如果模型骨骼都符合要求则生成可供驱动的3D 模型对象,然后在 session(:didUpdate anchors:)方法中实时更新模型的姿态信息。上述代码对 robotAnchor 位置进行了偏移处理,这是因为我们获取的ARBodyAnchor 所在位置为检测到的3D人体关节点的Root 位置,如果不进行偏移,则模型与人体会重合显示,代码中我们将模型向X轴负方向移动了 1m(ARBodyAnchor 位置三维空间中的位置,可以向任何方向偏移),我们也可以不加这个偏移。编译运行代码,将设备摄像头对准真实人体,在检测到人体时,加载一个机器人,并且人体姿态可以实时驱动机器人模型同步运动,效果如下图所示。

       

       经过测试,目前 ARKit 可以正确检测追踪人体正面或背面站立姿态,对坐姿也能比较好地跟踪,但不能检测跟踪倒立、俯卧姿态。并且我们在测试中发现,实时跟踪一个真实人体与跟踪显示器上视频中的人体跟踪精度似乎没有区别,使用iPad Pro 与iPhone 跟踪精度也似乎没有区别。

      在人体尺寸估计方面,使用纯图像处理时,虚拟模型有时会出现跳跃或者突然改变大小的现象。在配备了 LiDAR 传感器的设备上,由于可以直接从 LiDAR 传感器中采集到人体深度信息,因此在人体尺寸估计方面有很大提升,相比使用纯图像方式,估计的尺寸精度更高,对虚拟模型的大小控制更合理。

      从本节与2D检测实例可以看到,在运行 ARSession 进行人体检测跟踪时,将 ARBody TrackingConfiguration.frameSemantics 设置为 bodyDetection(即默认值),既可以检测2D人体骨骼关节点,也可以检测3D人体骨骼关节点,区别是检测的2D 人体骨骼关节点是在屏幕空间中,而检测的3D人体骨骼关节点是在世界空间中,因此,我们一般会在 session(:didUpdate frame:)代理方法中处理2D人体检测,在 session(:didUpdate 提示也可以在 session(:didUpdate anchors:)代理方法中处理2D人体检测,在使用 session(:didUpdateanchors:)方法处理2D人体检测时,由于获取的ARBodyAnchor 是在世界空间中,因此需要按照 3D人体检测的步骤进行处理。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python中的HTTP代理与网络安全

在当今数字化的世界里&#xff0c;网络安全已经成为我们无法忽视的重要议题。无数的信息在网络上传递&#xff0c;而我们的隐私和敏感数据也在这个过程中可能面临被窃取或滥用的风险。在Python编程中&#xff0c;HTTP代理作为一种工具&#xff0c;能够在网络安全方面发挥重要的…

docker重建镜像

DockerFile如下&#xff1a; FROM k8s-registry.qhtx.local/base/centos7-jdk8-haitong0704RUN yum -y update && yum install -y python3-devel && yum install -y python36 RUN mv /usr/bin/python /usr/bin/python_old RUN ln -s /usr/bin/python3 /usr/bi…

虹科技术|一文详解IO-Link Wireless技术如何影响工业无线自动化

导读&#xff1a;在工业无线自动化的飞速发展进程中&#xff0c;IO-Link Wireless技术成为了一项具有颠覆性的创新。它将IO-Link协议与无线连接完美结合&#xff0c;解决了传统通信技术在工业应用中的痛点。本文将深入解析IO-Link Wireless技术的原理、应用领域、优势以及实际案…

小华和小为的聚餐地点 - 华为OD统一考试

OD统一考试(C卷) 分值: 200分 题解: Java / Python / C++ 题目描述 小华和小为是很要好的朋友,他们约定周末一起吃饭。 通过手机交流,他们在地图上选择了多个聚餐地点(由于自然地形等原因,部分聚餐地点不可达)。 求小华和小为都能到达的聚餐地点有多少个? 输入描述…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人

专属领域论文订阅 关注{晓理紫|小李子}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 为了答谢各位网友的支持&#xff0c;从今日起…

WINDOWS搭建NFS服务器

下载并安装 Networking Software for Windows 启动配置 找到安装目录&#xff08;如C:\Program Files\nfsd&#xff09;&#xff0c;双击nfsctl.exe&#xff0c;菜单Edit->Preferences 启动后&#xff1a; 配置Export Exports->Edit exports file 其他的几句我都删除…

XML:可扩展标记语言

XML&#xff1a;可扩展标记语言 主要内容 XML介绍DTDXSDDOM解析SAX解析 学习目标 知识点要求XML介绍掌握DTD掌握XSD掌握DOM解析掌握SAX解析掌握 一、XML介绍 1. 简介 XML&#xff08;Extensible Markup Language&#xff09;可扩展标记语言。严格区分大小写。 2. XML和…

react将选中本文自动滑动到容器可视区域内

// 自动滚动到可视区域内useEffect(() > {const target ref;const wrapper wrapperRef?.current;if (target && wrapperRef) {const rect target.getBoundingClientRect();const wrapperRect wrapper.getBoundingClientRect();const isVisible rect.bottom &l…

RPM与YUM

RPM RPM是Red Hat Package Manager的缩写&#xff0c;它是一种用于安装、卸载、升级和管理RPM包的工具。RPM使用一种数据库记录的方式来将软件安装到Linux系统&#xff0c;可以自动解决依赖性问题&#xff0c;并且提供了查询和校验等功能。 以下是使用rpm的基本操作&#xff…

python Flask 写一个简易的 web 端程序(附demo)

python Flask 写一个简易的 web 端程序 &#xff08;附demo&#xff09; 介绍简单介绍装饰器 app.route("/") 进阶增加接口设置端口 静态网页核心代码完整代码 介绍 Flask 是一个用于构建 Web 应用程序的轻量级 Python Web 框架。它设计简单、易于学习和使用&#x…

[N-139]基于springboot,vue宠物领养系统

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vue3element-plus 服务端技术&#xff1a;springbootmybatis-plusr…

Vue-easy-tree封装及使用

1.使用及安装 下载依赖 npm install wchbrad/vue-easy-tree引入俩种方案 1.在main.js中引入 import VueEasyTree from "wchbrad/vue-easy-tree"; import "wchbrad/vue-easy-tree/src/assets/index.scss" Vue.use(VueEasyTree)2.当前页面引入 import VueEa…

PAT-Apat甲级题1007(python和c++实现)

PTA | 1007 Maximum Subsequence Sum 1007 Maximum Subsequence Sum 作者 CHEN, Yue 单位 浙江大学 Given a sequence of K integers { N1​, N2​, ..., NK​ }. A continuous subsequence is defined to be { Ni​, Ni1​, ..., Nj​ } where 1≤i≤j≤K. The Maximum Su…

2024年第三届能源与环境工程国际会议(CFEEE 2024) | Ei&Scopus双检索

会议简介 Brief Introduction 2024年第三届能源与环境工程国际会议(CFEEE 2024) 会议时间&#xff1a;2024年12月12日-14日 召开地点&#xff1a;澳大利亚凯恩斯 大会官网&#xff1a;CFEEE 2024-2024 International Conference on Frontiers of Energy and Environment Engine…

day42_jdbc

今日内容 0 复习昨日 1 JDBC概述 2 JDBC开发步骤 3 完成增删改操作 4 ResultSet 5 登录案例 0 复习昨日 1 写出JQuery,通过获得id获得dom,并给input输入框赋值的语句 $(“#id”).val(“值”) 2 mysql内连接和外连接的区别 内连接只会保留完全符合关联条件的数据 外连接会保留表…

antv/x6节点添加鼠标悬浮高亮和删除功能

antv/x6节点添加鼠标悬浮高亮和删除功能 效果鼠标悬浮高亮鼠标移出恢复原状态 效果 鼠标悬浮高亮 this.graph.on(node:mouseenter, ({ node }) > {node.addTools({name: button-remove,args: {x: 100%,y: 0,offset: { x: 0, y: 0 },},})})鼠标移出恢复原状态 this.graph.on(…

如何实现冻干机和产品全生命周期的验证和监测?

为什么冻干需要工艺优化和合规性 冻干是制药和生物技术产品的关键工艺&#xff0c;需要精确控制关键的温度和压力参数。通过遵守 GMP 和 FDA 合规性等监管准则&#xff0c;您可以生产出更高质量的产品&#xff0c;避免不必要的浪费&#xff0c;并缩短产品上市时间。 要想在冻干…

Java on Azure Tooling 2024年1月更新|Azure Key Vault 支持、示例项目创建支持及更多

作者&#xff1a;Jialuo Gan - Program Manager, Developer Division At Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到 2024 年 Java on Azure 工具的首次更新。在本次更新中&#xff0c;我们将介绍对于 Azure Key Vault 支持、基于 Azure 示例项目的创建支…

【面试】冲刺春招!每天三十道面试题——Java基础篇(一)

目录 一 JDK 和 JRE 的区分 二 简述编码的作用以及记事本的实现原理 三 基本类型有哪些&#xff1f;分别占据多少空间&#xff1f; 四 java中布尔类型的空间大小是怎么定下来的&#xff1f;为什么不是1bit&#xff0c; 把考虑因素说一下 五 int类型和float类型哪一个精度更…

nohost本地部署

1、安装node Node.js 官方网站下载&#xff1a;https://nodejs.org/en/download/ 2、安装whistle 安装命令为 npm install -g whistle 或 npm install -g cnpm --registryhttps://registry.npm.taobao.org 后&#xff0c;使用 cnpm install -g whistle 来安装 3、插件修改 官方…