OpenNI概念三层视图
顶层:展示了基于 OpenNI 实现体感的应用程序。
中间层:展示了 OpenNI,提供传感器和中间件组件之间交互的接口,中间件分析 传感器数据。
底层:展示了捕捉视觉和声音场景元素的硬件设备。
Modules 模块
OpenNI框架是个提供了物理设备和中间件组件的一个抽象层。API能够让众多组件 在OpenNI框架中注册。这些组件被称为模块,被用来生成和处理感官数据。
(1) 传感器模块:
3D sensor 三维传感器;
RGB camera RGB摄像头;
IR camera 红外摄像头;
Audio device 音频设备(麦克风);
(2) 中间件组件:
全肢体分析中间件:十一个处理感官数据,生成肢体相关信息(常见的数据结构如关节,方向,重心等)
手心分析中间件:是一个处理感官数据和生成手心的位置信息的软件组件。
手势探测中间件:是一个分辨预定义的手势和提醒应用程序的软件组件。
场景分析中间件:是一个分析场景图像的软件中间件,产生如下信息:场景的前景和背景的分离;平面图的坐标;场景中独特轮廓的识别。
Production Nodes 生产节点
OpenNI定义了生产节点 它具有拥有能在生成体感要求的数据过程中充当生产性角 色的一套单元。 每个生产节点都能够使用其他更低级的生产节点 (读数据, 控制配置等) , 也能够被其他高级节点或本应用程序使用。
生产节点类型 :传感器相关生产节点,中间件相关生产节点。
(1) 传感器相关生产节点Sensor-related Production Nodes :
设备Device :这种节点是物理的设备(例如:深度传感器,或者RGB摄像头)。这个节点的主要角色是使设备可配置。
a.深度生成器Depth Generator::这种节点能够生成深度映射。
b.图像生成器Image Generator :这种节点能够生成彩色图像映射。
c.红外生成器IR Generator :这种节点能够生成红外图像映射。
e.音频生成器Audio Generator :这种节点产生音频流。
(2) 中间件相关生产节点Middleware Related Production Nodes:
a.手势告警生成器Gestures Alert Generator :当特定手势被识别能够回调应用程序。
b.场景分析器Scene Analyzer :分析一个场景,包括前景从背景分开,识别场景中的体型,发现 平面图。场c景分析器的主要输出是标记的深度映射,每一个像素都包含一个标 签,指明是体型还是背景的一部分。
c.手心生成器Hand Point Generator :支持手的发现和跟踪。这个节点当发现一个手心(手掌),或者 当手心被跟踪时,位置发生了变化,就产生一个回调事件。
d.用户生成器User Generator :生成一个在三维场景中的全部或部分肢体图画。
对于记录目的,以下产品节点被支持:
记录器Recorder :实现数据记录。
播放器Player: :从记录里读取数据并且播放它。
编码器Codec :用来压缩和解压缩记录中的数据
Production Chains 生产链
节点顺序是相互依赖的,以产生所需的肢体数据,被称为生产链
Capabilities 能力
一个生产节点可以被问是否支持某个能力。如果支持,这些功能就可以被特定节点调用。 OpenNI发布包括一套能力,也可以在将来继续增加新的能力。每一个模块能申明它 所支持的能力。此外,当需要生产链的列表,应用程序可以指定能力作为支持的条件。 只有满足需要能力的模块才能被列出来。
目前支持的能力有:
(1) 替换视图Alternative View : 让任何类型的映射生成器(深度、 图像、 红外) 能够转换它的数据, 显得仿佛传感器被放到了另一个位置(被另一个生产节点显示,通常是另外一 个传感器)。
(2) 裁剪Cropping :让一个映射生成器(景深、图像、红外)能够输出帧的可选区域而区别 裁剪 于整个帧。 当具备裁剪能力时, 生成的映射的尺寸被减少为适合更低的分辨率。
例如, 一个映射生成器工作在VGA分辨率 (640x480) 应用程序要裁剪在300x200, 下一个像素行从300像素后开始。裁剪在性能提升方面非常有用。
(3) 帧同步Frame Sync :让两个传感器产生帧数据(例如:深度、图像)能够同步他们的帧, 以致他们同时到达。
(4) 镜像Mirror :让生成器能够生成的数据的镜像。如果传感器放在用户面前,传感器捕 捉到的影像被镜像,镜像这时很有用,这样右手就可以以镜像的体型中的左手 出现了。
(5) 姿势检测Pose Detection :让用户生成器认出用户摆出的特定姿势。
(6) 骨骼Skeleton :让用户生成器能够输出用户骨骼数据。这个数据包括骨骼关节的位置, 跟踪骨骼的位置的能力,用户校准的能力。
(7) 用户位置User Position :让深度生成器能够为场景的特定区域而优化输出特定深度映射。
(8) 错误状态Error State:使一个结点在出错的时候能报告它的状态
(9) 锁发现Lock Aware :让节点能够被上下文边界锁定。详细信息,参考在应用和锁节点间共锁发现
生成和读取数据
1.生成数据
产生数据的生产节点被称为生成器。数据生成器只等到确定指令要求才实际生成数 据。函数xn::Generator::StartGenerating()用来开始生成。应用程序为了保留配置,可以 通过函数xn::Generator::StopGenerating来停止数据生成,而不必释放节点对象。
2.读取数据
数据生成器还有个输出锁存功能,其在内部储存新数据,直到收到刷新指令UpdateData ,再把最新数据传输上去。当然,OpenNI让应用程序能够等到新数据可用再刷新,指令是函数xn::Generator::WaitAndUpdateData()。
在某些情况下,应用程序操作不止一个节点,想等所有节点一起刷新。OpenNI为此
提供了几个函数,根据在UpdateData之前什么应该发生来区分:
xn::Context::WaitAnyUpdateAll(): 一旦任意一个节点有 新数据,那么刷新所有节点数据。
xn::Context::WaitOneUpdateAll(): 一旦这个节点有新 数据, 那么刷新所有节点数据。 (在当几个节点都在生成数据但只有一个节点决定了 应用程序进度时,这个函数特别有用。)
xn::Context::WaitNoneUpdateAll(): 不等待。所有节点都立即刷新。
xn::Context::WaitAndUpdateAll(): 等到所有节点都有新数据是,再刷新他们。 以上四个函数都是在超时两秒后退出。建议使用其中一个加上函数 UpdateAll(),除非你需要刷新特定节点。
在应用和锁节点间共享设备
说白点,不同应用程序对同一设备有不同的配置,不能这个我还没用完,你就来给我改设备的配置,这样就乱了。
OpenNI有两个模式让多个应用能够共享硬件设备:
完全共享(缺省) :在这种模式中,应用程序申明可以处理这个节点的任何配置。 OpenNI 接口让注册者能够回调任何配置改变的函数,所以,当配置发生改变,应 用就被通知(被同一个应用,或者另一个使用相同硬件设备的应用) 。
锁配置:这种模式下,应用程序声明它要锁定节点的当前配置。OpenNI 因此不允 许这个节点的”Set”函数被调用。如果节点是硬件设备(或任何其他可以在进程间共享的模块) ,他应该实现” Lock Aware”能力,这样才能跨进程边界加锁。
注意:当一个节点被锁,加锁的应用收到一个锁句柄。不同于使用这个句柄解锁,句柄 用来在不解锁的情况下来改变节点配置(为了不让节点配置被其他应用“偷走”)。
Licensing 授权
模块使用授权机制来确保它们只被授权过的应用程序使用。特定的厂商的模块能被 安装在特定的设备上,如果使用该模块的应用程序提供授权才能访问该模块。当OpenNI 在列表中查找合适的生产链,模块能够检查授权列表。如果要求的授权没有注册过,模 块能够隐藏自己,也就是说不返回任何结果,因此也不被算到可能的生产链中。
The Context Object 上下文对象
上下文是指拥有适用OpenNI的应用程序的全部状态的 对象,包括应用程序使用的所有生产链。同一个应用可以生成多个上下文对象,但是上 下文对象之间不能共享信息。上下文在使用前必须首先初始化。这个时候,所有的外挂的模块被载入和解 析。应用程序通过调用shutdown函数来释放上下文对象的内存。
Metadata Objects 元数据对象
OpenNI元数据对象封装一组特定数据相关的属性和该数据绑在一起。例如,深度映射常 见属性是映射的坐标(例如,在 和Y轴的像素点数)。每个产生数据的生成器都有自己 特定的元数据对象。
配置改变
接口的每个配置选项有以下选项组成:
Set函数用来编辑配置 Get函数用来返回当前配置值
注册和注销函数,是能够在选项改变时被回调的函数。
数据生成器 | 解释 | 主要函数: |
Map Generator 映射生成器 | 对产生任何映射的数据生成器的基本接口 | Output Mode property: 控制按照哪个配置生成映射。 Cropping capability Alternative Viewpoint capability Frame Sync capability |
Depth Generator 深度生成器 | 能够产生深度视图的对象。 | Get depth map: 返回深度映射。 Get Device Max Depth: 深度生成器的最大可用距离。 Field of View property: 配置传感器的水平和垂直角度值。 User Position capability |
Image Generator 图像生成器 | 产生彩色图像映射的生成器。 | Get Image Map: 返回彩色图像映射 Pixel format property |
IR Generator 红外生成器 | 生成红外映射的生成器。 | Get IR Map: 返回当前红外映射 |
Scene Analyzer 场景分析器 | 映射生成器,能获取原始体感数据并产生有清晰场景标注的映射。 | Get Label Map: 返回一个每个像素带有意义标注(比如,体型1,体型2,背景等 等)的映射 Get Floor: 返回平面图坐标系 |
Audio Generator 语音生成器 | 产生语音数据的对象。 | Get Audio Buffer Wave Output Modes property: 配置语音输出,包括采样率、通道数和采样精度。 |
Gesture Generator 手势生成器 | 能够进行特定肢体或手势的跟踪的对象。 | Add/Remove Gesture: 打开或关闭手势。一旦打开,生成器就开始寻找手势。 Get Active Gestures: 返回当前活动的手势名称 Register/Unregister Gesture callbacks Register/Unregister Gesture change |
Hand Point Generator 手心生成器 | 能跟踪手心的对象。 | Start/Stop Tracking: 开始/停止跟踪特定的手(根据手的位置) Register/Unregister Hand Callbacks: 以下动作将产生手回调: 当出现一个新的手 ;已有的手出现在一个新位置 ;已有的手消失了 |
User Generator 用户生成器 | 产生和场景人物相关数据的对象。 | Get Number of Users: 返回当前识别的场景中的使用者数量。 Get Users: 返回当前使用者 Get User CoM:返回使用者人群的中心位置 Get User Pixels: 返回显示使用者的像素。输出时一个完整场景像素映射,像素通过 使用者 ID 的标注来显示使用者肢体。 Register/Unregister user callbacks: 以下动作将产生使用者回调: 当新的使用者被识别 ;当已存在的使用者消失 |
代码实例
1.Basic Functions: Initialize, Create a Node and Read Data 初始化, 创建一个节点和读取数据 |
(初始化一个上下文对象,创建并且从单一的深度节点中读取数据) XnStatus nRetVal = XN_STATUS_OK; xn::Context context; // Initialize context object nRetVal = context.Init(); // TODO: check error code xn::DepthGenerator depth; // Create a DepthGenerator node nRetVal = depth.Create(context); // TODO: check error code nRetVal = context.StartGeneratingAll(); // Make it start generating data // TODO: check error code // Main loop while (bShouldRun) { nRetVal = context.WaitOneUpdateAll(depth); // Wait for new data to be available if (nRetVal != XN_STATUS_OK) { printf("Failed updating data: %s\n", xnGetStatusString(nRetVal)); continue; } const XnDepthPixel* pDepthMap = depth.GetDepthMap(); // Take current depth map // TODO: process depth map } context.Shutdown(); // Clean-up |
2.Enumerating Possible Production Chains 枚举可能的生产链 |
xn::Query query; // Build a query object nRetVal = query.SetVendor("MyVendor"); // TODO: check error code query.AddSupportedCapability(XN_CAPABILITY_SKELETON); xn::NodeInfoList possibleChains; nRetVal = context.EnumerateProductionTrees // No errors so far. This means list has at least one item. Take the first one xn::NodeInfo selected = *possibleChains.Begin(); nRetVal = context.CreateProductionTree(selected); // Create it // TODO: check error code xn::UserGenerator userGen; nRetVal = selected.GetInstance(userGen); // Take the node // TODO: check error code // TODO: check error code // Now we can start to use it |
有时候,应用程序枚举指定节点,会收到 0 个结果。一个显而易见的原因可能是没有模块的实例安装了这个节点类型,尽管会有其它原因,包括模块可能已安装但没有许可,或者请求的硬件设备当前断开连接。 |
以下代码试图创建一个手生成器节点,并且当枚举失败的时候,检查所有的错误: xn::EnumerationErrors errors; xn::HandsGenerator handsGen; nRetVal = context.CreateAnyProductionTree(XN_NODE_TYPE_HANDS, NULL, handsGen, &errors); if (nRetVal == XN_STATUS_NO_NODE_PRESENT) { // Iterate over enumeration errors, and print each one for (xn::EnumerationErrors::Iterator it = errors.Begin(); it != errors.End(); ++it) { XnChar strDesc[512]; xnProductionNodeDescript 512); printf("%s failed to enumerate: %s\n", xnGetStatusString(it.Error())); } return (nRetVal); } else if (nRetVal != XN_STATUS_OK) { printf("Create failed: %s\n", xnGetStatusString(nRetVal)); return (nRetVal); } |
3.Working with Depth, Color and Audio Maps 使用深度、颜色和声音图 |
以下代码示例创建一个深度生成器,检查是否它能够以 30 帧每秒的速度生成 VGA 图,配 置它为这种模式,然后从它读取数据,打印出中间像素值: XnStatus nRetVal = XN_STATUS_OK; Context context; nRetVal = context.Init(); // TODO: check error code // Create a depth generator DepthGenerator depth; nRetVal = depth.Create(context); // TODO: check error code // Set it to VGA maps at 30 FPS XnMapOutputMode mapMode; mapMode.nXRes = XN_VGA_X_RES; mapMode.nYRes = XN_VGA_Y_RES; mapMode.nFPS = 30; nRetVal = depth.SetMapOutputMode(mapMode); // TODO: check error code // Start generating nRetVal = context.StartGeneratingAll(); // TODO: check error code // Calculate index of middle pixel XnUInt32 nMiddleIndex = XN_VGA_X_RES * XN_VGA_Y_RES/2 + // start of middle line XN_VGA_X_RES/2; // middle of this line while (TRUE) { // Update to next frame nRetVal = context.WaitOneUpdateAll(depth); // TODO: check error code const XnDepthPixel* pDepthMap = depth.GetDepthMap(); printf("Middle pixel is %u millimeters away\n", pDepthMap[nMiddleIndex]); } // Clean up context.Shutdown(); |
4.Working with Audio Generators 使用音频生成器 |
声音生成器积累数据直到调用 UpdateData()函数,将整个累积的音频缓冲区返回给应用程序。每次调用的音频缓冲区的大小可能不同,应用程序应该每次都调用xn::AudioGenerator::GetDataSize()函数获得当前缓冲区的大小。 |
以下示例了创建一个音频生成器,配置成 CD 音质,并且从它不断的读取数据: Context context; nRetVal = context.Init(); // TODO: check error code AudioGenerator audio; nRetVal = audio.Create(context); // TODO: check error code XnWaveOutputMode waveMode; waveMode.nSampleRate = 44100; waveMode.nChannels = 2; waveMode.nBitsPerSample = 16; nRetVal = audio.SetWaveOutputMode(waveMode); // TODO: check error code while (TRUE) { // Update to next data nRetVal = context.WaitOneUpdateAll(audio); // TODO: check error code // Get the audio buffer const XnUChar* pAudioBuf = audio.GetAudioBuffer(); XnUInt32 nBufSize = audio.GetDataSize(); // Queue the buffer for playing } // Clean up context.Shutdown(); |
5.Recording and Playing Data 录制和播放数据 |
Recording 录制: // Create a depth generator DepthGenerator depth; nRetVal = depth.Create(context); // TODO: check error code // Start generating nRetVal = context.StartGeneratingAll(); // TODO: check error code // Create Recorder Recorder recorder; nRetVal = recorder.Create(context); // TODO: check error code // Init it nRetVal = recorder.SetDestination(XN_RECORD_MEDIUM_FILE, "c:\\temp\\tempRec.oni"); // Add depth node to recording nRetVal = recorder.AddNodeToRecording(depth, XN_CODEC_16Z_EMB_TABLES); // TODO: check error code while (TRUE) { // Update to next frame (this will also record that frame) nRetVal = context.WaitOneUpdateAll(depth); // TODO: check error code // Do application logic } |
Playing 播放: 播放记录文件,使用 xn::Context::OpenFileRecording()函数。OpenNI 会打开该文件,对文件中的每一个节点创建一个模拟节点,然后用记录的配置填充节点。应用程序可以通过调用xn::Context::FindExistingNode()函数来取到应用程序需要的节点, 并正常的使用它们。 注意:被播放器创建的节点是被锁定的,并且不能被改变,配置必须仍然依据被录制的配置。 Context context; nRetVal = context.Init(); // Open recording nRetVal = context.OpenFileRecording("c:\\temp\\tempRec.oni"); // TODO: check error code // Take the depth node (we assume recording contains a depth node) DepthGenerator depth; nRetVal = context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth); // Add regular application logic |
6.Node Configuration 节点配置 |
应用程序通常想在开始流数据前完全的配置节点。由于这个原因,OpenNI 定义了一个流程, 在这个流程中可以进行配置, 一旦所有配置被设定, 节点的 xn::Generator::StartGenerating() 函数被调用,开始数据流。 |
以下代码创建一个深度生成器,配置为 VGA 解析度,30 帧每秒,并启动它: // Create a DepthGenerator node xn::DepthGenerator depth; nRetVal = depth.Create(context); // TODO: check error code XnMapOutputMode outputMode; outputMode.nXRes = 640; outputMode.nYRes = 480; outputMode.nFPS = 30; nRetVal = depth.SetMapOutputMode(outputMode); // TODO: check error code // We're done configuring it. Make it start generating data nRetVal = context.StartGeneratingAll(); // TODO: check error code // Main loop while (bShouldRun) { // Wait for new data to be available nRetVal = context.WaitOneUpdateAll(depth); if (nRetVal != XN_STATUS_OK) { printf("Failed updating data: %s\n", xnGetStatusString(nRetVal)); continue; } // Take current depth map const XnDepthPixel* pDepthMap = depth.GetDepthMap(); // TODO: process depth map } |
英语生词:
alert [ə'lə:t] adj.警觉的, 灵敏的 vt.使意识到, 警惕 n.警戒, 警报
skeleton /ˈskelɪtn; `skɛlətn/骨骼
vendor ['vendə] n.自动售货机, 小贩, 卖方, 供货商
context ['kɔntekst]上下文 the context object上下文对象
Generator ['dʒenəˌreitə] n. 发电机, 发生器
configuration [kənˌfigjə'reiʃən] n [计算机] 配置
implement /ˈɪmplɪmənt; `ɪmpləmənt/ v执行
implementation [ˌimpləmen'teiʃən] n.履行, 落实, 装置
codec 编码译码器
compatibility [kəmˌpætə'biləti] n.兼容 backwards compatibility 向后兼容
Scene [si:n] n.情景, 场, 景
simulate ['simjəˌleit] vt.假装, 模拟, 模仿
detection /dɪˈtekʃn; dɪ`tɛkʃən/ n检测
explicit [ik'splisit] adj显然的
mock /mɒk; mɑk/ adj not real; substitute 非真实的; 模拟的
portability [ˌpəurtə'biləti] n.可移植性, 可携带, 轻便
utility [ju:'tiləti] n.效用, 实用, 公用事业, [计]应用程序 adj.多效用的,多功能的
replica /ˈreplɪkə; `rɛplɪkə/ n复制品
initialize [i'niʃəˌlaiz] vt. 初始化
Metadata n. [计]元数据
sensory ['sensəri] adj. 知觉的, 感觉的, 知觉器官的
enumerate [i'nju:məˌreit] vt. 数, 列举, 枚举
query ['kwiri] 询问
accumulate [ə'kju:mjəˌleit] v.积累, 增加, 聚集
element /ˈelɪmənt; `ɛləmənt/ 元件
prior ['praiə] adj.优先的, 在前的, 更重要的 adv.居先,在前
attribute /əˈtrɪbjuːt; ə`trɪbjᴜt/ n属性
pixel ['piksəl] n.像素