C++开发人脸性别识别教程(16)——视频人脸性别识别
在之前的博文中我们已经可以顺利驱动摄像头来採集源图像。在这篇博文中将正式为其加入性别识别的代码,实现摄像头视频的人脸性别识别。
一、人脸检測
在得到摄像头採集的源图像之后,首先要做的就是对其进行人脸检測,将人脸区域切割出来。这步相对来说比較简单。仅仅需在定时器时间触发函数中加入人脸检測的代码就可以。这里给出OnTimer()函数的总体代码:
void CGenderRecognitionMFCDlg::OnTimer(UINT_PTR nIDEvent)
{
/***********人脸检測并识别**********/
m_pVideoInfo->m_pFrameImage = cvQueryFrame(m_pVideoInfo->m_pCapture);//得到视频流中的下一帧
IplImage* IplImg;
IplImg = m_pVideoInfo->m_pFrameImage;
detect_and_draw(IplImg); /***********显示图像**********/
CvvImage cvvImage;
cvvImage.CopyOf(IplImg);
cvvImage.DrawToHDC(m_pPicCtlHdc,m_PicCtlRect);
CDialogEx::OnTimer(nIDEvent);
}
注意这里相对于上一篇博客中的OnTimer()函数有一些修改,主要体如今两个方面:一是加入了detect_and_draw(IplImg)人脸检測与性别识别操作(detect_and_draw()函数内部默认调用了性别识别函数GenderRecognition())。完毕性别识别操作;二是在通过CvvImage类进行图片的显示时,推荐显示人脸检測之后的图像(这里的变量IplImg ),这样在显示结果中就行将人脸检測过程中所画的人脸框一并显示出来,显得更为形象。
OK。此时的程序已经具备了主要的摄像头视频性别识别功能,F5执行,初始化,打开视频,程序正常工作。
二、性别识别函数改进
尽管此时程序可以正常执行。但在执行过程中会发现程序的识别结果有时(甚至大部分时候)会不太稳定。即不断的在“帅哥”和“美女”之间变来变去。这直接说明了我们所採用的识别算法的鲁棒性非常不好,只是也情有可原。毕竟这里仅仅是使用了OpenCv提供的最主要的人脸检測方法和人脸识别方法,但这里我仍然希望在算法受限的条件下对其鲁棒性进行一下改进。这就用到了视频识别中经常使用的手段——多帧联合。
所谓多帧联合。就是对多帧图像进行识别分析。得到多个识别结果,然后在这个基础上通过加权融合,给出最后的识别结果。理论上多帧联合识别的手段可以排除某些突发的、极端错误的干扰,给出“总体正确”的判别结果,我们这里就採用这个手段对算法的鲁棒性略微做一点点改进。
2.1 确定识别帧数
要进行多帧联合识别。首先要确定一次处理多少帧,这里我们可以让用户来进行选择。与之前分类器的选择方法相似,我们在这里相同提供一个Combo-box控件,供用户在其列表中选择联合识别的帧数。
仿照之前下拉选择列表(Combo Box)控件的加入方法,这里再次加入一个相同类型的控件,将ID更改为:IDC_COMBO_NUM:
相应的,在CMFCShowVideoTestDlg类的OnInitDialog()函数中对其进行初始化。确定显示内容以及默认选项:
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("1");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("3");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("5");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("7");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("9");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("11");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("13");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->AddString("15");
((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->SetCurSel(4);
因为这里是二分类问题,因此联合帧数必须是奇数。这里相同须要提前指定Combo Box的下拉范围。以保证选项可以正常显示,同一时候将Combo Box控件的”sort“属性,应该置为”false“。此时执行程序。控件正常工作:
既然用户指定了联合识别的帧数,在进行视频性别识别之前。就须要先读取用户选择的帧数,与之前的分类器识别相似,须要在性别识别函数GenderRecognition()内部,通过switch语句来读取相应的选项值:
/***********依据当前用户选择的方法来确定联合识别的帧数**********/
int iFrameNum = 0;
index = ((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->GetCurSel();
switch (index)
{
case 0:
{
iFrameNum = 1;
break;
}
case 1:
{
iFrameNum = 3;
break;
}
case 2:
{
iFrameNum = 5;
break;
}
case 3:
{
iFrameNum = 7;
break;
}
case 4:
{
iFrameNum = 9;
break;
}
default:
{
break;
}
}
2.2 标记工作模式
在採用了帧联合判别的视频识别模式之后,导致了程序对单张图片和视频的识别的处理方式是不同的:单张图片意味着仅仅有一帧图像。无法进行帧联合判决,仅仅能一次给出结果。而在处理视频时则须要进行帧联合判决。
因此须要设置一个标志位来告诉程序当前的工作模式是单张图片还是视频流。向CGenderRecognitionMFCDlg类中加入一个布尔型变量m_boolModelFlag指示当前的工作模式,ture代表单张图片,False代表视频流:
在“图像目录”button相应的处理函数OnBnClickedButtonImagefile()中,将该标志位置为真:
相同。在“打开视频”button相应的事件处理函数OnBnClickedButton1Video()中。将该标志位置为False。
2.3 融合识别结果
接下来继续改造性别识别函数GenderRecognition()。这里首先须要向CGenderRecognitionMFCDlg类中加入两个整型变量m_ManNum和m_WomenNum,分别保存每一帧的识别结果,在加入一个整型变量m_FrameNum来保存当前已识别的帧数。然后就行进行多帧联合判别了,这里先给出总体代码,稍后解释:
if (m_boolModelFlag == TRUE) //若当前为图像模式
{
if (1 == m_genderLabel)
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("帅哥");
}
else if(2 == m_genderLabel)
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("美女");
}
}
else //若当前为视频模式
{
if (1 == m_genderLabel) //若当前帧识别结果为男性
{
m_ManNum = m_ManNum + 1;
}
else if(2 == m_genderLabel) //若当前帧识别结果为女性
{
m_WomenNum = m_WomenNum + 1;
}
m_FrameNum = m_FrameNum + 1;
if (m_FrameNum = iFrameNum) //达到指定识别帧数
{
if (m_ManNum > m_WomenNum)
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("帅哥");
}
else
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("美女");
}
m_ManNum = 0; //各个计数器清零
m_WomenNum = 0;
m_FrameNum = 0;
}
}
这段代码看似繁琐。事实上非常easy理解,目的就是对各个帧的识别结果进行累计。在达到指定联合帧数之后依据之前的奇数帧中所含男性识别结果和女性识别结果的数量来给出终于的推断,相似于一种投票表决的工作方式。
因为这里我们对性别识别函数做了较大的修改。这里给出眼下GenderRecognition()函数的完整代码:
void CGenderRecognitionMFCDlg::GenderRecognition(IplImage* img)
{
Mat image(img);
Mat trainImg;
resize(image,image,Size(92,112)); /***********依据当前用户选择的方法来使用相应的分类器进行分类**********/
int index = 0;
index = ((CComboBox*)GetDlgItem(IDC_COMBO_FUNCTION))->GetCurSel();
switch (index)
{
case 0:
{
m_genderLabel = model_PCA->predict(image);
break;
}
case 1:
{
m_genderLabel = model_Fisher->predict(image);
break;
}
case 2:
{
m_genderLabel = model_LBP->predict(image);
break;
}
case 3:
{
resize(image, trainImg, cv::Size(64,64), 0, 0, INTER_CUBIC);
HOGDescriptor *hog=new HOGDescriptor(cvSize(64,64),cvSize(16,16),cvSize(8,8),cvSize(8,8), 9);
vector<float>descriptors;
hog->compute(trainImg, descriptors,Size(1,1), Size(0,0));
Mat SVMtrainMat = Mat::zeros(1,descriptors.size(),CV_32FC1);
int n=0;
for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)
{
SVMtrainMat.at<float>(0,n) = *iter;
n++;
}
m_genderLabel = svm.predict(SVMtrainMat);
break;
}
default:
{
break;
}
} /***********依据当前用户选择的方法来确定联合识别的帧数**********/
int iFrameNum = 0;
index = ((CComboBox*)GetDlgItem(IDC_COMBO_NUM))->GetCurSel();
switch (index)
{
case 0:
{
iFrameNum = 1;
break;
}
case 1:
{
iFrameNum = 3;
break;
}
case 2:
{
iFrameNum = 5;
break;
}
case 3:
{
iFrameNum = 7;
break;
}
case 4:
{
iFrameNum = 9;
break;
}
default:
{
break;
}
} /**********显示识别结果**********/
if (m_boolModelFlag == TRUE) //若当前为图像模式
{
if (1 == m_genderLabel)
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("帅哥");
}
else if(2 == m_genderLabel)
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("美女");
}
}
else //若当前为视频模式
{
if (1 == m_genderLabel) //若当前帧识别结果为男性
{
m_ManNum = m_ManNum + 1;
}
else if(2 == m_genderLabel) //若当前帧识别结果为男性
{
m_WomenNum = m_WomenNum + 1;
}
m_FrameNum = m_FrameNum + 1;
if (m_FrameNum == iFrameNum) //达到指定识别帧数
{
if (m_ManNum > m_WomenNum)
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("帅哥");
}
else
{
GetDlgItem(IDC_EDIT_RESULT)->SetWindowText("美女");
}
m_ManNum = 0; //各个计数器清零
m_WomenNum = 0;
m_FrameNum = 0;
}
}
}
OK,此时在此执行程序,发现结果显示比之前要稳定很多。说明我们的改进是有效果的。
三、注意事项
1、人脸检測的精确度。性别识别的稳定性
在对视频图像进行识别的过程中,充分暴露了我们所採用算法的不可靠性,即结果变来变去的,因此假设须要真正开发性别识别的相关应用时,还是要寻求更准确,鲁棒性更高的算法。
2、性别识别的速度。图像显示的流畅性问题
这里因为人脸检检測、性别识别的过程存在较大消耗,因此会使得在显示过程中视频出现一定程度的卡顿现象。
3、变量的默认初始化
在向类中加入成员变量的时候一定要注意及时在构造函数中对成员变量进行初始化操作。这里VS的MFC类向导会默认帮助我们完毕一些(但不是所有)初始化操作。方便很多。
4、图片较少。不愿爆照
这篇博客中我没怎么粘贴程序的执行效果图,主要是因为在下相貌不够可人,不好意思爆照。只是代码都没有问题。亲測可用。
C++开发人脸性别识别教程(16)——视频人脸性别识别的更多相关文章
- 【GStreamer开发】GStreamer基础教程16——平台相关的element
目标 虽然GStreamer是跨平台的framework,但不是所有的element都是在所有平台下都有的.比如,音频和视频的sink都非常依赖于当前的window系统,根据当前的平台需要选择不同的e ...
- C++开发人脸性别识别教程(19)——界面美化
在这篇博文中将完毕<C++开发人脸性别识别>的收尾工作.主要内容分为两部分:加入视频暂定功能.界面规范化. 一 视频暂停功能 严格来说这个视频暂定功能算是视频人脸性别识别的一个遗留问题,本 ...
- C++开发人脸性别识别教程(12)——加入性别识别功能
经过之前几篇博客的解说,我们已经成功搭建了MFC应用框架,并实现了主要的图像显示和人脸检測程序,在这篇博文中我们要向当中加入性别识别代码. 关于性别识别,之前已经专门拿出两篇博客的篇幅来进行解说.这里 ...
- C++开发人脸性别识别教程(10)——加入图片的人脸检測程序
现在我们的MFC框架已经初具规模,能够读取并显示目录下的图片.在这篇博文中我们将向当中加入人脸检測的程序. 一.人脸检測算法 这里我们使用OpenCv封装的Adaboost方法来进行人脸检測,參见:C ...
- C++开发人脸性别识别教程(5)——通过FaceRecognizer类实现性别识别
在之前的博客中已经攻克了人脸检測的问题,我们计划在这篇博客中介绍人脸识别.性别识别方面的相关实现方法. 事实上性别识别和人脸识别本质上是相似的,由于这里仅仅是一个简单的MFC开发,主要工作并不在算法研 ...
- C++开发人脸性别识别教程(7)——搭建MFC框架之界面绘制
在之前的博客中我们已经将项目中用到的算法表述完成,包含人脸检測算法以及四种性别识别算法,在这篇博客中我们将着手搭建主要的MFC框架. 一.框架概况 在这篇博文中我们将搭建最主要的MFC框架.绘制MFC ...
- 基于Emgu CV+百度人脸识别,实现视频动态 人脸抓取与识别
背景 目前AI 处于风口浪尖,作为 公司的CTO,也作为自己的技术专研,开始了AI之旅,在朋友圈中也咨询 一些大牛对于AI 机器学习框架的看法,目前自己的研究方向主要开源的 AI 库,如:Emgu C ...
- C# 虹软SDK视频人脸识别和注册
一,准备工作 1.Afoge视频参数类 using AForge.Video.DirectShow; using System; using System.Collections.Generic; u ...
- 【《zw版·Halcon与delphi系列原创教程》 zw_halcon人脸识别
[<zw版·Halcon与delphi系列原创教程>zw_halcon人脸识别 经常有用户问,halcon人脸识别方面的问题. 可能是cv在人脸识别.车牌识别方面的投入太多了. 其实,人脸 ...
随机推荐
- at, batch, atq, atrm - 排队、检查或删除以后要执行的作业
总览 at [-V] [-q 队列] [-f 文件] [-mldbv] 时间 at -c 作业 [作业...] atq [-V] [-q 队列] [-v] atrm [-V] 作业 [作业...] b ...
- CAD交互绘制带颜色宽度的直线(com接口)
用户可以在控件视区任意位置绘制直线. 主要用到函数说明: _DMxDrawX::DrawLine 绘制一个直线.详细说明如下: 参数 说明 DOUBLE dX1 直线的开始点x坐标 DOUBLE dY ...
- vue工程化之去除Eslint验证
有的时候用vue-cli创建好项目之后,写代码时会出现换行和空格报错,出现这么写错误是什么原因呢? 相信第一次接触时有点摸不着头脑.其实是在你用vue-cli脚手架构建项目时用了ESLint代码检查工 ...
- vue+webpack静态资源路径引用
处理静态资产 你可能已经注意到,在项目结构中我们有两个静态资产目录:src/assets和static/.他们之间有什么区别? 要回答这个问题,我们首先需要了解Webpack如何处理静态资产.在*.v ...
- JavaSE-13 内部类
学习要点 内部类的定义 内部类的应用 内部类 定义 Java的一个类中包含着另一类. A类和B类是C类的外部类.B类是C类的外部类.A类也称为顶层类. 如何使用内部类 public class MyF ...
- 布尔上下文,这里misreading返回的是来源列表中元素的个数,如果列表中2个值都是undef,则列表元素个数是1: while( $misreading = (my $test_consideration, my $english_pragma) = each %map_function){
布尔上下文,这里misreading返回的是来源列表中元素的个数, 列表赋值运算的值将会是来源列表中元素的个数,空列表表示0,如果列表中2个值都是undef,则列表元素个数是1 布尔上下文,这里mis ...
- 去掉PhpStorm打开自动启动的项目
有时候启动的项目很大,导致很卡,而且在编辑器中更改也没效果,简单暴力的办法 找到项目的目录,删除其中的.idea文件夹
- tensorflow 学习笔记-- tf.reduce_max、tf.sequence_mask
1.tf.reduce_max函数的作用:计算张量的各个维度上的元素的最大值.例子: import tensorflow as tfmax_value = tf.reduce_max([1, 3, 2 ...
- better-scroll的使用
<template> <div> <div> <h2 class="h2">{{msg}}</h2> </div& ...
- [Python3网络爬虫开发实战] 7.1-Selenium的使用
Selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击.下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬.对于一些JavaScript动态渲染的页面来说 ...