在前面四篇文章中,我们已经完成了整套人脸识别系统的搭建,但是虽然有了最基础的demo,识别的精度仍然不甚理想,想要进一步优化人脸识别的精确度以及实现活体监测,表情识别等一系列功能,就需要用到更高级的人脸识别算法,这篇文章就来介绍几种人脸识别的算法并且对其优劣进行基于个人观点的分析

一、OpenCv库的人脸识别算法

OpenCv支持3种人脸识别的算法,分别是:

  1. Eigen Faces PCA(特征脸方法)
  2. Fisher Faces LDA(线性判别分析)
  3. Local Binary Pattern Histograms ​(LBP 局部二值模式直方图)

二.三种人脸识别算法介绍

2.1 EigenFaces人脸识别

EigenFaces通常也被称为特征脸,它使用主成分分析法PCA高维的人脸数据处理为低维数据后再进行数据分析和处理,获取识别结果。

2.1.1 基本原理

以每张人脸的一个维度(可以看出是矩阵的一列)为单位进行处理,求得的特征向量(特征脸)中包含训练集每个纬度的绝大部分信息,获取其中的主要成分信息,从而实现人脸识别

2.1.2 函数介绍

cv2.face.EigenFaceRecognizer_create((num_components, threshold)) 生成特征脸识别器实例模型

num_components:在PCA中要保留的分量个数,该参数通常要根据输入数据来具体决定,一般来说80个分量就足够了
threshold:进行人脸识别时所采用的阈值。

cv2.face_FaceRecognizer.train(src,labels) 训练模型

src:训练图像,用来学习的人脸图像
labels:人来图像所对应的标签

cv2.face_FaceRecognizer.predict(src) 完成人脸识别。

src:需要识别的人脸图像

2.1.3 代码实现

import cv2
import numpy as np
# 读取训练图像
images=[]
images.append(cv2.imread("e01.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("e02.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("e11.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("e12.png",cv2.IMREAD_GRAYSCALE))
# 给训练图像贴标签
labels=[0,0,1,1]
# 读取待识别图像
predict_image=cv2.imread("eTest.png",cv2.IMREAD_GRAYSCALE)
# 识别
recognizer = cv2.face.EigenFaceRecognizer_create()
recognizer.train(images, np.array(labels))  
label,confidence= recognizer.predict(predict_image) 
# 打印识别结果
print("识别标签label=",label)
print("置信度confidence=",confidence)
# 可视化输出
name=["first","second"]  
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(predict_image,name[label],(0,30), font, 0.8,(255,255,255),2)
cv2.imshow("result",predict_image)
cv2.waitKey()
cv2.destroyAllWindows()

运行效果如下:

2.2 Fisherfaces人脸识别

Fsiherfaces 采用​ LDA (线性判别分析)实现人脸识别。

2.2.1 基本原理

线性判别分析最早由Fisher在1936年提出,是一种经典的线性学习方法,也被称为“Fisher判别分析法”。其基本原理:在低维表示下,相同的类应该紧密地聚集在一起不同的类别应该尽可能地分散开,并且它们之间的距离尽可能地远。可以简单理解成类别间的差别尽可能大,类别内的差别尽可能小

2.2.2 函数介绍

cv2.face.FisherFaceRecognizer_create(num_components, threshold) 生成 Fisherfaces识别器实例模型

num_components 使用 Fisherfaces 准则进行线性判别分析时保留的成分数量,可以采用默认值0,让函数自动设置合适的成分数量。
threshold 进行识别时所用的阈值,如果最近的距离比阈值还大,函数会返回 -1

cv2.face_FaceRecognizer.train(src, labels) 训练模型

src:训练图像,用来学习的人脸图像
labels:人来图像所对应的标签

cv2.face_FaceRecognizer.predict(src ) 人脸识别。

src 待识别的图像

2.2.3 代码实现

import cv2
import numpy as np
# 读取训练图像
images=[]
images.append(cv2.imread("f01.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("f02.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("f11.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("f12.png",cv2.IMREAD_GRAYSCALE))
# 给训练图像贴标签
labels=[0,0,1,1]
# 读取待识别图像
predict_image=cv2.imread("fTest.png",cv2.IMREAD_GRAYSCALE)
# 识别
recognizer = cv2.face.FisherFaceRecognizer_create()
recognizer.train(images, np.array(labels)) 
label,confidence= recognizer.predict(predict_image) 
# 打印识别结果
print("识别标签label=",label)
print("置信度confidence=",confidence)
# 可视化输出
name=["first","second"]  
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(predict_image,name[label],(0,30), font, 0.8,(255,255,255),2)
cv2.imshow("result",predict_image)
cv2.waitKey()
cv2.destroyAllWindows()

运行效果如下:

2.3 LBPH人脸识别

LBPH(局部二值模式直方图)模型是基于LBP(局部二值模式)算法。LBP最早被用作纹理描述,因图像局部纹理特征表达效果出众而得到广泛应用。

2.3.1 基本原理

以每个像素为中心,判断与周围像素灰度值大小关系,对其进行二进制编码,从而获得整幅图像的LBP编码图像;再将LBP图像分为数个区域,获取每个区域的LBP编码直方图,继而得到整幅图像的LBP编码直方图,通过比较不同人脸图像LBP编码直方图实现人脸识别

2.3.2 函数介绍

cv2.face.LBPHFaceRecognizer_create(radius, neighbors, grid_x, grid_y, threshold) 生成LBPH实例模型

radius:半径值,默认为1
neighbors:邻域点的个数,默认采用8邻域
grid_x:将LBP特征图划分为一个个单元格时,每个单元格在水平方向上的像素个数,该参数值默认为8,即将LBP特征图像在行方向上以8个像素为单位分组。
grid_y:将LBP特征图划分为一个个单元格时,每个单元格在垂直方向上的像素值,该参数值默认为8,即将LBP特征图像在列方向上以8个像素为单位分组。
threshold:在预测时使用,如果大于该阈值,就认为没有识别到任何目标对象。

cv2.face_FaceRecognizer.train(src,labels) 训练模型

src:训练图像,用来学习的人脸图像
labels:标签,人脸图像所对应的标签

cv2.face_FaceRecognizer.predict(src) 完成人脸识别。

src:需要识别的人脸图片

2.3.3 代码实现

import cv2
import numpy as np
# 读取训练图像
images=[]
images.append(cv2.imread("e01.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("e02.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("e11.png",cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread("e12.png",cv2.IMREAD_GRAYSCALE))
# 给训练图像贴标签
labels=[0,0,1,1]
# 读取待识别图像
predict_image=cv2.imread("eTest.png",cv2.IMREAD_GRAYSCALE)
# 识别
recognizer = cv2.face.EigenFaceRecognizer_create()
recognizer.train(images, np.array(labels))  
label,confidence= recognizer.predict(predict_image) 
# 打印识别结果
print("识别标签label=",label)
print("置信度confidence=",confidence)
# 可视化输出
name=["first","second"]  
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(predict_image,name[label],(0,30), font, 0.8,(255,255,255),2)
cv2.imshow("result",predict_image)
cv2.waitKey()
cv2.destroyAllWindows()

运行效果如下:


2.4 优缺点分析

EigenFace: 识别不准确

FisherFace: 对人脸进行识别对光照、人脸姿态的变化更不敏感,有助于提高识别效果。

LBPH: 对光照有明显的鲁棒性

三.基于dlib的人脸识别

dlib库是一个用来人脸关键点检测的 python 库,里面包含了许多的机器学习算法,是一个优秀的人脸识别开源库。

Dlib中,人脸识别的基本思路为:

  • 计算已知图片中所有人脸对应的特征向量;
  • 计算要识别的未知图片中所有人脸对应的特征向量;
  • 计算人脸之间的欧式距离;
  • 如果两张人脸之间的欧式距离小于设定的阈值,则认为是同一个人,否则认为不是同一个人

代码实现

import dlib
import cv2
# 载入模型
cnn_face_detector = dlib.cnn_face_detection_model_v1("mmod_human_face_detector.dat")
# 读取图像
img = cv2.imread("people.jpg", cv2.IMREAD_COLOR)
# 检测
faces = cnn_face_detector(img, 1)
# 返回的结果faces是一个mmod_rectangles对象,有2个成员变量:
# dlib.rect类,表示对象的位置
# dlib.confidence,表示置信度。
for i, d in enumerate(faces):
    # 计算每个人脸的位置
    rect = d.rect  
    left = rect.left()
    top = rect.top()
    right = rect.right()
    bottom = rect.bottom()
    # 绘制人脸对应的矩形框
    cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)  
    cv2.imshow("result", img)
k = cv2.waitKey()
cv2.destroyAllWindows()

运行效果如下:

基于卷积神经网络的人脸识别在近些年应用广泛,主要优势是其优势是通过学习的过程获得对这些规律和规则的隐性表达,它的适应性较强。用上述代码替换原先代码中用于人脸识别的代码,可以提高人脸识别的准确度。

笔者对这一领域并没有较为深入的研究,以后如果有机会,可以深入学习一下卷积神经网络,优化一下现在用于人脸识别的算法