使用 Qt 顯示 OpenNI 的人體骨架


延續上一篇《使用 Qt GraphicsView 顯示 OpenNI 影像資料》,這邊繼續接著上次的範例往下寫;而這次主要加入的功能,則是 OpenNI 人體骨架的顯示了~相關的程式細節、以及說明,請先參考之前的《透過 OpenNI / NITE 分析人體骨架(上)》和《透過 OpenNI / NITE 分析人體骨架(下)》,這邊的程式基本上會沿用這兩篇的程式碼的概念、再做一些修改。

為了做到這件事,所以之前定義的 COpenNICKinectReader 這兩個類別,都必須要做對應的修改;除此之外,Heresy 也又另外定義了一個繼承自 QGraphicsItemCSkelItem,用來紀錄、處理人體的骨架資料。

完整的程式原始碼請到 Heresy 的 SkyDrive 下載;而接下來,就是簡單的說明了~

COpenNI 的部分

由於要進行人體的骨架分析與追蹤,需要使用到 xn::UserGenerator,所以自然除了在 COpenNI 的成員資料裡,要加上他的資料(這邊是名為 m_User 的變數),也必須要在 Initial() 裡,加上對他的初始化;而這邊主要的程式修改,則就是在建立完 Image Generator 和 Depth Generator 後,再繼續建立、並設定 User Generator 了~

/* Initial OpenNI context and create nodes. */
bool Initial()
{
  // Initial OpenNI Context
  ...
  // create depth node
  m_eResult = m_Depth.Create( m_Context );
  if( CheckError( "Create Depth Generator Error" ) )
    return false;
 
// create user node m_eResult = m_User.Create( m_Context ); if( CheckError( "Create User Generator Error" ) ) return false;
// set nodes m_eResult = m_Depth.GetAlternativeViewPointCap().SetViewPoint( m_Image ); CheckError( "Can't set the alternative view point on depth generator" );
XnCallbackHandle hUserCB; m_User.RegisterUserCallbacks( CB_NewUser, NULL, NULL, hUserCB ); m_User.GetSkeletonCap().SetSkeletonProfile( XN_SKEL_PROFILE_ALL ); XnCallbackHandle hCalibCB; m_User.GetSkeletonCap().RegisterToCalibrationComplete( CB_CalibrationComplete, &m_User, hCalibCB ); XnCallbackHandle hPoseCB; m_User.GetPoseDetectionCap().RegisterToPoseDetected( CB_PoseDetected, &m_User, hPoseCB );
return true; }

上面程式碼的黃底部分,就是新加入的部分。基本上,和之前《透過 OpenNI / NITE 分析人體骨架(上)》比較不一樣的是,新版的 OpenNI 在 Skeleton 和 Pose Detection 的 callback function register 上,做了一些介面的調整

本來 Skeleton Capability 是透過 RegisterCalibrationCallbacks() 來同時登記「開始校正」和「校正結束」這兩種事件的 callback function;不過在新版的 OpenNI 則是建議分別採用 RegisterToCalibrationStart()RegisterToCalibrationComplete() 來進行 callback function 的註冊。

同樣的狀況也發生在 Pose Detection 這個 Capability。他本來是使用 RegisterToPoseCallbacks() 來註冊「偵測到姿勢」和「姿勢結束」兩種 callback function;而現在則是建議分別使用 RegisterToPoseDetected()RegisterToOutOfPose() 來取代。

這些 API 的變更建議,基本上 Heresy 都是在編譯 OpenNI 的程式的時候,才注意到的;編譯有使用 OpenNI 舊式介面的城市的時候,編譯器會輸出警告訊息、建議更換成新的介面,感覺還滿貼心的(:p)。而上面的程式碼呢,就是針對這些建議修改後的結果了~

而這邊 Heresy 就只有註冊三個必要的 callback function,分別是 CB_NewUser()CB_PoseDetected()CB_CalibrationComplete();這三者都是 COpenNI 裡的 static function,其內容分別如下:

static void XN_CALLBACK_TYPE CB_NewUser(
    xn::UserGenerator& generator, XnUserID user, void* pCookie )
{
  cout << "New user identified: " << user << endl;
  generator.GetPoseDetectionCap().StartPoseDetection("Psi", user);
}
static void XN_CALLBACK_TYPE CB_CalibrationComplete(
    xn::SkeletonCapability& skeleton, XnUserID user,
    XnCalibrationStatus calibrationError, void* pCookie )
{
  cout << "Calibration complete for user " <<  user << ", ";
  if( calibrationError == XN_CALIBRATION_STATUS_OK )
  {
    cout << "Success" << endl;
    skeleton.StartTracking( user );
  }
  else
  {
    cout << "Failure" << endl;
    xn::UserGenerator* pUser = (xn::UserGenerator*)pCookie;
    pUser->GetPoseDetectionCap().StartPoseDetection( "Psi", user );
  }
}
static void XN_CALLBACK_TYPE CB_PoseDetected(
    xn::PoseDetectionCapability& poseDetection, const XnChar* strPose,
    XnUserID user, void* pCookie)
{
  cout << "Pose " << strPose << " detected for user " <<  user << endl;
  xn::UserGenerator* pUser = (xn::UserGenerator*)pCookie;
  pUser->GetSkeletonCap().RequestCalibration( user, FALSE );
  poseDetection.StopPoseDetection( user );
}

而由於內容基本上都和之前的大致相同,所以 Heresy 在這邊就不多做說明了~

最後,由於之後在讀取人體骨架顯示的時候,會用到 user generator 和 depth generator,所以這邊還另外加入了 GetUserGenerator()GetDepthGenerator() 這兩個函式,分別會回傳 m_Userm_Depth,讓 COpenNI 外部也可以使用。


CSkelItem 的部分

CSkelItem 這個類別,是為了處理、並顯示 OpenNI 的骨架資料而寫的,他基本上是繼承自 QGraphicsItem、在 Qt 的 Graphics Scene 裡使用的物件,這部分的說明,可以參考之前的《建立自己的 QGraphicsItem》一文。

而為了處理人體骨架資料,所以它除了 QGraphicsItem 必要的 paint()boundingRect() 兩個函式外,另外也實作了建構子、UpdateSkeleton() 等函式。下面就是這個類別的程式碼內容:

/* Class for draw skeleton */
class CSkelItem : public QGraphicsItem
{
public:
  /* Constructor */
  CSkelItem( XnUserID& uid, COpenNI& rOpenNI )
              : QGraphicsItem(), m_UserID( uid ), m_OpenNI( rOpenNI )
  {
    // build lines connection table
    m_aConnection[0][0] = 0;
    m_aConnection[0][1] = 1;
    ...
    m_aConnection[14][0] = 13;
    m_aConnection[14][1] = 14;
    }
  }
 
  /* update skeleton data */
  void UpdateSkeleton()
  {
    // read the position in real world
    XnPoint3D  JointsReal[15];
    JointsReal[ 0] = GetSkeletonPos( XN_SKEL_HEAD           );
    JointsReal[ 1] = GetSkeletonPos( XN_SKEL_NECK           );
    ...
    JointsReal[13] = GetSkeletonPos( XN_SKEL_RIGHT_KNEE     );
    JointsReal[14] = GetSkeletonPos( XN_SKEL_RIGHT_FOOT     );
 
    // convert form real world to projective
    m_OpenNI.GetDepthGenerator().ConvertRealWorldToProjective(
                                        15, JointsReal, m_aJoints );
  }
 
public:
  COpenNI&   m_OpenNI;
  XnUserID   m_UserID;
  XnPoint3D  m_aJoints[15];
  int        m_aConnection[15][2];
 
private:
  QRectF boundingRect() const
  {
    QRectF qRect( m_aJoints[0].X, m_aJoints[0].Y, 0, 0 );
    for( unsigned int i = 1; i < 15; ++ i )
    {
      if( m_aJoints[i].X < qRect.left() )
        qRect.setLeft( m_aJoints[i].X );
      if( m_aJoints[i].X > qRect.right() )
        qRect.setRight( m_aJoints[i].X );
 
      if( m_aJoints[i].Y < qRect.top() )
        qRect.setTop( m_aJoints[i].Y );
      if( m_aJoints[i].Y > qRect.bottom() )
        qRect.setBottom( m_aJoints[i].Y );
    }
    return qRect;
  }
 
  void paint( QPainter *painter, 
              const QStyleOptionGraphicsItem *option, QWidget *widget )
  {
    // set pen for drawing
    QPen pen( QColor::fromRgb( 0, 0, 255 ) );
    pen.setWidth( 3 );
    painter->setPen( pen );
    
    // draw lines
    for( unsigned int i = 0; i < 15; ++ i )
    {
      XnPoint3D  &p1 = m_aJoints[ m_aConnection[i][0] ],
                 &p2 = m_aJoints[ m_aConnection[i][1] ];
 
      painter->drawLine( p1.X, p1.Y, p2.X, p2.Y );
    }
    
    // draw joints
    for( unsigned int i = 0; i < 15; ++ i )
      painter->drawEllipse(QPointF(m_aJoints[i].X,m_aJoints[i].Y), 5, 5 );
  }
 
  XnPoint3D GetSkeletonPos( XnSkeletonJoint eJointName )
  {
    // get position
    XnSkeletonJointPosition mPos;
    m_OpenNI.GetUserGenerator().GetSkeletonCap().GetSkeletonJointPosition(
                                                  m_UserID,eJointName,mPos );
 
    // convert to XnPoint3D
    return xnCreatePoint3D(mPos.position.X,mPos.position.Y,mPos.position.Z);
  }
};

CSkelItem 有四個主要的 member data:m_OpenNIm_UserIDm_aJointsm_aConnection;其中,m_OpenNIm_UserID 是在建立 CSkelItem 的物件時必須要傳入的參數,並且讓 CSkelItem 可以在讀取骨架資料時使用的。

m_aJoints 則是一個大小為 15 的 XnPoint3D 陣列,分別儲存著 OpenNI / NITE 提供的十五個關節的座標;這邊的資料會在執行 UpdateSkeleton() 時被更新,詳細的資料讀取方法,之前已經有介紹過了,所以這邊不多做說明,請直接參考程式碼的內容。

不過這邊另外要注意的是,由於 Heresy 是要把它用 2D 的形式來畫,所以這邊紀錄的座標資料,要先透過 depth generator 的 ConvertRealWorldToProjective() 這個函式,把各關節的座標從 real world 座標系統轉換到 projective(投影)座標系統,這樣才能用在 2D 的顯示上。

另外,為了方便繪製,Heresy 還另外定義了一個名為 m_aConnection 的 int 二維陣列,代表關節之間的連結關係表;他基本上是一個 15 x 2 的陣列,代表整個人體骨架有十五條代表肢體的線、而每條線的兩個值,則代表是第幾個關節點(對應到 m_aJoints)。

m_aConnection 這個資料的內容都是固定的,一旦建立後就不需要去修改,目前是寫在建構子裡面。而實際上,由於這個表對所有 CSkelItem 的骨架資料都是共通的,所以其實是可以考慮改成 static member data、讓所有變數共用一份,不過這邊為了簡單化,就先這樣寫了~

而最重要的繪製的程式碼,則是 paint() 的部分。由於是使用 Qt 的架構,所以自然就是使用 Qt 的 QPainter 來作畫了~

在這邊的程式碼裡,第一個步驟就是先設定 QPainter 的畫筆,透過指定要它的顏色和筆的寬度(這邊是設定成藍色),來修改畫出來的效果。

接下來,則是根據 m_aConnection 這張表,透過迴圈的方法,把代表人體骨架的十五條線都畫出來;而由於只是簡單地示意用的,所以就是直接用 QPainter 內建的 drawLine() 來畫而已了~

在畫出骨架的線條後,Heresy 則是再透過 QPainterdrawEllipse(),把 m_aJoints 的關節點用圓形畫出來了。而最後呈現的結果呢,大致上就會像右上方的圖一樣了~如果希望可以顯示更多資訊的話,也可以自己再針對 CSkelItem 做調整、修改。


CKinectReader 的部分

為了要支援骨架資料的顯示,所以在 CKinectReader 的部分,也要做一些對應的修改。這邊 Heresy 主要是加入了型別是 vector<CSkelItem*> 的成員變數 m_vSkeleton,用來簡單地記錄、管理目前的骨架資料。

接下來,就是修改更新資料的 timerEvent() 這個函式了~修改的內容,主要就是在之前更新影像資料的程式碼後面、繼續來處理骨架的資料了。其內容如下:

void timerEvent( QTimerEvent *event )
{
  // Read OpenNI data
  m_OpenNI.UpdateData();
 
  // Read Image
  ...
 
  // Read Skeleton
  xn::UserGenerator& rUser = m_OpenNI.GetUserGenerator();
  XnUInt16 nUsers = rUser.GetNumberOfUsers();
  if( nUsers > 0 )
  {
    // get user id
    XnUserID* aUserID = new XnUserID[nUsers];
    rUser.GetUsers( aUserID, nUsers );
 
    // get skeleton for each user
    unsigned int counter = 0;
    xn::SkeletonCapability& rSC = rUser.GetSkeletonCap();
    for( int i = 0; i < nUsers; ++i )
    {
      // if is tracking skeleton
      if( rSC.IsTracking( aUserID[i] ) )
      {
        ++counter;
        if( counter > m_vSkeleton.size() )
        {
          // create new skeleton item
          CSkelItem* pSkeleton = new CSkelItem( aUserID[i], m_OpenNI );
          m_Scene.addItem( pSkeleton );
          m_vSkeleton.push_back( pSkeleton );
          pSkeleton->setZValue( 10 );
        }
        else
          m_vSkeleton[ counter-1 ]->m_UserID = aUserID[i];

        // update skeleton item data
        m_vSkeleton[ counter-1 ]->UpdateSkeleton();
        m_vSkeleton[ counter-1 ]->setVisible( true );
      }
    }
    // hide un-used skeleton items
    for( unsigned int i = counter; i < m_vSkeleton.size(); ++ i )
      m_vSkeleton[i]->setVisible( false );
 
    // release user id area
    delete [] aUserID;
  }
}

基本上,主要的流程都和之前《透過 OpenNI / NITE 分析人體骨架(上)》的相同,不過之前在確認到 user 正被進行骨架追蹤時,只會印出單一關節點的座標,作為輸出的結果,而現在這個版本,則是必須要去建立、修改整個人體的骨架資料。

當確認到目前偵測到的使用者有骨架資料需要顯示時,這裡的第一個動作,是先確認 scene 裡面的 CSkelItem 物件是否夠用(透過判斷 m_vSkeleton 的大小)?如果數量不夠的話,就必須要建立一個新的、並放到 Scene 裡;而如果數量夠的話,則是強制去修改現有的 CSkelItem 物件的 m_UserID,讓他對應到目前的使用者。

而當確定有 CSkelItem 物件可以拿來操作之後,接下來就是呼叫 CSkelItemUpdateSkeleton() 這個函式,讓他去抓取 OpenNI 的骨架資料、進行更新了!

最後,當所有的 user 都處理完了以後,這邊則是在用一個迴圈,去把 scene 裡面剩下沒有用到的 CSkelItem、透過 setVisible() 這個函式設定成隱藏,讓他們不會被顯示出來,這樣就完成所有動作了。

也就是,以目前的程式來說,Heresy 再加入新的 CSkelItem 後,就算之後沒有用了,基本上也只是把他設定成隱藏不顯示,而是不會真的去刪除他的。而如果有新的使用者的骨架要顯示的話,則是會優先使用這些被閒置、隱藏的 CSkelItem 物件,等到真正不夠,再建立新的。而這樣的結果,就是實際上每一個 CSkelItem 並不是真正固定去對應特定的 user,而是在每次更新資料的時候,都有可能對應到不同的 user。

雖然 Heresy 本來是想把每一個 CSkelItem 固定對應到個別的 user,但是由於要做到那樣的管理,還需要多一些程式來做控制,所以就先跳過、暫時先寫成這樣了;如果之後還有要再繼續用的話,應該會再做修改、寫一個更好的管理系統吧。


這個範例就先寫到這了。基本上,算是補完之前的《透過 OpenNI / NITE 分析人體骨架(上)》和《透過 OpenNI / NITE 分析人體骨架(下)》的圖形輸出的部分了~

而實際上,寫到這邊的時候,Heresy 也才發現,試圖把 COpenNICKinectReader 拆開,其實或多或少會有一些架構上的問題;當然,這些問題也都可以解決,但是要繼續寫下去,可能要認真考慮一下要不要改一下架構了。

再者,這個範例程式裡,其實 Heresy 自己對於 CSkelItem 的管理相當地不滿意…理論上,應該寫一個完善的管理器,直接對應到 OpenNI 的 callback function,來做使用者的新增、刪除,會是比較好的方法…但是相對的,要做到這些事,必須要多上不少東西,所以在這邊才暫時沒有這樣寫;就如同前面所說的,之後有空的話,或許會把這部分的功能補完吧?


OpenNI / Kinect 相關文章目錄
Nokia Qt 相關文章目錄

廣告

關於 Heresy
https://kheresy.wordpress.com

34 Responses to 使用 Qt 顯示 OpenNI 的人體骨架

  1. sp036378 says:

    Heresy 大大您好,我在編譯時沒有出錯,執行後沒有結果,偵錯訊息中顯示程式以返回碼 1 (0x1) 結束,我是安裝VS2010 32位元 QT4.8.5 ,程式本身沒有修改,想請問有沒有方法解決?

    喜歡

    • Heresy says:

      建議請先試著閱讀程式。
      只要看一下 main(),應該可以發現程式 return 1 是因為 OpenNI 沒辦法正確初始化的關係。

      所以接下來,就是應該進去看他是死在哪個階段;而此時,在 console 視窗應該也是會有對應的錯誤訊息的。

      喜歡

  2. kuku says:

    Heresy 大,在Qt中,運行程序時,報錯:“class CSkelItem has no member named ‘setVisable’。
    ckinectreader.cpp中出現的錯誤處:
    m_vSkeleton[ counter-1 ]->setVisible( true );
    m_vSkeleton[ counter-1 ]->setVisible(false);
    請問一下,這是什麽原因呢?

    喜歡

    • Heresy says:

      setVisible() 這個函式是從 QGraphicsItem 繼承來的,請確認是否有正確地繼承。

      喜歡

      • kuku says:

        謝謝Heresy 大大,已經能夠正確的運行了,但是顯示不出畫面,出現閃退,說:The program has unexpectedly finished.
        請問一下這是什麽原因呐?

        喜歡

        • Heresy says:

          說實話,光是這樣無法判斷到底哪裡出錯。
          建議請使用 VC 的偵錯模式,設個中斷點、看看程式停在哪裡,這樣才有辦法知道是哪裡出問題。

          喜歡

          • kuku says:

            Heresy 大大,xn::SkeletonCapability& rSC = rUser.GetSkeletonCap();運行程序時,加 & 會出錯,error: invalid initialization of non-const reference of type ‘xn::SkeletonCapability&’ from an rvalue of type ‘xn::SkeletonCapability‘。請問一下這是什麽原因啊?謝謝~

            喜歡

          • Heresy says:

            錯誤訊息已經算是有說明了,基本上就是不能針對非 xn::SkeletonCapability 型別的 rvalue 取非 const 的參考。

            這邊的範例程式與法本來應該是可以運作的,說實話,現在過了很久沒碰 OpenNI 1 的環境了,也不確定是什麼問題。
            原則上,OpenNI 1 應該沒有修改過,所以應該是編譯器的支援度(容錯姓)的問題了。

            這邊建議就不要取參考應該就可以了。

            喜歡

  3. Jeff says:

    Heresy大您好
    請問可否使用現有骨架各關節資訊(含 positions 和 orientations)的文字檔以及一段影片中的多張深度圖做結合,將關節座標轉換成影像的座標系統並繪至圖片上?若深度影像為320 * 240 是否影響處理?

    喜歡

    • Heresy says:

      如果你並非使用 OpenNI 的話,則需要自行作座標的轉換。
      而如果是使用 OpenNI 的話,基本上就和上面的範例一樣,可以使用 ConvertRealWorldToProjective 作轉換。

      喜歡

  4. apple says:

    請問前輩,
    我依照你的範例也作了一個Qt 顯示器,
    但是我出現的骨架都會超出螢幕,
    我的Qt顯示kinect的地方只有一小塊區域,
    是不是因為這樣才導致座標不對呢?
    再請教一件事情可以嗎?
    ConvertRealWorldToProjective是轉成何種座標呢?
    先感謝您的Blog~讓我的工作簡單許多。

    喜歡

    • apple says:

      剛剛試著縮小到視窗並且成功了,
      但是不知道為什麼,
      我的骨架是左右顛倒上下相反。
      並且偏離本人50px左右~

      喜歡

    • Heresy says:

      請教一下,你是直接用 Heresy 的範例?還是有另外修改過?

      ConvertRealWorldToProjective() 這個函式是把點由 OpenNI 的真實世界座標系統、轉換到投影座標系統。

      喜歡

      • apple says:

        我自己修改過了==
        kinect user被包裝成獨立的class和subclass的Qt QGraphicsItem分開來,
        然後再把kinect user那個class傳給QGraphicsItem儲存(剛剛發現我kinect user本來就有問題Orz)
        請問投影座標系統指的是…?
        網路上有人說是螢幕座標,
        可是依照結果轉換出來的結果有負的。

        喜歡

      • apple says:

        補充一下,我的QGraphicsItem完全跟前輩的一樣,
        只是把joints存放得點拉到我自己的kinect wrapper控制而已~

        喜歡

      • Heresy says:

        個人會建議你先試試看單獨用 Heresy 的範例,看看會不會有同樣的問題。

        另外,投影座標系統是指 Depth Generator 的原始影像。
        雖然他理論上應該會是 0-640, 0-480,但是實際上上透過 ConvertRealWorldToProjective,的確是有可能得到超出這個範圍的座標的。
        因為如果本來人的肢體就超出深度影像的範圍外的話,雖然 OpenNI 無法取得明確的資料,還是會去針對超出範圍的部分進行猜測;這時候再轉換回投影座標系統的話,就有可能得到超出範圍的值。

        喜歡

        • apple says:

          謝謝。
          現在剩下的問題是顯示kinect骨架的時候,會有整個倒轉得情況。
          http://imageupload.org/en/file/207255/kinect-test.png.html
          藍色的位置剛好跟我人在的位置顛倒。不知道為什麼…

          至於前輩的專案,在我電腦Compile完直接crash…(但是如果執行執行檔是正常的)
          加上我的Openni版本不能使用Debug mode(不知道新版解決了沒有,看討論版好像還擱置中)Error訊息沒出來,不知道死在哪裡…
          我的環境: VS 2010 + Qt 4.8.0 + OpenNI 1.4.0.2 + NITE 1.5.0.2 + Sensor Kinectmod 5.0.5.1

          喜歡

          • Heresy says:

            建議測試看看,如果是使用 NITE 的範例,不知道有沒有問題?

            直接 crash…這還真囧…
            不過,沒有升級到最新的 OpenNI 是因為 OpenNI 有問題嗎?
            1.5 在 Heresy 這邊似乎沒有無法使用 debug 的問題…

            喜歡

          • apple says:

            NITE、OpenNI,前輩已經編譯好的執行檔都可以。
            我舊版的OpenNI也有null ptr的bug…讓我沒辦法進vs2010的debug mode.
            目前還找不出原因,為什麼骨架顯示會反轉Orz

            喜歡

    • Heresy says:

      恩…說實話,這樣的問題好像很難追…個人會建議,試試看能不能另外找一台電腦,來做測試?
      也就是把編譯、執行的動作,都拿到別台電腦試試看?
      這樣或許可以釐清,會不會是電腦環境本身有問題。

      喜歡

      • apple says:

        感謝持續答覆> boundingRect() 和 paint() 沒有加到 mutex (爆)
        目前好像沒問題了,
        可是進來的骨架資訊是整個旋轉的…
        我試圖把 scale 加上負號,雖然不會再有頭腳顛倒,可是左右變成顛倒(我往左、骨架往右…)
        void timerEvent(…)
        {
        //…
        if( global->kinect_data_read_mutex.tryLock(5) == false ) return;
        // 更新人物Node顯示
        xn::SkeletonCapability& rSC = global->kinect->openni_user_generator.GetSkeletonCap();
        int player_amount = global->kinect->player_list.size();
        int counter = 0;
        float kinect_scale_x = kinect_view_w / (float)global->screen.w ;
        float kinect_scale_y = kinect_view_h / (float)global->screen.h ;
        if( kinect_scale_x > kinect_scale_y ) scale = kinect_scale_y; // 如果 y 縮放比例比 x 大
        else scale = kinect_scale_x; // 反之
        CL_Console::write_line(“amount=%1, list_size=%2″,player_amount, global->kinect->player_list.size());
        for( int i=0; ikinect->player_list[i].user_id ) )
        {
        counter++;
        if( counter > kinect_skeleton_list.size() )
        {
        kinect_skeleton_list.push_back( new KinectSkeletonItem(global, &global->kinect->player_list[i]));
        kinect_display_scene->addItem( kinect_skeleton_list[i]);
        kinect_skeleton_list[counter-1]->setZValue(10);
        kinect_skeleton_list[counter-1]->setScale(-scale);
        }else{
        kinect_skeleton_list[counter-1]->user = &global->kinect->player_list[i];
        kinect_skeleton_list[counter-1]->setZValue(10);
        kinect_skeleton_list[counter-1]->setScale(-scale);
        }
        }
        }
        global->kinect_data_read_mutex.unlock();

        #endif
        }

        喜歡

        • apple says:

          補充:上一篇被截字= =
          我的Qt和 Kinect各佔一個thread…
          結果,Qt 的 paint() 和 boundingRect() 沒加到 mutex … (爆)

          喜歡

          • apple says:

            謝謝前輩的回應,
            因為你的熱心我才忍住性子把bug抓完。
            (差想把openni全刪了換ms kinect sdk)
            原來在座標轉換的地方,
            我忘了把 counter – 1來作索引,
            所以才會有錯誤的座標位置。
            你們沒看錯……越界它竟然讓讀……冏….
            改回 counter-1後,
            一切都正常了 !! 冏x2
            剩下一些像是另外一個玩家加入會crash的bug,
            我想明天再檢查看看 list 有沒有「隱性」越界 。
            再次謝謝前輩!

            喜歡

        • Heresy says:

          雖然好像沒幫上忙,不過還是恭喜你找到問題所在。
          希望接下來的 bug 也能順利發現了。 ^^"

          喜歡

          • apple says:

            恩^^

            喜歡

  5. 引用通告: 不用校正姿勢的 NITE 1.5 « Heresy's Space

  6. 引用通告: 使用 XML 設定檔來初始化 OpenNI « Heresy's Space

  7. 引用通告: OpenNI XnSkeletonJointOrientation 簡單分析 « Heresy's Space

  8. Toyysbk says:

    Heresy 大大~我最近想增加Openni的SkeletonJoint裡增加其他關節,不曉得這方法有辦法做嗎?或者有其他參考方向嗎?

    喜歡

    • Heresy says:

      基本上,這是看你要在哪個層次做。
      最單純的方法,就是自己去分析 Image Map 和 Depth Map,或者 NITE 提供的關節資訊、以自己的演算法來計算出自己需要的關節。
      如果是希望可以符合 OpenNI 的架構的話,則是需要自己去寫 OpenNI 的 Middle ware,取代掉 NITE 的功能。
      但是不管怎樣,都是必須要有自己的演算法,可以算出自己需要的關節點。

      喜歡

      • toyysbk says:

        演算我有參考過其他的論文,只是在於OpenNI程式我不知道該如何下手?
        大大提到的"分析 Image Map 和 Depth Map或者 NITE 提供的關節資訊"我該朝哪個方向進行呢?

        喜歡

        • Heresy says:

          基本上,你要先看你要用的演算法要那些資料。
          如果是要讀取深度影像資料的話,之前的《透過 OpneNI 讀取 Kinect 深度影像資料》就已經有說明過,怎麼把資料讀出來了。

          喜歡

          • Toyysbk says:

            我的演算法希望透過偵測到人的深度影像(OpenNI已經有人體偵測的功能),
            我如果想分析偵測的人體(X.Y.Z)全身資料不曉得該如何處理?

            喜歡

          • Heresy says:

            如果你的演算法是根據人的深度影像的話,那只要透過 Deptp Generator 產生的深度圖、再搭配 User Generator 的人體輪廓,應該就夠了吧?

            這邊的重點,應該還是看你要如何把 OpenNI 轉換成你的演算法需要的資料了。
            建議你先確認你的演算法所需要的輸入資料是什麼、要怎樣的格式,再來研究要怎麼把 OpenNI 提供的資料,轉換成你能處理的程式。

            喜歡

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

%d 位部落客按了讚: