Kinect桥接OpenCV代码简介Kinect Bridge With OpenCV
驱动版本:Kinect for Windows SDK v1.7
项目名称:KinectBridgeWithOpenCVBasics-D2D
编程语言:C++
程序配置和运行
安装程序后,在文件夹KinectBridgeWithOpenCVBasics-D2D中看到KinectBridgeWithOpenCVBasics-D2D.docx这个文档。文档中只说了如何配置OpenCV环境变量等等之类的,使用OpenCV的孩纸一眼就明白了。这里我还是使用自己常用的方法配置。(如果没有配置过OpenCV的,请去opencv.org.cn去学习如何配置,初学者往往需要1个小时到1天才能学会,熟练后5分钟)【你也可以按照微软说的方法进行配置,微软的方法虽然麻烦,但是一劳永逸!】
我是把OpenCV2.4.4安装在F盘的Softs文件夹下。使用VS2010进行配置。
在属性页中—>VC++目录:
包含目录:
F:SoftsOpenCV244opencvbuildinclude
$(KINECTSDK10_DIR)inc
库目录:
F:SoftsOpenCV244opencvbuildx86vc10lib
$(KINECTSDK10_DIR)libx86
源目录:
F:SoftsOpenCV244opencvmodulescoresrc
F:SoftsOpenCV244opencvmodulesimgprocsrc
F:SoftsOpenCV244opencvmoduleshighguisrc
在属性页中—>链接器:
附加依赖项:
opencv_core244d.lib
opencv_imgproc244d.lib
opencv_highgui244d.lib
Kinect10.lib
comctl32.lib
全部配置完毕后,编译F7,再执行Ctrl+F5。一点没有问题(如果有问题是你的配置问题,不要留言,请去OpenCV论坛求救),得到以下运行图像(头像被我用画笔刷掉了):
程序简要解读
FrameRateTracker类
用于计算视频帧率,使用clock()函数,每秒钟会调整一次帧率显示结果。
KinectHelper类
这个类的定义和实现都放在头文件中了。主要作用就是调用Kinect SDK中的函数,对Kinect进行初始化(打开Kinect和各种数据流,更新彩色、深度图像数据帧和骨骼数据帧),使用事件对象进行同步。数据被存放在m_pColorBuffer,m_pDepthBuffer,m_skeletonFrame数组中。
OpenCVHelper类
用于对图像进行滤波(高斯模糊GaussianBlur,腐蚀erode,膨胀dilate,canndy边缘检测),把骨骼数据滑到彩色图像和深度图像中(line和circle方法)。
OpenCVFrameHelper类
KinectHelper的子类,把数据存放到OpenCV的Mat矩阵结构中(方法pImage->ptr<Vec4b>(y))。
=================================================================
MainWindow类
主程序。使用互斥锁(CreateMutex)进行同步(他们以前的程序不进行同步),CreateMutex()函数可用来创建一个有名或无名的互斥量对象。
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
BOOL bInitialOwner, // 初始化互斥对象的所有者
LPCTSTR lpName // 指向互斥对象名的指针);
返回值
Long,如执行成功,就返回互斥体对象的句柄;零表示出错。会设置GetLastError。即使返回的是一个有效句柄,但倘若指定的名字已经存在,GetLastError也会设为ERROR_ALREADY_EXISTS
参数表 参数 类型及说明
lpMutexAttributes SECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,或传递零值(将参数声明为ByVal As Long,并传递零值),
表示使用不允许继承的默认描述符。
bInitialOwner BOOL,如创建进程希望立即拥有互斥体,则设为TRUE。一个互斥体同时只能由一个线程拥有。是FALSE,表示刚刚创建的这个Mutex不属于任何线程
也就是没有任何线程拥有他,一个Mutex在没有任何线程拥有他的时候,他是处于激发态的, 所以处于有信号状态。
lpName String,指定互斥体对象的名字。用vbNullString创建一个未命名的互斥体对象。如已经存在拥有这个名字的一个事件,则打开现有的已命名互斥体。这个名字可能不与现有的事件、信号机、可等待计时器或文件映射相符该名称可以有一个”Global” 或”Local” 前缀,明确地建立在全局或会话命名空间的对象。剩余的名称可以包含任何字符,除反斜杠字符()。
生成方法:
m_hColorResolutionMutex = CreateMutex(NULL, FALSE, NULL);
微软创建的所有的互斥锁参数都一样。表明,1使用不允许继承的默认描述符,2刚刚创建的这个Mutex不属于任何线程,3互斥体对象没有名字。
调用方法:
WaitForSingleObject(m_hColorResolutionMutex, INFINITE);
m_colorResolution = NUI_IMAGE_RESOLUTION_640x480;
ReleaseMutex(m_hColorResolutionMutex);
从使用方法看,和临界区的方法一模一样。之所以使用这些同步方法(互斥锁,临界区)是因为有些变量(或称为资源)会同时在多个线程中使用,为了避免资源使用冲突,使得多个线程排队使用这个变量。凡是在多个线程中同时使用的变量,都需要为它们设定一个同步方法(如果变量太多,最好把它组合成一个变量结构)。
线程开始(Run函数中):
m_hProcessThread = CreateThread(NULL, 0, ProcessThread, this, 0, NULL);
线程结束事件:
m_hProcessStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
主线程ProcessThread中使用while无限循环
四个事件对象(426行)
HANDLE hEvents[4] = {m_hProcessStopEvent(退出), 彩色, 深度, 骨骼};
等待某个内核对象被触发(一共四个)
int eventId = WaitForMultipleObjects(numEvents, hEvents, FALSE, 100);
之后就针对不同情况进行处理。除了对退出(eventId==0)进行判断,其它的都直接对所有数据进行更新,而不针对eventId的大小(1,2,3)而分别处理。这算是改进吧,其它的C++程序都改成这样子的了,应该算变得简单了。但这样会导致每次只有1个函数是实际运行的,另外二个是不运行的。可以把我之前Kinect人脸跟踪Kinect Face Tracking SDK文章中的if
(WAIT_OBJECT_0 == WaitForSingleObject(m_hNextDepthFrameEvent, 0))都去掉,程序一样可以运行,但是会提示数据没有捕获到(三个数据不是同时都能获取的,得排队)。
在MainWindow中还可以看到OpenCV的Mat如何被使用的:
1.原始数据BYTE–>存放到m_pColorBuffer中(m_frameHelper.UpdateColorFrame())
2.m_pColorBuffer构建成m_colorMat结构(m_frameHelper.GetColorImage(&m_colorMat)),3.对m_colorMat进行各种滤波方法(m_openCVHelper.ApplyColorFilter(&m_colorMat))
4.最后把滤波后的画出来(UpdateBitmap(&m_colorMat, &m_hColorBitmap, &m_bmiColor);底层又调用了SetDIBits(m_hdc, *phBitmap, 0, height, pImg->ptr(), pBmi, DIB_RGB_COLORS);)
5.最最后还需要刷新屏幕区域(InvalidateRect(m_hWndMain, NULL, false);)
由于使用windows api创建窗体,这个程序显得很臃肿,一共3000行代码(包含注释和空行)。但依旧有很多可以学习的地方,特别是他们写代码都很精美。这个程序仅仅是把数据传到OpenCV的结构中,并使用OpenCV的基本函数进行一些图像处理,最后再显示出来,一年多前写的文章代码实现的就是这个原理。
如果你一直使用windows api显示窗口,那么很容入门。如果不是,那么需要很多时间学习它(我一直用简单的MFC作窗口)。微软给的例子很强健,很稳定,考虑了许多情况。但如果想要自己实现更好的软件,还是建议使用MFC吧。
可以直接在我前些天的文章Kinect人脸跟踪Kinect Face Tracking SDK中做一些修改,即可得到OpenCV显示的图像。也不知道有多少人使用MFC,如果人多的话,可以花时间写一个简单范例出来,更不知道Qt(VS2010中开发)是否可以进行显示。