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

  事实上性别识别和人脸识别本质上是相似的,由于这里仅仅是一个简单的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、新建一个控制台project。配置OpenCv

  这里不再赘述。建议加上预编译头就可以。这里project名暂定为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函数很easy。仅仅需三句话,以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控制台project下的简洁代码,同一时候,有下面几个方面须要特别注意一下。

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

  在这篇博客的開始部分曾提到过性别识别和人脸识别的关系。在这里须要再次强调一下。性别识别本质上属于人脸识别。可是和人脸识别还是有非常多方面的差别。

性别识别是二分类问题,人脸识别是多分类问题,二者在算法上也有非常大差异。我们这里之所以简单的将性别识别看做简化的人脸识别。是由于在这套教程中我们主要注重实践,注重OpenCv的使用以及MFC框架编程方法。因此在算法方面会显得不够严谨。因此希望大家不要被这些简化的观点所误导。真正的性别识别算法也远比这些复杂,也和人脸识别方法大不同样,作为图像处理的行内人。我认为非常有必要把这点说清楚。

  2、read_csv函数

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

  3、数据集原始路径问题

  这篇博文中并没有具体介绍怎样制作性别识别训练数据集。因此大家在使用网上下载的数据集时一定要注意路径的问题。

下载后数据集必须放在E盘根文件夹下,否则的话则须要又一次制作路径文件(at.txt)。只是这一步也并不复杂,參见一种批量读取文件的方法—CSV文件

  同一时候,这里在向路径文件后边加入类别标号时,当初我採用的是手动加入的方式,只是我相信大家可以找到更为简便的加入方式。

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

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

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

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

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

    经过之前几篇博客的解说,我们已经成功搭建了MFC应用框架,并实现了主要的图像显示和人脸检測程序,在这篇博文中我们要向当中加入性别识别代码. 关于性别识别,之前已经专门拿出两篇博客的篇幅来进行解说.这里 ...

  2. C++开发人脸性别识别教程(19)——界面美化

    在这篇博文中将完毕<C++开发人脸性别识别>的收尾工作.主要内容分为两部分:加入视频暂定功能.界面规范化. 一 视频暂停功能 严格来说这个视频暂定功能算是视频人脸性别识别的一个遗留问题,本 ...

  3. C++开发人脸性别识别教程(10)——加入图片的人脸检測程序

    现在我们的MFC框架已经初具规模,能够读取并显示目录下的图片.在这篇博文中我们将向当中加入人脸检測的程序. 一.人脸检測算法 这里我们使用OpenCv封装的Adaboost方法来进行人脸检測,參见:C ...

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

    历时一个月,最终在昨天把<C++开发人脸性别识别总结>系列博客完毕了,第一篇博客发表在2015年12月29日,截止昨天2016年2月29日最后一篇完毕,去除中间一个月的寒假,正好一个月,首 ...

  5. Nodejs开发人脸识别系统-教你实现高大上的人工智能

    Nodejs开发人脸识别系统-教你实现高大上的人工智能   一.缘起缘生 前段时间有个H5很火,上传个头像就可以显示自己穿军装的样子,无意中看到了一篇帖子叫 全民刷军装背后的AI技术及简单实现 ,里面 ...

  6. 使用Android Studio搭建Android集成开发环境(图文教程)

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  7. AutoCAD ObjectARX(VC)开发基础与实例教程2014版光盘镜像

    AutoCAD ObjectARX(VC)开发基础与实例教程2014,最新版,光盘镜像 作者:张帆 朱文俊 编著 出版社:中国电力出版社 出版时间:2014年6月 点击一下

  8. LayaAir引擎开发HTML5最简单教程(面向JS开发者)

    LayaAir引擎开发HTML5最简单教程(面向JS开发者) 一.总结 一句话总结:开发游戏还是得用游戏引擎来开发,其实很简单啦 切记:开发游戏还是得用游戏引擎来开发,其实很简单,引擎很多东西都帮你做 ...

  9. QT开发环境安装配置教程

    QT开发环境安装配置教程 分类: QT2012-11-29 23:31 35366人阅读 评论(12) 收藏 举报 Linux版的直接在ubutnu软件中心输入QT,安装响应的Designer,Cre ...

随机推荐

  1. [CSS] Build a Fluid Loading Animation in CSS

    In this lesson, we will create a fluid loading animation using Animations and Transformations in CSS ...

  2. pycharm快捷键、经常使用设置、配置管理

    http://blog.csdn.net/pipisorry/article/details/39909057 本博客一直在同步更新中! 内容包括:pycharm学习技巧 Learning tips. ...

  3. 安卓手机运行WINDOWS

    http://www.pcdiy.com.tw/detail/1974 我的ZenFone 2手机可以跑Windows啦! 就在台风来袭,有人正准备去泛舟的那天,国外的XDA论坛神人则是选择让自己的Z ...

  4. HTML5 API 是什么

    HTML5 API 是什么 一.总结 1.html5有很多好的api可以用:例如绘图的canvas,获取地理位置的,获取手机电池信息的等等,后面用的时候可以百度 2.html5 API是什么:html ...

  5. css基础属性

    color:设置文本颜色:属性值:1.表示颜色的英文单词,例如:red.blue.green.pink.purple.cyan等:2.十六进制表示法:#ff0000: 0.1.2...9.a.b.c. ...

  6. 一个简单http请求的jmeter压测实战流程

    1.新建线程组 2.创建http请求 注意:接口路径中的参数值要写变量 3.创建txt文件,存多个参数值 4.创建csv文件,在csv中上传txt文件 5.variable name填写txt中参数值 ...

  7. 新版本的AutoCAD2018 怎样删除 A360 Drive盘符

    通常的做法,如下: (1)点击开始菜单的“运行”(Win+R或者Win+X快捷选择运行),在弹出的对话框输入“regedit”,回车,进入注册表编辑器. (2)找到HKEY_LOCAL_MACHINE ...

  8. 把java程序打包成.exe

    准备工作:将可执行的jar包跟资源跟第三方包都放到一个目录下. 能够将jre包也放入里面.这样在没有安装jre的情况下也能够执行. watermark/2/text/aHR0cDovL2Jsb2cuY ...

  9. t_user is not mapped [from t_user as u where u.loginname = :loginname and u.password =:password]

    转自:https://blog.csdn.net/u010876380/article/details/52714539 错误: Struts Problem Report Struts has de ...

  10. liunx基本操作常用命令

    liunx通常用作服务器,运行服务器软件,服务器要等待,类似超市学关键命令操作 内核,外壳 shell命令跟内核打交道用的是发行版本,不是内核,Radhat公司的CentOS,阿里巴巴也用这个 liu ...