赞
踩
在当今全球范围内,新冠疫情对我们的生活方式带来了巨大的改变。在公共场所,佩戴口罩成为了常态,以保护我们自己和他人的健康安全。然而,这也给人脸识别技术带来了新的挑战。如何准确地辨别佩戴口罩的人成为了一个重要的问题。
本文介绍的一种基于Tensorflow、SDD和Python的人脸口罩识别系统,结合了深度学习技术和计算机视觉算法,能够高效地检测人脸并准确地判断是否佩戴口罩。通过使用这个系统,我们可以更好地管理公共场所的出入口,确保员工、顾客和访客的健康安全。
当然,您可以在文末下载本系统的全部工程源码(包括Web demo)进行个人二次开发。
本部分包括系统整体结构图和系统流程图。
系统整体结构如图所示。
系统流程如图所示。
本部分包括 Python 环境和Anaconda 环境。
需要 Python 3.5 及以上配置,在 Windows 环境下推荐下载 Anaconda 完成 Python 所需的配置,下载地址:https://www.anaconda.com/,也可以下载虚拟机在 Linux 环境下运行代码。
打开 Anaconda Prompt,输入清华仓库镜像。
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config –set show_channel_urls yes
创建 Python3.5 的环境,名称为 TensorFlow:
conda create -n tensorflow python=3.5
有需要确认的地方,都输入 y。
在 Anaconda Prompt 中激活 TensorFlow 环境:
activate tensorflow
安装 CPU 版本的 TensorFlow:
pip install –upgrade --ignore-installed tensorflow
安装完毕。
本项目包括 3 个模块:数据预处理、模型构建及算法实现、模型生成,下面分别给出各模块的功能介绍及相关代码。
WIDER Face 和 MAFA 这两个数据集包含了大量的人脸及人脸佩戴口罩的图片,但只使用WIDER Face 数据集,所含其他遮挡脸部的图片较少(如用手挡住脸部),会出现识别偏差的情况。因此,从 WIDER Face 数据集中选取 3894 张图片、MAFA 数据集中选取 4064张图片,包含戴口罩、不戴口罩、用其他物体遮挡面部共 8000 张图片进行训练。
数据集下载链接:https://pan.baidu.com/s/1nsQf_Py5YyKm87-8HiyJeQ 提取码:eyfz
数据加载进模型之后,需要定义模型结构,并进行算法实现。
1. 定义模型结构
定义的架构共 28 个卷积层,其中有 8 个主干层,即特征提取层;20 个定位和分类层。每个卷积层的通道数是 32、64、128 三种,为了能够更好地运行在终端设备上,模型设计的较小,共 101.5 万个参数,输入为 260*260 大小的图片。
2. 算法实现
基于 SSD 检测模型推理,实现以下三步:生成全部的 anchor、网络输出值根据全部的anchors 解码、NMS(非极大值抑制)即可,即以下三个函数:
function anchorGenerator()
、function decodeBBox()
、function nonMaxSuppression()
训练目标检测模型,最重要的是合理设置 anchor 的大小和宽高比。所谓 anchor 是指在图像上预设好的不同大小,不同长宽比的参照框。假设一个 256*256 大小的图片,经过 64、128 和 256 倍下采样,会产生 4X4、2X2、1X1 大小的特征图,每个点上都设置三个不同大小的 anchor。在实际的 SSD 模型中,300X300 的输入下,anchor 数量也特别多,在38X38、19X19、10X10、5X5、3X3、1X1 的六个特征图上,每个点分别设置 4、6、6、6、6、4 个不同大小和长宽比的 anchor,一共有 38X38X4+ 19X19X6+ 10X10X6+ 5X5X6+3X3X4+ 1X1X4= 8732个 anchor。借助卷积神经网络,可直接输出每个 anchor 是否包含(与物体有较大重叠,也就是 IoU 较大)物体,以及被检测物体相对本 anchor 的中心点偏移、长宽比例。
在训练的时候,也就是给每张图片物体的 Bounding Box,相对于 anchor 进行编码,如果物体 Bounding Box 与某个 anchor 的 IoU 较大,例如,大于 0.5 认为是正样本,否则是负样本。
对于检测网络,有的实现,是用没有归一化的坐标,例如,anchor 设置为(30, 42)尺度大小,但是,有的算法实现,是将坐标和物体框的宽高比,除以图片的宽和高。如果图片都是正方形的,那归一化后宽高比没有变化,如果是 1080P 分辨率的摄像头,那么长宽比是16:9。本来就小的宽度,再除以 1920,而高度除以 1080,会导致归一化后的人脸高度是宽度的 2 倍左右。归一化处理即:
根据数据的分布,将五个定位层的 anchor 的宽高比统一设置为 1,0.62, 0.42。(转换为高宽比,也就是约 1,1.6:1,2.4:1)。
3. 代码实现
# -*- coding:utf-8 -*- import cv2 import time import argparse import numpy as np from PIL import Image from keras.models import model_from_json from utils.anchor_generator import generate_anchors from utils.anchor_decode import decode_bbox from utils.nms import single_class_non_max_suppression from load_model.tensorflow_loader import load_tf_model, tf_inference # 载入模型 sess, graph = load_tf_model('models/face_mask_detection.pb') # anchor configuration #特征图大小 feature_map_sizes = [[33, 33], [17, 17], [9, 9], [5, 5], [3, 3]] #anchor大小 anchor_sizes = [[0.04, 0.056], [0.08, 0.11], [0.16, 0.22], [0.32, 0.45], [0.64, 0.72]] #anchor宽高比 anchor_ratios = [[1, 0.62, 0.42]] * 5 # generate anchors anchors = generate_anchors(feature_map_sizes, anchor_sizes, anchor_ratios) # 用于推断,批大小为 1,模型输出形状为 [1,N,4],因此将锚点的 dim 扩展为 [1,anchor_num,4] anchors_exp = np.expand_dims(anchors, axis=0) id2class = {0: 'Mask', 1: 'NoMask'} # 用训练好的模型做预测 def inference(image, conf_thresh=0.5, iou_thresh=0.4, target_shape=(160, 160), draw_result=True, show_result=True ): ''' 检测推理的主要功能 # :param image:3D numpy 图片数组 # :param conf_thresh:分类概率的最小阈值。 # :param iou_thresh:网管的 IOU 门限 # :param target_shape:模型输入大小。 # :param draw_result:是否将边框拖入图像。 # :param show_result:是否显示图像。 ''' output_info = [] height, width, _ = image.shape#获取初始图像的长宽 image_resized = cv2.resize(image, target_shape)#改变图像尺寸 image_np = image_resized / 255.0 # 归一化到0~1 image_exp = np.expand_dims(image_np, axis=0) #扩展图片维度 y_bboxes_output, y_cls_output = tf_inference(sess, graph, image_exp)#构建图,返回包含预测结果的框和颜色 # 删除批次维度,因为用于推断批次始终是1。 y_bboxes = decode_bbox(anchors_exp, y_bboxes_output)[0] y_cls = y_cls_output[0] # 为了加快速度,执行单类 NMS,而不是多类 NMS。即戴口罩人脸和不戴口罩人脸两个类别一起做NMS bbox_max_scores = np.max(y_cls, axis=1) bbox_max_score_classes = np.argmax(y_cls, axis=1) # keep_idx 是 nms 之后的活动边界框。 keep_idxs = single_class_non_max_suppression(y_bboxes, bbox_max_scores, conf_thresh=conf_thresh, iou_thresh=iou_thresh, ) for idx in keep_idxs: conf = float(bbox_max_scores[idx]) class_id = bbox_max_score_classes[idx] bbox = y_bboxes[idx] # 裁剪坐标,避免该值超出图像边界。 xmin = max(0, int(bbox[0] * width)) ymin = max(0, int(bbox[1] * height)) xmax = min(int(bbox[2] * width), width) ymax = min(int(bbox[3] * height), height)
该应用主要有两部分,一是对图片中的人脸进行识别;二是对视频中出现的人脸进行识别。
1. 调用训练好的模型
sess, graph = load_tf_model('models\face_mask_detection.pb') #载入模型
2. 图片处理
1)anchor 设定完成后,进行识别
2)解码及非极大值抑制
if draw_result:
if class_id == 0:
color = (0, 255, 0)#戴口罩显示绿框
else:
color = (255, 0, 0) #没戴口罩显示红框
cv2.rectangle(image, (xmin, ymin), (xmax, ymax), color, 2)#图像,左上角坐标,右下角坐标 ,颜色,粗细
cv2.putText(image, "%s: %.2f" % (id2class[class_id], conf), (xmin + 2, ymin - 2),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, color)#图片,添加的文字,左上角坐标,字体,字体大小,颜色
output_info.append([class_id, conf, xmin, ymin, xmax, ymax])
if show_result:
Image.fromarray(image).show()
return output_info
3. 视频处理
#视频检测 def run_on_video(video_path, output_video_name, conf_thresh): cap = cv2.VideoCapture(video_path) #读取本地视频 height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)#计算视频的高 width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)#计算视频的宽 fps = cap.get(cv2.CAP_PROP_FPS)#得到视频的帧率 fourcc = cv2.VideoWriter_fourcc(*'XVID')#XVID是MPEG-4编码类型,文件名后缀为.avi # writer = cv2.VideoWriter(output_video_name, fourcc, int(fps), (int(width), int(height))) total_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)#获取视频总帧数 if not cap.isOpened():#视频打开失败 raise ValueError("Video open failed.") return status = True idx = 0 while status: start_stamp = time.time()#返回当前时间的时间戳 status, img_raw = cap.read()#第一个参数ret 为True 或者False,代表有没有读取到图片 # #第二个参数frame表示截取到一帧的图片 img_raw = cv2.cvtColor(img_raw, cv2.COLOR_BGR2RGB)#色彩空间BGR转为RGB read_frame_stamp = time.time() if (status): inference(img_raw, conf_thresh, iou_thresh=0.5, target_shape=(260, 260), draw_result=True, show_result=False)#调用推断 cv2.imshow('image', img_raw[:, :, ::-1]) cv2.waitKey(1) inference_stamp = time.time() # writer.write(img_raw) write_frame_stamp = time.time() idx += 1#循环直到识别到口罩,然后输出帧数和时间. print("%d of %d" % (idx, total_frames)) print("read_frame:%f, infer time:%f, write time:%f" % (read_frame_stamp - start_stamp, inference_stamp - read_frame_stamp, write_frame_stamp - inference_stamp)) # writer.release()
本部分包括模型训练准确率及模型测试效果。
戴口罩的人脸识别准确率接近 92%,可识别绝大部分戴口罩的人脸,如图所示。
1. 在命令行中运行
# 跟命令行有关的 if __name__ == "__main__": # 跟命令行有关的 parser = argparse.ArgumentParser(description="Face Mask Detection") # 创建解析器 parser.add_argument('--img-mode', type=int, default=1, help='set 1 to run on image, 0 to run on video.')#这里设置为 1:检测图片;还是设置为 0:视频文件(实时图像数据)检测 parser.add_argument('--img-path', type=str, help='path to your image.') parser.add_argument('--video-path', type=str, default='0', help='path to your video, `0` means to use camera.') # parser.add_argument('--hdf5', type=str, help='keras hdf5 file') args = parser.parse_args()# 把parser中设置的所有"add_argument"给返回到args子类实例当中, 那么parser中增加的属性内容都会在args实例中,使用即可。 if args.img_mode: imgPath = args.img_path#图片路径 img = cv2.imread(imgPath)#读取图片 img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#转换色彩空间(R,G,B换个位置) inference(img, show_result=True, target_shape=(260, 260)) else: video_path = args.video_path#视频路径 if args.video_path == '0': video_path = 0 run_on_video(video_path, '', conf_thresh=0.5)
运行方法如下:
python tensorflow_infer.py --img-path /path/to/your/img
如果您要在视频上运行,需要:
python tensorflow_infer.py --img-mode 0 --video-path /path/to/video
如果要打开本地摄像头, video_path 填写 0 就可以:
python tensorflow_infer.py --img-mode 0 --video-path 0
2. 运行结果
照片测试结果如图所示:
如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。