【iOS ARKit】2D肢体动作捕捉

news2024/11/20 1:27:07

       人体肢体动作捕捉在动漫影视制作、游戏CG 动画、实时模型驱动中有着广泛的应用,利用 ARKit,无须额外的硬件设备即可实现 2D和3D人体一系列关节和骨骼的动态捕捉,由于移动AR 的便携性及低成本,必将促进相关产业的发展。

ARBody TrackingConfiguration

      ARKit 配置类 ARBodyTrackingConfiguration 专用于 2D、3D人体肢体检测捕捉,同时,该配置类也可以设置实现 2D 图像检测和平面检测,构建对现实环境的跟踪。为更真实地渲染虚拟元素,ARBodyTrackingConfiguration 还支持 HDR(High Dynamic Range Imaging,高动态范围成像)环境反射功能。其主要属性如下表所示。

属性名

描述

automaticSkeletonScaleEstimationEnabled

布尔值,指定 ARKit是否进行人体骨骼尺寸评估,设置为true 时,ARKit会根据人体距离摄像头的远近调整所驱动的模型大小,使其更匹配

isAutoFocusEnabled

设置是否自动对焦

planeDetection

在进行人体肢体检测跟踪时是否进行平面检测,可以设置为水平(horizontal)或者垂直(vertical),或者两者都设置。设置该值后就会启动平面检测功能

automaticImageScaleEstimationEnabled

自动评估检测到的2D图像的尺寸,这在设置2D 图像跟踪时有效

detectionImages

参考图像库

maximum NumberOfTrackedImages

最大可同时跟踪的2D 图像数量

wantsHDREnvironment Textures

是否使用HDR 环境纹理反射,使用后渲染的虚拟元素更真实

environment Texturing

环境纹理来源,可设置为自动(automatic)、手动(manual)、无(none)三者之一,当设置为手动时,需要提供环境纹理图

      通常,在实现人体肢体检测和人形遮挡功能时,还需要设置 frameSemantics 语义属性,使用ARBoodyTrackingConfiguration 配置类进行人体肢体检测和动作捕捉时,frameSemantics 语义属性值只能设置 bodyDetection(默认值)。frameSemantics 语义属性中 bodyDetection 用于肢体检测跟踪,后两个用于人形遮挡,personSegmentation现实屏幕空间的人形分离,而 personSegmentation WithDepth则是带有深度信息的人形分离。

2D人体姿态估计

      在ARKit 中,2D人体姿态估计是指对摄像头采集的视频图像中人像在屏幕空间中的姿态进行估计,通常使用人体骨骼关节点来描述人体姿态。近年来,随着深度学习技术的发展,人体骨骼关节点检测效率与效果不断提升,已经开始广泛应用于计算机视觉的相关领域。2D人体姿态检测估计在视频安防、动作分类、行为检测、人机交互、体育科学中有着广阔的应用前景。

人体骨骼关节点检测

     人体骨骼关节点检测(Pose Estimation)主要检测人体的一些关键节点,如关节、头部、手掌等,通过关节点描述人体骨骼及姿态信息。人体骨骼关节点检测在计算机视觉人体姿态检测相关领域的研究中起到了基础性的作用,是智能视频监控、病人监护系统、人机交互、虚拟现实、智能家居、智能安防、运动员辅助训练等应用的基础性算法。

      在实际应用中,由于人体具有相当的柔性,会出现各种姿态和形状,人体任何一个部位的微小变化都会产生一种新的姿态,同时其关节点的可见性受穿着、姿态、视角等影响非常大,而且还受到光照、遮挡等环境影响。除此之外,2D人体关节点和3D人体关节点在视觉上会有明显的差异,身体不同部位都会有视觉上的缩短效应(Fore Shortening),使得人体骨骼关节点检测成为计算机视觉领域中一个极具挑战性的课题。

使用2D 人体姿态估计

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

      2D人体姿态检测基于屏幕空间,获取的人体姿态信息没有深度值。在 ARKit 检测到屏幕空间中的人形后,可以通过 ARFrame. detectedBody 获取一个 ARBody2D 对象,也就是说 ARKit 目前对屏幕空间中的2D人体,只支持单个人形检测。ARBody2D对象描述了检测到的人形结构信息,其结构如下图所示。

     通过图可以看到,在使用 session(_ session: ARSession,didUpdate frame: ARFrame)方法获取ARFrame 中表示 2D人体的 ARBody2D 对象后,就可以使用其 skeleton. jointLandmarks 获取所有关节点位置信息,也可以通过其 skeleton. definition.jointNames 获取所有关节点名称。jointLandmarks 是一个包含所有关节点位置信息的数组,我们可以通过索引值检索某个关节点的位置,也可以通过 skeleton.landmark(for: ARSkeleton. JointName(rawValue: jointName))方法取指定关节点名称的位置信息。

     2D人体姿态估计是在屏幕空间中对摄像头采集的图像进行逐帧分析,解算出的关节点位置也是在屏幕空间中的归一化坐标,以屏幕左上角(0,0)右下角为(1,1),如下图所示。

    为了描述人体骨骼关节点,ARKit 新建了一个 ARSkeleton 类,该类包含一个人体关节点(Joint)集合及关节之间关系的定义,该类预定义了8个关节点,分别是 head、 leftFoot、 left Hand、 leftShoulder.hannnt riahtHand.rightShoulder、root.这是在应用开发中使用最多的关节点,2D和3D人体肢体都包含这些关节点,因此我们可以通过这些预定义的节点名字快速找到骨骼节点位置。ARSkeleton 类是ARSkeleton2D 和 ARSkeleton3D类的父类。

      ARSkeleton2D 类继承自 ARSkeleton,其 jointLandmarks 包含了所有2D关节点的位置信息,也可以通过该类的landmark(forJointNamed:)方法获取某个名字关节点的位置,此方法需要传递关节点的原始名称(rawValue)而不是 ARSkeleton 预定义的关节点名(预定义关节点名可以通过其.rawValue 获取原始名称)。jointLandmarks 是 simd_float2 类型数组,因此我们也可以直接通过下标获取特定的关节点位置信息,下标方法取值比使用 landmark(forJointNamed:)快得多,特别是对每帧都要执行的循环操作,可以节省很多时间。获取特定节点名称的索引值可以通过 definition.index(for:)方法实现。除此之外,还可以通过 ARSkeleton2D的isJointTracked(_:)方法查询每一个关节点在当前帧的检测跟踪情况,还可以获取每一个节点的父节点。

骨骼关节点名称

索引

父节点名称

索引

invalid

-1

head_ joint

0

neck_1 joint

1

neck_ 1_joint

1

root

16

right_ shoulder_1 _joint

2

neck_1_joint

1

right_ forearm_joint

3

right_shoulder_1_joint

2

right_hand _joint

4

right_ forearm_joint

3

left_shoulder_1_joint

5

neck_1 _joint

1

left_forearm_joint

6

5

left_hand joint

7

left forearm_joint

6

right_upLeg_joint

8

root

16

right_leg joint

9

right_upLeg_joint

8

right_foot_ joint

10

right_leg joint

9

left_ upLeg_joint

11

root

16

left_leg joint

12

left_upLeg_joint

11

left_foot joint

13

left_leg joint

12

right_eye joint

14

head _joint

0

left_eye_joint

15

head_joint

0

root

16

Invalid

-1

right_ear_joint

17

right_eye joint

14

left-ear- joint

18

left_eye_joint

15

     ARKit 2D 人体骨骼关节点定义及它们之间的关联关系如上表所示,通过表可以看到,在ARKit 中,检测到的2D人体共包含19个关节点(root 节点代表了整个 ARBody2D 对象,不计算在内时包含18个关节点),这些关节点相互之间有很强的相关性,存在紧密的父子连接关系,通过节点之间的相互关系,就可以画出各骨骼节点之间的连结图。

     下面演示利用 ARKit 检测到的2D人体骨骼关节点信息,将每一个关节点用一个圆圈标示出来,具体代码如下

//
//  BodyTrackingView.swift
//  ARKitDeamo
//
//  Created by zhaoquan du on 2024/2/1.
//

import SwiftUI
import ARKit
import RealityKit


struct BodyTrackingView: View {
    var body: some View {
        BodyTrackingViewContainer().edgesIgnoringSafeArea(.all).navigationTitle("人体骨架2D检测")
    }
}

struct BodyTrackingViewContainer: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 = uiView
        let config = ARBodyTrackingConfiguration()
        
        config.frameSemantics = .bodyDetection
        config.automaticSkeletonScaleEstimationEnabled = true
        
        uiView.session.delegate = context.coordinator
        
        uiView.session.run(config)
        
        
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    class Coordinator: NSObject,ARSessionDelegate {
        var arView : ARView? = nil
        let circleWidth: CGFloat = 10
        let circleHeight: CGFloat = 10
        var isPrinted = false
        func session(_ session: ARSession, didUpdate frame: ARFrame) {
            guard let arView = arView else {
                return
            }
            //清除骨骼圆圈
            arView.layer.sublayers?.compactMap({
                $0 as? CAShapeLayer
            }).forEach({
                $0.removeFromSuperlayer()
            })
            guard let detectedBody =  frame.detectedBody else {
                return
            }
            
            guard let orientation = arView.window?.windowScene?.interfaceOrientation else {
                return
            }
            let transform = frame.displayTransform(for: orientation, viewportSize: arView.frame.size)
            
            detectedBody.skeleton.jointLandmarks.forEach { landmark in
                let normalizeCenter = CGPoint(x: CGFloat(landmark.x), y: CGFloat(landmark.y)).applying(transform)
                let center = normalizeCenter.applying(.identity.scaledBy(x: arView.frame.width, y: arView.frame.height))
                
                let rect = CGRect(x: center.x - circleWidth/2, y: center.y - circleWidth/2, width: circleWidth, height: circleHeight)
                
                let circleLayer = CAShapeLayer()
                
                circleLayer.path = UIBezierPath(ovalIn: rect).cgPath
                
                arView.layer.addSublayer(circleLayer)
                
            }
            
            if !isPrinted {
                let jointNames = detectedBody.skeleton.definition.jointNames
                for name in jointNames {
                    let landmark = detectedBody.skeleton.landmark(for: ARSkeleton.JointName(rawValue: name))
                    let index = detectedBody.skeleton.definition.index(for: ARSkeleton.JointName(rawValue: name))
                    
                    print("\(name),\(String(describing: landmark)),the index is \(index) parent index is  \(detectedBody.skeleton.definition.parentIndices[index])")
                }
                print("last: \(ARSkeleton2D.JointName.rightShoulder.rawValue)")
                isPrinted = true
            }
            
            
        }
        
    }
    
    
    
    
}

      代码中实现的主要功能是在每一个检测到的2D人体关节点位置画一个圆圈,效果如图所示。

     ​​​​​代码很多语句都是执行画图操作,但也演示了 ARKit 2D 人体检测使用的几个重要功能:

  (1)演示了如何获取屏幕空间中的 ARBody2D对象,为确保代码在没有检测到2D人体时也能正确执行,我们使用了 guard 语句。

  (2)演示了如何获取2D 人体所有骨骼关节点名字集合,以及各关节点索引及其父节点索引。

  (3)演示了如何利用关节点名字获取该关节点在屏幕空间中的位置信息。

    如前所述,使用索引值获取特定的关节点位置信息比使用关节点名字快得多,代码清演示了利用关节点名字获取对应索引值,在实际开发中,可以直接使用前表中各关节点的索引值以提高性能。

具体代码地址:GitHub - duzhaoquan/ARkitDemo

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

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

相关文章

2024牛客寒假算法基础集训营1

文章目录 A DFS搜索M牛客老粉才知道的秘密G why外卖E 本题又主要考察了贪心B 关鸡C 按闹分配 今天的牛客,说是都是基础题,头昏昏的,感觉真不会写,只能赛后补题了 A DFS搜索 写的时候刚开始以为还是比较难的,和dfs有关…

【甲方安全建设】DevOps初体验

文章目录 前言传统的开发方式:Docker-解决环境问题 DevOps-CI/CD走向流水线Jenkins工作流程Git拉取代码Maven构建打包通过SSH连接后端服务器 实现效果 DevSecOps-安全赋能关于安全平台漏洞扫描漏洞预警TODO 前言 临近春节,笔者经过半年北漂,…

【GitHub项目推荐--一个由OpenAI提供支持的聊天机器人和虚拟助手的构建平台】【转载】

Botpress Botpress是一个开源项目,它提供了一个平台,用于构建、部署和管理基于人工智能的聊天机器人和虚拟助手 github地址: https://github.com/botpress/botpress Botpress的介绍 Botpress是一个开源项目,它提供了一个平台&…

【PaddleSpeech】语音合成-男声

环境安装 系统:Ubuntu > 16.04 源码下载 使用apt安装 build-essential sudo apt install build-essential 克隆 PaddleSpeech 仓库 # github下载 git clone https://github.com/PaddlePaddle/PaddleSpeech.git # 也可以从gitee下载 git clone https://gite…

音视频数字化(音乐CD)

上篇文章【音视频数字化(音频数字化)】我们聊了音频数字化原理,其中谈到了音乐CD,结尾也提到了一个小问题:“CD音质是最高吗?为什么?”不知道大家是怎么理解的。 其实CD质量只是“无损”存储,但是数字化标准只是“44.1kHz,16bit”,因此相对于现在,音质不能说最高。 …

day03.函数与指针(含new与delete关键字)

一.指针 #include<iostream> using namespace std; #define pi 3.14159//定义常量int main(){cout<<pi<<endl;int a10;int *pNULL;//定义指针p&a;cout<<"a的地址为&#xff1a;"<<p<<endl;cout<<"a"<&l…

【面试深度解析】快手后端一面:G1、IOC、AOP、并发、JVM生产问题定位、可重复读、ThreadLocal

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

C# 引用同一个dll不同版本的程序集

因为项目需要所以必须在项目中引用不同版本的同一程序集 我要引用的文件是newtonsoft.json.dll 两个版本为12.0.0.0 和4.0.0.0 1.如果已经先引入了newtonsoft.json 12.0.0.0版本的程序集&#xff0c;如果直接引入另一个版本的程序集的话会提示不成功&#xff0c;所以先将另一个…

C++学习Day01之双冒号作用域运算符

目录 一、程序二、输出三、分析与总结 一、程序 #include<iostream>int atk 1000; void test01() {int atk 2000;std::cout << "atk " << atk << std::endl;// ::代表作用域 如果前面什么都不添加 代表全局作用域std::cout << &q…

c++阶梯之auto关键字与范围for

auto关键字&#xff08;c11&#xff09; 1. auto关键字的诞生背景 随着程序的逐渐复杂&#xff0c;程序代码中用到的类型也越来越复杂。譬如&#xff1a; 类型难以拼写&#xff1b;含义不明确容易出错。 比如下面一段代码&#xff1a; #include <string> #include &…

Sketch使用手册:从入门到精通的完整教程

Sketch软件是Mac平台上流行的矢量图形编辑软件&#xff0c;旨在帮助用户创建网站、移动应用、图标等各种设计原型。Sketch软件的设计风格简洁明了&#xff0c;界面操作简单易用&#xff0c;非常适合UI/UX设计师、平面设计师等数字创意人员。本文将根据如何使用Sketch&#xff0…

嵌入式中C 语言中的三块技术难点

C 语言在嵌入式学习中是必备的知识&#xff0c;甚至大部分操作系统都要围绕 C 语言进行&#xff0c;而其中有三块技术难点&#xff0c;几乎是公认级别的“难啃的硬骨头”。 今天就来带你将这三块硬骨头细细拆解开来&#xff0c;一定让你看明白了。 0x01 指针 指针是公认最难理…

10. BI - 决策树的使用及可视化

本文为 「茶桁的 AI 秘籍 - BI 篇 第 10 篇」 文章目录 可视化探索决策树原理决策树算法决策树可视化泰坦尼克海难数据 Hi&#xff0c;你好。我是茶桁。 上一节课&#xff0c;咱们了解了图形的具体绘制方法&#xff0c;接下来咱们还要看看除了图形绘制之外&#xff0c;还有哪些…

Redis进阶(一):持久化

持久化 何为持久化&#xff1f; MySQL的事务有四个比较核心的特征&#xff1a;原子性、一致性、持久性和隔离性&#xff0c;这里的持久性和持久化说的是一个事&#xff0c;简单来说&#xff0c;数据存储在硬盘上就是持久&#xff0c;存在内存上那就是不持久&#xff08;重启之后…

PyTorch基础-Tensors属性、Tensor的运算

PyTorch的基本概念 Tensor的基本概念 张量高于标量、向量、矩阵 标量说零维的张量&#xff0c;向量是一维的张量&#xff0c;矩阵是二维的张量 Tensor与机器学习的关系 Tensor的创建 函数功能Tensor(*size)基础构造函数Tensor(data)类似np.arrayones(*size)全1Tensorzeros(…

举个栗子!Tableau 技巧(263):按需突出显示文本表的 N 个行

我们分享过 &#x1f330; &#xff1a;突出显示文本表的行或列&#xff0c;可以突出显示文本表中的某一行或某一列。有数据粉提出新的问题&#xff1a;如果想突出显示多行数据&#xff0c;该如何实现呢&#xff1f; 在 Tableau 中是可以实现的&#xff08;如上图&#xff09;&…

代码随想录刷题第23天

今天是二叉树的终章&#xff0c;不知不觉已经刷题23天了&#xff0c;先给自己点个赞&#x1f604;&#x1f44d; 第一题是修剪二叉搜索树&#xff0c;要注意修剪时被删除节点的子树可能符合要求&#xff0c;因此还需向下遍历。最后用root->left与right接住返回值。 迭代法先…

PHP漏洞查询

CVE - Search CVE List (mitre.org) 美国国家漏洞数据库&#xff08;需要梯子&#xff09; NATIONAL VULNERABILITY DATABASE NVD - Search and Statistics (nist.gov) 基本都能查询到&#xff0c;传结果详情页里面会有一些解决方案的连接 PHP的官方网站 PHP :: Bugs :: Se…

大模型实践笔记(1)——GLM-6B实践

目录 在Ubuntu上的配置Git Large File Storage 安装Git LFS&#xff1a; 设置Git LFS&#xff1a; 使用Git LFS&#xff1a; 安装GLM-6B 环境依赖 ChatGLM2-6B介绍 配置GLM 下载代码 构建环境 安装依赖 本地部署 网页UI 很多模型在hugging face上面&#xff0c;…

【Linux】 Linux编译器-gcc/g++使用

&#x1f497;个人主页&#x1f497; ⭐个人专栏——Linux学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读1. Linux编译器-gcc/g使用1.1 引入1.2 初识gcc/g1.3 程序运行的四个阶段1.3.1 预处理1.3.2 编译1.3.3 汇编1.3.4 链接 1.…