C ++アプリケーションでのMicrosoft Kinectの使用

はじめに



最近、MicrosoftはKinectツールキットのベータ版であるMicrosoft Research Kinect SDKをリリースしました。 ツールには、ヘッダーファイル、ライブラリ、およびC ++アプリケーションでの使用例が含まれています。 ただし、SDK自体が存在するだけでは、わかりやすい例やドキュメントがないという問題は解決しません。 Microsoftが.NET開発者により重点を置いていることは注目に値します。したがって、たとえば公式フォーラムでは、トピックの大部分はC#に関連しており、KinectのAPIの説明を見つけようとするGoogle検索は、いくつかのリンクのみを提供しています-公式ドキュメントへのリンクです。



この記事では、Microsoft Kinectを使用するためのオプション、および上記のC ++アプリケーションでwxWidgetsライブラリと組み合わせたソフトウェアツールについて説明します。







はじめに



開始するには、少なくとも開発ツールをダウンロードする必要があります。 これは、 Microsoft Research Kinect SDKページで実行できます。

また、wxWidgetsライブラリも必要です。 公式WebサイトまたはSVNリポジトリからダウンロードすることができます (私はSVN HEADを好みます -公式リリースにはない多くの新しい有用なものが含まれていますが、バグが頻繁に表示されます)。

「明るい」側を好み、無料のツールのみを使用してアプリケーションを開発することに熱心な場合は、 Visual C ++ 2010 Express 、およびWindows 7 (またはそれ以降) Microsoft Windows SDKをダウンロードし、それなしでVisual C ++ ExpressでwxWidgetsを構築し、ほとんどの場合失敗します。



wxWidgetsの収集



Visual StudioとKinect SDKのインストールプロセスは考慮しません。インストールウィザードの[次へ]ボタンを数回クリックするだけです。しかし、wxWidgetsビルドプロセスをより詳細に見ていきます。 アプリケーション開発の次のステップはそれに依存します。

wxWidgetsソースコードをダウンロードして別のフォルダーに解凍したら、 WXWIN環境変数を追加し、その値にwxWidgetsソースコードフォルダーへのパスを指定する必要があります。

SVNのソースコードを使用する場合、 %WXWIN%/ include / msw / setup0.hファイル%WXWIN%/ include / msw / setup.hにコピーする必要があります。

デフォルトでは、wxWidgetsにはいくつかの構成があります(図1)。



最初の2つの構成では、静的ライブラリの形式でwxWidgetsを構築できます。後者の場合-動的にロードされるいくつかのモジュールの形式で。







静的ライブラリの構築



静的ライブラリ(デバッグおよびリリース構成)を組み立てる前に、ソリューション内のすべてのプロジェクトのプロパティで、 C / C ++->コード生成->ランタイムライブラリパラメーターをそれぞれマルチスレッドデバッグとマルチスレッドに設定します(図2)。







これらのコンパイルオプションを設定すると、アプリケーションと一緒にエンドユーザーマシンにVisual C ++ Redistributableをインストールする必要がなくなります。 コンパイルオプションを設定したら、ソリューションを構築できます。 結果として、lib / vc_libサブディレクトリにいくつかの.libファイルを作成する必要があります。これは、後でアプリケーションで使用されます。



動的ライブラリを構築する



動的ライブラリを構築するには、コンパイラ設定で何も変更する必要はありません。 ただし、別の問題があります。ソリューションには依存関係がないため、ビルドプロセスを数回再起動する必要があります。 一部のライブラリをリンクするとエラーが発生します。 アセンブリ後、lib / vc_dllサブディレクトリにいくつかの.DLLおよび.LIBファイルを作成する必要があります。

ライブラリのバージョンを収集(デバッグ)およびデバッグおよび最適化(リリース)する必要があることに注意してください。



テストアプリケーションを作成する



したがって、現時点では次のとおりです。



アプリケーションの作成を開始できます。

テストアプリケーションには次のものがあります。



KinectTestApp.h

#ifndef _KINECTTESTAPP_H_

#define _KINECTTESTAPP_H_



#include "wx/image.h"

#include "KinectTestMainFrame.h"



class KinectTestApp: public wxApp

{

DECLARE_CLASS( KinectTestApp )

DECLARE_EVENT_TABLE()

public :

KinectTestApp();

void Init();

virtual bool OnInit();

virtual int OnExit();

};



DECLARE_APP(KinectTestApp)



#endif








KinectTestApp.cpp

...

bool KinectTestApp::OnInit()

{

#if wxUSE_LIBPNG

wxImage::AddHandler( new wxPNGHandler);

#endif

#if wxUSE_LIBJPEG

wxImage::AddHandler( new wxJPEGHandler);

#endif

KinectTestMainFrame* mainWindow = new KinectTestMainFrame( NULL );

mainWindow->Show( true );

return true ;

}








KinectTestMainFrame.h

class KinectTestMainFrame: public wxFrame, public wxThreadHelper

{

DECLARE_CLASS( KinectTestMainFrame )

DECLARE_EVENT_TABLE()

public :

KinectTestMainFrame();

KinectTestMainFrame( wxWindow* parent,

wxWindowID id = SYMBOL_KINECTTESTMAINFRAME_IDNAME,

const wxString& caption = SYMBOL_KINECTTESTMAINFRAME_TITLE,

const wxPoint& pos = SYMBOL_KINECTTESTMAINFRAME_POSITION,

const wxSize& size = SYMBOL_KINECTTESTMAINFRAME_SIZE,

long style = SYMBOL_KINECTTESTMAINFRAME_STYLE );

bool Create( wxWindow* parent,

wxWindowID id = SYMBOL_KINECTTESTMAINFRAME_IDNAME,

const wxString& caption = SYMBOL_KINECTTESTMAINFRAME_TITLE,

const wxPoint& pos = SYMBOL_KINECTTESTMAINFRAME_POSITION,

const wxSize& size = SYMBOL_KINECTTESTMAINFRAME_SIZE,

long style = SYMBOL_KINECTTESTMAINFRAME_STYLE );

~KinectTestMainFrame();

void Init();

void CreateControls();

wxBitmap GetBitmapResource( const wxString& name );

wxIcon GetIconResource( const wxString& name );

virtual wxThread::ExitCode Entry();



wxGridBagSizer* m_MainSizer;

wxListBox* m_DeviceListBox;

KinectCanvas* m_Canvas;

};



#endif








KinectTestMainFrame.cpp

...

void KinectTestMainFrame::CreateControls()

{

KinectTestMainFrame* itemFrame1 = this ;



m_MainSizer = new wxGridBagSizer(0, 0);

m_MainSizer->SetEmptyCellSize(wxSize(10, 20));

itemFrame1->SetSizer(m_MainSizer);



wxArrayString m_DeviceListBoxStrings;

m_DeviceListBox = new wxListBox( itemFrame1,

ID_DEVICE_LISTBOX, wxDefaultPosition,

wxDefaultSize, m_DeviceListBoxStrings,

wxLB_SINGLE );

m_MainSizer->Add(m_DeviceListBox,

wxGBPosition(0, 0), wxGBSpan(1, 1),

wxGROW|wxGROW|wxALL, 5);



m_Canvas = new KinectCanvas( itemFrame1,

ID_KINECT_CANVAS, wxDefaultPosition,

wxSize(320, 240), wxSIMPLE_BORDER );

m_MainSizer->Add(m_Canvas, wxGBPosition(0, 1),

wxGBSpan(1, 1), wxALIGN_CENTER_HORIZONTAL|

wxALIGN_CENTER_VERTICAL|wxALL, 5);

m_MainSizer->AddGrowableCol(1);

m_MainSizer->AddGrowableRow(0);

}

...

wxThread::ExitCode KinectTestMainFrame::Entry()

{

return NULL;

}








KinectCanvas.h

...

class KinectCanvas: public wxWindow

{

DECLARE_DYNAMIC_CLASS( KinectCanvas )

DECLARE_EVENT_TABLE()



public :

KinectCanvas();

KinectCanvas(wxWindow* parent,

wxWindowID id = ID_KINECTCANVAS,

const wxPoint& pos = wxDefaultPosition,

const wxSize& size = wxSize(100, 100),

long style = wxSIMPLE_BORDER);



bool Create(wxWindow* parent,

wxWindowID id = ID_KINECTCANVAS,

const wxPoint& pos = wxDefaultPosition,

const wxSize& size = wxSize(100, 100),

long style = wxSIMPLE_BORDER);

~KinectCanvas();



void Init();

void CreateControls();

void OnPaint( wxPaintEvent& event );



wxImage * GetCurrentImage() const { return m_CurrentImage ; }

void SetCurrentImage(wxImage * value ) { m_CurrentImage = value ; }



wxBitmap GetBitmapResource( const wxString& name );

wxIcon GetIconResource( const wxString& name );



wxImage * m_CurrentImage;

};



#endif










Kinectcanvas.cpp

...

IMPLEMENT_DYNAMIC_CLASS( KinectCanvas, wxWindow )

BEGIN_EVENT_TABLE( KinectCanvas, wxWindow )

EVT_PAINT( KinectCanvas::OnPaint )

END_EVENT_TABLE()

...

void KinectCanvas::OnPaint( wxPaintEvent& event )

{

wxAutoBufferedPaintDC dc( this );

if (!m_CurrentImage)

{

dc.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));

dc.Clear();

dc.DrawLabel(_( "No image" ), wxRect(dc.GetSize()),

wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL);

}

else

{

wxBitmap bmp(*m_CurrentImage);

dc.DrawBitmap(bmp,

(dc.GetSize().GetWidth()-bmp.GetWidth())/2,

(dc.GetSize().GetHeight()-bmp.GetHeight())/2);

}

}










上記のソースコードには、いくつかの空のメソッドと、明確ではないいくつかのメソッドがあります(たとえば、 GetIconResource()



GetBitmapResource()



Init()



)。 これはすべて、 DialogBlocksフォームデザイナがアプリケーションフレームワークの作成に使用されたためです。 これは有料のツールですが、試用版の機能でアプリケーションを作成できます。

アプリケーションをビルドする前に、wxWidgetsビルドオプションと一致するようにプロジェクト設定を変更する必要があります。 つまり、静的wxWidgetsライブラリを使用する場合は、 C / C ++->コード生成->ランタイムライブラリパラメーターのプロジェクトプロパティで、デバッグとリリースの構成に同じ値を設定する必要があります。 wxWidgetsダイナミックライブラリを使用する必要がある場合は、 C / C ++->プリプロセッサ->プリプロセッサ定義パラメータのプロジェクト設定で、 WXUSINGDLL



マクロを追加する必要があります。 このマクロは、動的なwxWidgetsライブラリの構築にも使用されるため、プロジェクトとwxWidgetsの設定は一致します(図3)。







また、アプリケーションのデバッグバージョンでは、リソースコンパイラ設定のプリプロセッサディレクティブにマクロwxUSE_NO_MANIFEST=1



を追加する必要があります。 これは、wxWidgetsリソースファイル( %WXWIN%/ include / msw / wx.rc )で指定されたマニフェストおよびVisual Studioがアプリケーションに自動的に追加するマニフェストと競合しないことを保証するためです。

上記の手順を実行した後、アプリケーションをビルドできます。 その結果、次のような結果が得られます(図4)。







Microsoft Research Kinect SDKを使用する



Kinect SDKをインストールすると、環境変数%MSRKINECTSDK%がシステムに表示され、SDKがインストールされたフォルダーへのパスが含まれます。 このフォルダーには、ヘッダーファイルを含むincサブディレクトリと、ライブラリを含むlibがあります。 ヘッダーファイルへのパスは、テストアプリケーションのコンパイラー設定、リンカー設定へのライブラリーへのパスに追加する必要があります。



デバイスのリストを取得する



現時点では、収集されたすべての依存関係とアプリケーションテンプレートがあります。 これで、Kinect SDKを使用してコードを直接書き始めることができます。

Kinect SDKには、1台のコンピューターに接続された複数のKinectデバイスを操作できる機能があります。 これは、アプリケーションを開発する際のより普遍的なソリューションです。 必要なデバイスの数は事前にわかりません。 したがって、この特定のAPIを使用することがより望ましいでしょう。

デバイスのリストを取得するには、 MSR_NuiGetDeviceCount()



関数を使用します。この関数は、整数変数へのポインターをパラメーターとして受け取り、成功すると、使用可能なセンサーの数が書き込まれます。



NUIAPI HRESULT MSR_NuiGetDeviceCount(

int * pCount

);






Kinectデバイスにはそれぞれ固有の識別子があり、これはINuiInstance::MSR_NuiGetPropsBlob()



メソッドを使用して取得できます。 このメソッドはパラメーターとして受け取ります。



virtual bool MSR_NuiGetPropsBlob(

MsrNui::NUI_PROPSINDEX Index,

void * pBlob,

DWORD * pdwInOutSize

);








新たに習得した知識を活用して、アプリケーションでデバイスのリストの受信を実装できます。



wxKinectHelper.h

#pragma once



#include <vector>



interface INuiInstance;



class KinectHelper

{

protected :

typedef std::pair<INuiInstance *, HANDLE> InstanceInfo;

typedef std::vector<InstanceInfo> InstanceVector;

public :

KinectHelper();

virtual ~KinectHelper();



size_t GetDeviceCount();

wxString GetDeviceName(size_t index);



bool IsDeviceOK(size_t deviceIndex);

protected :

InstanceVector m_Instances;



void Finalize();

InstanceInfo * GetInstanceByIndex(size_t index);

};






wxKinectHelper.cpp

#include <wx/wx.h>

#include "msr_nuiapi.h"

#include "KinectHelper.h"



KinectHelper::KinectHelper()

{

}



KinectHelper::~KinectHelper()

{

Finalize();

}



size_t KinectHelper::GetDeviceCount()

{

int result(0);

if (FAILED(MSR_NUIGetDeviceCount(&result))) return 0;

return (size_t)result;

}



KinectHelper::InstanceInfo * KinectHelper::GetInstanceByIndex(size_t index)

{

INuiInstance * instance = NULL;

for (InstanceVector::iterator i = m_Instances.begin();

i != m_Instances.end(); i++)

{

instance = (*i).first;

if (instance->InstanceIndex() == ( int )index) return &(*i);

}

if (!instance)

{

if (!FAILED(MSR_NuiCreateInstanceByIndex(( int )index, &instance)))

{

InstanceInfo info;

info.first = instance;

info.second = NULL;

m_Instances.push_back(info);

return &(m_Instances.at(m_Instances.size()-1));

}

}

return NULL;

}



void KinectHelper::Finalize()

{

for (InstanceVector::const_iterator i = m_Instances.begin();

i != m_Instances.end(); i++)

{

if ((*i).first && (*i).second)

{

(*i).first->NuiShutdown();

MSR_NuiDestroyInstance((*i).first);

}

}

}



wxString KinectHelper::GetDeviceName(size_t index)

{

BSTR result;

DWORD size;

InstanceInfo * info = GetInstanceByIndex(index);

if (info != NULL)

{

INuiInstance * instance = info->first;

if (instance != NULL && instance->MSR_NuiGetPropsBlob(

MsrNui::INDEX_UNIQUE_DEVICE_NAME, &result, &size))

{

wxString name = result;

SysFreeString(result);

return name;

}

}

return wxT( "Unknown Kinect Sensor" );

}



bool KinectHelper::IsDeviceOK(size_t deviceIndex)

{

return GetInstanceByIndex(deviceIndex) != NULL;

}






InstanceInfo



構造には、 INuiInstance



インスタンスへのポインタが含まれています。これにより、デバイスの名前と、イメージがキャプチャされるストリームへのハンドルを取得できます(後述)。

wxKinectHelper



クラスには、 InstanceInfo



構造のベクトルと、デバイスの数と各デバイスの名前を取得するためのメソッドが含まれています。 wxKinectHelper



クラスのデストラクタで、 Finalize()



メソッドが呼び出され、開いているすべての画像キャプチャスレッドが閉じられ、 INuiInstance



すべてのインスタンスが削除されます。

次に、デバイスのリストを取得する機能をアプリケーションに追加する必要があります。



wxKinectHelperMainFrame.h

...

class KinectTestMainFrame: public wxFrame, public wxThreadHelper

{

...

void ShowDevices();

...

KinectHelper * m_KinectHelper;

}

...






wxKinectHelperMainFrame.cpp

...

void KinectTestMainFrame::ShowDevices()

{

size_t count = m_KinectHelper->GetDeviceCount();

m_DeviceListBox->Clear();

for (size_t i = 0; i < count; ++i)

{

int item = m_DeviceListBox->Append(

m_KinectHelper->GetDeviceName(i));

m_DeviceListBox->SetClientData(item, ( void *)i);

}

}






その結果、アプリケーションを起動した後、利用可能なKinectデバイスのリストを取得します(図5):







Kinectで画像を取得する



デバイスから画像のキャプチャを開始する前に、初期化する必要があります。 これはINuiInstance::NuiInitialize()



メソッドを使用して行われます。このメソッドは、使用する予定のデバイスサブシステム(深度センサー、カメラ、またはビデオ上のプレーヤーの検索)のリストを記述するビットマスクをパラメーターとして受け取ります。

HRESULT NuiInitialize(

DWORD dwFlags,

);






Kinectで画像を受信するには、画像キャプチャストリームを初期化する必要があります。 これらの目的のために、 INuiInstance:: NuiImageStreamOpen()



メソッドが使用されます。



デバイスからの画像のキャプチャを停止するには、 INuiInstance::NuiShutdown()



メソッドを呼び出す必要があり、 INuiInstance



インスタンスでの作業が終了しINuiInstance



MSR_NuiDestroyInstance()



関数を使用して、 INuiInstance



オブジェクトへのポインターを渡すパラメーターにメモリを解放INuiInstance



必要があります。



深度バッファの取得



深度バッファの受信を開始するには、 INuiInstance:: NuiImageStreamOpen()



メソッドを呼び出して、 NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX



またはNUI_IMAGE_TYPE_DEPTH



フラグを含む値を最初のパラメーターとしてNUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX



必要があります。 後続の処理に最適なバッファーは、 NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX



フラグを使用してNUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX



。 ソースコードでは、同様の呼び出しは次のようになります。

if (FAILED(info->first->NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,

NUI_IMAGE_RESOLUTION_320x240, 0,

3,

hDepthFrameEvent,

&info->second))) { /* Handle error here */ }






上記の呼び出しの結果、変数info->second



は、画像キャプチャストリームへのハンドルがあります。 hDepthFrameEvent



イベントhDepthFrameEvent



は、 CreateEvent()



関数を使用して作成できます。



新しい画像が利用可能になると、 hDepthFrameEvent



イベントがhDepthFrameEvent



ます。 このイベントの待機は、 WaitForMultipleObjects()



またはWaitForSingleObject()



関数を使用して実装できます。



パラメーターとして渡す必要があるNuiImageStreamGetNextFrame()



メソッドを使用して、デバイスからバッファー自体を取得できます。



virtual HRESULT NuiImageStreamGetNextFrame(

_In_ HANDLE hStream,

_In_ DWORD dwMillisecondsToWait,

_Deref_out_ CONST NUI_IMAGE_FRAME **ppcImageFrame

);






結果のNUI_IMAGE_FRAME



インスタンスNUI_IMAGE_FRAME



は、現在、 NuiImageBuffer *pFrameTexture



最も関心がありNuiImageBuffer *pFrameTexture





バッファデータを直接操作するには、 LockRect()



メソッドを呼び出す必要があります。 LockRect()



メソッドにLockRect()



4つのパラメーターがあり、そのうち2つはベータ版のAPIで使用されます。

最初のパラメーターとして、0を2番目のパラメーターとしてKINECT_LOCKED_RECT



構造体へのポインターに渡す必要があります。このポインターには、関数が正常に完了した後、バッファーを操作するためのデータが書き込まれます。 NULL



を3番目のパラメーターとして、0を4番目のパラメーターとして渡しNULL





STDMETHODIMP LockRect(

UINT Level,

KINECT_LOCKED_RECT* pLockedRect,

CONST RECT* pRectUsuallyNull,

DWORD Flags

);






さらに、 KINECT_LOCKED_RECT



構造体では、深度データを直接含むpBits



フィールドに関心があります。 バッファ内の各ピクセルには、2バイトが割り当てられます。 公式フォーラムのFAQから判断すると、データ形式は次のとおりです。

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX



フラグを使用する場合、12個の最下位ビットが深度値に割り当てられ、残りの3ビットがプレーヤーインデックスに割り当てられ、最上位ビットは使用されません。

NUI_INITIALIZE_FLAG_USES_DEPTH



フラグを使用する場合、下位12ビットが深度値に割り当てられ、残りは使用されません。

グレーの濃淡で画像を取得するには、0〜255の範囲の値を取得するように深度値を正規化する必要があります。次のように実行できます。

USHORT RealDepth = (s & 0xfff8) >> 3;

BYTE l = 255 - (BYTE)(256*RealDepth/0x0fff);

RGBQUAD q;

q.rgbRed = q.rgbBlue = q.rgbGreen = l;

return q;






受信したイメージを使用して作業を完了するには、バッファに割り当てられたメモリを解放する必要があります。 これは、ストリーム記述子とNUI_IMAGE_FRAME



インスタンスへのポインターをNUI_IMAGE_FRAME



として取るNuiImageStreamReleaseFrame()



メソッドを使用してNuiImageStreamReleaseFrame()



できNUI_IMAGE_FRAME







私たちが今持っているものを要約しましょう:



では、これらすべてをどのように実践できるかを見てみましょう。

wxKinectHelper.h

class KinectHelper

{

...

const wxSize & GetFrameSize();

BYTE * CreateDataBuffer();

void FreeDataBuffer(BYTE * data);

size_t GetDataBufferLength();



bool StartGrabbing(size_t deviceIndex, HANDLE hDepthFrameEvent);

bool ReadKinectFrame(size_t deviceIndex, BYTE * data);



bool IsDeviceOK(size_t deviceIndex);

bool IsGrabbingStarted(size_t deviceIndex);



static RGBQUAD Nui_ShortToQuad_Depth( USHORT s );

protected :

InstanceVector m_Instances;

wxSize m_FrameSize;

...

};






wxKinectHelper.cpp

...

void ReadLockedRect(KINECT_LOCKED_RECT & LockedRect, int w, int h, BYTE * data)

{

if ( LockedRect.Pitch != 0 )

{

BYTE * pBuffer = (BYTE*) LockedRect.pBits;



// draw the bits to the bitmap

USHORT * pBufferRun = (USHORT*) pBuffer;

for ( int y = 0 ; y < h ; y++ )

{

for ( int x = 0 ; x < w ; x++ )

{

RGBQUAD quad = KinectHelper::Nui_ShortToQuad_Depth( *pBufferRun );

pBufferRun++;

int offset = (w * y + x) * 3;

data[offset + 0] = quad.rgbRed;

data[offset + 1] = quad.rgbGreen;

data[offset + 2] = quad.rgbBlue;

}

}

}

}

...

BYTE * KinectHelper::CreateDataBuffer()

{

size_t length = GetDataBufferLength();

BYTE * result = (BYTE*)CoTaskMemAlloc(length);

memset(result, 0, length);

return result;

}



size_t KinectHelper::GetDataBufferLength()

{

return m_FrameSize.GetWidth() * m_FrameSize.GetHeight() * 3;

}



void KinectHelper::FreeDataBuffer(BYTE * data)

{

CoTaskMemFree((LPVOID)data);

}



void KinectHelper::Finalize()

{

for (InstanceVector::const_iterator i = m_Instances.begin();

i != m_Instances.end(); i++)

{

if ((*i).first && (*i).second)

{

(*i).first->NuiShutdown();

MSR_NuiDestroyInstance((*i).first);

}

}

}



bool KinectHelper::StartGrabbing(size_t deviceIndex, HANDLE hDepthFrameEvent)

{

do

{

InstanceInfo * info = GetInstanceByIndex(deviceIndex);

if (!info || !info->first) break ;

if (FAILED(info->first->NuiInitialize(

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX))) break ;

if (FAILED(info->first->NuiImageStreamOpen(

NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,

NUI_IMAGE_RESOLUTION_320x240, 0,

3,

hDepthFrameEvent,

&info->second))) break ;

}

while ( false );

return false ;

}



bool KinectHelper::IsDeviceOK(size_t deviceIndex)

{

return GetInstanceByIndex(deviceIndex) != NULL;

}



bool KinectHelper::IsGrabbingStarted(size_t deviceIndex)

{

InstanceInfo * info = GetInstanceByIndex(deviceIndex);

return (info != NULL && info->first != NULL && info->second != NULL);

}



bool KinectHelper::ReadKinectFrame(size_t deviceIndex, BYTE * data)

{

do

{

if (deviceIndex < 0) break ;

InstanceInfo * info = GetInstanceByIndex((size_t)deviceIndex);

if (!info || !info->second) break ;

const NUI_IMAGE_FRAME * pImageFrame;

if (FAILED(NuiImageStreamGetNextFrame(

info->second, 200, &pImageFrame))) break ;

NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;

KINECT_LOCKED_RECT LockedRect;

pTexture->LockRect( 0, &LockedRect, NULL, 0 );

ReadLockedRect(LockedRect, m_FrameSize.GetWidth(),

m_FrameSize.GetHeight(), data);

NuiImageStreamReleaseFrame(info->second, pImageFrame);

return true ;

}

while ( false );

return false ;

}



RGBQUAD KinectHelper::Nui_ShortToQuad_Depth( USHORT s )

{

USHORT RealDepth = (s & 0xfff8) >> 3;

BYTE l = 255 - (BYTE)(256*RealDepth/0x0fff);

RGBQUAD q;

q.rgbRed = q.rgbBlue = q.rgbGreen = l;

return q;

}






KinectTestMainFrame.h

class KinectTestMainFrame: public wxFrame, public wxThreadHelper

{

...

void OnDEVICELISTBOXSelected( wxCommandEvent& event );

...

void ShowDevices();

void StopGrabbing();

HANDLE m_NewDepthFrameEvent;

KinectHelper * m_KinectHelper;

BYTE * m_pDepthBuffer;

wxImage * m_CurrentImage;

int m_SelectedDeviceIndex;

};






KinectTestMainFrame.cpp

...

BEGIN_EVENT_TABLE( KinectTestMainFrame, wxFrame )

EVT_LISTBOX( ID_DEVICE_LISTBOX, KinectTestMainFrame::OnDEVICELISTBOXSelected )

END_EVENT_TABLE()

...

void KinectTestMainFrame::Init()

{

m_NewDepthFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

m_KinectHelper = new KinectHelper;

m_pDepthBuffer = m_KinectHelper->CreateDataBuffer();

m_CurrentImage = new wxImage(

m_KinectHelper->GetFrameSize().GetWidth(),

m_KinectHelper->GetFrameSize().GetHeight(),

m_pDepthBuffer, true );

m_SelectedDeviceIndex = -1;

m_MainSizer = NULL;

m_DeviceListBox = NULL;

m_Canvas = NULL;

}

...

KinectTestMainFrame::~KinectTestMainFrame()

{

StopGrabbing();

wxDELETE(m_CurrentImage);

m_KinectHelper->FreeDataBuffer(m_pDepthBuffer);

wxDELETE(m_KinectHelper);

}

...

wxThread::ExitCode KinectTestMainFrame::Entry()

{

while (!GetThread()->TestDestroy())

{

int mEventIndex = WaitForMultipleObjects(

1, &m_NewDepthFrameEvent, FALSE, 100);

switch (mEventIndex)

{

case 0:

{

wxCriticalSectionLocker lock (m_CS);

m_KinectHelper->ReadKinectFrame(

m_SelectedDeviceIndex, m_pDepthBuffer);

m_Canvas->Refresh();

}

break ;

default :

break ;

}

}

return NULL;

}

...

void KinectTestMainFrame::OnDEVICELISTBOXSelected( wxCommandEvent& event )

{

do

{

StopGrabbing();

size_t deviceIndex =

(size_t)m_DeviceListBox->GetClientData( event .GetInt());

if (deviceIndex < 0 || deviceIndex >

m_KinectHelper->GetDeviceCount()) break ;

m_SelectedDeviceIndex = deviceIndex;

if (!m_KinectHelper->IsDeviceOK(deviceIndex)) break ;

if (!m_KinectHelper->IsGrabbingStarted(deviceIndex))

{

m_KinectHelper->StartGrabbing(

deviceIndex, m_NewDepthFrameEvent);

if (CreateThread() != wxTHREAD_NO_ERROR) break ;

m_Canvas->SetCurrentImage(m_CurrentImage);

GetThread()->Run();

}

}

while ( false );

}



void KinectTestMainFrame::StopGrabbing()

{

if (GetThread())

{

if (GetThread()->IsAlive())

{

GetThread()->Delete();

}

if (m_kind == wxTHREAD_JOINABLE)

{

if (GetThread()->IsAlive())

{

GetThread()->Wait();

}

wxDELETE(m_thread);

}

else

{

m_thread = NULL;

}

}

}






アプリケーションwxKinectHelper



と、 wxKinectHelper



オブジェクトは、解像度(320x240x24)に応じて、深度バッファーにメモリを割り当てます。 次に、割り当てられたメモリ領域がRGBバッファとしてm_CurrentImage



オブジェクトに転送されます。

使用可能なデバイスのリストでデバイスがm_CurrentImage



れると、デバイスからの画像キャプチャストリームが開始され、 m_CurrentImage



オブジェクトm_CurrentImage



キャンバスに関連付けられます。

Entry()



メソッドは、デバイスからの新しいイメージを待ちます。 画像が利用可能になると、RGBバッファが新しい値で満たされ、キャンバスが再描画されます。

その結果、アプリケーションを起動してリスト内のデバイス名をクリックすると、次のようなものが表示されます(図6)。







カメラからカラー画像を取得する



デバイスのカメラから画像を取得するには、 NuiInitialize()



メソッドを呼び出すときにNUI_INITIALIZE_FLAG_USES_COLOR



フラグを指定し、 NuiInitialize()



メソッドを呼び出すときに少なくとも640x480の解像度を指定する必要があります。

その結果、コードは次のようになります。

if (FAILED(info->first->NuiInitialize(

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX|

NUI_INITIALIZE_FLAG_USES_COLOR))) break ;

if (FAILED(info->first->NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR,

NUI_IMAGE_RESOLUTION_640x480, 0,

3,

hDepthFrameEvent,

&info->second))) break ;






したがって、 KINECT_LOCKED_RECT



構造のデータはRGBA形式に含まれています(SDKで利用可能なRGBQUAD



構造は、データにアクセスするのに非常に適しています)。 したがって、RGBバッファーを取得するコードは次のようになります。

if ( LockedRect.Pitch != 0 )

{

BYTE * pBuffer = (BYTE*) LockedRect.pBits;

for ( int y = 0 ; y < h ; y++ )

{

for ( int x = 0 ; x < w ; x++ )

{

RGBQUAD * quad = ((RGBQUAD*)pBuffer) + x;

int offset = (w * y + x) * 3;

data[offset + 0] = quad->rgbRed;

data[offset + 1] = quad->rgbGreen;

data[offset + 2] = quad->rgbBlue;

}

pBuffer += LockedRect.Pitch;

}

}






プレイヤーの位置の追跡(スケルトントラッキング)



プレイヤーの「スケルトン」のセグメントを取得して表示するアルゴリズムは、通常の画像とは異なります。



スケルトンセグメントを取得する可能性を有効にするには、 NuiInitialize()



メソッドが値NUI_INITIALIZE_FLAG_USES_SKELETON



を含むフラグを渡し、次にNuiSkeletonTrackingEnable()



メソッドを呼び出す必要があります。 2番目のパラメーターとして、一連のフラグ(SDKのベータ版ではこのパラメーターが無視されるため、0を渡すことができます)。



スケルトンセグメントの受信フローを完了するには、 NuiSkeletonTrackingDisable()



メソッドを呼び出す必要があります。

コードでは、次のようになります。

if (FAILED(info->first->NuiSkeletonTrackingEnable(hSkeletonFrameEvent, 0)))

{ /* error */ };






パラメーターとしてNuiSkeletonGetNextFrame()



メソッドを使用して、プレーヤーの位置に関する情報を含むデータバッファーを取得できます。



NuiSkeletonGetNextFrame()



メソッドを呼び出した後、NUI_SKELETON_FRAME構造体のインスタンスを取得します。 もっと詳しく見てみましょう。

struct _NUI_SKELETON_FRAME {

LARGE_INTEGER liTimeStamp;

DWORD dwFrameNumber;

DWORD dwFlags;

Vector4 vFloorClipPlane;

Vector4 vNormalToGravity;

NUI_SKELETON_DATA SkeletonData[NUI_SKELETON_COUNT];

} NUI_SKELETON_FRAME;








NUI_SKELETON_FRAME



構造体の説明からNUI_SKELETON_FRAME



ように、限られた数のプレーヤーがサポートされています(現在のSDKバージョンでは、 NUI_SKELETON_COUNT



の値は6です)。



ここで、 NUI_SKELETON_DATA



構造を検討しNUI_SKELETON_DATA





struct _NUI_SKELETON_DATA {

NUI_SKELETON_TRACKING_STATE eTrackingState;

DWORD dwTrackingID;

DWORD dwEnrollmentIndex;

DWORD dwUserIndex;

Vector4 Position;

Vector4 SkeletonPositions[NUI_SKELETON_POSITION_COUNT];

NUI_SKELETON_POSITION_TRACKING_STATE

eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_COUNT];

DWORD dwQualityFlags;

} NUI_SKELETON_DATA;








NUI_SKELETON_DATA



構造体の説明からNUI_SKELETON_DATA



ように、サポートされるジョイントジョイントの数は、 NUI_SKELETON_POSITION_COUNT



等しい数によって制限されます。

次に、上記のAPIを使用してプレーヤーの座標を取得する実装を検討します。



KinectHelper.h

...

struct KinectStreams

{

HANDLE hDepth;

HANDLE hColor;

HANDLE hSkeleton;

KinectStreams() : hDepth(NULL), hColor(NULL), hSkeleton(NULL) {}

};

...






KinectHelper.cpp

void KinectHelper::Finalize()

{

for (InstanceVector::const_iterator i = m_Instances.begin();

i != m_Instances.end(); i++)

{

if ((*i).first)

{

...

if ((*i).second.hSkeleton != NULL)

{

(*i).first->NuiSkeletonTrackingDisable();

}

MSR_NuiDestroyInstance((*i).first);

}

}

}



bool KinectHelper::StartGrabbing(size_t deviceIndex,

HANDLE hDepthFrameEvent,

HANDLE hColorFrameEvent,

HANDLE hSkeletonFrameEvent)

{

do

{

if (hDepthFrameEvent == NULL &&

hColorFrameEvent == NULL &&

hSkeletonFrameEvent == NULL) break ;

InstanceInfo * info = GetInstanceByIndex(deviceIndex);

if (!info || !info->first) break ;

if (FAILED(info->first->NuiInitialize(

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX |

NUI_INITIALIZE_FLAG_USES_COLOR |

NUI_INITIALIZE_FLAG_USES_SKELETON))) break ;

...

if (hSkeletonFrameEvent != NULL)

{

if (FAILED(info->first->NuiSkeletonTrackingEnable(

hSkeletonFrameEvent, 0))) break ;

info->second.hSkeleton = hSkeletonFrameEvent;

}

}

while ( false );

return false ;

}



void * KinectHelper::ReadSkeletonFrame(size_t deviceIndex)

{

do

{

if (deviceIndex < 0) break ;

InstanceInfo * info = GetInstanceByIndex((size_t)deviceIndex);

if (!info || !info->second.hColor) break ;

NUI_SKELETON_FRAME * frame = new NUI_SKELETON_FRAME;

if (FAILED(info->first->NuiSkeletonGetNextFrame(200, frame))) break ;

return frame;

}

while ( false );

return NULL;

}






プレイヤースケルトンレンダリング


現時点では、次の方法に関する情報があります。



ここで、アプリケーションで受信したデータを何らかの方法で表示する必要があります。

NUI_SKELETON_FRAME



データを使用して何かを行う前に、それらを前処理に送信する必要があります。 前処理はNuiTransformSmooth()



メソッドによって実行されます- NuiTransformSmooth()



や突然の動きを避けるために、セグメントの座標をフィルタリングします。 パラメーターとして、 NuiTransformSmooth()



メソッドは、 NUI_SKELETON_FRAME



構造体へのポインター、およびオプションで、前処理パラメーターを含むNUI_TRANSFORM_SMOOTH_PARAMETERS



オブジェクトへのポインターをNUI_TRANSFORM_SMOOTH_PARAMETERS



ます。

HRESULT NuiTransformSmooth(

NUI_SKELETON_FRAME *pSkeletonFrame,

CONST NUI_TRANSFORM_SMOOTH_PARAMETERS *pSmoothingParams

);






スケルトンセグメントを表示するには、それらの座標を画像座標に変換する必要があります。 これは、パラメーターとしてNuiTransformSkeletonToDepthImageF()



メソッドを使用してNuiTransformSkeletonToDepthImageF()



できます。



VOID NuiTransformSkeletonToDepthImageF(

Vector4 vPoint,

_Out_ FLOAT *pfDepthX,

_Out_ FLOAT *pfDepthY

);






すべてのアーティキュレーションポイントの座標を受け取ったら、通常のグラフィックプリミティブを使用してそれらをキャンバスに表示できます。



スケルトンセグメントを表示するための実際のコードは次のとおりです。

SkeletonPainter.h

#pragma once



#include <wx/wx.h>



class SkeletonPainterImpl;



class SkeletonPainter

{

public :

SkeletonPainter();

~SkeletonPainter();

void DrawSkeleton(wxDC & dc, void * data);

private :

SkeletonPainterImpl * m_Impl;

};






SkeletonPainter.cpp

#include "SkeletonPainter.h"

#if defined(__WXMSW__)

#include "SkeletonPainterImplMSW.h"

#endif



SkeletonPainter::SkeletonPainter()

{

#if defined(__WXMSW__)

m_Impl = new SkeletonPainterImplMSW;

#else

m_Impl = NULL;

#endif

}



SkeletonPainter::~SkeletonPainter()

{

wxDELETE(m_Impl);

}



void SkeletonPainter::DrawSkeleton(wxDC & dc, void * data)

{

if (m_Impl)

{

m_Impl->DrawSkeleton(dc, data);

}

}






SkeletonPainterImpl.h

#pragma once



#include <wx/wx.h>



class SkeletonPainterImpl

{

public :

virtual ~SkeletonPainterImpl() {}

virtual void DrawSkeleton(wxDC & dc, void * data) = 0;

};






SkeletonPainterImplMSW.h

#pragma once



#include "SkeletonPainterImpl.h"

#include "msr_nuiapi.h"



class SkeletonPainterImplMSW : public SkeletonPainterImpl

{

public :

~SkeletonPainterImplMSW();

void DrawSkeleton(wxDC & dc, void * data);

private :

void Nui_DrawSkeleton(wxDC & dc, NUI_SKELETON_DATA * data, size_t index);

void Nui_DrawSkeletonSegment(wxDC & dc, wxPoint * points, int numJoints, ... );



static wxPen m_SkeletonPen[6];

};






SkeletonPainterImplMSW.cpp

#include "SkeletonPainterImplMSW.h"



wxPen SkeletonPainterImplMSW::m_SkeletonPen[6] =

{

wxPen(wxColor(255, 0, 0), wxSOLID),

...

};



SkeletonPainterImplMSW::~SkeletonPainterImplMSW()

{

}



void SkeletonPainterImplMSW::DrawSkeleton(wxDC & dc, void * data)

{

do

{

NUI_SKELETON_FRAME * frame =

reinterpret_cast<NUI_SKELETON_FRAME*>(data);

if (!frame) break ;

int skeletonCount(0);

for ( int i = 0 ; i < NUI_SKELETON_COUNT ; i++ )

{

if ( frame->SkeletonData[i].eTrackingState ==

NUI_SKELETON_TRACKED )

{

skeletonCount++;

}

}

if (!skeletonCount) break ;

NuiTransformSmooth(frame, NULL);

for (size_t i = 0 ; i < NUI_SKELETON_COUNT ; i++ )

{

if (frame->SkeletonData[i].eTrackingState ==

NUI_SKELETON_TRACKED)

{

Nui_DrawSkeleton(dc, &frame->SkeletonData[i], i );

}

}

}

while ( false );

}



void SkeletonPainterImplMSW::Nui_DrawSkeleton(wxDC & dc,

NUI_SKELETON_DATA * data, size_t index)

{

wxPoint points[NUI_SKELETON_POSITION_COUNT];

float fx(0), fy(0);

wxSize imageSize = dc.GetSize();

for (size_t i = 0; i < NUI_SKELETON_POSITION_COUNT; i++)

{

NuiTransformSkeletonToDepthImageF(

data->SkeletonPositions[i], &fx, &fy);

points[i].x = ( int ) ( fx * imageSize.GetWidth() + 0.5f );

points[i].y = ( int ) ( fy * imageSize.GetHeight() + 0.5f );

}



Nui_DrawSkeletonSegment(dc,points,4,

NUI_SKELETON_POSITION_HIP_CENTER,

NUI_SKELETON_POSITION_SPINE,

NUI_SKELETON_POSITION_SHOULDER_CENTER,

NUI_SKELETON_POSITION_HEAD);

Nui_DrawSkeletonSegment(dc,points,5,

NUI_SKELETON_POSITION_SHOULDER_CENTER,

NUI_SKELETON_POSITION_SHOULDER_LEFT,

NUI_SKELETON_POSITION_ELBOW_LEFT,

NUI_SKELETON_POSITION_WRIST_LEFT,

NUI_SKELETON_POSITION_HAND_LEFT);

Nui_DrawSkeletonSegment(dc,points,5,

NUI_SKELETON_POSITION_SHOULDER_CENTER,

NUI_SKELETON_POSITION_SHOULDER_RIGHT,

NUI_SKELETON_POSITION_ELBOW_RIGHT,

NUI_SKELETON_POSITION_WRIST_RIGHT,

NUI_SKELETON_POSITION_HAND_RIGHT);

Nui_DrawSkeletonSegment(dc,points,5,

NUI_SKELETON_POSITION_HIP_CENTER,

NUI_SKELETON_POSITION_HIP_LEFT,

NUI_SKELETON_POSITION_KNEE_LEFT,

NUI_SKELETON_POSITION_ANKLE_LEFT,

NUI_SKELETON_POSITION_FOOT_LEFT);

Nui_DrawSkeletonSegment(dc,points,5,

NUI_SKELETON_POSITION_HIP_CENTER,

NUI_SKELETON_POSITION_HIP_RIGHT,

NUI_SKELETON_POSITION_KNEE_RIGHT,

NUI_SKELETON_POSITION_ANKLE_RIGHT,

NUI_SKELETON_POSITION_FOOT_RIGHT);

}



void SkeletonPainterImplMSW::Nui_DrawSkeletonSegment(wxDC & dc,

wxPoint * points, int numJoints, ...)

{

va_list vl;

va_start(vl,numJoints);

wxPoint segmentPositions[NUI_SKELETON_POSITION_COUNT];



for ( int iJoint = 0; iJoint < numJoints; iJoint++)

{

NUI_SKELETON_POSITION_INDEX jointIndex =

va_arg(vl,NUI_SKELETON_POSITION_INDEX);

segmentPositions[iJoint].x = points[jointIndex].x;

segmentPositions[iJoint].y = points[jointIndex].y;

}



dc.SetPen(*wxBLUE_PEN);

dc.DrawLines(numJoints, segmentPositions);



va_end(vl);

}






アプリケーションでSkeletonPainter



クラスを使用すると、次のようになります。



KinectTestMainFrame.h

...

class KinectTestMainFrame: public wxFrame, public wxThreadHelper

{

...

HANDLE m_NewSkeletonFrameEvent;

wxImage m_SkeletonImage;

...

};

...






KinectTestMainFrame.cpp

...

wxThread::ExitCode KinectTestMainFrame::Entry()

{

HANDLE eventHandles[3];

eventHandles[0] = m_NewDepthFrameEvent;

eventHandles[1] = m_NewColorFrameEvent;

eventHandles[2] = m_NewSkeletonFrameEvent;

SkeletonPainter painter;

while (!GetThread()->TestDestroy())

{

int mEventIndex = WaitForMultipleObjects(

_countof(eventHandles), eventHandles, FALSE, 100);

switch (mEventIndex)

{

...

case 2:

{

void * frame = m_KinectHelper->ReadSkeletonFrame(

m_SelectedDeviceIndex);

if (frame)

{

wxBitmap bmp(

m_SkeletonImage.GetWidth(),

m_SkeletonImage.GetHeight());

wxMemoryDC dc(bmp);

painter.DrawSkeleton(dc, frame);

m_KinectHelper->ReleaseSkeletonFrame(frame);

dc.SelectObject(wxNullBitmap);

m_SkeletonImage = bmp.ConvertToImage();

m_SkeletonCanvas->Refresh();

}

}

break ;

default :

break ;

}

}

return NULL;

}






上記のアクションの結果、次のような結果が得られます(図7)。







アプリケーション内のプラットフォーム固有のコードを取り除く





上記の例は、プロジェクトがクロスプラットフォームライブラリを使用してユーザーインターフェイスを開発し、GUIコードの一部がWindows専用のAPIを使用して記述されていることを除いて、すべての人に適しています。



libfreenectOpenNIなど、Kinectを操作するためのサードパーティライブラリがいくつかありますが、すでにこの段階で、アプリケーションコードがMicrosoftのSDKの使用に結び付けられている状況がありました。



この迷惑な誤解を解決するには、別のグラバークラスのデバイスから画像を受信することに関連するコードを削除し、 KinectHelper



クラスの機能をデバイスのリストの受信とグラバーインスタンスの作成に制限します。



KinectGrabberBase.h

#pragma once



#include <wx/wx.h>



class KinectGrabberBase

{

public :

KinectGrabberBase(wxEvtHandler * handler);

virtual ~KinectGrabberBase();



virtual bool GrabDepthFrame(unsigned char * data) = 0;

virtual bool GrabColorFrame(unsigned char * data) = 0;

virtual void * GrabSkeletonFrame() = 0;



virtual bool Start() = 0;

virtual bool Stop() = 0;

virtual bool IsStarted() = 0;



const wxSize & GetDepthFrameSize();

const wxSize & GetColorFrameSize();

protected :

wxSize m_DepthFrameSize;

wxSize m_ColorFrameSize;

wxEvtHandler * m_Handler;

};



BEGIN_DECLARE_EVENT_TYPES()



DECLARE_LOCAL_EVENT_TYPE(KINECT_DEPTH_FRAME_RECEIVED, -1)

DECLARE_LOCAL_EVENT_TYPE(KINECT_COLOR_FRAME_RECEIVED, -1)

DECLARE_LOCAL_EVENT_TYPE(KINECT_SKELETON_FRAME_RECEIVED, -1)



END_DECLARE_EVENT_TYPES()






KinectGrabberBase.cpp

#include "KinectGrabberBase.h"



DEFINE_EVENT_TYPE(KINECT_DEPTH_FRAME_RECEIVED)

DEFINE_EVENT_TYPE(KINECT_COLOR_FRAME_RECEIVED)

DEFINE_EVENT_TYPE(KINECT_SKELETON_FRAME_RECEIVED)

...






KinectGrabberMSW.h

#pragma once



#include "KinectGrabberBase.h"

#include "MSR_NuiApi.h"



class KinectGrabberMSW : public KinectGrabberBase, public wxThreadHelper

{

...

private :

virtual wxThread::ExitCode Entry();

BYTE * CreateDepthDataBuffer();

BYTE * CreateColorDataBuffer();

size_t GetDepthDataBufferLength();

size_t GetColorDataBufferLength();

void FreeDataBuffer(BYTE * data);

bool ReadDepthFrame();

bool ReadColorFrame();

bool ReadSkeletonFrame();



void ReadDepthLockedRect(KINECT_LOCKED_RECT & LockedRect,

int w, int h, BYTE * data);

void ReadColorLockedRect(KINECT_LOCKED_RECT & LockedRect,

int w, int h, BYTE * data);

static RGBQUAD Nui_ShortToQuad_Depth( USHORT s );

void ResetEvents();

void StopThread();

bool CopyLocalBuffer(BYTE * src, BYTE * dst, size_t count);

HANDLE m_NewDepthFrameEvent;

HANDLE m_NewColorFrameEvent;

HANDLE m_NewSkeletonFrameEvent;

HANDLE m_DepthStreamHandle;

HANDLE m_ColorStreamHandle;

BYTE * m_DepthBuffer;

BYTE * m_ColorBuffer;

INuiInstance * m_Instance;

size_t m_DeviceIndex;

NUI_SKELETON_FRAME m_SkeletonFrame;

};






KinectGrabberMSW.cpp

#include "KinectGrabberMSW.h"



KinectGrabberMSW::KinectGrabberMSW(wxEvtHandler * handler, size_t deviceIndex)

: KinectGrabberBase(handler), m_DeviceIndex(deviceIndex), m_Instance(NULL)

{

m_DepthBuffer = CreateDepthDataBuffer();

m_ColorBuffer = CreateColorDataBuffer();

ResetEvents();

do

{

if (FAILED(MSR_NuiCreateInstanceByIndex(( int )m_DeviceIndex, &m_Instance))) break ;

if (FAILED(m_Instance->NuiInitialize(

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX |

NUI_INITIALIZE_FLAG_USES_COLOR |

NUI_INITIALIZE_FLAG_USES_SKELETON))) break ;

}

while ( false );

}

...

void * KinectGrabberMSW::GrabSkeletonFrame()

{

do

{

if (!GetThread() || !GetThread()->IsAlive() ||

!m_Instance || !m_NewSkeletonFrameEvent) break ;

return &m_SkeletonFrame;

}

while ( false );

return NULL;

}



bool KinectGrabberMSW::Start()

{

do

{

if (!m_Instance) break ;

if (GetThread() && GetThread()->IsAlive()) break ;



if (CreateThread() != wxTHREAD_NO_ERROR) break ;



m_NewDepthFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

m_NewColorFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

m_NewSkeletonFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );



if (FAILED(m_Instance->NuiImageStreamOpen(

NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,

NUI_IMAGE_RESOLUTION_320x240, 0,

3,

m_NewDepthFrameEvent,

&m_DepthStreamHandle))) break ;

if (FAILED(m_Instance->NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR,

NUI_IMAGE_RESOLUTION_640x480, 0,

4,

m_NewColorFrameEvent,

&m_ColorStreamHandle))) break ;

if (FAILED(m_Instance->NuiSkeletonTrackingEnable(

m_NewSkeletonFrameEvent, 0))) break ;



GetThread()->Run();



return true ;

}

while ( false );

return false ;

}

...

wxThread::ExitCode KinectGrabberMSW::Entry()

{

HANDLE eventHandles[3];

eventHandles[0] = m_NewDepthFrameEvent;

eventHandles[1] = m_NewColorFrameEvent;

eventHandles[2] = m_NewSkeletonFrameEvent;

while (!GetThread()->TestDestroy())

{

int mEventIndex = WaitForMultipleObjects(

_countof(eventHandles), eventHandles, FALSE, 100);

switch (mEventIndex)

{

case 0: ReadDepthFrame(); break ;

case 1: ReadColorFrame(); break ;

case 2: ReadSkeletonFrame(); break ;

default :

break ;

}

}

return NULL;

}

...

void KinectGrabberMSW::StopThread()

{

if (GetThread())

{

if (GetThread()->IsAlive())

{

GetThread()->Delete();

}

if (m_kind == wxTHREAD_JOINABLE)

{

if (GetThread()->IsAlive())

{

GetThread()->Wait();

}

wxDELETE(m_thread);

}

else

{

m_thread = NULL;

}

}

wxYield();

}



bool KinectGrabberMSW::ReadDepthFrame()

{

do

{

if (m_DeviceIndex < 0 || !m_Instance) break ;

const NUI_IMAGE_FRAME * pImageFrame;

if (FAILED(NuiImageStreamGetNextFrame(

m_DepthStreamHandle, 200, &pImageFrame))) break ;

NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;

KINECT_LOCKED_RECT LockedRect;

pTexture->LockRect( 0, &LockedRect, NULL, 0 );

ReadDepthLockedRect(LockedRect,

m_DepthFrameSize.GetWidth(),

m_DepthFrameSize.GetHeight(),

m_DepthBuffer);

NuiImageStreamReleaseFrame(m_DepthStreamHandle, pImageFrame);

if (m_Handler)

{

wxCommandEvent e(KINECT_DEPTH_FRAME_RECEIVED, wxID_ANY);

e.SetInt(m_DeviceIndex);

m_Handler->AddPendingEvent(e);

}

return true ;

}

while ( false );

return false ;

}



bool KinectGrabberMSW::ReadColorFrame()

{

do

{

if (m_DeviceIndex < 0 || !m_Instance) break ;

const NUI_IMAGE_FRAME * pImageFrame;

if (FAILED(NuiImageStreamGetNextFrame(

m_ColorStreamHandle, 200, &pImageFrame))) break ;

NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;

KINECT_LOCKED_RECT LockedRect;

pTexture->LockRect( 0, &LockedRect, NULL, 0 );

ReadColorLockedRect(LockedRect,

m_ColorFrameSize.GetWidth(),

m_ColorFrameSize.GetHeight(),

m_ColorBuffer);

NuiImageStreamReleaseFrame(m_ColorStreamHandle, pImageFrame);

if (m_Handler)

{

wxCommandEvent e(KINECT_COLOR_FRAME_RECEIVED, wxID_ANY);

e.SetInt(m_DeviceIndex);

m_Handler->AddPendingEvent(e);

}

return true ;

}

while ( false );

return false ;

}



bool KinectGrabberMSW::ReadSkeletonFrame()

{

do

{

if (m_DeviceIndex < 0 || !m_Instance) break ;

if (FAILED(m_Instance->NuiSkeletonGetNextFrame(200, &m_SkeletonFrame))) break ;

if (m_Handler)

{

wxCommandEvent e(KINECT_SKELETON_FRAME_RECEIVED, wxID_ANY);

e.SetInt(m_DeviceIndex);

m_Handler->AddPendingEvent(e);

}

return true ;

}

while ( false );

return false ;

}



void KinectGrabberMSW::ReadDepthLockedRect(KINECT_LOCKED_RECT & LockedRect, int w, int h, BYTE * data)

{

if ( LockedRect.Pitch != 0 )

{

BYTE * pBuffer = (BYTE*) LockedRect.pBits;

USHORT * pBufferRun = (USHORT*) pBuffer;

for ( int y = 0 ; y < h ; y++ )

{

for ( int x = 0 ; x < w ; x++ )

{

RGBQUAD quad = KinectGrabberMSW::Nui_ShortToQuad_Depth( *pBufferRun );

pBufferRun++;

int offset = (w * y + x) * 3;

data[offset + 0] = quad.rgbRed;

data[offset + 1] = quad.rgbGreen;

data[offset + 2] = quad.rgbBlue;

}

}

}

}



void KinectGrabberMSW::ReadColorLockedRect(KINECT_LOCKED_RECT & LockedRect, int w, int h, BYTE * data)

{

if ( LockedRect.Pitch != 0 )

{

BYTE * pBuffer = (BYTE*) LockedRect.pBits;

for ( int y = 0 ; y < h ; y++ )

{

for ( int x = 0 ; x < w ; x++ )

{

RGBQUAD * quad = ((RGBQUAD*)pBuffer) + x;

int offset = (w * y + x) * 3;

data[offset + 0] = quad->rgbRed;

data[offset + 1] = quad->rgbGreen;

data[offset + 2] = quad->rgbBlue;

}

pBuffer += LockedRect.Pitch;

}

}

}

...






KinectHelper.h

#pragma once



class KinectGrabberBase;



class KinectHelper

{

public :

KinectHelper();

~KinectHelper();



size_t GetDeviceCount();

wxString GetDeviceName(size_t index);

KinectGrabberBase * CreateGrabber(wxEvtHandler * handler, size_t index);

};






KinectHelper.cpp

...

wxString KinectHelper::GetDeviceName(size_t index)

{

BSTR result;

DWORD size;

INuiInstance * instance(NULL);

wxString name = wxT( "Unknown Kinect Sensor" );

if (!FAILED(MSR_NuiCreateInstanceByIndex(index, &instance)))

{

if (instance != NULL)

{

if (instance->MSR_NuiGetPropsBlob(

MsrNui::INDEX_UNIQUE_DEVICE_NAME,

&result, &size))

{

name = result;

SysFreeString(result);

}

MSR_NuiDestroyInstance(instance);

}

}

return name;

}



KinectGrabberBase * KinectHelper::CreateGrabber(wxEvtHandler * handler, size_t index)

{

#if defined(__WXMSW__)

return new KinectGrabberMSW(handler, index);

#else

return NULL;

#endif

}

...






RGBバッファのメモリの割り当てと、別のストリームで画像をキャプチャするためのコードは、フォームクラスから削除できます。 これで、フォームクラスは次のようになります。



KinectTestMainFrame.h

class KinectTestMainFrame: public wxFrame

{

...

void OnDepthFrame(wxCommandEvent & event );

void OnColorFrame(wxCommandEvent & event );

void OnSkeletonFrame(wxCommandEvent & event );

...

wxImage m_CurrentImage;

int m_SelectedDeviceIndex;

wxImage m_ColorImage;

wxImage m_SkeletonImage;

KinectGrabberBase * m_Grabber;

...

};






KinectTestMainFrame.cpp

...

BEGIN_EVENT_TABLE( KinectTestMainFrame, wxFrame )

...

EVT_COMMAND (wxID_ANY, KINECT_DEPTH_FRAME_RECEIVED, \

KinectTestMainFrame::OnDepthFrame)

EVT_COMMAND (wxID_ANY, KINECT_COLOR_FRAME_RECEIVED, \

KinectTestMainFrame::OnColorFrame)

EVT_COMMAND (wxID_ANY, KINECT_SKELETON_FRAME_RECEIVED, \

KinectTestMainFrame::OnSkeletonFrame)

END_EVENT_TABLE()

...

void KinectTestMainFrame::OnDEVICELISTBOXSelected( wxCommandEvent& event )

{

do

{

size_t deviceIndex =

(size_t)m_DeviceListBox->GetClientData( event .GetInt());

if (deviceIndex < 0 ||

deviceIndex > m_KinectHelper->GetDeviceCount()) break ;

m_SelectedDeviceIndex = deviceIndex;

StartGrabbing();

}

while ( false );

}



void KinectTestMainFrame::StartGrabbing()

{

StopGrabbing();

m_Grabber = m_KinectHelper->CreateGrabber( this , m_SelectedDeviceIndex);

m_CurrentImage = wxImage(

m_Grabber->GetDepthFrameSize().GetWidth(),

m_Grabber->GetDepthFrameSize().GetHeight());

m_ColorImage = wxImage(

m_Grabber->GetColorFrameSize().GetWidth(),

m_Grabber->GetColorFrameSize().GetHeight());

m_SkeletonImage = wxImage(

m_Grabber->GetDepthFrameSize().GetWidth(),

m_Grabber->GetDepthFrameSize().GetHeight());

m_DepthCanvas->SetCurrentImage(&m_CurrentImage);

m_ColorCanvas->SetCurrentImage(&m_ColorImage);

m_SkeletonCanvas->SetCurrentImage(&m_SkeletonImage);

if (!m_Grabber->Start())

{

StopGrabbing();

}

}

...

void KinectTestMainFrame::OnDepthFrame(wxCommandEvent & event )

{

do

{

if (!m_Grabber) break ;

m_Grabber->GrabDepthFrame(m_CurrentImage.GetData());

m_DepthCanvas->Refresh();

}

while ( false );

}



void KinectTestMainFrame::OnColorFrame(wxCommandEvent & event )

{

do

{

if (!m_Grabber) break ;

m_Grabber->GrabColorFrame(m_ColorImage.GetData());

m_ColorCanvas->Refresh();

}

while ( false );

}



void KinectTestMainFrame::OnSkeletonFrame(wxCommandEvent & event )

{

do

{

if (!m_Grabber) break ;

SkeletonPainter painter;

wxBitmap bmp(m_SkeletonImage.GetWidth(), m_SkeletonImage.GetHeight());

wxMemoryDC mdc(bmp);

painter.DrawSkeleton(mdc, m_Grabber->GrabSkeletonFrame());

mdc.SelectObject(wxNullBitmap);

m_SkeletonImage = bmp.ConvertToImage();

m_SkeletonCanvas->Refresh();

}

while ( false );

}






コードからわかるように、グラバークラスは新しいフレームを受信すると、 wxEvtHandler



オブジェクトに通知を送信します(wxWidgetsのwxFrame



クラスはwxFrame



から派生しwxEvtHandler



)。 フォームには、グラバーからの通知を受信したときに呼び出されるイベントハンドラーがあります。



KinectGrabberBase::GrabSkeletonFrame()



メソッドがvoid*



返す理由も非常に単純です-さまざまなSDK(非公式のものを含む)を使用して画像キャプチャを実装する場合、これらすべてのSDKがプレーヤーの位置に関する情報を受け取るという事実ではありません同一のデータ構造の形式。 いずれにしても、座標は後処理のために送信する必要があります。 この場合、グラバーからポインターを受け取るコードは、それがどのタイプのデータに変換される必要があるかを自分で知っています。 グラフィカルインターフェイスは、グラバーの内部構造について知る必要はありません。



結論として



結論として、Microsoft SDKはベータ状態ですが、Kinect管理機能は完全には実装されていませんが、非常に使用可能です(たとえば、libfreenectでは、デバイス上のLEDと公式SDKをドキュメントによって判断できますが、できません)。 ライブラリは驚くほど安定して動作します。 開発者がメモリリークを避けるために注意を払ったことは注目に値します。 たとえば、終了時にストリームを閉じるのを忘れた場合、Visual Studioデバッガーはメモリリークを報告しません。ほとんどの場合、すべてが正しく終了し、ライブラリがアンロードされるとメモリから削除されます。

テストアプリケーションとライブラリのソースコードは、Google Code- wxKinectHelperにあります。

プロジェクトの開発とグラバーの新しい実装の追加を楽しみにしています。 現在、libfreenectを飼いならそうとしています。 箱から出して、予備のシャーマニズムなしで、画像を取得することを除いてすべてを開始することが可能でした-LEDインジケータが点滅し、エンジンが完全にブザーします。 OpenNIでも同じことをしようとしています。



便利なリンク



Microsoft Research Kinect SDK



Kinect SDK





SDK





, Kinect SDK







C++ Kinect SDK ( ) , — .



All Articles