基于深度学习的人脸检测算法,多数都是基于深度学习目标检测算法进行的改进,或者说是把通用的目标检测模型,为适应人脸检测任务而进行的特定配置。而众多的目标检测模型(Faster RCNN、SSD、YOLO)中,人脸检测算法最常用的是 SSD 算法(Single Shot MultiBox Detector,“Single Shot”指的是单目标检测,“MultiBox”中的“Box”就像是我们平时拍摄时用到的取景框,只关注框内的画面,屏蔽框外的内容。创建“Multi”个 "Box",将每个 "Box" 的单目标检测结果汇总起来就是多目标检测。换句话说,SSD 将图像切分为 N 片,并对每片进行独立的单目标检测,最后汇总每片的检测结果。),其他如 SSH 模型、S3FD 模型、RetinaFace 算法,都是受 SSD 算法的启发,或者基于 SSD 进行的任务定制化改进, 例如将定位层提到更靠前的位置,Anchor 大小调整、Anchor 标签分配规则的调整,在 SSD 基础上加入 FPN 等。本文训练口罩识别模型采用了 YOLO。
目标检测过程都可以分解为两个独立的操作:
定位(location): 用一个矩形(bounding box)来框定物体,bounding box 一般由 4 个整数组成,分别表示矩形左上角和右下角的 x 和 y 坐标,或矩形的左上角坐标以及矩形的长和高。
分类(classification): 识别 bounding box 中的(最大的)物体。
我选择采用 keras-yolo3-Mobilenet 方案,开源项目地址:
https://github.com/Adamdad/keras-YOLOv3-mobilenet。
MobileNet 的创新亮点是 Depthwise Separable Convolution(深度可分离卷积),与 VGG16 相比,在很小的精度损失情况下,将运算量减小了 30 倍。YOLOv3 的创新亮点是 DarkNet-53、Prediction Across Scales、多标签多分类的逻辑回归层。
基于开源数据集的实验结果:
动手训练模型
训练模型自然需要训练数据集和测试数据集,大家可以在这里下载:
https://modelarts-labs-bj4.obs.cn-north-4.myhuaweicloud.com/casezoo/maskdetect/datasets/maskdetectdatasets.zip
Yolo v3-MobileNet 目标检测工程的目录结构如下:
|--model_data |--voc_classes.txt |--yolo_anchors.txt |--yolo3 |--model.py |--model_Mobilenet.py |--utiles.py |--convert.py |--kmeans.py |--train.py |--train_Mobilenet.py |--train_bottleneck.py |--voc_annotation.py |--yolo.py |--yolo_Mobilenet.py |--yolo_video.py开源项目的好处是已经帮你封装了流程,例如涉及的 Yolo 代码不用修改,本次训练过程需要修改的代码主要是以下三个:
1.train_Mobilenet.py:模型训练代码;
2.yolo/model_Mobilenet.py:基于 mobilenet 的 yolo 的模型代码,如果相对模型代码仔细研究的人,可以研究这个代码;
3.yolo_Mobilenet.py:模型推理代码。
接下来具体介绍我们需要修改的代码,按照功能分为数据类、模型类、可视化类、迁移上云准备类。
•数据类:
仿照 modeldata/vocclasses.txt 写一个是否有戴口罩的类别的 txt,内容只有 yes_mask、no_mask 两个字符。
如果你下载我给出的数据集,你会发现,口罩数据集中给出的 xml 标注格式是 VOC 的标准的,仿照 convert.py 和 voc_annotation.py 写一个数据转换文件,代码如下所示:
import xml.etree.ElementTree as ET import os def convert_annotation(classes, label_path): in_file = open(label_path) tree=ET.parse(in_file) root = tree.getroot() output_list = [] for obj in root.iter(\'object\'): difficult = obj.find(\'difficult\').text cls = obj.find(\'name\').text if cls not in classes or int(difficult)==1: continue cls_id = classes.index(cls) xmlbox = obj.find(\'bndbox\') b = (int(xmlbox.find(\'xmin\').text), int(xmlbox.find(\'ymin\').text), int(xmlbox.find(\'xmax\').text), int(xmlbox.find(\'ymax\').text)) output_list.append(" " + ",".join([str(a) for a in b]) + \',\' + str(cls_id)) return (\' \'.join(output_list)) def mask_convert(data_path, classes): img_list = [] for i in list(os.listdir(data_path)): if i.split(\'.\')[1] == \'jpg\': img_list.append(i.split(\'.\')[0]) output_list = [] for image_id in img_list: img_path = (data_path + \'/%s.jpg\' % (image_id)) label_path = (data_path + \'/%s.xml\' % (image_id)) annotation = convert_annotation(classes, label_path) output_list.append(img_path + annotation) return output_list•模型类: