主要内容:
一、设备与驱动准备
最近忙着写论文,已好长时间没瞎写了,这两天偶然看到一篇有关OpenNI2操作两个体感设备的文章,自己复制粘贴运行下看了效果挺好的,所以我大胆的搬过来,和大家分享分享~~~
设备准备:一个ASUS Xtion;一个Kinect for Xbox360;
驱动准备:我是win7下运行的,只要在设备管理器中显示如下效果,就没问题;要是有出现一个显示黄的,那就表示其中有一个显示不了,我目前的做法是:反复拔了再插,插了再拔,直到显示如下效果就OK;
二、代码演示
在之前都是用到一个体感设备进行开发,但也有人拿两个把玩着,所以如何同时让两个都运行起来?
在OpenNI2中提供了一个OpenNI::enumerateDevices函数原型是这样的:
/**
Fills up an array of @ref DeviceInfo objects with devices that are available.
@param [in,out] deviceInfoList An array to be filled with devices.
*/
static void enumerateDevices(Array<DeviceInfo>* deviceInfoList)
{
OniDeviceInfo* m_pDeviceInfos;
int m_deviceInfoCount;
oniGetDeviceList(&m_pDeviceInfos, &m_deviceInfoCount);
deviceInfoList->_setData((DeviceInfo*)m_pDeviceInfos, m_deviceInfoCount, true);
oniReleaseDeviceList(m_pDeviceInfos);
}
英语学的不好,大概的意思是将可使用的divices设备信息保存到Array<DeviceInfo>* deviceInfoList中吧~通过看oniGetDeviceList函数:
/**
* Get the list of currently connected device.
* Each device is represented by its OniDeviceInfo.
* pDevices will be allocated inside.
*/
ONI_C_API OniStatus oniGetDeviceList(OniDeviceInfo** pDevices, int* pNumDevices);
通过Array<DeviceInfo>* deviceInfoList,我们就可以得到DeviceInfo信息操作Device了,其中DeviceInfo包括设备Uri,供应商名,供应商ID,设备Id,设备名:
class DeviceInfo : private OniDeviceInfo
{
public:
/**
Returns the device URI. URI can be used by @ref Device::open to open a specific device.
The URI string format is determined by the driver.
*/
const char* getUri() const { return uri; }
/** Returns a the vendor name for this device. */
const char* getVendor() const { return vendor; }
/** Returns the device name for this device. */
const char* getName() const { return name; }
/** Returns the USB VID code for this device. */
uint16_t getUsbVendorId() const { return usbVendorId; }
/** Returns the USB PID code for this device. */
uint16_t getUsbProductId() const { return usbProductId; }
friend class Device;
friend class OpenNI;
};
有了这些信息就可以看目前连接的设备的信息了,看代码:
int main( int argc, char **argv )
{
// 初始化OpenNI
OpenNI::initialize();
// 获取设备信息
Array<DeviceInfo> aDeviceList;
OpenNI::enumerateDevices( &aDeviceList );
// output information
//vector<CDevice> vDevices;
cout << "电脑上连接着 " << aDeviceList.getSize() << " 个体感设备." << endl;
for( int i = 0; i < aDeviceList.getSize(); ++ i )
{
cout << "设备 " << i << endl;
const DeviceInfo& rDevInfo = aDeviceList[i];
cout << "设备名: " << rDevInfo.getName() << endl;
cout << "设备Id: " << rDevInfo.getUsbProductId() << endl;
cout << "供应商名: "<< rDevInfo.getVendor() << endl;
cout << "供应商Id: " << rDevInfo.getUsbVendorId() << endl;
cout << "设备URI: " << rDevInfo.getUri() << endl;
}
system("pause"); // 编译运行之后可以
OpenNI::shutdown();
return 0;
}
显示效果:
接着在获取Kinect和Xtion信息之后,就可以驱动他们运行了,通过设备Uri驱动,估计大家都懂了,然后获得它们各自的深度信息流数据,再利用OpenCV进行处理,直接上代码:
// MoreDevice.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
// 标准库头文件
#include <iostream>
#include <string>
#include <vector>
// OpenCV头文件
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
// OpenNI头文件
#include <OpenNI.h>
// namespace
using namespace std;
using namespace openni;
class CDevice{
public:
const char* devicename;
Device* pDevice;
VideoStream* pDepthStream;
CDevice( int idx, const char* uri, const char* deviceName)
{
devicename = deviceName;
// 创建Device
pDevice = new Device();
// 打开指定Uri设备
pDevice->open( uri );
// 创建深度数据量
pDepthStream = new VideoStream();
pDepthStream->create( *pDevice, SENSOR_DEPTH );
// 创建OpenCV窗口
cv::namedWindow( deviceName, CV_WINDOW_AUTOSIZE );
// 开始
pDepthStream->start();
}
};
int main( int argc, char **argv )
{
// 初始化OpenNI
OpenNI::initialize();
// 获取设备信息
Array<DeviceInfo> aDeviceList;
OpenNI::enumerateDevices( &aDeviceList );
// 将每个设备信息用CDevice类封装
vector<CDevice> vDevices;
cout << "电脑上连接着 " << aDeviceList.getSize() << " 个体感设备." << endl;
for( int i = 0; i < aDeviceList.getSize(); ++ i )
{
cout << "设备 " << i << endl;
const DeviceInfo& rDevInfo = aDeviceList[i];
cout << "设备名: " << rDevInfo.getName() << endl;
cout << "设备Id: " << rDevInfo.getUsbProductId() << endl;
cout << "供应商名: "<< rDevInfo.getVendor() << endl;
cout << "供应商Id: " << rDevInfo.getUsbVendorId() << endl;
cout << "设备URI: " << rDevInfo.getUri() << endl;
// 封装类初始化,传入设备名和设备Uri
CDevice mDev(i, aDeviceList[i].getUri(), aDeviceList[i].getName());
vDevices.push_back( mDev );
}
while( true )
{
for( vector<CDevice>::iterator itDev = vDevices.begin();
itDev != vDevices.end(); ++ itDev )
{
// 获取深度图像帧
VideoFrameRef vfFrame;
itDev->pDepthStream->readFrame( &vfFrame );
// 转换成 OpenCV 格式
const cv::Mat mImageDepth( vfFrame.getHeight(),
vfFrame.getWidth(),
CV_16UC1,
const_cast<void*>( vfFrame.getData() )
);
// 从 [0,Max] 转为 [0,255]
cv::Mat mScaledDepth;
mImageDepth.convertTo( mScaledDepth, CV_8U,
255.0 / itDev->pDepthStream->getMaxPixelValue() );
// 显示图像
cv::imshow( itDev->devicename, mScaledDepth );
vfFrame.release();
}
// 退出键
if( cv::waitKey( 1 ) == 'q' )
break;
}
// 停止时的操作
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;
}
显示效果:
三、总结
代码挺简单的,我自己碰到的问题主要是xtion和kinect驱动共存的问题,剩下的就好解决了。最后说明的是:根据自己的感觉写代码,没做封装、优化、重构,完全是面向过程,而且肯定还存在细节的问题,会在后面进一步优化的。 写的粗糙,欢迎指正批评~~~