共计 4491 个字符,预计需要花费 12 分钟才能阅读完成。
背景与痛点
Cityscapes 数据集是自动驾驶和计算机视觉领域的重要基准数据集,包含来自 50 个不同城市的街景图像,涵盖各种天气条件和交通场景。该数据集提供了高质量的像素级标注,包括语义分割、实例分割和全景分割等多种任务。

然而,Cityscapes 数据集的官方下载流程相对复杂,需要注册、同意许可协议,并通过命令行工具下载。此外,数据集预处理也面临一些挑战:
- 数据量大(约 5GB 的压缩包,解压后约 70GB)
- 标注文件格式特殊(使用 JSON 和 PNG 混合格式)
- 类别标签需要转换才能用于训练
- 数据增强和批处理需要特别考虑内存效率
下载指南
1. 注册账户
首先访问 Cityscapes 官方网站(https://www.cityscapes-dataset.com/)并注册账户。注册后需要等待 1 - 2 个工作日获得下载权限。
2. 命令行下载(推荐)
获得权限后,可以使用官方提供的命令行工具下载:
-
安装下载工具
pip install cityscapesscripts -
下载数据集
python -m cityscapesscripts.download.downloader \ --username YOUR_USERNAME \ --password YOUR_PASSWORD \ --dataset all \ --quality fine \ --out_dir ./cityscapes_data
3. Python API 下载
对于更喜欢编程方式的用户,可以使用 Python API:
from cityscapesscripts.download import downloader
downloader.download_cityscapes(
username='YOUR_USERNAME',
password='YOUR_PASSWORD',
quality='fine', # or 'coarse'
dataset='all', # or specific subsets
out_dir='./cityscapes_data'
)
数据结构解析
下载完成后,目录结构如下:
cityscapes_data/
├── gtFine/
│ ├── train/
│ │ ├── city1/
│ │ │ ├── image1_gtFine_color.png
│ │ │ ├── image1_gtFine_instanceIds.png
│ │ │ ├── image1_gtFine_labelIds.png
│ │ │ └── image1_gtFine_polygons.json
│ │ └── ...
│ └── val/
│ └── ...
├── leftImg8bit/
│ ├── train/
│ │ ├── city1/
│ │ │ └── image1_leftImg8bit.png
│ │ └── ...
│ └── val/
│ └── ...
└── license.txt
leftImg8bit/: 包含原始 RGB 图像gtFine/: 包含精细标注_color.png: 彩色标注图(可视化用)_labelIds.png: 每个像素的类别 ID_instanceIds.png: 实例 ID(同一类别的不同实例)_polygons.json: 多边形标注信息
预处理实战
1. 加载和转换标注
Cityscapes 使用 19 个语义类别(忽略 id 为 0 的 ’void’ 类)。我们需要将 labelIds.png 转换为训练可用的格式:
import numpy as np
from PIL import Image
# 官方提供的类别映射
ID_TO_TRAINID = {
0: 255, 1: 255, 2: 255, 3: 255, 4: 255, 5: 255, 6: 255,
7: 0, 8: 1, 9: 255, 10: 255, 11: 2, 12: 3, 13: 4,
14: 255, 15: 255, 16: 255, 17: 5, 18: 255, 19: 6, 20: 7,
21: 8, 22: 9, 23: 10, 24: 11, 25: 12, 26: 13, 27: 14,
28: 15, 29: 255, 30: 255, 31: 16, 32: 17, 33: 18, -1: 255
}
def convert_label(label_path):
# 加载原始标注
label = np.array(Image.open(label_path))
# 创建空白数组
semantic_map = np.zeros_like(label, dtype=np.uint8)
# 转换每个像素
for id_in, id_out in ID_TO_TRAINID.items():
semantic_map[label == id_in] = id_out
return semantic_map
2. 创建 PyTorch Dataset
from torch.utils.data import Dataset
import os
class CityscapesDataset(Dataset):
def __init__(self, root_dir, split='train', transform=None):
self.root_dir = root_dir
self.split = split
self.transform = transform
# 收集所有图像和标注路径
self.images = []
self.labels = []
img_dir = os.path.join(root_dir, 'leftImg8bit', split)
label_dir = os.path.join(root_dir, 'gtFine', split)
for city in os.listdir(img_dir):
city_img_dir = os.path.join(img_dir, city)
city_label_dir = os.path.join(label_dir, city)
for img_name in os.listdir(city_img_dir):
if img_name.endswith('_leftImg8bit.png'):
base_name = img_name.replace('_leftImg8bit.png', '')
img_path = os.path.join(city_img_dir, img_name)
label_path = os.path.join(
city_label_dir,
f"{base_name}_gtFine_labelIds.png"
)
self.images.append(img_path)
self.labels.append(label_path)
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
img = Image.open(self.images[idx]).convert('RGB')
label = convert_label(self.labels[idx])
if self.transform:
img = self.transform(img)
return img, label
3. 数据增强
from torchvision import transforms
# 训练时的增强
train_transform = transforms.Compose([transforms.Resize((512, 1024)), # Cityscapes 常用分辨率
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# 验证时仅需基础转换
val_transform = transforms.Compose([transforms.Resize((512, 1024)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
性能优化
处理大规模数据集时,性能至关重要:
-
预加载标注 :在
__init__中预加载所有标注的路径,而不是每次迭代时搜索 -
多进程加载 :使用
torch.utils.data.DataLoader的num_workers参数
from torch.utils.data import DataLoader
train_dataset = CityscapesDataset('cityscapes_data', 'train', train_transform)
train_loader = DataLoader(
train_dataset,
batch_size=8,
shuffle=True,
num_workers=4, # 根据 CPU 核心数调整
pin_memory=True # 如果使用 GPU
)
-
使用内存映射文件:对于非常大的数据集,考虑使用
numpy.memmap -
混合精度训练:减少显存占用,加快训练速度
避坑指南
-
下载失败:确保账户已激活,网络连接稳定。如果中断,可以尝试分段下载。
-
标注转换错误 :检查
ID_TO_TRAINID映射是否正确,特别是新版本的数据集可能有变化。 -
内存不足:减小批次大小,或使用梯度累积。
-
类别不平衡:Cityscapes 中 ’road’ 和 ’sky’ 等类别占比很大,考虑使用加权损失函数。
-
验证集性能差:确保训练和验证使用相同的预处理流程。
应用示例
将处理好的数据输入到 UNet 模型中进行训练:
import torch
import torch.nn as nn
import torch.optim as optim
# 简化版 UNet
model = UNet(num_classes=19).cuda()
criterion = nn.CrossEntropyLoss(ignore_index=255)
optimizer = optim.Adam(model.parameters(), lr=1e-4)
# 训练循环
for epoch in range(100):
model.train()
for images, labels in train_loader:
images = images.cuda()
labels = labels.cuda()
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 验证...
总结与思考
Cityscapes 数据集虽然下载和预处理流程复杂,但通过合理的工具和方法可以高效完成。本文介绍了完整的下载、解析和预处理流程,并提供了可直接使用的代码示例。
思考题:
1. 如何针对 Cityscapes 的长尾分布设计更有效的损失函数?
2. 对于实时应用,如何在保持精度的同时减少输入分辨率?
3. 如何利用实例标注信息提升语义分割性能?
希望本指南能帮助你快速开始 Cityscapes 数据集的实验和研究。
