Cityscapes数据集深度解析:从标注规范到计算机视觉实战应用

1次阅读
没有评论

共计 3673 个字符,预计需要花费 10 分钟才能阅读完成。

image.webp

背景介绍

Cityscapes 数据集是自动驾驶和计算机视觉领域中最具影响力的语义分割基准之一。它包含来自 50 个不同城市的街道场景,涵盖了各种天气、光照和交通条件。对于开发者来说,Cityscapes 提供了丰富的场景多样性,但同时也带来了一些挑战:

Cityscapes 数据集深度解析:从标注规范到计算机视觉实战应用

  • 标注体系复杂,包含 19 个语义类别和多个实例级别标注
  • 数据分布不均衡,某些类别(如 ” 人 ”、” 车 ”)出现频率远高于其他类别
  • 高分辨率图像(2048×1024)对计算资源要求较高
  • 标注质量不一致,存在边界模糊区域

数据集解析

语义标签划分

Cityscapes 将场景划分为 19 个语义类别,这些类别又分为 7 个组别:

  1. 平面(flat):道路、人行道、停车场等
  2. 建筑(construction):建筑、墙、栅栏等
  3. 物体(object):杆子、交通标志、交通灯等
  4. 自然(nature):植被、地形等
  5. 天空(sky)
  6. 人类(human):行人、骑行者等
  7. 车辆(vehicle):汽车、卡车、公交车等

数据集划分

数据集包含:

  • 训练集:2975 张精细标注图像
  • 验证集:500 张精细标注图像
  • 测试集:1525 张图像(标注不公开)

场景分布考虑了城市多样性、天气条件和一天中的不同时间。值得注意的是,某些类别(如 ” 火车 ”、” 摩托车 ”)在数据集中出现频率较低,这在实际训练中可能导致类别不平衡问题。

数据处理

数据加载

以下是使用 Python 加载 Cityscapes 数据集的示例代码:

import os
from PIL import Image
import numpy as np

# 定义类别颜色映射(根据官方标签规范)COLOR_MAP = {0: (0, 0, 0),       # 未标注
    1: (70, 70, 70),    # 建筑
    # 其他类别颜色映射...
}

class CityscapesDataset:
    def __init__(self, root_dir, split='train', transform=None):
        self.root_dir = root_dir
        self.split = split
        self.transform = transform
        self.images = []
        self.masks = []

        # 加载图像和标注路径
        image_dir = os.path.join(root_dir, 'leftImg8bit', split)
        mask_dir = os.path.join(root_dir, 'gtFine', split)

        for city in os.listdir(image_dir):
            city_image_dir = os.path.join(image_dir, city)
            city_mask_dir = os.path.join(mask_dir, city)

            for file_name in os.listdir(city_image_dir):
                if file_name.endswith('_leftImg8bit.png'):
                    base_name = file_name.replace('_leftImg8bit.png', '')
                    mask_name = base_name + '_gtFine_labelIds.png'

                    self.images.append(os.path.join(city_image_dir, file_name))
                    self.masks.append(os.path.join(city_mask_dir, mask_name))

    def __len__(self):
        return len(self.images)

    def __getitem__(self, idx):
        image = Image.open(self.images[idx]).convert('RGB')
        mask = Image.open(self.masks[idx])

        # 转换为 numpy 数组
        mask = np.array(mask)

        if self.transform:
            image, mask = self.transform(image, mask)

        return image, mask

数据增强

针对语义分割任务,常用的数据增强包括:

import torchvision.transforms.functional as F
import random

class Compose:
    def __init__(self, transforms):
        self.transforms = transforms

    def __call__(self, image, mask):
        for t in self.transforms:
            image, mask = t(image, mask)
        return image, mask

class RandomHorizontalFlip:
    def __call__(self, image, mask):
        if random.random() < 0.5:
            image = F.hflip(image)
            mask = F.hflip(mask)
        return image, mask

class RandomScale:
    def __init__(self, scale_range=(0.5, 2.0)):
        self.scale_range = scale_range

    def __call__(self, image, mask):
        scale = random.uniform(*self.scale_range)
        new_size = (int(image.size[0] * scale), int(image.size[1] * scale))
        image = F.resize(image, new_size, Image.BILINEAR)
        mask = F.resize(mask, new_size, Image.NEAREST)
        return image, mask

模型训练

损失函数选择

对于语义分割任务,常用的损失函数组合是交叉熵损失和 Lovasz-Softmax 损失:

import torch
import torch.nn as nn
import torch.nn.functional as F

class CrossEntropyLovaszLoss(nn.Module):
    def __init__(self, ignore_index=255):
        super().__init__()
        self.ce_loss = nn.CrossEntropyLoss(ignore_index=ignore_index)

    def forward(self, outputs, targets):
        ce_loss = self.ce_loss(outputs, targets)
        lovasz_loss = lovasz_softmax(F.softmax(outputs, dim=1), targets)
        return ce_loss + lovasz_loss

训练代码框架

def train(model, train_loader, criterion, optimizer, device):
    model.train()
    running_loss = 0.0

    for images, masks in train_loader:
        images = images.to(device)
        masks = masks.to(device)

        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, masks)

        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

    return running_loss / len(train_loader)

避坑指南

  1. 忽略 ignore_index 设置:Cityscapes 中未标注区域标记为 255,在计算损失时需要忽略这些区域。解决方案是在损失函数中设置ignore_index=255

  2. 错误处理标注 ID:某些实现可能错误地将标注 ID 直接用作类别索引。正确做法是使用官方提供的 trainId 映射。

  3. 数据增强不一致:对图像和标注应用不同的增强变换会导致不一致。解决方案是确保对图像和标注应用相同的空间变换。

  4. 内存不足:高分辨率图像可能导致 GPU 内存不足。解决方案包括使用较小的批次大小、裁剪图像或降低分辨率。

  5. 类别不平衡:某些类别样本数量极少。解决方案包括使用类别加权损失或过采样稀有类别。

性能优化

  1. 输入分辨率:降低输入分辨率可以显著减少计算量,但会损失细节信息。常见折中方案是使用 1024×512 或 512×256 分辨率。

  2. 显存优化

  3. 使用混合精度训练
  4. 梯度累积
  5. 模型并行

  6. 模型选择:轻量级架构如 DeepLabv3+ MobileNet 或 BiSeNet 在精度和速度间提供良好平衡。

  7. 训练技巧

  8. 使用学习率预热
  9. 渐进式调整输入分辨率
  10. 困难样本挖掘

总结

Cityscapes 数据集为自动驾驶场景的语义分割研究提供了丰富的基准。通过深入理解标注规范、合理处理数据、选择合适的模型和优化策略,开发者可以充分利用这一数据集提升模型性能。实践中需要特别注意数据分布不平衡、标注细节处理和计算资源优化等问题。随着技术的进步,Cityscapes 仍然是评估算法在实际复杂场景中表现的重要基准。

正文完
 0
评论(没有评论)