-----该博客参考了一些大神的博客,在此表示感谢------
Mmdetection是商汤科技和香港中文大学开源的一个目标检测工具箱,目前支持了常见的目标检测网络Faster-RCNN、Mask-RCNN、Fast-RCNN、SSD、Cascade-RCNN等;
该工具箱具有以下三点优势:performance稍高、训练速度稍快、所需显存稍小
文章地址:https://arxiv.org/pdf/1906.07155.pdf
github地址:https://github.com/open-mmlab/mmdetection
此次安装MMDetection工具箱没有使用anaconda,而是采用python创建了虚拟环境,功能和anaconda创建的虚拟环境一样,都是为了避免污染系统,其可以防止在系统的Python解释器中避免安装包的混乱和版本冲突,因此创建一个隔离区域,在这个区域里安装各种我们所需要的依赖包。下面将对整个安装配置过程进行介绍:
1. Ubuntu下安装python虚拟环境
(1) 首先需要安装virtualenv
#python3环境下安装(默认系统为Python3)
sudo pip install virtualenv
(2) 创建虚拟环境目录,在ubuntu的home下创建一个文件夹,名称可以自己定义,我这里采用env
lm@lm:~$ mkdir env
lm@lm:~$ virtualenv -p python env
(3) 运行虚拟环境
安装好之后,运行虚拟环境
lm@lm:~$ source .env/bin/activate
(.env) lm@lm:~$
(.env) lm@lm:~$ deactivate
虚拟环境创建好后,就可以在这个环境中安装各种需要的包,例如numpy, pytorch,tensorflow等等。
这是我目前安装的所有包,都在.env环境下,可以通过pycharm查看,解释器的路径为:~/.env/bin/python3.5。
和anaconda一样,如果需要在终端下运行程序,首先需要进入这个虚拟环境,然后加载各种包,如果打开终端直接import,就会提示找不到。
Python虚拟环境安装好后,就可以在该环境下安装MMDetection工具箱所需要的各种包。
最新发布的MMDetection目标检测工具的安装需求如下:
关于显卡驱动、CUDA和Cudnn的安装,这里忽略,可以百度搜索相关博客
下面主要介绍其他部分
2. 安装Pytorch1.0以上版本
这里采用pip方式安装
首先在Pytorch官网根据自己的系统配置,选择合适的pytorch版本。
下面只是参考流程,安装时根据自己需要安装
lm@lm:~$ source .env/bin/activate
(.env) lm@lm:~$ pip3 install https://download.pytorch.org/whl/cpu/torch-1.0.1.post2-cp35-cp35m-linux_x86_64.whl
(.env) lm@lm:~$ pip3 install torchvision
# 安装numpy
(.env) lm@lm:~$ pip install numpy
# 测试是否安装成功
(.env) lm@lm:~$ python
Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch as t
>>>
3. 安装Cython
(.env) lm@lm:~$ pip install cython
4. 安装mmcv
(.env) lm@lm:~$ git clone https://github.com/open-mmlab/mmcv.git
cd mmcv
pip install . # 注意最后的点不要掉了
5. 查看和安装gcc
(.env) lm@lm:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.11' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11)
这里我的gcc版本为5.4.0。
gcc安装和更新如下:
(.env) lm@lm:~$ sudo apt-get install build-essential
(.env) lm@lm:~$ gcc--version
6. 安装mmdetection目标检测工具包
这里以我的安装为例,在home下创建MMDetection文件夹
(.env) lm@lm:~$ cd MMDetection
(.env) lm@lm:~/MMDetection$ git clone https://github.com/open-mmlab/mmdetection.git
(.env) lm@lm:~/MMDetection/mmdetection$
# 在最新发布的mmdetection工具箱中,对前版本的mmdetection进行了升级,没有了compile.sh文,因此,只需要执行下面的语句即可
(.env) lm@lm:~/MMDetection/mmdetection$ python setup.py develop
完成后的截图如下:
这里,checkpoints,test_image, save_image, my_configs是自己后面创建的,分别用来存放节点文件,测试图像/视频,检测结果保存,以及自己训练时的config文件。
至此,mmdetection目标检测工具包安装完成,为了验证是否安装成功,下面来通过新建一个demo进行测试。
测试验证主要以object detection为主,当然也可以是instance segmentation。
在pycharm下打开到mmdetection目录下,新建一个test.py文件,设置好Interpreter环境——即创建好的python虚拟环境。该测试文件的主要功能是:
(1) 对单张图片进行测试,并显示结果
(2) 对文件夹下的批量图像进行测试,并保存在save_image下
(3) 对视频文件进行测试。
在test.py中采用的模型为Faster_RCNN_FPN_resnet50,由于通过url下载模型的速度很慢,这里将faster_rcnn_r50_fpn_1x_20181010-3d1b3351.pth下载完后放在checkpoints文件夹下。test_image为测试图片和视频,如下:
下面是test.py文件
import os
import mmcv
from mmcv.runner import load_checkpoint
from mmdet.models import build_detector
from mmdet.apis import init_detector, inference_detector, show_result
import argparse
parser = argparse.ArgumentParser(description='MMDetection test!')
parser.add_argument('--test_type',
default='video_test',
help="'single_image_test', 'list_image_test' or 'video_test")
args = parser.parse_args()
# 加载config文件
config_file = 'configs/faster_rcnn_r50_fpn_1x.py'
checkpoint_file = 'checkpoints/faster_rcnn_r50_fpn_1x_20181010-3d1b3351.pth'
# 加载模型和节点文件
model = init_detector(config_file, checkpoint_file, device='cuda:0')
print('Test Model is:', model)
# (1)单幅图像测试
if args.test_type == 'single_image_test':
img = './test_image/4.jpg'
result = inference_detector(model, img)
show_result(img, result, model.CLASSES)
# 批量图像测试
if args.test_type == 'list_image_test':
test_path = './test_image/'
save_path = './save_image/'
filelist = os.listdir(test_path) # 打开文件夹
total_num = len(filelist) #得到文件夹中图像的个数
for i in range(total_num):
jpg_name = test_path + filelist[i]
result = inference_detector(model, jpg_name)
show_result(jpg_name, result, model.CLASSES, show=False, out_file= save_path + 'result_{}.jpg'.format(filelist[i][:-4]))
# 视频文件测试
if args.test_type == 'video_test':
video = mmcv.VideoReader('./test_image/video.mp4')
for frame in video:
result = inference_detector(model, frame)
show_result(frame, result, model.CLASSES, wait_time=1)
1. 单幅图像测试结果
2. 批量图像测试结果
3. 视频文件测试
读者可以根据代码自己去下载视频测试。
instance segmentation的测试结果
通过测试的结果来看,效果并不是很好,还是需要根据数据集,进行训练再测试。
4. 检测框和字体颜色的修改
另外,有的希望能够更改检测bounding box的颜色,或者是字体的颜色,可以按照如下进行更改:
在mmdetection/mmdet/apis下找到inference.py文件,对函数show_result进行如下修改(这里只截取inference.py文件下的show_result进行展示):
def show_result(img,
result,
class_names,
score_thr=0.3,
wait_time=0,
show=True,
out_file=None):
"""Visualize the detection results on the image.
Args:
img (str or np.ndarray): Image filename or loaded image.
result (tuple[list] or list): The detection result, can be either
(bbox, segm) or just bbox.
class_names (list[str] or tuple[str]): A list of class names.
score_thr (float): The threshold to visualize the bboxes and masks.
wait_time (int): Value of waitKey param.
show (bool, optional): Whether to show the image with opencv or not.
out_file (str, optional): If specified, the visualization result will
be written to the out file instead of shown in a window.
Returns:
np.ndarray or None: If neither `show` nor `out_file` is specified, the
visualized image is returned, otherwise None is returned.
"""
assert isinstance(class_names, (tuple, list))
img = mmcv.imread(img)
img = img.copy()
if isinstance(result, tuple):
bbox_result, segm_result = result
else:
bbox_result, segm_result = result, None
bboxes = np.vstack(bbox_result)
# draw segmentation masks
if segm_result is not None:
segms = mmcv.concat_list(segm_result)
inds = np.where(bboxes[:, -1] > score_thr)[0]
for i in inds:
color_mask = np.random.randint(0, 256, (1, 3), dtype=np.uint8)
mask = maskUtils.decode(segms[i]).astype(np.bool)
img[mask] = img[mask] * 0.5 + color_mask * 0.5
# draw bounding boxes
labels = [
np.full(bbox.shape[0], i, dtype=np.int32)
for i, bbox in enumerate(bbox_result)
]
labels = np.concatenate(labels)
mmcv.imshow_det_bboxes(
img,
bboxes,
labels,
class_names=class_names,
score_thr=score_thr,
bbox_color='red',
text_color='blue',
font_scale=0.5,
show=show,
wait_time=wait_time,
out_file=out_file)
if not (show or out_file):
return img
在倒数第八行和第九行,分别是text_color=‘blue’, bbox_color=‘red’。这里根据自己的喜好,随意更改bbox的颜色和文本的颜色。更改的结果如下所示:
1. 创建自己的数据集
MMDetection工具箱主要支持PASCAL VOC和COCO两种数据集格式,
(1) VOC格式的数据就是xml格式的标注数据,不过VOC规定了xml文件中各个节点的标签名字,统一了标注风格。
(2) COCO数据集是一种json格式的标注形式。
如果需要训练自己的数据集,最好将目标数据集的标注格式转换为VOC或COCO格式才行。使用标注工具LabelImg就可以产生VOC格式的标注文件,使用Labelme可以产生COCO格式的标注文件。
如果其他公开的数据集,没有提供VOC或者COCO格式的话,需要自己编码实现标注风格的转换,再进行训练。
这里以我自己的数据集为例,采用VOC格式,在文件夹下的目录如下:
----data
--------VOCdevkit
------------VOC2007
----------------Annotations
----------------JPEGImages
----------------ImageSets
--------------------Main
------------------------test.txt
------------------------train.txt
------------------------trainval.txt
------------------------val.txt
文件夹Annotations中存放voc2007格式的标注数据文件,JPEGImages存放图片文件,ImageSets存放测试文件test.txt, 训练文件train.txt, 验证文件val.txt和trainval.txt文件。
2. 修改config文件
为了避免修改MMDetection中原有的config文件,这里在mmdetection文件夹下创建一个新的my_config文件夹,里面存放训练模型需要的config文件,如下:
这里以faster_rcnn_r50_fpn_1x模型为例,config文件的内容如下:
由于这里采用的是VOC数据格式,因此在下面的config文件中,主要修改文件中的训练集、验证集和测试集下的路径:
# model settings 模型设置
model = dict(
type='FasterRCNN', #model类型
pretrained='modelzoo://resnet50', # 预训练模型:imagenet-resnet50
backbone=dict(
type='ResNet', # 骨干网络类型
depth=50, # 网络深度
num_stages=4, #resnet的stage数量
out_indices=(0, 1, 2, 3), # 输出的stage序号
frozen_stages=1,
style='pytorch'),
neck=dict(
type='FPN',
in_channels=[256, 512, 1024, 2048],
out_channels=256,
num_outs=5),
rpn_head=dict(
type='RPNHead',
in_channels=256,
feat_channels=256,
anchor_scales=[8],
anchor_ratios=[0.5, 1.0, 2.0],
anchor_strides=[4, 8, 16, 32, 64],
target_means=[.0, .0, .0, .0],
target_stds=[1.0, 1.0, 1.0, 1.0],
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),
bbox_roi_extractor=dict(
type='SingleRoIExtractor',
roi_layer=dict(type='RoIAlign', out_size=7, sample_num=2),
out_channels=256,
featmap_strides=[4, 8, 16, 32]),
bbox_head=dict(
type='SharedFCBBoxHead',
num_fcs=2,
in_channels=256,
fc_out_channels=1024,
roi_feat_size=7,
num_classes=81,
target_means=[0., 0., 0., 0.],
target_stds=[0.1, 0.1, 0.2, 0.2],
reg_class_agnostic=False,
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)))
# 模型训练和测试设置
# model training and testing settings
train_cfg = dict(
rpn=dict(
assigner=dict(
type='MaxIoUAssigner', #bounding box的最大IOU设置
pos_iou_thr=0.7, # 正样本iou
neg_iou_thr=0.3, # 负样本iou
min_pos_iou=0.3, # 最小正样本iou
ignore_iof_thr=-1),
sampler=dict(
type='RandomSampler', # 随机采样
num=256,
pos_fraction=0.5,
neg_pos_ub=-1,
add_gt_as_proposals=False),
allowed_border=0,
pos_weight=-1,
debug=False),
rpn_proposal=dict(
nms_across_levels=False,
nms_pre=2000,
nms_post=2000,
max_num=2000,
nms_thr=0.7,
min_bbox_size=0),
rcnn=dict(
assigner=dict(
type='MaxIoUAssigner',
pos_iou_thr=0.5,
neg_iou_thr=0.5,
min_pos_iou=0.5,
ignore_iof_thr=-1),
sampler=dict(
type='RandomSampler',
num=512,
pos_fraction=0.25,
neg_pos_ub=-1,
add_gt_as_proposals=True),
pos_weight=-1,
debug=False))
test_cfg = dict(
rpn=dict(
nms_across_levels=False,
nms_pre=1000,
nms_post=1000,
max_num=1000,
nms_thr=0.7,
min_bbox_size=0),
rcnn=dict(
score_thr=0.05, nms=dict(type='nms', iou_thr=0.5), max_per_img=100)
# soft-nms is also supported for rcnn testing
# e.g., nms=dict(type='soft_nms', iou_thr=0.5, min_score=0.05)
)
# 数据集设置
# dataset settings
dataset_type = 'VOCDataset' # 数据集类型
data_root = 'data/VOCdevkit/' #数据集根目录
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
data = dict(
imgs_per_gpu=2, # 每个gpu计算的图像数量
workers_per_gpu=2, # 每个gpu分配的线程数
# 训练集
train=dict(
type=dataset_type,
ann_file=data_root + 'VOC2007/ImageSets/Main/train.txt',
img_prefix = data_root + 'VOC2007/',
img_scale=(1000, 600),
img_norm_cfg=img_norm_cfg,
size_divisor=32,
flip_ratio=0.5,
with_mask=False,
with_crowd=True,
with_label=True),
# 验证集
val=dict(
type=dataset_type,
ann_file=data_root + 'VOC2007/ImageSets/Main/trainval.txt',
img_prefix=data_root + 'VOC2007/',
img_scale=(1000, 600),
img_norm_cfg=img_norm_cfg,
size_divisor=32,
flip_ratio=0,
with_mask=False,
with_crowd=True,
with_label=True),
# 测试集
test=dict(
type=dataset_type,
ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',
img_prefix=data_root + 'VOC2007/',
img_scale=(1000, 600),
img_norm_cfg=img_norm_cfg,
size_divisor=32,
flip_ratio=0,
with_mask=False,
with_label=False,
test_mode=True))
# 优化器-optimizer
# lr为学习率,momentum为动量因子,weight_decay为权重衰减因子
optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
# 学习策略-learning policy
lr_config = dict(
policy='step',
warmup='linear',
warmup_iters=500,
warmup_ratio=1.0 / 3,
step=[8, 11])
checkpoint_config = dict(interval=1)
# yapf:disable
log_config = dict(
interval=50,
hooks=[
dict(type='TextLoggerHook'),
# dict(type='TensorboardLoggerHook')
])
# yapf:enable
# runtime settings
total_epochs = 12
dist_params = dict(backend='nccl')
log_level = 'INFO'
work_dir = './work_dirs/faster_rcnn_r50_fpn_1x' #log文件和模型文件存储路径
load_from = None
resume_from = None
workflow = [('train', 1)]
3. 更改数据集类别文件
我自己的数据集只有两个类别,一个是背景(unknown),一个是目标(sam)。
在mmdetection/mmdet/datasets/下,新建一个voc文件,将里面的CLASSES设置如下:
读者可以根据自己数据集的类别情况进行修改。
from .registry import DATASETS
from .xml_style import XMLDataset
@DATASETS.register_module
class VOCDataset(XMLDataset):
CLASSES = ('unknown', 'sam')
def __init__(self, **kwargs):
super(VOCDataset, self).__init__(**kwargs)
if 'VOC2007' in self.img_prefix:
self.year = 2007
elif 'VOC2012' in self.img_prefix:
self.year = 2012
else:
raise ValueError('Cannot infer dataset year from img_prefix')
如果不采用VOC格式,也可以自己创建,如下:
在mmdetection/mmdet/datasets/下新建一个my_dataset.py文件,文件的内容如下:
from .coco import CocoDataset
from .registry import DATASETS
@DATASETS.register_module
class MyDataset(CocoDataset):
CLASSES = ('unknown','sam')
然后在__init__.py文件夹下,新增
from .my_dataset import MyDataset
4. 模型训练
在mmdetection文件夹下创建模型训练日志的存放文件夹:work_dirs
打开重点,进入虚拟环境,输入如下代码:
(.env) lm@lm:~/MMDetection/mmdetection$ python tools/train.py my_configs/faster_rcnn_r50_fpn_1x.py