基于mtcnn+facenet+svm实现人脸识别系统GUI界面 附完整代码数据集

前言

如何用人工"智障"快速的识别人脸呢?首先我们拿到一张图片,需要去看看图片中是否有人脸,如果有人脸,我们需要把人脸截取出来,放入到特征提取网络中去提取特征,再把提取好的特征向量进行分类,这样就实现人脸识别了。是不是也没有想象中的那么难呢?

本篇博客使用 mtcnn 作为人脸定位的网络,facenet 作为特征提取网络,使用 svm 作为分类器,将 facenet 提取到的 128 维特征向量,用于训练 svm,通过 svm 实现人脸的识别。这样简单的人脸识别系统就完成了。

接下来我会简要的介绍一下,mtcnn ,facenet 和 svm 的原理。

本篇博文只是教大家搭建一个简单的人脸识别系统,并没有解决网络单次训练的问题。 单次训练就是说,训练完后,当新的人脸存入到数据库中,不许要重新训练网络,只需要少量的几张图片就可以准确识别。本文用的方法,当新的人脸进来是需要重新训练 svm 分类器。所以比较简单。

一、原理介绍

我们是站在巨人的肩膀上,完成自己的任务,mtcnn 和 facenet 使用别人已经预训练好的网络模型,我们只训练 svm。

1.mtcnn

mtcnn (Multi-task convolutional neural network,多任务卷积神经网络),将人脸区域检测和关键点检测放在了一起,检测的关键掉包括眼睛,鼻子,嘴角,五个关键点。

我们来看一下他的工作流程:

首先呢,将原图裁剪成不同的尺寸,再 resize 成 12*12,再输入到 P-Net 中。

P-Net(Proposal Network)

P-Net 的网络结构如上图所示,通过浅层的全卷积网络来获得 Bounding-Box 回归向量,并使用 NMS(非极大值抑制)进行大部分窗口的过滤,

R-Net(Refine Network)

图片经过 P-Net 之后,会产生很多的预测窗口,将预测窗口全部送入到 R-Net 中,通过 R-Net 后,会消除掉质量很差的候选框,最后对剩下的候选框进行 Bounding-Box 回归和 NMS 进一步优化预测结果。最后将输出较为可信的区域给 O-Net。

O-Net (Output Network)

这一层会产生更细致的人脸信息,最后还回产生 5 个关键点 landmark。

2. facenet

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 的学习过程。

3.SVM(Support Vector Machine)

SVM 支持向量机,将 facenet 提取到的 128 维的特征向量,输入到支持向量机中进行训练,用支持向量机来做分类判断。那有人可能就要问了,为什么不在 facenet 后之间链接全连接层来做分类呢。因为这样的做的话,需要训练的参数会很多,如果你要去识别 1w 个人 那么参数就是 128*1w。如果每个人只提供的图片不多。那么网络是很难训练的。对于支持向量机,则需要很少的训练数据,就可以达到一个还不错的效果。支持向量机是将低维线性不可分的向量,映射到高维可分。介绍玩原理,我们可以来看如何实现的了。

二、人脸识别实现

1.准备工作
引入 mtcnn

就在代码文件的 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()

运行结果:

下载 facenet 的权重文件:

下载链接:https://pan.baidu.com/s/1DC929csx8Vtadbuk7YNTCw 提取码:atdf 放入到工程的 weights 目录下:

安装 sklearn
pip install sklearn -i https://pypi.tuna.tsinghua.edu.cn/simple 

这里我们使用 sklearn 机器学习框架。

2.训练和预测

数据准备

首先我们将需要识别的人的照片,放入到以他们名字命名的目录下,用英文进行命名。如下图所示:

照片中必须只包含识别对象的单一人脸,如下图:

下面是不合法图片:

训练

训练过程如下图所示:

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

  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计算机毕设论文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值