人脸识别 之 OpenCV Haar(EigenFaces、FisherFaces、LBPH)人脸识别
- 人脸识别
- 2021-08-26
- 16热度
- 0评论
EigenFaces:
通常也被称为特征脸,它使用主成分分析(Principal Component Analysis, PCA)方法将高维的人脸数据处理为低维数据后(降维),再进行数据分析和处理,获取识别结果。
FisherFaces:
PCA方法是EigenFaces方法的核心,它找到了最大化数据总方差特征的线性组合。不可否认,EigenFaces是一种非常有效的方法,但是它的缺点在于在操作过程中会损失许多特征信息。因此,在一些情况下,如果损失的信息正好是用于分类的关键信息,必然会导致无法完成分类。
FisherFaces采用LDA(Linear Discriminant Analysis,线性判别分析)实现人脸识别。线性判别识别最早由Fisher在1936年提出,是一种经典的线性学习方法,也被称为“Fisher判别分析法”。
LBP算法的基本原理是:将像素点A的值与其最邻近的8个像素点的值逐一比较:
● 如果A的像素值大于其临近点的像素值,则得到0。
● 如果A的像素值小于其临近点的像素值,则得到1。
● 最后将像素点A与其周围8个像素点比较所得到的0、1值连起来,得到一个8位的二进制序列,将该二进制序列转换为十进制数作为点A的LBP值。
下图中左侧3×3区域的中心点(像素值为76的点)为例,说明如何计算该点的LBP值。计算时,以其像素值76作为阈值,对其8邻域像素进行二值化处理,
● 将像素值大于76的像素点处理为1。例如,其邻域中像素值为128、251、99、213的点,都被处理为1,填入对应的像素点位置上。
● 将像素值小于76的像素点处理为0。例如,其邻域中像素值为36、9、11、48的点,都被处理为0,填入对应的像素点位置上。
根据上述计算,可以得到下图中右图所示的二值结果:

完成二值化以后,任意指定一个开始位置,将得到的二值结果进行序列化,组成一个8位的二进制数。例如,从当前像素点的正上方开始,以顺时针为序得到二进制序列“01011001”。
最后,将二进制序列“01011001”转换为所对应的十进制数“89”,作为当前中心点的像素值,如下图所示:

对图像逐像素用以上方式进行处理,就得到LBP特征图像,这个特征图像的直方图被称为LBPH,或称为LBP直方图。
为了得到不同尺度下的纹理结构,还可以使用圆形邻域,将计算扩大到任意大小的邻域内。圆形邻域可以用(P, R)表示,其中P表示圆形邻域内参与运算的像素点个数,R表示邻域的半径。
例如,在下图中就分别采用了不同的圆形邻域。
● 左侧使用的是(4, 1)邻域,比较当前像素与邻域内4个像素点的像素值大小,使用的半径是1。
● 右侧使用的是(8, 2)邻域,比较当前像素与邻域内8个像素点的像素值大小,使用的半径是2。在参与比较的8个邻域像素点中,部分邻域可能不会直接取实际存在的某个位置上的像素点,而是通过计算构造一个“虚拟”像素值来与当前像素点进行比较。

人脸的整体灰度由于受到光线的影响,经常会发生变化,但是人脸各部分之间的相对灰度会基本保持一致。LBP的主要思想是以当前点与其邻域像素的相对关系作为处理结果,正是因为这一点,在图像灰度整体发生变化(单调变化)时,从LBP算法中提取的特征能保持不变。因此,LBP在人脸识别中得到了广泛的应用。
从上面的介绍可以看到,LBP特征与Haar特征很相似,都是图像的灰度变化特征。
在OpenCV中,可以用函数 cv2.face.LBPHFaceRecognizer_create() 生成 LBPH识别器 实例模型,然后应用 .train() 函数完成训练,最后用 .predict() 函数完成人脸识别。
Python 源码、测试效果:
安装:
pip install opencv-python
pip install opencv-contrib-python
import cv2
import numpy as np
import os
face_database_folder_path = "../face_img"
face_database_imgs = []
face_database_labels = []
face_database_names = []
index = 1
for filename in os.listdir(face_database_folder_path):
if filename.endswith('.jpg'):
print('处理第 %s 张图片' % index)
else:
continue
image = cv2.imread(face_database_folder_path + '/' + filename)
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# cv2.imshow("Camera", cv2.imread(face_database_folder_path + '/' + filename, cv2.IMREAD_GRAYSCALE))
# cv2.waitKey(0)
# cv2.imshow("Camera", cv2.imread(face_database_folder_path + '/' + filename, cv2.COLOR_BGR2GRAY))
# cv2.waitKey(0)
face_database_imgs.append(gray_img)
face_database_labels.append(index - 1)
face_database_names.append(filename.split('.')[0])
index += 1
# 在python3中,cv2.face.createLBPHFaceRecognizer() 方法已经修改成为 cv2.face.LBPHFaceRecognizer_create()
recognizer = cv2.face.LBPHFaceRecognizer_create()
# 使用之前训练好的模型
# recognizer.read('my_trainner.yml')
'''
cv2.face.LBPHFaceRecognizer_create( [, radius[, neighbors[,grid_x[, grid_y[, threshold]]]]])
其中全部的参数都是可选的,含义如下:
radius:半径值,默认值为1。
neighbors:邻域点的个数,默认采用8邻域,根据需要可以计算更多的邻域点。
grid_x:将LBP特征图像划分为一个个单元格时,每个单元格在水平方向上的像素个数。该参数值默认为8,即将LBP特征图像在行方向上以8个像素为单位分组。
grid_y:将LBP特征图像划分为一个个单元格时,每个单元格在垂直方向上的像素个数。该参数值默认为8,即将LBP特征图像在列方向上以8个像素为单位分组。
threshold:在预测时所使用的阈值。如果大于该阈值,就认为没有识别到任何目标对象。
'''
# recognizer = cv2.face.EigenFaceRecognizer_create()
# recognizer = cv2.face.FisherFaceRecognizer_create()
recognizer.train(face_database_imgs, np.array(face_database_labels))
# 保存模型,方便下次直接使用训练好的模型
# recognizer.save('my_trainner.yml')
# 基于 Haar,载入模型
cascade = cv2.CascadeClassifier(r"haarcascade_frontalface_default.xml")
# cascade = cv2.CascadeClassifier(r"cascade.xml")
cameraCapture = cv2.VideoCapture(0)
# cameraCapture = cv2.VideoCapture("rtsp://admin:jinzhong123@192.168.7.122/h265/ch1/main/av_stream")
# cameraCapture = cv2.VideoCapture('人脸识别.mp4')
success, frame = cameraCapture.read()
while success and cv2.waitKey(1) == -1:
success, frame = cameraCapture.read()
# frame = cv2.resize(frame, (320, 240))
# frame = cv2.imread('D:\\1.jpg')
# 生成灰度图
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# cv2.imwrite('gray_image/gray_img_{}.png'.format(number), gray_img)
faces = cascade.detectMultiScale(gray_img, scaleFactor=1.3, minNeighbors=5)
'''
objects = cascade.detectMultiScale(image[, scaleFactor[,minNeighbors[, flags[, minSize[, maxSize]]]]] )
各个参数及返回值的含义为:
image:待检测图像,通常为灰度图像。
scaleFactor:表示在前后两次相继的扫描中,搜索窗口的缩放比例。
minNeighbors:表示构成检测目标的相邻矩形的最小个数。默认情况下,该值为3,意味着有3个以上的检测标记存在时,才认为人脸存在。如果希望提高检测的准确率,可以将该值设置得更大,但同时可能会让一些人脸无法被检测到。
flags:该参数通常被省略。在使用低版本OpenCV(OpenCV 1.X版本)时,它可能会被设置为CV_HAAR_DO_CANNY_PRUNING,表示使用Canny边缘检测器来拒绝一些区域。
minSize:目标的最小尺寸,小于这个尺寸的目标将被忽略。
maxSize:目标的最大尺寸,大于这个尺寸的目标将被忽略。
objects:返回值,目标对象的矩形框向量组。
'''
for (x, y, w, h) in faces:
# 矩形标注
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 圆形标注
# cv2.circle(frame, (int((x + x + w) / 2), int((y + y + h) / 2)), int(w / 2), (0, 255, 0), 2)
predict_image = gray_img[y:y + h, x:x + w]
# cv2.imshow("Camera", predict_image)
# cv2.waitKey(10)
label, confidence = recognizer.predict(predict_image)
'''
# label:返回的识别结果标签。
# confidence:返回的置信度评分。置信度评分用来衡量识别结果与原有模型之间的距离。0表示完全匹配。
# LBPH:通常情况下,认为小于50的值是可以接受的,如果该值大于80则认为差别较大。
# EigenFaces、FisherFaces:通常在0到20000之间,若低于5000,就认为是相当可靠的识别结果。需要注意,该评分值的范围与LBPH方法的评分值范围不一致。
'''
print('Label:%s,Name:%s,confidence:%.2f' % (label, face_database_names[label], confidence))
cv2.putText(frame, face_database_names[label], (x, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)
# 保存检测结果
# cv2.imwrite("res.jpg", frame)
cv2.imshow("Camera", frame)
if cv2.waitKey(10) & 0xFF == ord("q"):
break
cameraCapture.release()
cv2.destroyAilwindows()
人脸检测效果不是很理想,稍微一动就检测不到。效果图如下:


鲁ICP备19063141号
鲁公网安备 37010302000824号