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

相关文章

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.