人脸识别可能需要分为几十甚至上百个类(因为有几十甚至上百个人),而性别识别则是一种特殊的人脸识别——只有两个类。
1、基本工具
通过OpenCv进行性别识别的基本工具是FaceRecognizer。这是OpenCv2.x版本中的一个基本的人脸识别类,它封装了三种基本但也是经典的人脸识别算法:基于PCA变换的人脸识别(EigenFaceRecognizer)、基于Fisher变换的人脸识别(FisherFaceRecognizer)、基于局部二值模式的人脸识别(LBPHFaceRecognizer)。这些算法差不多都是十年以前的人脸识别方法了,因此在今天看来正确率应该不会太让人满意,不过我们这里重在实践,而非算法研究(虽然本人就是搞图像识别算法研究的),因此我们不会在算法创新方面下太多功夫,所以选择了这三个基本的识别算法。
这里我们直接使用FaceRecognizer类的相关操作方法,对于其基本用法就不再赘述。
2、数据集准备
进行性别识别理所应当需要先准备一些性别识别方面的训练样本,需要强调的一点是,数据集的准备过程中也需要一些小的技巧,在之后我会专门写一篇博文来解释如何制作一个简易的性别识别训练集,这里直接用已经做好的训练集,性别识别训练集是取自中科院的人脸数据库CAS-PEAL的光照子集,包含400张男性人脸图片和400张女性人脸图片,剩余人脸图片作为测试样本。训练集包含三部分:男性样本、女性样本、测试样本。
3、识别算法的训练与测试
(1)新建一个控制台工程,配置OpenCv
(2)编写批量读取文件函数read_csv()
首先,批量txt文件是典型的io操作,需要包含以下头文件:
#include <iostream>
#include <sstream>
#include <fstream>
- 1
- 2
- 3
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())); //读取训练样本标号
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
read_csv()函数的主要功能就是读取指定目录下的路径文件(例如这里的at.txt),然后根据路径文件中的记录,逐行读入对应路径的训练样本路径及其标号,并放入对应容器(vector)中。至于为什么采用vector数据结构来存储训练样本,一是因为这样做简单直观,二是因为OpenCv的训练函数提供的是vector接口。当然这样做也存在一定弊端,就是必须一次性将训练样本全部读入到内存中,当训练样本数量庞大时这种方法不但会消耗掉巨额内存,而且效率低下。
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4、训练分类器
OpenCv中的FaceRecognizer类提供的分类器训练API函数非常简单,只需三句话,以EigenFaceRecognizer为例:
Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
modelPCA->train(images,labels);
modelPCA->save("E:\\性别识别数据库—CAS-PEAL\\PCA_Model.xml");
- 1
- 2
- 3
同理,训练FisherFaceRecognizer、LBPHFaceRecognizer两个分类器并保存:
tr<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");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
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");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
然后读入一张测试样本,通过三个分类器对其进行预测:
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);