opencv封装了可用于人脸识别的类FaceRecognizer,这个类目前包含三种人脸识别方法:基于PCA变换的人脸识别(EigenFaceRecognizer)、基于Fisher变换的人脸识别(FisherFaceRecognizer)、基于局部二值模式的人脸识别(LBPHFaceRecognizer)。对于像我这样的人脸识别初学者,对人脸识别理论了解得不是很透彻,但并不影响对函数的使用,下面就LBPHFaceRecognizer来详细的谈一下opencv人脸识别的实现。
LBP local binary patterns,从纹理分析的角度来看,图像上某个像素点的纹理特征,大多数情况下是指这个点和周围像素点的关系,即这个点和它的邻域内点的关系。从哪个角度对这种关系提取特征,就形成了不同种类的特征。有了特征,就能根据纹理进行分类。LBP构造了一种衡量一个像素点和它周围像素点的关系。
。灰度不变性LBP( gray scale invariant)
。旋转不变性LBP(rotation invariant)
。旋转不变等价LBP(rotation & uniform invariant)
具体可参考这篇博文,写的很详细。
import numpy as np
import pandas as pd
np.random.seed(37) # 使得每次运行得到的随机数都一样
import matplotlib.pyplot as plt
import cv2
from sklearn import preprocessing
from glob import glob
import os
from sklearn.preprocessing import LabelEncoder
# 构建createLBPHFaceRecognizer分类模型
from cv2.face import LBPHFaceRecognizer_create
# 定义一个函数来加载图片数据集
def load_train_set(imgs_folder,face_cascade):
'''
从imgs_folder中加载图片数据和标记,注意imgs_folder中包含有多个子文件夹,每个子文件夹的名称就是label
os.path.join(path1, path2, ...)对多个路径进行拼接
glob.glob(pathname)遍历当前文件夹下的文件,不能遍历子文件夹中的文件
'''
folders=glob(os.path.join(imgs_folder,'*')) #遍历子文件夹
imgs_paths=[]
[imgs_paths.extend(glob(os.path.join(folder, '*.*'))) for folder in folders] #遍历子文件夹中的图片
face_imgs=[]
labels=[]
# 对每一张图片都检测画面上的人脸
for img_path in imgs_paths:
image = cv2.imread(img_path, 0)
#os.path.split():将文件名和路径分割开
label=os.path.split(img_path)[1] #索引为0视为路径,索引为1视为文件名
img_folder=os.path.split(img_path)[0]
#用训练好的模型检测每一张人脸
faces = face_cascade.detectMultiScale(image, 1.1, 2, minSize=(100,100))
for (x, y, w, h) in faces:
face_imgs.append(image[y:y+h, x:x+w]) #切割的人脸图片
labels.append(os.path.split(img_folder)[1]) #labels为图名
# 此处有点不合理,本数据集中每张图片只有一个人脸,故而可以用这个方式,
# 如果有多个不同人的脸,则不能用折冲方式。
# 将labels转换为数字。LabelEncoder 将一列文本数据转化成数值
label_encoder=LabelEncoder()
encode_labels=label_encoder.fit_transform(labels)
return face_imgs, encode_labels, label_encoder, labels
#找到错误面孔
def find_false_faces(face_imgs):
'''
将所有脸部照片显示出来,如果发现有错误的,按d键,记录下错误的脸部照片
'''
need_del_ids=[]
for idx,face in enumerate(face_imgs):
cv2.namedWindow('check', cv2.WINDOW_NORMAL)
cv2.resizeWindow('check', 500, 500)
cv2.imshow('check', face)
key = cv2.waitKey(0)
if key==27: # 如果输入时Esc,则退出循环
print('esc to exit')
break
elif key==100: # 如果输入d键,则记录该脸对应的id
need_del_ids.append(idx)
cv2.destroyAllWindows()
print('finished...')
return need_del_ids
# 从数据集中删除非人脸图片
def delete_error_images(face_imgs, need_del_ids, encode_labels, labels):
face_imgs=np.delete(np.array(face_imgs), need_del_ids, axis=0)
encode_labels=np.delete(np.array(encode_labels), need_del_ids, axis=0)
labels=np.delete(np.array(labels), need_del_ids,axis=0)
print(face_imgs.shape) # 53张图没错,元素已经变成了np.ndarray,故而只有行
return face_imgs, encode_labels, labels
# 用训练好的模型预测新照片
def predict_imgs(new_imgs_folder, face_cascade,recognizer,label_encoder):
'''
用训练好的人脸识别器来识别人脸
'''
img_paths=glob(new_imgs_folder+'/*.*')
predicted_imgs=[]
for img_path in img_paths:
image=cv2.imread(img_path)
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
#人脸检测,把检测到的人脸用矩形画出来
faces=face_cascade.detectMultiScale(gray,1.1, 2, minSize=(100,100))
for (x, y, w, h) in faces:
cv2.rectangle(image,(x,y),(x+w,y+h),(0,0,255),3)
predicted_index, conf = recognizer.predict(gray[y:y+h, x:x+w])
#inverse_transform将标准化后的数据转化成原始数据
predicted_label=label_encoder.inverse_transform([predicted_index])[0]
#在测试的图片上添加预测出的文字说明
cv2.putText(image, predicted_label,(x,y-20), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 3)
predicted_imgs.append(image)
return predicted_imgs
if __name__ == '__main__':
#获取训练好的人脸参数数据
face_cascade=cv2.CascadeClassifier('/Users/chenyan/important/python_demo/Project/LBP/cascade_files/haarcascade_frontalface_alt.xml')
face_imgs, encode_labels, label_encoder, labels=load_train_set('/Users/chenyan/important/python_demo/Project/LBP/train',face_cascade)
print(len(face_imgs)) # 有53张脸,但是检测得到55个结果,显然有几张图片中检测了多张脸
# 任意显示任一张人脸
# 由于cv2读取的是BGR,而plt是RGB,故而需要转化一下
#plt.imshow(face_imgs[3],cmap='gray')
#plt.show()
#找到错误面孔
#need_del_ids=find_false_faces(face_imgs)
#print(need_del_ids)
need_del_ids =[25,47]
#删除错误面孔
face_imgs, encode_labels, labels = delete_error_images(face_imgs,need_del_ids,encode_labels,labels)
# 构建createLBPHFaceRecognizer分类模型
recognizer=LBPHFaceRecognizer_create()
recognizer.train(face_imgs, encode_labels) # 模型训练
predicted=predict_imgs('/Users/chenyan/important/python_demo/Project/LBP/test',face_cascade,recognizer,label_encoder)
# 随便显示几张预测后的图片
print(len(predicted))
for i in range(9):
i=predicted[i].copy()
i=cv2.cvtColor(i,cv2.COLOR_BGR2RGB)
plt.imshow(i)
plt.show()