Kinect SDKのソースコードを書き下した。
KinectSDKを使ってC++ソースコードを書いたので紹介します。使いたい方はご自由にどうぞ
- 概要
Kinectの深度画像、カメラ画像、スケルトン情報等を取得するKinectSDKのラッパーです。
Windows 7 + MS Visual Studio C++ 2010 expressで開発しました。
OpenCVをよく使うのでC/C++で書きました。
音源方位、音声認識は今後対応予定。
CKinectUtilというクラスをmain()から使うプログラムを以下に載せます。スケルトンを表示する部分は省いてます。
LogCommon.hはオレオレ用ログです。消してご利用ください。
- KinectUtil.h
/** * @file KinectUtil.h * Kinect for Windows SDK v1.0 * OpenCV v2.3.1 */ #pragma once #include <opencv/cv.h> #include <memory> #include <windows.h> #include <NuiApi.h> #include <vector> namespace Utility { /** * @brief 深度画像上の座標情報 */ typedef class CDepthPoint { public: float fX; float fY; USHORT usDepth; static const int CAP_WIDTH = 320; static const int CAP_HEIGHT = 240; public: CDepthPoint(); CDepthPoint(float x, float y, USHORT depth); virtual ~CDepthPoint(); CDepthPoint(const CDepthPoint& obj); } CDptPoint; /** * @brief トラッキング情報 */ class CTrackingInfo { public: // ID long lTrackingID; // 各関節点座標 std::vector<CDptPoint> skeletalPoints; public: CTrackingInfo(); virtual ~CTrackingInfo(); CTrackingInfo(const CTrackingInfo& obj); }; /** * @class CKinectController * @brief Kinect制御 * OpenCV型依存 */ typedef class CKinectController { public: //--------------------------------------------------------- // API //--------------------------------------------------------- /** * @brief コンストラクタ */ CKinectController(); /** * @brief デストラクタ */ virtual ~CKinectController(); /** * @brief 初期化 * キャプチャを開始する * @return 成功:0 / 失敗:0以外 */ long Initialize(); /** * @brief 終了処理 * キャプチャを終了する * @return 成功:0 / 失敗:0以外 */ long Finalize(); /** * @brief 次フレームを取得する * @return 成功:0 / 失敗:0以外 */ long GrabFrame(); /** * @brief Depth画像を取得する * 1チャンネル uint16幅 * @return 成功:0 / 失敗:0以外 */ long GetDepthFrame(IplImage*& pImage); /** * @brief RGB画像を取得する * 4チャンネル uchar幅 * @return 成功:0 / 失敗:0以外 */ long GetRGBFrame(IplImage*& pImage); /** * @brief トラッキングID分布画像を取得 * @return 成功:0 / 失敗:0以外 */ long GetTrakingIdFrame(IplImage*& pImage); /** * @brief トラッキング情報を取得 * @return 成功:0 / 失敗:0以外 */ long GetTrakingInfo(std::vector<CTrackingInfo>& trackingInfoList); /** * @brief ピッチ駆動 * @return 成功:0 / 失敗:0以外 */ long SetElevationAngle(float fAgl); /** * @brief ピッチ現在角度を取得 * @return 成功:0 / 失敗:0以外 */ long GetElevationAngle(float& fAgl); public: static long Depth16bitTo8bitImage(const IplImage* srcImage, IplImage*& dstImage); protected: //--------------------------------------------------------- // 操作 //--------------------------------------------------------- // 次フレーム取得 long UpdateFrame(); // 現フレーム破棄 long ReleaseFrame(); // Depth画像バッファリング long BufferDepthImage(const NUI_IMAGE_FRAME* pSrcFrame, const NUI_LOCKED_RECT& lockRect, IplImage*& pDstImage, IplImage*& pUserIdImage); // RGB画像バッファリング long BufferRgbImage(const NUI_IMAGE_FRAME* pSrcFrame, const NUI_LOCKED_RECT& lockRect, IplImage*& pDstImage); // LockRectから画素値を取り出す long PixelVal(USHORT& val, int x, int y, const NUI_IMAGE_FRAME* pSrcFrame, const NUI_LOCKED_RECT& lockRect); protected: //--------------------------------------------------------- // 属性 //--------------------------------------------------------- //------ RGB関連 ------ HANDLE m_hImgStreamEvent; HANDLE m_hImgStreamHandle; const NUI_IMAGE_FRAME* m_pImgFrame; // フレームワーク側のバッファ. NUI_LOCKED_RECT m_ImgLckRect; IplImage* m_pRgbImageBuff; //------ DEPTH関連 ------ HANDLE m_hDepStreamEvent; HANDLE m_hDepStreamHandle; const NUI_IMAGE_FRAME* m_pDepFrame; // フレームワーク側のバッファ. NUI_LOCKED_RECT m_DepLckRect; IplImage* m_pDepImageBuff; //------ SKELTON/USER TRACKING関連 ------ HANDLE m_hSklStreamEvent; NUI_SKELETON_FRAME m_SklFrame; IplImage* m_pUserImageBuff; protected: //--------------------------------------------------------- // 定数 //--------------------------------------------------------- /** * @brief フレーム解放-->次フレーム取得の実施を待つ時間(unit:msec). */ static const DWORD MSEC_REFRESH_WAIT_TIME = 50; } CKinectCtrl; }
- KinectUtil.cpp
/** * @file KinectUtil.cpp */ #include "KinectUtil.h" #include "LogCommon.h" #include <vector> #define _USE_MATH_DEFINES #include <math.h> #define LOG INF_LOG namespace Utility { long CKinectController::GrabFrame() { // フレーム破棄. long lRes = ReleaseFrame(); if(lRes != 0){ LOG(" ERR: 前フレームリリース失敗. res=%ld.", lRes); } Sleep(MSEC_REFRESH_WAIT_TIME); // 待つ. // 次フレーム取得. lRes = UpdateFrame(); if(lRes != 0){ LOG(" ERR: 次フレーム取得失敗. res=%ld.", lRes); return lRes; } // RGB画像バッファリング. if(m_pRgbImageBuff != NULL){ cvReleaseImage(&m_pRgbImageBuff); m_pRgbImageBuff = NULL; } lRes = BufferRgbImage(m_pImgFrame, m_ImgLckRect, m_pRgbImageBuff); if(lRes != 0){ LOG(" ERR: RGB画像バッファリング失敗. res=%ld.", lRes); return lRes; } // DEPTH画像バッファリング. if(m_pDepImageBuff != NULL){ cvReleaseImage(&m_pDepImageBuff); m_pDepImageBuff = NULL; } lRes = BufferDepthImage(m_pDepFrame, m_DepLckRect, m_pDepImageBuff, m_pUserImageBuff); if(lRes != 0){ LOG(" ERR: DEPTH画像バッファリング失敗. res=%ld.", lRes); return lRes; } return 0; } long CKinectController::GetRGBFrame(IplImage*& pImage) { if(m_pRgbImageBuff == NULL){ LOG(" ERR: キャプチャがバッファされてない. Capture is Not Buffering!"); return 1; } pImage = cvCloneImage( m_pRgbImageBuff ); if(pImage == NULL){ LOG(" ERR: cvCloneImage() returns NULL."); return 2; } return 0; } long CKinectController::GetDepthFrame(IplImage*& pImage) { if(m_pDepImageBuff == NULL){ LOG(" ERR: キャプチャがバッファされてない. Capture is Not Buffering!"); return 1; } pImage = cvCloneImage( m_pDepImageBuff ); if(pImage == NULL){ LOG(" ERR: cvCloneImage() returns NULL."); return 2; } return 0; } long CKinectController::GetTrakingIdFrame(IplImage*& pImage) { if(m_pUserImageBuff == NULL){ LOG(" ERR: キャプチャがバッファされてない. Capture is Not Buffering!"); return 1; } pImage = cvCloneImage( m_pUserImageBuff ); if(pImage == NULL){ LOG(" ERR: cvCloneImage() returns NULL."); return 2; } return 0; } long CKinectController::Initialize() { // 初期化. static const DWORD dwFlag = NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON | NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_AUDIO ; HRESULT hRes = NuiInitialize(dwFlag); if(hRes != S_OK){ LOG(" ERR: Kinect初期化失敗. NuiInitialize() returns %d.", hRes); return 1; } // RGBストリームオープン. hRes = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480, 0, 2, m_hImgStreamEvent, &m_hImgStreamHandle); if(hRes != S_OK){ LOG(" ERR: RGBストリームオープン失敗. NuiImageStreamOpen() returns %d.", hRes); return 2; } // Depthストリームオープン. hRes = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX, NUI_IMAGE_RESOLUTION_320x240 , 0, 2, m_hDepStreamEvent, &m_hDepStreamHandle); if(hRes != S_OK){ LOG(" ERR: Depthストリームオープン失敗. NuiImageStreamOpen() returns %d.", hRes); return 3; } // スケルトントラッキングオン設定 hRes = NuiSkeletonTrackingEnable(m_hSklStreamEvent, 0); if(hRes != S_OK){ LOG(" スケルトントラッキングオン設定失敗. NuiSkeletonTrackingEnable() returns %ld.", hRes); return 4; } return 0; } long CKinectController::Finalize() { NuiSkeletonTrackingDisable(); // 終了処理. NuiShutdown(); return 0; } long CKinectController::UpdateFrame() { // RGBフレーム取得. HRESULT hRes = NuiImageStreamGetNextFrame(m_hImgStreamHandle, 0, &m_pImgFrame); if(hRes != S_OK){ LOG(" ERR: RGB次フレーム取得失敗. NuiImageStreamGetNextFrame() returns %d.", hRes); return 1; } // RGBバッファをロック. hRes = m_pImgFrame->pFrameTexture->LockRect(0, &m_ImgLckRect, 0, 0); if(hRes != S_OK){ LOG(" ERR: RGBフレームバッファロック失敗. res=%d.", hRes); return 2; } // Depthフレーム取得. hRes = NuiImageStreamGetNextFrame(m_hDepStreamHandle, 0, &m_pDepFrame); if(hRes != S_OK){ LOG(" ERR: Depth次フレーム取得失敗. NuiImageStreamGetNextFrame() returns %d.", hRes); return 11; } // Depthバッファをロック. m_pDepFrame->pFrameTexture->LockRect(0, &m_DepLckRect, 0, 0); if(hRes != S_OK){ LOG(" ERR: Depthフレームバッファロック失敗. res=%d.", hRes); return 12; } hRes = NuiSkeletonGetNextFrame(0, &m_SklFrame); if(hRes != S_OK){ LOG(" ERR: スケルトン次フレーム取得失敗. res=%d.", hRes); //return 3; } return 0; } long CKinectController::ReleaseFrame() { if(m_pImgFrame != NULL){ HRESULT hRes = NuiImageStreamReleaseFrame(m_hImgStreamHandle, m_pImgFrame); if(hRes != S_OK){ LOG(" ERR: RGBフレーム解放失敗. res=%d.", hRes); return 1; } } if(m_pImgFrame != NULL){ HRESULT hRes = NuiImageStreamReleaseFrame(m_hDepStreamHandle, m_pDepFrame); if(hRes != S_OK){ LOG(" ERR: Depthフレーム解放失敗. res=%d.", hRes); return 2; } } return 0; } long CKinectController::BufferDepthImage(const NUI_IMAGE_FRAME* pSrcFrame, const NUI_LOCKED_RECT& lockRect, IplImage*& pDstImage, IplImage*& pUserIdImage) { if(pSrcFrame == NULL){ LOG(" ERR: pSrcFrame is NULL."); return -1; } // 画像サイズ取得. NUI_SURFACE_DESC surfaceSize; HRESULT hRes = pSrcFrame->pFrameTexture->GetLevelDesc(0, &surfaceSize); if(hRes != S_OK){ LOG(" ERR: バッファ画像サイズ取得失敗. GetLevelDesc() returns %d.", hRes); return 1; } // 画像バッファ作成. CvSize imgSize = cvSize(surfaceSize.Width, surfaceSize.Height); IplImage* dstImage = cvCreateImage(imgSize, IPL_DEPTH_16U, 1); // unsigned short x 1ch if(dstImage == NULL){ LOG(" ERR: cvCreateImage() returns NULL. img(W=%d,H=%d).", imgSize.width, imgSize.height); return 2; } cvSetZero(dstImage); IplImage* usrImage = cvCreateImage(imgSize, IPL_DEPTH_8U, 1); // byte x 1ch. if(usrImage == NULL){ LOG(" ERR: cvCreateImage() returns NULL. img(W=%d,H=%d).", imgSize.width, imgSize.height); return 3; } cvSetZero(usrImage); // Depthバッファコピー. unsigned short* dstPtr = (unsigned short*)dstImage->imageData; unsigned char* usrPtr = (unsigned char*) usrImage->imageData; UINT* srcPtr = (UINT*)lockRect.pBits; for(int y=0; y<imgSize.height; y++){ for(int x=0; x<imgSize.width; x++){ USHORT usValue = 0; // 画素値取得. long lRes = PixelVal(usValue, x, y, pSrcFrame, lockRect); if(lRes != 0){ LOG(" [%d] ERR: バッファ画素値取得失敗. res=%ld. pt(X=%,Y=%d). --> 終了.", lRes, (x/2), (y/2)); return lRes; } // 画素読み取り. *usrPtr = (usValue & 0x7) * 10; // UserID. *dstPtr = (usValue >> 3); // 3bitズラす. // 次の画素. dstPtr++; usrPtr++; } } // 引数に格納. pDstImage = dstImage; pUserIdImage = usrImage; return 0; } long CKinectController::BufferRgbImage(const NUI_IMAGE_FRAME* pSrcFrame, const NUI_LOCKED_RECT& lockRect, IplImage*& pDstImage) { if(pSrcFrame == NULL){ LOG(" ERR: pSrcFrame is NULL."); return -1; } // 画像サイズ取得. NUI_SURFACE_DESC surfaceSize; HRESULT hRes = pSrcFrame->pFrameTexture->GetLevelDesc(0, &surfaceSize); if(hRes != S_OK){ LOG(" ERR: バッファ画像サイズ取得失敗. GetLevelDesc() returns %d.", hRes); return 1; } // 画像バッファ作成. CvSize imgSize = cvSize(surfaceSize.Width, surfaceSize.Height); IplImage* dstImage = cvCreateImage(imgSize, IPL_DEPTH_8U, 4); if(dstImage == NULL){ LOG(" ERR: cvCreateImage() returns NULL. img(W=%d,H=%d).", imgSize.width, imgSize.height); return 2; } unsigned int cpySize = imgSize.width * imgSize.height * dstImage->nChannels * sizeof(BYTE); memcpy( dstImage->imageData, (BYTE*)lockRect.pBits, cpySize ); // 引数に格納. pDstImage = dstImage; return 0; } long CKinectController::PixelVal(USHORT& val, int x, int y, const NUI_IMAGE_FRAME* pSrcFrame, const NUI_LOCKED_RECT& lockRect) { // 引数チェック. if(pSrcFrame == NULL){ return -1; } NUI_SURFACE_DESC surfaceSize; HRESULT hRes = pSrcFrame->pFrameTexture->GetLevelDesc(0, &surfaceSize); if(hRes != S_OK){ LOG(" ERR: バッファ画像サイズ取得失敗. GetLevelDesc() returns %d.", hRes); return 1; } if( (x < 0) || (x > surfaceSize.Width) || (y < 0) || (y > surfaceSize.Height) ){ LOG(" ERR: Invalid Coord Input. pt(X=%d,Y=%d) <--> img(W=%d,H=%d).", x, y, surfaceSize.Width, surfaceSize.Height); return -1; } // 計算. const USHORT* usPtr = (const USHORT*)lockRect.pBits; const UCHAR* ucPtr = (UCHAR*) &usPtr[ (surfaceSize.Width * y) + x ]; val = (USHORT)(ucPtr[0] | ucPtr[1] << 8); return 0; } long CKinectController::GetTrakingInfo(std::vector<CTrackingInfo>& trackingInfoList) { // スケルトン情報の参照. //LOG(" スケルトントラッキング情報取得."); for(int ns=0; ns<NUI_SKELETON_COUNT; ns++){ if(m_SklFrame.SkeletonData[ns].eTrackingState == NUI_SKELETON_TRACKED){ // 関節情報の取り出し CTrackingInfo cInfo; cInfo.lTrackingID = m_SklFrame.SkeletonData[ns].dwTrackingID; //LOG(" [%d/%d] TRACKING!! id=%d. ", ns, NUI_SKELETON_COUNT, cInfo.lTrackingID); for(int np=0; np<NUI_SKELETON_POSITION_COUNT; np++){ long lX = 0, lY = 0; USHORT usDp = 0; // 座標変換. NuiTransformSkeletonToDepthImage( m_SklFrame.SkeletonData[ns].SkeletonPositions[np], &lX, &lY, &usDp); //LOG(" [%d/%d][skl:%d] (X=%d,Y=%d,D=%d).", ns, NUI_SKELETON_COUNT, np, lX, lY, usDp); CDptPoint cDptPt((float)lX, (float)lY, usDp); // リストに追加. cInfo.skeletalPoints.push_back( cDptPt ); } trackingInfoList.push_back( cInfo ); }else{ //LOG(" [%d/%d] NOT TRACKING..... ", ns, NUI_SKELETON_COUNT); } } return 0; } long CKinectController::SetElevationAngle(float fAgl) { long lAngle = (long)fAgl; HRESULT hRes = NuiCameraElevationSetAngle(lAngle); LOG(" ピッチ角制御: res=%d. angle=%ld.", hRes, lAngle); if(hRes != S_OK){ return 1; } return 0; } long CKinectController::GetElevationAngle(float& fAgl) { long lAngle = -1; HRESULT hRes = NuiCameraElevationGetAngle(&lAngle); LOG(" ピッチ角取得: res=%d. angle=%ld.", hRes, lAngle); if(hRes != S_OK){ return 1; } fAgl = (float)lAngle; return 0; } long CKinectController::Depth16bitTo8bitImage(const IplImage* srcImage, IplImage*& dstImage) { if(srcImage == NULL){ return -1; } if(srcImage->nChannels != 1){ return -1; } if(srcImage->depth != IPL_DEPTH_16U){ return -1; } IplImage* tmpImage = cvCreateImage( cvGetSize(srcImage), IPL_DEPTH_16U, 1 ); if(tmpImage == NULL){ LOG(" ERR: cvCreateImage() returns NULL."); return 1; } static const int NORM_COEFF = 6; cvSetZero(tmpImage); unsigned short* pSrc = (unsigned short*)srcImage->imageData; unsigned short* pDst = (unsigned short*)tmpImage->imageData; for(int y=0; y<tmpImage->height; y++){ for(int x=0; x<tmpImage->width; x++, pSrc++, pDst++){ *pDst = *pSrc * NORM_COEFF; } } dstImage = cvCreateImage( cvGetSize(tmpImage), IPL_DEPTH_8U, 1 ); if(dstImage == NULL){ LOG(" ERR: cvCreateImage() returns NULL."); return 2; } double dScale = 1.0 / UCHAR_MAX; cvCvtScale(tmpImage, dstImage, dScale); cvReleaseImage(&tmpImage); return 0; } CKinectController::CKinectController() { // RGB関連 m_hImgStreamEvent = NULL; m_hImgStreamHandle = NULL; m_pImgFrame = NULL; m_pRgbImageBuff = NULL; // Depth関連 m_hDepStreamEvent = NULL; m_hDepStreamHandle = NULL; m_pDepFrame = NULL; m_pDepImageBuff = NULL; //------ SKELTON/USER TRACKING関連 ------ m_hSklStreamEvent = NULL; m_pUserImageBuff = NULL; } CKinectController::~CKinectController() { Finalize(); } CDepthPoint::CDepthPoint() :fX(0.0), fY(0.0), usDepth(0) { } CDepthPoint::CDepthPoint(float x, float y, USHORT depth) { fX = x; fY = y; usDepth = depth; } CDepthPoint::~CDepthPoint(){ } CDepthPoint::CDepthPoint(const CDepthPoint& obj){ this->fX = obj.fX; this->fY = obj.fY; this->usDepth = obj.usDepth; } CTrackingInfo::CTrackingInfo() : lTrackingID(0) { } CTrackingInfo::~CTrackingInfo(){ } CTrackingInfo::CTrackingInfo(const CTrackingInfo& obj){ this->lTrackingID = obj.lTrackingID; this->skeletalPoints = obj.skeletalPoints; } }
- main.cpp
#include "stdafx.h" #include "KinectUtil.h" #include <opencv/highgui.h> #include <vector> int _tmain(int argc, _TCHAR* argv[]) { Utility::CKinectCtrl cKinectCtrl; // 初期化. long lRes = cKinectCtrl.Initialize(); if(lRes != 0){ return lRes; } cKinectCtrl.SetElevationAngle(0); // サーボ角制御. while(1){ // キャプチャ処理. lRes = cKinectCtrl.GrabFrame(); if(lRes != 0){ break; } // RGB画像. IplImage* rgbImage = NULL; lRes = cKinectCtrl.GetRGBFrame(rgbImage); if(lRes != 0){ break; } cvShowImage("RGB", rgbImage); // DEPTH画像. IplImage* depthSrc = NULL; lRes = cKinectCtrl.GetDepthFrame(depthSrc); if(lRes != 0){ break; } cvShowImage("DEPTH", depthSrc); int key = cvWaitKey(10); if((key == 27)||(key == 'q')){ break; } } return 0; }
- 追伸