MMDetection 是一个基于 PyTorch 的开源对象检测工具箱。它是OpenMMLab项目的一部分。master 分支与PyTorch 1.3+ 一起使用。旧版本v1.x分支适用于PyTorch 1.1 到1.4,但强烈建议使用v2.0,以获得更快的速度、更高的性能、更好的设计和更友好的使用。

一、环境配置 1.创建虚拟环境、激活环境 在Linux系统安装Anaconda,进入终端,进行创建和激活环境

//创建虚拟环境opne-mmlab并激活(python=3.7)
conda create -n open-mmlab python=3.7
conda activate open-mmlab

2.下载pytroch、torchvision、cudatoolkit

//pytroch==1.5.0 torchvision==0.6.0 cudatoolkit==10.2.89
conda install pytorch==1.5.0 torchvision==0.6.0 cudatoolkit=10.2 -c pytorch

如果下载较慢,可以到网站上下载对应的离线包,然后在虚拟环境中进入到下载文件夹,使用pip安装离线包到虚拟环境当中 Pytorch_stable下载地址. 清华镜像下载

3.下载mmcv-full

pip install mmcv-full

4.下载mmdetection 文件夹放到桌面上命名为mmdetection,如果下载过慢,就到mmdetection网站上下载

git clone https://github.com/open-mmlab/mmdetection.git

5.下载mmdetection需求包 从桌面终端进入根目录(在open-mmlab虚拟环境中),我下载的mmdetection版本是V2.11

cd mmdetection
pip install -r requirements.txt
// 如果上面的不行使用下面的命令
pip install -r build.txt
pip install -v -e .   或者  python setup.py develop

6.下载mmdet

pip install mmdet 

后续差什么依赖包就按照提示pip安装对应包 参考:球场书生的博文

验证环境是否安装完所有依赖包

1.下载预训练模型,创建文件夹checkpoints,并把下载的预训练模型放在其中

2.运行demo命令验证

python demo/image_demo.py demo/demo.jpg configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth

环境搭建或安装也可见GitHub官网:https://github.com/open-mmlab/mmdetection

二、如何训练自定义数据集

mmdetection兼容coco数据集以及voc数据集,但是coco数据集可能训练的结果比较好。如果数据符合COCO或VOC数据集格式,可以直接进入模型选择、训练和参数配置环节,否则需要将数据集转换至COCO格式或VOC格式。

数据准备 VOC格式数据目录结构如下

mmdetection
├── mmdet
├── tools
├── configs
├── data     #手动创建data、VOCdevkit、VOC2007、Annotations、JPEGImages、ImageSets、Main这些文件夹
│   ├── VOCdevkit(数据集名称)
│   │   ├── VOC2007
│   │   │   ├── Annotations         #把valid.txt、train.txt对应的xml文件放在这
│   │   │   ├── JPEGImages          #把valid.txt、train.txt对应的图片放在这
│   │   │   ├── ImageSets
│   │   │   │   ├── Main
│   │   │   │   │   ├── valid.txt 
│   │   │   │   │   ├── train.txt

Annotations:为xml格式文件,用来记录目标的坐标以及类别信息。(可以使用LabelImg工具标注自己的数据集,可以生成与voc相同的xml格式标签。)

**JPEGImages:**基本为jpg格式文件,将自己整理的图片存入此文件夹

**ImageSets:**train.txt:训练集 ,val.txt:验证集

COCO格式数据目录结构如下:

mmdetection
├── mmdet
├── tools
├── configs
├── data
│   ├── coco
│   │   ├── annotations
│   │   │   │──instances_train.json    #由训练集对应的xml文件转化而来
│   │   │   │──instances_val.json
│   │   │   │──instances_test.json     #可有可无
│   │   ├── train                #存放训练集图像.jpg
│   │   ├── val                  #存放验证集图像.jpg
│   │   ├── test                 #存放测试集图像.jpg

VOC格式数据转为COCO格式数据:

终端运行voc2coco.py文件

import sys
import os
import json
import xml.etree.ElementTree as ET
import glob

START_BOUNDING_BOX_ID = 1
PRE_DEFINE_CATEGORIES = None
# If necessary, pre-define category and its id
#  PRE_DEFINE_CATEGORIES = {"aeroplane": 1, "bicycle": 2, "bird": 3, "boat": 4,
#  "bottle":5, "bus": 6, "car": 7, "cat": 8, "chair": 9,
#  "cow": 10, "diningtable": 11, "dog": 12, "horse": 13,
#  "motorbike": 14, "person": 15, "pottedplant": 16,
#  "sheep": 17, "sofa": 18, "train": 19, "tvmonitor": 20}


def get(root, name):
    vars = root.findall(name)
    return vars


def get_and_check(root, name, length):
    vars = root.findall(name)
    if len(vars) == 0:
        raise ValueError("Can not find %s in %s." % (name, root.tag))
    if length > 0 and len(vars) != length:
        raise ValueError(
            "The size of %s is supposed to be %d, but is %d."
            % (name, length, len(vars))
        )
    if length == 1:
        vars = vars[0]
    return vars


def get_filename_as_int(filename):
    try:
        filename = filename.replace("\\", "/")
        filename = os.path.splitext(os.path.basename(filename))[0]
        return int(filename)
    except:
        raise ValueError("Filename %s is supposed to be an integer." % (filename))


def get_categories(xml_files):
    """Generate category name to id mapping from a list of xml files.
    
    Arguments:
        xml_files {list} -- A list of xml file paths.
    
    Returns:
        dict -- category name to id mapping.
    """
    classes_names = []
    for xml_file in xml_files:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        for member in root.findall("object"):
            classes_names.append(member[0].text)
    classes_names = list(set(classes_names))
    classes_names.sort()
    return {name: i for i, name in enumerate(classes_names)}


def convert(xml_files, json_file):
    json_dict = {"images": [], "type": "instances", "annotations": [], "categories": []}
    if PRE_DEFINE_CATEGORIES is not None:
        categories = PRE_DEFINE_CATEGORIES
    else:
        categories = get_categories(xml_files)
    bnd_id = START_BOUNDING_BOX_ID
    for xml_file in xml_files:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        path = get(root, "path")
        if len(path) == 1:
            filename = os.path.basename(path[0].text)
        elif len(path) == 0:
            filename = get_and_check(root, "filename", 1).text
        else:
            raise ValueError("%d paths found in %s" % (len(path), xml_file))
        ## The filename must be a number
        image_id = get_filename_as_int(filename)
        size = get_and_check(root, "size", 1)
        width = int(get_and_check(size, "width", 1).text)
        height = int(get_and_check(size, "height", 1).text)
        image = {
            "file_name": filename,
            "height": height,
            "width": width,
            "id": image_id,
        }
        json_dict["images"].append(image)
        ## Currently we do not support segmentation.
        #  segmented = get_and_check(root, 'segmented', 1).text
        #  assert segmented == '0'
        for obj in get(root, "object"):
            category = get_and_check(obj, "name", 1).text
            if category not in categories:
                new_id = len(categories)
                categories[category] = new_id
            category_id = categories[category]
            bndbox = get_and_check(obj, "bndbox", 1)
            xmin = int(get_and_check(bndbox, "xmin", 1).text) - 1
            ymin = int(get_and_check(bndbox, "ymin", 1).text) - 1
            xmax = int(get_and_check(bndbox, "xmax", 1).text)
            ymax = int(get_and_check(bndbox, "ymax", 1).text)
            assert xmax > xmin
            assert ymax > ymin
            o_width = abs(xmax - xmin)
            o_height = abs(ymax - ymin)
            ann = {
                "area": o_width * o_height,
                "iscrowd": 0,
                "image_id": image_id,
                "bbox": [xmin, ymin, o_width, o_height],
                "category_id": category_id,
                "id": bnd_id,
                "ignore": 0,
                "segmentation": [],
            }
            json_dict["annotations"].append(ann)
            bnd_id = bnd_id + 1

    for cate, cid in categories.items():
        cat = {"supercategory": "none", "id": cid, "name": cate}
        json_dict["categories"].append(cat)

    os.makedirs(os.path.dirname(json_file), exist_ok=True)
    json_fp = open(json_file, "w")
    json_str = json.dumps(json_dict)
    json_fp.write(json_str)
    json_fp.close()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(
        description="Convert Pascal VOC annotation to COCO format."
    )
    parser.add_argument("xml_dir", help="Directory path to xml files.", type=str)
    parser.add_argument("json_file", help="Output COCO format json file.", type=str)
    args = parser.parse_args()
    xml_files = glob.glob(os.path.join(args.xml_dir, "*.xml"))

    # If you want to do train/test split, you can pass a subset of xml files to convert function.
    print("Number of xml files: {}".format(len(xml_files)))
    convert(xml_files, args.json_file)
    print("Success: {}".format(args.json_file))

运行命令如下:

python voc2coco.py ./data/VOC/Annotations ./data/coco/output.json

注意:VOC格式数据中,图像和xml文件名称必须是数字,而且一样,比如"10008.jpg 10008.xml"

三、修改相关文件

coco格式数据相关文件类似

  1. 修改class_names.py文件

修改路径:

mmdetection/mmdet/core/evaluation/class_names.py

修改内容:将voc_classes的返回值改为要训练数据集的类别名称。 图片说明

  1. 修改voc.py文件 修改路径:
mmdetection/mmdet/datasets/voc.py

修改内容:将VOCDataset中的CLASSES改为对应训练数据集的类别集合。 图片说明

  1. 修改配置文件 配置文件路径:
mmdetection/configs

默认使用的是COCO格式,我们找到我们需要的模型,修改成我们所使用的VOC格式。假设我们使用的是cascade_rcnn_r50_fpn_1x_coco.py,我们可复制同一文件内容到cascade_rcnn_r50_fpn_1x_voc_(对应数据集名称).py。

里面内容为:

_base_ = [
    '../_base_/models/cascade_rcnn_r50_fpn_xxx.py',
    '../_base_/datasets/voc_xxx_detection.py',
    '../_base_/schedules/schedule_1x_xxx_voc.py', '../_base_/xxx_voc_runtime.py'
]
# xxx为对应数据集名称
# models文件夹为模型结构设置
# datasets文件夹为数据集设置
# schedules文件夹为优化器、学习策略设置
# runtime文件为整体训练超参设置

  1. models中对应文件修改num_classes变量 (2.x版本中num_classes不需要+1) 图片说明

  2. datasets中对应文件修改dataset_type、data_root、img_scale、ann_file、img_prefix变量。

图片说明

图片说明

  1. schedules中新建schedule_xxx_voc.py设置对应优化器

图片说明

  1. 新建xxx_voc_runtime.py文件设置整体超参

图片说明

训练之前需要编译 修改完后,需要重新编译(python setup.py install),不然会出现“AssertionError: The num_classes (20) in Shared2FCBBoxHead of MMDataParallel does not matches the length of CLASSES 80) in RepeatDataset"的报错。

开始训练

python tools/train.py configs/cascade_rcnn/cascade_rcnn_r50_fpn_1x_voc_xxx.py

对应log日志以及断点保存在work_dirs目录下。

模型测试 对于检测,需要自己写一个demo,调用训练好的模型,来生成目标检测的结果并保存。

图片说明

终端运行上述代码:python image_demo.py 报如下错误:

cannot import name ‘show_result‘ from ‘mmdet.apis‘

经过查阅文献,得知可能MMDetection在版本更新时删除了这个函数show_result()。于是在Github上寻找解决办法,发现新的代码对于’show_result’的使用发生了改变。

解决办法: 重新打开image_demo.py 文件 ,删掉show_result函数 图片说明

再次运行下面代码(是下面的),即可

import mmcv
import os
import numpy as np
from mmcv.runner import load_checkpoint
from mmdet.models import build_detector
from mmdet.apis import init_detector, inference_detector


config_file = 'configs/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco.py'
checkpoint_file = 'work_dirs/cascade_rcnn_r50_fpn_1x_coco/epoch_9.pth'

model = init_detector(config_file,checkpoint_file)

img = 'data/1111.jpg'
out_dir = 'result/'

if not os.path.exists(out_dir):
    os.mkdir(out_dir)


result = inference_detector(model,img)
#model.show_result(img, result, model.CLASSES, out_file='testOut.jpg')
model.show_result(img, result, score_thr=0.3, show=False, out_file='result/result1111.jpg')

print(result)

mmdetection(重要全) 用mmdetection跑通自己的数据集 【小伟哥AI之路】mmdetection之configs中的各项参数具体解释 MMDetection,训练VOC格式数据集 【mmdetection】使用cascade-rcnn、faster-rcnn训练自定义的coco数据集

mmdetection训练,测试,学习 环境搭建和安装github 如何将VOC XML文件转化成COCO数据格式 使用mmdetection测试以及生成检测结果 关于cannot import name ‘show_result‘ from ‘mmdet.apis‘问题的解决 cannot import name ‘show_result‘ from ‘mmdet.apis‘问题的解

mmdetection训练自己的数据

MMDETECTION的安装并训练自己的VOC数据集(内存不足,调整batch_size大小 或者调整输入图片尺寸) 【mmdetection】使用自定义的coco格式数据集进行训练及测试