上周完成了汇编语言这门课程的大作业展示,总体来说做得还是挺满意的。我们小组做的是一个摄像头控制类似微信飞机大战的程序。我们不想只是简单做一个小游戏,希望能将摄像头也加入进来,做一些控制的事情,所以才有了这个项目。在这个项目中,我主要负责的用汇编语言实现摄像头控制,需哥和磊哥负责实现类飞机大战的游戏程序。这是与普通微信飞机大战的区别在于就是我们额外实现了可以用电脑摄像头捕捉人脸移动,对应到飞机的移动,从而实现控制飞机大战游戏。

具体源码可参看 https://github.com/xu-wang11/AirWar

我这部分的工作主要是用汇编语言实现摄像头控制,控制的原理就是通过识别人脸并跟踪人脸的运动方向来控制飞机的运动。用汇编写基于摄像头的人脸识别程序,初听感觉笔者是在作死,这个难度太大了。其实我没有作死,只是走了些捷径罢了。

我就不卖关子了,我的做法是用 OpenCV 来实现人脸识别与跟踪部门,打开摄像头获取图像流数据用汇编实现。 起初,我们的想法是直接调用 OpenCV 的 DLL 文件,后面才发现 OpenCV 的函数虽然好用,但是参数实在太复杂了,从汇编传递参数过去比较麻烦。于是在请教了钱康来大神后,他机智地建议我们直接先用 C++ 将 OpenCV 的函数封装一下,将参数简化,然后编译成 DLL 供汇编调用。 汇编打开摄像头主要是通过调用 Win32 的 avicap32.dll 来实现,这部分主要参考自 http://www.aogosoft.com/downpage.asp?mode=viewtext&id=197, 这里就不再赘述了。

现在似乎有一个问题还没有解决,如何将汇编获取的每一帧图像传递给 OpenCV 程序呢? 通过查找资料发现,avicap32.dll 中提供了一种方式获取每一帧图像,就是回调 FrameCallbackProc 中 lpVHdr 里存放图像数据。因此我们只需要在 FrameCallbackProc 回调函数中调用我们编写的 DLL 函数,传递参数为 lpVHdr 即可,类型是 LPVIDEOHDR。那么我们在 C++ 函数中如何处理 LPVIDEOHDR lpVHdr 这个数据结构呢,我们知道 OpenCV 希望的图片数据类型是 IplImage, 那么如何实现从 LPVIDEOHDR 到 IplImage 的转换呢?

其实图像数据存储在 LPVIDEOHDR 中 ldata 域下, ldata 实质就是一个 BYTE* 类型。我想直接将 BYTE* 转为 IplImage,花了很长时间一直都没有成功。最后将 BYTE* 类型数据输出到文本,通过分析并查阅资料才发现,原来是因为数据格式的问题,这里获取到的 BYTE* 存储的图像数据的格式是 YUY2 格式的,一种压缩后的格式,而我们主流使用的格式是 RGB,然后就先做了 YUY2RGB 的转换, 再做 Byte2IplImg 的转换,最终顺利实现了 OpenCV 使用 MASM 汇编捕捉的视频图像。

下面给给出图像数据传递部分的主要代码:

FrameCallbackProc proc hWnd,lpVHdr
invoke detect_and_draw, lpVHdr
ret
FrameCallbackProc endp
void detect_and_draw(LPVIDEOHDR lpVHdr) 
{ 
    BYTE* data = new BYTE[512000];
	YUY2RGB(lpVHdr->ldata, data);
    IplImage* img = Byte2IplImg(data)
	......
}

之后的工作就是实现人脸识别与追踪算法,并通过判断相邻两帧图像中人脸的相对位移,可以得知人脸移动的方向,从而触发对应按键事件来实现控制。 这里的人脸识别与追踪算法使用的是 OpenCV 库里自带的样例程序,稍作修改就能达到我们的要求,就不一一赘述了。