3.3 基本深度学习的车道检测算法
基本深度学习的车道检测算法使用卷积神经网络结合图像语义分割技术,从车辆摄像头采集的图像中提取车道特征,并标注出车道位置。通过训练模型识别图像中的车道,实现车道检测和标记,为驾驶辅助系统提供关键信息。
实例3-4:检测指定图像中的车道线(codes/2/lane-detection.ipynb)
3.3.1 项目介绍
在本节的例子中,基于深度学习方法,使用 PyTorch 和 segmentation_models_pytorch 库实现了一个车道检测系统。本项目主要功能如下所示:
- 数据加载与可视化:通过加载训练和验证数据,并使用 matplotlib 库进行可视化,了解车道检测任务的数据特征。
- 模型构建:使用 segmentation_models_pytorch 库构建车道检测模型,采用预训练的 Encoder(efficientnet-b0)和自定义的 Decoder。
- 数据增强与预处理:定义数据增强和预处理函数,以提高模型的泛化性能。
- 模型训练:使用定义的损失函数(多类别 Dice 损失)和 Adam 优化器对模型进行训练,动态调整学习率以优化性能。
- 训练循环与模型保存:执行多个训练周期,保存在验证集上表现最佳的模型,并在训练中调整学习率。
- 模型评估:在测试集上评估保存的最佳模型,计算多类别 Dice 损失和其他评估指标。
- 可视化结果:随机选择测试集中的样本,使用最佳模型进行预测,并可视化真实标签与预测结果,以直观了解模型性能。
本项目的主要目标是通过深度学习技术实现精准的车道检测,为自动驾驶和计算机视觉领域提供可行的解决方案。
3.3.2 具体实现
实例文件lane-detection.ipynb的具体实现流程如下所示。
(1)列出数据集目录下的文件和文件夹列表信息,具体实现代码如下所示。
import os
root = 'input/lane-detection-for-carla-driving-simulator'
print(os.listdir(root))
执行后输出:
['val_label', 'train_label', 'val', 'train']
本项目使用的数据集是与车道检测相关的自定义数据集,而不是公共数据集。训练数据集的图像存储在 '/input/lane-detection-for-carla-driving-simulator/train' 目录下,对应的标签(ground truth)存储在 'input/lane-detection-for-carla-driving-simulator/train_label' 目录下。验证集和测试集的数据集结构类似,分别存储在 '/input/lane-detection-for-carla-driving-simulator/val' 和 '/input/lane-detection-for-carla-driving-simulator/val_label' 目录下。
(2)加载并显示了存储在 "train" 目录下的第一张训练图像,具体实现代码如下所示。
train = os.path.join(root, "train")
train_imgs = os.listdir(train)
train_labels = os.path.join(root, "train_label")
import matplotlib.pyplot as plt
img = plt.imread(os.path.join(root, "train", train_imgs[0]))
plt.imshow(img)
plt.show()
执行效果如图2-1所示。在深度学习中,这样的步骤是为了查看数据的样本,确保数据加载和预处理步骤正确执行。
图2-1 显示"train" 目录下的第一张训练图像
(3)加载并显示存储在 "train_label" 目录下的第一个标签图像,具体实现代码如下所示。
label_images = os.listdir(train_labels)
img = plt.imread(os.path.join(root, "train_label", label_images[0]))
plt.imshow(img)
plt.show()
执行效果如图2-1所示。
图2-1 显示"train_label" 目录下的第一个标签图像
在训练深度学习模型时,这个步骤对于验证标签数据的正确性以及与训练图像的对应关系非常重要。标签图像通常包含有关图像中对象的信息,例如本实例显示的是车道线的位置等信息。
(4)打印输出 label_images 列表中的第一个元素(标签图像的文件名)和 train_imgs 列表中的第一个元素(训练图像的文件名),具体实现代码如下所示。
print(label_images[0])
print(train_imgs[0])
执行后会输出:
Town04_Clear_Noon_09_09_2020_14_57_22_frame_2887_label.png
Town04_Clear_Noon_09_09_2020_14_57_22_frame_2267.png
这可帮助我们了解第一张训练图像和对应的标签图像的文件名是什么,通常,这些文件名的结构和命名规范是为了方便模型训练,以便可以轻松地将训练图像与相应的标签图像关联起来。
(5)通过如下命令安装PyTorch 版本的分割模型,该库提供了一系列已经预训练好的分割模型,方便用于图像分割任务。在使用这个库之前,确保你的环境中已经安装了 PyTorch 和 torchvision。
pip install segmentation-models-pytorch
(6)定义训练和验证数据的目录路径,分别包括训练图像、训练标签、验证图像和验证标签。这些路径的设定是为了指定模型训练和验证所需的数据存储位置。具体实现代码如下所示。
DATA_DIR = "input/lane-detection-for-carla-driving-simulator"
x_train_dir = os.path.join(DATA_DIR, 'train')
y_train_dir = os.path.join(DATA_DIR, 'train_label')
x_valid_dir = os.path.join(DATA_DIR, 'val')
y_valid_dir = os.path.join(DATA_DIR, 'val_label')
(7)创建可视化函数visualize(),用于在一行中显示多个图像。通过接受关键字参数,该函数可以适应不同数量的图像。对于每个图像,函数会创建一个子图,将图像显示在该子图中,并设置标题以反映图像的名称。最终,所有子图都被放在一个大的图形对象中,并一起显示,以便更轻松地比较和查看多个图像。具体实现代码如下所示。
def visualize(**images):
"""Plot images in one row."""
n = len(images)
plt.figure(figsize=(16, 5))
for i, (name, image) in enumerate(images.items()):
plt.subplot(1, n, i + 1)
plt.xticks([])
plt.yticks([])
plt.title(' '.join(name.split('_')).title())
plt.imshow(image)
plt.show()
(8)创建一个用于处理CarlaLanes数据集的PyTorch数据集类CarlaLanesDataset,使得你可以轻松地迭代数据集中的图像和标签。具体实现代码如下所示。
from torch.utils.data import DataLoader, Dataset
from torch import LongTensor
import re
import cv2
class CarlaLanesDataset(Dataset):
CLASSES = ['background', 'left_marker', 'right_marker']
def __init__(
self,
images_dir,
masks_dir,
classes=None,
augmentation=None,
preprocessing=None,
):
self.ids = os.listdir(images_dir)
#random.shuffle(self.ids)
self.images_fps = [os.path.join(images_dir, image_id) for image_id in self.ids]
get_label_name = lambda fn: re.sub(".png", "_label.png", fn)
self.masks_fps = [os.path.join(masks_dir, get_label_name(image_id)) for image_id in self.ids]
# convert str names to class values on masks
self.class_values = [self.CLASSES.index(cls.lower()) for cls in classes]
self.augmentation = augmentation
self.preprocessing = preprocessing
def __getitem__(self, i):
# read data
image = cv2.imread(self.images_fps[i])
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
mask = cv2.imread(self.masks_fps[i], 0)
# apply augmentations
if self.augmentation:
sample = self.augmentation(image=image, mask=mask)
image, mask = sample['image'], sample['mask']
# apply preprocessing
if self.preprocessing:
sample = self.preprocessing(image=image, mask=mask)
image, mask = sample['image'], sample['mask']
return image, LongTensor(mask)
def __len__(self):
return len(self.ids)
对上述代码的具体说明如下:
- 初始化方法 (__init__):接受图像目录 (images_dir) 和标签目录 (masks_dir) 作为输入,以及一些可选参数,如类别列表 (classes)、数据增强 (augmentation) 和预处理 (preprocessing)。通过 os.listdir 获取图像目录下的所有文件名,并构建图像文件路径列表 (self.images_fps)。使用一个 lambda 函数 get_label_name 从图像文件名中构建相应的标签文件路径列表 (self.masks_fps)。将类别名称转换为相应的类值,存储在 self.class_values 中。
- __getitem__ 方法:通过索引 i 从图像和标签文件路径列表中读取对应的图像和标签。将图像从 BGR 格式转换为 RGB 格式,如果存在数据增强 (augmentation)则应用相应的增强。如果存在数据预处理 (preprocessing),则应用相应的预处理。最终,返回处理后的图像和相应的标签。
- __len__ 方法:返回数据集的长度,即数据集中样本的数量。
(9)可视化训练数据集中的第五个样本的图像和标签,以便进行检查和调试工作。具体实现代码如下所示。
dataset = CarlaLanesDataset(x_train_dir, y_train_dir, classes=CarlaLanesDataset.CLASSES)
image, mask = dataset[4] # get some sample
visualize(
image=image,
label = mask
)
执行后显示可视化结果,如图2-8所示。
图2-8 可视化训练数据集中的第五个样本的图像和标签
(10)创建函数get_training_augmentation(),该函数使用Albumentations 库实现,用于获取训练数据增强(augmentation)信息。具体实现代码如下所示。
import albumentations as albu
def get_training_augmentation():
train_transform = [
albu.ShiftScaleRotate(scale_limit=0.1, rotate_limit=0., shift_limit=0.1, p=1, border_mode=0),
albu.IAAAdditiveGaussianNoise(p=0.2),
albu.OneOf(
[
albu.CLAHE(p=1),
albu.RandomBrightness(p=1),
albu.RandomGamma(p=1),
],
p=0.6,
),
albu.OneOf(
[
albu.IAASharpen(p=1),
albu.Blur(blur_limit=3, p=1),
albu.MotionBlur(blur_limit=3, p=1),
],
p=0.6,
),
albu.OneOf(
[
albu.RandomContrast(p=1),
albu.HueSaturationValue(p=1),
],
p=0.6,
),
]
return albu.Compose(train_transform)
上述代码的实现流程如下:
- 创建了一个由不同数据增强操作组成的列表 train_transform。
- 使用 albu.ShiftScaleRotate 实现图像的平移、缩放和旋转操作。
- 使用 albu.IAAAdditiveGaussianNoise 添加高斯噪声。
- 使用 albu.OneOf 包裹了几个数据增强操作,其中至少一个会被应用。这些操作包括对比度增强、随机亮度、随机伽马校正。
- 同样,使用 albu.OneOf 包裹了其他一组数据增强操作,例如图像锐化、模糊、运动模糊。
- 最后,再次使用 albu.OneOf 包裹了一组数据增强操作,包括随机对比度、色调饱和度亮度调整。
函数get_training_augmentation()返回一个由上述数据增强操作组成的 Albumentations 组合对象,用于在训练过程中对图像进行增强。这种增强有助于使模型更具鲁棒性,并提高泛化性能。