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_splitdef 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_maskif __name__ == '__main__':# 1.label设置:# 根据根据json文件中的shape情况,分别设置每个label的名称、类型、以及颜色,其中颜色要从1开始编号,因为背景为0class_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_DB1saved_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/trainingtmp_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/validationtmp_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 isincluded 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。
目前就想到这么多,有什么不懂的可以问,后续我想起来也会继续更新