当前位置:   article > 正文

OpenCV实战项目 -- 口罩识别_基于opencv的口罩识别

基于opencv的口罩识别

每次我忘记戴口罩去食堂吃饭的时候,门口都会有志愿者学生提醒你:“你好,麻烦戴下口罩。” 进门后里面那块大屏幕还会发出声音:“请佩戴口罩”。

上次博客仿照宿舍楼下那块大屏幕写了个人脸考勤,所以这次我打算弄一个口罩识别


实现口罩识别的方式有很多种,也并非是一件难事。重要的是要学会把学到的知识运用到生活中去。为此,我特地翻出了吃灰的“树莓派”主板,打算放到宿舍门口。无他,卷一卷督促室友学习。

一、简单流程思路分析

相信大多数初学人工智能的小伙伴,最早接触的一定是CNN处理图像分类的问题,本次项目的核心也就是CNN分类了。所以,这只是前面所学知识的结合,不做过多赘述。

A. 训练模型 

  • 在此之前,我们要弄到数据集:佩戴口罩未佩戴口罩佩戴口罩不规范 的图片。
  • 然后对数据集进行预处理。
  • 搭建CNN网络模型并进行图片分类。
  • 此时,我们便拿到了模型权重文件!

B. 处理摄像头输入

  • 打开摄像头,然后捕捉到我们的照片。
  • 处理输入图片,裁剪出人脸区域。
  • 裁剪后的图片作为神经网络的输入,得到网络预测分类结果。
  • 将结果显示在屏幕上。

C. 树莓派部署

  • 不难,但是有点花时间,这里不做过多说明。

权重文件和整体代码在文章最后 

二、CNN实现口罩佩戴图片分类

1. 数据集处理

无论是机器学习还是深度学习,最重要的便是数据集,之后才是相关的模型和算法。

在这里,我们首先处理图片,把每一张佩戴口罩的照片裁剪出人脸部分,之后为了便于计算和训练我们将图片进行压缩。相关方法之前博客已经介绍过这里不做赘述。

大家可以自己到网上下载数据集,也可以使用我参考的这份,数据量比较小,用于演示:链接

  1. face_detector = cv2.dnn.readNetFromCaffe('./weights/deploy.prototxt.txt','weights/res10_300x300_ssd_iter_140000.caffemodel')
  2. # 人脸检测函数
  3. def face_detect(img):
  4. #转为Blob
  5. img_blob = cv2.dnn.blobFromImage(img,1,(300,300),(104,177,123),swapRB=True)
  6. # 输入
  7. face_detector.setInput(img_blob)
  8. # 推理
  9. detections = face_detector.forward()
  10. # 获取原图尺寸
  11. img_h,img_w = img.shape[:2]
  12. # 人脸框数量
  13. person_count = detections.shape[2]
  14. for face_index in range(person_count):
  15. # 通过置信度选择
  16. confidence = detections[0,0,face_index,2]
  17. if confidence > 0.5:
  18. locations = detections[0,0,face_index,3:7] * np.array([img_w,img_h,img_w,img_h])
  19. # 获得坐标 记得取整
  20. l,t,r,b = locations.astype('int')
  21. return img[t:b,l:r]
  22. return None

效果:

  1. # 转为Blob格式函数
  2. def imgBlob(img):
  3. # 转为Blob
  4. img_blob = cv2.dnn.blobFromImage(img,1,(100,100),(104,177,123),swapRB=True)
  5. # 维度压缩
  6. img_squeeze = np.squeeze(img_blob).T
  7. # 旋转
  8. img_rotate = cv2.rotate(img_squeeze,cv2.ROTATE_90_CLOCKWISE)
  9. # 镜像
  10. img_flip = cv2.flip(img_rotate,1)
  11. # 去除负数,并归一化
  12. img_blob = np.maximum(img_flip,0) / img_flip.max()
  13. return img_blob

效果:

 有了这两个函数,我们就可以进行数据集的处理了:

  1. import tqdm
  2. import os,glob
  3. labels = os.listdir('images/')
  4. img_list = []
  5. label_list = []
  6. for label in labels:
  7. # 获取每类文件列表
  8. file_list =glob.glob('images/%s/*.jpg' % (label))
  9. for img_file in tqdm.tqdm( file_list ,desc = "处理文件夹 %s " % (label)):
  10. # 读取文件
  11. img = cv2.imread(img_file)
  12. # 裁剪人脸
  13. img_crop = face_detect(img)
  14. # 转为Blob
  15. if img_crop is not None:
  16. img_blob = imgBlob(img_crop)
  17. img_list.append(img_blob)
  18. label_list.append(label)

 最后,我们将其转换为npz格式文件:

  1. X = np.asarray(img_list)
  2. Y = np.asarray(label_list)
  3. np.savez('./data/imageData.npz',X,Y)

2. 模型训练

首先我们读取之前保存的npz文件:

  1. import numpy as np
  2. arr = np.load('./data/imageData.npz')
  3. img_list = arr['arr_0']
  4. label_list =arr['arr_1']
  5. print(img_list.shape,label_list.shape)
((5328, 100, 100, 3), (5328,))

设置为onehot独热编码:

  1. from sklearn.preprocessing import OneHotEncoder
  2. onehot = OneHotEncoder()
  3. # 编码
  4. y_onehot =onehot.fit_transform(label_list.reshape(-1,1))
  5. y_onehot_arr = y_onehot.toarray()

划分数据集:

  1. from sklearn.model_selection import train_test_split
  2. x_train,x_test,y_train,y_test=train_test_split(img_list,y_onehot_arr,test_size=0.2,random_state=123)
  3. x_train.shape,x_test.shape,y_train.shape,y_test.shape
((4262, 100, 100, 3), (1066, 100, 100, 3), (4262, 3), (1066, 3))

构建并编译模型:

  1. from tensorflow import keras
  2. from tensorflow.keras import layers,models
  3. import tensorflow as tf
  4. gpus = tf.config.list_physical_devices("GPU")
  5. if gpus:
  6. gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU
  7. tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用
  8. tf.config.set_visible_devices([gpu0],"GPU")
  9. model = models.Sequential([
  10. layers.Conv2D(16,3,padding='same',input_shape=(100,100,3),activation='relu'),
  11. layers.MaxPool2D(),
  12. layers.Conv2D(32,3,padding='same',activation='relu'),
  13. layers.MaxPool2D(),
  14. layers.Conv2D(64,3,padding='same',activation='relu'),
  15. layers.MaxPool2D(),
  16. layers.Flatten(),
  17. layers.Dense(166,activation='relu'),
  18. layers.Dense(22,activation='relu'),
  19. layers.Dense(3,activation='sigmoid')
  20. ])
  21. # 编译模型
  22. model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
  23. loss=tf.keras.losses.categorical_crossentropy,
  24. metrics=['accuracy'])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 100, 100, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 50, 50, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 50, 50, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 25, 25, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 25, 25, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 12, 12, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 9216)              0         
_________________________________________________________________
dense (Dense)                (None, 166)               1530022   
_________________________________________________________________
dense_1 (Dense)              (None, 22)                3674      
_________________________________________________________________
dense_2 (Dense)              (None, 3)                 69        
=================================================================
Total params: 1,557,349
Trainable params: 1,557,349
Non-trainable params: 0
_________________________________________________________________

训练模型:

  1. history = model.fit(x=x_train,
  2. y=y_train,
  3. validation_data=(x_test,y_test),
  4. batch_size=30,
  5. epochs=15)

模型评估:

  1. acc = history.history['accuracy']
  2. val_acc = history.history['val_accuracy']
  3. loss = history.history['loss']
  4. val_loss = history.history['val_loss']
  5. epochs_range = range(len(loss))
  6. plt.figure(figsize=(12, 4))
  7. plt.subplot(1, 2, 1)
  8. plt.plot(epochs_range, acc, label='Training Accuracy')
  9. plt.plot(epochs_range, val_acc, label='Validation Accuracy')
  10. plt.legend(loc='lower right')
  11. plt.title('Training and Validation Accuracy')
  12. plt.subplot(1, 2, 2)
  13. plt.plot(epochs_range, loss, label='Training Loss')
  14. plt.plot(epochs_range, val_loss, label='Validation Loss')
  15. plt.legend(loc='upper right')
  16. plt.title('Training and Validation Loss')
  17. plt.show()

保存模型:

model.save('./data/face_mask_model')

三、模型测试

我们上面可以看到,简单的数据集和模型已经可以使准确率达到98%了,我们接下来就可以打开摄像头,然后获取自己的图片放入模型进行预测了!

在这之前,可以简单测试一下模型:

  1. # 加载模型
  2. model = tf.keras.models.load_model('./data/face_mask_model/')
  3. # 挑选测试图片
  4. img = cv2.imread('./images/2.no/0_0_caizhuoyan_0009.jpg')
  5. plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
  6. plt.axis("off")

由于我们训练的时候,数据集是什么样的,做过什么处理,我们输入就要对其做同样的处理,才能保证预测的准确率:

  1. # 裁剪人脸
  2. img_crop = face_detect(img)
  3. # 转为Blob
  4. img_blob = imgBlob(img_crop)
  5. # reshape
  6. img_input = img_blob.reshape(1,100,100,3)
  7. # 预测
  8. result = model.predict(img_input)

预测结果:

  1. labels = os.listdir('./images/')
  2. labels[result.argmax()]

四、处理摄像头输入

就像上面说的,模型的输入要与训练时一致,所以我们同样要对其进行裁剪、格式转换、压缩、归一化的操作。

下面直接附上完整代码。

五、项目代码

权重文件和数据集:链接

大家可以根据自己需求更改代码。 

  1. import cv2
  2. import time
  3. import numpy as np
  4. import tensorflow as tf
  5. class MaskDetection:
  6. def __init__(self,mode='rasp'):
  7. """
  8. 加载人脸检测模型 和 口罩模型
  9. """
  10. gpus = tf.config.list_physical_devices("GPU")
  11. if gpus:
  12. gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU
  13. tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用
  14. tf.config.set_visible_devices([gpu0],"GPU")
  15. self.mask_model = tf.keras.models.load_model('./data/face_mask_model.h5')
  16. # 类别标签
  17. self.labels = ['正常','未佩戴','不规范']
  18. # 标签对应颜色,BGR顺序,绿色、红色、黄色
  19. self.colors = [(0,255,0),(0,0,255),(0,255,255)]
  20. # 获取label显示的图像
  21. self.zh_label_img_list = self.getLabelPngList()
  22. def getLabelPngList(self):
  23. """
  24. 获取本地label显示的图像的列表
  25. """
  26. overlay_list = []
  27. for i in range(3):
  28. fileName = './label_img/%s.png' % (i)
  29. overlay = cv2.imread(fileName,cv2.COLOR_RGB2BGR)
  30. overlay = cv2.resize(overlay,(0,0), fx=0.3, fy=0.3)
  31. overlay_list.append(overlay)
  32. return overlay_list
  33. def imageBlob(self,face_region):
  34. """
  35. 将图像转为blob
  36. """
  37. if face_region is not None:
  38. blob = cv2.dnn.blobFromImage(face_region,1,(100,100),(104,117,123),swapRB=True)
  39. blob_squeeze = np.squeeze(blob).T
  40. blob_rotate = cv2.rotate(blob_squeeze,cv2.ROTATE_90_CLOCKWISE)
  41. blob_flip = cv2.flip(blob_rotate,1)
  42. # 对于图像一般不用附属,所以将它移除
  43. # 归一化处理
  44. blob_norm = np.maximum(blob_flip,0) / blob_flip.max()
  45. return blob_norm
  46. else:
  47. return None
  48. def detect(self):
  49. """
  50. 识别
  51. """
  52. face_detector = cv2.dnn.readNetFromCaffe('./weights/deploy.prototxt.txt','./weights/res10_300x300_ssd_iter_140000.caffemodel')
  53. cap = cv2.VideoCapture(0)
  54. frame_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  55. frame_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  56. frameTime = time.time()
  57. videoWriter = cv2.VideoWriter('./record_video/out'+str(time.time())+'.mp4', cv2.VideoWriter_fourcc(*'H264'), 10, (960,720))
  58. while True:
  59. ret,frame = cap.read()
  60. frame = cv2.flip(frame,1)
  61. frame_resize = cv2.resize(frame,(300,300))
  62. img_blob = cv2.dnn.blobFromImage(frame_resize,1.0,(300,300),(104.0, 177.0, 123.0),swapRB=True)
  63. face_detector.setInput(img_blob)
  64. detections = face_detector.forward()
  65. num_of_detections = detections.shape[2]
  66. # 记录人数(框)
  67. person_count = 0
  68. # 遍历多个
  69. for index in range(num_of_detections):
  70. # 置信度
  71. detection_confidence = detections[0,0,index,2]
  72. # 挑选置信度
  73. if detection_confidence>0.5:
  74. person_count+=1
  75. # 位置坐标 记得放大
  76. locations = detections[0,0,index,3:7] * np.array([frame_w,frame_h,frame_w,frame_h])
  77. l,t,r,b = locations.astype('int')
  78. # 裁剪人脸区域
  79. face_region = frame[t:b,l:r]
  80. # 转为blob格式
  81. blob_norm = self.imageBlob(face_region)
  82. if blob_norm is not None:
  83. # 模型预测
  84. img_input = blob_norm.reshape(1,100,100,3)
  85. result = self.mask_model.predict(img_input)
  86. # softmax分类器处理
  87. result = tf.nn.softmax(result[0]).numpy()
  88. # 最大值索引
  89. max_index = result.argmax()
  90. # 最大值
  91. max_value = result[max_index]
  92. # 标签
  93. label = self.labels[max_index]
  94. # 对应中文标签
  95. overlay = self.zh_label_img_list[max_index]
  96. overlay_h,overlay_w = overlay.shape[:2]
  97. # 覆盖范围
  98. overlay_l,overlay_t = l,(t - overlay_h-20)
  99. overlay_r,overlay_b = (l + overlay_w),(overlay_t+overlay_h)
  100. # 判断边界
  101. if overlay_t > 0 and overlay_r < frame_w:
  102. overlay_copy=cv2.addWeighted(frame[overlay_t:overlay_b, overlay_l:overlay_r ],1,overlay,20,0)
  103. frame[overlay_t:overlay_b, overlay_l:overlay_r ] = overlay_copy
  104. cv2.putText(frame, str(round(max_value*100,2))+"%", (overlay_r+20, overlay_t+40), cv2.FONT_ITALIC, 0.8, self.colors[max_index], 2)
  105. # 人脸框
  106. cv2.rectangle(frame,(l,t),(r,b),self.colors[max_index],5)
  107. now = time.time()
  108. fpsText = 1 / (now - frameTime)
  109. frameTime = now
  110. cv2.putText(frame, "FPS: " + str(round(fpsText,2)), (20, 40), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)
  111. cv2.putText(frame, "Person: " + str(person_count), (20, 60), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)
  112. videoWriter.write(frame)
  113. cv2.imshow('demo',frame)
  114. if cv2.waitKey(10) & 0xFF == ord('q'):
  115. break
  116. videoWriter.release()
  117. cap.release()
  118. cv2.destroyAllWindows()
  119. mask_detection = MaskDetection()
  120. mask_detection.detect()

效果如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/650384
推荐阅读
相关标签
  

闽ICP备14008679号