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 Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�