在 OpenNI 2 環境下使用多個 Device

這一篇,稍微寫一下要怎麼在 OpenNI 2 的架構下,使用多個 Device。

首先,OpenNI 這個類別本身有提供一個 enumerateDevices() 的函式,是用來列出系統上所有的裝置的;而透過這個函式取得的結果,會是 DeviceInfo 的陣列。它的使用方法,基本上如下:

Array<DeviceInfo> aDeviceList;
OpenNI::enumerateDevices( &aDeviceList );

DeviceInfo 這個類別,基本上是用來取得裝置的一些基本資訊用的,它提供了五個可以用來取得資訊的函式:

  • const char* getName() const
  • uint16_t    getUsbProductId() const
  • const char* getVendor() const
  • uint16_t    getUsbVendorId() const
  • const cha * getUri() const

其中,getName() 可以取得裝置本身的名稱、getVendor() 則可以取得供應商的名稱,算是可以讓使用者在不同裝置間做判斷的方法;而 getUsbProductId()getUsbVendorId() 這兩個函式的用途,基本上和上面兩個函式類似,只不過他輸出的結果是給電腦看的編號,一般人比較看不出意義。

最後,他還有提供一個 getUri() 的函式,可以取得這個裝置在系統上的連接位置的字串,這個字串可以在呼叫 Deviceopen() 這個函式的時候傳入,指定 Device 要開啟哪個裝置~基本上,也是在 OpenNI 2 的環境下,控制多個裝置最重要的資訊了。

下面就是一個用來輸出 DeviceInfo 陣列的範例:

cout << "There are " << aDeviceList.getSize()
<< " devices on this system." << endl;
for( int i = 0; i < aDeviceList.getSize(); i )
{
cout << "Device " << i << "\n";
const DeviceInfo& rDevInfo = aDeviceList[i];
 
cout << " " << rDevInfo.getName() << " by " << rDevInfo.getVendor() << "\n";
cout << " PID: " << rDevInfo.getUsbProductId() << "\n";
cout << " VID: " << rDevInfo.getUsbVendorId() << "\n";
cout << " URI: " << rDevInfo.getUri() << endl;
}

而這樣的程式碼,在 Heresy 接了一台 ASUS Xtion Pro Live 和一台 Microsoft Kinect 的電腦上,會輸出下面的文字:

There are 2 devices on this system.
Device 0
Kinect by Microsoft
PID: 0
VID: 0
URI: USB\VID_045E&PID_02C2\5&C0AA250&0&2
Device 1
PS1080 by PrimeSense
PID: 1537
VID: 7463
URI: \\?\usb#vid_1d27&pid_0601&mi_00#8&23c7366d&0&0000#
{c3b5f022-5a42-1980-1909-ea72095601b1}

可以看到,Kinect 回傳的裝置名稱就是「Kinect」,而 Xtion Pro Live 則是回傳是「PS1080」;另外,也可以發現,Kinect 回傳的 PID 和 VID 都是 0…


上面基本上只是取得裝置的資訊,如果要開啟裝置的話,他的用法就會像下面這樣:

const DeviceInfo& rDevInfo = aDeviceList[i];
Device mDev;
mDev.open( rDevInfo.getUri() );

簡單地講,就是把 getUri() 回傳的字串,丟給 Deviceopen() 就可以了;而接下來,只要透過開啟好的 Device,就可以個別控制不同的裝置了。

下面就是一段使用 OpenCV 來同時顯示多個裝置的深度影像的程式碼:

// STL Header
#include <iostream>
#include <string>
#include <vector>
 
// OpenCV Header
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
 
// OpenNI Header
#include <OpenNI.h>
 
// namespace
using namespace std;
using namespace openni;
 
class CDevice
{
public:
string sWindow;
Device* pDevice;
VideoStream* pDepthStream;
 
CDevice( int idx, const char* uri )
{
pDevice = new Device();
pDevice->open( uri );
 
pDepthStream = new VideoStream();
pDepthStream->create( *pDevice, SENSOR_DEPTH );
 
// create OpenCV Window
stringstream mStrStream;
mStrStream << "Sensor_" << idx << endl;
sWindow = mStrStream.str();
cv::namedWindow( sWindow.c_str(),  CV_WINDOW_AUTOSIZE );
 
// start
pDepthStream->start();
}
};
 
int main( int argc, char **argv )
{
// Initial OpenNI
OpenNI::initialize();
 
// enumerate devices
Array<DeviceInfo> aDeviceList;
OpenNI::enumerateDevices( &aDeviceList );
 
// output information
vector<CDevice> vDevices;
cout << "There are " << aDeviceList.getSize()
<< " devices on this system." << endl;
for( int i = 0; i < aDeviceList.getSize(); i )
{
cout << "Device " << i << "\n";
const DeviceInfo& rDevInfo = aDeviceList[i];
 
cout << " " << rDevInfo.getName() << " by " << rDevInfo.getVendor() << "\n";
cout << " PID: " << rDevInfo.getUsbProductId() << "\n";
cout << " VID: " << rDevInfo.getUsbVendorId() << "\n";
cout << " URI: " << rDevInfo.getUri() << endl;
 
// initialize
CDevice mDevWin( i, aDeviceList[i].getUri() );
vDevices.push_back( mDevWin );
}
 
while( true )
{
for( vector<CDevice>::iterator itDev = vDevices.begin();
itDev != vDevices.end(); itDev )
{
// get depth frame
VideoFrameRef vfFrame;
itDev->pDepthStream->readFrame( &vfFrame );
 
// convert data to OpenCV format
const cv::Mat mImageDepth( vfFrame.getHeight(), vfFrame.getWidth(),
CV_16UC1,
const_cast<void*>( vfFrame.getData() ) );
 
// re-map depth data [0,Max] to [0,255]
cv::Mat mScaledDepth;
mImageDepth.convertTo( mScaledDepth, CV_8U,
255.0 / itDev->pDepthStream->getMaxPixelValue() );
 
// show image
cv::imshow( itDev->sWindow.c_str(), mScaledDepth );
 
vfFrame.release();
}
 
// check keyboard
if( cv::waitKey( 1 ) == 'q' )
break;
}
 
// stop
for( vector<CDevice>::iterator itDev = vDevices.begin();
itDev != vDevices.end(); itDev )
{
itDev->pDepthStream->stop();
itDev->pDepthStream->destroy();
delete itDev->pDepthStream;
 
itDev->pDevice->close();
delete itDev->pDevice;
}
OpenNI::shutdown();
 
return 0;
}

這個範例執行後,除了會在命令提示字元,輸出有偵測到的裝置資訊外,也會使用 OpenCV、針對每一個裝置都開啟一個視窗,來顯示深度圖。

當然,這個範例程式在架構上,也還有很大的修改空間,不過反正只是範例而已,所以就先這樣吧~

2 thoughts on “在 OpenNI 2 環境下使用多個 Device”

  1. 博主 请教你个问题 关于两个kinect的 怎么让两个kinect同时跟踪一个人 并且能够实时的更新 我现在用C 写的大循环 运行特别慢 而且有问题 只能一个一个的更新

  2. @峰回路转0835

    不太確定你的程式內容,不過現階段的 NiTE 應該沒辦法在單一程式裡面,使用多個 UserTracker 才對;也就是說,目前應該沒辦法同時用兩個 Kinect 來做追蹤。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *