這一篇基本上是延續上一篇《在 OpenNI 管理多個裝置》,繼續來研究怎麼在 OpenNI 的環境裡,同時使用一台電腦上的多個裝置了~由於開始要寫的時候,Heresy 才發現自己也有不確定的地方,所以索性就到 OpenNI 官方論壇去發問了;而結果,也有 PrimeSense 的人幫忙給了段範例,有興趣的人可以參考《Problem with using multiple device》這篇的回應。
根據這邊的範例程式,可以看的出來,這邊的基本概念,是使用同一個 xn::Context,先透過列舉出來的 xn::NodeInfo 個別建立出 xn::Device 後,再以 device 的 instance name 作為 query 的條件(xn::Query)、以此來建立對應特定 device 的其他 production node 了~
而為了顯示上的方便,這邊 Heresy 一樣是使用 Nokia Qt 來做圖形界上面上的顯示,這部分的程式,建議請參考之前的《使用 Qt GraphicsView 顯示 OpenNI 影像資料》一文。程式在執行後,會去列出所有電腦裡的 OpenNI 相容裝置,針對每一個 Device 都開一個 Qt 的 Graphics View 的視窗來同時顯示 Color Generator 的彩色影像和 Depth Generator 的深度影像;像下圖就是 Heresy 電腦上,有連接兩台 Kinect 時的結果(理論上可以更多,但是就沒試過)。
而完整的程式碼,可以到 Heresy 的 SkyDrive 上下載。接下來,就開始做簡單的說明吧~
首先,這個程式裡除了 main() 以外,Heresy 還定義了兩個 Class,分別是 QOpenNIWindows 和 CDeviceWindow。前者主要的目的,是用來做 OpenNI 的 context 管理,以及 QT 的 timer 的控制(定時去更新畫面);後者則是對應到個別的 Device,主要用來記錄 OpenNI 的各種 Nodes、並且處理影像的實際更新。
CDeviceWindow
首先,先來看一下 CDeviceWindow 這個類別的內容。他基本上是用來對應 OpenNI 環境裡的個別的單一個 device(Heresy 這邊就是 Microsoft Kinect),所以裡面儲存了這個裝置的、各種有用到的 node;在這裡,就是 mDevice(device node)、mDepth(depth generator)和 mImage(image generator)。
此外,為了要使用 Qt 的 Graphics View 來做視覺上的呈現,所以也還有 qScene 和 qView 等其他的 member data,這些就等有用到再講了。
不過,這部分主要是 OpenNI 和 Qt 之間的連動,如果只是想看怎麼樣在 OpenNI 裡面同時使用多個裝置的話,可以跳過這一段,直接跳到下面 QOpenNIWindows 的部分。
而下面,就是 CDeviceWindow 這個類別的內容:
// class to handle OpenNI Device and Qt Window class CDeviceWindow { public: // constructor CDeviceWindow(); // destructor ~CDeviceWindow(); // Update Qt graphic image from OpenNI device bool UpdateImage(); public: xn::Device mDevice; // OpenNI Device Node xn::DepthGenerator mDepth; // OpenNI Depth Generator xn::ImageGenerator mImage; // OpenNI Image Generator XnDepthPixel mMaxDepth; // Keep max. depth value QGraphicsScene qScene; // scene of Qt QGraphicsView qView; // graphics view of Qt private: unsigned char* pTmpDepthImage; // temporary 8bit depth image xn::ImageMetaData mColorImage; // RGB Color image from OpenNI xn::DepthMetaData mDepthImage; // Raw depth image from OpenNI QGraphicsPixmapItem qColorImage; // color image in Qt format QGraphicsPixmapItem qDepthImage; // depth image in Qt format };
其中,建構子的部分,主要是進行 Qt 環境的設定,不算是本文的重點,所以在這邊就先跳過了;有需要的話,請參考之前的《使用 Qt GraphicsView 顯示 OpenNI 影像資料》。
而實際上,CDeviceWindow 主要的功用除了資料的儲存外,就是 UpdateImage() 這個函式了~在這個函式裡,會去讀取 OpenNI Image Generator(mImage)和 Depth Generator(mDepth)的資料,並把她轉換為 Qt 所需要的格式、拿來顯示。其程式碼內容如下:
// Update Qt graphic image from OpenNI device bool UpdateImage() { if( !qView.isVisible() ) return false; // 1. get color image mImage.GetMetaData( mColorImage ); qColorImage.setPixmap( QPixmap::fromImage( QImage( mColorImage.Data(), mColorImage.XRes(), mColorImage.YRes(), QImage::Format_RGB888 ) ) ); // 2. get depth image mDepth.GetMetaData( mDepthImage ); // 2a. create 8 bit depth image unsigned int uSize = mDepthImage.XRes() * mDepthImage.YRes(); if( pTmpDepthImage == NULL ) pTmpDepthImage = new unsigned char[ 4 * uSize ]; for( unsigned int i = 0; i < uSize; ++ i ) { unsigned char ucValue = 0; if( mDepthImage[i] != 0 ) ucValue = 255 * ( mMaxDepth - mDepthImage[i] ) / mMaxDepth; pTmpDepthImage[ 4 * i ] = 0; // blue pTmpDepthImage[ 4 * i + 1 ] = ucValue; // green pTmpDepthImage[ 4 * i + 2 ] = ucValue; // red pTmpDepthImage[ 4 * i + 3 ] = ucValue; // alpha } // 2b. update image in Qt qDepthImage.setPixmap( QPixmap::fromImage( QImage( pTmpDepthImage, mDepthImage.XRes(), mDepthImage.YRes(), QImage::Format_ARGB32 ) ) ); return true; }
在上面的程式碼中,第一部分就是先透過 xn::ImageGenerator 的 GetMetaData() 函式,取得包含 metadata 的彩色影像資料,然後再把它轉型成 Qt 的 QImage、建立 QPixmap,然後讓 qColorImage 來使用。
由於 image generator 的資料格式基本上就是一般的 RGB888 的格式,所以這邊可以很方便、不用做特殊的處理,就直接把得到的資料拿來使用。
但是由於 Depth Generator 的深度資料不是一般的影像形式,所以就必須要先進行轉換,把它轉換成 8bit 的圖;而為了讓彩色的圖和深度圖可以疊在一起,所以 Heresy 這邊是把 depth map 轉換為帶有透明度(alpha)的 ARGB32 的形式。而在這裡,pTmpDepthImage 就是用來儲存轉換出來的結果的,由於是 ARGB 的形式,所以他的資料量會是點數的四倍。
在把深度資料轉換成 8bit 的計算,Heresy 這邊是用很簡單的算法:
ucValue = 255 * ( mMaxDepth - mDepthImage[i] ) / mMaxDepth;
也就是先記下 depth generator 的最大深度值(mMaxDepth,可透過 depth generator 的 GetDeviceMaxDepth() 取得),然後以此把每一個像素的資料,都以線性調整的方法,轉換成 0 – 255 之間的值。(註 1)
而當處理完成整張深度圖後,接下來就可以和處理彩色影像時一樣,把它轉換成 qDepthImage 可以使用的 QPixmap 的形式了。
QOpenNIWindows
由於在這個程式裡,Heresy 只用了一個 xn::Context 來做 OpenNI 的管理,所以這邊也建立了一個 QOpenNIWindows 的類別來對應,作為 OpenNI context 以及所有 device 的統一管理;基本上,他在程式裡只會有一個物件,而在 main() 裡,也只會對 QOpenNIWindows 這個類別的物件進行操作,不會直接碰到內部的 CDeviceWindow 。
而實際上在 OpenNI 裡,控制控制電腦同時去存取多個裝置的程式,也就是這一部分。
他的內容基本上如下:
// class for OpenNI context and qt windows manager class QOpenNIWindows: public QObject { public: // destructor ~QOpenNIWindows(); // initial OpenNI, enumerate device bool Initial(); // create OpenNI nodes and Qt window bool CreateDeviceAndWindow(); // start timer to update image bool Start( int iInterval = 33 ); private: XnStatus m_eResult; xn::Context m_xContext; xn::NodeInfoList m_xDeviceList; vector<CDeviceWindow*> m_vDevWinList; private: bool CheckOpenNIError( const string& sStatus ); // timer to update data void timerEvent( QTimerEvent *event ); // release OpenNI nodes and qt resource void Release(); };
QOpenNIWindows 為了使用 QT 裡的 QObject::timerEvent() 來做定時的更新(註 2),所以必須繼承 QObject;而在 member data 的部分,除了 m_xContext 是 OpenNI 的 xn::Context 外,還包含了透過 EnumerateProductionTrees() 所列舉出來的 device 的資訊列表 m_xDeviceList,以及自己建立出來的 CDeviceWindow 清單:m_vDevWinList。
而在 member function 的部分,Initial() 就是在進行 OpenNI 環境的初始化、並且透過 xn::Context 提供的 EnumerateProductionTrees() 這個函式,進行裝置的查詢,並將其結果儲存於 m_xDeviceList 內。其程式碼內容如下:
// initial OpenNI, enumerate device bool Initial() { // 1. initial context m_eResult = m_xContext.Init(); if( CheckOpenNIError( "Context initialization" ) ) return false; // 2. Enumerate Device m_eResult = m_xContext.EnumerateProductionTrees( XN_NODE_TYPE_DEVICE, NULL, m_xDeviceList ); if( CheckOpenNIError( "Enumerate device" ) ) return false; // 3. check if there is device existed if( m_xDeviceList.IsEmpty() ) { cerr << "No OpenNI device found" << endl; return false; } return true; }
而 CreateDeviceAndWindow() 所做的事,就是去把 m_xDeviceList 裡的每一個 device node 都建立出來,並建立出對應的 depth generator 以及 image generator;最後,則是以 CDeviceWindow 的形式,儲存在 m_vDevWinList 裡,拿來做之後的操作、管理。
他的程式碼如下:
// create OpenNI nodes and Qt window bool CreateDeviceAndWindow() { // check if there is already Device window existed if( !m_vDevWinList.empty() ) { Release(); } // create nodes and window for each device for( xn::NodeInfoList::Iterator itDev = m_xDeviceList.Begin(); itDev != m_xDeviceList.End(); ++ itDev ) { CDeviceWindow* pDevWin = new CDeviceWindow(); xn::NodeInfo mNodeInfo = *itDev; // 1. create and initial OpenNI nodes { // a. create device m_eResult = m_xContext.CreateProductionTree( mNodeInfo, pDevWin->mDevice ); if( CheckOpenNIError( "Create Device" ) ) break; // b. build query rule for given device xn::Query mQuery; mQuery.AddNeededNode( mNodeInfo.GetInstanceName() ); // c. create depth generator m_eResult = pDevWin->mDepth.Create( m_xContext, &mQuery ); if( CheckOpenNIError( "Create Depth Generator" ) ) { pDevWin->mDevice.Release(); delete pDevWin; break; } pDevWin->mMaxDepth = pDevWin->mDepth.GetDeviceMaxDepth(); // d. create image generator m_eResult = pDevWin->mImage.Create( m_xContext, &mQuery ); if( CheckOpenNIError( "Create Image Generator" ) ) { pDevWin->mDepth.Release(); pDevWin->mDevice.Release(); delete pDevWin; break; } // e. Set Alternative View Point pDevWin->mDepth.GetAlternativeViewPointCap().SetViewPoint( pDevWin->mImage ); } // 2. Qt Setting pDevWin->qView.setWindowTitle( mNodeInfo.GetInstanceName() ); pDevWin->qView.show(); m_vDevWinList.push_back( pDevWin ); } // check result if( m_vDevWinList.empty() ) return false; return true; }
這邊的工作,就是先透過 iterator 去掃過整個 m_xDeviceList,針對裡面的每一個 NodeInfo 來做處理;而最主要的部分,自然就是針對 NodeInfo 來建立對應的 OpenNI production node 了~
首先,在 a 的部分,這裡還是一樣使用 xn::Context 的 CreateProductionTree() 這個函式,透過指定 xn::NodeInfo 的方法,來建立出所需要的 device node(pDevWin->mDevice)。
而在 device node 建立完成後, 接著就是使用他的 instance name,來做為後續其他 production node 的使用條件。所以在這邊是先建立一個 xn::Query 的物件 mQuery、代表一個查詢的條件;然後再透過他的 AddNeededNode() 函式,要求「一定要使用給定的 node」,而這邊的判斷條件,就是靠 NodeIfno 的 instance name 了~
接下來在 c、d 的部分,就是要建立 depth generator 和 image generator 的 production node 了。雖然在論壇上的範例是使用 xn::Context 的 CreateAnyProductionTree() 這個函式來建立 production node,不過實際上每個 node 都有的 Create() 函式,也是可以透過附加指定 xn::Query 的方法,來做到有條件的 production node 的建立的~它的用法就是:
pDevWin->mDepth.Create( m_xContext, &mQuery );
也就是在呼叫的時候,加上第二個參數,把設定好的 xn::Query 的物件的指標傳進去,這樣他就會去 m_xContext 裡面尋找、建立符合條件的 node 了~(註 3)
而當 OpenNI 的這些 node 都建立完成後,接下來就是進行 Qt 的最後設定(拿 Instance name 當視窗標題)、並且把視窗顯示出來了~
當然,為了之後的操作與管理,這裡也把建立出來的 CDeviceWindow 物件(pDevWin),存放到 m_vDevWinList 內。
而最後,就是設定 Qt 的 timer,讓程式可以自動去更新畫面了~前面已經有提到了,Heresy 在這邊是使用 QT 的 QObject::timerEvent() 來實作這一部分的。
首先,是 Start() 這個函式。這個函式主要是給外部控制,開始 OpenNI 以及 Qt 的更新用的(目前沒有寫 stop),內容主要就是在呼叫 OpenNI context 的 StartGeneratingAll() 後,再去呼叫 Qt QObject 的 startTimer(),來要求 Qt 在每隔一段時間(iInterval、單位毫秒)後,就自動去執行 timerEvent() 這個 callback function,以進行更新。
由於做的事不多,所以 Start() 的內容也不長,它的原始碼就只有下面這幾行:
// start timer to update image bool Start( int iInterval = 33 ) { // start generating OpenNI data m_eResult = m_xContext.StartGeneratingAll(); if( CheckOpenNIError( "Start Generating" ) ) return false; // start Qt timer startTimer( iInterval ); return true; }
而在 timerEvent() 的部分,它就是實際上每隔一段時間,就會被自動執行的程式了~他所做的是有兩件,第一個是去呼叫 OpenNI Context 的 WaitAndUpdateAll()、要求 OpenNI 更新所有 production node 的資料。等到資料都更新完後,接下來就是 iterator 掃過整個 m_vDevWinList,呼叫每個建立出來的 CDeviceWindow 的 UpdateImage() 函式、以進行視窗畫面的更新。
// timer to update data void timerEvent( QTimerEvent *event ) { // request OpenNI to update data m_eResult = m_xContext.WaitAndUpdateAll(); if( CheckOpenNIError( "Update data" ) ) return; // update data for each device and window for( vector<CDeviceWindow*>::iterator itDev = m_vDevWinList.begin(); itDev != m_vDevWinList.end(); ++ itDev ) (*itDev)->UpdateImage(); }
最後,main() 的部分呢?由於主要的工作都已經分到 QOpenNIWindows 和 CDeviceWindow 了,所以這裡的內容也很簡短,就只有下面幾行。
// Main function int main( int argc, char** argv ) { // create Qt Application QApplication App( argc, argv ); QOpenNIWindows mNIWindows; if( mNIWindows.Initial() ) { if( mNIWindows.CreateDeviceAndWindow() ) { mNIWindows.Start(); // start! return App.exec(); } } return -1; }
主要,就是先建立 Qt 的 QApplication 環境,然後再建立 QOpenNIWindows 物件,用來建立出 OpenNI 的環境;接下來則是依序呼叫 QOpenNIWindows 的 Initial()、CreateDeviceAndWindow(),以建立 OpenNI 完整環境、並針對每一個 device 都建立一組 production node 以及 Qt 視窗。
最後,就是呼叫 QOpenNIWindows 的 Start()、開始 OpenNI 的資料更新,並呼叫 QApplication 的 exec()、進入 Qt 的主迴圈、開始執行整個程式了~
而以 Heresy 這邊接了兩台 Kinect 的狀況、執行起來後,就會出現兩個視窗、個別顯示兩台 Kinect 的彩色和深度影像了(如最上方的圖)~不過要注意的是,Heresy 沒有刻意去控制視窗出現的位置,所以一開始兩個視窗可能會是重疊的,請自己拉開。 ^^"
另外,由於現在要處理兩台 Kinect 的資料,所以在 USB 控制器的頻寬、以及 CPU 的使用上,也會比較兇,有可能會覺得畫面更新頓頓的。理論上,這邊部分的程式是可以透過平行化來做加速的,只是這邊還只是範例,所以就先不考慮到這些地方了~
附註:
-
把深度圖轉換成 8bit 的方法有很多種,可以視自己的需要做修改。像 Heresy 之前的方法,就是每個畫面都各自去找最大值,然後再做線性調整,效率會比較差一點。
-
QObject::timerEvent() 的詳細說明請參考官網。
雖然 Qt 本身也有提供更高階的 QTimer 可以用(官方介紹),但是由於一定得搭配 Qt 自己的 signal / slot 來使用,會比較麻煩,所以這邊不採用這個方案。
-
在 CreateDeviceAndWindow() 裡建立 image generator 和 depth generator 時(c、d),Heresy 都加上了失敗時的錯誤處理(CheckOpenNIError() 的部分),主要是用來把已建立的資源釋放掉。
您好,请问如果使用两台kinect从不同位置同时获取同一个人的深度图像,会有干扰么?
讚讚
由於 Kinect 是用紅外線的光斑來做深度的偵測的,所以當投影範圍有所重疊時,或多或少會增加深度影像的雜訊。
讚讚
先谢谢你,还想问一下是否可以通过两个kinect从不同角度来解决遮挡问题,这个想法从理论上是否可行?
讚讚
理論上可以,但是就是得要自己處理了。
讚讚
好的,谢谢你。
讚讚
Hello Heresy,
非常謝謝您寫了這麼詳細的文章。
不過因為小弟對Qt實在很不熟,所以目前正在嘗試把這篇的Qt和OpenNI分開,
但是實力淺薄,寫一寫就寫不下去了,故想請問Heresy大有沒有不是使用Qt的sample code能參考呢?
麻煩您了!
讚讚
本文的範例也僅只是把 QT 當作範例框架,內容應該也有把在 OpenNI 環境下使用多裝置的步驟做說明了,有必要的話,應該是可以自行套用到其他圖形介面框架的。
不知道你的問題是卡在哪裡?
讚讚
目前我的目標是想要取至少兩台Kinect的深度及彩色影像,然後用xnRecorder錄下來,所以想不去用Qt的class或library,故CDeviceWindow我就完全沒有用這樣。
對這一篇文我個人的理解是,在QOpenNIWindows的class定義那裡,繼承了一個QObject,而在接下來還有new一個pDevWin,且之後就是用pDevWin去create device和generator還有set alternative view point。
應該大部分的問題就是卡在pDevWin不知道該怎麼替換掉,所以不知如何是好‧‧‧
一來我的理解不確定是否正確,二來也是程式能力太差,不知道怎麼變通,還請Heresy大能給點建議!
非常非常感激~
讚讚
你的理解基本上是對的。
而如果要把 Qt 抽掉,第一個要做的,應該就是 CDeviceWindow 裡面的 Qt 相關物件抽掉。
當然,由於這個類別是對應每一個裝置、建立一個視窗來顯示,所以如果把 Qt 的部分抽掉,就沒辦法顯示了;如果要顯示的話,就需要替換成自己要用的 GUI framwork。
再來,文章中也有提到了,QOpenNIWindows 之所以繼承 QObject,主要是為了做 timer 之用;所以不用 Qt 的話,就必須要用其他方式來定時更新每個 CDeviceWindow 的資料,也就是去呼叫他們的 UpdateImage() 這個函式。
大致上應該是這樣吧。
讚讚
好的,先謝謝您
我再試試!
讚讚
Heresy 先生,我前几天在网上下载了你的程序。
我在运行的时候,将两台kinect再插上电脑,显示驱动正常(绿指示灯亮),然后我运行QTMultiDevice.exe,结果发现只有一个kinect在工作,另外一个虽然驱动正常,但并没有图像输入(比如A有图像显示,然后B没有输入图像)。我在尝试了几次之后还是这样,不知道是哪里出了问题,之后我看了你的程序,没有发现问题,请问这是什么原因呢?谢谢
讚讚
個人建議先測試看看:
1. 兩台單獨用是否都沒問題?
2. 裝置管理員內是否有裝置有異常?
3. 有沒有確認兩台是否接在同一個 USB 控制器上?有試過更換 USB port 嗎?
讚讚
非常感谢你的回复,我们现在已经成功了,不过接三个的时候还是出了问题,不过会继续尝试下去的
讚讚
所以之前的問題是?
讚讚
之前的问题是,我的电脑USB端口有问题,并不是所有的端口都是独立的,所以接了两个kinect之后会出问题。换了电脑之后就可以运行了
讚讚
heresy先生,我现在同时接三个kinect之后,发现在电脑的 设备管理器 里面只发现了 两个camera,两个audio,还有三个motor,后来总结了一下,存在的问题有可能是一个usb controller 下面只能接一个 kinect,所以接几个kinect就直接决定于电脑有几个usb controller,而不是有几个usb port
讚讚
這個是有可能的。官方的確有說,可能需要將感應器接在不同的 USB 控制器上。
讚讚
heresy先生,之前忘了给你回复了,一个电脑能够接几个kinect是由电脑有几个usb root hub决定的,并不是有几个usb端口决定的,有可能是因为kinect对于通信带宽的要求比较高,所以如果多个kinect共享一个root hub带宽就不够了,这是我的理解
讚讚
理論上的確是根據有幾個 USB 控制器,來決定可以用幾個裝置。
讚讚
Hi Heresy
小弟目前在連接2台Xtion時,會有1台驅動程式安裝失敗的狀況。如果使用USB3.0擴充卡,連1台都會驅動程式安裝失敗。目前能成功安裝2台Xtion的電腦只有Apple的iMac。請問您是在什麼環境下安裝好2台的呢?有沒有什麼建議?謝謝您的回覆。
Roger
讚讚
請到裝置管理員裡面,選擇「檢視」裡面的「裝置(依連線)」,這樣可以看得出裝置的階層關係。
讚讚
Heresy您好
之前一直打扰您,学习到了很多东西,非常感谢。
一直参考您的文章,我已经大致完成了代码,能够运行,但是无论如何也无法显示点阵云图像。我只能大概认为是读取实时数据方面出现了问题。我自己也在网上搜索了很久,但是大部分都是使用OpenKinect进行开发的。由于您之前的文章也有用OpenGl显示点阵云的内容,虽然很冒昧,但是如果可以的话,能否百忙中抽空阅读下我的代码,帮我指出一下改进方向呢?
代码链接如下http://sdrv.ms/QvztZ2
实在不好意思,非常感谢。
讚讚
如果你是要用 OpenGL 來繪製 OpenNI 資料的範例,請參考之前課程的範例程式
https://kheresy.wordpress.com/2012/08/26/resource-of-openni-course-update/
03_GLPointCloud 就是最基本用來畫 point cloud 的程式了
讚讚
Heresy大大您好
我在單一設備的情況下,能夠用OpenGL描繪出點陣云。但是將代碼修改成對應多設備的時候就會出現問題。代碼參考了google OpenNI討論組內的資料,我建立了一個struct MultipleKinect,并建立了它的對象kinects[NUM_OF_SENSORS],裏面包含了kinect需要的NODE,枚舉出所有硬件並且建立了所有NODE后,在GetMetaData環節出現了問題。例如kinects[1].mImage.GetMetaData(kinects[1].mImageMD)之後,再memcpy(),最後無法顯示出最基本的圖像。處於無法讀取kinect的實時數據的狀態。無論如何修改,還是出現同樣的問題。我已經困擾了近一周,完全找不到任何頭緒,不知道大大能否提供一些建議。多次打擾,非常感謝您的細心回覆。
讚讚
你的錯誤訊息是什麼?
另外,如果你的程式是放在 SkyDrive 的那份的話,Heresy 會覺得他的架構很怪。
裡面名為 update() 的函式只有在初始化後執行過一次,而之後都沒有再呼叫過了?感覺名稱和呼叫的時機完全不同。
但是程式碼裡,卻又進行了全部的原始碼裡面、唯一一個呼叫 WaitAnyUpdateAll()、要求進行更新的指令。而且讀取資料的程式碼也在這裡,變成你重頭到尾都只有去讀取一次資料而已。
實際上這個程式完全沒有更新資料 Heresy 也會覺得是正常的…
感覺上,這個函式的用途,應該還是要每次畫之前都得呼叫吧?
但是另一方面,OpenNI 的 Production Node 都是建立一次就好的東西,你卻是把它放在 update() 這個函式裡面…
Heresy 個人是建議程式的架構最好重新整理一下,確認你的 update() 這個函式到底是要做什麼事。
另外,glut 多視窗的時候,你可能會要用到這個函式
http://www.opengl.org/resources/libraries/glut/spec3/node18.html
讚讚
Heresy大大您好
由於您的建議,代碼總算能成功運行了。兩台kinect都能正確的在兩個窗口內表示點陣云的圖像了。由於我才剛剛開始接觸編程,之前雜亂無章的代碼讓你見笑了,真是不好意思。另外還有些事情想請教一下,現在OPENGL描繪出來的東西,只是在點上附加顏色而已。我想能在這些點的基礎上進行重建多邊形,請問Heresy大大有沒有相關的較為簡單的範例能讓我參考一下呢?真是非常感謝~~~
讚讚
要建立多邊形的方法有很多種,比較好的方法通常還要再加上適度的簡化、補洞。
不過基本上最簡單的方法,就是直接根據 dpeth map 是 regular grid 的性質,直接把相鄰的點連起來。
在前面提到的課程範例裡面,就有對應的範例程式了。
讚讚
想請問一下…該例子中…
有段內容如下…
#include “../common/OpenGLCamera.h"
這段標頭檔…我該去哪下載呢?還是有我沒看仔細之處?謝謝囉!~
讚讚
如果你是指之前的課程範例的話,Heresy 有提供整個大的方案,裡面就包含這個檔案了。
就在它的相對路徑的地方。
讚讚
Heresy 您好,
我將您此篇文章與"OpenNI 的 User Generator",這兩篇的文章中功能作結合,即使用多台kinect來進行人體偵測並去背,但是第一台的影像遮罩會疊到第二台裝置內,該如何將第一台所抓到的影像遮罩釋放,而不會顯示在第二台上呢?
讚讚
抱歉,不知道你的遮罩是什麼?
如果是指 User Generator 的偵測結果的話,目前 NITE 所提供的 user generator 應該是有問題,無法在同一個程式裡面正確地使用多個的 user generator。
讚讚
Heresy大大您好
小弟我從您的bolg上學到了很多知識,獲益良多!
我現在在弄一個利用複數kinect來讀取物體360度影像的程序,一直在讀您這篇文章來參考。
但是我現在有一個問題有些想不明白,您的這篇文章是用枚舉的方法來構築複數kinect的工作環境,我想請問下:在複數kinect的環境下,能不能對特定的一台kinect指定工作?光是枚舉出來的話好像不能對單獨的裝置進行操作的樣子。
百忙之中打擾了,萬分感謝~
CC
讚讚
這篇文章的方法裡建構出多個 Kinect 後,就是多個 CDeviceWindow 的物件,可以個別進行操作了。在 CDeviceWindow 裡對於一個裝置,都會有一個獨立的 Depth Generator、Image Generator 可以各自使用。
這邊雖然是用一個 QOpenNIWindows 來管理不同的 CDeviceWindow 物件、讓他們都做一樣的動作,但是有需要的話,也是可以自己去個別做控制的。
讚讚
非常感謝您的回覆~
也就是說從DeviceList里讀取再指定設備咯。設備名就是您那篇《OPENNI環境中管理多個裝置》中顯示的Device1 Device2嗎?冒昧的問一下,您是否有這種單獨指定設備進行不同工作的範例呢?不好意思一直打攪你,非常感謝。
讚讚
OpenNI 的 Production node 的名稱,是在建立後會由系統產生的,請透過 GetInstanceName() 來取得。
至於做不同的工作…
基本上,就是建立出不同的 production node,個別控制了。重點是你要做什麼吧?
以 Map Generator 來說,他們基本上也就只能讀取影像資料而已,似乎也沒有什麼可以個別控制的。
讚讚
大大·晚上好~
就像您這篇文章的這個例子,顯示了兩台kinect捕捉到的不同的畫面,假如我要對其中一台kinect捕捉到的畫面進行編輯,類似翻轉或者縮放,就需要能指定特定的kinect了。
我現在是利用kinect取得的pointcloud數據用opengl描繪出來,所以希望能對每一台kinect描繪出來的圖像進行個別操作。
再三麻煩您,非常感謝!
讚讚
如同前面所說的,由於最後建立出來的會是多個、對應多個 device 的 depth generator;只要針對不同的 Depth Generator 取出來的資料作個別的處理就可以了。
讚讚
Hello heresy 大大
小弟想問個問題
兩個裝置的userGenerator ,depthGenerator, imageGenerator
都是存在同一個中,還是說一個裝置需要一個generator來接收
不好意思可能您code有寫到,小弟想確認一下!!
(鞠躬
讚讚
文章和程式裡面應該都算是有說明了,不同的 Sensor 都要各自建立對應的 generator。
讚讚
Hi, Heresy,
在使用多個Kinect讀取skeleton的時候遇到的問題。
每次在register callbacks時都會顯示access violation reading location。
請問能否給出使用多個Kinect讀取skeleton的範例呢?
萬分感謝!
– Richard
讚讚
沒弄錯的話,這應該是 NITE 的一個 bug,需要等他修正。
https://groups.google.com/forum/#!searchin/openni-dev/multiple$20user$20crash/openni-dev/GIoqyCNYQRc/Fh9ozUeQPucJ
讚讚