Depth在kinect中经常被翻译为深度图,指的是图像到摄像头的距离,这些距离数据能让机器知道物理距离有多远。kinect通过两个红外摄像头来实现这个功能的。在这个例子里,就实现了深度图的提取和现实功能。
下面我们来研究下这个例子的代码,让我们对kinect for windows的开发包有个粗浅的认识。
代码结构:
主要的代码是DepthBasic.cpp,这个代码实现了深度图的读取
另外一个主要的代码文件时ImageRenderer,这个代码实现的是,将获取的深度图,展示在窗口上。
main函数:
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { CDepthBasics application; application.Run(hInstance, nCmdShow); }从main函数看,main函数只调用了两个函数,一个是CDepthBasics的构造函数,另外一个是CDepthBasics的Run函数。构造函数只是初始化,我们略过,继续看Run函数。
CDepthBasics::Run函数,解释都在代码中,大家可以看注释
int CDepthBasics::Run(HINSTANCE hInstance, int nCmdShow) { MSG msg = {0}; WNDCLASS wc; // Dialog custom window class win32创建窗口前的准备工作,构造窗口类结构 ZeroMemory(&wc, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hInstance; wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCE(IDI_APP)); wc.lpfnWndProc = DefDlgProcW; wc.lpszClassName = L"DepthBasicsAppDlgWndClass"; // 注册窗口类 if (!RegisterClassW(&wc)) { return 0; } // Create main application window 用该窗口类创建对话框 HWND hWndApp = CreateDialogParamW( hInstance, MAKEINTRESOURCE(IDD_APP), NULL, (DLGPROC)CDepthBasics::MessageRouter, reinterpret_cast<LPARAM>(this)); // Show window 显示对话框 ShowWindow(hWndApp, nCmdShow); // 这个用来检测kinect消息的event const int eventCount = 1; HANDLE hEvents[eventCount]; // Main message loop windwos消息循环,在这个消息循环里,如果没有kinect,那就是简单的处理窗口消息即可,但是.... while (WM_QUIT != msg.message) { // 为什么在这里赋值,相当于每次循环都赋值?因为这个句柄随着消息处理会变化 hEvents[0] = m_hNextDepthFrameEvent; // 检查kinect事件,第一个参数1表示等待一个句柄,第二个参数是消息数组,第三个参数指示是不是要等数组里的所有消息,参数是false // 第四个参数是等待多久,INFINITE表示永远,第五个参数呢,因为第四个参数说没有kinect消息这个函数就一直阻塞这里,那么它肯定可能影响正常的windows消息处理 // 所以第五个参数表示说有些情况下也要打断这个等待,QS_ALLINPUT就表示在有windows消息时,该函数也不再阻塞的继续往下执行 DWORD dwEvent = MsgWaitForMultipleObjects(eventCount, hEvents, FALSE, INFINITE, QS_ALLINPUT); // Check if this is an event we're waiting on and not a timeout or message 返回WAIT_OBJECT_0表示kinect有消息来,否则表示没消息 if (WAIT_OBJECT_0 == dwEvent) { // 处理kinect的消息 Update(); } // 处理windows消息 if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { // If a dialog message will be taken care of by the dialog proc if ((hWndApp != NULL) && IsDialogMessageW(hWndApp, &msg)) { continue; } TranslateMessage(&msg); DispatchMessageW(&msg); } } return static_cast<int>(msg.wParam); }
LRESULT CALLBACK CDepthBasics::DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: { // Bind application window handle m_hWnd = hWnd; // Init Direct2D 初始化DirectX D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory); // Create and initialize a new Direct2D image renderer (take a look at ImageRenderer.h) // We'll use this to draw the data we receive from the Kinect to the screen 创建图片展示对象 m_pDrawDepth = new ImageRenderer(); // 初始化展示对象,这里用到了窗口,用到了DirectX对象,以及宽度高度参数 HRESULT hr = m_pDrawDepth->Initialize(GetDlgItem(m_hWnd, IDC_VIDEOVIEW), m_pD2DFactory, cDepthWidth, cDepthHeight, cDepthWidth * sizeof(long)); if (FAILED(hr)) { SetStatusMessage(L"Failed to initialize the Direct2D draw device."); } // Look for a connected Kinect, and create it if found,连接kinect设备 CreateFirstConnected(); } break;
下面是初始化Kinect设备代码
HRESULT CDepthBasics::CreateFirstConnected() { INuiSensor * pNuiSensor; HRESULT hr; int iSensorCount = 0; hr = NuiGetSensorCount(&iSensorCount); // 获取连接的kinect数量 if (FAILED(hr)) { return hr; } // Look at each Kinect sensor 对每个kinect进行初始化 for (int i = 0; i < iSensorCount; ++i) { // Create the sensor so we can check status, if we can't create it, move on to the next // 获取kinect对象 hr = NuiCreateSensorByIndex(i, &pNuiSensor); if (FAILED(hr)) { continue; } // Get the status of the sensor, and if connected, then we can initialize it // 查看kinect状态,有的设备没连接电源,也许有的设备有其他异常 hr = pNuiSensor->NuiStatus(); if (S_OK == hr) // 如果有一台正常的,那我们这个程序的初始化就算完毕了,因为这个例子只用一个kinect而已 { m_pNuiSensor = pNuiSensor; break; } // This sensor wasn't OK, so release it since we're not using it 如果是不正常的设备,那么Release掉,免得内存泄露 pNuiSensor->Release(); } // 如果m_pNuiSensor不为空,那表明找到某一个正常的kinect设备了 if (NULL != m_pNuiSensor) { // 初始化kinect,用NUI_INITIALIZE_FLAG_USES_DEPTH表示要使用深度图 hr = m_pNuiSensor->NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH); if (SUCCEEDED(hr)) { // 创建这个Event,这个Event是kinect和应用程序通信的Event,当kinect有消息时,kinect SDK会通过SetEvent来通知应用程序 // 应用程序则通过WaitObject来等待这个Event,完成通信 m_hNextDepthFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Open a depth image stream to receive depth frames // 打开深度图流,用来接收图像 hr = m_pNuiSensor->NuiImageStreamOpen( NUI_IMAGE_TYPE_DEPTH, // 表示要打开深度图流 NUI_IMAGE_RESOLUTION_640x480, // 深度图大小 0, // 帧设置,0表示无设置 2, // 缓存多少帧,最大为4 m_hNextDepthFrameEvent, // 用来通信的Event句柄 &m_pDepthStreamHandle); // 用来读取数据的流句柄,要从这里读取深度图数据 } } if (NULL == m_pNuiSensor || FAILED(hr)) { SetStatusMessage(L"No ready Kinect found!"); return E_FAIL; } return hr; }