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を䜿甚しお蚘述されおいるこずを陀いお、すべおの人に適しおいたす。

libfreenectやOpenNIなど、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 ( ) , — .

Source: https://habr.com/ru/post/J123588/


All Articles