• MMDetection训练自己的数据集



    如有错误,恳请指出。


    这篇博客是关于如何使用MMDetection训练自己的数据集,这里我使用的Kaggle的一个口罩数据集,本来是xml格式的,然后之前使用的是yolov5进行训练,所以将xml格式转换成了txt格式。现在使用MMDetection需要将其转换成coco格式,详细过程如下。

    1. 数据集准备

    这个没啥好说的,因为可以发现mmdet只支持voc格式'VOCDataset'或者是coco的格式'CocoDataset'所以对于自己的数据集,需要将标注文件转换成coco的格式或者是voc的格式。(没有yolo格式)

    一般来说,还是转化成coco格式比较方便,对标注的文件构建一个coco格式的json文件即可。

    • COCO 格式实例分割的必要键如下:
    {
        "images": [image],
        "annotations": [annotation],
        "categories": [category]
    }
    
    
    image = {
        "id": int,
        "width": int,
        "height": int,
        "file_name": str,
    }
    
    annotation = {
        "id": int,
        "image_id": int,
        "category_id": int,
        "segmentation": RLE or [polygon],
        "area": float,
        "bbox": [x,y,width,height],
        "iscrowd": 0 or 1,
    }
    
    categories = [{
        "id": int,
        "name": str,
        "supercategory": str,
    }]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    完整的coco格式介绍如下:https://cocodataset.org/#format-data,只要完成上面这些必要信息的编写,将自己数据集的相关标注格式构建成一个json文件即可,然后我们就可以CocoDataset用来训练和评估模型了。(同样的,你当然也可以转换成voc的格式VOCDataset,不过现在coco格式已经成为了主流)

    • 参考coco的数据存放格式:

    在这里插入图片描述
    对于存放图像的训练数据集或者是验证数据集,这里coco会单独为其构建一个标注的json文件。基于此进行参考。

    由于我们本来是跑yolov5的项目,属于数据集的构建的本来格式是:

    |-----image
    	|-----train
    	|-----val
    |-----label
    	|-----train
    	|-----val
    |-----mask.yml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中,label中存放是txt文件,image中存放的是具体的图像文件,用过yolov5项目的朋友应该都知道,这里就不详细介绍了。这里,我们参考coco数据集的格式,将我们自己的数据集进行相同的配置,首先需要将txt标注文件转换成一个coco格式的json文件。

    • yolo2coco转换脚本:
    import os
    import json
    import random
    import time
    from PIL import Image
    import csv
    
    # print('chance the file')
    
    task = 'train'  # or 'val'
    # root = r'/home/lab/LLC/mmdetection/mmdetection-2.24.0'
    coco_format_save_path = '../../data/mask/annotations'  # 要生成的标准coco格式标签所在文件夹
    yolo_format_annotation_path = '../../data/mask/labels/' + task  # yolo格式标签所在文件夹
    img_pathDir = '../../data/mask/images/' + task  # 图片所在文件夹
    
    assert os.path.exists(coco_format_save_path), "Please mkdir the save path"
    
    # 类别设置
    categories = []
    class_names = ['with_mask', 'without_mask', 'mask_weared_incorrect']
    for label in class_names:
        categories.append({'id': class_names.index(label), 'name': label, 'supercategory': ""})
    
    write_json_context = dict()  # 写入.json文件的大字典
    # write_json_context['licenses'] = [{'name': "", 'id': 0, 'url': ""}]
    # write_json_context['info'] = {'contributor': "", 'date_created': "", 'description': "",
    #                               'url': "", 'version': "", 'year': ""}
    write_json_context['categories'] = categories
    write_json_context['images'] = []
    write_json_context['annotations'] = []
    
    # 接下来的代码主要添加'images'和'annotations'的key值
    imageFileList = os.listdir(img_pathDir)
    # 遍历该文件夹下的所有文件,并将所有文件名添加到列表中
    img_id = 0  # 图片编号
    anno_id = 0     # 标注标号
    for i, imageFile in enumerate(imageFileList):
        if '_' not in imageFile:
            img_id += 1
            imagePath = os.path.join(img_pathDir, imageFile)  # 获取图片的绝对路径
            image = Image.open(imagePath)  # 读取图片
            W, H = image.size  # 获取图片的高度宽度
            img_context = {}  # 使用一个字典存储该图片信息
            # img_name=os.path.basename(imagePath)
            img_context['id'] = img_id  # 每张图像的唯一ID索引
            img_context['width'] = W
            img_context['height'] = H
            img_context['file_name'] = imageFile
            # img_context['license'] = 0
            # img_context['flickr_url'] = ""
            # img_context['color_url'] = ""
            # img_context['date_captured'] = ""
    
            write_json_context['images'].append(img_context)  # 将该图片信息添加到'image'列表中
    
            txtFile = imageFile.split('.')[0] + '.txt'  # 获取该图片获取的txt文件
            with open(os.path.join(yolo_format_annotation_path, txtFile), 'r') as fr:
                lines = fr.readlines()  # 读取txt文件的每一行数据,lines2是一个列表,包含了一个图片的所有标注信息
    
            for j, line in enumerate(lines):
                anno_id += 1  # 标注的id从1开始
                bbox_dict = {}  # 将每一个bounding box信息存储在该字典中
    
                class_id, x, y, w, h = line.strip().split(' ')  # 获取每一个标注框的详细信息
                class_id, x, y, w, h = int(class_id), float(x), float(y), float(w), float(h)  # 将字符串类型转为可计算的int和float类型
    
                # 坐标转换
                xmin = (x - w / 2) * W
                ymin = (y - h / 2) * H
                xmax = (x + w / 2) * W
                ymax = (y + h / 2) * H
                w = w * W
                h = h * H
                height, width = abs(ymax - ymin), abs(xmax - xmin)
    
                # bounding box的坐标信息
                bbox_dict['id'] = anno_id               # 每个标注信息的索引
                bbox_dict['image_id'] = img_id          # 当前图像的ID索引
                bbox_dict['category_id'] = class_id     # 类别信息
                bbox_dict['segmentation'] = [[xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax]]
                bbox_dict['area'] = height * width
                bbox_dict['bbox'] = [xmin, ymin, w, h]  # 注意目标类别要加一
                bbox_dict['iscrowd'] = 0
                bbox_dict['attributes'] = ""
    
                write_json_context['annotations'].append(bbox_dict)  # 将每一个由字典存储的bounding box信息添加到'annotations'列表中
    
    name = os.path.join(coco_format_save_path, task + '.json')
    with open(name, 'w') as fw:  # 将字典信息写入.json文件中
        json.dump(write_json_context, fw, indent=4, ensure_ascii=False)
    print('finish converters')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    ps:使用这个脚本需要自行设置路径和设置类别,将class_names设置为自己数据集的类别即可。

    然后,删除无关的文件夹,配置成如下结构即可。
    在这里插入图片描述


    2. 准备配置文件

    接着是准备配置,从而可以成功加载数据集。假设配置在目录下configs/balloon/并命名为mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_balloon.py,对于模型来说,首先需要先进行继承,然后再进行更改,参考配置如下:(个人认为的两个重点是设置dataset_type数据集类型,还需要设置类别名称与最后的预测类别数)

    # The new config inherits a base config to highlight the necessary modification
    _base_ = 'mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco.py'
    
    # We also need to change the num_classes in head to match the dataset's annotation
    model = dict(
        roi_head=dict(
            bbox_head=dict(num_classes=1),
            mask_head=dict(num_classes=1)))
    
    # Modify dataset related settings
    dataset_type = 'COCODataset'
    classes = ('balloon',)
    data = dict(
        train=dict(
            img_prefix='balloon/train/',
            classes=classes,
            ann_file='balloon/train/annotation_coco.json'),
        val=dict(
            img_prefix='balloon/val/',
            classes=classes,
            ann_file='balloon/val/annotation_coco.json'),
        test=dict(
            img_prefix='balloon/val/',
            classes=classes,
            ann_file='balloon/val/annotation_coco.json'))
    
    # We can use the pre-trained Mask RCNN model to obtain higher performance
    load_from = 'checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    上面是官方的参考,下面来对我们的数据集进行配置。

    • 数据集路径配置:
    train_dataset = dict(
        type='MultiImageMixDataset',
        dataset=dict(
            type=dataset_type,
            classes=classes,
            ann_file=data_root + 'annotations/train.json',
            img_prefix=data_root + 'train/',
            pipeline=[
                dict(type='LoadImageFromFile'),
                dict(type='LoadAnnotations', with_bbox=True)
            ],
            filter_empty_gt=False,
        ),
        pipeline=train_pipeline)
    ......
    
    data = dict(
        samples_per_gpu=2,
        workers_per_gpu=2,
        persistent_workers=True,
        train=train_dataset,
        val=dict(
            type=dataset_type,
            classes=classes,
            ann_file=data_root + 'annotations/val.json',
            img_prefix=data_root + 'val/',
            pipeline=test_pipeline),
        test=dict(
            type=dataset_type,
            classes=classes,
            ann_file=data_root + 'annotations/val.json',
            img_prefix=data_root + 'val/',
            pipeline=test_pipeline))
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    ps:ann_file表存放标注文件的路径,img_prefix表示存放图像的目录

    这里我使用的yolox-s模型进行训练,所以在对于的config/yolox目录下,改写yolox_s_8x8_300e_coco.py配置文件即可。

    主要的更改是自定义的数据集路径,已经模型设置,由于这里我的口罩检测数据集只有3类,所以在对于的num_classes设置为3.

    • 模型参数配置:
    # model settings
    model = dict(
        type='YOLOX',
        input_size=img_scale,
        random_size_range=(15, 25),
        random_size_interval=10,
        backbone=dict(type='CSPDarknet', deepen_factor=0.33, widen_factor=0.5),
        neck=dict(
            type='YOLOXPAFPN',
            in_channels=[128, 256, 512],
            out_channels=128,
            num_csp_blocks=1),
        bbox_head=dict(
            type='YOLOXHead', num_classes=3, in_channels=128, feat_channels=128),  # 需要改变为3类
        train_cfg=dict(assigner=dict(type='SimOTAAssigner', center_radius=2.5)),
        # In order to align the source code, the threshold of the val phase is
        # 0.01, and the threshold of the test phase is 0.001.
        test_cfg=dict(score_thr=0.01, nms=dict(type='nms', iou_threshold=0.65)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    此外,由于类别名称不一样,还需要设置名称。

    • 类别名称设置:
    # dataset settings
    data_root = 'data/mask/'
    dataset_type = 'CocoDataset'
    classes = ('with_mask', 'without_mask', 'mask_weared_incorrect')
    
    train_dataset = dict(
        type='MultiImageMixDataset',
        dataset=dict(
            type=dataset_type,
            classes=classes,
            ann_file=data_root + 'annotations/train.json',
            img_prefix=data_root + 'train/',
            pipeline=[
                dict(type='LoadImageFromFile'),
                dict(type='LoadAnnotations', with_bbox=True)
            ],
            filter_empty_gt=False,
        ),
        pipeline=train_pipeline)
    ...
    
    data = dict(
        samples_per_gpu=2,
        workers_per_gpu=2,
        persistent_workers=True,
        train=train_dataset,
        val=dict(
            type=dataset_type,
            classes=classes,
            ann_file=data_root + 'annotations/val.json',
            img_prefix=data_root + 'val/',
            pipeline=test_pipeline),
        test=dict(
            type=dataset_type,
            classes=classes,
            ann_file=data_root + 'annotations/val.json',
            img_prefix=data_root + 'val/',
            pipeline=test_pipeline))
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    3. 训练与推理

    设置完配置文件,即可进行训练与推理测试

    # Train:
    # 设置在单卡0卡上进行训练
    python tools/train.py configs/yolox/yolox_s_8x8_300e_mask.py --gpu-id 0
    # 设置在0,1,3卡上进行训练
    CUDA_VISIBLE_DEVICES=0,1,3 ./tools/dist_train.sh configs/yolox/yolox_s_8x8_300e_mask.py 3
    
    # Test
    python tools/test.py configs/yolox/yolox_s_8x8_300e_mask.py work_dirs/yolox_s_8x8_300e_mask/latest.pth --eval bbox --gpu-id 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    参考资料:

    https://mmdetection.readthedocs.io/en/latest/2_new_data_model.html

  • 相关阅读:
    JavaScript协程(function*/yield)转化为异步(async/await)
    Java日志系统之Logback
    Elasticsearch全文搜索技术之二kibana的简介和使用
    实战项目: 负载均衡
    Unity微信小游戏无法调起输入框
    ADS算力芯片的多模型架构研究
    在Android应用中通过Chaquopy使用Python
    一张图进阶 RocketMQ - 消息存储
    mock+封装axios+vuex分发
    Deepin常用环境配置
  • 原文地址:https://blog.csdn.net/weixin_44751294/article/details/126761534