Microsoft Kinect SDK中的Event Model

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 NuiSkeletonGetNextFrameDuring this time, the event is reset by the NUI Image Camera API.

 

后话:Microsoft Kinect SDK目前的内部同步(RGB/DEPTH/SKELETON)仍然做的不是特别精准,所以下一步将要考虑如何设置缓存,并根据timestamp手工同步这三组数据。

下一篇文章,先测试下NuiSetFrameEndEvent~

posted @ 2012-09-10 16:53  tangyili  阅读(1635)  评论(2编辑  收藏  举报