Windows上でマルチスレッドで動作するプログラムを記述するにはまず process.h を読み込みます。
#include <process.h> |
スレッドで走る関数 unsigned func (LPVOID) は次の形式で定義します。
unsigned __stdcall func (LPVOID pParam) { // このスレッドがやるべき仕事 _endthreadex(0); return 0; } |
マルチスレッドで動作するプログラムにおいては、 クリティカル・セクション(競合が発生する可能性がある場所)では 排他制御をする必要があります。これには Mutex を利用します。 すなわち、どのスレッドからも参照できる変数として Mutex を生成しておき、 この利用権を取れたスレッドだけがクリティカル・セクションに入って よいことにします。
Handle mutex; int main(int argc, char** argv) { mutex = CreateMutex(NULL, FALSE, NULL); // Mutexを生成する if (GetLastError() == ERROR_ALREADY_EXISTS) throw エラー; ...(略)... } unsigned __stdcall func (LPVOID pParam) { ...(略)... DWORD ret = WaitForSingleObject(mutex, INFINITE); // Mutexの利用権を取得する if (ret == WAIT_FAILED) throw エラー; ...(略)... //クリティカル・セクション ReleaseMutex(mutex); // Mutexの利用権を解放する ...(略)... _endthreadex(0); return 0; } |
メインのスレッドにおいて、N 個のスレッドを起動する場合は次のように記述します。 スレッドの起動を _beginthreadex()関数で、 全てのスレッドの終了待ちを WaitForMultipleObjects()関数で行います。
HANDLE hThread[N ] = { 0 }; hThread[0] = (HANDLE) _beginthreadex(NULL,0,関数名1,NULL,0,NULL); // 別スレッドで関数1を実行する if (hThread[0] == 0) throw エラー; ...(略)... // 2〜(N-2)のスレッドを生成する hThread[N -1] = (HANDLE) _beginthreadex(NULL,0,関数名N,NULL,0,NULL); // 別スレッドで関数Nを実行する if (hThread[N -1] == 0) throw エラー; ...(略)... // メインスレッドでやるべき仕事 DWORD ret = WaitForMultipleObjects(N ,hThread,TRUE,INFINITE); // 全てのスレッドの終了を待つ if (ret == WAIT_FAILED) throw エラー; return 0; } |
USE_THREAD 定数を define してから NtKinect.h を include する と NtKinect のマルチスレッド関係のメソッドが有効になります。
返り値の型 | メソッド名 | 説明 |
---|---|---|
void | acquire() | version1.6以降。 Mutex の権利を獲得する。獲得できるまで呼び出したスレッドの実行はブロックされる。 |
void | release() | version1.6以降。 Mutex の権利を解放する。 |
複数の関数呼び出しの間でatomic性を確保したい場合は、 自分でacquire()とrelease()を呼び出して排他制御した方がよいとは思いますが、 利用を簡便にするために、以下の関数を用意しました。 基本的に次の動作を行います。
acquire()を呼び出す。 Kinectの関数を呼び出す。 結果を引数で指定した変数にコピーする。 release()を呼び出す |
返り値の型 | メソッド名 | 説明 |
---|---|---|
void | _setRGB(cv::Mat& image ) |
version1.6以降。スレッドセーフ。 setRGB()を呼び出し、rgbImageをimage にコピーする。以下の操作と同じ。 acquire(); setRGB(); rgbImageをimage にコピーする。 release(); |
void | _setDepth(cv::Mat image, bool raw = true) |
version1.6以降。スレッドセーフ。 setDepth(raw)を呼出しdepthImageをimage にコピーする。以下の操作と同じ。 acquire(); setDepth(raw ); depthImageをimage にコピーする。 release(); |
void | _setInfrared(cv::Mat& image ) |
version1.6以降。スレッドセーフ。 setInfrared()を呼び出し、infraredImageをimage にコピーする。以下の操作と同じ。 acquire(); setInfrared(); infraredImageをimage にコピーする。 release(); |
void | _setBodyIndex(cv::Mat& image ) |
version1.6以降。スレッドセーフ。 setBodyIndex()を呼び出し、bodyIndexImageをimage にコピーする。以下の操作と同じ。 acquire(); setBodyIndex(); bodyIndexImageをimage にコピーする。 release(); |
void | _setSkeleton(vector<vector<Joint> >& skel , vector<int>& skelId , vector<UINT64>& skelTrackingId ) |
version1.6以降。スレッドセーフ。 setSkeleton()を呼び出し、結果を引数にコピーする。以下の操作と同じ。 acquire(); setSkeleton(); skeletonをskel にコピーする。 skeletonIdをskelId にコピーする。 skeletonTrakingIdをskelTrackingId にコピーする。 release(); |
pair<int,int> | _handState(int id =0, bool isLeft =true) |
version1.6以降。スレッドセーフ。 handState()を呼び出し、結果を返す。以下の操作と同じ。 acquire(); auto ret = handState(id , isLeft ); release(); return ret; |
void | _setFace(vector<vector<PointF> >& point , vector<cv::Rect>& rect , vector<cv::Vec3f>& direction , vector<vector<DetectionResult> >& property , vector<UINT64>& trackingId , bool isColorSpace =true) |
version1.6以降。スレッドセーフ。USE_FACEをdefineした場合。 setFace(isColorSpace )を呼び出し、結果を引数にコピーする。次の操作と同等。 acquire(); setFace(isColorSpace ); facePointを point にコピーする。; faceRectを rect にコピーする。; faceDirectionを direction にコピーする。; facePropertyを property にコピーする。; faceTrackingIdを trakingId にコピーする。; release(); |
void | _setHDFace(vector<vector<CameraSpacePoint> >& vertices , vector<UINT64>& trackingId , vector<pair<int,int> >& status ) |
version1.6以降。スレッドセーフ。USE_FACEをdefineした場合。 setHDFace()を呼び出し、結果を引数にコピーする。次の操作と同等。 acquire(); setHDFace(); hdfaceVerticesを vertices にコピーする。; hdfaceTrackingIdを trackingId にコピーする。; hdfaceStatusを status にコピーする。; release(); |
void | _setGesture( vector<pair<CComPtr<IGesture>,float> >& discrete , vector<pair<CComPtr<IGesture>,float> >& continuous , vector<UINT64>& dTrackingId , vector<INT64>& cTrackingId ) |
version1.6以降。スレッドセーフ。USE_GESTUREをdefineした場合。 setGesture()を呼び出し、結果を引数にコピーする。次の操作と同等。 acquire(); setGesture(); discreteGestureを discrete にコピーする。; continuousGestureを continuous にコピーする。; discreteTrackingIdを dTrackingId にコピーする。; continuousTrackingIdを cTrackingId にコピーする。; release(); |
string | _gesture2string(const CComPtr<IGesture>& gesture ) | version1.6以降。スレッドセーフ。USE_GESTUREをdefineした場合。 gesture2string(gesture)を呼び出す。 |
void | _setAudio(float& angle , float& confidence , UINT64& trackingId , bool flag = false) |
version1.6以降。スレッドセーフ。USE_AUDIOをdefineした場合。 setAudio(flag)を呼び出し、結果を引数にコピーする。次の操作と同等。 acquire(); setAudio(flag); beamAngleを angle にコピーする。; beamAngleConfidenceを confidence にコピーする。; beamTrackingIdを trackingId にコピーする。; release(); |
void | _setSpeech(pair<wstring,wstring>& p ) |
version1.6以降。スレッドセーフ。USE_SPEECHをdefineした場合。 setSpeech()を呼び出し、結果を引数にコピーする。次の操作と同等。 acquire(); setAudio(flag); speechTagのコピーとspeechItemのコピーのpairを p に代入する。; release(); |
HRESULT | _MapCameraPointToColorSpace( CameraSpacePoint sp , ColorSpacePoint *cp ) |
version1.6以降。スレッドセーフ。 coordinateMapper->MapCameraPointToColorSpace(sp,cp)を呼び出す。 |
HRESULT | _MapCameraPointToDepthSpace( CameraSpacePoint sp , DelpthSpacePoint *dp ) |
version1.6以降。スレッドセーフ。 coordinateMapper->MapCameraPointToDepthSpace(sp,dp)を呼び出す。 |
HRESULT | _MapDepthPointToColorSpace( DepthSpacePoint dp , UINT16 depth , ColorSpacePoint *cp ) |
version1.6以降。スレッドセーフ。 coordinateMapper->MapDepthPointToColorSpace(dp,depth,cp)を呼び出す。 |
HRESULT | _MapDepthPointToCameraSpace( DepthSpacePoint dp , UINT16 depth , CameraSpacePoint *sp ) |
version1.6以降。スレッドセーフ。 coordinateMapper->MapDepthPointToCameraSpace(dp,depth,sp)を呼び出す。 |
main.cpp |
|
コンソール画面以外のウィンドウを'q'キーで閉じると、コンソール画面も閉じてプログラムが終了します。
上記のzipファイルには必ずしも最新の NtKinect.h が含まれていない場合があるので、 こちらから最新版をダウンロードして 差し替えてお使い下さい。
音声認識に必要なファイル(WaveFile.h, KinectAudioStream.h, KinectAudioStream.cpp, Grammaer_jaJP.grxml)がプロジェクトに追加されていて、ライブラリ(sapi.lib)も参照するように追加されているはずです。
スレッドセーフではない関数を呼び出すところでは kinect.acquire(); と kinect.release(); で囲んで、競合状態が発生するのを避けています。
main.cpp |
|
全てのスレッドが終了すると、コンソール画面も閉じてプログラムが終了します。
ただし、「音声認識と骨格認識」および「音声認識とbodyIndex画像取得」の同時動作は相性が悪く、 前者では骨格認識のスレッドが非常に遅くなり、また後者では音声認識の精度が非常に悪くなります。 どちらかのスレッドにSleep(INT32)関数を入れて動作のタイミングを調整すれば 少しはマシになるのかもしれませんが未確認です。
上記のzipファイルには必ずしも最新の NtKinect.h が含まれていない場合があるので、 こちらから最新版をダウンロードして 差し替えてお使い下さい。