PrimeSense Grab Detector 编程示例

PrimeSense Grab Detector编程示例

上篇文章介绍过,PrimeSense有推出一能检测手部“握拳”(grab)和“松开”(release)动作的函数库Grab Detector。虽然这两个库的使用有一些限制,但是作为动作捕捉程序开发爱好者,值得看看。

下载GrabDetector库,里面PrimeSense提供一个名为“GrabViewer”的示例可参考(在 \Samples\GrabViewer),程序架构和OpenNI 2 NiTE 2 的示例程序架构大致相同,所以如果之前有做过这方面的程序,很好上手。

OpenNI NiTE初始化

由于 GrabDetector除了需要用到深度和彩色图像,也需要使用 NiTE HandTracker 来定位手的位置,所以在使用 GrabDetector之前,需要针对 OpenNI 2 NiTE 2,配置好各参量。

OpenNI的部分为例,主要就是建立出深度和彩色图像的VideoStream,完成参数配置,代码如下:

// Initial OpenNI
OpenNI::initialize();
 
// Open Device
Device  devDevice;
devDevice.open( ANY_DEVICE );
 
// create depth stream
VideoStream vsDepthStream;
vsDepthStream.create( devDevice, SENSOR_DEPTH );
 
// set video mode
VideoMode mMode;
mMode.setResolution( 640, 480 );
mMode.setFps( 30 );
mMode.setPixelFormat( PIXEL_FORMAT_DEPTH_1_MM );
vsDepthStream.setVideoMode( mMode);
 
// Create color stream
VideoStream vsColorStream;
vsColorStream.create( devDevice, SENSOR_COLOR );
 
// set video mode
mMode.setResolution( 640, 480 );
mMode.setFps( 30 );
mMode.setPixelFormat( PIXEL_FORMAT_RGB888 );
vsColorStream.setVideoMode( mMode);
 
// image registration
devDevice.setImageRegistrationMode( IMAGE_REGISTRATION_DEPTH_TO_COLOR );
//devDevice.setDepthColorSyncEnabled( true );

因为 GrabDetector使用彩色图像辅助深度图像来检测用户手部动作,所以一定要把彩色图像和深度图像,通过Device的setImageRegistrationMode()来做矫正处理,毕竟两个摄像头之间还是有几厘米的距离,采集到的图像信息有一定的位置偏差。

另外,官方文档要求一定要使用setDepthColorSyncEnabled()开启彩色图像和深度图像的同步,不过实际上如果不打开也一样能使用,可能会有点监测偏差。

NiTE 的部分要建立HandTracker对象以检测手部位置,还是老流程依据 click wave 两种手势让摄像头找到运动中的手。

// Initial NiTE
  NiTE::initialize();
 
  // create hand tracker
  HandTracker mHandTracker;
mHandTracker.create( &devDevice );
 
  // set gesture
mHandTracker.startGestureDetection( GESTURE_WAVE );
mHandTracker.startGestureDetection( GESTURE_CLICK );

GrabDetector 只是要手部的位置,所以理论上如果使用UserTracker追踪到人体骨骼中的左右手位置,应该也还是可以使用的,不过没试过,有兴趣的兄弟可以搞搞看。

GrabDetector 初始化

在前一篇文章已经提过,基本上要使用 GrabDetector,主要是要使用PSLabs::CreateGrabDetector()函数,建立出PSLabs::IGrabDetector的对象pGrabDetector来进一步处理。

在建立时,除了需要输入使用的Device外(OpenNI支持多摄像头同时检测),也需要指定数据文档(Redistributable\Common\Data\grab_gesture.dat)所在的位置;我是直接将该文档复制到了VC++项目工程中:

PSLabs::IGrabDetector* pGrabDetector 
     = PSLabs::CreateGrabDetector( devDevice, "GrabDetector/" );
  if( pGrabDetector == NULL
    || pGrabDetector->GetLastEvent( NULL ) != openni::STATUS_OK)
{
  cerr << "Can't initialize grab detector: " 
       << pGrabDetector->GetLastEvent( NULL ) << endl;
  return -1;
}

到了这一步,准备工作就基本完成了。

主程序

主程序的部分,使用HandTracker中当前手的世界坐标xyz值检测该位置的手掌张开或握紧的状态,所以开始还是一样,通过检测手势,开始手部的追踪(process gestures的部分):

vsDepthStream.start();
vsColorStream.start();
PSLabs::IGrabEventListener::GrabEventType eLastEvent
     = PSLabs::IGrabEventListener::NO_EVENT;
  HandId mHandID = 0;
  for( int t = 0; t < 500; ++ t )
{
  // get new frame
  HandTrackerFrameRef mHandFrame;
  if( mHandTracker.readFrame( &mHandFrame ) == nite::STATUS_OK )
  {
    // process gestures
    const nite::Array<GestureData>& aGestures = mHandFrame.getGestures();
    for( int i = 0; i < aGestures.getSize(); ++ i )
    {
      const GestureData& rGesture = aGestures[i];
      const Point3f& rPos =rGesture.getCurrentPosition();
      cout << "Get gesture: " << rGesture.getType() << " at " ;
      cout << rPos.x << ", " << rPos.y << ", " << rPos.z << endl;
 
      mHandTracker.startHandTracking( rPos, &mHandID );
    }
 
    // process hands
    // ...
  }
}

通过mHandID变量,记录最后一个被追踪的手,给 GrabDetector做手部状态的分析。

而“process hand”的部分,开始是和一般的HandTracker的用法相同,需要先使用getHands()取得手部的位置;之后,如果找到我们要处理的手的ID的话,就进入处理程序部分。

// process hands
  const nite::Array<HandData>& aHands = mHandFrame.getHands();
  for( int i = 0; i < aHands.getSize(); ++ i )
{
  const HandData& rHand = aHands[i];
  if( rHand.getId() == mHandID )
  {
    if( rHand.isLost() )
    {
      cout << "Hand Lost";
      mHandID = 0;
    }
    if( rHand.isTracking() )
    {
      // update hand position
      const Point3f& rPos =rHand.getPosition();
      pGrabDetector->SetHandPosition( rPos.x, rPos.y, rPos.z );
 
      // read color frame
      VideoFrameRef mColor;
      vsColorStream.readFrame( &mColor );
 
      // update depth and color image
      pGrabDetector->UpdateFrame( mHandFrame.getDepthFrame(), mColor );
 
      // check last event if not using listener
      PSLabs::IGrabEventListener::EventParams mEvent;
      if( pGrabDetector->GetLastEvent( &mEvent ) == openni::STATUS_OK )
      {
        // if status changed
        if( mEvent.Type != eLastEvent )
        {
          switch( mEvent.Type )
          {
          case PSLabs::IGrabEventListener::GRAB_EVENT:
            cout << "Grab" << endl;
            break;
  
          case PSLabs::IGrabEventListener::RELEASE_EVENT:
            cout << "Release" << endl;
            break;
  
          case PSLabs::IGrabEventListener::NO_EVENT:
            break;
          }
        }
        eLastEvent = mEvent.Type;
      }
    }
    break;
  }
}

接下来,要把新的数据传递给pGrabDetector,让它来分析。首先调用setHandPosition()方法,把手的位置传入,之后调用UpdateFrame(),把深度、彩色的VideoFrameRef作为参数传入,以便分析。

之后,pGrabDetector就开始分析,并根据输入的资料,在内部产生对应的事件(event)。

以官方的示例使用的是 Listener的架构(callback function)来处理信息,不过也可以使用GetLastEvent(),来取得手的最后状态。状态信息在PSLabs::IGrabEventListener::EventParams中,包含了pGrabDetector最后一次产生的事件内容;一般可直接通过其Type,来判断为哪一种事件。

而目前 GrabDetector的事件有三种,分别为GRAB_EVENT、RELEASE_EVENT以及NO_EVENT。

如果每一个图像帧都去进行手部状态监测,并输出当前的手部状态,输出会很乱,因此在本程序中,使用了另外一个eLastEvent来记录之前的手部状态,只有在当前手掌状态和上一次不同时,才有输出。

Listener 模式

而如果是要使用 listener模式,就是常见的事件响应模式,只有当有事件、也就是手的状态发生改变,才触发事件。其实OpenNI 2 NiTE 2 也是支持事件响应模式的。

GrabDetector为例,如果要使用 listener 模式的话,是要先继承PSLabs::IGrabEventListener,创建一个自己的 Listener对象,并在里面重载ProcessGrabEvent()方法即可;如下:

class GrabEventListener : public PSLabs::IGrabEventListener
{
  public:
  GrabEventListener(){}
 
  virtual void DLL_CALL ProcessGrabEvent( const EventParams& params )
  {
    switch( params.Type )
    {
    case PSLabs::IGrabEventListener::GRAB_EVENT:
      cout << "Grab" << endl;
      break;
 
    case PSLabs::IGrabEventListener::RELEASE_EVENT:
      cout << "Release" << endl;
      break;
 
    case PSLabs::IGrabEventListener::NO_EVENT:
      break;
    }
  }
};

可以看到,ProcessGrabEvent()的工作,和上面、放在主程序里面的代码是相同的。

定义完GrabEventListener对象后,如果要使用,就是要在完成 GrabDetector的初始化后,调用AddListener()函数,来指定这个方法处理 GrabDetector产生的事件。代码如下:

GrabEventListener mEventListener;
pGrabDetector->AddListener( &mEventListener );

如果按照上面的流程,那上面主程序里面,本来用GetLastEvent()来分析事件的“// check last event if not using listener”整段代码就可以删除了~因为使用pGrabDetector的setHandPosition()和UpdateFrame()这两个方法更新手部坐标后,如果有触发新的事件,pGrabDetector就自动调用mEventListener的ProcessGrabEvent()方法~

 

结合OpenCV测试检测手及其动作

 

suqinghua1985@gmail.com

2014.4.1

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值