OpenCV 有三种人脸识别的算法:
- Eigenfaces 是通过 PCA(主成分分析)实现的,它识别人脸数据集的主成分,并计算出待识别图像区域相对于数据集的发散程度(0~20k),该值越小,表示差别越小,0值表示完全匹配。低于4k~5k都是相当可靠的识别。
- FisherFaces 是从 PCA发展而来,采用更复杂的计算,容易得到更准确的结果。低于4k~5k都是相当可靠的识别。
- LBPH 将人脸分成小单元,并将其与模型中的对应单元进行比较,对每个区域的匹配值产生一个直方图。它允许待检测人脸区域可以和数据集中图像的形状、大小不同,更方便灵活。参考值低于50则算是好的识别,高于80则认为比较差。
当然,除了这三种预定义的算法外,我们可以自己写深度学习算法或者其他机器学习的分类算法来进行人脸识别,这里不再详述。
不管使用哪种算法都需要有训练集。从视频或者动图创建训练集的效率比较高。我从网上下载了一些明星的动图,然后分解,检测人脸区域,全部存为200X200的灰度图,存入对应的文件夹中,创建训练集。
代码语言:javascript复制from PIL import Image
import os
import cv2
def gifSplit2Array(gif_path):
import numpy as np
img = Image.open(gif_path)
for i in range(img.n_frames):
img.seek(i)
new = Image.new("RGBA", img.size)
new.paste(img)
arr = np.array(new).astype(np.uint8) # image: img (PIL Image):
yield arr[:,:,2::-1] # 逆序(RGB 转BGR), 舍弃alpha通道, 输出数组供openCV使用
def face_generate(img):
gray =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
front_face_cascade = cv2.CascadeClassifier(r'E:Python36MyPythonFilesmy OpenCVface_detectioncascades/haarcascade_frontalface_default.xml')#检测正脸
faces0 = front_face_cascade.detectMultiScale(gray, 1.02, 5)
#eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')#检测眼睛
eye_cascade = cv2.CascadeClassifier(r'E:Python36MyPythonFilesmy OpenCVface_detectioncascades/haarcascade_eye_tree_eyeglasses.xml')#检测眼睛
for (x, y, w, h) in faces0:
face_area = gray[y: y h, x: x w] # (疑似)人脸区域
quasi_eyes = eye_cascade.detectMultiScale(face_area, 1.03, 5, 0)#在人脸区域检测眼睛
if len(quasi_eyes) ==0: continue
quasi_eyes = tuple(filter(lambda x : x[2]/w>0.18 and x[1]<0.5*h, quasi_eyes)) # ex,ey,ew,eh; ew/w>0.18 #尺寸过滤 ,且眼睛在脸的上半部
if len(quasi_eyes) <1 : continue
yield cv2.resize(face_area, (200,200))
i = 0
for gif_path in ("Yangme5.gif","Yangme6.gif","Shishi.gif","Shishi2.gif","Shishi3.gif","Shishi3.gif" ,"Yangzi.gif","Yangzi2.gif","Yangzi3.gif","Zhenshuang.gif","ZDY.gif", "ZDY2.gif"):
print(gif_path)
for img in gifSplit2Array(gif_path):
for face in face_generate(img):
cv2.imwrite("./dataset/%s.pgm" % (i), face)
print(i)
i = 1
我们可以剔除掉其中一些效果不好的图片。
接着我们加载数据集,训练模型:
代码语言:javascript复制def load_dataset(datasetPath):
names = []
X = []
y = []
ID = 0
for name in os.listdir(datasetPath):
subpath = os.path.join(datasetPath, name)
if os.path.isdir(subpath):
names.append(name)
for file in os.listdir(subpath):
im = cv2.imread(os.path.join(subpath,file), cv2.IMREAD_GRAYSCALE)
X.append(np.asarray(im, dtype = np.uint8))
y.append(ID)
ID = 1
X = np.asarray(X)
y = np.asarray(y, dtype = np.int32)
return X, y, names
datasetPath =".dataset"
X, y , names = load_dataset(datasetPath)
#报错找不到face模块是因为只安装了主模块
#pip uninstall opencv-python, pip install opencv0-contrib-python
#创建人脸识别模型(三种识别模式)
#model = cv2.face.EigenFaceRecognizer_create() #createEigenFaceRecognizer()函数已被舍弃
#model = cv2.face.FisherFaceRecognizer_create()
model = cv2.face.LBPHFaceRecognizer_create() #
model.train(X, y)
最后我们将待预测的图像中的人脸区域与训练集中的图像进行比对预测:
代码语言:javascript复制def recognize(img):
global model, names
gray =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
front_face_cascade = cv2.CascadeClassifier(r'E:Python36MyPythonFilesmy OpenCVface_detectioncascades/haarcascade_frontalface_default.xml')#检测正脸
faces0 = front_face_cascade.detectMultiScale(gray, 1.025, 5)
eye_cascade = cv2.CascadeClassifier(r'E:Python36MyPythonFilesmy OpenCVface_detectioncascades/haarcascade_eye_tree_eyeglasses.xml')#检测眼睛
for (x, y, w, h) in faces0:
#print(x,y,w,h)
face_area = gray[y: y h, x: x w] #疑似人脸区域
quasi_eyes = eye_cascade.detectMultiScale(face_area, 1.03, 5, 0)#在人脸区域检测眼睛
if len(quasi_eyes) ==0: continue
quasi_eyes = tuple(filter(lambda x : x[2]/w>0.15, quasi_eyes)) # ex,ey,ew,eh; ew/w>0.18 #尺寸过滤
#print(quasi_eyes)
if len(quasi_eyes) <1 : continue#if len(quasi_eyes) <2 : continue
cv2.rectangle(img, (x,y), (x w,y h), (0,0,255), 2) #画红色矩形框标记正脸
roi = cv2.resize(face_area, (200,200), interpolation = cv2.INTER_LINEAR) #尺寸缩放到与训练集中图片的尺寸一致
cv2.imwrite("e.pgm", roi) #若识别错误,可以添加到正确的数据集,提高后续的识别率
ID_predict, confidence = model.predict(roi)#预测!!!
name = names[ID_predict]
print("name:%s, confidence:%.2f"%(name, confidence))
text = name if confidence <70 else "unknow" #10000 for EigenFaces #70 for LBPH
cv2.putText(img, text , (x, y-20), cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)#绘制绿色文字
return img
imgPath = "AA.jpg"
img = cv2.imread(imgPath)
cv2.imshow(imgPath, recognize(img))
cv2.waitKey()
cv2.destroyAllWindows()
下面是一些预测结果的展示:
单人照:
伪双人照:
三人照(杀马特的诗诗......):
胡歌不在数据集中,所以肯定会识别错误,我们需舍弃置信度过差的结果:
当然,真实的识别效果没这么理想,识别的准确度主要取决于我们的数据集的优劣。