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分辨率 (640×480) 应用程序要裁剪在300×200, 下一个像素行从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(XN_NODE_TYPE_USER, &query, possibleChains, NULL); // Enumerate // TODO: check error code
// 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];
xnProductionNodeDescriptionToString(&it.Description(), strDesc,
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.像素