当前位置: 首页 > news >正文

【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

相关文章:

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

与2D人体姿态检测一样&#xff0c;在ARKit 中&#xff0c;我们不必关心底层的人体骨骼关节点检测算法&#xff0c;也不必自己去调用这些算法&#xff0c;在运行使用 ARBodyTrackingConfiguration 配置的 ARSession 之后&#xff0c;基于摄像头图像的3D人体姿态估计任务也会启动…...

ROS2 CMakeLists.txt 和 package.xml

这里记录一下ROS2中功能包package.xml和CMakeLists.txt的格式。以LIO-SAM的ROS2版本为例&#xff1a; 一&#xff1a;CMakeLists.txt cmake_minimum_required(VERSION 3.5) project(lio_sam)if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)set(CMAKE_BUILD_TYPE…...

代码献瑞,算力有礼!低代码开发工具PaddleX特色产线新春福利来啦

回望2023年&#xff0c;飞桨在开发套件能力基础上&#xff0c;充分结合大模型能力&#xff0c;正式在飞桨星河社区上线发布了低代码开发工具PaddleX&#xff0c;实现AI应用开发效果和效率的大幅提升。产品通过提供图形界面开发模式&#xff0c;将复杂的编程任务简化为简单易用的…...

C语言:操作符详解

创作不易&#xff0c;给个三连吧&#xff01;&#xff01; 一、算术操作符 C语言中为了方便计算&#xff0c;提供了算数操作符&#xff0c;分别是:,-,*,/,% 由于这些操作符都是有两个操作数&#xff08;位于操作符两边&#xff09;&#xff0c;所以这种操作符也叫做双目操作…...

Rust 初体验2

变量类型 Rust 语言的变量数据类型&#xff0c;主要包括整型、浮点型、字符、布尔型、元组、数组、字符串、枚举、结构体和可变变量等。 fn main() { // 整型 let integer: i32 100; println!("整型: {}", integer); // 浮点型 let floating_point: f64 3.1…...

vue-cil的watch函数详解

在Vue中&#xff0c;watch是一个非常有用的API&#xff0c;用于侦听一个响应式引用&#xff08;例如由ref创建&#xff09;或响应式对象&#xff08;由reactive创建&#xff09;的属性&#xff0c;并在值变化时执行回调函数。Vue 3的Composition API引入了这种侦听方式&#xf…...

堆排及时间复杂度分析

箴言: 初始阶段&#xff0c;不需要去纠结那一种更优美&#xff0c;非要找出那一种是最好的&#xff0c;其实能解决问题的就是好办法。 一&#xff0c;常见排序时间复杂度 冒泡快排归并堆排桶排时间O(n^2)O(nlogn)O(nlogn)O(nlogn)kn空间O(1)O(1)O(nlogn)O(1)kn 二&#xff…...

数据结构:双向链表

文章目录 1. 双向带头循环链表的结构2. 相关操作2.1 创建节点2.2 尾插2.3 头插2.4 打印2.5 尾删2.6 头删2.7 查找2.8 指定位置前/后插入2.9 删除指定位置的节点2.10 删除指定位置后的节点2.11 销毁链表 3.顺序表与链表区别 1. 双向带头循环链表的结构 与单链表不同的是&#xf…...

51单片机之数码管显示表白数字篇

朝菌不知晦朔 蟪蛄不知春秋 眼界决定境界 CSDN 请求进入专栏 是否进入《51单片机专栏》? 确定 目录 数码管的简介 数码管引脚定义 数码管的原理图 74HC245 代码实现 静态数码管的显示 动态数码管的显示 数码管实现表白画面 数码管的简介 L…...

代码随想录算法训练营DAY16 | 二叉树 (3)

一、LeetCode 104 二叉树的最大深度 题目链接&#xff1a;104.二叉树的最大深度https://leetcode.cn/problems/maximum-depth-of-binary-tree/ 思路&#xff1a;采用后序遍历递归求解。 class Solution {int ans 0;public int maxDepth(TreeNode root) {if(root null){retur…...

springboot(ssm大学生计算机基础网络教学系统 在线课程系统Java系统

springboot(ssm大学生计算机基础网络教学系统 在线课程系统Java系统 开发语言&#xff1a;Java 框架&#xff1a;springboot&#xff08;可改ssm&#xff09; vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mys…...

前端架构: 脚手架的开发流程和常用框架

脚手架的开发流程 脚手架的创建 $ npm init 脚手架的开发 分包 分包是指当我们一个脚手架比较复杂的时候&#xff0c;不可能把所有的js代码全部写在一个脚手架当中势必会把它建很多的不同的模块 package&#xff0c;通常我们会把它称之为一个分包的过程会和实际的这个项目一样…...

3.0 Hadoop 概念

本章着重介绍 Hadoop 中的概念和组成部分&#xff0c;属于理论章节。如果你比较着急可以跳过。但作者不建议跳过&#xff0c;因为它与后面的章节息息相关。 Hadoop 整体设计 Hadoop 框架是用于计算机集群大数据处理的框架&#xff0c;所以它必须是一个可以部署在多台计算机上…...

mysql 对于null字段排序处理

最近遇到一个需求 &#xff0c;需要对一个报表的多个字段进行多字段复杂条件排序 排序字段为NULL时 Mysql对于排序字段为NULL时&#xff0c;有自身默认的排序规则&#xff0c;默认是认为null 值 是无穷小 ELECT id,script_id,last_modified,live_count,next_show FROM virtua…...

NLP_语言模型的雏形 N-Gram 模型

文章目录 N-Gram 模型1.将给定的文本分割成连续的N个词的组合(N-Gram)2.统计每个N-Gram在文本中出现的次数&#xff0c;也就是词频3.为了得到一个词在给定上下文中出现的概率&#xff0c;我们可以利用条件概率公式计算。具体来讲&#xff0c;就是计算给定前N-1个词时&#xff0…...

mac电脑flutter环境配置,解决疑难问题

准备工作 首先搭建flutter的环境需要使用到flutter的sdk&#xff0c;可以直接跳去官网下载&#xff1a;Choose your first type of app - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter&#xff0c;下载时要注意你电脑所使用的芯片是Intel的还是苹果的芯片。 下载好的…...

C++ bool 布尔类型

在C 中 bool类型占用1个字节长度&#xff0c;bool 类型只有两个取值&#xff0c;true 和 false&#xff0c;true 表示“真”&#xff0c;false 表示“假”。 需要注意的C中使用cout 打印的时候是没有true 和 false 的 只有0和1 &#xff0c;这里0表示假&#xff0c;非0表示真 …...

DC-7靶机渗透详细流程

信息收集&#xff1a; 1.存活扫描&#xff1a; 由于靶机和kali都是nat的网卡&#xff0c;都在一个网段&#xff0c;我们用arp-scan会快一点&#xff1a; arp-scan arp-scan -I eth0 -l └─# arp-scan -I eth0 -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:dd:ee:6…...

提速MySQL:数据库性能加速策略全解析

提速MySQL&#xff1a;数据库性能加速策略全解析 引言理解MySQL性能指标监控和评估性能指标索引优化技巧索引优化实战案例 查询优化实战查询优化案例分析 存储引擎优化InnoDB vs MyISAM选择和优化存储引擎存储引擎优化实例 配置调整与系统优化配置调整系统优化优化实例 实战案例…...

Flink实战六_直播礼物统计

接上文&#xff1a;Flink实战五_状态机制 1、需求背景 现在网络直播平台非常火爆&#xff0c;在斗鱼这样的网络直播间&#xff0c;经常可以看到这样的总榜排名&#xff0c;体现了主播的人气值。 人气值计算规则&#xff1a;用户发送1条弹幕互动&#xff0c;赠送1个荧光棒免费…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...