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。
目前就想到这么多,有什么不懂的可以问,后续我想起来也会继续更新