赞
踩
如何用人工"智障"快速的识别人脸呢?首先我们拿到一张图片,需要去看看图片中是否有人脸,如果有人脸,我们需要把人脸截取出来,放入到特征提取网络中去提取特征,再把提取好的特征向量进行分类,这样就实现人脸识别了。是不是也没有想象中的那么难呢?
本篇博客使用 mtcnn 作为人脸定位的网络,facenet 作为特征提取网络,使用 svm 作为分类器,将 facenet 提取到的 128 维特征向量,用于训练 svm,通过 svm 实现人脸的识别。这样简单的人脸识别系统就完成了。
接下来我会简要的介绍一下,mtcnn ,facenet 和 svm 的原理。
本篇博文只是教大家搭建一个简单的人脸识别系统,并没有解决网络单次训练的问题。 单次训练就是说,训练完后,当新的人脸存入到数据库中,不许要重新训练网络,只需要少量的几张图片就可以准确识别。本文用的方法,当新的人脸进来是需要重新训练 svm 分类器。所以比较简单。
我们是站在巨人的肩膀上,完成自己的任务,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。如果每个人只提供的图片不多。那么网络是很难训练的。对于支持向量机,则需要很少的训练数据,就可以达到一个还不错的效果。支持向量机是将低维线性不可分的向量,映射到高维可分。介绍玩原理,我们可以来看如何实现的了。
就在代码文件的 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。
完整代码:https://download.csdn.net/download/qq_38735017/87416631
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。