常见的语义分割数据集有VOC2012, MS COCO以及Cityscapes等。 今天我们介绍Cityscapes数据集
Cityscapes数据集,即城市景观数据集,其中包含从50个不同城市的街景中记录的各种立体视频序列,除了更大的20000个弱注释帧之外,还有高质量的5000帧像素级注释。Cityscapes数据集共有fine和coarse两套评测标准,前者提供5000张精细标注的图像,后者提供5000张精细标注外加20000张粗糙标注的图像。一般都是拿这5000张精细标注(gt fine)的样本集来进行训练和评估的,图片可分为train、val、test总共5000张,2975张训练图,500张验证图和1525张测试图,每张图片大小都是1024x2048。
链接:https://www.cityscapes-dataset.com/downloads/
官方的处理脚本地址:https://github.com/mcordts/cityscapesScripts
下载下面两个压缩包
分别解压之后包含两个文件夹:
文件名 | 用处 |
---|---|
bochum_xxxxxx_xxxxxx_gtFine_color.png | 可视化出来的彩色图像,对应第一部分表格最后一列 |
bochum_xxxxxx_xxxxxx_gtFine_instanceIds.png | 实例分割标签 |
bochum_xxxxxx_xxxxxx_gtFine_labelIds.png | 对应第一部分id列 |
bochum_xxxxxx_xxxxxx_gtFine_polygons.png | 手工标注的第一手数据 |
对样本图片对应的就是标注目录,即gtFine,显然这里的fine就是精细标注的意思。gtFine下面也是分为train, test以及val,然后它们的子目录也是以城市为单位来放置图片。这些都是和leftImg8bit的一一对应。 不同的是,在城市子目录下面,每张样本图片对应有6个标注文件,如下所示:
注意点
原始精细标注数据集里面其实每张图片只对应四张标注文件:xxx_gtFine_color.png, xxx_gtFine_instanceIds.png, xxx_gtFine_labelsIds.png以及xxx_gtFine_polygons.json。 xxx_color.png是标注的可视化图片,真正对训练有用的是后面三个文件。xxx_instanceIds.png是用来做实例分割训练用的,而xxx_labelsIds.png是语义分割训练需要的。它们的像素值就是class值。而最后一个文件xxx_polygons.json是用labelme工具标注后所生成的文件,里面主要记录了每个多边形标注框上的点集坐标。
这三个子目录的图片又以城市为单元来存放。这里解释下leftImg8bit的含义,因为cityscapes实际上来源于双摄像头拍摄的立体视频序列,所以这里的leftImg就是来自于左摄像头的图片,而8bit意味着该图片集都为每个分量为8bit的24位深度的图片。
当需要对label进行处理(例如只识别两个类别)
打开下载好的脚本中heplers里面的labels.py
# name id trainId category catId color
Label( 'unlabeled' , 0 , 255 , 'void' , 0 ( 0, 0, 0) ),
Label( 'ego vehicle' , 1 , 255 , 'void' , 0 , ( 0, 0, 0) ),
Label( 'rectification border' , 2 , 255 , 'void' , 0 , ( 0, 0, 0) ),
Label( 'out of roi' , 3 , 255 , 'void' , 0 , ( 0, 0, 0) ),
Label( 'static' , 4 , 255 , 'void' , 0 , ( 0, 0, 0) ),
Label( 'dynamic' , 5 , 255 , 'void' , 0 , (111, 74, 0) ),
Label( 'ground' , 6 , 255 , 'void' , 0 , ( 81, 0, 81) ),
Label( 'road' , 7 , 0 , 'flat' , 1 , (128, 64,128) ),
Label( 'sidewalk' , 8 , 1 , 'flat' , 1 , (244, 35,232) ),
Label( 'parking' , 9 , 255 , 'flat' , 1 , (250,170,160) ),
Label( 'rail track' , 10 , 255 , 'flat' , 1 , (230,150,140) ),
Label( 'building' , 11 , 2 , 'construction' , 2 , ( 70, 70, 70) ),
Label( 'wall' , 12 , 3 , 'construction' , 2 , (102,102,156) ),
Label( 'fence' , 13 , 4 , 'construction' , 2 , (190,153,153) ),
Label( 'guard rail' , 14 , 255 , 'construction' , 2 , (180,165,180) ),
Label( 'bridge' , 15 , 255 , 'construction' , 2 , (150,100,100) ),
Label( 'tunnel' , 16 , 255 , 'construction' , 2 , (150,120, 90) ),
Label( 'pole' , 17 , 5 , 'object' , 3 , (153,153,153) ),
Label( 'polegroup' , 18 , 255 , 'object' , 3 , (153,153,153) ),
Label( 'traffic light' , 19 , 6 , 'object' , 3 , (250,170, 30) ),
Label( 'traffic sign' , 20 , 7 , 'object' , 3 , (220,220, 0) ),
Label( 'vegetation' , 21 , 8 , 'nature' , 4 , (107,142, 35) ),
Label( 'terrain' , 22 , 9 , 'nature' , 4 , (152,251,152) ),
Label( 'sky' , 23 , 10 , 'sky' , 5 , ( 70,130,180) ),
Label( 'person' , 24 , 11 , 'human' , 6 , (220, 20, 60) ),
Label( 'rider' , 25 , 12 , 'human' , 6 , (255, 0, 0) ),
Label( 'car' , 26 , 13 , 'vehicle' , 7 , ( 0, 0,142) ),
Label( 'truck' , 27 , 14 , 'vehicle' , 7 , ( 0, 0, 70) ),
Label( 'bus' , 28 , 15 , 'vehicle' , 7 , ( 0, 60,100) ),
Label( 'caravan' , 29 , 255 , 'vehicle' , 7 , ( 0, 0, 90) ),
Label( 'trailer' , 30 , 255 , 'vehicle' , 7 , ( 0, 0,110) ),
Label( 'train' , 31 , 16 , 'vehicle' , 7 , ( 0, 80,100) ),
Label( 'motorcycle' , 32 , 17 , 'vehicle' , 7 , ( 0, 0,230) ),
Label( 'bicycle' , 33 , 18 , 'vehicle' , 7 , (119, 11, 32) ),
Label( 'license plate' , -1 , -1 , 'vehicle' , 7 , ( 0, 0,142) ),
主要改动下面这两项:
trainId就是标签的像素值,代表类别
ignoreInEval都设为False
例如,将road设为1,其它设为0,就是训练两类
createTrainIdLabelImgs的处理
注意将代码中的路径换成自己的
之后就直接运行这个createTrainIdLabelImgs.py脚本文件就可以了
运行成功后,直接打开cityscapes数据集的标签,每张图片的gtFine_labelTrainIds都会发生变化,这样就可以进行训练了。
考虑到原有的1024x2048的图像太大,显存实在是紧张,所以论文里面一般都会对其进行裁剪,我遵循HRNet中的设置,将其处理成512x1024大小的训练图片,为了获得多尺度输入,采用resize和slide window crop两种方式:
import numpy as np
import os
from glob import glob
import cv2
def genarate_dataset(data_dir, convert_dict, target_size, save_dir=None, flags=['train', 'val', 'test']):
for flag in flags:
save_num = 0
# 获取待裁剪影像和label的路径
images_paths = glob(data_dir + "leftImg8bit/" + flag + "/*/*_leftImg8bit.png")
images_paths = sorted(images_paths)
gts_paths = glob(data_dir + "gtFine/" + flag + "/*/*gtFine_labelIds.png")
gts_paths = sorted(gts_paths)
print(len(gts_paths))
# 遍历每一张图片
for image_path, gt_path in zip(images_paths, gts_paths):
# 确保图片和标签对应
image_name = os.path.split(image_path)[-1].split('_')[0:3]
# e.g. ['zurich', '000121', '000019']
gt_name = os.path.split(gt_path)[-1].split('_')[0:3]
assert image_name == gt_name
# 读取图片和标签,并转换标签为0-19(20类,0是未分类)
image = cv2.imread(image_path)
gt = cv2.imread(gt_path, 0)
binary_gt = np.zeros_like(gt)
# 循环遍历字典的key,并累加value值
for key in convert_dict.keys():
index = np.where(gt == key)
binary_gt[index] = convert_dict[key]
# 尺寸
target_height, target_width = target_size
# ----------------- resize ----------------- #
# resize, 参数输入是 宽×高, 不是常用的高×宽(多少行多少列)
resize_image = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
resize_gt = cv2.resize(binary_gt, (target_width, target_height), interpolation=cv2.INTER_NEAREST)
# save_path
image_save_path = save_dir + flag + "/images/" + str(save_num) + "_resize.png"
gt_save_path = save_dir + flag + "/gts/" + str(save_num) + "_resize.png"
# save
cv2.imwrite(image_save_path, resize_image)
cv2.imwrite(gt_save_path, resize_gt)
# 每保存一次图片和标签,计数加一
save_num += 1
# ----------------- slide crop ----------------- #
h_arr = [0, 256, 512]
w_arr = [0, 512, 1024]
# 遍历长宽起始坐标列表,将原始图片随机裁剪为512大小
for h in h_arr:
for w in w_arr:
# crop
crop_image = image[h: h + target_height, w: w + target_width, :]
crop_gt = binary_gt[h: h + target_height, w: w + target_width]
# save_path
image_save_path = save_dir + flag + "/images/" + str(save_num) + "_crop.png"
gt_save_path = save_dir + flag + "/gts/" + str(save_num) + "_crop.png"
# save
cv2.imwrite(image_save_path, crop_image)
cv2.imwrite(gt_save_path, crop_gt)
# 每保存一次图片和标签,计数加一
save_num += 1
# 依据https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/helpers/labels.py
# 不同之处在于将trainId里面的255定为第0类,原本的0-18类向顺序后加`1`
pixLabels = { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0,
6: 0, 7: 1, 8: 2, 9: 0, 10: 0, 11: 3,
12: 4, 13: 5, 14: 0, 15: 0, 16: 0, 17: 6,
18: 0, 19: 7, 20: 8, 21: 9, 22: 10, 23: 11,
24: 12, 25: 13, 26: 14, 27: 15, 28: 16, 29: 0,
30: 0, 31: 17, 32: 18, 33: 19, -1: 0}
genarate_dataset(data_dir='../cityscapes/',
convert_dict=pixLabels,
target_size=(512, 1024),
save_dir='../CityScapesDataset/')
Cityscapes评测集给出的四项相关指标:IoU class 、 iIoU class、 IoU category 、 iIou category。
作为IoUclass之外的另外三样指标iIoUclass,IoUcategory,iIoUcategory,其设置的初衷在于对模型整体进行评估的基础上,从不同的角度对模型算法的效果进行探讨,以便进一步优化和改进模型的效果。