【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人体姿态检测一样,在ARKit 中,我们不必关心底层的人体骨骼关节点检测算法,也不必自己去调用这些算法,在运行使用 ARBodyTrackingConfiguration 配置的 ARSession 之后,基于摄像头图像的3D人体姿态估计任务也会启动…...

ROS2 CMakeLists.txt 和 package.xml
这里记录一下ROS2中功能包package.xml和CMakeLists.txt的格式。以LIO-SAM的ROS2版本为例: 一: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年,飞桨在开发套件能力基础上,充分结合大模型能力,正式在飞桨星河社区上线发布了低代码开发工具PaddleX,实现AI应用开发效果和效率的大幅提升。产品通过提供图形界面开发模式,将复杂的编程任务简化为简单易用的…...

C语言:操作符详解
创作不易,给个三连吧!! 一、算术操作符 C语言中为了方便计算,提供了算数操作符,分别是:,-,*,/,% 由于这些操作符都是有两个操作数(位于操作符两边),所以这种操作符也叫做双目操作…...
Rust 初体验2
变量类型 Rust 语言的变量数据类型,主要包括整型、浮点型、字符、布尔型、元组、数组、字符串、枚举、结构体和可变变量等。 fn main() { // 整型 let integer: i32 100; println!("整型: {}", integer); // 浮点型 let floating_point: f64 3.1…...
vue-cil的watch函数详解
在Vue中,watch是一个非常有用的API,用于侦听一个响应式引用(例如由ref创建)或响应式对象(由reactive创建)的属性,并在值变化时执行回调函数。Vue 3的Composition API引入了这种侦听方式…...
堆排及时间复杂度分析
箴言: 初始阶段,不需要去纠结那一种更优美,非要找出那一种是最好的,其实能解决问题的就是好办法。 一,常见排序时间复杂度 冒泡快排归并堆排桶排时间O(n^2)O(nlogn)O(nlogn)O(nlogn)kn空间O(1)O(1)O(nlogn)O(1)kn 二ÿ…...

数据结构:双向链表
文章目录 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. 双向带头循环链表的结构 与单链表不同的是…...

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

代码随想录算法训练营DAY16 | 二叉树 (3)
一、LeetCode 104 二叉树的最大深度 题目链接:104.二叉树的最大深度https://leetcode.cn/problems/maximum-depth-of-binary-tree/ 思路:采用后序遍历递归求解。 class Solution {int ans 0;public int maxDepth(TreeNode root) {if(root null){retur…...
springboot(ssm大学生计算机基础网络教学系统 在线课程系统Java系统
springboot(ssm大学生计算机基础网络教学系统 在线课程系统Java系统 开发语言:Java 框架:springboot(可改ssm) vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mys…...
前端架构: 脚手架的开发流程和常用框架
脚手架的开发流程 脚手架的创建 $ npm init 脚手架的开发 分包 分包是指当我们一个脚手架比较复杂的时候,不可能把所有的js代码全部写在一个脚手架当中势必会把它建很多的不同的模块 package,通常我们会把它称之为一个分包的过程会和实际的这个项目一样…...

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

mysql 对于null字段排序处理
最近遇到一个需求 ,需要对一个报表的多个字段进行多字段复杂条件排序 排序字段为NULL时 Mysql对于排序字段为NULL时,有自身默认的排序规则,默认是认为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在文本中出现的次数,也就是词频3.为了得到一个词在给定上下文中出现的概率,我们可以利用条件概率公式计算。具体来讲,就是计算给定前N-1个词时࿰…...

mac电脑flutter环境配置,解决疑难问题
准备工作 首先搭建flutter的环境需要使用到flutter的sdk,可以直接跳去官网下载:Choose your first type of app - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter,下载时要注意你电脑所使用的芯片是Intel的还是苹果的芯片。 下载好的…...
C++ bool 布尔类型
在C 中 bool类型占用1个字节长度,bool 类型只有两个取值,true 和 false,true 表示“真”,false 表示“假”。 需要注意的C中使用cout 打印的时候是没有true 和 false 的 只有0和1 ,这里0表示假,非0表示真 …...

DC-7靶机渗透详细流程
信息收集: 1.存活扫描: 由于靶机和kali都是nat的网卡,都在一个网段,我们用arp-scan会快一点: 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:数据库性能加速策略全解析 引言理解MySQL性能指标监控和评估性能指标索引优化技巧索引优化实战案例 查询优化实战查询优化案例分析 存储引擎优化InnoDB vs MyISAM选择和优化存储引擎存储引擎优化实例 配置调整与系统优化配置调整系统优化优化实例 实战案例…...

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

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...

高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...

Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...

高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...