人脸口罩识别的项目总结
最近一段时间,国内部分地区又爆发了新冠疫情,传播速度很快。但最近天气炎热,人们戴口罩的自觉性不如从前了,在商场等公共场所,需要专门的人员去提醒顾客佩戴好口罩。因此萌发了使用计算机视觉来做一个口罩识别模型的想法,以下是对近期工作的总结。
1、初步想法
一开始,我把这个当成了一个分类模型,使用佩戴口罩的人脸图像和不佩戴口罩的人脸图像来训练一个卷积神经网络,只要模型在测试集上的准确度达到要求即可。具体做法可以看这一篇文章。这篇文章最后训练的模型完全可以识别人脸是否佩戴口罩(在测试集上的分类准确度达到了0.9698)。
但还是存在以下几个问题:
- 模型是用裁剪后的人脸训练的,那么在实际的应用中也应当使用裁剪后的人脸。
- 模型只能识别佩戴口罩和不佩戴口罩,却不能识别当前口罩是否佩戴正确,如下图所示。
- (最大的坑)这个文章用的测试集中的图片有很多在训练集和验证集中已经用过了,有部分data leakage的问题,实际的模型准确度不一定能达到0.9698。

# 在测试集上的表现
model.evaluate(X_test, y_test)
31/31 [==============================] - 0s 9ms/step - loss: 0.2265 - accuracy: 0.9698
[0.22651898860931396, 0.9697580933570862]
2、进一步的工作
因为上一次用的数据集中有数据泄露,整个数据集较大,因此我在这一部分更换了数据集,从新训练模型。依然将卷积神经网络当成一个图像分类的模型。具体操作可以看这一篇文章。这里的主要改进是以下几个方面:
- 更换了数据集,自己来划分训练集、验证集和测试集,避免了数据泄露的可能。
- 该数据集中增加了“口罩佩戴不正确”的这个类别
- 使用了opencv中自带
CascadeClassifier()的来裁剪图片中的人脸区域,从而让模型具备在实际中应用的能力。 - 使用了opencv调用摄像头采集的图像,交予模型预测,并实时返回信息。
经过以上的改进后,至少解决了第一节中的数据泄露问题,模型在测试集上的准确度是真实可信的,
# 模型在测试集上的表现
18/18 [==============================] - 0s 25ms/step - loss: 0.4023 - accuracy: 0.8636


但这篇文章训练得到的模型依然有以下几个问题:
-
虽然使用了opencv中的人脸区域检测,但是opencv对佩戴口罩的人脸区域检测不准确,甚至不能检测到佩戴口罩的人脸,大大降低了实际中的应用价值
-
模型对口罩佩戴不正确的识别效果依然不好
3、进阶
既然这里我都用了卷积神经网络来实现图像的分类,为什么不能也做出图像中目标定位呢?这让我想到了使用yolo(you only look once)算法,可以同时实现图像定位与分类。
但yolo算法实现有点困难,因此我在github上找了几个yolo算法的实现:yolov5、keras-yolov3、tensorflow-yolov3等等,最后选择了yolov5,因为这个项目给的操作说明是最全面的,也是我试了很多次之后最不容易报错的。
具体的做法可以看这个项目本身的操作说明,下面简单的给出我自己的操作步骤:
(1)配置环境
建议这里新建一个虚拟环境,按照操作说明,执行以下命令,即可自动安装所需要的第三方库
pip install -r requirements.txt
对于torch的安装,应当注意与自己的cuda版本匹配,并使用pip安装,不要使用conda安装,conda安装的是torch的cpu版本,这里十分感谢这篇文章给的帮助。
(2)准备数据集
其实这里主要是准备每个图像对应的label文件,按照这个模板来基本不会出现什么问题,基本的格式是:
class x_center y_center width height # 0 < x_center, y_center, width, height<1
下面给出我生成label的代码

import os
import xml.etree.ElementTree as ET
import cv2
from pathlib import Path
images_path = # 数据集图片文件的目录
annotations_path = # 数据集中标注文件的目录
for image_name in os.listdir(images_path):
image_name_nosuffix = os.path.splitext(image_name)[0]
file = open(image_name_nosuffix + '.txt', 'w')
image_path = os.path.join(images_path, image_name)
annotation_path = os.path.join(annotations_path, image_name_nosuffix + '.xml')
root = ET.parse(annotation_path).getroot() # 读取xml文件
width = root.find('size').find('width').text # 照片宽度
height = root.find('size').find('height').text # 照片高度
for obj in root.iter('object'): # 遍历所有目标框
name = obj.find('name').text # 获取目标框名称,即label名
xmlbox = obj.find('bndbox') # 找到框目标
xmin = int(xmlbox.find('xmin').text) / int(width)
ymin = int(xmlbox.find('ymin').text) / int(height)
xmax = int(xmlbox.find('xmax').text) / int(width)
ymax = int(xmlbox.find('ymax').text) / int(height)
X_center = (xmin + xmax) / 2
y_ceter = (ymin + ymax) / 2
obj_width = xmax - xmin
obj_height = ymax - ymin
if name == 'without_mask':
label = '0'
elif name == 'mask_weared_incorrect':
label = '1'
elif name == 'with_mask':
label = '2'
file.write(label + ' ')
file.write(str(X_center) + ' ' + str(y_ceter) + ' ' + str(obj_width) + ' ' + str(obj_height))
file.write('\n')
因为口罩佩戴不正确这个类别始终识别效果不好,因此我后期又追加了一部分图像进去。并用labelimg对图像进行标记。


修改dataset.yaml,这个文件的作用是索引,让算法找到数据集的位置,按照操作说明来做,不会出现问题,这是我的mask_face.yaml文件
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../datasets/mask # dataset root dir
train: images/train # train images (relative to 'path')
val: images/val # val images (relative to 'path')
test: images/test # test images (optional)
# Classes
nc: 3 # number of classes
names: ['without_mask','mask_weared_incorrect','with_mask'] # class names
(3)训练
同样按照操作说明,在终端输入以下命令即可训练,这里是在预训练模型yolov5s.pt的基础上继续训练的。
python train.py --img 640 --batch 2 --epochs 30 --data mask_face.yaml --weights yolov5s.pt
注意:因为笔者的显卡是GTX 960M(五年前的老笔记本了),显存只有2G,因此我的batch只能设置成2张,设置成4张,显卡内存就不够用了。
输入完毕命令后,就静静等待结果就好,最后在runs/train/exp11中给出训练的结果。



然后通过以下命令即可在测试集上测试并返回图像结果:
python detect.py --source F:\PythonProjects\datasets\mask\images\test --weights runs/train/exp11/weights/best.pt --device 0



在摄像头上测试:
python detect.py --source 0 --device 0 --weight runs/train/exp11/weights/best.pt



效果基本上达到了我的预期,可以很好的识别人脸位置并判断是否佩戴口罩以及口罩佩戴是否正确。
最后考虑到一些人会故意用手和其他物体挡住嘴巴和鼻子骗过识别,所以又手贱尝试了以下用其他的东西遮住嘴巴和鼻子,效果。。。。。
用手挡住鼻子和嘴巴,模型判断没有口罩

用纸巾挡住:模型判断戴上了口罩

用书本挡住:模型判断戴上了口罩

判断根本原因是在训练的时候并没有使用这种故意用其他物件遮住嘴巴和鼻子的照片,因此在这种情形下应当增加数据集,扩充这种故意遮挡的图片,以提高模型性能。但因为这类图片不是那么容易寻找,所以我想的方法是提高识别的可信度阈值。一般来说,人们正常佩戴口罩时,模型给出的可信度在0.8以上,而使用其他物体遮挡时,可信度低于0.8,因此这里就将阈值设置成0.8,这样就可以区分是否真的佩戴口罩了。
加上--conf-thres 0.8这个参数后,重新启动摄像头
python detect.py --source 0 --device 0 --weight runs/train/exp11/weights/best.pt --conf-thres 0.8
这个模型就什么也不返回了,只能算一个折中之计,根本解决办法还是在扩充数据集。

总结:本文训练出的模型基本上可以识别出人们是否正确佩戴口罩,在实际种具有应用的潜力。但要真正部署到实际中还应当增加数据集,扩充一些故意遮挡的图片。这个项目暂时就到这里啦,后面有空还会继续优化模型的。
最近delta变异的传播速度让人震惊,大家还是主动佩戴口罩吧,增强防范意识,尽可能减轻医务工作者的劳动。
本文总结了人脸口罩识别项目,从初步的卷积神经网络分类模型到使用YOLO算法实现目标定位。通过更换数据集、解决数据泄露问题,提升了模型准确性。然而,模型在口罩佩戴不正确及遮挡情况下的识别仍有待改善,需扩大数据集并调整阈值以增强实际应用效果。
1823

被折叠的 条评论
为什么被折叠?



