Kinect OpenNIによる3次元ポイントクラウド - 3次元描画 高速化
今回はマイクロソフトが公式で公開しているKinect for Windows SDKを使ってポイントクラウドを行ってみます。
描画にはいつもどおりOpenGLを使い、その過程でOpenCVを使っています。
まず初めにKinect SDKでRGB画像とDepth画像の取得方法について簡単に説明します。
細かい説明なんてどうでもいいんだよ!って人は 下のほうでサンプルプログラムがダウンロードできるので、下の方へスクロールしてってください。
では、プログラムの初期化処理は以下のようになる
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//初期化 | |
int init(){ | |
//Kinectの初期化関数 | |
NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON ); | |
//各ハンドルの設定 | |
m_hNextImageFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
m_pImageStreamHandle = NULL; | |
m_hNextDepthFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
m_pDepthStreamHandle = NULL; | |
//深度センサストリームの設定 | |
HRESULT hr; | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR , NUI_IMAGE_RESOLUTION_640x480 , 0 , 2 , m_hNextImageFrameEvent , &m_pImageStreamHandle ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_320x240 , 0 , 2 , m_hNextDepthFrameEvent , &m_pDepthStreamHandle ); | |
#else if KINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_640x480 , 0 , 2 , m_hNextDepthFrameEvent , &m_pDepthStreamHandle ); | |
#endif | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
return 1; | |
} |
初期化処理では
HRESULT NuiInitialize( DWORD dwFlags )
で初期化します。
引数はRGB画像、Depth画像、骨格情報を得る場合
NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON
とします
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Kinectの初期化関数 | |
NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//各ハンドルの設定 | |
m_hNextImageFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
m_pImageStreamHandle = NULL; | |
m_hNextDepthFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
m_pDepthStreamHandle = NULL; | |
//深度センサストリームの設定 | |
HRESULT hr; | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR , NUI_IMAGE_RESOLUTION_640x480 , 0 , 2 , m_hNextImageFrameEvent , &m_pImageStreamHandle ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_320x240 , 0 , 2 , m_hNextDepthFrameEvent , &m_pDepthStreamHandle ); | |
#else if KKINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_640x480 , 0 , 2 , m_hNextDepthFrameEvent , &m_pDepthStreamHandle ); | |
#endif | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 |
Depth画像の大きさによって使用するNuiImageStreamOpen関数の引数が違うので
KINECT_DEPTH_WIDTHを#defineで事前に定義して、ここで条件分岐しています。
KinectSDKはBetaの時はDepth画像の解像度を320x240しか設定できなかったのですがv1.0になり、640x480に設定することが可能となりました。
上記で設定したイベントで以下のようにしてRGB画像とDepth画像を取得します
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const NUI_IMAGE_FRAME *pImageFrame = NULL; | |
//次のRGBフレームが来るまで待機 | |
WaitForSingleObject(m_hNextImageFrameEvent,INFINITE); | |
HRESULT hr = NuiImageStreamGetNextFrame(m_pImageStreamHandle, 30 , &pImageFrame ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
//Todo:RGB画像を処理 | |
//次のDepthフレームが来るまで待機 | |
WaitForSingleObject(m_hNextDepthFrameEvent,INFINITE); | |
HRESULT hr = NuiImageStreamGetNextFrame(m_pDepthStreamHandle, 30 , &pImageFrame ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
//Todo:Depth画像を処理 |
扱いやすい形にするため、OpenCVのMatに格納します
また、その処理を関数化します
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int GetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle){ | |
//フレームを入れるクラス | |
const NUI_IMAGE_FRAME *pImageFrame = NULL; | |
//次のRGBフレームが来るまで待機 | |
WaitForSingleObject(frameEvent,INFINITE); | |
HRESULT hr = NuiImageStreamGetNextFrame(streamHandle, 30 , &pImageFrame ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
//フレームから画像データの取得 | |
INuiFrameTexture * pTexture = pImageFrame->pFrameTexture; | |
NUI_LOCKED_RECT LockedRect; | |
pTexture->LockRect( 0, &LockedRect, NULL, 0 ); | |
if( LockedRect.Pitch != 0 ){ | |
//pBitsに画像データが入っている | |
BYTE *pBuffer = (BYTE*) LockedRect.pBits; | |
memcpy(image.data,pBuffer,image.step * image.rows); | |
} | |
hr = NuiImageStreamReleaseFrame( streamHandle, pImageFrame ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
return 0; | |
} |
ここではGetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle)
という関数を作りました。
この関数は以下のように用います。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if(GetImage(image,m_hNextImageFrameEvent,m_pImageStreamHandle)==-1){ | |
printf("Failed get RGB image\n"); | |
} | |
if(GetImage(depth,m_hNextDepthFrameEvent,m_pDepthStreamHandle)==-1){ | |
printf("Failed get depth image\n"); | |
} |
関数の戻り値が-1の時は取得失敗ということです。
Kinect SDKではOpenNIを違い、Matへの格納が少しめんどうですね。
次にポイントクラウドを行う上で、画像のピクセルが現実座標ではどこにあるのかを知る必要があります。
OpenNIではConvertProjectiveToRealWorldという関数がありました。
Kinect SDKでは以下の関数を用います
Vector4 NuiTransformDepthImageToSkeleton(
LONG lDepthX,
LONG lDepthY,
USHORT usDepthValue
)
この関数はDepth画像のピクセルの位置(x,y)とその場所の値を与えることで現実座標系で帰ってきます。
では、Depth画像から現実座標配列を格納する関数を以下のように作ります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//3次元ポイントクラウドのための座標変換 | |
void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ){ | |
unsigned short* dp = (unsigned short*)depth.data; | |
Point3f *point = (Point3f *)pointCloud_XYZ.data; | |
for(int y = 0;y < depth.rows;y++){ | |
for(int x = 0;x < depth.cols;x++, dp++,point++){ | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
Vector4 RealPoints = NuiTransformDepthImageToSkeleton(x,y,*dp); | |
#else if KINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
Vector4 RealPoints = NuiTransformDepthImageToSkeleton(x,y,*dp, NUI_IMAGE_RESOLUTION_640x480); | |
#endif | |
point->x = RealPoints.x; | |
point->y = RealPoints.y; | |
point->z = RealPoints.z; | |
} | |
} | |
} |
NuiTransformDepthImageToSkeletonはDepth画像の解像度により引数を追加します。
これはbetaの時にDepth画像が640x480に対応していなかったからかなぁっと思います。
で、最後にポイントクラウドで描画します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//ポイントクラウド描画 | |
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ){ | |
static int x,y; | |
glPointSize(2); | |
glBegin(GL_POINTS); | |
uchar *p; | |
Point3f *point = (Point3f*)pointCloud_XYZ.data; | |
LONG colorX,colorY; | |
for(y = 0;y < pointCloud_XYZ.rows;y++){ | |
for(x = 0;x < pointCloud_XYZ.cols;x++,point++){ | |
if(point->z == 0) | |
continue; | |
//ズレを直す | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
NuiImageGetColorPixelCoordinatesFromDepthPixel(NUI_IMAGE_RESOLUTION_640x480,NULL,x,y,0,&colorX,&colorY); | |
#else if KINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution(NUI_IMAGE_RESOLUTION_640x480,NUI_IMAGE_RESOLUTION_640x480,NULL,x,y,0,&colorX,&colorY); | |
#endif | |
//画像内の場合 | |
if(0 <= colorX && colorX <= rgbImage.cols && 0 <= colorY && colorY <= rgbImage.rows){ | |
p = &rgbImage.data[colorY * rgbImage.step + colorX * rgbImage.channels()]; | |
glColor3ubv(p); | |
glVertex3f(point->x,point->y,point->z); | |
} | |
} | |
} | |
glEnd(); | |
} |
drawPointCloudではRGB画像とretrievePointCloudMapで取得した三次元位置を渡し、描画します。
また、この関数内でRGBとDepthのズレを補正しています。
OpepNIでいう
DepthGenerator.GetAlternativeViewPointCap().SetViewPoint は
KinectSDKでは
NuiImageGetColorPixelCoordinatesFromDepthPixel
または
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution
を使います
これも、Depthの解像度により関数を変える必要があります。
まあ、NuiImageGetColorPixelCoordinatesFromDepthPixelのなかでNuiImageGetColorPixelCoordinatesFromDepthPixelAtResolutionが呼ばれてるんですけどね
これまで長く説明してきましたけど、まあ、こんな説明読むよりサンプルプログラムを見たほうが早いかな?
全体のプログラムはこんな感じです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
author @kassy708 | |
OpenGLとKinect SDK v1.0を使ったポイントクラウド | |
*/ | |
#include<Windows.h> | |
#include<NuiApi.h> //KinectSDK利用時にinclude | |
#pragma comment(lib,"Kinect10.lib") | |
#include <GL/glut.h> | |
#include <opencv2/opencv.hpp> | |
#ifdef _DEBUG | |
//Debugモードの場合 | |
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220d.lib") // opencv_core | |
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220d.lib") // opencv_imgproc | |
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220d.lib") // opencv_highgui | |
#else | |
//Releaseモードの場合 | |
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220.lib") // opencv_core | |
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220.lib") // opencv_imgproc | |
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220.lib") // opencv_highgui | |
#endif | |
using namespace cv; | |
//openNIのための宣言・定義 | |
//マクロ定義 | |
#define KINECT_IMAGE_WIDTH 640 | |
#define KINECT_IMAGE_HEGIHT 480 | |
#define KINECT_DEPTH_WIDTH 640 | |
#define KINECT_DEPTH_HEGIHT 480 | |
//#define KINECT_DEPTH_WIDTH 320 | |
//#define KINECT_DEPTH_HEGIHT 240 | |
Mat image(KINECT_IMAGE_HEGIHT,KINECT_IMAGE_WIDTH,CV_8UC4); | |
Mat depth(KINECT_DEPTH_HEGIHT,KINECT_DEPTH_WIDTH,CV_16UC1); | |
//ポイントクラウドの座標 | |
Mat pointCloud_XYZ(KINECT_DEPTH_HEGIHT,KINECT_DEPTH_WIDTH,CV_32FC3,cv::Scalar::all(0)); | |
void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ); //3次元ポイントクラウドのための座標変換 | |
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ); //ポイントクラウド描画 | |
//画像データを取得する関数 | |
int GetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle); | |
//openGLのための宣言・定義 | |
//---変数宣言--- | |
int FormWidth = 640; | |
int FormHeight = 480; | |
int mButton; | |
float twist, elevation, azimuth; | |
float cameraDistance = 0,cameraX = 0,cameraY = 0; | |
int xBegin, yBegin; | |
//---マクロ定義--- | |
#define glFovy 45 //視角度 | |
#define glZNear 1.0 //near面の距離 | |
#define glZFar 150.0 //far面の距離 | |
void polarview(); //視点変更 | |
//ハンドル | |
HANDLE m_hNextImageFrameEvent; | |
HANDLE m_hNextDepthFrameEvent; | |
HANDLE m_pImageStreamHandle; | |
HANDLE m_pDepthStreamHandle; | |
//描画 | |
void display(){ | |
// clear screen and depth buffer | |
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); | |
// Reset the coordinate system before modifying | |
glLoadIdentity(); | |
glEnable(GL_DEPTH_TEST); //「Zバッファ」を有効 | |
gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0); //視点の向き設定 | |
if(GetImage(image,m_hNextImageFrameEvent,m_pImageStreamHandle)==-1) | |
return; | |
if(GetImage(depth,m_hNextDepthFrameEvent,m_pDepthStreamHandle)==-1) | |
return; | |
//3次元ポイントクラウドのための座標変換 | |
retrievePointCloudMap(depth,pointCloud_XYZ); | |
//視点の変更 | |
polarview(); | |
imshow("image",image); | |
imshow("depth",depth); | |
//RGBAからBGRAに変換 | |
cvtColor(image,image,CV_RGBA2BGRA); | |
//ポイントクラウド | |
drawPointCloud(image,pointCloud_XYZ); | |
glFlush(); | |
glutSwapBuffers(); | |
} | |
//初期化 | |
int init(){ | |
//Kinectの初期化関数 | |
NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON ); | |
//各ハンドルの設定 | |
m_hNextImageFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
m_pImageStreamHandle = NULL; | |
m_hNextDepthFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
m_pDepthStreamHandle = NULL; | |
//深度センサストリームの設定 | |
HRESULT hr; | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR , NUI_IMAGE_RESOLUTION_640x480 , 0 , 2 , m_hNextImageFrameEvent , &m_pImageStreamHandle ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_320x240 , 0 , 2 , m_hNextDepthFrameEvent , &m_pDepthStreamHandle ); | |
#else if KINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_640x480 , 0 , 2 , m_hNextDepthFrameEvent , &m_pDepthStreamHandle ); | |
#endif | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
return 1; | |
} | |
// アイドル時のコールバック | |
void idle(){ | |
//再描画要求 | |
glutPostRedisplay(); | |
} | |
//ウィンドウのサイズ変更 | |
void reshape (int width, int height){ | |
FormWidth = width; | |
FormHeight = height; | |
glViewport (0, 0, (GLsizei)width, (GLsizei)height); | |
glMatrixMode (GL_PROJECTION); | |
glLoadIdentity (); | |
//射影変換行列の指定 | |
gluPerspective (glFovy, (GLfloat)width / (GLfloat)height,glZNear,glZFar); | |
glMatrixMode (GL_MODELVIEW); | |
} | |
//マウスの動き | |
void motion(int x, int y){ | |
int xDisp, yDisp; | |
xDisp = x - xBegin; | |
yDisp = y - yBegin; | |
switch (mButton) { | |
case GLUT_LEFT_BUTTON: | |
azimuth += (float) xDisp/2.0; | |
elevation -= (float) yDisp/2.0; | |
break; | |
case GLUT_MIDDLE_BUTTON: | |
cameraX -= (float) xDisp/40.0; | |
cameraY += (float) yDisp/40.0; | |
break; | |
case GLUT_RIGHT_BUTTON: | |
cameraDistance += xDisp/40.0; | |
break; | |
} | |
xBegin = x; | |
yBegin = y; | |
} | |
//マウスの操作 | |
void mouse(int button, int state, int x, int y){ | |
if (state == GLUT_DOWN) { | |
switch(button) { | |
case GLUT_RIGHT_BUTTON: | |
case GLUT_MIDDLE_BUTTON: | |
case GLUT_LEFT_BUTTON: | |
mButton = button; | |
break; | |
} | |
xBegin = x; | |
yBegin = y; | |
} | |
} | |
//視点変更 | |
void polarview(){ | |
glTranslatef( cameraX, cameraY, cameraDistance); | |
glRotatef( -twist, 0.0, 0.0, 1.0); | |
glRotatef( -elevation, 1.0, 0.0, 0.0); | |
glRotatef( -azimuth, 0.0, 1.0, 0.0); | |
} | |
//メイン | |
int main(int argc, char *argv[]){ | |
glutInit(&argc, argv); | |
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB); | |
glutInitWindowSize(FormWidth, FormHeight); | |
glutCreateWindow(argv[0]); | |
//コールバック | |
glutReshapeFunc (reshape); | |
glutDisplayFunc(display); | |
glutIdleFunc(idle); | |
glutMouseFunc(mouse); | |
glutMotionFunc(motion); | |
init(); | |
glutMainLoop(); | |
NuiShutdown(); | |
return 0; | |
} | |
int GetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle){ | |
//フレームを入れるクラス | |
const NUI_IMAGE_FRAME *pImageFrame = NULL; | |
//次のRGBフレームが来るまで待機 | |
WaitForSingleObject(frameEvent,INFINITE); | |
HRESULT hr = NuiImageStreamGetNextFrame(streamHandle, 30 , &pImageFrame ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
//フレームから画像データの取得 | |
INuiFrameTexture * pTexture = pImageFrame->pFrameTexture; | |
NUI_LOCKED_RECT LockedRect; | |
pTexture->LockRect( 0, &LockedRect, NULL, 0 ); | |
if( LockedRect.Pitch != 0 ){ | |
//pBitsに画像データが入っている | |
BYTE *pBuffer = (BYTE*) LockedRect.pBits; | |
memcpy(image.data,pBuffer,image.step * image.rows); | |
} | |
hr = NuiImageStreamReleaseFrame( streamHandle, pImageFrame ); | |
if( FAILED( hr ) ) | |
return -1;//取得失敗 | |
return 0; | |
} | |
//ポイントクラウド描画 | |
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ){ | |
static int x,y; | |
glPointSize(2); | |
glBegin(GL_POINTS); | |
uchar *p; | |
Point3f *point = (Point3f*)pointCloud_XYZ.data; | |
LONG colorX,colorY; | |
for(y = 0;y < pointCloud_XYZ.rows;y++){ | |
for(x = 0;x < pointCloud_XYZ.cols;x++,point++){ | |
if(point->z == 0) | |
continue; | |
//ズレを直す | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
NuiImageGetColorPixelCoordinatesFromDepthPixel(NUI_IMAGE_RESOLUTION_640x480,NULL,x,y,0,&colorX,&colorY); | |
#else if KINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution(NUI_IMAGE_RESOLUTION_640x480,NUI_IMAGE_RESOLUTION_640x480,NULL,x,y,0,&colorX,&colorY); | |
#endif | |
//画像内の場合 | |
if(0 <= colorX && colorX <= rgbImage.cols && 0 <= colorY && colorY <= rgbImage.rows){ | |
p = &rgbImage.data[colorY * rgbImage.step + colorX * rgbImage.channels()]; | |
glColor3ubv(p); | |
glVertex3f(point->x,point->y,point->z); | |
} | |
} | |
} | |
glEnd(); | |
} | |
//3次元ポイントクラウドのための座標変換 | |
void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ){ | |
unsigned short* dp = (unsigned short*)depth.data; | |
Point3f *point = (Point3f *)pointCloud_XYZ.data; | |
for(int y = 0;y < depth.rows;y++){ | |
for(int x = 0;x < depth.cols;x++, dp++,point++){ | |
#if KINECT_DEPTH_WIDTH == 320 | |
//奥行き画像の解像度が320x240の場合 | |
Vector4 RealPoints = NuiTransformDepthImageToSkeleton(x,y,*dp); | |
#else if KINECT_DEPTH_WIDTH == 640 | |
//奥行き画像の解像度が640x480の場合 | |
Vector4 RealPoints = NuiTransformDepthImageToSkeleton(x,y,*dp, NUI_IMAGE_RESOLUTION_640x480); | |
#endif | |
point->x = RealPoints.x; | |
point->y = RealPoints.y; | |
point->z = RealPoints.z; | |
} | |
} | |
} |
github
サンプルダウンロード
windows7 32bit VC++2010でKinect for Windows SDKによる3次元ポイントクラウド を実行すると
返信削除1>------ ビルド開始: プロジェクト: fukuda, 構成: Debug Win32 ------
1>main.obj : error LNK2019: 未解決の外部シンボル _glutSwapBuffers@0 が関数 "void __cdecl display(void)" (?display@@YAXXZ) で参照されました。
1>main.obj : error LNK2019: 未解決の外部シンボル "void __cdecl cv::cvtColor(class cv::Mat const &,class cv::Mat &,int,int)" (?cvtColor@cv@@YAXABVMat@1@AAV21@HH@Z) が関数 "void __cdecl display(void)" (?display@@YAXXZ) で参照されました。
・・・・・
1>C:\Users\mori\Documents\Visual Studio 2010\Projects\fukuda\Debug\fukuda.exe : fatal error LNK1120: 外部参照 51 が未解決です。
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
というエラーが出ます。
どうすれば解決できるか至急教えてください。
>>後藤さん
削除はじめまして。
エラーに関してですが、拝見したところOpenGLとOpenCVのライブラリにリンクできていないようです。
環境がわからないのでどこをどうすれば良いとは答えられないのですが、OpenGLやOpenCVの再インストールしたりしてインストールを確認したり、ライブラリのリンクや環境変数にPathが通っているかを確認してみてください。
OpenCVは2.0と2.2のbinのパスを通してますが、OpenGLの場合どうすればよいのかわからないので、詳しく教えてください。
返信削除私は、正直OpenGLの設定はよく覚えていないのでこちらのサイトを参考にしてみてください。
削除http://opengl.softwarecarpenter.biz/?eid=949759
とは言ったものの、今までglutを使ってプログラムを作っていられましたら、関係無いかもしれません。
関係ないものを進められても困ります。
削除必要なソフトと、VSに必要な設定を詳しく教えてください。
関係ない・・・と言うことはglutでの開発経験があるということですね。
削除今までOpenCVやOpenGLを使っていてこのエラーが出たことはありませんか?
こちらで再現することができないので、憶測で解決策を列挙してみます。
・OpenGLライブラリのリンク
プログラムに下記を追加
#pragma comment(lib, "OpenGL32.lib")
・freeglutをインストールしてみる
下記のサイトを参考に
http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html#2.3
・OpenCVのエラー
cvtColorの一行をコメントアウトしてみてください。
OpenCVのエラーがなくなれば、cvtColorに関するヘッダやライブラリの設定にミスがあるか
別のエラーが出た場合、OpenCV全体の設定にミスがあるかもしれません。
いえ、前の文章で、「関係無いかもしれません」と書いていたもので。また、kassy708さまがApr 10, 2012 07:44 PMのメールを出す前に、OpenGLを設定し直していたら、新たに以下のエラーが出ました。
削除1>------ ビルド開始: プロジェクト: fukuda, 構成: Debug Win32 ------
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::setSVMDetector(class std::vector > const &)" (?setSVMDetector@HOGDescriptor@cv@@UAEXABV?$vector@MV?$allocator@M@std@@@std@@@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual bool __thiscall cv::HOGDescriptor::read(class cv::FileNode &)" (?read@HOGDescriptor@cv@@UAE_NAAVFileNode@2@@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::write(class cv::FileStorage &,class std::basic_string,class std::allocator > const &)const " (?write@HOGDescriptor@cv@@UBEXAAVFileStorage@2@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual bool __thiscall cv::HOGDescriptor::load(class std::basic_string,class std::allocator > const &,class std::basic_string,class std::allocator > const &)" (?load@HOGDescriptor@cv@@UAE_NABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::save(class std::basic_string,class std::allocator > const &,class std::basic_string,class std::allocator > const &)const " (?save@HOGDescriptor@cv@@UBEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::copyTo(struct cv::HOGDescriptor &)const " (?copyTo@HOGDescriptor@cv@@UBEXAAU12@@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::compute(class cv::Mat const &,class std::vector > &,class cv::Size_,class cv::Size_,class std::vector,class std::allocator > > const &)const " (?compute@HOGDescriptor@cv@@UBEXABVMat@2@AAV?$vector@MV?$allocator@M@std@@@std@@V?$Size_@H@2@2ABV?$vector@V?$Point_@H@cv@@V?$allocator@V?$Point_@H@cv@@@std@@@5@@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::detect(class cv::Mat const &,class std::vector,class std::allocator > > &,double,class cv::Size_,class cv::Size_,class std::vector,class std::allocator > > const &)const " (?detect@HOGDescriptor@cv@@UBEXABVMat@2@AAV?$vector@V?$Point_@H@cv@@V?$allocator@V?$Point_@H@cv@@@std@@@std@@NV?$Size_@H@2@2ABV45@@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::detectMultiScale(class cv::Mat const &,class std::vector,class std::allocator > > &,double,class cv::Size_,class cv::Size_,double,int)const " (?detectMultiScale@HOGDescriptor@cv@@UBEXABVMat@2@AAV?$vector@V?$Rect_@H@cv@@V?$allocator@V?$Rect_@H@cv@@@std@@@std@@NV?$Size_@H@2@2NH@Z)" は未解決です。
1>main.obj : error LNK2001: 外部シンボル ""public: virtual void __thiscall cv::HOGDescriptor::computeGradient(class cv::Mat const &,class cv::Mat &,class cv::Mat &,class cv::Size_,class cv::Size_)const " (?computeGradient@HOGDescriptor@cv@@UBEXABVMat@2@AAV32@1V?$Size_@H@2@2@Z)" は未解決です。
1>MSVCRTD.lib(crtexew.obj) : error LNK2019: 未解決の外部シンボル _WinMain@16 が関数 ___tmainCRTStartup で参照されました。
1>C:\Users\morisa\Documents\Visual Studio 2010\Projects\fukuda\Debug\fukuda.exe : fatal error LNK1120: 外部参照 11 が未解決です。
「関係無いかもしれません」というのは、紹介するサイトがglutのインストール方法が記載されたページでしたので、OpenGL(glut)を使ったプログラムを今まで開発しておられた場合、インストール関係のサイトを紹介してもエラーとは関係なさそう、という意味です。
削除上記エラーはOpenCVのリンカエラーですね。
サンプルのプログラムには追加していない関数がエラーで出ているのがよくわかりませんが。
プログラムに下記を追加してみてください
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_objdetect220d.lib")
こちらも再現ができないので憶測です。
2つお聞きしたいのですが、後藤さんはOpenCVやOpenGLでの開発経験はございますか?
今までこれらのライブラリで作られたプログラムをコンパイルし実行したことはありますか?
コメントありがとうございます。
削除kassy708さんのおかげでプログラムを実行することができました。
失礼ですが、マウスで思うように3D画像を回転したり、移動できないません。
また、OpenCVは4年間使用していますが、OpenGLはあまり使用していません。
プログラムが動いてよかったです。
削除OpenGLの方は経験が浅いということですが、無事動いたようで安心しました。
マウスの操作で思うように動かしたいということですが、操作に関しては人それぞれですので、思うような操作を行えるようプログラムを各自で改良していただくしかありません。
サンプルのプログラムではpolarview()で視点の操作を行なっていますが、この関数を改良したり、まったく別の関数を独自で作るなどして頑張ってください。
後藤です。
返信削除kassy708様、解答をコメントに載せていただき、まことにありがとうございます。
あれから、私は、PCLとkinectSDKを用いて、物体認識の研究を行っています。
それと、非常に申し訳ありませんが、またわからないところがでてきました。
PCLとkinectSDKを用いて、http://d.hatena.ne.jp/kirii/20111120/1321779238のサンプル1のような3次元の点群画像(深度画像)と、深度画像の各画素の3次元座標値を検出する方法がわからないので、教えてていただけないでしょうか。
申し訳ありませんが、私はPCLを使ったことがないのでわかりません。
削除調べた感じだと、PCLはKinect SDKをサポートしていないとありました。
http://www.pcl-users.org/Getting-Data-through-Kinect-SDK-td3771858.html
"No, MS Kinect SDK is not supported in PCL"
コメントありがとうございます、kassy708様。
削除申し訳ありません、PCLを使わず、Kinect SDKのみでhttp://d.hatena.ne.jp/kirii/20111120/1321779238のサンプル1のような3次元の点群画像(深度画像)と、深度画像の各画素の3次元座標値を検出する方法を教えていただけないでしょうか。
後藤様
削除Kinect SDKのみでというのはOpenGLやOpenCVといった他のライブラリを使用しないということでしょうか。その場合はDirectXを使ったりするのでしょうが、私にはわかりません。
OpenGL、OpenCVを使うということでしたら、お答えできます。
「サンプル1のような3次元の点群画像(深度画像)」というのは色が付いてない白色のポイントクラウドの画像のことでよろしかったでしょうか。
本記事サンプルプログラムのvoid drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ)関数内にあるglColor3ubv(p);をglColor3ub(255,255,255);とすればよいと思います。
「深度画像の各画素の3次元座標値」というのは本記事のretrievePointCloudMap()関数を参考にしてください。NuiTransformDepthImageToSkeleton()で取得できます。
申し訳ありません。言葉足らずでした。
削除pclは使用せず、kinect sdk, OpenCV, OpenGLを使用して3次元の点群画像と、各画素の座標情報を取得することを考えています。
たびたびすみません、kassy708様
返信削除プログラムはエラーなく通りましたが、出力されたOpenGLが真っ白いままで、深度画像が表示されません。
申し訳ありません。出力画面は表示できるようになりました。
削除解決されたようで、安心しました。
削除このサンプルプログラムをニアモードで実行したいのですが、
返信削除距離カメラ初期化後に、
NuiImageStreamSetImageFrameFlags(m_pDepthStreamHandle, NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE);
の一行を追加してみたのですが、ニアモードになりませんでした。
どのようにすればうまく表示されるのでしょうか。。。
ご教示お願いします。
すみません。自己解決しました。
返信削除わたしもこのサンプルプログラムをニアモードで実行したいのですが、同じことしていてニアモードになりませんでした。
返信削除自己解決できないのでご教示お願いします。
ちょいと質問です。
返信削除NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolutionの第六引数は0がセットされていますが、USHORTでdepth情報を指定しなくてもいいのでしょうか。こちらで同じようにやると、どうもRGBとDepthのズレが修正できてない気がします。素朴に考えると、距離の補正がいらなければ、そもそも毎ループごとにこの関数を呼ばなくても、初期化で一回だけ呼べばいいだけな気がします。で、試しにdepthを入れて見たのですが、それでもうまく動かないようです。ご教授、ご助言いただけるとうれしいです。