2012年4月1日日曜日

Kinect for Windows SDKによる3次元ポイントクラウド

以前、OpenNIを用いたポイントクラウドの方法を紹介していました
 Kinect OpenNIによる3次元ポイントクラウド - 3次元描画 高速化

今回はマイクロソフトが公式で公開しているKinect for Windows SDKを使ってポイントクラウドを行ってみます。

描画にはいつもどおりOpenGLを使い、その過程でOpenCVを使っています。




まず初めにKinect SDKでRGB画像とDepth画像の取得方法について簡単に説明します。

細かい説明なんてどうでもいいんだよ!って人は 下のほうでサンプルプログラムがダウンロードできるので、下の方へスクロールしてってください。


では、プログラムの初期化処理は以下のようになる

//初期化
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;
}
view raw init.cpp hosted with ❤ by GitHub


初期化処理では
HRESULT NuiInitialize(
         DWORD dwFlags
)

で初期化します。

引数はRGB画像、Depth画像、骨格情報を得る場合
 NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON
とします
//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 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画像を取得します
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画像を処理
view raw getImage.cpp hosted with ❤ by GitHub

扱いやすい形にするため、OpenCVのMatに格納します
また、その処理を関数化します
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;
}
view raw getImage.cpp hosted with ❤ by GitHub


ここではGetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle)
という関数を作りました。

この関数は以下のように用います。
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");
}
view raw getImage.cpp hosted with ❤ by GitHub

関数の戻り値が-1の時は取得失敗ということです。

Kinect SDKではOpenNIを違い、Matへの格納が少しめんどうですね。

 次にポイントクラウドを行う上で、画像のピクセルが現実座標ではどこにあるのかを知る必要があります。

OpenNIではConvertProjectiveToRealWorldという関数がありました。

Kinect SDKでは以下の関数を用います

Vector4 NuiTransformDepthImageToSkeleton(
         LONG lDepthX,
         LONG lDepthY,
         USHORT usDepthValue
)


この関数はDepth画像のピクセルの位置(x,y)とその場所の値を与えることで現実座標系で帰ってきます。

では、Depth画像から現実座標配列を格納する関数を以下のように作ります。
//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に対応していなかったからかなぁっと思います。


で、最後にポイントクラウドで描画します。
//ポイントクラウド描画
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が呼ばれてるんですけどね





これまで長く説明してきましたけど、まあ、こんな説明読むよりサンプルプログラムを見たほうが早いかな?

全体のプログラムはこんな感じです。
/**
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
サンプルダウンロード

22 件のコメント:

  1. 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 スキップ ==========
    というエラーが出ます。

    どうすれば解決できるか至急教えてください。

    返信削除
    返信
    1. >>後藤さん
      はじめまして。
      エラーに関してですが、拝見したところOpenGLとOpenCVのライブラリにリンクできていないようです。
      環境がわからないのでどこをどうすれば良いとは答えられないのですが、OpenGLやOpenCVの再インストールしたりしてインストールを確認したり、ライブラリのリンクや環境変数にPathが通っているかを確認してみてください。

      削除
  2. OpenCVは2.0と2.2のbinのパスを通してますが、OpenGLの場合どうすればよいのかわからないので、詳しく教えてください。

    返信削除
    返信
    1. 私は、正直OpenGLの設定はよく覚えていないのでこちらのサイトを参考にしてみてください。
      http://opengl.softwarecarpenter.biz/?eid=949759

      とは言ったものの、今までglutを使ってプログラムを作っていられましたら、関係無いかもしれません。

      削除
    2. 関係ないものを進められても困ります。
      必要なソフトと、VSに必要な設定を詳しく教えてください。

      削除
    3. 関係ない・・・と言うことは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全体の設定にミスがあるかもしれません。

      削除
    4. いえ、前の文章で、「関係無いかもしれません」と書いていたもので。また、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 が未解決です。

      削除
    5. 「関係無いかもしれません」というのは、紹介するサイトがglutのインストール方法が記載されたページでしたので、OpenGL(glut)を使ったプログラムを今まで開発しておられた場合、インストール関係のサイトを紹介してもエラーとは関係なさそう、という意味です。

      上記エラーはOpenCVのリンカエラーですね。
      サンプルのプログラムには追加していない関数がエラーで出ているのがよくわかりませんが。
      プログラムに下記を追加してみてください
      #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_objdetect220d.lib")
      こちらも再現ができないので憶測です。

      2つお聞きしたいのですが、後藤さんはOpenCVやOpenGLでの開発経験はございますか?
      今までこれらのライブラリで作られたプログラムをコンパイルし実行したことはありますか?

      削除
    6. コメントありがとうございます。
      kassy708さんのおかげでプログラムを実行することができました。
      失礼ですが、マウスで思うように3D画像を回転したり、移動できないません。

      また、OpenCVは4年間使用していますが、OpenGLはあまり使用していません。

      削除
    7. プログラムが動いてよかったです。
      OpenGLの方は経験が浅いということですが、無事動いたようで安心しました。

      マウスの操作で思うように動かしたいということですが、操作に関しては人それぞれですので、思うような操作を行えるようプログラムを各自で改良していただくしかありません。
      サンプルのプログラムではpolarview()で視点の操作を行なっていますが、この関数を改良したり、まったく別の関数を独自で作るなどして頑張ってください。

      削除
  3. 後藤です。
    kassy708様、解答をコメントに載せていただき、まことにありがとうございます。
    あれから、私は、PCLとkinectSDKを用いて、物体認識の研究を行っています。
    それと、非常に申し訳ありませんが、またわからないところがでてきました。
    PCLとkinectSDKを用いて、http://d.hatena.ne.jp/kirii/20111120/1321779238のサンプル1のような3次元の点群画像(深度画像)と、深度画像の各画素の3次元座標値を検出する方法がわからないので、教えてていただけないでしょうか。

    返信削除
    返信
    1. 申し訳ありませんが、私は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"

      削除
    2. コメントありがとうございます、kassy708様。
      申し訳ありません、PCLを使わず、Kinect SDKのみでhttp://d.hatena.ne.jp/kirii/20111120/1321779238のサンプル1のような3次元の点群画像(深度画像)と、深度画像の各画素の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()で取得できます。

      削除
    4. 申し訳ありません。言葉足らずでした。
      pclは使用せず、kinect sdk, OpenCV, OpenGLを使用して3次元の点群画像と、各画素の座標情報を取得することを考えています。

      削除
  4. たびたびすみません、kassy708様
    プログラムはエラーなく通りましたが、出力されたOpenGLが真っ白いままで、深度画像が表示されません。

    返信削除
    返信
    1. 申し訳ありません。出力画面は表示できるようになりました。

      削除
    2. 解決されたようで、安心しました。

      削除
  5. このサンプルプログラムをニアモードで実行したいのですが、
    距離カメラ初期化後に、
    NuiImageStreamSetImageFrameFlags(m_pDepthStreamHandle, NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE);
    の一行を追加してみたのですが、ニアモードになりませんでした。
    どのようにすればうまく表示されるのでしょうか。。。
    ご教示お願いします。

    返信削除
  6. すみません。自己解決しました。

    返信削除
  7. わたしもこのサンプルプログラムをニアモードで実行したいのですが、同じことしていてニアモードになりませんでした。
    自己解決できないのでご教示お願いします。

    返信削除
  8. ちょいと質問です。
    NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolutionの第六引数は0がセットされていますが、USHORTでdepth情報を指定しなくてもいいのでしょうか。こちらで同じようにやると、どうもRGBとDepthのズレが修正できてない気がします。素朴に考えると、距離の補正がいらなければ、そもそも毎ループごとにこの関数を呼ばなくても、初期化で一回だけ呼べばいいだけな気がします。で、試しにdepthを入れて見たのですが、それでもうまく動かないようです。ご教授、ご助言いただけるとうれしいです。

    返信削除