這一篇,稍微寫一下要怎麼在 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() 的函式,可以取得這個裝置在系統上的連接位置的字串,這個字串可以在呼叫 Device 的 open() 這個函式的時候傳入,指定 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() 回傳的字串,丟給 Device 的 open() 就可以了;而接下來,只要透過開啟好的 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、針對每一個裝置都開啟一個視窗,來顯示深度圖。
當然,這個範例程式在架構上,也還有很大的修改空間,不過反正只是範例而已,所以就先這樣吧~
上面的文章描述有错误,是当连接一台kinect v2的时候,枚举
讚讚
同一個問題麻煩請回覆在同一串回應中,謝謝。
讚讚
Heresy大神你好,
我在学您的这篇文章openni2使用多个devices博客中,尝试运行您的代码,但是一直不成功,能够显示两台kinect v2的信息,不能显示深度影像。(即使只连接一台kienct v2也只能显示信息而不能显示深度影像,但是在您的另一篇文章运用openni2+nite2+opencv能够正常显示)。当连接两台kinect v2时候,我在控制台看到openni2枚举出来的设备数居然是(
***** VIDEOINPUT LIBRARY – 0.1995 – TFW07 *****
[Info] [Freenect2Impl] enumerating devices…
[Info] [Freenect2Impl] 18 usb devices connected
[Info] [Freenect2Impl] found valid Kinect v2 @3:23 with serial 002721261547
[Info] [Freenect2Impl] found 1 devices
[Info] [Freenect2Impl] enumerating devices…
[Info] [Freenect2Impl] 18 usb devices connected
[Info] [Freenect2Impl] found valid Kinect v2 @3:23 with serial 002721261547
[Info] [Freenect2Impl] found 1 devices
Device 0
Kinect by Microsoft
PID: 51536
VID: 57456
URI: freenect2://0
[Info] [Freenect2DeviceImpl] opening…
[Info] [Freenect2DeviceImpl] transfer pool sizes rgb: 3*1048
[Info] [Freenect2DeviceImpl] opened
[Info] [Freenect2DeviceImpl] starting…
[Info] [Freenect2DeviceImpl] submitting rgb transfers…
[Info] [Freenect2DeviceImpl] submitting depth transfers…
[Info] [Freenect2DeviceImpl] started
Device 1
Kinect by Microsoft
PID: 51536
VID: 57456
URI: freenect2://0?depth-size=640×480
Device 2
Kinect by Microsoft
PID: 51536
VID: 57456
URI: freenect2://0?depth-size=512×424)
我的驱动是基于libfreenect2,电脑设备USB能够看到两个USB控制器。
我的电脑配置:
电脑型号 戴尔Precision M6800笔记本
处理器 Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz
内存容量 32.0GB
显卡 1、NVIDIA Quadro K3100M
2、Intel(R) HD Graphics 4600
硬盘 1、PLEXTOR PX-128M6S+ (128GB)
2、HGST HTS545050A7E380 (500GB)
主板 0XD1M5 (A00)
网卡 1、Intel(R) Ethernet Connection I217-LM
2、Dell Wireless 1550 802.11ac
声卡 1、Realtek High Definition Audio
2、NVIDIA High Definition Audio
3、蓝牙音频
显示器 LGD:da02 分辨率:1920×1080
当前操作系统 Windows 8.1 64位
我的最终目标是想通过nite2获取人体骨骼点,但是想要这么做必须通过openni2枚举多个设备,再通过设备URI来控制每一台kinect v2启动顺序。希望您能够在百忙之中抽出一点时间。指点一下我。
讚讚
Heresy 並沒有使用 libfreenect2 的經驗,所以不能確定你的問題。
但是,基本上 Kinect v2 並不能給 NiTE 2 使用。
讚讚
可是我根据您的代码,NiTE2 是可以利用 kinect v2 来获取到骨骼关节数据的啊。驱动是基于libfreenect2的。不知道多台联合之后怎么样?
讚讚
請閱讀 NiTE 的授權協議,裡面有明確地說明,NiTE 不能搭配非 PrimeSense 的感應器使用。
而 libfreenect2 基本上應該是有在內部做加工,才讓他可以的。但是儘管如此,這樣的使用方法,還是違反授權協議的。
另外,在使用 Kinect for Windows SDK v2 的時候,單一台電腦僅能連結一台感應器,無法同時連接多台裝置。
讚讚
谢谢Heresy您的回复!我是现在只是课题需要,所以可能会用nite2来开发。
讚讚
這邊再稍微補充幾點:
1. 雖然沒用過 libfreenect2,但是以紀錄來看,他應該是把單一感應器模擬成兩個裝置,兩個的深度解析度不一樣;其中「freenect2://0?depth-size=640×480」這個是給 NiTE 用的,之所以要修改大小為 VGA,是因為 NiTE 的限制。
2. NiTE2 無法在單一台電腦上建立兩個各自獨立的 UserTracker,所以就算接了兩台感應器、而且可以使用,還是沒辦法同時使用兩台感應器來追蹤人體。
讚讚
我也遇到一样的问题,两个同型号的相机装在不同位置,使用openni启动时如何区分??
讚讚
大概只能靠 URI 或是透過屬性去要序號看看了。
https://github.com/OpenNI/OpenNI2/blob/master/Include/OniProperties.h#L33
讚讚
Hersey大大,话说取得的这两个stream应该是分开的吧QAQ 怎么来分开这两个stream呢。。。感觉如果能分开这两个stream就能分别录影成功了吧QAQ
讚讚
兩個不同的 device 的 stream 本來就是分開的,沒有怎麼分開的問題。
另,麻煩請把同樣的問題放在同一串回應裡,以方便管理。
讚讚
Hersey大神,窝按照Hersey大大的范例自己修改了一下,想用OpenNI2里面自带的recorder同时录制这两个摄像头的深度图像,但是怎么修改都没有成功,现在是能创建两个.oni的文件,但是都只有2KB,打开也没有画面 心好碎QAQ 有点绝望,想问问大大有可能实现嘛,同时录制一个程序里面接的两个摄像头画面?QAQ
讚讚
抱歉,沒有測試過這樣使用,所以不確定是什麼問題。
不過,你有測試過單一個錄影是正常的嗎?
建議先把你的程式改成只錄單一台試試看。
讚讚
感谢大大的回复!
恩恩 之前学习录影功能的时候测试过一个是正常录影的。我现在是把录影的程式放在了CDevice里面pDepthStream->start(); 的下面,用recorder.attach( pDepthStream[0] ); 结果还是录不了,只有一个2KB的文件QAQ
stringstream filename;
string wfilename;
filename<< "Stream_"<< idx << ".oni";
wfilename = filename.str();
openni::Recorder recorder;
recorder.create(wfilename.c_str() );
recorder.attach( pDepthStream[0] );
recorder.start();
讚讚
如果你是把這段程式加在 CDevice::CDevice() 裡的話,那錄不東西應該是正常的。
請注意你的變數的生命週期,照你的寫法,recorder 的物件在離開 CDevice 的建構子後就已經消失了。
讚讚
窝也大概感觉到是这个问题了,但是stream的定义又是在CDevice里面的,换成全局变量的话也不对。果然整个架构上要重新改写吗?QAQ
(抱歉呀大大 窝不怎么上网留言不太明白规则什么的,回复了好几格抱歉呀,窝有办法删除什么的吗?QAQ添麻烦了真抱歉啊。。。)
额还有窝之前指的分开那个stream是指能用文字表示区分比如stream[0]和stream[1]什么的。QAQ
讚讚
個人建議,麻煩仔細看一下範例的程式,確認裡面到底在做什麼。
這麼範例程式這樣的架構,本身就是對應多裝置而設計的,每一個 CDevice 的物件,都對應一個實體感應器;如果在系統偵測到兩個感應器,就會建立出兩個獨立的 CDevice 型別的物件。
裡面的 pDevice 和 pDepthStream 也都是各自獨立、不會彼此干擾的。
另外,也請注意,這邊的 pDepthStream 是指標、不是陣列。
讚讚
是的昨天重新好好把流程图大概画了一下,又把C++的class部分学习了一遍,总之现在是成功了,感谢大神点拨了呀!添麻烦了不好意思呀,以后也会常来支持的!>.<
讚讚
天啦噜原来是这么回事,之前查了好久都没有成功啊!!!太谢谢Heresy大神了!!!话说之前都是潜水学的都没给大神回复过感到好内疚啊!QAQ 以后也要每天来支持大神!真是太感谢了!!!
讚讚
Heresy大神你好,我一直根据你的博客学习OpenNI2的使用方法,都很好的实现了,可是这篇运用多个device的,我却一直运行不成功,能显示两台Xtion的信息,却不能显示深度影像,如果可以的话,希望能够得到大神的些许指点呦QAQ
讚讚
請確認兩台感應器沒有接在同一個 USB 控制器下。
因為 Xtion 所需的頻寬不低,如果接在同一個 USB 控制器下,可能會有問題。
基本上,能做的事就是多換幾個 USB port 試試看。
讚讚