目录1.实例分割简介2.实例分割应用3.实例分割算法演进4.Mask-RCNN实战之口罩检测5.Mask-RCNN代码分析6.样本标注工具1.实例分割简介可以看到右图中,弹幕没有遮盖人物图像,给用户更好的观影体验。2.2 背景智能替换在疫情期间,检测行人以及特定场所工作人员佩戴口罩的情况。
3.实例分割算法演进实例分割算法从是否需要目标框检测阶段角度分为两种,Anchor-based与Anchor-free。
下面分别做下简要介绍。3.1 Anchor-based类算法实例分割比较有挑战性,因为它需要正确的检测到物体并且能够精确的分割图中的每一个物体。因此物体分割需要把计算机视觉的经典任务–目标检测结合进来,其目的是能够分清并用边界框定位每一个物体,同时语义分割的目的是把每个像素分为固定的类别而不用区分目标物体。
Anchor-based类的实例分割算法基本思想如下:
实例分割 = 目标检测 + 语义分割
目标检测任务:通常目标检测的输出为:yˆ=[p_{c},b_{x},b_{y},b_{h},b_{w},c_{1},c_{2},c_{3}]^T
其中:p_{c}
:图中有物体的概率.[b_{x},b_{y},b_{h},b_{w}]
:对应物体的位置参数.c_{1},c_{2},c_{3}
,c_{4}
:对应哪个物体的class.语义分割任务:语义分割是指将图像中的每个像素归于类标签的过程,这些类标签可以包括一个人、汽车、鲜花、一件家具等。3.1.1 Mask-RCNNMaskR-CNN在Faster R-CNN的基础上添加了一个掩码分支,同时将ROI Pool更换为ROI Align大大提升了目标分割检测的准确率。从上图可以看出Mask Scoring RCNN的结构主要是在Mask-RCNN的基础上发展而来。MS-RCNN首先经过一个FPN多层提取特征,然后通过anchor与nms提取出一系列的proposals,之后通过RoI Align对齐,最后接class,box,mask以及MaskIou各个分支。
BMask R-CNN包含一个保持边界的mask head,其中对象边界和mask是通过特征融合块相互学习的,使得预测的蒙版与对象边界更好地对齐。3.2 Anchor-free类算法Anchor-free类算法不需要提前预测出Anchor的位置,也是近两年经常出现在各CV会议中的方法,总体来说还不是很成熟,感兴趣的可以参考文章:
3.3 特定任务优化策略3.3.1 数据集优化- 训练数据增强
其中,Mixup是指将2张输入图像按照一定权重合并成一张图像,基于这种合成图像进行训练的模型更加鲁棒,能够有效降低对抗图像的影响。
上图交通灯检测任务中,在原有的Anchor尺寸[8,16,32]基础上增加小尺寸[4,8,16,32],可以增强对小目标的检测性能。
- Head分支增强
4.Mask-RCNN实战之口罩检测4.1 GPU服务器MaskRCNN需要使用GPU训练,使用CPU训练速度太慢。有条件的可以自己搭建GPU环境, 临时没有条件的推荐使用腾讯云按量付费模式的GPU云服务器。参考资料:
https://
zhuanlan.zhihu.com/p/75
958734
在服务器console命令行中输入命令nvidia-smi,如果显示如下信息说明GPU已经配置成功:annotations中配置文件:images maksssksksss0.png # 图像尺寸 512 366 3 0 # 图像中的标注的目标列表
在给定数据集的基础上还要转换成可用于模型训练的样本,以下是解析图像Xml格式配置文件的代码:class MaskDataset: # 加载数据集 def load_dataset:class_list = list# 图像文件路径images_path = config.DATASET_PATH + '/images/'# 标注文件路径annotations_path = config.DATASET_PATH + '/annotations/'for filename in listdir: tree = ElementTree.parse root = tree.getroot img_filename = root.find.text for classname in root.findall: class_list.append # 去掉文件扩展名 img_id = img_filename[:-4] if is_train and int >= 600: continue if not is_train and int < 600: continue img_path = images_path + img_filename ann_path = annotations_path + filename self.add_imageclass_list = list)i = 1for classname in class_list: self.add_class i=i+1 # 提取物体框坐标 def extract_boxes:tree = ElementTree.parseroot = tree.getrootboxes = listclass_names = listfor obj in root.findall: class_names.append.text) for box in obj.findall: xmin = int.text) ymin = int.text) xmax = int.text) ymax = int.text) coors = [xmin, ymin, xmax, ymax] boxes.appendwidth = int.text)height = int.text)return class_names, boxes, width, height # 提取遮罩 def load_mask:# 图像详情info = self.image_info[image_id]# 定义annotation标注文件path = info['annotation']# 解析图像目标区域class_names, boxes, w, h = self.extract_boxesmasks = zeros], dtype='uint8')# 提取目标遮罩class_ids = listfor i in range): box = boxes[i] row_s, row_e = box[1], box[3] col_s, col_e = box[0], box[2] masks[row_s:row_e, col_s:col_e, i] = 1 class_ids.append)return masks, asarray def image_reference:info = self.image_info[image_id]printreturn info['path']
分别声明训练集和验证集:# 准备训练数据集train_set = MaskDatasettrain_set.load_datasettrain_set.prepareprint)# 准备验证数据集test_set = MaskDatasettest_set.load_datasettest_set.prepareprint)输出: Train: 600 Test : 253
4.3 模型训练Step1:MaskRCNN训练参数class MaskRCNNConfig: # 配置名称 NAME = "MaskRCNN_config" # GPU数量 GPU_COUNT = 1 IMAGES_PER_GPU = 1 # number of classes # mask + nomask + worn_incorrectly + bg NUM_CLASSES = 3+1 # 每个Epoch轮次迭代次数 STEPS_PER_EPOCH = 80 # 学习率 LEARNING_RATE = 0.006 # 置信度阈值 DETECTION_MIN_ConFIDENCE = 0.85 # setting Max ground truth instances MAX_GT_INSTANCES = 10
实例化配置信息:config = MaskRCNNConfigconfig.display输出:Configurations:# 图像基础特征提取模型BACKBONE resnet101BACKBONE_STRIDES [4, 8, 16, 32, 64]BATCH_SIZE1BBOX_STD_DEV [0.1 0.1 0.2 0.2]COMPUTE_BACKBONE_SHAPE NoneDETECTION_MAX_INSTANCES 100DETECTION_MIN_CONFIDENCE0.85DETECTION_NMS_THRESHOLD 0.3FPN_CLASSIF_FC_LAYERS_SIZE 1024GPU_COUNT 1GRADIENT_CLIP_NORM 5.0IMAGES_PER_GPU 1IMAGE_CHANNEL_COUNT 3# 图像归一化尺寸IMAGE_MAX_DIM1024IMAGE_meta_SIZE 16IMAGE_MIN_DIM800IMAGE_MIN_SCALE 0IMAGE_RESIZE_MODEsquareIMAGE_SHAPE [1024 10243]LEARNING_MOMENTUM0.9LEARNING_RATE0.006LOSS_WEIGHTS {'rpn_class_loss': 1.0, 'rpn_bbox_loss': 1.0, 'mrcnn_class_loss': 1.0, 'mrcnn_bbox_loss': 1.0, 'mrcnn_mask_loss': 1.0}MASK_POOL_SIZE 14MASK_SHAPE[28, 28]# 设置图像中最多可检测出来的物体数量MAX_GT_INSTANCES 10MEAN_PIXEL[123.7 116.8 103.9]MINI_MASK_SHAPE NAME MaskRCNN_configNUM_CLASSES 4POOL_SIZE 7POST_NMS_ROIS_INFERENCE 1000POST_NMS_ROIS_TRAINING 2000PRE_NMS_LIMIT6000ROI_POSITIVE_RATIO 0.33RPN_ANCHOR_RATIOS[0.5, 1, 2]RPN_ANCHOR_SCALESRPN_ANCHOR_STRIDE1RPN_BBOX_STD_DEV [0.1 0.1 0.2 0.2]RPN_NMS_THRESHOLD0.7RPN_TRAIN_ANCHORS_PER_IMAGE256STEPS_PER_EPOCH 80TOP_DOWN_PYRAMID_SIZE 256TRAIN_BN FalseTRAIN_ROIS_PER_IMAGE200USE_MINI_MASKTrueUSE_RPN_ROIS TruevalIDATION_STEPS 50WEIGHT_DECAY 0.0001
Step2:加载预训练模型# 加载预训练模型model = modellib.MaskRCNNmodel.load_weights
Step3:模型训练# 模型训练# layers='all':Fine tune all layers# layers='heads' :Train the head branchesmodel.trainhistory = model.keras_model.history.history
如果输出如下信息说明Tensorflow+GPU搭建成功.2020-08-08 02:35:54.792261: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1005] successful NUMA node read from SysFS had negative value , but there must be at least one NUMA node, so returning NUMA node zero2020-08-08 02:35:54.792483: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1763] Adding visible gpu devices: 0
训练过程迭代信息如下:下图是迭代训练20个Epoch之后的检测结果,可以看到效果还是不错的,准确识别出图像中哪些人脸是带口罩而哪些人脸是没有带口罩的。5.Mask-RCNN代码解析代码位置:https://
github.com/matterport/M
ask_RCNN/blob/master/mrcnn/model.py
主要是获得图片基本信息和目标框标注信息,如图片名称、图片尺寸、边框位置、边框类别等。总结如下:在MaskR-CNN中,采用了FPN架构,FPN使用的是图像金字塔的思想以解决物体检测场景中小尺寸物体检测困难的问题,FPN的优势在于融合多尺度特征,即考虑图像全局特征也兼顾局部细节特征。 # 自上向下 + 横向连接P5 = KL.Conv2D, name='fpn_c5p5')P4 = KL.Add, name="fpn_p5upsampled"), KL.Conv2D, name='fpn_c4p4')])P3 = KL.Add, name="fpn_p4upsampled"), KL.Conv2D, name='fpn_c3p3')])P2 = KL.Add, name="fpn_p3upsampled"), KL.Conv2D, name='fpn_c2p2')])# 用3x3 conv 对特征再次增强P2 = KL.Conv2D, padding="SAME", name="fpn_p2")P3 = KL.Conv2D, padding="SAME", name="fpn_p3")P4 = KL.Conv2D, padding="SAME", name="fpn_p4")P5 = KL.Conv2D, padding="SAME", name="fpn_p5")# P6用在RPN中P6 = KL.MaxPooling2D, strides=2, name="fpn_p6")# 用于RPN网络rpn_feature_maps = [P2, P3, P4, P5, P6]# 用于分类网络+Mask网络mrcnn_feature_maps = [P2, P3, P4, P5]
5.3 RPN网络RPN网络的目的是生成RoI区域,如下图:再聪明的算法也避免不了"穷举",RPN网络也需要某种策略搜寻最可能的RoI。这里引入anchor概念,anchor其实就是预训练网络卷积层的最后一层feature map上的一个像素,以该 anchor 为中心,可以生成 k 种 anchor boxes。每个anchor box 对应有一组缩放比例和宽高比。
比如,3种scale与3 种aspect两两组合,则每个anchor位置产生9种可能的anchor boxs。如下图:这种方式生成候选框看上去简单粗暴,初次接触可能会有不少疑问:1).对于不同的目标检测任务,检测框尺度和长宽如何调整 可以统计特定领域目标框的分布比例,取最常见的目标框尺度和高宽比。2).如何排除质量较差的候选框 前背景分类器,会有一个分类器判断是否前景。3).如何对预测检测框做修正以提高精度 RPN网络通过在初始预测候选框的基础上学习"偏移量"来纠正。
用
P
表示预测包含目标的region , proposal
,G
表示这个region , proposal
对应的groud , truth
。x, y, w, h
分别表示横坐标、纵坐标、宽和高。d_{x}, d_{y}, d_{w}, d_{h}
,RPN表示模型预测的4个值,它们表征的是对位置平移与大小缩放的系数,即G
=P
+预测修正量。又由于G的四个值与P的四个值都是已知的,那么我们训练时就有了
d_{x}
,d_{y}
,d_{w}
,d_{h}
的目标值如图所示:在筛选出来的候选框送入到下一阶段处理时,需要归一化到相同尺寸:mrcnn_class_logits, mrcnn_class, mrcnn_bbox = fpn_classifier_graphmrcnn_mask = build_fpn_mask_graph
代码位置:https://
github.com/matterport/M
ask_RCNN/blob/master/mrcnn/model.py
5.6 损失函数Mask R-CNN采用了和Faster R-CNN相同的两步走策略,即先使用RPN提取候选区域,。不同于Faster R-CNN中使用分类和回归的多任务回归,Mask R-CNN在其基础上并行添加了一个用于语义分割的Mask损失函数,所以Mask R-CNN的损失函数可以表示为下式:L=L_{cls}+L_{box}+L_{mask}
L_{cls}
和L_{box}
是 Faster RCNN 中的损失函数,而L_{mask}
则是 mask 分支中的sigmoid 二分类损失。是对每个像素进行分类,其含有K∗m∗m维度的输出,K代表类别的数量,m*m是提取的ROI图像的大小。# RPN前景分类损失函数# 参考资料:https://www.cnblogs.com/hellcat/p/9907837.htmlrpn_class_loss = KL.Lambda, name="rpn_class_loss")# RPN前景框坐标修正量回归损失函数# 参考资料:https://www.cnblogs.com/hellcat/p/9907837.htmlrpn_bbox_loss = KL.Lambda, name="rpn_bbox_loss")# MRCNN分类损失函数# 参考资料:https://www.cnblogs.com/hellcat/p/9907837.htmlclass_loss = KL.Lambda, name="mrcnn_class_loss")# MRCN偏移量回归损失函数# 参考资料:https://www.cnblogs.com/hellcat/p/9907837.htmlbbox_loss = KL.Lambda, name="mrcnn_bbox_loss")# MRCNN掩码损失函数# 参考资料:https://www.cnblogs.com/hellcat/p/9907837.htmlmask_loss = KL.Lambda, name="mrcnn_mask_loss")
最后盗了一张图,整体看下MaskR-CNN的模型结构:Labelme是一个使用Python语言开发的图形界面的图像标注软件,可以用于场景分割、实例分割、目标检测等计算机视觉任务。