当前位置:   article > 正文

MTCNN+FACENET人脸识别

mtcnn+facenet人脸识别

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

一、代码下载

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

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

 二、使用

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

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

  1. from facenet import MTCNN,InceptionResnetV1
  2. 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'))

三、完整代码

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

  1. from facenet import MTCNN,InceptionResnetV1
  2. import torch
  3. from torch.utils.data import DataLoader
  4. from torchvision import datasets
  5. import pandas as pd
  6. import os
  7. # 定义加载图像的线程数
  8. workers = 0 if os.name=="nt" else 4
  9. # 定义设备
  10. device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
  11. # 创建mtcnn
  12. mtcnn = MTCNN(image_size=160,margin=0,min_face_size=20,thresholds=[0.6,0.7,0.7],
  13. factor=0.709,post_process=True,device=device
  14. )
  15. # 创建resnet
  16. resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device)
  17. # 定义数据加载器的函数
  18. def collate_fn(x):
  19. return x[0]
  20. #将所有的单人照图片放在各自的文件夹中,文件夹名字就是人的名字,存放格式如下::
  21. dataset = datasets.ImageFolder(r"C:\Users\11147\Desktop\detect") #加载数据库
  22. dataset.idx_to_class = {i:c for c,i in dataset.class_to_idx.items()}
  23. loader = DataLoader(dataset,collate_fn=collate_fn,num_workers=workers)
  24. aligned = [] #aligned就是从图像上抠出的人脸,大小是之前定义的image_size=160
  25. names = []
  26. # 将获取的人脸图片保存下来,注意路径
  27. i = 1
  28. for x, y in loader:
  29. x_aligned, prob = mtcnn(x, return_prob=True,save_path=None)
  30. print(x_aligned)
  31. i = i+1
  32. # 如果有预测的值,放入结果列表中
  33. if x_aligned is not None:
  34. print('Face detected with probability: {:8f}'.format(prob))
  35. aligned.append(x_aligned)
  36. names.append(dataset.idx_to_class[y])
  37. # 把获取的值保存下来
  38. aligned = torch.stack(aligned).to(device)
  39. embeddings = resnet(aligned).detach().cpu()
  40. #两两之间计算混淆矩阵
  41. dists = [[(e1 - e2).norm().item() for e2 in embeddings] for e1 in embeddings]
  42. print(names)
  43. print(pd.DataFrame(dists, columns=names, index=names))
  44. torch.save(embeddings,'database.pt')
  45. torch.save(names,'names.pt')

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

  1. import time
  2. from facenet import MTCNN,InceptionResnetV1
  3. import torch
  4. import cv2
  5. device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
  6. mtcnn = MTCNN(keep_all=True, device=device)
  7. resnet = InceptionResnetV1(pretrained='vggface2').eval().to('cuda')
  8. names = torch.load("./names.pt")
  9. embeddings = torch.load("./database.pt").to('cuda')
  10. def detect_frame(frame):
  11. faces, boxes = mtcnn(frame)
  12. # boxes, _ = mtcnn.detect(frame)
  13. if boxes is None:
  14. frame = frame
  15. else:
  16. print("检测人脸数目:", len(boxes))
  17. for i, box in enumerate(boxes):
  18. print(box)
  19. # face = frame[int(box[1]):int(box[3]), int(box[0]):int(box[2])]
  20. cv2.rectangle(frame, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 2)
  21. face_embedding = resnet(faces[i].unsqueeze(0).to('cuda'))
  22. probs = [(face_embedding - embeddings[i]).norm().item() for i in range(embeddings.size()[0])]
  23. if 0<=min(probs)<0.9:
  24. index = probs.index(min(probs))
  25. name = names[index] # 对应的人脸
  26. text = str(name)
  27. cv2.putText(frame, text, (int(box[0]), int(box[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (100, 200, 200), 1)
  28. else:
  29. cv2.putText(frame, 'None', (int(box[0]), int(box[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1)
  30. return frame
  31. if __name__ == '__main__':
  32. capture = cv2.VideoCapture(0)
  33. while capture.isOpened():
  34. start = time.time()
  35. ret, frame = capture.read()
  36. action = cv2.waitKey(10) & 0xFF
  37. frame = detect_frame(frame)
  38. end = time.time()
  39. fps = 1 / (end - start)
  40. text = "FPS : " + str(int(fps))
  41. cv2.putText(frame, text, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (100, 200, 200), 1)
  42. cv2.imshow('frame', frame)
  43. if action == ord('q') or action == 113: break
  44. capture.release()
  45. cv2.destroyAllWindows()

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

P.S.

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

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

 

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

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号