人脸识别问题概述
人脸识别概述
人脸视觉特征信息进行身份鉴别的计算机技术。
广义的人脸识别实际包括构建人脸识别系统的一系列相关技术,包括人脸图像采集、
人脸定位、人脸识别 预处理、身份确认以及身份查找等;而狭义的人脸识别特指
通过人脸进行身份确认或者身份查找的技术或 系统。
于生物特征识别技术,是对生物体(一般特指人)本身
的生物特征来区分生物体个体。生物特征识别技术所研究的生物特征包括脸、指纹、手掌纹、虹膜、视网
膜、声音(语音)、体形、个人习惯(例如敲击键盘的力度和频率、签字)等,相应的识别技术就有人脸
识别、指纹识别、掌纹识别、虹膜识别、视网膜识别、语音识别(用语音识别可以进行身份识别,也可以
进行语音内容的识别,只有前者属于生物特征识别技术)、体形识别、键盘敲击识别、签字识别等。
人脸识别的技术困难
虽然人脸识别有很多其他识别无法比拟的优点,但是它本身也存在许多困难。
人脸识别被认为是生物特征 识别领域甚至人工智能领域最困难的研究课题之一。
人脸识别的困难主要是人脸作为生物特征的特点所带 来的。人脸在视觉上的特点是:
1. 不同个体之间的区别不大,所有的人脸的结构都相似,甚至人脸器官的结构外形都很相似。
这样的特 点对于利用人脸进行定位是有利的,但是对于利用人脸区分人类个体是不利的。
2. 人脸的外形很不稳定,人可以通过脸部的变化产生很多表情,而在不同观察角度,
人脸的视觉图像也 相差很大,另外,人脸识别还受光照条件(例如白天和夜晚,室内和室外等)、
人脸的很多遮盖物 (例如口罩、墨镜、头发、胡须等)、年龄、拍摄的姿态角度等多方面因素的影响。
人脸识别典型流程
人脸识别的经典流程分为三个步骤:
1 )人脸检测;
2 )人脸对齐;
3 )人脸特征表示。
基于传统机器学习的人脸识别一般分为高维人工特征提取(例如:LBP, Gabor等)和降维 两个步骤。
在深度学习流行之后,我们可以从原始图像空间直接学习判别性的人脸表示, 实现端到端的人脸识别模型。
深度学习“引爆”人脸识别
过去几年,深度学习正在彻底改变人脸识别领域。由于GPU的计算效率不断提高,
谷歌 的研究人员在 CVPR (Computer Vision and Pattern Recognition ) 2015 上发表了一篇开创性 的论文 :FaceNet。
FaceNet是一个解决人脸识别和人脸聚类问题的全新深度神经网络架 构,其在LFW ( Labeled Faces in the Wild )
人脸识别数据集上十折平均精度达到99.63%。 同时,它为使用深度学习创建下一代人脸识别系统打下了坚实基础。
人脸识别应用
典型人脸相关数据集介绍
LFW ( Labeled Face in the Wild )
2007年以来,LFW数据库成为事实上的真实条件下的人脸识别问题的测试基准。
LFW数 据集包括来源于因特网的5749人的13233张人脸图像,其中有1680人有两张或以上的 图像。
LFW的标准测试协议包括6000对人脸的十折确认任务,每折包括300对正例和300对反例,
采用十折平均精度作为性能评价指标。自从LFW发布以来,性能被不断刷新。
2013年之前,主要技术路线为人造或基于学习的局部描述子+测度学习。
2014年之后,主要技术路线为深度学习。
2014年以来,深度学习+大数据(海量的有标注人脸数据)成为人脸识别领域的主流技术路 线,
其中两个重要的趋势为:
• 网络变大变深(VGGFace16层,FaceNet22层);
• 数据量不断增大(DeepFace 400万,FaceNet2亿),大数据成为提升人脸识别性能的关键。
YouTube Faces DB
YouTube Faces是一个面部视频数据库,旨在研究视频中非约束环境下的人脸识别问题。
该数据集包含3,425个视频,1,595个不同的人。所有视频都是从YouTube下载的。每个主题平均有2.15个视频。
剪辑最短的视频为48帧,最长的为6,070帧,视频平均长度为181.3 帧.
CASIA-WebFace
CASIA-WebFace数据库包含 10k+ 人和约500K张图片,用于非约束环境下人脸识别的科学研究。
数据库中的面部图像由中国科学院(CASIA )自动化研究所从互联网上爬取。
官方允许该数据库进行教育或非商业用途的免费使用。但由于数据库中的图像可能受版 权保护,
官方没有在他们的网站上公开发布。如果是研究使用,可以在官方网站上申请 访问权限。
人脸识别数据集
FDDB: Face Detection Data Set and Benchmark
人脸检测数据集和基准测试(FDDB )是一个面部区域数据集,用于研究非约束环境下人 脸检测问题。
FDDB从LFW数据集选取了 5171张人脸和2845张图片,由马萨诸塞大学(University of Massachusetts , UMASS )维护。
http://vis-www.cs.umass.edu/fddb/index.html
WIDER FACE: A Face Detection Benchmark
WIDER FACE数据集是由汤晓鸥团队发布的人脸检测基准数据集。
其从公开数据集 WIDER中选取了32,203个图像并标记了 393,703个面部,其比例、姿势和遮挡度具有 高度可变性。
数据集共分为61个类。每一类别的训练、验证和测试集比例都是4:1:5。 基准测试的评估指标与PASCAL VQC数据集采用的相同。
http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/
Large-scale CelebFaces Attributes (CelebA) Dataset
CelebFaces Attributes Dataset (CelebA)是一个大型人脸属性数据集,
拥有超过 10,177个名人的202,599张面部图像,每张图像都有5个特征点标注和40个属性注释。
同时,此数据集中的图像还覆盖了巨大的姿势变化和杂乱背景。
http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html
人脸检测数据集
人脸检测算法介绍
人脸检测算法简介
自动人脸检测技术是所有人脸影像分析衍生应用的基础,这些扩展应用细分有人脸识别、
人脸验证、人脸跟踪、人脸属性识别,人脸行为分析、个人相册管理、机器人人机交互、 社交平台的应用等。
人脸检测的目标是找出图像中所有的人脸对应的位置,算法的输出是人脸外接矩形在图 像中的坐标,
可能还包括姿态如倾斜角度等信息。虽然人脸的结构是确定的,由眉毛、 眼睛、鼻子和嘴等部位组成,近似是一个刚体,
但由于姿态和表情的变化,不同人的外 观差异,光照,遮挡的影响,准确的检测处于各种条件下的人脸是一件相对困难的事情。
经典人脸检测算法流程
用大量的人脸和非人脸样本图像进行训练,得到一个解决二分类问题的分类器,也称为 人脸检测模板。
这个分类器接受固定大小的输入图片,判断这个输入图片是否为人脸, 即解决是和否的问题。
人脸二分类器的原理如下图所示:
人脸检测-研究进展
人脸检测算法研究分为3个发展阶段:
L基于模版匹配的算法
2. 基于 AdaBoost 的框架
3. 基于深度学习的算法
基于模板匹配的人脸检测算法
位置进行匹配,
确定这个位置处是否有人脸,即针对图像中某个区域进行人脸-非人脸二分类的判别。
早期有代表性的成果是 Rowley
脸图像训练了一个多层感知器模型。论文[1]中的方法用于解决近似正面的人脸检测问题,原理如下图所示:
[1] Henry A Rowley, Shumeet Baluja, Takeo Kanade. Neural network- based face detection. 1998, IEEE Transactions on Pattern Analysis and Machine Intelligence.
Rowley等人提出的方法[2]解决了多角度人脸检测问题,整个系统由两个神经网络构成,
第一个网络用于 估计人脸的角度,第二个用于判断是否为人脸。角度估计器输出一个旋转角度,然后用整个角度对检测窗
进行旋转,然后用第二个网络对旋转后的图像进行判断,确定是否为人脸。系统结构如下图所示:
[2] Henry A Rowley, Shumeet Baluja, Takeo Kanade. Rotation invariant neural network -based face detection. 1998, computer vision and pattern recognition.
基于AdaBoost框架的人脸检测算法
Boost算法是基于PAC ( Probably Approximately Correct)学习理论而建立的一套集成学习(Ensemble Learning )算法。
俗话说〃三个臭皮匠,顶个诸葛亮Boost的核心思想便是利用多个简单的弱分类器,构 建出高准确率的强分类器。
Viola 和 Jones
它使用简单的 Haar - like 特征和级联的 AdaBoost 分类器构造检测器,检测速度较之前的方法有2个数量级的提高,并且保持了很好的精度。
在深度学习出现以前工业界的方案都是基于VJ算法。但VJ算法仍存在一些问题:
稳定性较低;
容易过拟合。因此,该算法对于解决正面的人脸效果好,对于人脸的遮挡,
姿态,表情等特殊且复杂的情况效果一般;
3. 基于VJ- cascade的分类器设计,进入下一个stage后,之前的信息都丢弃了。分类器评价一个样本不会基于
鲁棒性差。
[3] P.Viola and M.Jones. Rapid object detection using a boosted cascade of simple features. In Proceedings IEEE Conf, on Computer Vision and Pattern Recognition, 2001
基于深度学习的人脸检测算法
CNN在图像分类问题上取得成功之后很快被用于人脸检测问题,在精度上大幅度超越之 前的AdaBoost框架。
在此之前,滑动窗口 +卷积对窗口图像进行分类的计算量巨大,无 法做到实时检测。当前,已经有一些快速、
高效的基于深度学习的算法,我们介绍重点 介绍 Cascade CNN[4]和 MTCNN[5]。
Cascade CNN可以认为是传统技术和深度网络相结合的一个代表,和VJ人脸检测器一样, 其包含了多个分类器,
这些分类器采用级联结构进行组织。不同的地方在于,Cascade CNN采用卷积网络作为每一级的分类器。
[4] Haoxiang Li, Zhe Lin, Xiaohui Shen, Jonathan Brandt, Gang Hua. A convolutional neural network cascade for face detection. 2015, computer vision and pattern recognition
[5] Kaipeng Zhan, Zhanpeng Zhang, Zhifeng L, Yu Qiao. Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks. 2016, IEEE Signal Processing Letters.
Cascade CNN
12-net
剩下窗口送入12-calibration-net调整尺寸和位置,使其更靠近潜在 的人脸图像附近。接着采用非极大值抑制(NMS )合并高度重叠的检测窗口,
保留下来的候选检测窗口将 会被归一化到24x24作为24-net的输入,进一步剔除掉剩下来的近90%的检测窗口。接着通过24-calibration-net矫正检测窗口,
并应用NMS进一步合并减少检测窗口的数量。将通过之前所有层级的检 测窗口对应的图像区域归一化到48x48送入48-net进行分类得到进一步过滤的人脸候选窗口。
然后利用 NMS进行窗口合并,送入48-calibration-net矫正检测窗口作为最后的输出。
Kaipeng Zhan, Zhanpeng Zhang, Zhifeng L, Yu Qiao. Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks. 2016, IEEE Signal Processing Letters.
MTCNN
P-Net, R-Net, O-Net
0) 首先按不同比例缩放照片,形成图片的特征金字塔作为P-Net输入。
1) P-Net主要获得了人脸区域的候选窗口和边界框的回归向量。并用 该边界框做回归,对候选窗口进行校准,然后通过NMS来合并高度重 叠的候选框。
2) 然后将候选框输入R-Net网络训练,利用边界框的回归值微调候选 窗体,再利用NMS去除重叠窗体。
3) O-Net功能与R-Net作用类似,只是在去除重叠候选窗口的同时, 显示五个人脸关键点定位。
MTCNN同Cascade CNN一样也是基于cascade的框架,但是降低了模型整体复杂度,使其更好落地到工业界应用
Kaipeng Zhan, Zhanpeng Zhang, Zhifeng L, Yu Qiao. Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks. 2016, IEEE Signal Processing Letters.
How many people here?
人脸识别算法介绍
人脸识别算法流程
人脸识别算法主要分为三个流程:
1. 人脸检测(Face Detection )
2. 人脸对齐(Face Alignment)
3. 人脸特征表征(Feature Representation )
1. 人脸检测(Face Detection )
2. 人脸对齐(Face Alignment)
3. 人脸特征表征(Feature Representation )
人脸识别-研究进展
人脸识别算法的研究分为3个发展阶段:
1. 早期算法:基于几何特征、模版匹配、子空间等
2. 人工特征+分类器
3. 基于深度学习的算法
早期算法—线性降维
在研究早期,人们使用子空间算法:将人脸图像当作一个高维向量, 将其投影到低维空间后,
期望得到的低维向量对不同的人具有区分度。 比如Matthew等使用PCA
[1]降维得到特征脸(Eigenface )、 Peter等使用LDA
[2]降维得到Fisherface来实现人脸识别。
但是,PCA和LDA都是线性降维技术,显然人脸在高维空间中的分 布是非线性的。
因此,我们可以进一步使用非线性降维算法。如:流 形[3]和核方法[4]。
[1] Matthew Turk,Alex Pentland. Eigenfaces for recognition. 1991, Journal of Cognitive Neuroscience.
[2] Eigenfaces vs. Fisherfaces: recognition using class specific linear projection. Peter N Belhumeur J P Hespanha David Kriegman. 1997 IEEE Transactions on Pattern Analysis and Machine Intelligence.
[3] He, Xiaofei, et al. Face recognition using Laplacianfaces. Pattern Analysis and Machine Intelligence, IEEE Transactions on 27.3 (2005) : 328-340.
[4] Bartlett M S , Movellan J R , Sejnowski TJ. Face Recognition by Independent Component Analysis [J]. IEEE Trans. on Neural Network , 2002 , 13(6) : 1450-1464
早期算法-非线性降维
流形学习是一种非线性降维方法,它假设向量点在高维空间中的分布具有某些几何形状,
然后在保持这些几何形状约束的前提下将向量投影到低维空间中,这种投影是通过非线性变换完成的。
如下所示,我们将3 维空间中的瑞士卷(Swiss Roll)数据集非线性降维到2维,并尽可能降低对数据分布的影响
但是,这些通过直接降维来进行人脸识别的早期算法,普遍都存在严重依赖训练集和测试集场景的问题, 且对光照、
人脸的表情、姿态敏感,泛化能力不足,不具有太多的实用价值。
人工特征+分类器
第二阶段的人脸识别算法普遍采用了 人工特征+分类割
分类模型过多年的发展,已经有比较成熟的分类器,如逻辑回归、贝叶斯、支持向量机、神经网络等。
因此,算法的关键是特征工程,即如何设计处有效区分不同人的特征。
于是,计算机视觉领域很多描述图像的特征都先后被用于人脸识别问题,包括HOG、SIFT、Gabor、LBP 等。
它们中的典型代表是简单高效的LBP (局部二值模式)特征。LBP部分解决了光照敏感问题。
联合贝叶斯[5 ]是对贝叶斯人脸的改进方法,选用LBP和LE作为基础特征,将人脸图像的差异表示为:
相同人因姿态、表情等导致的差异和不同人间的差异两个因素,用潜在变量组成的协方差,建立两张人脸的关联。
联合贝叶斯在LFW上取得了 92.4%的准确率。
[5 ] Dong Chen, Xudong Cao, Liwei Wang, Fang Wen, Jian Sun. Bayesian face revisited: a joint formulation. 2012, european conference on computer vision.
MSRA “Feature Master”
2013年,MSRA的Dong Chen [6]等继续发力,发表了一篇关于如何使用高维度特征在人脸验证中的文章,
作者主要以LBP为例,论述了高维特征和验证性能成正相关,即人脸维度越高,验证的准确度就越高。
文中最好的方法在LFW上的准确率达到了95.17%,可谓是集特征工程技艺之大成。
[6] Dong Chen,Xudong Cao,Fang Wen,Jian Sun.Blessing of Dimensionality: High-Dimensional Feature and Its Efficient Compression for Face Verification.2013, computer vision and pattern recognition.
基于深度学习的人脸识别
第三阶段是基于深度学习的算法。2012年深度学习在ILSVRC -2012取得成功后,
学术界意识到通过CNN学习得到的卷积核明显优于"人工特征+分类器〃的方案。自此开始,大家便使用CNN +海量人脸 图片来提取特征,
以此替代人工特征的设计。
前期,大家在神经网络结构和输入数据设计方面做了大量尝试。
后期,大家主要集中在人脸损失函数的改进上,即约束CNN学习对区分不同人更有效的特征。至今,仍然 不断有效果更好的损失函数设计。
而人脸识别研究领域(从人脸检测到人脸识别)也彻底被深度学习〃攻 占〃了。
我们主要介绍两个模型:Facebook在CVPR 2014发布的DeepFace [7]和Google在CVPR 2015发布 的 FaceNet [8]
[7] Yaniv Taigman, Ming Yang, Marcaurelio Ranzato, Lior Wolf. DeepFace: Closing the Gap to Human-Level Performance in Face Verification. 2014, computer vision and pattern recognition.
[8] Florian Schroff, Dmitry Kalenichenko, James Philbin. FaceNet: A unified embedding for face recognition and clustering. 2015, computer vision and pattern recognition.
Facebook DeepFace
DeepFace是深度卷积神经网络在人脸识别领域的奠基之作。他们使用3D模型来解决人脸对齐问题,
同时又使用了9层深度神经网络来做人脸特征表示。损失函数使用了 Softmax Loss,最后通过特征嵌入 (Feature Embedding ) 得到固定长度的人脸特征向量。
DeepFace在LFW上取得了97.35%的准确率,已经接近人类的水平。
Yaniv Taigman, Ming Yang, Marcaurelio Ranzato, Lior Wolf. DeepFace: Closing the Gap to Human-Level Performance in Face Verification. 2014,computer vision and pattern recognition.
Google FaceNet
次年,Google FaceNet创新的提出了使用三元组损失函数(Triplet Loss )替代Softmax Loss。
在一个超球空间上进行优化使类内距离更紧凑,类间距离更远,最后得到了一个紧凑的128维人脸特征,
其网络使用GoogLeNet的Inception模型,模型参数量较小,精度更高。 FaceNet在LFW上取得了99.63%的准确率。
Florian Schroff, Dmitry Kalenichenko, James Philbin. FaceNet: A unified embedding for face recognition and clustering. 2015, computer vision and pattern recognition.
人脸检测工具介绍
开放API-某AI开放平台
OpenCV
开源计算机视觉库(Open Source Computer Vision Library, OpenCV )遵从BSD协议许可。
它具有C ++ , Python和Java接口,支持Windows , Linux , Mac OS , iOS和Android。 OpenCV专为提高计算效率而设计,
专注于实时应用。该库以优化的C / C ++编写,通过 OpenCL可以启用多核处理和硬件加速模式。
安装 OpenCV 的 Python 接口:
C:\Users\67001>pip3 install opencv-python
使用OpenCV进行人脸检测
代码实现:
#The line below is necesary to show Matplotlib's plots inside a Jupyter Notebook
%matplotlib inline
import cv2
from matplotlib import pyplot as plt
imagePath = './face_detection/test_face_detection.jpg'
cascPath = "./face_detection/haarcascade_frontalface_default.xml"
# Create the haar cascade
faceCascade = cv2.CascadeClassifier(cascPath)
# Read the image
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Detect faces in the image
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30)
#flags = cv2.CV_HAAR_SCALE_IMAGE
)
print("Found {0} faces!".format(len(faces)))
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
plt.title("Faces found")
plt.axis("off")
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()
检测结果:
使用face_recognition库进行人脸检测
win10下安装face_recognition:
使用Dlib最先进的面部识别功能构建而成,具有深度学习功能。该模型在LFW上的准确率为99.38%。 安装 face_recognition 库:
pip3 install face_recognition
代码实现:
import face_recognition
imagePath = './face_detection/test_face_detection.jpg'
# Load the image with face_recognition
image = face_recognition.load_image_file(imagePath)
# Detect faces in the image
face_locations = face_recognition.face_locations(image)
print("Found {0} faces!".format(len(face_locations)))
# Read the image with openCV
image = cv2.imread(imagePath)
# Draw a rectangle around the faces
for (top, right, bottom, left) in face_locations:
cv2.rectangle(image, (left, top), (right, bottom), (0, 255, 0), 2)
plt.title("Faces found")
plt.axis("off")
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()
检测结果:
解析 FaceNet 人脸识别模型
FaceNet 模型介绍
FaceNet在复杂光照和姿态下表现出色
模型创新点:Triplet Loss
FaceNet 模型架构
FaceNet-NN1
FaceNet - NN2
FaceNet 计算量对结果影响
FaceNet CNN 架构对结果影响
FaceNet 图像质量对结果影响
FaceNet 特征空间维度对结果影响
FaceNet 训练数据量对结果影响
FaceNet模型的创新与突破
实战 FaceNet 人脸识别模型
OpenFace 是用 Python 和T orch 实现的基于深度神经网络的人脸识别模型FaceNet。
因为 该项目是使用 Torch 开发的,所以也支持 CUDA。
https://github.com/cmusatyalab/openface
目前,许多开源爱好者受到OpenFace项目启发,又实现了 Keras和TensorFlow (low-level API)版本的 FaceNeto
人脸识别模型 FaceNet
使用基于 NN4 改造的 CNN 模型训练和提取特征
nn4.small2.v1 是 FaceNet 论文中描述的 NN4 模型的变体,在 OpenFace 的模型列表中有 nn4.small2 详细介绍。
模型列表
本教程使用其 Keras版本(https://github.com/krasserm/face-recognition) 的一种实现,
模型定义在 model.py ,模型可视化的图像存储在 nn4_small2_model.png 。
Retrain 人脸识别模型工作流程
- 加载训练数据集
- 人脸检测、对齐和提取(使用 OpenFace 的人脸对齐工具 AlignDlib)
- 人脸特征向量学习(使用预训练的 nn4.small1.v1 模型)
- 人脸分类(使用 KNN 或 SVM)
加载训练数据集
训练数据集组织形式
-每人一个文件目录,目录以人名命名,如”Fan_Bingbing“
-每个人的文件目录下包含10张图像(最好是1:1比例),图像文件以"人名_序号"命名,仅支持.jpg和.jpeg 两种格式。如”Fan_Bingbing_0001.jpg“。
import numpy as np
import cv2
import os.path
class IdentityMetadata():
def __init__(self, base, name, file):
self.base = base # 数据集根目录
self.name = name # 目录名
self.file = file # 图像文件名
def __repr__(self):
return self.image_path()
def image_path(self):
return os.path.join(self.base, self.name, self.file)
def load_metadata(path):
metadata = []
for i in os.listdir(path):
for f in os.listdir(os.path.join(path, i)):
# 检查文件名后缀,仅支持 jpg 和 jpeg 两种文件格式
ext = os.path.splitext(f)[1]
if ext == '.jpg' or ext == '.jpeg':
metadata.append(IdentityMetadata(path, i, f))
return np.array(metadata)
def load_image(path):
img = cv2.imread(path, 1)
# OpenCV 默认使用 BGR 通道加载图像,转换为 RGB 图像
return img[...,::-1]
metadata = load_metadata('images')
print(metadata)
'''
[images\Deng_chao\Deng_chao_0001.jpg images\Deng_chao\Deng_chao_0002.jpg
images\Deng_chao\Deng_chao_0003.jpg images\Deng_chao\Deng_chao_0004.jpg
images\Deng_chao\Deng_chao_0005.jpg images\Deng_chao\Deng_chao_0006.jpg
images\Deng_chao\Deng_chao_0007.jpg images\Deng_chao\Deng_chao_0008.jpg
images\Deng_chao\Deng_chao_0009.jpg images\Deng_chao\Deng_chao_0010.jpg
images\Di_lireba\Di_Lireba_0001.jpg images\Di_lireba\Di_Lireba_0002.jpg
images\Di_lireba\Di_Lireba_0003.jpg images\Di_lireba\Di_Lireba_0004.jpg
images\Di_lireba\Di_Lireba_0005.jpg images\Di_lireba\Di_Lireba_0006.jpg
images\Di_lireba\Di_Lireba_0007.jpg images\Di_lireba\Di_Lireba_0008.jpg
images\Di_lireba\Di_Lireba_0009.jpg images\Di_lireba\Di_Lireba_0010.jpg
images\Fan_Bingbing\Fan_Bingbing_0001.jpg
images\Fan_Bingbing\Fan_Bingbing_0002.jpg
images\Fan_Bingbing\Fan_Bingbing_0003.jpg
images\Fan_Bingbing\Fan_Bingbing_0004.jpg
images\Fan_Bingbing\Fan_Bingbing_0005.jpg
images\Fan_Bingbing\Fan_Bingbing_0006.jpg
images\Fan_Bingbing\Fan_Bingbing_0007.jpg
images\Fan_Bingbing\Fan_Bingbing_0008.jpg
images\Fan_Bingbing\Fan_Bingbing_0009.jpg
images\Fan_Bingbing\Fan_Bingbing_0010.jpg
images\Liu_Haoran\Liu_Haoran_0001.jpg
images\Liu_Haoran\Liu_Haoran_0002.jpg
images\Liu_Haoran\Liu_Haoran_0003.jpg
images\Liu_Haoran\Liu_Haoran_0004.jpg
images\Liu_Haoran\Liu_Haoran_0006.jpg
images\Liu_Haoran\Liu_Haoran_0007.jpg
images\Liu_Haoran\Liu_Haoran_0008.jpg
images\Liu_Haoran\Liu_Haoran_0009.jpg
images\Liu_Haoran\Liu_Haoran_0010.jpg
images\Peng_Yuyan\Peng_Yuyan_0001.jpg
images\Peng_Yuyan\Peng_Yuyan_0002.jpg
images\Peng_Yuyan\Peng_Yuyan_0003.jpg
images\Peng_Yuyan\Peng_Yuyan_0004.jpg
images\Peng_Yuyan\Peng_Yuyan_0005.jpg
images\Peng_Yuyan\Peng_Yuyan_0006.jpg
images\Peng_Yuyan\Peng_Yuyan_0007.jpg
images\Peng_Yuyan\Peng_Yuyan_0008.jpg
images\Peng_Yuyan\Peng_Yuyan_0009.jpg
images\Peng_Yuyan\Peng_Yuyan_0010.jpg images\Sun_li\Sun_li_0001.jpg
images\Sun_li\Sun_li_0002.jpg images\Sun_li\Sun_li_0003.jpg
images\Sun_li\Sun_li_0004.jpg images\Sun_li\Sun_li_0005.jpg
images\Sun_li\Sun_li_0006.jpg images\Sun_li\Sun_li_0007.jpg
images\Sun_li\Sun_li_0008.jpg images\Sun_li\Sun_li_0009.jpg
images\Sun_li\Sun_li_0010.jpg images\Wu_Yifan\Wu_Yifan_0001.jpg
images\Wu_Yifan\Wu_Yifan_0002.jpg images\Wu_Yifan\Wu_Yifan_0003.jpg
images\Wu_Yifan\Wu_Yifan_0004.jpg images\Wu_Yifan\Wu_Yifan_0005.jpg
images\Wu_Yifan\Wu_Yifan_0006.jpg images\Wu_Yifan\Wu_Yifan_0007.jpg
images\Wu_Yifan\Wu_Yifan_0008.jpg images\Wu_Yifan\Wu_Yifan_0009.jpg
images\Wu_Yifan\Wu_Yifan_0010.jpg images\Yang_mi\Yang_Mi_0001.jpg
images\Yang_mi\Yang_Mi_0002.jpg images\Yang_mi\Yang_Mi_0003.jpg
images\Yang_mi\Yang_Mi_0004.jpg images\Yang_mi\Yang_Mi_0005.jpg
images\Yang_mi\Yang_Mi_0006.jpg images\Yang_mi\Yang_Mi_0007.jpg
images\Yang_mi\Yang_Mi_0008.jpg images\Yang_mi\Yang_Mi_0009.jpg
images\Yang_mi\Yang_Mi_0010.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0001.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0002.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0003.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0004.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0005.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0006.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0007.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0008.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0009.jpg
images\Yi_Yangqianxi\Yi_Yangqinxi_0010.jpg
images\Zhao_Liying\Zhao_liying_0001.jpg
images\Zhao_Liying\Zhao_liying_0002.jpg
images\Zhao_Liying\Zhao_liying_0003.jpg
images\Zhao_Liying\Zhao_liying_0004.jpg
images\Zhao_Liying\Zhao_liying_0005.jpg
images\Zhao_Liying\Zhao_liying_0006.jpg
images\Zhao_Liying\Zhao_liying_0007.jpg
images\Zhao_Liying\Zhao_liying_0008.jpg
images\Zhao_Liying\Zhao_liying_0009.jpg
images\Zhao_Liying\Zhao_liying_0010.jpg]
'''
人脸检测、对齐和提取
从原图提取 96x96 RGB人脸图像。如果原图不是 1:1 比例,提取后的人脸会进行拉伸变换。
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from align import AlignDlib
# 初始化 OpenFace 人脸对齐工具,使用 Dlib 提供的 68 个关键点
alignment = AlignDlib('face_detection/landmarks.dat')
# 加载一张训练图像
img = load_image(metadata[0].image_path())
# 检测人脸并返回边框
bb = alignment.getLargestFaceBoundingBox(img)
# 使用指定的人脸关键点转换图像并截取 96x96 的人脸图像
aligned_img = alignment.align(96, img, bb, landmarkIndices=AlignDlib.OUTER_EYES_AND_NOSE)
# 绘制原图
plt.subplot(131)
plt.imshow(img)
plt.xticks([])
plt.yticks([])
# 绘制带人脸边框的原图
plt.subplot(132)
plt.imshow(img)
plt.gca().add_patch(patches.Rectangle((bb.left(), bb.top()), bb.width(), bb.height(), fill=False, color='red'))
plt.xticks([])
plt.yticks([])
# 绘制对齐后截取的 96x96 人脸图像
plt.subplot(133)
plt.imshow(aligned_img)
plt.xticks([])
plt.yticks([])
'''
([], <a list of 0 Text yticklabel objects>)
'''
加载 nn4.small2.v1 模型
from model import create_model
nn4_small2 = create_model()
from keras.models import Model
from keras.layers import Input, Layer
# 输入 anchor, positive and negative 96x96 RGB图像
in_a = Input(shape=(96, 96, 3))
in_p = Input(shape=(96, 96, 3))
in_n = Input(shape=(96, 96, 3))
# 输出对应的人脸特征向量
emb_a = nn4_small2(in_a)
emb_p = nn4_small2(in_p)
emb_n = nn4_small2(in_n)
from keras.utils.vis_utils import plot_model
plot_model(nn4_small2, to_file='nn4_small2_model.png', show_shapes=True)
Triplet Loss Layer
模型训练的目标是学习出一个将人脸图像嵌入到欧几里得特征空间的函数 ?(?) ,使得对于特定人脸图像 ? ,同一人不同人脸的欧式距离(Squared L2 Distance)尽可能小,不同人直接的欧式距离尽可能大。
通过最小化 triplet loss ? 可以学习到我们想要的模型:
下面使用 Keras 的自定义 Loss 来实现 Triplet Loss
from keras import backend as K
class TripletLossLayer(Layer):
def __init__(self, alpha, **kwargs):
self.alpha = alpha
super(TripletLossLayer, self).__init__(**kwargs)
def triplet_loss(self, inputs):
a, p, n = inputs
p_dist = K.sum(K.square(a-p), axis=-1)
n_dist = K.sum(K.square(a-n), axis=-1)
return K.sum(K.maximum(p_dist - n_dist + self.alpha, 0), axis=0)
def call(self, inputs):
loss = self.triplet_loss(inputs)
self.add_loss(loss)
return loss
triplet_loss_layer = TripletLossLayer(alpha=0.2, name='triplet_loss_layer')([emb_a, emb_p, emb_n])
nn4_small2_train = Model([in_a, in_p, in_n], triplet_loss_layer)
plot_model(nn4_small2_train, to_file='nn4_small2_train.png', show_shapes=True)
加载预训练模型 nn4.small2.v1
我们从 OpenFace 提供的 预训练模型(https://cmusatyalab.github.io/openface/models-and-accuracies/#pre-trained-models)
中选择 nn4.small2.v1。
这些模型使用公开数据集 FaceScrub(http://vintage.winklerbros.net/facescrub.html) 和
CASIA-WebFace(https://arxiv.org/abs/1411.7923)进行训练。Keras-OpenFace 项目将这些模型文件转换为
csv (https://github.com/iwantooxxoox/Keras-OpenFace/tree/master/weights)文件,
然后我们将其转换为 Keras h5 模型文件 nn4.small2.v1.h5。
预训练模型
nn4_small2_pretrained = create_model()
nn4_small2_pretrained.load_weights('models/nn4.small2.v1.h5')
def align_image(img):
return alignment.align(96, img, alignment.getLargestFaceBoundingBox(img),
landmarkIndices=AlignDlib.OUTER_EYES_AND_NOSE)
metadata = load_metadata('orig_images')
embedded = np.zeros((metadata.shape[0], 128))
for i, m in enumerate(metadata):
img = load_image(m.image_path())
img = align_image(img)
# 数据规范化
img = (img / 255.).astype(np.float32)
# 人脸特征向量
embedded[i] = nn4_small2_pretrained.predict(np.expand_dims(img, axis=0))[0]
# Squared L2 Distance
def distance(emb1, emb2):
return np.sum(np.square(emb1 - emb2))
def show_pair(idx1, idx2):
plt.figure(figsize=(8,3))
plt.suptitle(f'Distance = {distance(embedded[idx1], embedded[idx2]):.2f}')
plt.subplot(121)
plt.imshow(load_image(metadata[idx1].image_path()))
plt.xticks([])
plt.yticks([])
plt.subplot(122)
plt.imshow(load_image(metadata[idx2].image_path()))
plt.xticks([])
plt.yticks([])
show_pair(2, 3)
show_pair(2, 12)
人脸分类
from sklearn.preprocessing import LabelEncoder
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score
targets = np.array([m.name for m in metadata])
encoder = LabelEncoder()
encoder.fit(targets)
# Numerical encoding of identities
y = encoder.transform(targets)
train_idx = np.arange(metadata.shape[0]) % 2 != 0
test_idx = np.arange(metadata.shape[0]) % 2 == 0
# 50 train examples of 10 identities (5 examples each)
X_train = embedded[train_idx]
# 50 test examples of 10 identities (5 examples each)
X_test = embedded[test_idx]
y_train = y[train_idx]
y_test = y[test_idx]
knn = KNeighborsClassifier(n_neighbors=1, metric='euclidean')
svc = LinearSVC()
knn.fit(X_train, y_train)
svc.fit(X_train, y_train)
acc_knn = accuracy_score(y_test, knn.predict(X_test))
acc_svc = accuracy_score(y_test, svc.predict(X_test))
print(f'KNN accuracy = {acc_knn}, SVM accuracy = {acc_svc}')
'''
KNN accuracy = 0.96, SVM accuracy = 0.98
'''
import warnings
warnings.filterwarnings('ignore')
example_idx = 44
example_image = load_image(metadata[example_idx].image_path())
example_prediction = svc.predict([embedded[example_idx]])
example_identity = encoder.inverse_transform(example_prediction)[0]
plt.imshow(example_image)
plt.title(f'Recognized as {example_identity}');
plt.xticks([])
plt.yticks([])
'''
([], <a list of 0 Text yticklabel objects>)
'''
模型测试与可视化分析 from sklearn.metrics import f1_score distances = [] # squared L2 distance between pairs identical = [] # 1 if same identity, 0 otherwise num = len(metadata) for i in range(num - 1): for j in range(1, num): distances.append(distance(embedded[i], embedded[j])) identical.append(1 if metadata[i].name == metadata[j].name else 0) distances = np.array(distances) identical = np.array(identical) thresholds = np.arange(0.1, 1.0, 0.01) f1_scores = [f1_score(identical, distances < t) for t in thresholds] acc_scores = [accuracy_score(identical, distances < t) for t in thresholds] opt_idx = np.argmax(f1_scores) opt_tau = thresholds[opt_idx] # 最大F1值对应的 threshold opt_acc = accuracy_score(identical, distances < opt_tau) # 最大F1值对应的准确率 # 绘制F1值和准确率与 threshold 间关系 plt.plot(thresholds, f1_scores, label='F1 score'); plt.plot(thresholds, acc_scores, label='Accuracy'); plt.axvline(x=opt_tau, linestyle='--', lw=1, c='lightgrey', label='Threshold') plt.title(f'Accuracy at threshold {opt_tau:.2f} = {opt_acc:.3f}'); plt.xlabel('Distance threshold') plt.legend();
dist_pos = distances[identical == 1]
dist_neg = distances[identical == 0]
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.hist(dist_pos)
plt.axvline(x=opt_tau, linestyle='--', lw=1, c='lightgrey', label='Threshold')
plt.title('Distances (positive. pairs)')
plt.legend();
plt.subplot(122)
plt.hist(dist_neg)
plt.axvline(x=opt_tau, linestyle='--', lw=1, c='lightgrey', label='Threshold')
plt.title('Distances (negative. pairs)')
plt.legend();
降维人脸特征向量 from sklearn.manifold import TSNE X_embedded = TSNE(n_components=2).fit_transform(embedded) for i, t in enumerate(set(targets)): idx = targets == t plt.scatter(X_embedded[idx, 0], X_embedded[idx, 1], label=t) plt.legend(bbox_to_anchor=(1, 1)); plt.xticks([]) plt.yticks([]) ''' ([], <a list of 0 Text yticklabel objects>) '''
测试和可视化分析
人脸识别测试
人脸识别对不同人种敏感
左边是由是个欧美人训练得出的结果,右边是是个亚洲人。
人脸识别对不同人种敏感
欧美人 Threshold 界限:
亚洲人 Threshold 界限:
人脸识别对不同人种敏感
特征向量降维后结果: 左图为欧美的十个降维后分布,右图为亚洲人