labelme标注数据转mask(用于mmsegmentation)

mmsegmentation原本支持的数据格式主要有以下几种:

mmsegmentation
├── mmseg
├── tools
├── configs
├── data
│   ├── cityscapes
│   │   ├── leftImg8bit
│   │   │   ├── train
│   │   │   ├── val
│   │   ├── gtFine
│   │   │   ├── train
│   │   │   ├── val
│   ├── VOCdevkit
│   │   ├── VOC2012
│   │   │   ├── JPEGImages
│   │   │   ├── SegmentationClass
│   │   │   ├── ImageSets
│   │   │   │   ├── Segmentation
│   │   ├── VOC2010
│   │   │   ├── JPEGImages
│   │   │   ├── SegmentationClassContext
│   │   │   ├── ImageSets
│   │   │   │   ├── SegmentationContext
│   │   │   │   │   ├── train.txt
│   │   │   │   │   ├── val.txt
│   │   │   ├── trainval_merged.json
│   │   ├── VOCaug
│   │   │   ├── dataset
│   │   │   │   ├── cls
│   ├── ade
│   │   ├── ADEChallengeData2016
│   │   │   ├── annotations
│   │   │   │   ├── training
│   │   │   │   ├── validation
│   │   │   ├── images
│   │   │   │   ├── training
│   │   │   │   ├── validation
│   ├── coco_stuff10k
│   │   ├── images
│   │   │   ├── train2014
│   │   │   ├── test2014
│   │   ├── annotations
│   │   │   ├── train2014
│   │   │   ├── test2014
│   │   ├── imagesLists
│   │   │   ├── train.txt
│   │   │   ├── test.txt
│   │   │   ├── all.txt
│   ├── coco_stuff164k
│   │   ├── images
│   │   │   ├── train2017
│   │   │   ├── val2017
│   │   ├── annotations
│   │   │   ├── train2017
│   │   │   ├── val2017
│   ├── CHASE_DB1
│   │   ├── images
│   │   │   ├── training
│   │   │   ├── validation
│   │   ├── annotations
│   │   │   ├── training
│   │   │   ├── validation
│   ├── DRIVE
│   │   ├── images
│   │   │   ├── training
│   │   │   ├── validation
│   │   ├── annotations
│   │   │   ├── training
│   │   │   ├── validation
│   ├── HRF
│   │   ├── images
│   │   │   ├── training
│   │   │   ├── validation
│   │   ├── annotations
│   │   │   ├── training
│   │   │   ├── validation
│   ├── STARE
│   │   ├── images
│   │   │   ├── training
│   │   │   ├── validation
│   │   ├── annotations
│   │   │   ├── training
│   │   │   ├── validation
|   ├── dark_zurich
|   │   ├── gps
|   │   │   ├── val
|   │   │   └── val_ref
|   │   ├── gt
|   │   │   └── val
|   │   ├── LICENSE.txt
|   │   ├── lists_file_names
|   │   │   ├── val_filenames.txt
|   │   │   └── val_ref_filenames.txt
|   │   ├── README.md
|   │   └── rgb_anon
||   ├── val
||   └── val_ref
|   ├── NighttimeDrivingTest
|   |   ├── gtCoarse_daytime_trainvaltest
|   |   │   └── test
|   |   │       └── night
|   |   └── leftImg8bit
|   |   |   └── test
|   |   |       └── night
│   ├── loveDA
│   │   ├── img_dir
│   │   │   ├── train
│   │   │   ├── val
│   │   │   ├── test
│   │   ├── ann_dir
│   │   │   ├── train
│   │   │   ├── val
│   ├── potsdam
│   │   ├── img_dir
│   │   │   ├── train
│   │   │   ├── val
│   │   ├── ann_dir
│   │   │   ├── train
│   │   │   ├── val
│   ├── vaihingen
│   │   ├── img_dir
│   │   │   ├── train
│   │   │   ├── val
│   │   ├── ann_dir
│   │   │   ├── train
│   │   │   ├── val
│   ├── iSAID
│   │   ├── img_dir
│   │   │   ├── train
│   │   │   ├── val
│   │   │   ├── test
│   │   ├── ann_dir
│   │   │   ├── train
│   │   │   ├── val
│   ├── synapse
│   │   ├── img_dir
│   │   │   ├── train
│   │   │   ├── val
│   │   ├── ann_dir
│   │   │   ├── train
│   │   │   ├── val
│   ├── REFUGE
│   │   ├── images
│   │   │   ├── training
│   │   │   ├── validation
│   │   │   ├── test
│   │   ├── annotations
│   │   │   ├── training
│   │   │   ├── validation
│   │   │   ├── test
│   ├── mapillary
│   │   ├── training
│   │   │   ├── images
│   │   │   ├── v1.2
|   │   │   │   ├── instances
|   │   │   │   ├── labels
|   │   │   │   └── panoptic
│   │   │   ├── v2.0
|   │   │   │   ├── instances
|   │   │   │   ├── labels
|   │   │   │   ├── panoptic
|   │   │   │   └── polygons
│   │   ├── validation
│   │   │   ├── images
|   │   │   ├── v1.2
|   │   │   │   ├── instances
|   │   │   │   ├── labels
|   │   │   │   └── panoptic
│   │   │   ├── v2.0
|   │   │   │   ├── instances
|   │   │   │   ├── labels
|   │   │   │   ├── panoptic
|   │   │   │   └── polygons
│   ├── bdd100k
│   │   ├── images
│   │   │   └── 10k
|   │   │   │   ├── test
|   │   │   │   ├── train
|   │   │   │   └── val
│   │   └── labels
│   │   │   └── sem_seg
|   │   │   │   ├── colormaps
|   │   │   │   │   ├──train
|   │   │   │   │   └──val
|   │   │   │   ├── masks
|   │   │   │   │   ├──train
|   │   │   │   │   └──val
|   │   │   │   ├── polygons
|   │   │   │   │   ├──sem_seg_train.json
|   │   │   │   │   └──sem_seg_val.json
|   │   │   │   └── rles
|   │   │   │   │   ├──sem_seg_train.json
|   │   │   │   │   └──sem_seg_val.json
│   ├── nyu
│   │   ├── images
│   │   │   ├── train
│   │   │   ├── test
│   │   ├── annotations
│   │   │   ├── train
│   │   │   ├── test
│   ├── HSIDrive20
│   │   ├── images
│   │   │   ├── train
│   │   │   ├── validation
│   │   │   ├── test
│   │   ├── annotations
│   │   │   ├── train
│   │   │   ├── validation
│   │   │   ├── test

labelme标注得到的数据是jpg(或者png)、json混合在一个文件夹里的。如果用于语义分割,通常需要将json数据转为mask图像。mmseg支持的数据集类型比较多,对应的也有自己的数据集结构。对此,我选择的是按照DRIVE,CHASE-DB1这些数据的格式来做的,即:

│   ├── DRIVE
│   │   ├── images
│   │   │   ├── training
│   │   │   ├── validation
│   │   ├── annotations
│   │   │   ├── training
│   │   │   ├── validation

代码参考了https://github.com/TommyZihao/Label2Everything/tree/main/labelme2mask,
整体代码如下:

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


def labelme2mask_single_img(img_path, labelme_json_path):
    '''
    输入原始图像路径和labelme标注路径,输出 mask
    '''

    img_bgr = cv2.imread(img_path)
    # 创建空白图像 0-背景
    img_mask = np.zeros(img_bgr.shape[:2])

    with open(labelme_json_path, 'r', encoding='utf-8') as f:
        labelme = json.load(f)

    # 按顺序遍历每一个类别
    for one_class in class_info:
        # 遍历所有标注,找到属于当前类别的标注
        for each in labelme['shapes']:
            if each['label'] == one_class['label']:
                # polygon 多段线标注
                if one_class['type'] == 'polygon':
                    # 获取点的坐标
                    points = [np.array(each['points'], dtype=np.int32).reshape((-1, 1, 2))]
                    # 在空白图上画 mask(闭合区域)
                    img_mask = cv2.fillPoly(img_mask, points, color=one_class['color'])

                # line 或者 linestrip 线段标注
                elif one_class['type'] == 'line' or one_class['type'] == 'linestrip':
                    # 获取点的坐标
                    points = [np.array(each['points'], dtype=np.int32).reshape((-1, 1, 2))]
                    # 在空白图上画 mask(非闭合区域)
                    img_mask = cv2.polylines(img_mask, points, isClosed=False, color=one_class['color'],
                                             thickness=one_class['thickness'])

                # circle 圆形标注
                elif one_class['type'] == 'circle':
                    points = np.array(each['points'], dtype=np.int32)
                    center_x, center_y = points[0][0], points[0][1]  # 圆心点坐标
                    edge_x, edge_y = points[1][0], points[1][1]  # 圆周点坐标
                    radius = np.linalg.norm(np.array([center_x, center_y] - np.array([edge_x, edge_y]))).astype(
                        'uint32')  # 半径
                    img_mask = cv2.circle(img_mask, (center_x, center_y), radius, one_class['color'],
                                          one_class['thickness'])

                else:
                    print('未知标注类型', one_class['type'])

    return img_mask


if __name__ == '__main__':
    #  1.label设置:
    # 根据根据json文件中的shape情况,分别设置每个label的名称、类型、以及颜色,其中颜色要从1开始编号,因为背景为0
    class_info = [
        {'label': 'space', 'type': 'polygon', 'color': 1},  # polygon 多段线
        # {'label': 'condyl', 'type': 'polygon', 'color': 1},
        # {'label': 'fossa', 'type': 'polygon', 'color': 3},
        # {'label': 'bone marrow', 'type': 'polygon', 'color': 4},
        # {'label':'clock', 'type':'circle', 'color':11, 'thickness':-1},   # circle 圆形,-1表示填充
        # {'label':'lane', 'type':'line', 'color':12, 'thickness':5},       # line 两点线段,填充线宽
        # {'label':'sign', 'type':'linestrip', 'color':13, 'thickness':3}   # linestrip 多段线,填充线宽
    ]

    #  2.路径设置:
    # labelme文件路径
    labelme_path = r"F:\Data\doctor_research\Arthritis"
    # 储存文件路径,这边准备采用的是chase_db1的数据格式,为了减少代码修改量,我直接命名为CHASE_DB1
    saved_path = r"F:\Data\doctor_research\DRIVE_space"
    if not os.path.exists(saved_path):
        os.makedirs(saved_path)


    # 1.images文件路径:
    ## images路径:
    img_path = os.path.join(saved_path, 'images')
    ## training images路径:
    train_img_path = os.path.join(img_path, 'training')
    ## validation images路径:
    val_img_path = os.path.join(img_path, 'validation')

    # 创建文件夹
    if not os.path.exists(img_path):
        os.makedirs(img_path)
    if not os.path.exists(train_img_path):
        os.makedirs(train_img_path)
    if not os.path.exists(val_img_path):
        os.makedirs(val_img_path)

    # 2.mask文件路径
    ## mask路径:
    mask_path = os.path.join(saved_path, 'annotations')
    ## training mask路径:
    train_annotation_path = os.path.join(mask_path, 'training')
    ## validation mask路径:
    val_annotation_path = os.path.join(mask_path, 'validation')
    print('reading...')

    #  创建文件夹
    if not os.path.exists(mask_path):
        os.makedirs(mask_path)
    if not os.path.exists(train_annotation_path):
        os.makedirs(train_annotation_path)
    if not os.path.exists(val_annotation_path):
        os.makedirs(val_annotation_path)

    # 获取images目录下所有的json文件列表
    json_list_path = glob.glob(labelme_path + "/*.json")
    print('数据集总量为:', len(json_list_path))

    # 划分训练集和验证集
    # 验证集和训练集比例为1:9,可以根据自己需要更改
    train_path, val_path = train_test_split(json_list_path, test_size=0.1, train_size=0.9)
    print("训练集数据量为:", len(train_path), '验证集数据量为:', len(val_path))

    # 依次对两个数据集进行转换:
    # 对训练集进行转换:
    for train_json in tqdm(train_path):
        try:
            # 1.将训练图像复制到images/training
            # 获取对应的图像文件地址
            train_img = os.path.join(train_json.split('.')[0] + '.png')

            # 分离文件名与路径
            path_1, img_name_1 = os.path.split(train_img)
            # 复制到新的文件夹
            tmp_img_path_1 = os.path.join(train_img_path, img_name_1)
            shutil.copy(train_img, tmp_img_path_1)

            # 2.将json文件转为mask图像
            train_img_mask = labelme2mask_single_img(train_img, train_json)
            # 将mask文件保存到annotations/training
            tmp_mask_path_1 = os.path.join(train_annotation_path, img_name_1)
            cv2.imwrite(tmp_mask_path_1, train_img_mask)

        except Exception as E:
            print(train_json, '转换失败', E)

    # 对测试集进行转换:
    for val_json in tqdm(val_path):
        try:
            # 1.将测试集图像复制到images/validation
            # 获取对应的图像文件地址
            val_img = os.path.join(val_json.split('.')[0] + '.png')

            # 分离文件名与路径
            path_2, img_name_2 = os.path.split(val_img)
            # 复制到新的文件夹
            tmp_img_path_2 = os.path.join(val_img_path, img_name_2)
            shutil.copy(val_img, tmp_img_path_2)

            # 2.将json文件转为mask图像
            val_img_mask = labelme2mask_single_img(val_img, val_json)
            # 将mask文件保存到annotations/validation
            tmp_mask_path_2 = os.path.join(val_annotation_path, img_name_2)
            cv2.imwrite(tmp_mask_path_2, val_img_mask)

        except Exception as E:
            print(val_json, '转换失败', E)

这边需要修改的就是路径以及class_info。具体怎么使用mmseg训练自己的数据集,我另外一篇blog提到了。这里简单提一下,主要是修改几个地方:

1.修改num_classes

这个在config/base/models里找到自己想用的那个模型修改就好。要记得把自己的类别数量加1,因为背景也算1类。

2.修改路径

这个在config/base/datasets,找到自己想用的数据集格式,比如DRIVE,就在drive.py里面,改一下路径。

3.修改label种类

在mmseg/datasets中对应的数据集格式里:

class DRIVEDataset(BaseSegDataset):
    """DRIVE dataset.

    In segmentation map annotation for DRIVE, 0 stands for background, which is
    included in 2 categories. ``reduce_zero_label`` is fixed to False. The
    ``img_suffix`` is fixed to '.png' and ``seg_map_suffix`` is fixed to
    '_manual1.png'.
    """
    METAINFO = dict(
        classes=('background', 'space'),
        palette=[[120, 120, 120], [6, 230, 230]])

    def __init__(self,
                 img_suffix='.png',
                 # seg_map_suffix='_manual1.png',
                 seg_map_suffix='.png',
                 reduce_zero_label=False,
                 **kwargs) -> None:
        super().__init__(
            img_suffix=img_suffix,
            seg_map_suffix=seg_map_suffix,
            reduce_zero_label=reduce_zero_label,
            **kwargs)
        assert fileio.exists(
            self.data_prefix['img_path'], backend_args=self.backend_args)

需要修改的有3处:
1.classes,把自己的label加上去就行
2.palette,对应label的颜色,别重复就行
3.seg_map_suffix,这个要注意改一下,是各个数据集自己用来识别是否为mask的一个文件拓展名,嫌麻烦就删掉了

4.修改训练参数

这方面看个人需求,主要在config/base/default_runtime.py和config/base/schedules/schedule_40k.py里修改,我主要是改一下学习率和迭代次数。单卡训练的话,0.01的学习率有点大,我一般改为0.0025,迭代次数从文件名就可以看出来,40K就是40000次,注意,这里是iteration,不是epoch。

目前就想到这么多,有什么不懂的可以问,后续我想起来也会继续更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值