Kinect学习(六):提取人体关节点数据
前言
Kinect可以通過處理深度數據來得到人體各個關節點的位置坐標,比如:頭、手、腳等等。下面是人體關節點的示意圖:
這篇學習筆記的目標就是通過Kinect獲取人體的骨骼點數據。
代碼
#include <Windows.h> #include <iostream> #include <NuiApi.h> #include <opencv2/opencv.hpp>using namespace std; using namespace cv;void drawSkeleton(cv::Mat &img, cv::Point pointSet[], int which_one);int main(int argc, char * argv[]) {cv::Mat skeletonImg;skeletonImg.create(240, 320, CV_8UC3);cv::Point skeletonPoint[NUI_SKELETON_COUNT][NUI_SKELETON_POSITION_COUNT] = { cv::Point(0, 0) };bool tracked[NUI_SKELETON_COUNT];// 1、初始化NUIHRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON);if (FAILED(hr)){cout << "NuiInitialize failed" << endl;return hr;}// 2、定義骨骼信號事件句柄HANDLE skeletonEvent = CreateEvent(NULL, TRUE, FALSE, NULL);// 3、打開骨骼數據跟蹤事件hr = NuiSkeletonTrackingEnable(skeletonEvent, 0);if (FAILED(hr)){cout << "Could not open skeleton tracking event" << endl;NuiShutdown();return hr;}cv::namedWindow("skeletonImg", CV_WINDOW_AUTOSIZE);// 4、開始讀取骨骼數據while (1){NUI_SKELETON_FRAME skeletonFrame = { 0 }; //骨骼幀的定義 bool foundSkeleton = false;// 4.1、無限等待新的數據,收到數據就返回if (WaitForSingleObject(skeletonEvent, INFINITE) == 0){// 4.2、讀取骨骼數據幀的數據,讀到的數據地址存在skeletonFramehr = NuiSkeletonGetNextFrame(0, &skeletonFrame);if (SUCCEEDED(hr)){//NUI_SKELETON_COUNT是檢測到的骨骼數(即,跟蹤到的人數)for (int i = 0;i < NUI_SKELETON_COUNT;i++){NUI_SKELETON_TRACKING_STATE trackingState = skeletonFrame.SkeletonData[i].eTrackingState;// 4.3、Kinect最多檢測到6個人,但只能跟蹤2個人的骨骼,再檢查是否跟蹤到了if (trackingState == NUI_SKELETON_TRACKED){foundSkeleton = true;}}}if (!foundSkeleton){continue;}// 4.4、平滑骨骼幀,消除抖動NuiTransformSmooth(&skeletonFrame, NULL);skeletonImg.setTo(0);for (int i = 0;i < NUI_SKELETON_COUNT;i++){// 判斷是否是一個正確骨骼的條件:骨骼被跟蹤到并且肩部中心(頸部位置)必須跟蹤到if (skeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED && skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_SHOULDER_CENTER] != NUI_SKELETON_POSITION_NOT_TRACKED){float fx, fy;// 拿到所有跟蹤到的關節點的坐標,并轉換為我們的深度空間的坐標,因為我們是在深度圖像中 // 把這些關節點標記出來的 // NUI_SKELETON_POSITION_COUNT為跟蹤到的一個骨骼的關節點的數目,為20for (int j = 0;j < NUI_SKELETON_POSITION_COUNT;j++){NuiTransformSkeletonToDepthImage(skeletonFrame.SkeletonData[i].SkeletonPositions[j], &fx, &fy);skeletonPoint[i][j].x = (int)fx;skeletonPoint[i][j].y = (int)fy;}for (int j = 0;j < NUI_SKELETON_POSITION_COUNT;j++){if (skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[j] != NUI_SKELETON_POSITION_NOT_TRACKED){cv::circle(skeletonImg, skeletonPoint[i][j], 3, cv::Scalar(0, 255, 255), 1, 8, 0);tracked[i] = true;}}drawSkeleton(skeletonImg, skeletonPoint[i], i);}}imshow("skeletonImg", skeletonImg);}else{cout << "Buffer length of received texture is bogus\r\n" << endl;}if (cv::waitKey(20) == 27)break;}//5、關閉NUI鏈接NuiShutdown();cv::destroyAllWindows();return 0; }//通過傳入關節點的位置,把骨骼畫出來 void drawSkeleton(cv::Mat &img, cv::Point pointSet[], int which_one) {cv::Scalar color;switch (which_one){case 0:color = cv::Scalar(255, 0, 0);break;case 1:color = cv::Scalar(0, 255, 0);break;case 2:color = cv::Scalar(0, 0, 255);break;case 3:color = cv::Scalar(255, 255, 0);break;case 4:color = cv::Scalar(255, 0, 255);break;case 5:color = cv::Scalar(0, 255, 255);break;}// 脊柱if ((pointSet[NUI_SKELETON_POSITION_HEAD].x != 0 || pointSet[NUI_SKELETON_POSITION_HEAD].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HEAD], pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SPINE].x != 0 || pointSet[NUI_SKELETON_POSITION_SPINE].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], pointSet[NUI_SKELETON_POSITION_SPINE], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SPINE].x != 0 || pointSet[NUI_SKELETON_POSITION_SPINE].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HIP_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_CENTER].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SPINE], pointSet[NUI_SKELETON_POSITION_HIP_CENTER], color, 2);// 左上肢if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT], pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT], pointSet[NUI_SKELETON_POSITION_WRIST_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HAND_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_HAND_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_WRIST_LEFT], pointSet[NUI_SKELETON_POSITION_HAND_LEFT], color, 2);// 右上肢if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT], pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT], pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HAND_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_HAND_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT], pointSet[NUI_SKELETON_POSITION_HAND_RIGHT], color, 2);// 左下肢if ((pointSet[NUI_SKELETON_POSITION_HIP_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HIP_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_CENTER], pointSet[NUI_SKELETON_POSITION_HIP_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_HIP_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_LEFT], pointSet[NUI_SKELETON_POSITION_KNEE_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_KNEE_LEFT], pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_FOOT_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_FOOT_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT], pointSet[NUI_SKELETON_POSITION_FOOT_LEFT], color, 2);// 右下肢if ((pointSet[NUI_SKELETON_POSITION_HIP_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_CENTER], pointSet[NUI_SKELETON_POSITION_HIP_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_RIGHT], pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT], pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_FOOT_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_FOOT_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT], pointSet[NUI_SKELETON_POSITION_FOOT_RIGHT], color, 2); }結果
說明
整個程序流程如下:
1、初始化NUI
// 1、初始化NUI HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON); if (FAILED(hr)) {cout << "NuiInitialize failed" << endl;return hr; }初始化時要使用骨架數據(NUI_INITIALIZE_FLAG_USES_SKELETON)。
2、定義骨骼信號事件句柄
// 2、定義骨骼信號事件句柄 HANDLE skeletonEvent = CreateEvent(NULL, TRUE, FALSE, NULL);定義一個事件句柄,其對應骨骼數據是否準備好,即是否可讀。
3、打開骨骼數據跟蹤事件
// 3、打開骨骼數據跟蹤事件 hr = NuiSkeletonTrackingEnable(skeletonEvent, 0); if (FAILED(hr)) {cout << "Could not open skeleton tracking event" << endl;NuiShutdown();return hr; }4、等待數據
// 4.1、無限等待新的數據,收到數據就返回 if (WaitForSingleObject(skeletonEvent, INFINITE) == 0) {... }查詢skeletonEvent,如果有信號就讀取并進行下一步處理,如果沒有就無限等待下去(INFINITE)。
5、處理得到的骨架數據
好了,總算到了重頭戲了,前面的都是相對基本的套路。
骨骼數據相關的結構體
NUI_SKELETON_FRAME結構體中保存了骨骼幀的數據,可以看下它的定義:
typedef struct _NUI_SKELETON_FRAME{LARGE_INTEGER liTimeStamp;DWORD dwFrameNumber;DWORD dwFlags;Vector4 vFloorClipPlane;Vector4 vNormalToGravity;NUI_SKELETON_DATA SkeletonData[ 6 ];} NUI_SKELETON_FRAME;簡要說明:
- liTimeStamp記錄自Kinect傳感器初始化(調用NuiInitialize函數)以來經過的累計毫秒時間;
- dwFrameNumber是深度數據幀中的用來產生骨骼數據幀的幀編號;
- SkeletonData表示的就是骨骼數據段,也是其中最重要的一個變量。
SkeletonData是一個NUI_SKELETON_DATA類型的結構體,保存了骨骼數據。注意到,它還是個數組,有6個元素,對應可知Kinect最多可以跟蹤6個用戶。NUI_SKELETON_DATA結構體定義如下:
typedef struct _NUI_SKELETON_DATA{NUI_SKELETON_TRACKING_STATE eTrackingState;DWORD dwTrackingID;DWORD dwEnrollmentIndex;DWORD dwUserIndex;Vector4 Position;Vector4 SkeletonPositions[ 20 ];NUI_SKELETON_POSITION_TRACKING_STATE eSkeletonPositionTrackingState[ 20 ];DWORD dwQualityFlags;} NUI_SKELETON_DATA;可能有的人會想,又有一堆東西了,繼續跟蹤每個結構體成員的定義耐心往下看看吧。。。
1、NUI_SKELETON_TRACKING_STATE eTrackingState:eTrackingState表示骨骼數據跟蹤狀態。NUI_SKELETON_TRACKING_STATE是一個枚舉類型,定義如下:
嘛,從字面意思看大致就能理解了:
- NUI_SKELETON_NOT_TRACKED:表示沒有跟蹤到;
- NUI_SKELETON_POSITION_ONLY:表示檢測到了骨骼數據,但是沒有激活跟蹤模式,即Position字段有值,沒有其他數據,不常用可以不考慮;
- NUI_SKELETON_TRACKED:表示跟蹤到骨骼數據;
2、DWORD dwTrackingID:跟蹤用戶的ID。每個Kinect跟蹤的用戶都會有一個ID,盡管這個值不確定,如果用戶離開了Kinect的視野,當前用戶的ID值就會失效,下次進入視野時又會重新分配新的ID值。
3、Vector4 Position:表示整個骨架的中間點。比如說用戶到攝像頭的距離,可以直接使用這個參數來表示,即將其視作骨架中心點到攝像頭的距離。
Vector4表示空間坐標,定義如下:
4、Vector4 SkeletonPositions[ 20 ]:20個人體關節點的位置,使用Vector4即表示其坐標。注意,只有在前面提到的eTrackingState等于NUI_SKELETON_TRACKED的時候,這個數據才不是0。
5、NUI_SKELETON_POSITION_TRACKING_STATE eSkeletonPositionTrackingState[ 20 ]:eSkeletonPositionTrackingState跟前面的SkeletonPositions對應,其表示跟蹤的關節點的好壞。
看看NUI_SKELETON_POSITION_TRACKING_STATE的定義:
又是一個枚舉類型,定義了三種狀態:
- NUI_SKELETON_POSITION_NOT_TRACKED:沒有跟蹤到;
- NUI_SKELETON_POSITION_INFERRED:表示骨骼點數據是通過前一幀或是其他已跟蹤到的骨骼點的數據推斷而來的,而這個骨骼點本身是沒有跟蹤到的;
- NUI_SKELETON_POSITION_TRACKED:骨骼點成功跟蹤,并且是從當前幀中得到的;
骨骼幀的獲取
NUI_SKELETON_FRAME skeletonFrame = { 0 }; //骨骼幀的定義 bool foundSkeleton = false;// 讀取骨骼數據幀的數據,讀到的數據地址存在skeletonFrame hr = NuiSkeletonGetNextFrame(0, &skeletonFrame); if (SUCCEEDED(hr)) {//NUI_SKELETON_COUNT是檢測到的骨骼數(即,跟蹤到的人數)for (int i = 0;i < NUI_SKELETON_COUNT;i++){NUI_SKELETON_TRACKING_STATE trackingState = skeletonFrame.SkeletonData[i].eTrackingState;// Kinect最多檢測到6個人,但只能跟蹤2個人的骨骼,再檢查是否跟蹤到了if (trackingState == NUI_SKELETON_TRACKED){foundSkeleton = true;}} }if (!foundSkeleton) {continue; }skeletonFrame是NUI_SKELETON_FRAME類型的結構體。foundSkeleton是一個bool值,表示是否找到了骨骼點數據。
通過NuiSkeletonGetNextFrame函數來獲取骨骼點數據。獲取的數據會保存在skeletonFrame中。前面介紹了NUI_SKELETON_FRAME和NUI_SKELETON_DATA結構體,這里不做贅述了,直接給出答案。skeletonFrame.SkeletonData是一個數組,有6個元素,我們通過判斷每一個skeletonFrame.SkeletonData[i].eTrackingState的值,來判斷這個ID是否已經分配出去,即已跟蹤到目標。如果跟蹤到,其值應該等于NUI_SKELETON_TRACKED。
如果跟蹤了某個目標,就令foundSkeleton為True,如果沒有就不改變它,沒有跟蹤就直接跳過這次循環。
平滑骨骼幀
// 4.4、平滑骨骼幀,消除抖動 NuiTransformSmooth(&skeletonFrame, NULL);相當于對骨骼幀做一個濾波,防止出現抖動或是動作不連續引起的幀與幀之間的跳變。簡單點說就是讓動作連續,防止幀之間出現跳變。
其第一個參數就是骨骼幀,第二個參數可以用來配置濾波的參數,平滑的程度等等,這里直接取NULL即采用默認配置即可。
獲取關節點坐標
for (int i = 0;i < NUI_SKELETON_COUNT;i++) {// 判斷是否是一個正確骨骼的條件:骨骼被跟蹤到并且肩部中心(頸部位置)必須跟蹤到if (skeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED && skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_SHOULDER_CENTER] != NUI_SKELETON_POSITION_NOT_TRACKED){float fx, fy;// 拿到所有跟蹤到的關節點的坐標,并轉換為我們的深度空間的坐標,因為我們是在深度圖像中 // 把這些關節點標記出來的 // NUI_SKELETON_POSITION_COUNT為跟蹤到的一個骨骼的關節點的數目,為20for (int j = 0;j < NUI_SKELETON_POSITION_COUNT;j++){NuiTransformSkeletonToDepthImage(skeletonFrame.SkeletonData[i].SkeletonPositions[j], &fx, &fy);skeletonPoint[i][j].x = (int)fx;skeletonPoint[i][j].y = (int)fy;}for (int j = 0;j < NUI_SKELETON_POSITION_COUNT;j++){if (skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[j] != NUI_SKELETON_POSITION_NOT_TRACKED){cv::circle(skeletonImg, skeletonPoint[i][j], 3, cv::Scalar(0, 255, 255), 1, 8, 0);tracked[i] = true;}}drawSkeleton(skeletonImg, skeletonPoint[i], i);} }NUI_SKELETON_COUNT是最多可以跟蹤的用戶數,即為6。遍歷所有的這些ID數,尋找跟蹤的再作處理。
skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState是一個數組,有20個元素,表示20個骨骼點是否跟蹤到了。而這20個骨骼點是預先定義好了順序的:
下面是這20個骨骼點對應的位置的示意圖:
按照NUI_SKELETON_POSITION_INDEX定義的參數來訪問數組元素,判斷是否跟蹤到了數據,代碼如下:
if (skeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED && skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_SHOULDER_CENTER] != NUI_SKELETON_POSITION_NOT_TRACKED)隨后將骨骼點在空間坐標系中的坐標轉換為在圖像坐標系中的坐標,詳細的函數參數說明就不做介紹了,可以自行查看函數原型。代碼如下:
NuiTransformSkeletonToDepthImage(skeletonFrame.SkeletonData[i].SkeletonPositions[j], &fx, &fy);隨后會在程序中調用cv::circle函數,將骨骼點畫出來,此時的骨骼點是經過了坐標轉換后得到的圖像坐標系中的坐標。
之后就是繪制骨架了,放到后面詳細介紹,需要自己定義函數drawSkeleton來實現,調用該函數的代碼如下:
繪制骨架
drawSkeleton函數定義如下:
//通過傳入關節點的位置,把骨骼畫出來 void drawSkeleton(cv::Mat &img, cv::Point pointSet[], int which_one) {cv::Scalar color;switch (which_one){case 0:color = cv::Scalar(255, 0, 0);break;case 1:color = cv::Scalar(0, 255, 0);break;case 2:color = cv::Scalar(0, 0, 255);break;case 3:color = cv::Scalar(255, 255, 0);break;case 4:color = cv::Scalar(255, 0, 255);break;case 5:color = cv::Scalar(0, 255, 255);break;}// 脊柱if ((pointSet[NUI_SKELETON_POSITION_HEAD].x != 0 || pointSet[NUI_SKELETON_POSITION_HEAD].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HEAD], pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SPINE].x != 0 || pointSet[NUI_SKELETON_POSITION_SPINE].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], pointSet[NUI_SKELETON_POSITION_SPINE], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SPINE].x != 0 || pointSet[NUI_SKELETON_POSITION_SPINE].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HIP_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_CENTER].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SPINE], pointSet[NUI_SKELETON_POSITION_HIP_CENTER], color, 2);// 左上肢if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_LEFT], pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ELBOW_LEFT], pointSet[NUI_SKELETON_POSITION_WRIST_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HAND_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_HAND_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_WRIST_LEFT], pointSet[NUI_SKELETON_POSITION_HAND_LEFT], color, 2);// 右上肢if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_CENTER], pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_SHOULDER_RIGHT], pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ELBOW_RIGHT], pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HAND_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_HAND_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_WRIST_RIGHT], pointSet[NUI_SKELETON_POSITION_HAND_RIGHT], color, 2);// 左下肢if ((pointSet[NUI_SKELETON_POSITION_HIP_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HIP_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_CENTER], pointSet[NUI_SKELETON_POSITION_HIP_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_HIP_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_LEFT], pointSet[NUI_SKELETON_POSITION_KNEE_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_KNEE_LEFT], pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_FOOT_LEFT].x != 0 || pointSet[NUI_SKELETON_POSITION_FOOT_LEFT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ANKLE_LEFT], pointSet[NUI_SKELETON_POSITION_FOOT_LEFT], color, 2);// 右下肢if ((pointSet[NUI_SKELETON_POSITION_HIP_CENTER].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_CENTER].y != 0) &&(pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_CENTER], pointSet[NUI_SKELETON_POSITION_HIP_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_HIP_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_HIP_RIGHT], pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_KNEE_RIGHT], pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT], color, 2);if ((pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT].y != 0) &&(pointSet[NUI_SKELETON_POSITION_FOOT_RIGHT].x != 0 || pointSet[NUI_SKELETON_POSITION_FOOT_RIGHT].y != 0))cv::line(img, pointSet[NUI_SKELETON_POSITION_ANKLE_RIGHT], pointSet[NUI_SKELETON_POSITION_FOOT_RIGHT], color, 2); }比較長,但是思路很簡單,就是根據不同的ID(共6個),分配不同的顏色,然后根據20個關節點兩兩畫直線,繪制出骨架。
參考資料
總結
以上是生活随笔為你收集整理的Kinect学习(六):提取人体关节点数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kinect学习(五):提取带用户ID的
- 下一篇: Kinect学习(七):综合提取彩色、深