kinect for windows - DepthBasics-D2D详解之一

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);
}

从Run函数可以看出,对kinect的处理主要在Update函数中,在看Update函数之前,我们要考虑一点,就是对象的初始化,在构造函数中,只是赋值为NULL,像m_hNextDepthFrameEvent这样的对象,它是啥时候被初始化的呢?这个就需要读者对windows窗口机制要有一些了解了,在Run的时候,调用CreateDialogParamW函数时,系统会给对话框发送窗口初始化消息WM_INITDIALOG消息,那么这些句柄的初始化,都是在窗口初始化的时候做的,所以在看Update函数之前,我们先看窗口初始化的处理:

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;

在DlgProc的消息处理函数中我们看到了WM_INITDIALOG的处理,在这里初始化了DirectX和ImageRenderer对象,最后调用了CreateFirstConnected函数去查找和初始化Kinect对象。

下面是初始化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;
}


你可能感兴趣的:(kinect for windows - DepthBasics-D2D详解之一)