购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

6.1 OpenNI基础应用

前面章节中已经介绍了OpenNI的相关概念,以及一些功能,本节会详细讲述这些基础概念以及相关功能在实例中的简单的运用方法。

6.1.1 上下文对象初始化

1.实例

OpenNI中的主要对象就是上下文对象,上下文对象记录了使用OpenNI的所有信息,而上下文对象在被使用的时候,必须先被初始化。

2.实现方法

对于上下文对象初始化来说,这里介绍两种初始化方法,首先就是系统默认的初始化方法,也就是使用上下文对象Context下的Init()函数,其实现方法如下代码所示。


XnStatus nRetVal = XN_STATUS_OK;
xn::Context context;
// Initialize context object
nRetVal = context.Init();

首先声明一个Context类型的物件Context而后调用其自身的Init()函数就可以完成默认的初始化,不过默认初始化之后,上下文对象中并未经过任何设置,后续还需要进一步地设置才可以使用,比如在上下文对象中添加生产节点等。

还有一种方法就是通过XML文档进行初始化,这里XML文档实质上就是一个已有的配置文件,其具体介绍请参考后续章节,本章只简单介绍一下使用XML文档初始化上下文对象的方法。使用XML文件进行初始化的方法如下代码所示。


// Initialize OpenNI context using an XML file
xn::Context context;
nRetVal = context.InitFromXmlFile(".\\ Config.xml", NULL);

同样首先需要声明一个Context类型的物件,而后调用Context对象下的InitFromXmlFile()函数,这样就可以使用预先设定好的配置文件,比如使用本例中的Config.xml对上下文对象进行设定了。因为xml文档中可以进行多项设定,所以使用xml文档进行初始化之后,可以预先配置好整个OpenNI的上下文对象环境。

6.1.2 创建生产节点

1.实例

在OpenNI框架中,实际的硬件设备在应用程序中会对应一个生产节点,生产节点就是具有产生数据功能的虚拟节点,在OpenNI程序中的上下文对象内,可以有一个或者多个生产节点,这些生产节点在实际运用中起到供给开发者数据的功能,当然在使用这些节点之前首先要创建这些节点。

2.实现方法

OpenNI中生产节点的创建主要分成三步,首先是声明一个生产节点,其次是创建一个生产节点,最后是将生产节点导入到相应的上下文对象中,这里以深度生成器和用户生成器为例,其方法如下代码所示。


// Create a DepthGenerator node
XnStatus eResult = XN_STATUS_OK;                // OpenNI
中的状态量
xn::DepthGenerator mDepthGenerator;             // 
定义一个深度生成器DepthGenerator
eResult = mDepthGenerator.Create(mContext);     // 
将深度生成器创建到mContext
上下文对象中
CheckError(eResult);

首先声明一个深度生成器DepthGenerator,例中名为mDepthGenerator,而后使用深度生成器类下的Create函数,传递的参数为相应的上下文对象,例中使用Create函数,把mDepthGenerator深度生成器传入mContext上下文对象中。

再以创建一个用户生成器为例,代码如下。


//  Create a UserGenerator node
XnStatus eResult = XN_STATUS_OK;
xn::UserGenerator mUserGenerator;
eResult = mUserGenerator.Create(mContext);
CheckError(eResult);

用户生成器创建方法的实际过程和上面的深度生成器相同,实际上OpenNI中其他生产节点的创建方法也是如此,因此读者可以参考下面的代码,有兴趣的读者可以根据给出的代码自行创建所需要的生产节点。


xn::DepthGenerator::Create()
xn::ImageGenerator::Create()
xn::IRGenerator::Create(),
xn::AudioGenerator::Create()
xn::GestureGenerator::Create()
xn::SceneAnalyzer::Create()
xn::HandsGenerator::Create()
xn::UserGenerator::Create()

6.1.3 使用XML文档中的生产节点

1.实例

在实际使用情况中,通过XML文档对上下文对象进行初始化之后,如果XML文档中有预先定义好的生产节点,此时就无须再另外创建生产节点,而可以直接使用XML文档中已有的生产节点。

2.实现方法

前提条件是使用XML文档进行初始化后,并且文档内有预先定义好的生产节点可供使用,此时定义一个生产节点,使用上下文对象(Context)下的FindExistingNode函数就可以找到预先定义好的生产节点。以深度生成器为例,其方法如下代码所示。


//  Initialize OpenNI context using an XML file
xn::Context context;
nRetVal = context.InitFromXmlFile("SampleConfig.xml");
//  Look for the depth node
xn::DepthGenerator depth;
nRetVal = context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth);
if (nRetVal != XN_STATUS_OK)
{
    printf("Can't run - no depth generator is present!\n");
    exit(-1);
}

首先是创建一个上下文对象Context并采用InitFromXmlFile的初始化方法,在SampleConfig.xmL文档中已经存在有预先定义好的深度节点,所以只要调用上下文对象内的FindExistingNode函数即可找到预先定义好的深度节点,这里FindExistingNode的第一个参数,其类型是XnProductionNodeType,它一共包含了如下代码所示的节点类型,所以如果需要找寻其他类型的节点,只要在第一个参数传递进去相应的类型即可。


XN_NODE_TYPE_INVALID            An invalid node type 
XN_NODE_TYPE_DEVICE             A device node 
XN_NODE_TYPE_DEPTH              A depth generator 
XN_NODE_TYPE_IMAGE              An image generator 
XN_NODE_TYPE_AUDIO              An audio generator 
XN_NODE_TYPE_IR                 An IR generator 
XN_NODE_TYPE_USER               A user generator 
XN_NODE_TYPE_RECORDER           A recorder 
XN_NODE_TYPE_PLAYER             A player 
XN_NODE_TYPE_GESTURE            A gesture generator 
XN_NODE_TYPE_SCENE              A scene analyzer 
XN_NODE_TYPE_HANDS              A hands generator 
XN_NODE_TYPE_CODEC              A Codec 
XN_NODE_TYPE_PRODUCTION_NODE    Abstract types 
XN_NODE_TYPE_GENERATOR
XN_NODE_TYPE_MAP_GENERATOR
XN_NODE_TYPE_SCRIPT
XN_NODE_TYPE_FIRST_EXTENSION    

6.1.4 错误信息返回

1.实例

每一个OpenNI应用程序中的某些功能模块都有可能出错,并且会返回一个状态值XnStatus,在OpenNI中XnStatus类型的变量的值为XN_STATUS_OK则代表某块功能被成功执行,而为其他值时则表示这块功能出现了某些错误,如果想进一步获得具体是什么错误的信息,则可以使用xnGetStatusString()函数,该函数会返回关于错误的具体描述。在日常OpenNI程序开发中,如果在处理某些步骤或者功能时发生了某些错误并且被获取到,则需要开发者做出相应的处理,比如发出一个信息,并且关闭应用。

2.实现方法

OpenNI中这些错误信息返回的值会交给XnStatus类型的变量,所以首先需要声明一个XnStatus类型的变量,而后将实现某一功能的过程的结果状态交给该变量,最后开发者可以根据返回的值判断该功能是否正常运行,这里以上下文对象的初始化过程返回的运行结果状态栏来判断为例,其方法如下代码所示。


xn::Context context;
XnStatus nRetVal = context.Init();
if (nRetVal != XN STATUS OK)
{
    printf("Failed to initialize OpenNI: %s\n", xnGetStatusString(nRetVal));
exit(-1);
}

例中创建了一个名为nRetVal的XnStatus类型的变量,并将上下文对象初始化运行的结果状态量传递给nRetVal,而后根据nRetVal的值进行判断,如果值为XN_STATUS_OK,则表明上下文对象的初始化是成功的,如果为其他值,则表明上下文对象的初始化失败了,这时可以通过xnGetStatusString()函数来获取具体的错误信息。本例中的代码只做了告知开发者错误信息,以及关闭OpenNI程序这两步,具体的处理方法还需要根据开发者不同的需求进行相应的处理。

6.1.5 开始产生数据

1.实例

在OpenNI中,每一个生产节点被创建后并不会立即产生数据,在接收到启动命令之后才会开始产生数据。在上下文对象内的生产节点产生数据的“开关”,是由上下文对象统一进行控制的。

2.实现方法

通过上下文对象统一进行控制,意味着这些生产节点产生数据的过程是一致的,并不能针对某个特定节点进行单独控制,这里统一控制的方法就是调用上下文对象中的StartGeneratingAll()函数,如下代码所示。


//  Make it start generating data
nRetVal = context.StartGeneratingAll();

调用了StartGeneratingAll()函数之后,本例中上下文对象Context内的生产节点都会开始产生数据,如果以深度生成器和图像生成器为例来说,调用StartGeneratingAll()之后,上下文对象下这两个生成器对应的两个物理设备,深度感应器(3D Sensor)和彩色(RGB)摄像头则会开始工作。

6.1.6 停止产生数据

1.实例

OpenNI应用中,在开发者利用获取到数据进行相应的处理完成之后,或者中途需要进行其他作业而要暂时停止产生数据,此时就需要通过一些功能使得所有的生产节点停止产生数据,这一步的过程也是统一控制的,即通过上下文对象控制停止产生数据之后,该上下文对象内的所有生产节点都会停止产生数据。

2.实现方法

上下文对象中提供了开始产生数据的函数,同时也提供了停止产生数据的控制函数StopGeneratingAll(),通过上下文对象调用该函数之后,上下文对象内的所有的生产节点则会停止产生数据,不过此时上下文对象的配置环境以及生产节点的配置仍然存在,也就是说重新调用StartGeneratingAll()函数之后,仍可以重新开始产生数据,具体使用方法如下代码所示。


//  Make it stop generating data
nRetVal = context.StartGeneratingAll();

调用StopGeneratingAll()函数之后,还是以深度生成器和图像生成器为例,上下文对象下这两个生成器对应的两个物理设备,深度感应器(3D Sensor)和彩色(RGB)摄像头则会停止工作。

6.1.7 上下文对象资源释放

1.实例

在应用程序的开发过程中,某一项功能或者整个应用程序结束时,会需要释放一些资源,OpenNI应用开发同样如此,在OpenNI应用完成功能之后,需要销毁上下文对象,以用来回收资源。

2.实现方法

OpenNI程序某些功能或者整体运行结束之后,需要销毁上下文对象,释放资源,在OpenNI中上下文对象下就提供了这样的自销毁函数Shutdown(),其使用方法如下代码所示。


//  Clean-up
context.Shutdown();

例中名为context的上下文对象在调用Shutdown函数之后,context上下文对象里的生产节点、配置、环境等所有的资源均会被销毁,此时若想重新使用则需要重新进行初始化,而原先上下文对象里的内容也需要重新添加进去才可以重新被使用。

6.1.8 更新数据

1.实例

OpenNI中,生产节点在被调用开始产生数据的函数之后,会以预先设定好的速率不停地产生数据,而在实际应用中,生产节点产生数据的速率和开发者处理数据的速率很难一致,如果使用缓冲池的方法使用这些数据,很可能造成开发者获取到的数据是旧的数据,所以OpenNI就采用了update这样的更新机制,来保证开发者获取到当前最新的数据。

2.实现方法

OpenNI里的更新机制在应用程序中的实现方法就是在开发者要获取数据之前,调用update函数,将生产节点预置的数据更新为最新的数据,然后再从生产节点获取数据,从而确保获得最新的数据,调用更新这个功能是由上下文对象来控制的,由于一个上下文对象中可能有多个生产节点,所以OpenNI就提供了4个update函数,这里以WaitOneUpdateAll()为例,其方法如下代码所示。


// 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;
  }

例中,通过调用context上下文对象下的WaitOneUpdateAll()函数来更新数据,其中WaitOneUpdateAll()的含义是等指定的生产节点有新的数据之后,则更新所有的生产节点。例中传递进去的是depth,含义就是当名为depth的深度生产节点有新的数据之时,则更新所有的生产节点。之前曾介绍过除了这个更新函数之外还有三个更新函数,分别是WaitAndUpdateAll()、WaitAnyUpdateAll()、WaitNoneUpdateAll(),这些更新机制函数的不同功能请参阅前面的章节。

6.1.9 镜像设置

1.实例

深度感应器(3D sensor)获取到的数据是经过成像的,也就是说实际上拿到的深度数据是经过一次镜像的,表现出来的情况就是通常所说的左右相反,在一些情况下,程序可能需要把这些数据恢复到正常情况,此时就需要再进行一次镜像操作,这样开发者就可以获取符合人们认知习惯的数据了。

2.实现方法

在OpenNI中,镜像功能属于所有的图生成器,首先获取到图类生成器下的镜像功能,而后再设置镜像,具体过程如下代码所示。


//  SetMirror
xn::UserGenerator mUserGenerator;       
xn::DepthrGenerator mDepthGenerator;    
mDepthGenerator.GetMirrorCap().SetMirror(FALSE); 
xn::MirrorCapability MirrorCAP = NULL;
MirrorCAP = mUserGenerator.GetMirrorCap();
MirrorCAP.SetMirror(TRUE);

代码中列举了深度生成器和用户生成器两种图生成器的镜像功能,首先是深度生成器mDepthGenerator,调用其下的GetMirrorCap()函数获取Mirror功能,接着调用Mirror功能下的SetMirror函数。设置参数为FALSE则表示关闭Mirror功能,反之则打开Mirror功能。

第二个是用户生成器mUserGenerator,其中还定义了一个镜像功能(MirrorCapability),例中命名为MirrorCAP,先获取镜像功能,之后再通过镜像功能来设置用户生成器的镜像效果,本质上这和深度生成器的用法是一样的。

6.1.10 图像位置校正

通常情况下统一把设备当成原点来使用其坐标系,然而在结合使用彩色图像和深度图像时,就会出现一个问题,虽然广义上的设备是原点,不过实际上深度摄像头(3D Sensor)和彩色摄像头(RGB Sensor)的物理位置并不在同一位置,并且这两个摄像头的参数也不同,所以不可避免地会造成彩色摄像头所拍出来的图像和深度摄像头所拍出来的图像并不完全相同,也就是说它们并不完全重合。

图6-1为彩色摄像头所拍摄的画面,图6-2为深度摄像头所拍摄的画面。

图6-1 Image图像

图6-2 Depth图像

仔细看可以发现,这两个画面并不完全重合。针对这种情况,以及在彩色图像和深度图像结合分析的应用需求下,OpenNI中提供了一个AlternativeViewPointCapability()函数,通过调用这个函数,就可以完成彩色图像和深度图像的校准,该函数的使用方法如下代码所示。


//  AlternativeViewPointCapability
xn::Context mContext;
xn::DepthGenerator mDepthGenerator;
eResult = mDepthGenerator.Create(mContext);
xn::ImageGenerator mImageGenerator;
eResult = mImageGenerator.Create(mContext);
XnMapOutputMode mapMode;
mapMode.nXRes = 640;
mapMode.nYRes = 480;
mapMode.nFPS = 30;
eResult = mDepthGenerator.SetMapOutputMode(mapMode);
eResult = mImageGenerator.SetMapOutputMode(mapMode);
// 
修正深度生成器视角
mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint(mImageGenerator);

因为是彩色摄像头和深度摄像头画面的修正,所以例中定义了两个生产节点,图像生成器和深度生成器,并给这两个生产节点设置了同样的输出模式640*480@30FPS,此时调用深度生成器下的AlternativeViewPointCapability()函数,并传入图像生产节点,该函数的本质意义就是使深度生成器的视角坐标为和图像生成器的视角坐标一致。

图6-1和图6-2则是未修正时两个摄像头拍出来的画面,可以看出来它们并不重合。针对这个现象,开发者可以使用Depth Generator的xn::AlternativeViewPointCapability进行修正,使用方法如下所示:


xDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( xImageGenerator )

修正之后,就会看到图6-3的效果,物体不再有明显的错位,彩色影像会以符合深度视角的情况出现(基本重合)。

图6-3 Depth+Image校正后的图像 qH3/sLpIgg1DGkYB8lKICBFjf14T02NqeagGZjq9Nj4qNEDXiaMNtO8xLTKVGGeq

点击中间区域
呼出菜单
上一章
目录
下一章
×