Microsoft Kinect SDK在初始化的时候,需要设置一个ManualReset的Event Handle丢进NuiImageStreamOpen里。
1 hNextColor = CreateEvent(NULL, TRUE, FALSE, NULL); 2 3 hr = m_pNuiSensor->NuiImageStreamOpen( 4 NUI_IMAGE_TYPE_COLOR, 5 NUI_IMAGE_RESOLUTION_640x480, 0, 2, 6 hNextColor, 7 &hColorStream);
但在处理的时候,没有任何地方调用ResetEvent来重置hNextColor。看微软提供的SkeletonViewer里面的有一段函数:第18行,调用一次WaitForMultipleObjects后,又在41/50/55行再次调用WaitForSingleObject来确认当前是否有Depth/Color/Skeleton帧。
1 DWORD WINAPI CSkeletalViewerApp::Nui_ProcessThread( ) 2 { 3 const int numEvents = 4; 4 HANDLE hEvents[numEvents] = { m_hEvNuiProcessStop, m_hNextDepthFrameEvent, m_hNextColorFrameEvent, m_hNextSkeletonEvent }; 5 int nEventIdx; 6 DWORD t; 7 8 m_LastDepthFPStime = timeGetTime( ); 9 10 // Blank the skeleton display on startup 11 m_LastSkeletonFoundTime = 0; 12 13 // Main thread loop 14 bool continueProcessing = true; 15 while ( continueProcessing ) 16 { 17 // Wait for any of the events to be signalled 18 nEventIdx = WaitForMultipleObjects( numEvents, hEvents, FALSE, 100 ); 19 20 // Timed out, continue 21 if ( nEventIdx == WAIT_TIMEOUT ) 22 { 23 continue; 24 } 25 26 // stop event was signalled 27 if ( WAIT_OBJECT_0 == nEventIdx ) 28 { 29 continueProcessing = false; 30 break; 31 } 32 33 // Wait for each object individually with a 0 timeout to make sure to 34 // process all signalled objects if multiple objects were signalled 35 // this loop iteration 36 37 // In situations where perfect correspondance between color/depth/skeleton 38 // is essential, a priority queue should be used to service the item 39 // which has been updated the longest ago 40 41 if ( WAIT_OBJECT_0 == WaitForSingleObject( m_hNextDepthFrameEvent, 0 ) ) 42 { 43 //only increment frame count if a frame was successfully drawn 44 if ( Nui_GotDepthAlert() ) 45 { 46 ++m_DepthFramesTotal; 47 } 48 } 49 50 if ( WAIT_OBJECT_0 == WaitForSingleObject( m_hNextColorFrameEvent, 0 ) ) 51 { 52 Nui_GotColorAlert(); 53 } 54 55 if ( WAIT_OBJECT_0 == WaitForSingleObject( m_hNextSkeletonEvent, 0 ) ) 56 { 57 Nui_GotSkeletonAlert( ); 58 }
若无ResetEvent的话,势必会造成下次调用WaitForMultipleObjects/WaitForSingleObject仍能成功,于是会导致重复帧。但实际情况上不会出现重复帧,于是猜测是在用户手动调用NuiImageStreamGetNextFrame更新FrameHandle时,这个函数在SDK内部将当前的 hNextColor重置。
模拟函数如下:
1 #include <iostream> 2 #include <Windows.h> 3 using namespace std; 4 5 HANDLE hNextRgb; 6 int timestamp = 0; 7 8 void FakeNuiSkeletonGetNextFrame() 9 { 10 // HRESULT NuiImageStreamGetNextFrame(HANDLE hStream,DWORD dwMillisecondsToWait,const NUI_IMAGE_FRAME **ppcImageFrame) 11 // update rgb stream handle : "HANDLE hStream" and reset next rgb event 12 ResetEvent(hNextRgb); 13 } 14 15 DWORD WINAPI RgbCaptureThread(LPVOID lpParameter) 16 { 17 while(1) 18 { 19 Sleep(30); // assume rgb frame is update 30ms/frame 20 timestamp++; 21 cout << "+ "; 22 SetEvent(hNextRgb); 23 } 24 return 0; 25 } 26 27 DWORD WINAPI MyProcssThread(LPVOID lpParameter) 28 { 29 while(1) 30 { 31 if ( WAIT_OBJECT_0 == WaitForSingleObject(hNextRgb,INFINITE) ) 32 { 33 FakeNuiSkeletonGetNextFrame(); 34 cout<< timestamp <<endl; 35 { 36 // copy the rgb frame, run your own algorithm 37 // if your process time is beyond 30ms, you will lose some rgb frames 38 int ProcessTime = 30; 39 Sleep(ProcessTime); 40 } 41 } 42 } 43 return 0; 44 } 45 46 void simulateKinect() 47 { 48 hNextRgb = CreateEvent(NULL,TRUE,FALSE,NULL); 49 50 HANDLE hThrd[2]; 51 hThrd[0] = CreateThread(NULL,0,MyProcssThread,NULL,0,NULL); 52 // hThrd[1] is hidden in SDK, there should be a rgb/depth thread that continues update kinect rgb/depth camera 53 hThrd[1] = CreateThread(NULL,0,RgbCaptureThread,NULL,0,NULL); 54 55 WaitForMultipleObjects(2, hThrd, TRUE, INFINITE); 56 CloseHandle(hThrd[0]); 57 CloseHandle(hThrd[1]); 58 CloseHandle(hNextRgb); 59 } 60 61 int main(){ 62 simulateKinect(); 63 return 0; 64 }
可以看到,如果用户在MyProcessThread中处理一帧的时间过长,超过30ms,就会导致错过下一帧的更新。
eg:ProcessTime = 40时,每更新4个Frame,只处理3个Frame,程序输出如下:
为了证实想法的正确性,搜了一下MSDN,发现如下描述:
http://msdn.microsoft.com/en-us/library/hh973076.aspx
Event Model
The event model supports the ability to integrate retrieval of a skeleton frame into an application engine with more flexibility and more accuracy.
In this model, C++ code passes an event handle to NuiImageStreamOpen to open a color or depth stream. The event handle should be a manual-reset event handle, created by a call to the Windows CreateEvent API. When a new frame of color or depth data is ready, the event is signaled. Any thread waiting on the event handle wakes and gets the frame of color or depth data by calling NuiImageStreamGetNextFrame or skeleton data by calling NuiSkeletonGetNextFrame. During this time, the event is reset by the NUI Image Camera API.
后话:Microsoft Kinect SDK目前的内部同步(RGB/DEPTH/SKELETON)仍然做的不是特别精准,所以下一步将要考虑如何设置缓存,并根据timestamp手工同步这三组数据。
下一篇文章,先测试下NuiSetFrameEndEvent~