前言
常见的语义分割数据集有VOC2012, MS COCO以及Cityscapes等。 今天我们介绍Cityscapes数据集
1. 数据集简介
Cityscapes数据集,即城市景观数据集,其中包含从50个不同城市的街景中记录的各种立体视频序列,除了更大的20000个弱注释帧之外,还有高质量的5000帧像素级注释。Cityscapes数据集共有fine和coarse两套评测标准,前者提供5000张精细标注的图像,后者提供5000张精细标注外加20000张粗糙标注的图像。一般都是拿这5000张精细标注(gt fine)的样本集来进行训练和评估的,图片可分为train、val、test总共5000张,2975张训练图,500张验证图和1525张测试图,每张图片大小都是1024x2048。
2. 数据集下载
链接:https://www.cityscapes-dataset.com/downloads/
官方的处理脚本地址:https://github.com/mcordts/cityscapesScripts
下载下面两个压缩包
分别解压之后包含两个文件夹:
- gtFine:每张原图对应4个标签文件
文件名 | 用处 |
---|---|
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:
train: 18个城市
val: 3个城市
test: 6个城市
这三个子目录的图片又以城市为单元来存放。这里解释下leftImg8bit的含义,因为cityscapes实际上来源于双摄像头拍摄的立体视频序列,所以这里的leftImg就是来自于左摄像头的图片,而8bit意味着该图片集都为每个分量为8bit的24位深度的图片。
3.数据集标签处理
当需要对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都会发生变化,这样就可以进行训练了。
4.数据集预处理
考虑到原有的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/')
5.精度指标
Cityscapes评测集给出的四项相关指标:IoU class 、 iIoU class、 IoU category 、 iIou category。
作为IoUclass之外的另外三样指标iIoUclass,IoUcategory,iIoUcategory,其设置的初衷在于对模型整体进行评估的基础上,从不同的角度对模型算法的效果进行探讨,以便进一步优化和改进模型的效果。
- IoUClass更多考虑的是全局场景的分割结果准确度,其所考量的是整个场景的划分准确度,因此Cityscapes评测集将其默认的优先参照指标。
- 而iIoUClass指标则是在IoUClass的基础上,对图像场景进行实例级的分割和结果预测。该指标以场景中的每个实例作为分割对象,类别相同的不同个体实例在该项评估中分别进行评测以得到最终的结果,该指标更加看重算法在各个实例之间的分割准确度。
- 此外,IoU指标又根据分割的粒度区分为Class和Category,其中19个细分类别(Class)被划分为7个粗分类别(Category)进行粗粒度的分割,并给出相应的评估指标IoUCategory和iIoUCategory。
参考链接:
1.https://blog.csdn.net/chenzhoujian_/article/details/106874950?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_utm_term-0&spm=1001.2101.3001.4242
2.https://blog.csdn.net/xijuezhu8128/article/details/111165116?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161931649816780261921033%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=161931649816780261921033&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-1-111165116.pc_search_result_no_baidu_js&utm_term=cityscapes