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

  在之前的博客中已经解决了人脸检测的问题,我们计划在这篇博客中介绍人脸识别、性别识别方面的相关实现方法。

  其实性别识别和人脸识别本质上是相似的,因为这里只是一个简单的MFC开发,主要工作并不在算法研究上,因此我们直接将性别识别视为一种特殊的人脸识别模式。人脸识别可能需要分为几十甚至上百个类(因为有几十甚至上百个人),而性别识别则是一种特殊的人脸识别——只有两个类。

  一、基本工具

  通过OpenCv进行性别识别的基本工具是FaceRecognizer。这是OpenCv2.x版本中的一个基本的人脸识别类,它封装了三种基本但也是经典的人脸识别算法:基于PCA变换的人脸识别(EigenFaceRecognizer)、基于Fisher变换的人脸识别(FisherFaceRecognizer)、基于局部二值模式的人脸识别(LBPHFaceRecognizer)。这些算法差不多都是十年以前的人脸识别方法了,因此在今天看来正确率应该不会太让人满意,不过我们这里重在实践,而非算法研究(虽然本人就是搞图像识别算法研究的),因此我们不会在算法创新方面下太多功夫,所以选择了这三个基本的识别算法。

  关于FaceRecognizer类人脸识别的详细操作,这里为大家推荐两篇博客:FaceRecognizer帮助文档以及FaceRecognizer

  这里我们直接使用FaceRecognizer类的相关操作方法,对于其基本用法就不再赘述。

  二、数据集准备

  进行性别识别理所应当需要先准备一些性别识别方面的训练样本,需要强调的一点是,数据集的准备过程中也需要一些小的技巧,在之后我会专门写一篇博文来解释如何制作一个简易的性别识别训练集,这里我们直接用我已经做好的训练集,下载地址:性别识别数据集

  1、概况

  我这里整理的性别识别训练集是取自中科院的人脸数据库CAS-PEAL的光照子集,包含400张男性人脸图片和400张女性人脸图片,剩余人脸图片作为测试样本

  2、训练集基本结构

  训练集包含三部分:男性样本、女性样本、测试样本:

  这里我们通过CSV文件方法来批量读取训练样本,因此这里提前制作了一个txt文件来存储每一个训练样本图片的路径:

  注意这里at.txt文件中的路径实际上是由两部分内容组成,即“路径;性别标号”。性别标号“1”代表男性,“2”代表女性。至于如何通过csv文件方法来批量读取文件,参见:一种批量读取文件的方法—CSV文件

  同理,在测试样本中同样需要用txt文件来记录样本路径和标签:

  三、识别算法的训练与测试

  1、新建一个控制台工程,配置OpenCv

  这里不再赘述,建议加上预编译头即可,这里工程名暂定为GenderRecognition

  2、编写批量读取文件函数read_csv()

  首先,批量txt文件是典型的io操作,需要包含以下头文件:

#include <iostream>
#include <sstream>
#include <fstream>

  然后开始编写read_csv函数,函数相对比较简单,这里直接给出代码:

void read_csv(string& fileName,vector<Mat>& images,vector<int>& labels,char separator = ‘;‘)
{
    ifstream file(fileName.c_str(),ifstream::in);    //以读入的方式打开文件
    String line,path,label;
    while (getline(file,line))                       //从文本文件中读取一行字符,未指定限定符默认限定符为“/n”
    {
        stringstream lines(line);
        getline(lines,path,separator);               //根据指定分割符进行分割,分为“路径+标号”
        getline(lines,label);
        if (!path.empty()&&!label.empty())           //如果读取成功,则将图片和对应标签压入对应容器中
        {
            images.push_back(imread(path,1));        //读取训练样本
            labels.push_back(atoi(label.c_str()));   //读取训练样本标号
        }
    }
}

  read_csv()函数的主要功能就是读取指定目录下的路径文件(例如这里的at.txt),然后根据路径文件中的记录,逐行读入对应路径的训练样本路径及其标号,并放入对应容器(vector)中。至于为什么采用vector数据结构来存储训练样本,一是因为这样做简单直观,二是因为OpenCv的训练函数提供的是vector接口。当然这样做也存在一定弊端,就是必须一次性将训练样本全部读入到内存中,当训练样本数量庞大时这种方法不但会消耗掉巨额内存,而且效率低下。

  更多关于read_csv()批量读取的知识参见一种批量读取文件的方法—CSV文件

  3、读入训练样本

  接下来在主函数中调用read_csv()函数,读取训练样本及标签,并放入对应容器中:

int _tmain(int argc, _TCHAR* argv[])
{
    String csvPath = "E:\\性别识别数据库—CAS-PEAL\\at.txt";
    vector<Mat> images;
    vector<int> labels;
    read_csv(csvPath,images,labels);
    return 0;
}

  读取成功,images和labels两个容器都包含800个样本:

  4、训练分类器

  OpenCv中的FaceRecognizer类提供的分类器训练API函数非常简单,只需三句话,以EigenFaceRecognizer为例:

    Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
    modelPCA->train(images,labels);
    modelPCA->save("E:\\性别识别数据库—CAS-PEAL\\PCA_Model.xml");

  训练完成后(大约五分钟左右),训练好的分类器已经以XML文件的形式保存在了指定路径下:

  同理,训练FisherFaceRecognizer、LBPHFaceRecognizer两个分类器并保存:

    Ptr<FaceRecognizer> modelFisher = createFisherFaceRecognizer();
    modelFisher->train(images,labels);
    modelFisher->save("E:\\性别识别数据库—CAS-PEAL\\Fisher_Model.xml");

    Ptr<FaceRecognizer> modelLBP = createLBPHFaceRecognizer();
    modelLBP->train(images,labels);
    modelLBP->save("E:\\性别识别数据库—CAS-PEAL\\LBP_Model.xml");

  得到另外两个分类器:

  4、测试分类器

  训练完分类器后,接下来我们介绍如何使用这些训练好的分类器对测试样本进行分类。首先加载三个分类器

    Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
    Ptr<FaceRecognizer> modelFisher = createFisherFaceRecognizer();
    Ptr<FaceRecognizer> modelLBP = createLBPHFaceRecognizer();

    modelPCA->load("E:\\性别识别数据库—CAS-PEAL\\PCA_Model.xml");
    modelFisher->load("E:\\性别识别数据库—CAS-PEAL\\Fisher_Model.xml");
    modelLBP->load("E:\\性别识别数据库—CAS-PEAL\\LBP_Model.xml");

  然后读入一张测试样本,通过三个分类器对其进行预测:

    Mat testImage = imread("E:\\性别识别数据库—CAS-PEAL\\测试样本\\男性测试样本\\face_480.bmp",0);
    int predictPCA = modelPCA->predict(testImage);
    int predictLBP = modelLBP->predict(testImage);
    int predictFisher = modelFisher->predict(testImage);

  预测结果如图:

  可见对于这张测试图片,三个分类器均给出了正确预测(数字“1”代表男性),正确率可以接受。

  四、代码

  这部分博客所涉及的代码同样较为简洁,因此在这里给出整体代码:

// GenderRecognition.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <opencv2\opencv.hpp>
#include <iostream>
#include <sstream>
#include <fstream>

using namespace std;
using namespace cv;

void read_csv(string& fileName,vector<Mat>& images,vector<int>& labels,char separator = ‘;‘)
{
    ifstream file(fileName.c_str(),ifstream::in);    //以读入的方式打开文件
    String line,path,label;
    while (getline(file,line))                       //从文本文件中读取一行字符,未指定限定符默认限定符为“/n”
    {
        stringstream lines(line);
        getline(lines,path,separator);               //根据指定分割符进行分割,分为“路径+标号”
        getline(lines,label);
        if (!path.empty()&&!label.empty())           //如果读取成功,则将图片和对应标签压入对应容器中
        {
            images.push_back(imread(path,0));        //读取训练样本
            labels.push_back(atoi(label.c_str()));   //读取训练样本标号
        }
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    String csvPath = "E:\\性别识别数据库—CAS-PEAL\\at.txt";
    vector<Mat> images;
    vector<int> labels;
    read_csv(csvPath,images,labels);

    Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
    modelPCA->train(images,labels);
    modelPCA->save("E:\\性别识别数据库—CAS-PEAL\\PCA_Model.xml");

    Ptr<FaceRecognizer> modelFisher = createFisherFaceRecognizer();
    modelFisher->train(images,labels);
    modelFisher->save("E:\\性别识别数据库—CAS-PEAL\\Fisher_Model.xml");

    Ptr<FaceRecognizer> modelLBP = createLBPHFaceRecognizer();
    modelLBP->train(images,labels);
    modelLBP->save("E:\\性别识别数据库—CAS-PEAL\\LBP_Model.xml");

    //Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
    //Ptr<FaceRecognizer> modelFisher = createFisherFaceRecognizer();
    //Ptr<FaceRecognizer> modelLBP = createLBPHFaceRecognizer();

    modelPCA->load("E:\\性别识别数据库—CAS-PEAL\\PCA_Model.xml");
    modelFisher->load("E:\\性别识别数据库—CAS-PEAL\\Fisher_Model.xml");
    modelLBP->load("E:\\性别识别数据库—CAS-PEAL\\LBP_Model.xml");

    Mat testImage = imread("E:\\性别识别数据库—CAS-PEAL\\测试样本\\男性测试样本\\face_480.bmp",0);
    int predictPCA = modelPCA->predict(testImage);
    int predictLBP = modelLBP->predict(testImage);
    int predictFisher = modelFisher->predict(testImage);

    return 0;
}

  四、总结

  这篇博客主要介绍了如何使用OpenCv提供的人脸识别类FaceRecognizer来进行性别识别,并提供了一段win32控制台工程下的简洁代码,同时,有以下几个方面需要特别注意一下。

  1、人脸识别和性别识别的关系

  在这篇博客的开始部分曾提到过性别识别和人脸识别的关系,在这里需要再次强调一下。性别识别本质上属于人脸识别,但是和人脸识别还是有很多方面的区别。性别识别是二分类问题,人脸识别是多分类问题,二者在算法上也有很大差异。我们这里之所以简单的将性别识别看做简化的人脸识别,是因为在这套教程中我们主要注重实践,注重OpenCv的使用以及MFC框架编程方法,因此在算法方面会显得不够严谨。因此希望大家不要被这些简化的观点所误导,真正的性别识别算法也远比这些复杂,也和人脸识别方法大不相同,作为图像处理的行内人,我觉得很有必要把这点说清楚。

  2、read_csv函数

  这里对read_csv()批量读取函数介绍得相对简洁,大家可以参照我提供的博客来进行详细学习,同时考虑到这个函数相对简洁,可以凡在main()函数之前,从而避免提前声明。

  3、数据集原始路径问题

  这篇博文中并没有详细介绍如何制作性别识别训练数据集,因此大家在使用网上下载的数据集时一定要注意路径的问题。下载后数据集必须放在E盘根目录下,否则的话则需要重新制作路径文件(at.txt),不过这一步也并不复杂,参见一种批量读取文件的方法—CSV文件

  同时,这里在向路径文件后边添加类别标号时,当初我采用的是手动添加的方式,不过我相信大家能够找到更为简便的添加方式。

  这里之所以没有介绍数据集的制作,是因为我计划将这部分内容作为程序的一个附加功能来单独进行介绍(也就是所谓的“人脸批量分割”),在之后进入到MFC编程部分时会进行专门的介绍。

  4、关于性别识别的其他方法

  在接下来的博文中我会介绍性别识别中的另外一种基础方法——SVM方法。

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

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

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

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

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

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

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

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

C++开发人脸性别识别教程(6)——通过SVM实现性别识别

上一篇教程中我们介绍了怎样使用OpenCv封装的FaceRecognizer类实现简单的人脸性别识别,这里我们为大家提供第二种主要的性别识别手段--支持向量机(SVM). 支持向量机在解决二分类问题方面有着强大的威力(当然也能够解决多分类问题).性别识别是典型的二分类模式识别问题,因此非常适合用SVM进行处理,同一时候OpenCv又对SVM进行了非常好的封装,调用非常方便,因此我们在这个性别识别程序中考虑增加SVM方法. 在这里我们採用了HOG+SVM的模式来进行,即先提取图像的HOG特征.然后

(转)C++开发人脸性别识别教程(6)——通过SVM实现性别识别

原文地址:http://blog.csdn.net/u013088062/article/details/50480518 上一篇教程中我们介绍了如何使用OpenCv封装的FaceRecognizer类实现简单的人脸性别识别,这里我们为大家提供另外一种基本的性别识别手段——支持向量机(SVM). 支持向量机在解决二分类问题方面有着强大的威力(当然也可以解决多分类问题),性别识别是典型的二分类模式识别问题,因此很适合用SVM进行处理,同时OpenCv又对SVM进行了很好的封装,调用非常方便,因此我

C++开发人脸性别识别总结

历时一个月,终于在昨天把<C++开发人脸性别识别总结>系列博客完成了,第一篇博客发表在2015年12月29日,截止昨天2016年2月29日最后一篇完成,去除中间一个月的寒假,正好一个月,首先这里把这系列博客的地址呈上:C++开发人脸性别识别教程. 在发牢骚之前首先强调几个重要问题: (1)在程序中存在一个小的易触发的BUG,就是”初始化“按钮对应的事件触发函数OnBnClickedButtonInitial()中有一句这样的代码: 在实际编程过程中经常需要对代码进行规范化,如果你习惯通过“ct

PHP开发工具PHP基础教程

    PHP开发 工具PHP基础教程,以下是兄弟连PHP培训小编整理: PHP IDE PHP IDE也不少,主要从几个方面进行筛选: 跨平台(能够同时在windows,mac或者ubuntu上面运行) 版本控制(SVN,GIT) 文件历史(自动记录历史改动) 多语言支持(至少支持html+css+javascript的智能提示和代码补全,支持Zencode),当然现在的IDE基本都可以通过安装扩展来实现 方便的TODO LIST,系统命令行 远程文件同步 方便调试 界面好看...... 好了

使用IntelliJ IDEA 13搭建Android集成开发环境(图文教程)

原文:使用IntelliJ IDEA 13搭建Android集成开发环境(图文教程) ?[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4013535.html 联系方式:[email protected] [正文] [开发环境] 物理机版本:Win 7旗舰版(32位) Java SDK版本:jdk1.8.0_20(32位) Android SDK

【0002(基础)】Skyline二次开发入门经典系列教程&mdash;&mdash;目录

这段时间由于个人事务太多,以至于没有按照时间更新系列教程,本人将尽快补上所欠章节,在此说声抱歉!   这一章在我心目中占有很重要的地位,因为我想通过这一章的内容来确定整个系列的大致走向.然而在整理的过程中,发现现实远比想象要复杂得多:首先想尽可能介绍较多的知识点,其次又要考虑学习的简易程度,再次又要确定顺当的知识体系流程-- 由于暂时的考虑无论如何也是不可能完备的,因此为了不影响后面教程的编写,暂定把这一章作为一个[流动性质]的可变章节,在编写每一章时,动态根据实际情况修改(添加.移动.修改)系