赞
踩
项目原因,学习人脸识别相关的算法。本来是想用熟悉的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的话应该都能看的懂。
此博客的代码借鉴了这位大佬的快速上手项目1:基于FaceNet的人脸识别项目_facenet vggface2_自学小白菜的博客-CSDN博客这个代码中有一个地方存在了重复推理:
可以看到这里调用了两次mtcnn,于是去看了一下源码,把最后返回的数据做一下修改就行了,一行代码调用一次即可,修改之后我的帧数从原来12到了20,提升非常明显。修改的地方是mtcnn alt点进去,在这:
这个if return_pro就不说了,前面有提到,else的话返回的是faces,即整个图片中把人脸单独提取出来之后的脸部特征(同样也是resnet网络的输入),batch_boxes就是人脸的这个框框的坐标。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。