python口罩识别,口罩佩戴检测算法

  python口罩识别,口罩佩戴检测算法

  本文主要介绍基于Python的口罩佩戴检测。在这个实验中,我们想建立一个目标检测模型,可以识别图片中的人是否戴着面具。本文通过一个示例代码向您做了非常详细的介绍。请有需要的朋友参考一下。

  00-1010戴口罩测试题目背景1.1实验介绍1.2实验要求1.3实验环境1.4实验思路2实验内容2.1已知文件和数据集2.2图片大小调整2.3训练所需的批量数据集2.4调用MTCNN2.5加载预训练模型MobileNet2.6训练模型2.6.1加载并保存2 .6.2手动调整学习速率。2.6.3早停法。2.6.4无序的训练数据。2.6.5培训模式。三个算法描述。3.1 MTCNN3.2 MobileNet。四个解决方案结果。五比较分析。六种体验和感受。

  

目录

  

口罩佩戴检测

  

一 题目背景

  今年,一种新型冠状病毒席卷全球,给人们带来了重大的生命和财产损失。对这种传染性病毒的有效防御是积极佩戴口罩。中国也对此采取了严肃的措施,要求人们在公共场所戴口罩。在这个实验中,我们将建立一个目标检测的模型,它可以识别图片中的人是否戴着面具。

  

1.1 实验介绍

  建立深度学习模型,检测图片中的人是否戴着面具,尽可能调整好。了解经典模型MTCNN和MobileNet的结构。训练方法。

  

1.2 实验要求

  试用重要的python软件包:

  将cv2作为cv导入

  将numpy作为np导入

  将matplotlib.pyplot作为plt导入

  从tensorflow.keras.callbacks导入模型检查点,ReduceLROnPlateau,早期停止

  我担心GPU平台不够长,所以我在自己的电脑上搭建了一个配套的实验环境。由于CUDA版本的电脑显卡比较老,本地配置如下:

  python:3.8 tensor flow-GPU:2.3 . 0 keras : 2.7 . 0

  

1.3 实验环境

  目标检测的任务可以分为两部分:目标识别和位置检测。通常,特征提取需要由特殊的特征提取神经网络来完成,例如VGG、MobileNet、ResNet.这些特征提取网络通常被称为Backbone.而BackBone之后是全连接层***(FC)***来执行分类任务。然而,FC在确定目标位置方面很薄弱。经过算法的发展,目前FC的作用主要由特定的功能网络来代替,比如Mask-Rcnn、SSD、YOLO.我们选择充分利用现有的人脸检测模型,然后训练一个面具识别模型,这样可以增加训练支出,增强模型的准确性。

  常规

  目标检测:

  

  本次案例:

  

  图1 实验口罩佩戴检测流程

  

  

二 实验内容

  

  

2.1 已知文件与数据集

  首先,导入已经写好的python文件并对数据集进行处理。

  

  • image 文件夹:图片分成两类,戴口罩的和没有戴口罩的
  • train.txt: 存放的是 image 文件夹下对应图片的标签
  • keras_model_data 文件夹:存放 keras 框架相关预训练好的模型

  

  

2.2 图片尺寸调整

  将图片尺寸调整到网络输入的图片尺寸

  

  

2.3 制作训练时需要用到的批量数据集

  图片生成器的主要方法:

  

  • fit(x, augment=False, rounds=1):计算依赖于数据的变换所需要的统计信息(均值方差等)。
  • flow(self, X, y, batch_size=32, shuffle=True, seed=None, save_to_dir=None, save_prefix='', save_format='png'):接收 Numpy 数组和标签为参数,生成经过数据提升或标准化后的 batch 数据,并在一个无限循环中不断的返回 batch 数据。
  • flow_from_directory(directory): 以文件夹路径为参数,会从路径推测 label,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生 batch 数据。

  结果:

  

Found 693 images belonging to 2 classes.
Found 76 images belonging to 2 classes.
{'mask': 0, 'nomask': 1}
{0: 'mask', 1: 'nomask'}

  

  

  

2.4 调用MTCNN

  通过搭建 MTCNN 网络实现人脸检测

  

  • keras_py/mtcnn.py 文件是在搭建 MTCNN 网络。
  • keras_py/face_rec.py 文件是在绘制人脸检测的矩形框。

  

这里直接使用现有的表现较好的 MTCNN 的三个权重文件,它们已经保存在datasets/5f680a696ec9b83bb0037081-momodel/data/keras_model_data 文件夹下

  

  

  

2.5 加载预训练模型MobileNet

  

# 加载 MobileNet 的预训练模型权重

  weights_path = basic_path + keras_model_data/mobilenet_1_0_224_tf_no_top.h5

  

  

2.6 训练模型

  

  

2.6.1 加载和保存

  为了避免训练过程中遇到断电等突发事件,导致模型训练成果无法保存。我们可以通过 ModelCheckpoint 规定在固定迭代次数后保存模型。同时,我们设置在下一次重启训练时,会检查是否有上次训练好的模型,如果有,就先加载已有的模型权重。这样就可以在上次训练的基础上继续模型的训练了。

  

  

2.6.2 手动调整学习率

  学习率的手动设置可以使模型训练更加高效。这里我们设置当模型在三轮迭代后,准确率没有上升,就调整学习率。

  

# 学习率下降的方式,acc三次不下降就下降学习率继续训练

  reduce_lr = ReduceLROnPlateau(

   monitor=accuracy, # 检测的指标

   factor=0.5, # 当acc不下降时将学习率下调的比例

   patience=3, # 检测轮数是每隔三轮

   verbose=2 # 信息展示模式

   )

  

  

2.6.3 早停法

  当我们训练深度学习神经网络的时候通常希望能获得最好的泛化性能。但是所有的标准深度学习神经网络结构如全连接多层感知机都很容易过拟合。当网络在训练集上表现越来越好,错误率越来越低的时候,就极有可能出现了过拟合。早停法就是当我们在检测到这一趋势后,就停止训练,这样能避免继续训练导致过拟合的问题。

  

early_stopping = EarlyStopping(

   monitor=val_accuracy, # 检测的指标

   min_delta=0.0001, # 增大或减小的阈值

   patience=3, # 检测的轮数频率

   verbose=1 # 信息展示的模式

   )

  

  

2.6.4 乱序训练数据

  打乱txt的行,这个txt主要用于帮助读取数据来训练,打乱的数据更有利于训练。

  

 np.random.seed(10101)

   np.random.shuffle(lines)

   np.random.seed(None)

  

  

2.6.5 训练模型

  一次训练集大小设定为64,优化器使用Adam,初始学习率设定为0.001,优化目标为accuracy,总的学习轮次设定为20轮。(通过多次实验测定,在这些参数条件下,准确率较高)

  

# 一次的训练集大小

  batch_size = 64

  # 编译模型

  model.compile(loss=binary_crossentropy, # 二分类损失函数

   optimizer=Adam(lr=0.001), # 优化器

   metrics=[accuracy]) # 优化目标

  # 训练模型

  history = model.fit(train_generator,

   epochs=20, # epochs: 整数,数据的迭代总轮数。

   # 一个epoch包含的步数,通常应该等于你的数据集的样本数量除以批量大小。

   steps_per_epoch=637 // batch_size,

   validation_data=test_generator,

   validation_steps=70 // batch_size,

   initial_epoch=0, # 整数。开始训练的轮次(有助于恢复之前的训练)。

   callbacks=[checkpoint_period, reduce_lr])

  

  

三 算法描述

  

  

3.1 MTCNN

  

  • 三阶段的级联(cascaded)架构
  • coarse-to-fine 的方式
  • new online hard sample mining 策略
  • 同时进行人脸检测和人脸对齐
  • state-of-the-art 性能

  

  图2 MTCNN架构

  

  

3.2 MobileNet

  

  图3 MobileNet架构

  MobileNet的网络结构如图3所示。首先是一个3x3的标准卷积,然后后面就是堆积depthwise separable convolution,并且可以看到其中的部分depthwise convolution会通过strides=2进行down sampling。然后采用average poolingfeature变成1x1,根据预测类别大小加上全连接层,最后是一个softmax层。

  

  

四 求解结果

  最终确定最佳取值为batch_size=64lr=0.0001epochs=20,其它参数如下,连续训练两次,可以获得最佳结果。此处仅展示两个参数条件下的结果作为对比

  

# 一次的训练集大小

  batch_size = 64

  # 编译模型

  model.compile(loss=binary_crossentropy, # 二分类损失函数

   optimizer=Adam(lr=0.001), # 优化器

   metrics=[accuracy]) # 优化目标

  # 训练模型

  history = model.fit(train_generator,

   epochs=20, # epochs: 整数,数据的迭代总轮数。

   # 一个epoch包含的步数,通常应该等于你的数据集的样本数量除以批量大小。

   steps_per_epoch=637 // batch_size,

   validation_data=test_generator,

   validation_steps=70 // batch_size,

   initial_epoch=0, # 整数。开始训练的轮次(有助于恢复之前的训练)。

   callbacks=[checkpoint_period, reduce_lr])

  条件1: 取batch_size=48, lr=0.001epochs=20,对训练之后的模型进行测试,得到结果如下:

  

  图4 条件1 loss曲线

  由loss曲线可以看出,随着训练迭代次数的加深,验证集上的损失在逐渐的减小,最终稳定在0.2左右;而在训练集上loss始终在0附近。

  

  图5 条件1 acc曲线

  从验证集和测试集的准确率变化曲线上可以看出,随着训练轮次的增加,验证集的准确率逐渐上升,最终稳定在96%左右,效果还是不错的。

  

  图6 条件1 测试样例1

  使用样例照片进行测试,首先人脸识别部分顺利识别到了五张人脸,但是口罩识别部分将一个没有带口罩的人识别成了带着口罩的人,说明还有进步空间,实际错误率达到了20%。

  

  图7 条件1 测试样例2

  另一张样例照片的测试结果同样是人脸识别部分没有出现问题,正确识别到了四张人脸,但是同样将一个没有带口罩的人识别成了带有口罩的人。

  平台测试:

  

  后续通过调整各项参数并打乱测试集和训练集图片顺序来进行了多次实验,最终确定的最佳状态如下:

  条件2: 取batch_size=64, lr=0.0001epochs=20,对训练之后的模型进行测试,得到结果如下:

  

  图8 条件2 loss曲线

  观察准确率曲线可以看出,在该条件下,验证集上的准确率最终稳定在98%附近,效果非常的好,说明我们做出的一些优化还是具有一定效果的。

  

  图9 条件2 acc曲线

  观察此条件下的loss曲线可以看到最终验证集的loss稳定在0.2左右,训练集的loss非常小,基本趋近于0

  

  图10 条件2 测试样例1

  使用两张测试样例对模型进行检测,第一张图片所有检测点均正确,正确识别出了五张人脸并且口罩佩戴检测均正确,识别正确率100%。

  

  图11 条件2 测试样例2

  第二章测试样例上,正确识别出了4张人脸并且口罩佩戴检测结果均正确。

  两张测试样例上所有检测点检测结果均正确,说明在此参数条件下,模型识别效果较好,达到了口罩佩戴检测的要求。

  平台测试:

  

  条件3:

  使用更多测试样例发现MTCNN人脸识别部分存在不能正确识别人脸的问题,故通过多次实验和测试,修改了mask_rec()的门限函数权重self.threshold,由原来的self.threshold = [0.5,0.6,0.8] 修改为self.threshold = [0.4,0.15,0.65]

  在本地使用更多自选图片进行测试,发现人脸识别准确率有所提升。在条件2训练参数不变的情况下,使用同一模型进行平台测试,结果如下:

  

  平台测试成绩有所提升。

  条件4:

  继续调整mask_rec()的门限函数权重self.threshold,通过系统测试反馈来决定门限函数的权重,通过多次测试,由原来的self.threshold = [0.4,0.15,0.65] 修改为self.threshold = [0.4,0.6,0.65]

  平台测试,结果如下:

  

  平台测试成绩有所提升,达到95分。

  

为了达到条件4所展示的效果,对门限函数的数值进行了大量的尝试,根据提交测试的反馈结果,最终确定数值为条件4时,可以达到最优。由于不知道后台测试图片是什么且没有反馈数据,所以最终再次修改人脸识别的门限函数或者修改参数重新训练口罩识别模型依旧没有提升。

  

  

  

五 比较分析

  验证集准确率测试样例结果平台成绩条件196%7/977.5条件298%9/988.33333334条件398%9/990条件498%9/995

  最终通过不断调试与优化算法,得到了95分的平台成绩。

  

  

六 心得与感想

  本次实验过程中主要使用了keras方法进行训练,由于初次使用这些方法,所以前期实现的过程相对困难。最初我想通过调用GPU资源来进行训练,所以给自己的电脑配套安装了tensorflow-gpu、CUDA等等配套的软件和包,由于个人电脑的显卡版本较老,所以安装的过程也是非常的曲折。好在最终安装好了所有的东西,但是由于显卡显存比较小,所以bath_size大小一直上不去,最大只能给到32,不过影响也不大。调整参数的过程花费了很多的时间,优化算法也花费了很多的时间。之后又对门限函数进行了修改,虽然过程非常的辛苦,但最终的结果还是很不错的,最终整体达到95分,在两张给定的测试样例上所有检测点都是正确的,由于不知道平台的五张检测照片是什么,所以不知道到底出错在哪里,希望之后平台可以反馈一些修改意见~。总的来说在过程中收获还是很大的,受益匪浅。

  训练源代码:

  

import warnings

  # 忽视警告

  warnings.filterwarnings(ignore)

  import os

  import matplotlib

  import cv2 as cv

  import numpy as np

  import matplotlib.pyplot as plt

  from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping

  from tensorflow.keras.applications.imagenet_utils import preprocess_input

  from tensorflow.keras import backend as K

  from tensorflow.keras.optimizers import Adam

  K.image_data_format() == channels_last

  from keras_py.utils import get_random_data

  from keras_py.face_rec import mask_rec

  from keras_py.face_rec import face_rec

  from keras_py.mobileNet import MobileNet

  from tensorflow.keras.preprocessing.image import ImageDataGenerator

  # 数据集路径

  basic_path = "./datasets/5f680a696ec9b83bb0037081-momodel/data/"

  def letterbox_image(image, size): # 调整图片尺寸,返回经过调整的照片

   new_image = cv.resize(image, size, interpolation=cv.INTER_AREA)

   return new_image

  read_img = cv.imread("test1.jpg")

  print("调整前图片的尺寸:", read_img.shape)

  read_img = letterbox_image(image=read_img, size=(50, 50))

  print("调整前图片的尺寸:", read_img.shape)

  def processing_data(data_path, height, width, batch_size=32, test_split=0.1): # 数据处理,batch_size默认大小为32

   train_data = ImageDataGenerator(

   # 对图片的每个像素值均乘上这个放缩因子,把像素值放缩到0和1之间有利于模型的收敛

   rescale=1. / 255,

   # 浮点数,剪切强度(逆时针方向的剪切变换角度)

   shear_range=0.1,

   # 随机缩放的幅度,若为浮点数,则相当于[lower,upper] = [1 - zoom_range, 1+zoom_range]

   zoom_range=0.1,

   # 浮点数,图片宽度的某个比例,数据提升时图片水平偏移的幅度

   width_shift_range=0.1,

   # 浮点数,图片高度的某个比例,数据提升时图片竖直偏移的幅度

   height_shift_range=0.1,

   # 布尔值,进行随机水平翻转

   horizontal_flip=True,

   # 布尔值,进行随机竖直翻转

   vertical_flip=True,

   # 在 0 和 1 之间浮动。用作验证集的训练数据的比例

   validation_split=test_split

   )

   # 接下来生成测试集,可以参考训练集的写法

   test_data = ImageDataGenerator(

   rescale=1. / 255,

   validation_split=test_split)

   train_generator = train_data.flow_from_directory(

   # 提供的路径下面需要有子目录

   data_path,

   # 整数元组 (height, width),默认:(256, 256)。 所有的图像将被调整到的尺寸。

   target_size=(height, width),

   # 一批数据的大小

   batch_size=batch_size,

   # "categorical", "binary", "sparse", "input" 或 None 之一。

   # 默认:"categorical",返回one-hot 编码标签。

   class_mode=categorical,

   # 数据子集 ("training" 或 "validation")

   subset=training,

   seed=0)

   test_generator = test_data.flow_from_directory(

   data_path,

   target_size=(height, width),

   batch_size=batch_size,

   class_mode=categorical,

   subset=validation,

   seed=0)

   return train_generator, test_generator

  # 数据路径

  data_path = basic_path + image

  # 图像数据的行数和列数

  height, width = 160, 160

  # 获取训练数据和验证数据集

  train_generator, test_generator = processing_data(data_path, height, width)

  # 通过属性class_indices可获得文件夹名与类的序号的对应字典。

  labels = train_generator.class_indices

  print(labels)

  # 转换为类的序号与文件夹名对应的字典

  labels = dict((v, k) for k, v in labels.items())

  print(labels)

  pnet_path = "./datasets/5f680a696ec9b83bb0037081-momodel/data/keras_model_data/pnet.h5"

  rnet_path = "./datasets/5f680a696ec9b83bb0037081-momodel/data/keras_model_data/rnet.h5"

  onet_path = "./datasets/5f680a696ec9b83bb0037081-momodel/data/keras_model_data/onet.h5"

  # 加载 MobileNet 的预训练模型权重

  weights_path = basic_path + keras_model_data/mobilenet_1_0_224_tf_no_top.h5

  # 图像数据的行数和列数

  height, width = 160, 160

  model = MobileNet(input_shape=[height,width,3],classes=2)

  model.load_weights(weights_path,by_name=True)

  print(加载完成...)

  def save_model(model, checkpoint_save_path, model_dir): # 保存模型

   if os.path.exists(checkpoint_save_path):

   print("模型加载中")

   model.load_weights(checkpoint_save_path)

   print("模型加载完毕")

   checkpoint_period = ModelCheckpoint(

   # 模型存储路径

   model_dir + ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5,

   # 检测的指标

   monitor=val_acc,

   # ‘auto,‘min,‘max中选择

   mode=max,

   # 是否只存储模型权重

   save_weights_only=False,

   # 是否只保存最优的模型

   save_best_only=True,

   # 检测的轮数是每隔2轮

   period=2

   )

   return checkpoint_period

  checkpoint_save_path = "./results/last_one88.h5"

  model_dir = "./results/"

  checkpoint_period = save_model(model, checkpoint_save_path, model_dir)

  # 学习率下降的方式,acc三次不下降就下降学习率继续训练

  reduce_lr = ReduceLROnPlateau(

   monitor=accuracy, # 检测的指标

   factor=0.5, # 当acc不下降时将学习率下调的比例

   patience=3, # 检测轮数是每隔三轮

   verbose=2 # 信息展示模式

   )

  early_stopping = EarlyStopping(

   monitor=val_accuracy, # 检测的指标

   min_delta=0.0001, # 增大或减小的阈值

   patience=3, # 检测的轮数频率

   verbose=1 # 信息展示的模式

   )

  # 一次的训练集大小

  batch_size = 64

  # 图片数据路径

  data_path = basic_path + image

  # 图片处理

  train_generator, test_generator = processing_data(data_path, height=160, width=160, batch_size=batch_size, test_split=0.1)

  # 编译模型

  model.compile(loss=binary_crossentropy, # 二分类损失函数

   optimizer=Adam(lr=0.001), # 优化器

   metrics=[accuracy]) # 优化目标

  # 训练模型

  history = model.fit(train_generator,

   epochs=20, # epochs: 整数,数据的迭代总轮数。

   # 一个epoch包含的步数,通常应该等于你的数据集的样本数量除以批量大小。

   steps_per_epoch=637 // batch_size,

   validation_data=test_generator,

   validation_steps=70 // batch_size,

   initial_epoch=0, # 整数。开始训练的轮次(有助于恢复之前的训练)。

   callbacks=[checkpoint_period, reduce_lr])

  # 保存模型

  model.save_weights(model_dir + temp.h5)

  plt.plot(history.history[loss],label = train_loss)

  plt.plot(history.history[val_loss],r,label = val_loss)

  plt.legend()

  plt.show()

  plt.plot(history.history[accuracy],label = acc)

  plt.plot(history.history[val_accuracy],r,label = val_acc)

  plt.legend()

  plt.show()

  

  到此这篇关于基于Python实现的口罩佩戴检测的文章就介绍到这了,更多相关python口罩佩戴检测内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: