前言

1.前面已经演示过使用OpenCV官方分类器实现人脸检测并拍照下要用来训练人脸识别的样本数据,并生成包含有人脸样本的列表文件(.txt)文件。
2.我的编程环境是Windows 7 64位,IDE是VS2015,配置了OpenCV3.3与OpenCV_Contrib,Boost 1.66,其中Boost是用来操作文件和目录用的,是于如果配置以上的环境,可以看我之前写的博文。
3.OpenCV 自带了三个人脸识别算法:Eigenfaces,Fisherfaces 、LBPH(和局部二值模式直方图)。如果想知道它们的工作原理及相互之间的区别,请阅读 OpenCV 的官方文档。
4.我在这里只演示如何使用Eigenfaces来实现人脸识别的训练与测试,如果想测试Fisherfaces和LBPH这两个识别算法,只要把声明与初始化类的部分改成对应的算法就可以了。

一、资源准备

1、要训练之前,应该有两个数据,一个放着人脸样本的文件夹,可以从这里下载我上传好的文件夹。一个是内容里面有人脸样本绝对路径加标签的txt文件,人脸样本的文件夹如下图,41这个文件夹是放着我从摄像头拍下来自己的脸,42这个文件夹是我从网上下载下来的芦田爱菜(Ashida Mana)的人脸图像,因为她的表情真的很丰富,差异相对大一些。
存放训练集的文件夹:

2.然后调用buildList()这个函数,生成内容里有人脸样本绝对路径加标签的txt文件,格式如下图,前是样本数据的绝对路径,分号之后该样本的标签名,分号是用来隔开样本与标签用的,只是方便后面的文件读取。

二、训练代码

这里我给两个方式的读写文件方式的训练代码,一个是从txt文件里读取样本路径和标签代码,一个是使用boost把样本读到到C++ STL容器的训练代码。
1.从txt文件读取样本和标签的代码,使用这个函数的前提是必须在前面生成内容里有人脸样本绝对路径加标签的txt文件,函数的第第一个参数传入txt文件的路径,第二参数是保存模型文件的路径:
trainFacesTxt()函数代码:

void trainFacesTxt(string faces_list, string save_model)
{//打开人脸列表文件ifstream face_file(faces_list, ifstream::in);if(!face_file){std::cout << "无法打开训练集的列表文件!" << endl;return;}string line, path, class_label;vector<Mat> faces;vector<int> labels;char separator = ';';while (getline(face_file, line)){stringstream liness(line);//读一行getline(liness, path, separator);getline(liness, class_label);if (!path.empty() && !class_label.empty()){//把图像压入容器faces.push_back(imread(path, 0));//把标签压入容器labels.push_back(atoi(class_label.c_str()));}}//判断是否为空if (faces.size() < 1 || labels.size() < 1){std::cout << "初始化训练集....." << std::endl;return;}int height = faces[0].rows;int width = faces[0].cols;std::cout << "训练集的图像的高:" << height << "训练集的图像的宽:" << width << std::endl;Mat test_sample = faces[faces.size() - 1];int test_label = labels[labels.size() - 1];faces.pop_back();labels.pop_back();//判断图像类型for (size_t i = 0; i < faces.size(); i++){if (faces.at(i).type() != CV_8UC1){std::cerr << "图像的类型必须为CV_8UC1!" << endl;return;}}//检测尺寸等于正样本尺寸第一张的尺寸Size positive_image_size = faces[0].size();cout << "正样本的尺寸是:" << positive_image_size << endl;//遍历所有样品,检测尺寸是否相同for (size_t i = 0; i < faces.size(); i++){if (positive_image_size != faces[i].size()){std::cerr << "所有的样本的尺寸大小不一,请重新调整好样本大小!" << endl;return;}}//创建一个人脸识别的类Ptr<BasicFaceRecognizer> model = EigenFaceRecognizer::create();//开始训练model->train(faces, labels);// recognition faceint predicted_label = model->predict(test_sample);std::cout << "样本标签类型为:" << test_label << "预测的样本标签为:" << predicted_label << endl;//保存训练好的模型model->write(save_model);std::cout << "训练完成!" << endl;
}

2.直接用boost库从存放路径读取,这里就不需要提前生成txt,函数的第一个参数是存放人脸样本的目录,第二个是是保存模型的路径和模型的名字。
trainFacesDir()函数代码:

void trainFacesDir(string faces_list,string save_model)
{vector<int> labels;vector<Mat> faces;multimap<int,Mat> faces_labels;int index = 0;if (!fs::is_directory(faces_list)){std::cerr << "请输入一个合法的路径!" << endl;return;}else if (fs::is_empty(faces_list)){std::cerr << "输入路径为空路径!" << endl;return;}//递归遍历当前目录下的子文件fs::recursive_directory_iterator begin_iter(faces_list);fs::recursive_directory_iterator end_iter;for (; begin_iter != end_iter; begin_iter++){string file_path = begin_iter->path().string();fs::path file_name(file_path);//判断是否为目录if (fs::is_directory(file_name)){//标签index++;}else{// 读取文件夹下的文件level 1表示这是一副训练图,通过multimap容器来建立由类目名称到训练图的一对多的映射string filename = begin_iter->path().string();//图像的类型为CV_8CU1Mat temp = imread(filename,0);if (temp.empty()){continue;}pair<int, Mat> p(index, temp);//键名可以重复faces_labels.insert(p);}}multimap<int, Mat> ::iterator i = faces_labels.begin();//得到要训练的数据集与标签for (; i != faces_labels.end(); i++){labels.push_back((*i).first);faces.push_back((*i).second);}//从最后一个人脸样品集读出一张图像用来测试Mat test_sample =faces[faces.size() - 1];//标签等于最后人脸样品标签int test_label = labels[labels.size() - 1];//从容器里删掉最后一张faces.pop_back();//标签减1labels.pop_back();//判断图像类型for (size_t i = 0; i < faces.size(); i++){if (faces.at(i).type() != CV_8UC1){std::cerr << "图像的类型必须为CV_8UC1!" << endl;return;}}//检测尺寸等于正样本尺寸第一张的尺寸Size positive_image_size = faces[0].size();cout << "正样本的尺寸是:" << positive_image_size << endl;//遍历所有样品,检测尺寸是否相同for (size_t i = 0; i < faces.size(); i++){if (positive_image_size != faces[i].size()){std::cerr << "所有的样本的尺寸大小不一,请重新调整好样本大小!" << endl;return;}}//创建一个人脸识别的类Ptr<BasicFaceRecognizer> model = EigenFaceRecognizer::create();//开始训练model->train(faces, labels);// recognition faceint predicted_label = model->predict(test_sample);std::cout << "样本标签类型为:" << test_label << "预测的样本标签为:" << predicted_label << endl;//保存训练好的模型model->write(save_model);std::cout << "训练完成!" << endl;
}

3.运行上面的函数,在传入的路径下生成一个xml的模型文件,我这里命名为face_model.xml文件,测试时调用这个文件。

三、测试代码

测试代码我也写两个函数,一个是从摄像头读取,然后识别当前人脸。
1、从摄像头读取图像并识别,函数的第一个函数是传入的摄像头索引号,第二个是训练好人脸模型路径文件名。
testFace()函数代码:

void testFace(int cap_index,string model_path)
{//加载一个人脸识别器Ptr<BasicFaceRecognizer> model_test = EigenFaceRecognizer::create();//opencv3.3要用read,要不然会出错model_test->read(model_path);//加载一个人脸检测分类器CascadeClassifier faceDetector;faceDetector.load(face_path);//检测传入的摄像头VideoCapture capture(cap_index);if (!capture.isOpened()){std::cerr << "无法打开当前摄像头!" << endl;return;}Mat frame;namedWindow("faceRecognition", CV_WINDOW_AUTOSIZE);vector<Rect> faces;Mat dst;Mat test_sample;string name;while (capture.read(frame)){//镜像flip(frame, frame, 1);//检测人脸faceDetector.detectMultiScale(frame, faces, 1.1, 1, 0, Size(80, 100), Size(380, 400));for (int i = 0; i < faces.size(); i++){Mat roi = frame(faces[i]);cvtColor(roi, dst, COLOR_BGR2GRAY);resize(dst, test_sample, Size(92, 112));int label = 0;label = model_test->predict(test_sample);//输出检测到的人脸标签cout << label << endl;//画出人脸rectangle(frame, faces[i], Scalar(255, 0, 0), 2, 8, 0);switch (label){case 41:name = "matt";break;case 42:name = "Ashida Mana";break;default:name = "Unknown";break;}putText(frame, name, faces[i].tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 2, 8);}imshow("faceRecognition", frame);char c = waitKey(10);if (c == 27){break;}}
}

运行结果:

从摄像头拍下的人脸样本标签保存为41,但有时因为人脸的摆动,会出现误识别现象。
2、从文件夹下读取图像并识别,函数的第一个函数是传入的存放包含人脸的图像,第二个是训练好人脸模型路径文件名。
testFace()函数代码:

void testFace(string image_path, string model_path)
{//加载一个人脸识别器Ptr<BasicFaceRecognizer> model_test = EigenFaceRecognizer::create();//opencv3.3要用read,要不然会出错model_test->read(model_path);//加载一个人脸检测分类器CascadeClassifier faceDetector;faceDetector.load(face_path);Mat frame;namedWindow("faceRecognition", CV_WINDOW_AUTOSIZE);vector<Rect> faces;Mat dst;Mat test_sample;string name;if (!fs::is_directory(image_path)){std::cerr << "请传入一个合法的非空的路径!" << endl;return;}fs::directory_iterator begin_iter(image_path);fs::directory_iterator end_iter;for(;begin_iter != end_iter; begin_iter ++){frame = imread(begin_iter->path().string());if (frame.empty()){continue;}//检测人脸faceDetector.detectMultiScale(frame, faces, 1.1, 1, 0, Size(80, 100), Size(380, 400));for (int i = 0; i < faces.size(); i++){Mat roi = frame(faces[i]);cvtColor(roi, dst, COLOR_BGR2GRAY);resize(dst, test_sample, Size(92, 112));int label = 0;label = model_test->predict(test_sample);//输出检测到的人脸标签cout << label << endl;//画出人脸rectangle(frame, faces[i], Scalar(255, 0, 0), 2, 8, 0);switch (label){case 41:name = "matt";break;case 42:name = "Ashida Mana";break;default:name = "Unknown";break;}putText(frame, name, faces[i].tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 2, 8);}imshow("faceRecognition", frame);waitKey(0);}
}

运行结果,识别正确的图像:

识别不到的:

结语

1.以上的所有关于人脸训练与识别的代码演示,只是一个通用的代码框架,识别的准确率并不能达到可以使用在项目上。要真正达到应用级,好多地方都要优化,如样本的提取,提取样本的摄像头的角度与识别时的使用的摄像头角度,识别时检测到人脸之后的相关矫正等等。
2.现在好多深度学习的网络可以实现出更高的准确率,比如caffe的DeepID等,之后有时间会去写相关的训练与应用。
3.关于整个工程的源码,运行程序时的bug,或者有如何优化的想法都可以加这个群(487350510)互相讨论学习。

OpenCV3实现人脸识别(三)——训练与识别自己的人脸数据相关推荐

  1. Labview文字识别-从训练到识别

    一个简单的Labview文字识别例子,包含从训练到最终的识别,比较简单,主要是熟悉labview文字识别的整个流程. 首先需要安装视觉开发模块(NI Vision Development Module ...

  2. Pytorch实现戴口罩人脸检测和戴口罩识别(含训练代码 戴口罩人脸数据集)

    Pytorch实现戴口罩人脸检测和戴口罩识别(含训练代码 戴口罩人脸数据集) 目录 Pytorch实现戴口罩人脸检测和戴口罩识别(含训练代码 戴口罩人脸数据集) 1.戴口罩识别的方法 (1)基于多类别 ...

  3. 车架号图像识别,车架号拍照识别,车架号扫描识别,车架号OCR识别

        车架号识别         车架号识别技术要求能够将运动中的汽车牌照从复杂背景中提取并识别出来,通过车架号识别提取.图像预处理.特征提取.车牌字符识别等技术,识别车辆VIN码(车架号)信息. ...

  4. opencv 训练人脸对比_【项目案例python与人脸识别】基于OpenCV开源计算机视觉库的人脸识别之python实现...

    " 本项目是一个基于OpenCV开源库使用python语言程序实现人脸检测的项目,该项目将从[项目基础知识](即人脸识别的基本原理).[项目实践](人脸识别所需要的具体步骤及其python程 ...

  5. 利用人脸微笑数据集训练识别模型,完成对人脸图片微笑识别

    目录 一.数据集下载 二.提取人脸并判别是否为笑脸 (一)数据处理 (二)测试 三.视频识别并保存图片 四.总结 五.参考链接 一.数据集下载 1.链接:https://pan.baidu.com/s ...

  6. Kaggle ICML2013 fer2013人脸表情识别/面部表情识别:训练、调优、调试与踩坑

    目录 概要: 问题来源: 论文对此比赛的说明: 选择原因: 实现与优化思路: 前置: 数据处理: 原csv数据的读取与分割: csv数据转图片和tfrecord的存取: tfrecord接生产队列供模 ...

  7. opencv 人脸识别 (二)训练和识别

    上一篇中我们对训练数据做了一些预处理,检测出人脸并保存在\pic\color\x文件夹下(x=1,2,3,...类别号),本文做训练和识别.为了识别,首先将人脸训练数据 转为灰度.对齐.归一化,再放入 ...

  8. unityar自动识别人脸_三款Unity插件带您玩转人脸识别与模拟

    原标题:三款Unity插件带您玩转人脸识别与模拟 若想让游戏或电影中的虚拟角色表现得更加真实,面部表情是至关重要的.人脸识别技术则借助真人表演来为虚拟角色赋予丰富的表情与面部动作,小至皱眉带动眼部肌肉 ...

  9. 智能驾驶 车牌检测和识别(三)《CRNN和LPRNet实现车牌识别(含车牌识别数据集和训练代码)》

    智能驾驶 车牌检测和识别(三)<CRNN和LPRNet实现车牌识别(含车牌识别数据集和训练代码)> 目录 智能驾驶 车牌检测和识别(三)<CRNN和LPRNet实现车牌识别(含车牌识 ...

最新文章

  1. CNN架构复现实战 | 附视频和github项目
  2. 广度优先搜索求解迷宫问题
  3. Administer Service Cloud
  4. 使用python+ffmpeg批量转换格式
  5. js变量和java变量相等,js中变量和jsp中java代码中变量互相访问解决方案
  6. 通过代码解决SharePoint列表视图权限分配问题
  7. 经典的 div + css 鼠标 hover 下拉菜单
  8. 给LINUX添加一个开机执行脚本
  9. gre 填空双选题目
  10. SQL 基础教程 (第2版)
  11. 单片机温度传感器c语言编码,单片机中使用DS18B20温度传感器C语言程序.doc
  12. github项目ratel:JAVA实现斗地主
  13. 分享143个ASP源码,总有一款适合您
  14. C语言中的strstr函数的用法
  15. 1.7 使用不同设备类型的iOS模拟器 [原创iOS开发-Xcode教程]
  16. Explain执行计划key_len详解
  17. Python并发之协程gevent数据结构和实践(6)
  18. T_SQL和SQL的区别
  19. xilinx 账户申请以及vivado 安装
  20. 老板,缺人吗?这里有一大波产品人才想安利给你~

热门文章

  1. Python_基础_5
  2. JAVA期末简答题参考
  3. 利用七牛云实现简单的图床
  4. google Guava包的ListenableFuture解析
  5. 面试心得与总结—BAT、网易、蘑菇街
  6. Python计算机视觉:第二章 图像局部描述符
  7. 程序员面试题精选100题(11)-求二元查找树的镜像[数据结构]
  8. WKWebView之cookie请求
  9. 第四章 对象的类型和动态绑定
  10. 10个最佳Node.js企业应用案例:从Uber到LinkedIn