深度学习之人脸识别

本内容主要介绍深度学习的人脸识别应用。在深度学习应用中各位需要平衡人脸识别准确率与检测帧率,本文会涉及 OpenCV 现有人脸检测、识别等模型及其算法以及深度学习在人脸相似度检测等方面进行介绍。

  • OpenCV人脸检测理论
  • 人脸检测分类器

OpenCV 采用 Adaboost 级联分类器进行人脸检测与识别等,其支持 Haar、LBP 以及 HOG 等特征接口类型。鉴于篇幅原因,本文对人脸检测领域具有里程碑意义的 Haar 特征进行介绍,后者支持人脸检测、微笑、眼睛与嘴巴检测等,通过加载这些预先训练的 Haar 模型数据可以实现相关的对象检测;尤其是对正面人脸的检测,其拥有  6 种类型,如图 1.1 所示。对比上述类型中 alt、alt2、alt_tree、default 等,alt 和 alt2 的效果比较好,尤其是前者的检测效果更好,而 alt_tree 的时效性较差,同时轻量级 default效果一般,还经常出现误检测。



opencv人脸识别模型区别 opencv人脸识别算法准确率_深度学习 颜色识别

图 1.1 Haar 特征类型

作为匹配 Haar 特征检测人脸的 API 函数, detectMultiScale 函数可以在上述各种分类器基础上继续优化,其函数介绍如图 1.2 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_3d人脸识别算法opencv_02

图 1.2 detectMultiScale 函数介绍

  • 人脸识别模型

OpenCV 使用 FaceRecognizer 类实现人脸的识别功能。其功能的实现由训练、预测等两部分组成,分别对应着train 函数和 predict 函数,前者的两个参数也很简单,训练的图像组 vector 和对应的标签组 vector,这个 label 标签只需保证同一个人的标签相同即可,不需要保证图像的按标签顺序输入;后期有两种调用,其中的参数有测试图像、返回的标签值和测试样本和标签样本的相似性。返回的标签值为 -1,说明测试样本在训练集中无对应或距离较远。另外,还有对应的数据加载保存函数 save 和 load。

目前 OpenCV 支持的算法如下:

1)Eigenface特征脸                     EigenFaceRecognizer::create()

2)Fisherface                           FisherFaceRecognizer::create()

3)LBP局部二值直方图      LBPHFaceRecognizer::create()

对上述各种算法介绍如下:

EigenFace 主要是使用 PCA (主成分分析),通过消除数据中的相关性,将训练数据集中的高维图像降低到低维空间,然后计算离测试图片最近样本点是哪一个,将最近样本点的结果赋值给测试图片。其详细步骤分别是获取面部图像数据集、对齐和调整图像大小、创建数据矩阵、计算平均向量[可选]、计算主成分、重塑特征向量以获得 EigenFaces 等。

Fisherface 是基于提高识别效率,对人脸图像的特征向量进行降维和寻求更有利于分类的向量,其具体方法是主成分分析( PCA )与 Fisher 线性判别分析( FLD Fisher Linear Discriminant Analysis )相结合的算法,算法首先对高维特征样本进行 PCA 降维,投影到低维特征空间,再采用 LDA 方法得到最优判别向量。

LBP指局部二值模式,英文全称:Local Binary Pattern,是一种用来描述图像局部特征的算子,LBP特征具有灰度不变性和旋转不变性等显著优点。由于LBP特征计算简单、效果较好,因此LBP特征在计算机视觉的许多领域都得到了广泛的应用,LBP特征比较出名的应用是用在人脸识别和目标检测中,LBP提取局部特征作为判别依据,LBP方法显著的优点是对光照不敏感,但是依然没有解决姿态和表情的问题。不过相比于特征脸方法,LBP的识别率已经有了很大的提升。LBP 原理如下:

1)对预处理后的人脸图像进行分块

2)对分块后的各小块图像区域进行LBP特征提取变换

3)使用 LBP 直方图向量作为人脸特征的描述。

一般分块数越多,人脸表达的效果就会越好,但是分块数越多,会直接导致特征向量维数的增加,会增加计算的复杂度。对每个分块计算LBP值的直方图,然后将所有分块直方图进行连接得到最终的直方图特征向量,这个特征向量代表原来的人脸图像,可以用来描述整体图像。

补充特征脸的训练与识别流程图如图 1.3 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_opencv人脸识别模型区别_03

图 1.3 特征脸训练与识别

  • 人脸识别常规步骤

人脸识别的常规步骤主要包括数据收集与预处理、训练模型以及人脸识别等三个部分。

1)数据收集与预处理 前者涉及人脸数据的下载或准备识别人脸图片数据集的收集、整理;后者涉及检测、分割出人脸,并改变人脸的大小,主要涉及加载人脸分类器、调用人脸检测函数等。

2)训练模型 主要涉及人脸数据集的处理:人脸模型的训练需要读取人脸及其对应的标签 Label —— 此时可以借助 CSV 文件,其保护人脸数据的路径及其标签值,可以实现高效读取。

3)人脸识别 主要涉及OpenCV 的 Facerecognizer 类。OpenCV中所有的人脸识别模型都是来源于这个类,这个类为所有人脸识别算法提供了一种通用的接口,其后就是使用 OpenCV 自带了三个人脸识别算法 —— Eigenfaces(特征脸)、Fisherfaces、LBP局部二进制模式直方图等的使用。

  • 人脸相似度检测

人脸相似度检测,其主要内容是对比任意两张图片中人脸的相似度,并返回相似度分值。通常处理方法是

采用欧氏距离和余弦距离来衡量人脸特征的相似度,判别是否为同一个人。

人脸相似度检测一方面可以用于计算机视觉中的检测跟踪中目标位置的获取,根据已有模板在图像中找到一个与之最接近的区域。然后一直跟着;另一方面是基于图像内容的图像检索,也就是通常说的以图检图。

  • OpenCV 人脸识别实操

本文将借助OpenCV-4.0.1 实现人脸检测与识别,并结合前面章节所提及的人脸分类器、人脸识别多种算法进行实际应用进行介绍。详细介绍如下:

2.1 数据准备与预处理

此处以四个人为处理对象,其标签 Label 分别为 1、2、3、4 等,采集的图像库及其 txt 文件分别如图 2.1、2.2 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_人脸识别_04

图 2.1 采集的人脸数据库



opencv人脸识别模型区别 opencv人脸识别算法准确率_人脸识别_05

图 2.2 人脸数据库整合文件

此外,linux 环境下安装 OpenCV 请参考往期博文:深度学习之 OpenCV 安装_kalpa.Liu_2020_07_15。

2.2 人脸检测与识别模块

此处介绍人脸检测与识别模块的创建。详细代码如下:

//******************************************************************//
#include 
#include 
#include 
#include 
#include
#include "stdio.h"
#include
#include
#include
#include
#include //C语言有bai一个system函数(在头中,C++则为du头),可以用来调用终端命令
#include  
#include //vector成员函数头文件,否则没有max_element.....
using namespace cv;
using namespace cv::face;
using namespace std;
Mat frame_camera_display;//应用于整体显示
Ptr ft2;
//如果出现运行错误,检查image路径和描述是否正确,分类器加载是否正确
string haar_face_datapath = "./XML/haarcascades/haarcascade_frontalface_alt2.xml";
CascadeClassifier faceDetector;//定义级联分类器
Ptr model;
int labels_max_existing;//用于获取最大的标签数值   labels_max_existing = *maxlabels;
void face_recogenition_pre_func();
void face_recogeniton_sub_func();
int main(int argc, char** argv) {
//定义字符串保存含有样张照片的所有文件
ft2=freetype::createFreeType2();//创建 freetype 实例
ft2->loadFontData("/usr/share/fonts/truetype/arphic/uming.ttc",0);//加载字体文件
//Haar 特征分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值
faceDetector.load(haar_face_datapath);//加载已训练好的级联分类器文件
//人脸图像预处理
face_recogenition_pre_func();
//打开摄像头
VideoCapture cap(-1);
if (!cap.isOpened()) {
           printf("could not open camera...\n");
           return -1;
}
//按照正常尺寸读取
Mat frame_camera;
namedWindow("frame_camera_display", CV_WINDOW_AUTOSIZE);
while(cap.read(frame_camera))
{
           frame_camera_display = frame_camera.clone();
           //人脸识别子函数
           face_recogeniton_sub_func();
           #if 0
           //未来多任务处理途径
           thread face_recoginition_thread_process(face_recogeniton_sub_func);
           face_recoginition_thread_process.join();
           #endif
           imshow("frame_camera_display", frame_camera_display);
           if(waitKey(50) == 27)
                    break;
}
return 0;
}
//人脸识别子函数区域
void face_recogeniton_sub_func(){
//camera 图像检测、分割人脸模块
Mat frame_copy = frame_camera_display.clone();
vector face_reco_rect;//faces 属于 Rect 类数组
faceDetector.detectMultiScale(frame_copy, face_reco_rect, 1.1, 2, 0, Size(50, 50), Size(500, 500));
//检测所有识别的人脸对象
Mat dst;
for (int i = 0; i < face_reco_rect.size(); i++) {
           Mat roi = frame_copy(face_reco_rect[i]);
           //设置灰度图,防止颜色不一致
           cvtColor(roi, dst, COLOR_BGR2GRAY);
           //设置大小,与窗口一致
           resize(dst, dst, Size(100, 100));
           //保存识别的结果
           int label = model->predict(dst);//比对新传入的图像看能否找到对应标签
           //识别到使用红色边框括起来
           rectangle(frame_camera_display, face_reco_rect[i].tl(), face_reco_rect[i].br(), Scalar(255, 0, 0), 2, 8, 0);//faces[i].tl(), faces[i].br()
           String text;
           if(label == 1)
                    text = "唐国强";
           else if(label == 2)
                    text = "黄勃";
           else if(label == 3)
                    text = "关晓彤";
           else if(label == 4)
                    text = "kalpa";
           else
                    text = "不熟";
           ft2->putText(frame_camera_display, text, face_reco_rect[i].tl(), 30, Scalar(0,255,0), -1, 8, false );//函数绘制文本。40/*size*/
}
return;
}
//采集、处理已有人脸图片及其标签
void face_recogenition_pre_func(){
string filename = string("./myimage.txt");
ifstream file(filename.c_str(), ifstream::in);//以输入方式从硬盘打开或读取文件到内存
if (!file) {
           printf("could not load file correctly...\n");
           return;
}
//读取信息并保存
string line, path, classlabel;//宣告三个字串变量
vector images;//创建保存 Mat 类型的容器中 vector 声明
vector labels;//创建保存 int 类型的容器中 vector 声明
char separator = ';';
while (getline(file, line)) {
           stringstream liness(line);
           getline(liness, path, separator);//从输入流liness中读取数据, 并把它们转换成字符串path,每行截止分隔符为separator
           //测试
           printf("path : %s\n", path.c_str());//path.c_str() 返回一个指向正规C字符串的指针常量, 内容与本string串相同
           getline(liness, classlabel);
           //人脸图片及对应标签集中处理
           if (!path.empty() && !classlabel.empty()) {
                    images.push_back(imread(path, 0));
                    labels.push_back(atoi(classlabel.c_str()));
           }
}
#if 0
//查看处理结果,验证正确性
auto maxlabels = max_element(labels.begin(), labels.end());
    //cout << "labels_max_existing is " << *maxlabels << endl;
labels_max_existing = *maxlabels;
//cout << "labels_existing is " << labels_existing << endl;
#endif
if (images.size() < 1 || labels.size() < 1) {
           printf("invalid image path...\n");
           return;
}
//模型训练好之后直接预测
Mat testSample = images[images.size() - 1];
int testLabel = labels[labels.size() - 1];
images.pop_back();
labels.pop_back();
//使用三种不同算法获取特征脸
model = EigenFaceRecognizer::create();
//model = FisherFaceRecognizer::create();
//model = LBPHFaceRecognizer::create();
//模型训练与结果保存
    model->train(images, labels);//提取 model 中的成员 train
model->save("./model/FaceRecognizer_Model.xml");
//模型预测与实际结果对比验证
int predictedLabel = model->predict(testSample);//返回识别到的标签
printf("actual label : %d, predict label :  %d\n", testLabel, predictedLabel);//验证标签
return;      
}
//******************************************************************//

2.3 后期执行与效果展示

本次所需OpenCV 配套文件体系、CMakeLists.txt修改的内容、编译的执行情况分别如图 2.3、2.4、2.5 所示。最后,编译后生成 模型文件 FaceRecognizer_Model.xml(如图 2.6 所示)以及可执行文件 opencv_facerecognize,其执行效果如图 2.7 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_3d人脸识别算法opencv_06

图 2.3 OpenCV 配套文件



opencv人脸识别模型区别 opencv人脸识别算法准确率_人脸识别_07

图 2.4 CMakeLists.txt



opencv人脸识别模型区别 opencv人脸识别算法准确率_3d人脸识别算法opencv_08

图 2.5 OpenCV 编译执行情况



opencv人脸识别模型区别 opencv人脸识别算法准确率_3d人脸识别算法opencv_09

图 2.6 生成的模型文件



opencv人脸识别模型区别 opencv人脸识别算法准确率_3d人脸识别算法opencv_09

图 2.7 可执行文件执行效果

NXP 人脸识别 demo

 NXP 人脸识别 demo —— Face Recognition using TensorFlow Lite 采用 TensorFlow Lite 深度学习框架实现人脸识别 —— 严格来说属于人脸相似度对比识别。本文将以 i.MX 8 M 系列应用处理器平台运行上述 demo 的运行情况对其进行简单介绍,后续会在此基础上实现多人相似度对比。

3.1 前期准备

前期准备工作主要包括 demo 文件下载、交叉编译工具链文件的安装与使用介绍以及硬件环境的搭建。前者需要从

https://source.codeaurora.org/external/imxsupport/eiq_sample_apps/tree/examples-tflite 下载所需文件,如图 3.1 所示;第二项需要下载,并放置于 linux 根目录 /opt,并使用 source 命令使其立即生效,以便执行交叉编译命令,如图 3.2 所示;最后,基于 i.MX 8 M 系列应用处理器 i.MX 8 M - EVK 搭建的硬件平台如图 3.3 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_3d人脸识别算法opencv_11

图 3.1 demo 下载结果



opencv人脸识别模型区别 opencv人脸识别算法准确率_深度学习 颜色识别_12

图 3.2 交叉编译工具链



opencv人脸识别模型区别 opencv人脸识别算法准确率_深度学习 颜色识别_13

图 3.3 NXP 人脸识别 demo 所需硬件展示

3.2 执行编译与效果展示

执行编译 demo 及其生成的可执行文件 FaceRecognition 如图 3.4 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_opencv人脸识别模型区别_14

图 3.4 demo 执行编译并生成可执行文件

运行可执行文件执行命令:“./FaceRecognition -c 0 -h 0.85”。

1)、其中 0 代表 camera 代号,0.85 代表 识别人脸识别的阈值。

2)、添加新人脸的步骤如下:

Detect face

Input new person's name

Click 'Add new person'

执行效果分别如图 3.5、3.6 所示。



opencv人脸识别模型区别 opencv人脸识别算法准确率_opencv人脸识别模型区别_15

图 3.5 相似度对比执行情况



opencv人脸识别模型区别 opencv人脸识别算法准确率_深度学习 颜色识别_16

图 3.6 添加新人脸执行效果

目前此 demo 仅能实现单人检测与识别,后期将安排多人检测、多人识别等工作,敬请期待

总结

本文介绍了人脸检测与识别的理论知识;其次,借助 OpenCV 实现了指定人脸图像的采集、训练与预测,并生成了对应的识别模型与可执行文件,后者即可作为人脸检测与识别的最终文件。最后,本人介绍了 NXP 基于 TensorFlow Lite 框架下的人脸识别 demo 执行效果,后期将对其多人检测、识别作进一步处理,敬请期待。此方案涉及到的所有文件,可免费提供。如对此方案有更多需求,敬请联系世平集团 ATU 部门,atu.cn@wpi-group.com。