如何用人工"智障"快速的识别人脸呢?首先我们拿到一张图片,需要去看看图片中是否有人脸,如果有人脸,我们需要把人脸截取出来,放入到特征提取网络中去提取特征,再把提取好的特征向量进行分类,这样就实现人脸识别了。是不是也没有想象中的那么难呢?
本篇博客使用mtcnn作为人脸定位的网络,facenet作为特征提取网络,使用svm作为分类器,将facenet提取到的128维特征向量,用于训练svm,通过svm实现人脸的识别。这样简单的人脸识别系统就完成了。
接下来我会简要的介绍一下,mtcnn ,facenet 和svm的原理。
附上资源链接:
项目完整:https://pan.baidu.com/s/1rmRItADJdwvCQrC3TNPwRQ
提取码:gwto
GitHub地址:https://github.com/jiantenggei/face_recognization
我们是站在巨人的肩膀上,完成自己的任务,mtcnn和facenet使用别人已经预训练好的网络模型,我们只训练svm。
mtcnn (Multi-task convolutional neural network,多任务卷积神经网络),将人脸区域检测和关键点检测放在了一起,检测的关键掉包括眼睛,鼻子,嘴角,五个关键点。
我们来看一下他的工作流程:
首先呢,将原图裁剪成不同的尺寸,再resize成12*12,再输入到P-Net中。
P-Net的网络结构如上图所示,通过浅层的全卷积网络来获得Bounding-Box回归向量,并使用NMS(非极大值抑制)进行大部分窗口的过滤,
图片经过P-Net之后,会产生很多的预测窗口,将预测窗口全部送入到R-Net中,通过R-Net后,会消除掉质量很差的候选框,最后对剩下的候选框进行 Bounding-Box回归和NMS进一步优化预测结果。最后将输出较为可信的区域给O-Net。
这一层会产生更细致的人脸信息,最后还回产生5个关键点landmark。
facenet 是谷歌提出的人脸算法,提出cnn+triplet loss mining的方法,在在LFW数据集上,准确率为0.9963,在YouTube Faces DB数据集上,准确率为0.9512。我们来看一下facenet的网络结构:
DEEP ARCHITECTURE 是一个卷积网络,它可以是Mobilenet或者是ResnetV1,主要作用就是用于特此区域 在通过L2正则化,再embeding成一个128维的向量,再用triplet loss计算损失。但再实际的训练中,还要引入一个优化器来帮助网络收敛。
facenet是将人脸特征映射到128维的特征空间中,然后通过计算欧式距离来判断分类。上图就是triplet loss的学习过程。
SVM 支持向量机,将facenet提取到的128维的特征向量,输入到支持向量机中进行训练,用支持向量机来做分类判断。那有人可能就要问了,为什么不在facenet后之间链接全连接层来做分类呢。因为这样的做的话,需要训练的参数会很多,如果你要去识别1w个人 那么参数就是128*1w。如果每个人只提供的图片不多。那么网络是很难训练的。对于支持向量机,则需要很少的训练数据,就可以达到一个还不错的效果。支持向量机是将低维线性不可分的向量,映射到高维可分。介绍玩原理,我们可以来看如何实现的了。
下载链接:https://github.com/ipazc/mtcnn
将图中的文件夹下载后放入工程目录中。或者直接使用pip 安装:
pip install mtcnn
要求OpenCV>=4.1 Keras>=2.0.0
先面试测试mtcnn的一段代码:
from mtcnn import MTCNN
import cv2
test_img = cv2.imread("imgs/img.png")
detector = MTCNN()
result=detector.detect_faces(test_img)
print(result[0]['box'])
for item in result:
x,y,width,height = item['box']
confidence=item['confidence']
confidence=round(confidence,3)
test_img = cv2.rectangle(test_img,(x,y),(x+width,y+height),color=(0,255,0),thickness=2)
test_img = cv2.putText(test_img,str(confidence),(x,y-10),color=(0,0,255),fontScale=3,fontFace=cv2.FONT_HERSHEY_PLAIN,thickness=2)
cv2.imshow('img',test_img)
cv2.waitKey()
运行结果:
下载链接:https://pan.baidu.com/s/1DC929csx8Vtadbuk7YNTCw 提取码:atdf
放入到工程的weights目录下:
pip install sklearn -i https://pypi.tuna.tsinghua.edu.cn/simple
这里我们使用sklearn机器学习框架。
首先我们将需要识别的人的照片,放入到以他们名字命名的目录下,用英文进行命名。如下图所示:
照片中必须只包含识别对象的单一人脸,如下图:
下面是不合法图片:
训练过程如下图所示:
SVM模型保存成pkl文件。
我们这里主要看训练SVM的过程,其他步骤的代码在github中会展示。
from sklearn.metrics import accuracy_score
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder # 将离散的数据转换到0~classes-1
from sklearn.preprocessing import Normalizer
from sklearn.svm import SVC
from get_feature import get_feature
import pickle
def train():
#过去特征和对应的标签,有
X_train, y_train, X_test, y_test = get_feature()
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
# 样本的特征值除以个特征值的平方和,归一化
in_encoder = Normalizer()
X_train = in_encoder.transform(X_train) #编码
X_test = in_encoder.transform(X_test) #编码
# lable_encoder
out_encoder = LabelEncoder()
out_encoder.fit(y_train)
y_train = out_encoder.transform(y_train)
y_test = out_encoder.transform(y_test)
#定义支持向量机,使用线性核
model = SVC(kernel='linear', probability=True)
model.fit(X_train, y_train)
# 预测,predict
yhat_train = model.predict(X_train)
yhat_test = model.predict(X_test)
score_train = accuracy_score(y_train, yhat_train)
score_test = accuracy_score(y_test,yhat_test)
print('Accuracy: train=%.3f, test=%.3f' % (score_train * 100, score_test * 100))
#模型的保存
pkl_filename = "sklearn_model.pkl"
with open(pkl_filename,'wb') as f:
pickle.dump(model,f)
if __name__ == '__main__':
train()
保存训练好的模型方便用于预测。
预测过程和训练过程类似,如下图所示:
我们来看一下代码实现:
from sklearn.preprocessing import Normalizer
from extract_face.extract_face import extract_faces
import cv2
import pickle
from tensorflow.keras.models import load_model
import numpy as np
from get_feature import get_embedding, create_dict
# 预测跟训练类
# 1.打开摄像头,mtcnn获取人脸
# 2.facenet 计算特征向量
# 放入svm中进行分类 最后输出预测结果
dict = create_dict(r'data/train/')
def predict(image):
global dict
print(dict)
face_list, boxes_list = extract_faces(image)
if len(face_list) != 0:
facenet_model = load_model('weights/facenet_keras.h5')
emdTrainX = list()
for face in face_list:
emd = get_embedding(facenet_model, face)
emdTrainX.append(emd)
emdTrainX = np.asarray(emdTrainX)
emdTrainX.reshape(-1, 1)
in_encoder = Normalizer()
X_train = in_encoder.transform(emdTrainX)
pkl_filename = "sklearn_model.pkl"
with open(pkl_filename, 'rb') as file:
pickle_model = pickle.load(file)
result_list = pickle_model.predict_proba(X_train)
print(result_list)
print(type(result_list))
result_index = np.argmax(result_list, axis=1)
print(result_list)
index = 0
for result in result_index:
if result_list[index][result] <= 0.6:
label = 'unkonw'
else:
label = dict[result]
x, y, width, height = boxes_list[index]
image = cv2.rectangle(image, (x, y), (x + width, y + height), color=(0, 255, 0), thickness=2)
image = cv2.putText(image, str(label), (x, y - 10), color=(0, 0, 255), fontScale=1,
fontFace=cv2.FONT_HERSHEY_PLAIN, thickness=2)
index += 1
return image
if __name__ == '__main__':
img = cv2.imread("imgs/7dec538286139700dc9f34e94048b85.jpg")
predict(img)
cv2.imshow('t', img)
cv2.waitKey()
这里需要注意的是有一个置信度的判断,如果没有这个判断,那么识别的时候,如果识别对象不在你的人脸库,(假设人脸库中有两个人,一个是彭于晏,一个是胡歌),那么如果判别这个人有10%跟彭于晏相似,30%跟胡歌相似,那么就会把这个人识别为胡歌。我们需要在代码中添加一段如下代码:
if result_list[index][result] <= 0.6:
label = 'unknow'
如何识别的置信度小与0.6那么就判断为unknow。到此,简单的人脸识别系统就搭建完成了。
为了方便我使用tkinter写了一个简单的几面,如下图所示:
启动时启动摄像头进行识别,采集时使用摄像头采集图片,放入到训练集和测试集中,重新训练是采集完成后,重新训练是训练采集后的图片,让模型能够识别采集后的人。
这是最终的识别结果,因为叛变这个人不在我们的人脸数据库总,所以识别为unknown。
代码会上传到GitHub中,供大家学习和参考,实现整个系统后识别速度比较慢,可能是我显卡不太好的原因。深度学习有很多网络,来完成不同的任务,如果我们要投入应用中,需要选择一个合适的网络,然后通过更改和微调,使其准确率和速度能跟上我们的需求。总结不易,点赞鼓励。~
https://zhuanlan.zhihu.com/p/130931317
https://zhuanlan.zhihu.com/p/337690783
https://blog.csdn.net/qq_36782182/article/details/83624357
https://blog.csdn.net/u014380165/article/details/78906898
https://zhuanlan.zhihu.com/p/30195134
https://arxiv.org/ftp/arxiv/papers/1604/1604.02878.pdf
https://www.jianshu.com/p/4236679aa3ac