在 OpenNI 環境同時使用多個 Kinect


這一篇基本上是延續上一篇《在 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,分別是 QOpenNIWindowsCDeviceWindow。前者主要的目的,是用來做 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 來做視覺上的呈現,所以也還有 qSceneqView 等其他的 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::ImageGeneratorGetMetaData() 函式,取得包含 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::ContextCreateProductionTree() 這個函式,透過指定 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::ContextCreateAnyProductionTree() 這個函式來建立 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 QObjectstartTimer(),來要求 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,呼叫每個建立出來的 CDeviceWindowUpdateImage() 函式、以進行視窗畫面的更新。

// 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() 的部分呢?由於主要的工作都已經分到 QOpenNIWindowsCDeviceWindow 了,所以這裡的內容也很簡短,就只有下面幾行。

// 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 的環境;接下來則是依序呼叫 QOpenNIWindowsInitial()CreateDeviceAndWindow(),以建立 OpenNI 完整環境、並針對每一個 device 都建立一組 production node 以及 Qt 視窗。

最後,就是呼叫 QOpenNIWindowsStart()、開始 OpenNI 的資料更新,並呼叫 QApplicationexec()、進入 Qt 的主迴圈、開始執行整個程式了~

而以 Heresy 這邊接了兩台 Kinect 的狀況、執行起來後,就會出現兩個視窗、個別顯示兩台 Kinect 的彩色和深度影像了(如最上方的圖)~不過要注意的是,Heresy 沒有刻意去控制視窗出現的位置,所以一開始兩個視窗可能會是重疊的,請自己拉開。 ^^"

另外,由於現在要處理兩台 Kinect 的資料,所以在 USB 控制器的頻寬、以及 CPU 的使用上,也會比較兇,有可能會覺得畫面更新頓頓的。理論上,這邊部分的程式是可以透過平行化來做加速的,只是這邊還只是範例,所以就先不考慮到這些地方了~


附註:
  1. 把深度圖轉換成 8bit 的方法有很多種,可以視自己的需要做修改。像 Heresy 之前的方法,就是每個畫面都各自去找最大值,然後再做線性調整,效率會比較差一點。

  2. QObject::timerEvent() 的詳細說明請參考官網

    雖然 Qt 本身也有提供更高階的 QTimer 可以用(官方介紹),但是由於一定得搭配 Qt 自己的 signal / slot 來使用,會比較麻煩,所以這邊不採用這個方案。

  3. CreateDeviceAndWindow() 裡建立 image generator 和 depth generator 時(c、d),Heresy 都加上了失敗時的錯誤處理(CheckOpenNIError() 的部分),主要是用來把已建立的資源釋放掉。


OpenNI / Kinect 相關文章目錄

對「在 OpenNI 環境同時使用多個 Kinect」的想法

  1. 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() 這個函式。

          大致上應該是這樣吧。

  2. 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带宽就不够了,这是我的理解

  3. Hi Heresy

    小弟目前在連接2台Xtion時,會有1台驅動程式安裝失敗的狀況。如果使用USB3.0擴充卡,連1台都會驅動程式安裝失敗。目前能成功安裝2台Xtion的電腦只有Apple的iMac。請問您是在什麼環境下安裝好2台的呢?有沒有什麼建議?謝謝您的回覆。

    Roger

    • 請到裝置管理員裡面,選擇「檢視」裡面的「裝置(依連線)」,這樣可以看得出裝置的階層關係。

  4. Heresy您好
    之前一直打扰您,学习到了很多东西,非常感谢。
    一直参考您的文章,我已经大致完成了代码,能够运行,但是无论如何也无法显示点阵云图像。我只能大概认为是读取实时数据方面出现了问题。我自己也在网上搜索了很久,但是大部分都是使用OpenKinect进行开发的。由于您之前的文章也有用OpenGl显示点阵云的内容,虽然很冒昧,但是如果可以的话,能否百忙中抽空阅读下我的代码,帮我指出一下改进方向呢?
    代码链接如下http://sdrv.ms/QvztZ2
    实在不好意思,非常感谢。

      • 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 有提供整個大的方案,裡面就包含這個檔案了。
          就在它的相對路徑的地方。

  5. Heresy 您好,
    我將您此篇文章與"OpenNI 的 User Generator",這兩篇的文章中功能作結合,即使用多台kinect來進行人體偵測並去背,但是第一台的影像遮罩會疊到第二台裝置內,該如何將第一台所抓到的影像遮罩釋放,而不會顯示在第二台上呢?

    • 抱歉,不知道你的遮罩是什麼?
      如果是指 User Generator 的偵測結果的話,目前 NITE 所提供的 user generator 應該是有問題,無法在同一個程式裡面正確地使用多個的 user generator。

  6. 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 取出來的資料作個別的處理就可以了。

  7. Hello heresy 大大

    小弟想問個問題

    兩個裝置的userGenerator ,depthGenerator, imageGenerator

    都是存在同一個中,還是說一個裝置需要一個generator來接收

    不好意思可能您code有寫到,小弟想確認一下!!

    (鞠躬

  8. Hi, Heresy,

    在使用多個Kinect讀取skeleton的時候遇到的問題。

    每次在register callbacks時都會顯示access violation reading location。

    請問能否給出使用多個Kinect讀取skeleton的範例呢?

    萬分感謝!

    – Richard

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料