KINECT内幕——解析SDK(MS SDK 2)

Wesley13
• 阅读 465

NUI图像数据流概述

NUI的流数据是通过连续静态图像序列传递的。在上下文初始化阶段,应用程序将识别需要读取的流数据,并对其进行附加的流相关设置,包括数据解析度、图像类型、用于存储输入帧的缓冲区数量等内容。在应用程序检索并释放相关帧之前,如果运行时数据占满了缓冲区,那么系统将自动丢弃最旧的帧并重用缓冲区,也就是说,帧数据是可被丢弃的。同时系统最多允许请求四个缓冲区,而在大多数应用情形下通常只需要其中的两个。应用程序可通过API获取如下类型的图像信息:彩色图像数据、深度图像数据、用户分割数据等。下面将分别对上述三种类型的数据进行一些说明。

彩色图像数据:系统提供两种格式的彩色图像数据,包括32位X8R8G8B8格式的sRGB位图数据和16位的UYVY格式的YUV位图数据。由于两者实际来自同一图像数据,因此两种格式的最终图像实际上没有任何差别。不过后者要求图像保持640×480的分辨率和15FPS的帧率,同时内存需求更小。应注意,由于系统采用USB连接,传感器层会首先将1280×1024分辨率的Bayer彩色滤波马赛克图像压缩并转换为RGB格式传输,运行时系统再对该数据进行解压缩操作。上述特性可以保证数据帧率可达30FPS,但解码操作会损失一定的图像精度。

深度图像数据:深度图像帧中的每一个像素点都表示从摄像头所在平面到视野内最近物体的笛卡尔坐标系距离,单位为毫米。系统目前支持630×480、320×240、80×60三种规格的深度图像帧。应用程序可根据深度图像数据跟踪人物动作、识别并忽略背景物体。深度图像数据含有两种格式,其一是唯一表示深度值:那么像素的低12位表示一个深度值,高4位未使用;其二是既表示深度值又含有人物序号,则低三位保存人物序号,其余数据位表示深度值。应注意如果获取的深度值为0,则说明物体距离摄像头过近或过远以致超出了设备规格。

用户分割数据:SDK beta目前支持读取两个用户的分割映射数据,其数据帧的相关像素分别记录了用户的序号。尽管用户分割数据是独立生成的数据流,在实际应用中仍可以将深度数据和用户分割数据整合成一个帧,其中像素值的高13位保存了深度值,低三位保存用户序号,其中序号为0则表示无用户,1和2分别表示两个不同的用户。在实际应用中,应用程序往往利用用户分割数据在深度图像和原始彩色图像中获取ROI感兴趣信息。

基本编程模型

基于NUI API的编程模型本质上就是获取传感器图像数据的过程。应用程序往往通过相关代码首先将图像的最后一帧读入至缓冲区中,如果该帧已预备好,那么其将进入缓冲区,如果帧数据尚未就绪,代码仍可选择是否继续挂起等待或暂时释放并稍后重试。NUI摄像头API绝对不会多次传递相同的图像数据。框架包含的基本编程模型如下:

1、POLLing模型,该模型较为基础易用。首先应开启图像数据流,然后请求并设置等待下一帧的时间,范围允许从0到无穷大,单位为毫秒;如果帧数据尚未就绪,则系统将等待刚才指定的时间然后返回。如果帧数据成功返回,则应用程序可请求下一帧数据并在同一线程执行其它操作。通常一个C++应用程序应调用NuiImageStreamOpen函数首先启动一个彩色或深度数据流,并忽略可选事件。托管代码则需调用ImageStream.Open方法。请求彩色或深度图像帧的C++函数为NuiImageStreamGetNextFrame,C#为ImageStream.GetNextFrame方法。

2、事件模型,事件模型允许将获取骨骼帧的功能精确、灵活地集成入应用程序引擎。在该模型中,C++程序首先调用NuiImageStreamOpen函数并传入一个事件句柄。每当一个新的图像帧数据可用时,事件信号将被触发。任何相关的等待线程将被唤醒并通过调用NuiImageGetNextFrame函数获取骨骼信息,与此同时事件将被系统重置。托管代码应绑定Runtime.DepthFrameReady和Runtime.ImageFrameReady事件到相关的处理函数,当新数据可用时,处理函数可调用ImageStream.GetNextFrame获取该数据。

NUI骨骼跟踪应用

NUI还包括一个Skeleton骨骼跟踪模块,该部分提供最多两名用户的详细位置和朝向信息。骨骼跟踪的输出是一个点集,称作Skeleton Positions,该点集表示了一个完整的人体骨骼信息,如下图所示。

KINECT内幕——解析SDK(MS SDK 2)

骨骼位置信息表示了用户当前的位置和姿态,如果要使用骨骼跟踪功能,应用程序应在NUI初始化阶段设置相关内容。NUI骨骼数据获取的方式与NUI Image部分基本一致,其基本编程模型也包括Polling和Event两种。前者的C++函数为NuiSkeletonGetNextFrame,托管函数为SkeletonEngine.GetNextFrame;后者的C++句柄绑定操作由NuiSkeletonTrackingEnable完成,并在处理线程内调用NuiSkeletonGetNextFrame;C#则使用Runtime.SkeletonFrameReady绑定事件,然后调用SkeletonEngine.GetNextFrame获取相关信息。

骨骼跟踪模块通过深度数据计算地板裁切面,其基本方法将在后文进行介绍。如果应用程序在NUI初始化时开启了骨骼跟踪功能,则其每处理完一套深度数据则就放出相应的骨骼数据信号,而无论该深度数据中是否真的包含骨骼信息。应用程序使用地板裁切面数据获取骨骼框架,返回的骨架信息将携带一个时间戳以和相关的深度信息进行匹配。

NUI骨骼跟踪分主动和被动两种模式,提供最多两副完整的骨骼跟踪数据。主动模式下需要调用相关帧读取函数获得用户骨骼数据,而被动模式下还支持额外最多四人的骨骼跟踪,但是在该模式下仅包含了用户的位置信息。对于所有获取的骨骼数据,其至少包含以下信息:

1、相关骨骼的跟踪状态,被动模式时仅包括位置数据,主动模式包括完整的骨骼数据。

2、唯一的骨骼跟踪ID,用于分配给视野中的每个用户。

3、用户质心位置,该值仅在被动模式下可用。

4、对于主动模式下的骨骼跟踪数据,还包括用户完整的骨骼数据。

5、对于被动模式下的骨骼跟踪数据,仅包括用户位置信息,不包括详细的骨骼数据。

NUI坐标变换原理

深度图像空间:这是一个仅包含物体到传感器法平面的深度数据的投影空间,其z值是唯一有效的,而x、y值仅仅是进行了插值计算的结果,其数据实际并无任何物理意义;

骨骼空间:用户骨骼位置使用x、y、z三维坐标表示,与深度图像空间坐标系不同的是,该空间的单位为米,且是一个传感器朝向为z轴正向的右手系。应注意,骨骼空间坐标系与Kinect位置息息相关,如果传感器被放置在一个非水平面上,那么计算得到的坐标系可能并非是标准形式,最终的用户数据也有可能是倾斜的。

地板裁剪面的确定:骨骼数据均需要包含一个地板裁剪面向量,该向量保存了与地板平面方程有关的系数coefficients,骨骼跟踪模块通过地板裁剪平面除去背景,并将用户图像分割出来。对于平面的一般式方程Ax+By+Cz+D=0,该方程经过规范化即D值实际是指传感器到地板平面的高度值。如果地板不可见,那么地板裁剪平面向量实际为0。地板裁剪面向量值可在NUI_SKELETON_FRAME结构的vFloorClipPlane成员中获取。托管代码则保存在SkeletonFrame.FloorClipPlane域中。

骨骼镜像:默认的用户图像实际上是一个镜像数据,也就是说该数据表示了用户当前面朝屏幕内部。然而有时确实需要图像面向用户本人一侧,那么需要确定一个镜像变换矩阵以达到此类要求。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Chase620 Chase620
3年前
CSS3动画之逐帧动画
CSS3动画开发指南第二弹,剥丝抽茧为你解析逐帧动画,同时放送从实战经验中总结出来的逐帧动画使用技巧。什么是逐帧动画要了解CSS3逐帧动画,首先要明确什么是逐帧动画。看一下维基百科中的定义:定格动画,又名逐帧动画,是一种动画技术,其原理即将每帧不同的图像连续播放,从而产生动画效果。简而言之,实现逐帧动画需要两个条件:(1)相关联
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
2年前
Python骚操作:利用Python获取摄像头并实时控制人脸!
实现流程从摄像头获取视频流,并转换为一帧一帧的图像,然后将图像信息传递给opencv这个工具库处理,返回灰度图像(就像你使用本地静态图片一样)程序启动后,根据监听器信息,使用一个while循环,不断的加载视频图像,然后返回给opencv工具呈现图像信息。创建一个键盘事件监听,按下"d"键,则开始执行面部匹配,并进行面具加
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究