C++开发人脸性别识别教程(15)——搭建MFC框架启动摄像头

阳光穿透心脏的1/2处 2022-08-20 12:18 370阅读 0赞

  在之前的博文中已经完成了针对图片的人脸性别识别功能,在这篇文章中我们开始引入摄像头设备,为程序添加第二个功能:视频人脸性别识别。

  一、添加控件

  这里需要新添加两个与视频人脸性别识别相关的功能控件,一个是“打开视频”按钮(ID为IDC_OpenVideo),一个是“暂停按钮”按钮。为了适当减少主窗口中的按钮控件的数量,这里再次采用一种复用策略,即将视频识别模式中的“暂停”功能与之前图片文件夹识别模式中的“下一张”功能合并,通过一个按钮来控制:

589850-20160123095919672-1801552136.png

  二、CVideoInfo类

  2.1 添加视频流类

  接下来需要开启视频摄像头,将摄像头得到的图像实时的显示在主程序的picture控件中。有关OpenCv中的摄像头操作我在C++开发人脸性别识别教程(4)——OpenCv的人脸检测函数这篇博文中进行了较为详细的介绍。这里我们对摄像头的开启进行一下小小的封装,即将其声明为一个类CVideoInfo,这样做的原因主要是为了能够方便后期的处理,我们将摄像头输出的视频流、帧图像、帧图像的尺寸(宽度和高度)都封装在同一个对象中,方便读取,也方便获取图像的相关属性。

  在VS中切换到类视图窗口,右击工程名,添加类:

589850-20160226193658177-1515835296.png

  指定添加类的类型为“C++类”:

589850-20160226195021849-2102781885.png  输入类名CVideoInfo,对应的.h文件和.cpp文件名称系统会自动生成,无继承,访问属性默认使用public,单击完成:

589850-20160226195738318-43505808.png

  此时在类视图下可以看到工程中多了一个名为CVideoInfo的类:

589850-20160226195942583-147976444.png

  2.2 编辑CVideoInfo类

  接下来为CVideoInfo添加相应的代码。切换到解决方案资源管理器窗口,会发现此时工程目录下会多出两个文件,VideoInfo.h和VideoInfo.cpp:

589850-20160226200334208-1984316745.png

  首先编辑类对应的头文件VideoInfo.h,发现VS已经在头文件中提供了基本的类声明,并给出了缺省的构造函数和析构函数,我们在这里只需向其中添加若干成员变量即可,这里我们添加四个成员变量:CvCapture*格式的视频流变量,IplImage*格式的帧图像变量,以及两个整型变量用以表示帧图像的宽度和高度:

  1. CvCapture* m_pCapture; //用于存储摄像头输入的视频流
  2. IplImage* m_pFrameImage; //存储图片
  3. int m_FrameWidth; //图片宽度
  4. int m_FrameHeight; //图片高度

  最终VideoInfo.h文件中的类声明代码如下:

  1. #pragma once
  2. #include "opencv2/opencv.hpp"
  3. class CVideoInfo
  4. {
  5. public:
  6. CvCapture* m_pCapture; //用于存储摄像头输入的视频流
  7. IplImage* m_pFrameImage; //存储图片
  8. int m_FrameWidth; //图片宽度
  9. int m_FrameHeight; //图片高度
  10. public:
  11. CVideoInfo(void); //缺省构造函数
  12. public:
  13. ~CVideoInfo(void); //缺省析构函数
  14. };

  至于VideoInfo.cpp,由于VideoInfo类目前只是负责图像的存储和显示,没有其他额外的功能,因此在这里只需要完成构造函数对成员变量的初始化,而析构函数采用系统提供的缺省函数即可,VideoInfo.cpp文件的代码如下:

  1. #include "StdAfx.h"
  2. #include "VideoInfo.h"
  3. CVideoInfo::CVideoInfo(void) //缺省构造函数
  4. {
  5. m_pCapture = NULL;
  6. m_pFrameImage = NULL;
  7. }
  8. CVideoInfo::~CVideoInfo(void)//缺省析构函数
  9. {
  10. }

  完成了类的定义之后,需要向CGenderRecognitionMFCDlg类中添加一个VideoInfo*形式的变量m_pVideoInfo用来保存当前的视频流信息,当然使用全局变量也可以实现这个功能,不过会是代码变得更复杂,不推荐。

589850-20160226205521552-1295501644.png

  在添加变量之前需要先在CGenderRecognitionMFCDlg.h中包含一下VideoInfo.h头文件,使得VideoInfo类可见:

589850-20160226204534802-922812752.png

  接下来就可以正式着手开启摄像头了。

  三、开启摄像头

  双击“打开视频”按钮,添加对应的事件处理函数:

589850-20160226204743099-1028554923.png

  首先,判断程序是否进行了分类器加载初始化:

  1. if (m_boolInitOK == FALSE)
  2. {
  3. MessageBox("请先进行初始化");
  4. return;
  5. }

  然后使用cvCreateCameraCapture函数开启摄像头,并将视频流赋值给m_pVideoInfo:

  1. m_pVideoInfo->m_pCapture = cvCreateCameraCapture(0);//创建一个Capture(摄像头)

  注意此时运行程序的话在这句代码会报错,原因是m_pVideoInfo这个成员变量尚未被分配内存空间,因此需要在程序开始运行时手动通过new操作符对其进行内存空间的分配,这里将内存分配的语句放置在OnInitDialog()这个成员函数中:

  1. /*********为m_pVideoInfo变量分配内存空间**********/
  2. m_pVideoInfo = new CVideoInfo;

  四、显示图像

  在摄像头捕捉到图像之后,接下来需要将帧图像实时的显示在picture界面中,实现这个功能的方法有很多,这里采用定时器的方法。即设置一个定时器,每隔一定时间就触发,在对应的回调函数中完成帧图像的显示工作。因此在OnBnClickedButton1Video()函数中需要对定时器进行初始化,单击“打开视频”按钮后,就开始触发定时器,轮询播放摄像头画面:

  1. SetTimer(1,1,NULL);//触发一个计数器,在响应函数中完成图像显示

  有关MFC中定时器的使用大家参见网上资料,这里就不再赘述。

  接下来添加定时器消息响应函数,在类视图中右键单击CGenderRecognitionMFCDlg类,选择属性,弹出属性对话框,在消息栏中找到WM_TIMER,单击右边的下拉箭头,添加消息响应函数OnTimer:

589850-20160226211113708-256273872.png

  在OnTimer()函数体中完成帧图像的绘制,这里直接给出代码:

  1. void CGenderRecognitionMFCDlg::OnTimer(UINT_PTR nIDEvent)
  2. {
  3. m_pVideoInfo->m_pFrameImage = cvQueryFrame(m_pVideoInfo->m_pCapture);//得到视频流中的下一帧
  4. CvvImage cvvImage;
  5. cvvImage.CopyOf(m_pVideoInfo->m_pFrameImage);
  6. cvvImage.DrawToHDC(m_pPicCtlHdc,m_PicCtlRect);
  7. CDialogEx::OnTimer(nIDEvent);
  8. }

  此时运行程序,程序正常执行,能够在picture界面上实时的显示摄像头采集的图像,在下一篇博客中将介绍如何向其中添加性别识别的步骤。

589850-20160227091406458-1343147009.png

  五、注意事项

  1、CVideoInfo类的封装问题

  说实话这里将摄像头输入的视频流封装为CVideoInfo类的工作方式显得有点小题大做,似乎直接在类中添加一个CvCapture*类型的成员变量就能够完成任务,但随着程序的改进,我们可能需要得到越来越多的视频帧图像的属性值(如尺寸,通道数等等),甚至需要在读取帧图像之前对原图像做一些必要的、固化的初始化操作,如果我们将视频流封装成CVideoInfo类,就能够方便的以成员函数的形式给出这些操作、属性值,避免了在程序的大框架下添加,也避免了对各个属性值的频繁读取。

  2、类成员访问属性问题

  C++中对类成员变量的访问属性推荐设置为private,对成员函数的访问属性推荐设置为public,并且推荐通过成员函数来访问成员变量,而非直接读取。

  3、定时器问题

  关于VC中定时器的用法网上有很多详细的说明,例如VC定时器的用法:SetTimer和Ontimer ,这里将时间间隔设置为1毫秒,即每一毫秒就刷新一帧,大家可以尝试更改其他的时间间隔,观察效果。

  4、OnInitDialog函数问题

  OnInitDialog()函数在对话框生成的时候调用,因此在基于对话框的程序中,这个函数用来执行程序必要的初始化操作,目前为止在我们的工程中这个函数承担了初始化Combo Box控件、初始化Combo Box控件、为m_pVideoInfo变量分配内存空间的任务,随着程序的编写,还会有更多的初始化操作被加入到这个函数体中。

  5、致歉

  这篇博客是我在寒假之后写的,中间有一个多月的时间没写过代码,所以思路有点混乱,叙述的不是很清晰,大家见谅。

如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢。

20180420194623129

发表评论

表情:
评论列表 (有 0 条评论,370人围观)

还没有评论,来说两句吧...

相关阅读