MTCNN+FACENET人脸识别

项目原因,学习人脸识别相关的算法。本来是想用熟悉的yolov5s的,发现一个是训练的时间太长,第二每次要是来新的数据,又得重新训练一次,太麻烦。网上看了一下MTCNN加FACENET的方案比较不错,记录一下部署流程。

一、代码下载

https://github.com/timesler/facenet-pytorch/从上面整个download下来,解压之后把文件全部复制到anaconda/Lib/site-package里面:

之后在pycharm里面直接导入就行了:

 二、使用

具体的原理分析在网上一搜一大把,这里简单介绍一下:

MTCNN网络是用于识别脸的,比方说一个图片中有人脸和其他东西,这个网络可以把人脸单独识别出来,比方说:

from facenet import MTCNN,InceptionResnetV1

face, prob = mtcnn(x, return_prob=True,save_path= None)#这里x是pytorch格式的数据,直接copy这个代码肯定运行不了

返回得到的face就是人脸啦,但是你把这个face打印出来的时候,是一个tensor,所以严格来说应该是人脸的特征向量。prob是识别为人脸的概率,打印出来一般都非常高的。把代码中的savepath改成自己的目录的话会把这个人脸保存下来,如下:

 (华仔是真的帅)

需要注意一个,查看mtcnn源码的时候,根据这个参数return_pro是ture还是false返回的结果是不同的,像上面这个代码里,设置的是true,因此返回的脸和概率。如果是false的话,后面再说。

 其次,InceptionResnet是用于提取人脸特征的网络(大概是这个意思,这里更关心如何使用它),所以是把mtcnn提取出来的face再输入到这个网络中就行了,大概如下:

face_embedding = resnet(face.unsqueeze(0).to('cuda'))

三、完整代码

上面的这些介绍看完之后,再看整体的代码就比较清楚了,起码知道这些网络得到的是啥,然后流程是什么。

from facenet import MTCNN,InceptionResnetV1
import torch
from torch.utils.data import DataLoader
from torchvision import datasets
import pandas as pd
import os
# 定义加载图像的线程数
workers = 0 if os.name=="nt" else 4
# 定义设备
device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
# 创建mtcnn
mtcnn = MTCNN(image_size=160,margin=0,min_face_size=20,thresholds=[0.6,0.7,0.7],
              factor=0.709,post_process=True,device=device
              )
# 创建resnet
resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device)
# 定义数据加载器的函数
def collate_fn(x):
    return x[0]
#将所有的单人照图片放在各自的文件夹中,文件夹名字就是人的名字,存放格式如下::
dataset = datasets.ImageFolder(r"C:\Users\11147\Desktop\detect") #加载数据库
dataset.idx_to_class = {i:c for c,i in dataset.class_to_idx.items()}
loader = DataLoader(dataset,collate_fn=collate_fn,num_workers=workers)
aligned = [] #aligned就是从图像上抠出的人脸,大小是之前定义的image_size=160
names = []

# 将获取的人脸图片保存下来,注意路径
i = 1
for x, y in loader:
    x_aligned, prob = mtcnn(x, return_prob=True,save_path=None)
    print(x_aligned)
    i = i+1
    # 如果有预测的值,放入结果列表中
    if x_aligned is not None:
        print('Face detected with probability: {:8f}'.format(prob))
        aligned.append(x_aligned)
        names.append(dataset.idx_to_class[y])


# 把获取的值保存下来
aligned = torch.stack(aligned).to(device)
embeddings = resnet(aligned).detach().cpu()
#两两之间计算混淆矩阵
dists = [[(e1 - e2).norm().item() for e2 in embeddings] for e1 in embeddings]
print(names)
print(pd.DataFrame(dists, columns=names, index=names))
torch.save(embeddings,'database.pt')  
torch.save(names,'names.pt')

就像前面说的一样,首先是使用mtcnn把训练图片的特征提取出来,最后得到的是两个pt模型,但这不是网络模型,可以理解成类似数据库的东西,其中database.pt就是人脸的特征,比方说刘德华的脸部特征就保存在这里,如果有新的人脸进来会与database.pt里已有的特征进行对比。names.pt是图片的名字,这里的话0就代表刘德华。之后再把特征输入到resnet网络中:

import time

from facenet import MTCNN,InceptionResnetV1
import torch
import cv2

device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
mtcnn = MTCNN(keep_all=True, device=device)
resnet = InceptionResnetV1(pretrained='vggface2').eval().to('cuda')
names = torch.load("./names.pt")
embeddings = torch.load("./database.pt").to('cuda')

def detect_frame(frame):
    faces, boxes = mtcnn(frame)
    # boxes, _ = mtcnn.detect(frame)
    if boxes is None:
        frame = frame
    else:
        print("检测人脸数目:", len(boxes))
        for i, box in enumerate(boxes):
            print(box)
            # face = frame[int(box[1]):int(box[3]), int(box[0]):int(box[2])]
            cv2.rectangle(frame, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 2)
            face_embedding = resnet(faces[i].unsqueeze(0).to('cuda'))
            probs = [(face_embedding - embeddings[i]).norm().item() for i in range(embeddings.size()[0])]
            if 0<=min(probs)<0.9:
                index = probs.index(min(probs))
                name = names[index]  # 对应的人脸
                text = str(name)
                cv2.putText(frame, text, (int(box[0]), int(box[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (100, 200, 200), 1)
            else:
                cv2.putText(frame, 'None', (int(box[0]), int(box[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1)
    return frame


if __name__ == '__main__':
    capture = cv2.VideoCapture(0)
    while capture.isOpened():
        start = time.time()
        ret, frame = capture.read()
        action = cv2.waitKey(10) & 0xFF
        frame = detect_frame(frame)
        end = time.time()
        fps = 1 / (end - start)
        text = "FPS : " + str(int(fps))
        cv2.putText(frame, text, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (100, 200, 200), 1)
        cv2.imshow('frame', frame)
        if action == ord('q') or action == 113: break
    capture.release()
    cv2.destroyAllWindows()

这里就不多说了,知道opencv的话应该都能看的懂。

P.S.

此博客的代码借鉴了这位大佬的快速上手项目1:基于FaceNet的人脸识别项目_facenet vggface2_自学小白菜的博客-CSDN博客这个代码中有一个地方存在了重复推理:

 可以看到这里调用了两次mtcnn,于是去看了一下源码,把最后返回的数据做一下修改就行了,一行代码调用一次即可,修改之后我的帧数从原来12到了20,提升非常明显。修改的地方是mtcnn alt点进去,在这:

 

这个if return_pro就不说了,前面有提到,else的话返回的是faces,即整个图片中把人脸单独提取出来之后的脸部特征(同样也是resnet网络的输入),batch_boxes就是人脸的这个框框的坐标。 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值