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代表视频流:

  在“图像文件夹”按钮对应的处理函数OnBnClickedButtonImagefile()中,将该标志位置为真:

  同样,在“打开视频”按钮对应的事件处理函数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;
        }
    }

  这段代码看似繁琐,其实很容易理解,目的就是对各个帧的识别结果进行累计,在达到指定联合帧数之后根据之前的奇数帧中所含男性识别结果和女性识别结果的数量来给出最终的判断,类似于一种投票表决的工作方式。

  由于这里我们对性别识别函数做了较大的改动,这里给出目前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、图片较少,不愿爆照

  这篇博客中我没怎么粘贴程序的运行效果图,主要是因为在下相貌不够可人,不好意思爆照,不过代码都没有问题,亲测可用。

时间: 2024-10-07 02:04:32

C++开发人脸性别识别教程(16)——视频人脸性别识别的相关文章

Android 实现人脸识别教程[运用虹软人脸识别SDK]

基于虹软人脸识别引擎,在Android平台上实现人脸识别功能,即使在离线的情况下依旧运行,不被人采集个人照片的感觉,还是爽爽的.经过整个测试过来,虹软的人脸识别还是很强大的,人脸检测可以控制在20ms之内,人脸识别大概在200ms左右.今天就来分享一下开发经验 项目的目标 我们需要实现一个人脸识别功能.简单来说,就是机的后置摄像头,识别摄像头中实时拍到的人脸信息,如果人库注册过,则显示识别后的人脸信息,如登记的名字:如果不在,提示未注册. 这个功能具有多个应用场景,比如,火车站或者打卡和门禁系统

证件识别,身份证识别,人脸校对,活体检测,OCR身份证识别

在实名制铺天盖地的情况下,为了提高效率.节省成本,同时提升用户体验,身份证识别已迅速被各个企业所应用. 目前,市面上有身份证识别产品的公司大约有七八家,各家特点不一样,在选择身份证识别产品时要综合考量几家的优缺点. 作为后起之秀,译图智讯精于匠心,追求极致,努力将产品打造得更好,其身份证OCR识别具有以下几大特点: 1.快速:识别时间小于1秒 2.精准:识别率高达99% 3.多模式:可图像导入识别.可视频预览识别.也可拍照识别 4.正反面均可识别 5.可支持少数民族身份证识别及部分生僻字识别 6

C++开发人脸性别识别教程(13)——针对单张图片的性别识别

在之前的博文中我们的性别识别程序已经初步成型,能够识别某个文件夹下的图片文件.不过这里有一个问题,假设这个文件夹下有着大量的图片,而我们希望识别这些图片中的某一张,此时需要我们不停的单击“下一张”按钮才会轮询到对应的图片,这是相当麻烦的,因此在这篇博客中我们向程序中添加一个功能——单张图片的性别识别. 一.基本思想 最基本的办法就是在主界面再添加一个按钮控件,命名为“图片文件”(之前的按钮为“图片文件夹”),不过这样会使得界面上的按钮控件过于繁多,给人一种“作者只会用button控件”的感觉.这

C++开发人脸性别识别教程(12)——添加性别识别功能

经过之前几篇博客的讲解,我们已经成功搭建了MFC应用框架,并实现了基本的图像显示和人脸检测程序,在这篇博文中我们要向其中添加性别识别代码. 关于性别识别,之前已经专门拿出两篇博客的篇幅来进行讲解,这里不再赘述,具体参见:C++开发人脸性别识别教程(5)——通过FaceRecognizer类实现性别识别和C++开发人脸性别识别教程(6)——通过SVM实现性别识别. 一.分类器训练 在进行人脸性别识别之前需要训练性别识别的分类器,而分类器的训练过程是相对耗时的(大约五分钟),因此这里我们采用离线训练

C++开发人脸性别识别教程(5)——通过FaceRecognizer类实现性别识别

在之前的博客中已经解决了人脸检测的问题,我们计划在这篇博客中介绍人脸识别.性别识别方面的相关实现方法. 其实性别识别和人脸识别本质上是相似的,因为这里只是一个简单的MFC开发,主要工作并不在算法研究上,因此我们直接将性别识别视为一种特殊的人脸识别模式.人脸识别可能需要分为几十甚至上百个类(因为有几十甚至上百个人),而性别识别则是一种特殊的人脸识别——只有两个类. 一.基本工具 通过OpenCv进行性别识别的基本工具是FaceRecognizer.这是OpenCv2.x版本中的一个基本的人脸识别类

C++开发人脸性别识别教程(18)——辅助功能之文件名批量修改、方法验证

时光推移了30多天,这个人脸性别识别的小项目也接近尾声了,预计再通过三篇博文的篇幅来完成这个项目的收尾工作.在这篇博文中我们再为程序添加另外两个小的辅助功能:文件名批量修改.方法验证. 一 文件名批量修改 批量修改文件名是一件很基础也很常用的小操作,核心操作就是图像文件的批量读取.批量改名.批量保存.基本思想就是把文件读出来,然后在保存回去(注意不要和别的文件发生覆盖),从这个角度来讲文件名批量修改与上一篇博客C++开发人脸性别识别教程(17)——辅助功能之人脸批量分割中的人脸批量分割简直如出一

【《zw版&#183;Halcon与delphi系列原创教程》 zw_halcon人脸识别

[<zw版·Halcon与delphi系列原创教程> zw_halcon人脸识别 经常有用户问,halcon人脸识别方面的问题. 可能是cv在人脸识别.车牌识别方面的投入太多了. 其实,人脸识别.车牌识别,只是图像处理,机器视觉一个很小的领域,对于halcon而言,非常简单. 人脸识别.车牌识别,ocr.汉字识别原理.算法都差不多,自己建库就可以了. halcon与cv不同,面对的是工业(超市)流水线,建库基本是全自动的,扫描一下,和二维码差不多,就自动生成了相关的模型库. 非常简单,halc

Play Framework Web开发教程(16): 处理HTTP请求和响应

设计应用的一个方面是规划HTTP请求的URL规范,超链接,HTTP表单以及可能的公用API接口.在Play这是通过路由配置,然后在控制器中实现相应的接口.Play应用中的路由配置可以Controller类构成了MVC框架中的控制层,如下图所示:在Play应用中Controller为定义了HTTP接口的Scala类,而你的路由配置决定了给定的HTTP请求调用哪个Controller中定义的方法,这些Controller中的方法称为Action(动作),因此Play 的MVC框架也称为基于"动作&q

深度学习下的验证码识别教程

转:https://bbs.125.la/thread-14031313-1-1.html 随着大数据时代,人工智能时代的到来,深度学习的应用越来越广,场景识别.目标检测.人脸识别.图像识别等等广泛应用.在人工智能方面.深度学习框架主要运用于python,c++等资源.而易语言使用深度学习框架的资料,源码缺十分稀少.于是我决定给大家录制一套易语言深度学习的教程. 本套教程基于我自己开发的CC框架,下面这些图片案例,就是深度学习做的效果,非常棒: <ignore_js_op> 人脸识别.场景分割