当前位置:   article > 正文

pytorch CNN特定人脸识别入门实战_pytorch 人脸识别

pytorch 人脸识别

写在前面:

“Talk is cheap, show me the code.”

本项目为大一学习pytorch的练手之作,代码参考较多,若有错漏请不吝赐教。


目录

1.数据集获取

2.数据集处理

3.卷积神经网络的pytorch实现

4.模型训练

5.模型应用(特定人脸识别)


0.编程环境

python                              3.9.9

opencv-python                4.5.4.60

torch                           1.10.0+cu113

numpy                            1.22.3

torchvision                  0.11.1+cu113

1.数据集获取

数据集的获取完全参考以下博客中的人脸数据获取方法,使用opencv中的haarcascade_frontalface_alt2.xml 模型进行人脸识别,框出每一张人脸并保存到本地。

​​​​​​利用python、tensorflow、opencv实现人脸识别(包会)!_就是这个七昂的博客-CSDN博客_tensorflow 人脸识别

  1. import cv2
  2. import sys
  3. def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
  4. cv2.namedWindow(window_name)
  5. #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
  6. cap = cv2.VideoCapture(camera_idx)
  7. #告诉OpenCV使用人脸识别分类器
  8. classfier = cv2.CascadeClassifier("D:\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml")
  9. #识别出人脸后要画的边框的颜色,RGB格式
  10. color = (0, 255, 0)
  11. num = 0
  12. while cap.isOpened():
  13. ok, frame = cap.read() #读取一帧数据
  14. if not ok:
  15. break
  16. grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #将当前桢图像转换成灰度图像
  17. #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
  18. faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  19. if len(faceRects) > 0: #大于0则检测到人脸
  20. for faceRect in faceRects: #单独框出每一张人脸
  21. x, y, w, h = faceRect
  22. #将当前帧保存为图片
  23. img_name = '%s/%d.jpg'%(path_name, num)
  24. image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  25. cv2.imwrite(img_name, image)
  26. num += 1
  27. if num > (catch_pic_num): #如果超过指定最大保存数量退出循环
  28. break
  29. #画出矩形框
  30. cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
  31. #显示当前捕捉到了多少人脸图片
  32. font = cv2.FONT_HERSHEY_SIMPLEX
  33. cv2.putText(frame,'num:%d' % (num),(x + 30, y + 30), font, 1, (255,0,255),4)
  34. #超过指定最大保存数量结束程序
  35. if num > (catch_pic_num): break
  36. #显示图像
  37. cv2.imshow(window_name, frame)
  38. c = cv2.waitKey(10)
  39. if c & 0xFF == ord('q'):
  40. break
  41. #释放摄像头并销毁所有窗口
  42. cap.release()
  43. cv2.destroyAllWindows()
  44. if __name__ == '__main__':
  45. if len(sys.argv) != 1:
  46. print("Usage:%s camera_id face_num_max path_name\r\n" % (sys.argv[0]))
  47. else:
  48. CatchPICFromVideo("截取人脸", 0, 1000, 'C:\\Users\\73559\\Desktop\\ml\\pic3')

捕获结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5pm66IO96LCD5Y-C5bel5Lq6,size_20,color_FFFFFF,t_70,g_se,x_16

2.数据集处理

(pytorch自带的DataLoader还不太会用,正在学习中,届时将更新本节内容,本节将用传统方法对数据集进行处理)

读取文件夹中图片后,使用opencv对图片进行resize,避免暴力resize使图片像素损失

  1. import os
  2. import numpy as np
  3. import cv2
  4. import random
  5. # from torch.utils.data import DataLoader 学习中...
  6. TRAIN_RATE = 0.8
  7. VALID_RATE = 0.1
  8. SAMPLE_QUANTITY = 2000
  9. IMAGE_SIZE = 227
  10. MYPATH = "C:\\Users\\73559\\Desktop\\ml\\data"
  11. #读取训练数据
  12. images = []
  13. labels = []
  14. def read_path(path_name):
  15. for dir_item in os.listdir(path_name):
  16. #从初始路径开始叠加,合并成可识别的操作路径
  17. full_path = os.path.abspath(os.path.join(path_name, dir_item))
  18. if os.path.isdir(full_path): #如果是文件夹,继续递归调用
  19. read_path(full_path)
  20. else: #文件
  21. if dir_item.endswith('.jpg'):
  22. image = cv2.imread(full_path)
  23. image = cv2.resize(image, (IMAGE_SIZE, IMAGE_SIZE), interpolation=cv2.INTER_AREA) #修改图片的尺寸为64*64
  24. images.append(image)
  25. labels.append(path_name)
  26. return images,labels
  27. #从指定路径读取训练数据
  28. def load_dataset(path_name):
  29. images,labels = read_path(path_name)
  30. #两个人共2000张图片,IMAGE_SIZE为64,故尺寸为2000 * 64 * 64 * 3
  31. #图片为64 * 64像素,一个像素3个通道(RGB)
  32. #标注数据,'pic'文件夹下都是我的脸部图像,全部指定为0,另外一个文件夹下是同学的,全部指定为1
  33. labels = np.array([0 if label.endswith('pic') else 1 for label in labels])
  34. #简单交叉验证
  35. images = list(images)
  36. for i in range(len(images)):
  37. images[i] = [images[i],labels[i]]
  38. random.shuffle(images)
  39. train_data = []
  40. test_data = []
  41. valid_data = []
  42. #训练集
  43. for i in range(int(SAMPLE_QUANTITY * TRAIN_RATE)):
  44. train_data.append(images[i])
  45. #验证集
  46. for i in range(int(SAMPLE_QUANTITY * TRAIN_RATE),int(SAMPLE_QUANTITY * (TRAIN_RATE + VALID_RATE))):
  47. valid_data.append(images[i])
  48. #测试集
  49. for i in range(int(SAMPLE_QUANTITY * (TRAIN_RATE + VALID_RATE)), SAMPLE_QUANTITY):
  50. test_data.append(images[i])
  51. return train_data, test_data , valid_data
  52. # if __name__ == "__main__":
  53. # train_loader = load_dataset(MYPATH)

3.卷积神经网络的pytorch实现

网络结构:AlexNet

将最后全连接层一层改为2

  1. import torch
  2. # 定义网络结构
  3. class AlexNet(torch.nn.Module):
  4. def __init__(self):
  5. super(AlexNet,self).__init__()
  6. self.conv = torch.nn.Sequential(
  7. torch.nn.Conv2d(in_channels=3,
  8. out_channels=96,
  9. kernel_size=11,
  10. stride=4),
  11. torch.nn.BatchNorm2d(96),
  12. torch.nn.ReLU(),
  13. torch.nn.MaxPool2d(3,2),
  14. torch.nn.Conv2d(96,256,5,padding=2),
  15. torch.nn.BatchNorm2d(256),
  16. torch.nn.ReLU(),
  17. torch.nn.MaxPool2d(3,2),
  18. torch.nn.Conv2d(256,384,3,padding=1),
  19. torch.nn.ReLU(),
  20. torch.nn.Conv2d(384,384,3,padding=1),
  21. torch.nn.ReLU(),
  22. torch.nn.Conv2d(384,256,3,padding=1),
  23. torch.nn.ReLU(),
  24. torch.nn.MaxPool2d(3,2),
  25. )
  26. self.fc = torch.nn.Sequential(
  27. torch.nn.Linear(256*6*6,4096),
  28. torch.nn.ReLU(),
  29. torch.nn.Linear(4096, 4096),
  30. torch.nn.ReLU(),
  31. torch.nn.Linear(4096, 2),
  32. #torch.nn.Softmax(dim=1)
  33. #dim=1是按行softmax——降到(0,1)区间内相当于概率,此处不用softmax因为定义的交叉熵损失函数CrossEntropy包含了softmax
  34. )
  35. def forward(self, x):
  36. x = self.conv(x)
  37. #print(x.size())
  38. x = x.contiguous().view(-1,256*6*6) #使用.contiguous()防止用多卡训练的时候tensor不连续,即tensor分布在不同的内存或显存中
  39. x = self.fc(x)
  40. return x

注意事项pytorch的卷积层参数所需  [输入通道,输出通道,卷积核大小,步长,补丁数]

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5pm66IO96LCD5Y-C5bel5Lq6,size_20,color_FFFFFF,t_70,g_se,x_16

其中 padding 支持 "same" 和 "valid"。"same"情况下不能自行设定除1以外的步长,可通过所给公式解一元方程求得padding。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5pm66IO96LCD5Y-C5bel5Lq6,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5pm66IO96LCD5Y-C5bel5Lq6,size_20,color_FFFFFF,t_70,g_se,x_16

卷积层函数详见官方文档:

Conv2d — PyTorch 1.11.0 documentation

相关资料:

在线卷积池化计算器:

在线卷积池化公式计算器

BatchNorm2d原理:

BatchNorm2d原理、作用及其pytorch中BatchNorm2d函数的参数讲解_LS_learner的博客-CSDN博客_batchnorm2d

快速理解CNN原理(可视化):

​​​​​CNN Explainer

SoftMax:

torch.nn.functional中softmax的作用及其参数说明 - 慢行厚积 - 博客园

4.模型训练

注释写的应该挺详细了吧大概)

test data没有用到,可根据实际情况调用。

  1. import torch
  2. import numpy as np
  3. from torch.autograd import Variable
  4. from prepare import load_dataset, MYPATH #上文中的准备文件
  5. from alexnet import AlexNet #上文中的模型
  6. from torch import nn, optim
  7. #定义学习率
  8. learning_rate = 0.001
  9. #是否使用GPU训练
  10. device = torch.device("cuda" if torch.cuda.is_available() else"cpu")
  11. model = AlexNet().to(device)
  12. #定义交叉熵损失函数与SGD随机梯度下降
  13. criterion = nn.CrossEntropyLoss()
  14. optimizer = optim.SGD(model.parameters(), lr=learning_rate)
  15. #数据载入
  16. train_data, test_data , valid_data = load_dataset(MYPATH)
  17. epoch = 0
  18. for data in train_data:
  19. img, label = data
  20. img = torch.LongTensor(img)
  21. #升维,因为pytorch规定输入卷积层的张量至少为4纬,故在此加一个batch的维度
  22. img = Variable(torch.unsqueeze(img, dim=0).float(), requires_grad=False)
  23. #改变张量维度的顺序,pytorch规定卷积层的张量为[batch_size,channel,image_height,image_width],即此处要求2000*3*64*64,而我们原来为2000*64*64*3。
  24. img= np.transpose(img, (0,3,1,2))
  25. #label不能直接转换为LongTensor否则会报错,原因未知-_-
  26. label = torch.tensor(label)
  27. label = label.long()
  28. #转换为向量,否则无法进行比较
  29. label = torch.flatten(label)
  30. label = Variable(label)
  31. img = img.to(device)
  32. label = label.to(device)
  33. out = model(img)
  34. loss = criterion(out, label)
  35. optimizer.zero_grad()
  36. loss.backward()
  37. optimizer.step()
  38. epoch+=1
  39. if epoch%50 == 0:
  40. print('epoch: {}, loss: {:.4}'.format(epoch, loss.data.item()))
  41. model.eval()
  42. eval_loss = 0
  43. eval_acc = 0
  44. for data in valid_data:
  45. img, label = data
  46. img = torch.LongTensor(img)
  47. img = Variable(torch.unsqueeze(img, dim=0).float(), requires_grad=False)
  48. img= np.transpose(img, (0,3,1,2))
  49. label = torch.tensor(label)
  50. label = label.long()
  51. label = torch.flatten(label)
  52. label = Variable(label)
  53. img = img.to(device)
  54. label = label.to(device)
  55. out = model(img)
  56. loss = criterion(out, label)
  57. eval_loss += loss.data.item()*label.size(0)
  58. _, pred = torch.max(out, 1)
  59. num_correct = (pred == label).sum()
  60. eval_acc += num_correct.item()
  61. print('Test Loss: {:.6f}, Acc: {:.6f}'.format(
  62. eval_loss / (len(valid_data)),
  63. eval_acc / (len(valid_data))
  64. ))
  65. #保存训练好的模型
  66. torch.save(model, 'net.pkl')

5.模型应用(特定人脸识别)

同样参考此博客,修改了调用模型以及img的处理。其他不再赘述。利用python、tensorflow、opencv实现人脸识别(包会)!_就是这个七昂的博客-CSDN博客_tensorflow 人脸识别j

  1. import numpy as np
  2. import cv2
  3. import sys
  4. import torch
  5. from torch.autograd import Variable
  6. if __name__ == '__main__':
  7. if len(sys.argv) != 1:
  8. print("Usage:%s camera_id\r\n" % (sys.argv[0]))
  9. sys.exit(0)
  10. #加载模型
  11. device = torch.device("cuda" if torch.cuda.is_available() else"cpu")
  12. model = torch.load('net.pkl').to(device)
  13. #框住人脸的矩形边框颜色
  14. color = (0, 255, 0)
  15. #捕获指定摄像头的实时视频流
  16. cap = cv2.VideoCapture(0)
  17. #人脸识别分类器本地存储路径
  18. cascade_path = "D:\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml"
  19. #循环检测识别人脸
  20. while True:
  21. ret, frame = cap.read() #读取一帧视频
  22. if ret is True:
  23. #图像灰化,降低计算复杂度
  24. frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  25. else:
  26. continue
  27. #使用人脸识别分类器,读入分类器
  28. cascade = cv2.CascadeClassifier(cascade_path)
  29. #利用分类器识别出哪个区域为人脸
  30. faceRects = cascade.detectMultiScale(frame_gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  31. if len(faceRects) > 0:
  32. for faceRect in faceRects:
  33. x, y, w, h = faceRect
  34. #截取脸部图像提交给模型识别这是谁
  35. img = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  36. img = cv2.resize(img, (227, 227), interpolation=cv2.INTER_AREA)
  37. img = torch.from_numpy(img)
  38. img = Variable(torch.unsqueeze(img, dim=0).float(), requires_grad=False)
  39. img = np.transpose(img, (0,3,1,2))
  40. img = img.to(device)
  41. faceID = model(img)
  42. ACC = str(faceID[0][0].item())
  43. cv2.putText(frame,"ACC:" + ACC[:6],
  44. (x - 40, y - 40), #坐标
  45. cv2.FONT_HERSHEY_SIMPLEX, #字体
  46. 1, #字号
  47. (255,0,255), #颜色
  48. 2) #字的线宽
  49. #如果是“我”
  50. if faceID[0][0] > faceID[0][1] and faceID[0][0] > 0.9:
  51. cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
  52. #文字提示是谁
  53. cv2.putText(frame,'It\'s me!',
  54. (x + 30, y + 30), #坐标
  55. cv2.FONT_HERSHEY_SIMPLEX, #字体
  56. 1, #字号
  57. (255,0,255), #颜色
  58. 2) #字的线宽
  59. else:
  60. pass
  61. cv2.imshow("me", frame)
  62. #等待10毫秒看是否有按键输入
  63. k = cv2.waitKey(10)
  64. #如果输入q则退出循环
  65. if k & 0xFF == ord('q'):
  66. break
  67. #释放摄像头并销毁所有窗口
  68. cap.release()
  69. cv2.destroyAllWindows()

结果示意图:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5pm66IO96LCD5Y-C5bel5Lq6,size_20,color_FFFFFF,t_70,g_se,x_16

 注:若有多块GPU则cuda要改成cuda:0(代表第一张GPU)

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/729889
推荐阅读
相关标签
  

闽ICP备14008679号