扫盲:mmdetection安装以及训练自己的数据集

扫盲:mmdetection安装以及训练自己的数据集,第1张

一、安装

# 创建环境名为mmdet
conda create -n mmdet python=3.7
# 激活环境mmdet
conda activate mmdet
# 安装pytorch1.6
# 安装torchvision0.7.0
# 安装cuda,此处注意cuda版本要对应上或电脑已经安装了更高的版本,cuda版本向下兼容,电脑安装最新的cuda并把驱动升级到最新。
conda install pytorch==1.6.0 torchvision==0.7.0 cudatoolkit=10.2 -c pytorch
# 安装对应版本的mmcv-full,这个是官方已经给编译过的,安装时注意版本与上一步相同。cu102为cudatoolkit10.2,torch1.6.0为pytorch1.6.0。对应不上安装时看不出来,用的时候必报错,而且找不到原因。而且这是官方编译好的,意味着不能瞎改pytorch版本。
pip install mmcv-full==1.4.6 -f https://download.openmmlab.com/mmcv/dist/cu102/torch1.6.0/index.html
# 安装mmdet,安装检测模块的需要的包
pip install mmdet
# 安装mmrotate,安装旋转检测模块需要的包,这个不用mmrotate可不安
pip install mmrotate
# 也可以用另一种方法安装mmrotate
git clone https://github.com/open-mmlab/mmrotate.git
cd mmrotate
pip install -r requirements/build.txt
pip install -v -e .  

1.报错不要慌,领悟每个命令行作用的同时,看看版本对没对上,csdn好多博文版本没对上或者用mim安装导致各种报错。

2.安装失败,这只是一种可能,看看是否安装了VS2019 C++buildtool,因为之前安装pycocotool的时候因为没安装这个工具导致安装不上。安装这个工具即使不需要,也没有任何害处,可能将来会用到。

二、demo演示文件测试效果

进入image_demo.py文件,读一读。

# ....
def parse_args():
    parser = ArgumentParser()
    parser.add_argument('img', help='Image file')
    parser.add_argument('config', help='Config file')
    parser.add_argument('checkpoint', help='Checkpoint file')
# ....

 稍加修改,让配置参数时,更加规整而且配置时一一对应,更加严谨。

# ....
def parse_args():
    parser = ArgumentParser()
    parser.add_argument('--img', help='Image file')
    parser.add_argument('--config', help='Config file')
    parser.add_argument('--checkpoint', help='Checkpoint file')
# ....

pycharm中配置参数:1.图片路径2.模型路径3.权重路径;此处的模型与权重最好时完全对应上。

--img
D:\Project\mmdetection-master\demo\demo.jpg
--config
D:\Project\mmdetection-master\configs\faster_rcnn\faster_rcnn_r50_fpn_1x_coco.py
--checkpoint
D:\Project\mmdetection-master\faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth

报个小错,无伤大雅。小小警告,一般都是告诉我xx已经淘汰了;被xx替代了;建议替换等等。

 结果出来了,意味着你的环境配置已经问题不大了,后面如果缺组件,在现在的环境内pip install xxx即可。

三、简单的口罩检测——训练自己的数据集

1.首先准备好数据集——一些未标注的图片

 2.用labelme标注软件进行标注

labelme安装很简单,在本环境安装就行。

# 安装pyqt5
pip install pyqt5
# 安装labelme
pip install labelme
# 运行labelme
labelme

标注过程过于简单,不赘述。注意:

(1)不要乱改标注后的json文件名,要一一对应。

(2)矩形框标注从左上到右下符合图片坐标的规定。

3.json to coco,格式转换。

 运行一下脚本,注意输入文件位置与输出文件位置。

import os
import json
import numpy as np
import glob
import shutil
import cv2
from sklearn.model_selection import train_test_split

np.random.seed(41)

classname_to_id = {
    "mask": 0, #改成自己的类别
    "person": 1
}


class Lableme2CoCo:

    def __init__(self):
        self.images = []
        self.annotations = []
        self.categories = []
        self.img_id = 0
        self.ann_id = 0

    def save_coco_json(self, instance, save_path):
        json.dump(instance, open(save_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)  # indent=2 更加美观显示

    # 由json文件构建COCO
    def to_coco(self, json_path_list):
        self._init_categories()
        for json_path in json_path_list:
            obj = self.read_jsonfile(json_path)
            self.images.append(self._image(obj, json_path))
            shapes = obj['shapes']
            for shape in shapes:
                annotation = self._annotation(shape)
                self.annotations.append(annotation)
                self.ann_id += 1
            self.img_id += 1
        instance = {}
        instance['info'] = 'spytensor created'
        instance['license'] = ['license']
        instance['images'] = self.images
        instance['annotations'] = self.annotations
        instance['categories'] = self.categories
        return instance

    # 构建类别
    def _init_categories(self):
        for k, v in classname_to_id.items():
            category = {}
            category['id'] = v
            category['name'] = k
            self.categories.append(category)

    # 构建COCO的image字段
    def _image(self, obj, path):
        image = {}
        from labelme import utils
        img_x = utils.img_b64_to_arr(obj['imageData'])
        h, w = img_x.shape[:-1]
        image['height'] = h
        image['width'] = w
        image['id'] = self.img_id
        image['file_name'] = os.path.basename(path).replace(".json", ".jpg")
        return image

    # 构建COCO的annotation字段
    def _annotation(self, shape):
        # print('shape', shape)
        label = shape['label']
        points = shape['points']
        annotation = {}
        annotation['id'] = self.ann_id
        annotation['image_id'] = self.img_id
        annotation['category_id'] = int(classname_to_id[label])
        annotation['segmentation'] = [np.asarray(points).flatten().tolist()]
        annotation['bbox'] = self._get_box(points)
        annotation['iscrowd'] = 0
        annotation['area'] = 1.0
        return annotation

    # 读取json文件,返回一个json对象
    def read_jsonfile(self, path):
        with open(path, "r", encoding='utf-8') as f:
            return json.load(f)

    # COCO的格式: [x1,y1,w,h] 对应COCO的bbox格式
    def _get_box(self, points):
        min_x = min_y = np.inf
        max_x = max_y = 0
        for x, y in points:
            min_x = min(min_x, x)
            min_y = min(min_y, y)
            max_x = max(max_x, x)
            max_y = max(max_y, y)
        return [min_x, min_y, max_x - min_x, max_y - min_y]
 #训练过程中,如果遇到Index put requires the source and destination dtypes match, got Long for the destination and Int for the source
#参考:https://github.com/open-mmlab/mmdetection/issues/6706
if __name__ == '__main__':
    labelme_path = "./labelme-data/maskdataset"
    saved_coco_path = "./labelme-data/coco-format"
    print('reading...')
    # 创建文件
    if not os.path.exists("%scoco/annotations/" % saved_coco_path):
        os.makedirs("%scoco/annotations/" % saved_coco_path)
    if not os.path.exists("%scoco/images/train2017/" % saved_coco_path):
        os.makedirs("%scoco/images/train2017" % saved_coco_path)
    if not os.path.exists("%scoco/images/val2017/" % saved_coco_path):
        os.makedirs("%scoco/images/val2017" % saved_coco_path)
    # 获取images目录下所有的joson文件列表
    print(labelme_path + "/*.json")
    json_list_path = glob.glob(labelme_path + "/*.json")
    print('json_list_path: ', len(json_list_path))
    # 数据划分,这里没有区分val2017和tran2017目录,所有图片都放在images目录下
    train_path, val_path = train_test_split(json_list_path, test_size=0.1, train_size=0.9)
    print("train_n:", len(train_path), 'val_n:', len(val_path))

    # 把训练集转化为COCO的json格式
    l2c_train = Lableme2CoCo()
    train_instance = l2c_train.to_coco(train_path)
    l2c_train.save_coco_json(train_instance, '%scoco/annotations/instances_train2017.json' % saved_coco_path)
    for file in train_path:
        # shutil.copy(file.replace("json", "jpg"), "%scoco/images/train2017/" % saved_coco_path)
        img_name = file.replace('json', 'jpg')
        temp_img = cv2.imread(img_name)
        try:
            cv2.imwrite("{}coco/images/train2017/{}".format(saved_coco_path, img_name.split('\\')[-1].replace('png', 'jpg')), temp_img)
        except Exception as e:
            print(e)
            print('Wrong Image:', img_name )
            continue
        print(img_name + '-->', img_name.replace('png', 'jpg'))

    for file in val_path:
        # shutil.copy(file.replace("json", "jpg"), "%scoco/images/val2017/" % saved_coco_path)
        img_name = file.replace('json', 'jpg')
        temp_img = cv2.imread(img_name)
        try:
            cv2.imwrite("{}coco/images/val2017/{}".format(saved_coco_path, img_name.split('\\')[-1].replace('png', 'jpg')), temp_img)
        except Exception as e:
            print(e)
            print('Wrong Image:', img_name)
            continue
        print(img_name + '-->', img_name.replace('png', 'jpg'))

    # 把验证集转化为COCO的json格式
    l2c_val = Lableme2CoCo()
    val_instance = l2c_val.to_coco(val_path)
    l2c_val.save_coco_json(val_instance, '%scoco/annotations/instances_val2017.json' % saved_coco_path)

路径分布

instances_train2017.json节取

{
 "info": "spytensor created",
 "license": [
  "license"
 ],
 "images": [
  {
   "height": 737,
   "width": 1024,
   "id": 0,
   "file_name": "19.jpg"
  },
  ...
 ],
 "annotations": [
  {
   "id": 0,
   "image_id": 0,
   "category_id": 0,
   "segmentation": [
    [
     52.74809160305344,
     350.9083969465649,
     191.6793893129771,
     457.0152671755725
    ]
   ],
   "bbox": [
    52.74809160305344,
    350.9083969465649,
    138.93129770992365,
    106.1068702290076
   ],
   "iscrowd": 0,
   "area": 1.0
  },
  ...
 ],
 "categories": [
  {
   "id": 0,
   "name": "mask"
  },
  {
   "id": 1,
   "name": "person"
  }
 ]
}

在 json 文件中有三个必要的键:

images: 包含多个图片以及它们的信息的数组,例如 file_nameheightwidth 和 id

annotations: 包含多个实例标注信息的数组。

categories: 包含多个类别名字和 ID 的数组。

4.对应自己的数据集去修改源码的类别

因为采用的时coco格式,故去修改源码中coco(80个类别)的类别,改成自己数据集的两个类别。

1.修改第一处

mmdet\core\evaluation\class_names.py
# ...
def coco_classes():
    # return [
    #     'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',
    #     'truck', 'boat', 'traffic_light', 'fire_hydrant', 'stop_sign',
    #     'parking_meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',
    #     'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',
    #     'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',
    #     'sports_ball', 'kite', 'baseball_bat', 'baseball_glove', 'skateboard',
    #     'surfboard', 'tennis_racket', 'bottle', 'wine_glass', 'cup', 'fork',
    #     'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',
    #     'broccoli', 'carrot', 'hot_dog', 'pizza', 'donut', 'cake', 'chair',
    #     'couch', 'potted_plant', 'bed', 'dining_table', 'toilet', 'tv',
    #     'laptop', 'mouse', 'remote', 'keyboard', 'cell_phone', 'microwave',
    #     'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',
    #     'scissors', 'teddy_bear', 'hair_drier', 'toothbrush'
    # ]
    return [
        'mask', 'person', 
    ]
# ...

把80类注释掉,改成自己的类别,不改就会出现类别的检测错误,mask检测成person,person检测成bicycle。

2.修改第二处

mmdet\datasets\coco.py
class CocoDataset(CustomDataset):

    # CLASSES = ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus',
    #            'train', 'truck', 'boat', 'traffic light', 'fire hydrant',
    #            'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog',
    #            'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe',
    #            'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
    #            'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat',
    #            'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
    #            'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl',
    #            'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',
    #            'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
    #            'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop',
    #            'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',
    #            'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock',
    #            'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush')
    #
    # PALETTE = [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230),
    #            (106, 0, 228), (0, 60, 100), (0, 80, 100), (0, 0, 70),
    #            (0, 0, 192), (250, 170, 30), (100, 170, 30), (220, 220, 0),
    #            (175, 116, 175), (250, 0, 30), (165, 42, 42), (255, 77, 255),
    #            (0, 226, 252), (182, 182, 255), (0, 82, 0), (120, 166, 157),
    #            (110, 76, 0), (174, 57, 255), (199, 100, 0), (72, 0, 118),
    #            (255, 179, 240), (0, 125, 92), (209, 0, 151), (188, 208, 182),
    #            (0, 220, 176), (255, 99, 164), (92, 0, 73), (133, 129, 255),
    #            (78, 180, 255), (0, 228, 0), (174, 255, 243), (45, 89, 255),
    #            (134, 134, 103), (145, 148, 174), (255, 208, 186),
    #            (197, 226, 255), (171, 134, 1), (109, 63, 54), (207, 138, 255),
    #            (151, 0, 95), (9, 80, 61), (84, 105, 51), (74, 65, 105),
    #            (166, 196, 102), (208, 195, 210), (255, 109, 65), (0, 143, 149),
    #            (179, 0, 194), (209, 99, 106), (5, 121, 0), (227, 255, 205),
    #            (147, 186, 208), (153, 69, 1), (3, 95, 161), (163, 255, 0),
    #            (119, 0, 170), (0, 182, 199), (0, 165, 120), (183, 130, 88),
    #            (95, 32, 0), (130, 114, 135), (110, 129, 133), (166, 74, 118),
    #            (219, 142, 185), (79, 210, 114), (178, 90, 62), (65, 70, 15),
    #            (127, 167, 115), (59, 105, 106), (142, 108, 45), (196, 172, 0),
    #            (95, 54, 80), (128, 76, 255), (201, 57, 1), (246, 0, 122),
    #            (191, 162, 208)]
    CLASSES = ('mask', 'person')

    PALETTE = [(220, 20, 60), (119, 11, 32)]

类别和颜色做一下修改。

3.配置文件修改

生成自己的配置文件:因为项目自带的配置文件由于模块化设计,有继承关系,参数不能在一个文件上修改,配置参数很麻烦。采用一种方法:先把要用的模型的配置文件在train中作为配置参数运行一遍,会在work_dir文件中找到一个完整的配置文件。

1.复制想要的模型的配置文件绝对路径。

2.将绝对路径复制放进pycharm配置参数

3.运行后在work_dir中找到并改名

 4.修改输出类别数量,coco数据集80类,自己数据集2类。num_classes

# ...
bbox_head=dict(
        type='DeformableDETRHead',
        num_query=300,
        num_classes=80,
        in_channels=2048,
        sync_cls_avg_factor=True,
        as_two_stage=False,
# ...
# ...
bbox_head=dict(
        type='DeformableDETRHead',
        num_query=300,
        num_classes=2,
        in_channels=2048,
        sync_cls_avg_factor=True,
        as_two_stage=False,
# ...

5.修改训练集和验证集路径

# ...
    train=dict(
        type='CocoDataset',
        ann_file='D:\Project\mmdetection-master\mmdet\data\labelme-data\coco-formatcoco\annotations\instances_train2017.json',
        img_prefix='D:\Project\mmdetection-master\mmdet\data\labelme-data\coco-formatcoco\images\train2017',
# ...

# ...
    val=dict(
        type='CocoDataset',
        ann_file='D:\Project\mmdetection-master\mmdet\data\labelme-data\coco-formatcoco\annotations\instances_val2017.json',
        img_prefix='D:\Project\mmdetection-master\mmdet\data\labelme-data\coco-formatcoco\images\val2017',
# ...

6.加上预训练模型

load_from = 'D:\Project\mmdetection-master\deformable_detr_r50_16x2_50e_coco_20210419_220030-a12b9512.pth'

7.修改训练的配置

batch size和num_workers,这个算法很吃内存,batch size=1

# ...
data = dict(
    samples_per_gpu=1,
    workers_per_gpu=1,
# ...

修改这一块,50个epoch

evaluation = dict(interval=10, metric='bbox')
checkpoint_config = dict(interval=50)
log_config = dict(interval=10, hooks=[dict(type='TextLoggerHook')])
custom_hooks = [dict(type='NumClassCheckHook')]
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = 'D:\Project\mmdetection-master\deformable_detr_r50_16x2_50e_coco_20210419_220030-a12b9512.pth'
resume_from = None
workflow = [('train', 1)]
opencv_num_threads = 0
mp_start_method = 'fork'
auto_scale_lr = dict(enable=False, base_batch_size=32)

8.重新编译文件,修改完 class_names.py 和 voc.py 之后一定要重新编译代码(运行python setup.py install,要不报错AssertionError: The `num_classes` (80) in Shared2FCBBoxHead of MMDataParallel does not matche。

9.开始训练

 电脑配置不够,内存爆了,一顿 *** 作猛如虎,一看结果250,以后拿个简单的模型试一试,但是过程是可以借鉴的。

换Faster r-cnn,同样的思路,再用demo检测一下。

--img
D:\Project\mmdetection-master\demo\mask.jpg
--config
D:\Project\mmdetection-master\configs\faster_rcnn\my_faster_rcnn_r50_fpn_1x_coco.py
--checkpoint
D:\Project\mmdetection-master\tools\work_dirs\faster_rcnn_r50_fpn_1x_coco\epoch_30.pth

 这么少的数据集,能有这个效果,已经不错了。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1295086.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存