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

2024-08-28 18:18

本文主要是介绍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方法。

 

这篇关于C++开发人脸性别识别教程(5)——通过FaceRecognizer类实现性别识别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1115523

相关文章

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

windos server2022的配置故障转移服务的图文教程

《windosserver2022的配置故障转移服务的图文教程》本文主要介绍了windosserver2022的配置故障转移服务的图文教程,以确保服务和应用程序的连续性和可用性,文中通过图文介绍的非... 目录准备环境:步骤故障转移群集是 Windows Server 2022 中提供的一种功能,用于在多个

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa