ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

车道线检测源码拆解:PyTorch CNN训练权重与CULane/Tusimple实战

车道线检测源码拆解:PyTorch CNN训练权重与CULane/Tusimple实战 简介车道线检测是自动驾驶与智能交通中的核心视觉任务对光照、天气和道路变化都有较高要求。项目源码包基于Python卷积神经网络完整覆盖数据加载、模型搭建、训练评估与推理演示并附带已在公开数据集上训练好的模型权重可直接运行演示脚本观察检测效果适合作为毕业设计、课程项目或科研入门参考。压缩包共包含91个文件整体约548MB主要类型有21个Python脚本、33张测试/结果图片、3个预训练模型权重以及txt说明、配置文件和C辅助代码等目录按数据、模型、工具、评估等模块划分便于按需查阅。当前已有74人学习下载。配套文档对环境安装、参数调优、数据预处理和实时视频流处理等关键步骤给出指引同时提供Culane/Tusimple训练模型与示例图片用户可以先跑通推理再深入研读训练逻辑学习路径清晰、上手门槛较低。1. 车道线检测源码包拆解Python 卷积神经网络、训练好的权重直接跑通需要几步做毕业设计最怕的不是没想法而是想法有了、环境装了、代码也下载了结果demo.py一跑就报错。这套车道线检测源码包就是给这种场景准备的Python 写的卷积神经网络训练与推理流程、CULane 和 Tusimple 两套主流数据集配置、culane_18.pth和tusimple_18.pth两个训练好的权重可以直接加载另附作业报告和说明文档。无论想快速验证算法效果还是拿来做毕设基线和改进起点都不用从零起步。但直接跑之前有三件事必须先确认PyTorch 版本和显卡驱动对不对得上、数据集路径有没有配进configs/、加载权重时 backbone 和训练时是否一致。这篇就按这个顺序拆从文件结构到训练细节最后把最容易翻车的地方一个个点出来。2. 工程结构拆解从 Lane-Detection 文件树看懂训练、推理、评估的分工拿到压缩包解压之后第一反应通常是文件怎么这么多。实际上这套代码的目录组织是清晰的只是把 Python 工程里该有的东西都备齐了。我拿到视觉项目有个习惯先分三路看——训练入口、推理入口、评估入口。这套源码里对应的是train.py、demo.py和evaluation/。把这三条线捋顺了剩下就是配置和数据的问题。2.1 三条主线train.py、demo.py 与 evaluation/ 各管什么先看根目录。train.py是训练脚本承担数据加载、模型初始化、损失计算、反向传播、checkpoint 保存这些完整环节。demo.py是单张图片推理脚本读入一张图、加载权重、前向推理、把车道线画回原图并保存适合第一时间验证模型有没有跑通。requirements.txt列的是第三方依赖库包括 torch、torchvision、numpy、opencv-python、tensorboard 这些。model/目录下是模型定义data/目录下是数据集加载与预处理configs/下是不同数据集的配置utils/下是 loss、metrics、factory、dist_utils 这类杂项。这种划分在 PyTorch 项目里很常见好处是想改模型结构不用动数据代码想换数据集不用改模型代码训练和推理是两条独立链路。data/constant.py存的是数据集路径和类别常量data/dataset.py定义了怎么从磁盘读图、读标注、做 transformdata/dataloader.py封装了 DataLoader 逻辑包括 shuffle、多进程读取、pin_memory 这些工程细节。data/mytransforms.py是自定义的图像变换比如随机裁剪、颜色抖动、随机翻转训练时做数据增强用推理时不需要。还有一个容易被忽略但值得说的cpp/目录里面有 CMakeLists.txt 和 build.sh。这是把模型往 C 部署方向引的尝试核心推理逻辑用 LibTorch 或 ONNX Runtime 重写训练和 PyTorch 推理仍是 Python。对做毕业设计来说C 部分可以放着不碰不影响主流程但如果你答辩时需要展示工程化能力这倒是个加分项。作业报告.docx和说明.txt是文档部分里面有环境安装步骤和运行说明。out/目录存的是推理输出图tmp/和__pycache__/是运行产生的临时文件可以忽略。两个.pth权重文件分别对应 CULane 和 Tusimple 数据集。这两个数据集的车道线定义、图片分辨率、标注方式不同权重不能混用。比如你用configs/tusimple.py加载culane_18.pth大概率运行时报 shape 不匹配或者不报错但输出乱七八杂——最后一个卷积层的输出通道数对不上或者即使对上了也因训练分布不同而失效。2.2 model 与 backboneCNN 是怎么把车道线特征抽出来的车道线检测的核心是卷积神经网络。backbone.py负责特征提取把 288×800 或 288×512 的输入图像通过一系列卷积、池化、残差连接变成分辨率更低但通道数更多的特征图。model.py在此基础上接检测头输出每个像素属于车道线的概率。这套项目用的是典型的 encoder-decoder 结构配置文件configs/culane.py和configs/tusimple.py里能看到 backbone 类型、输入输出尺寸这些参数。用 CNN 做车道线检测相对目标检测要简单直接不需要 anchor、不需要 NMS模型输出就是和输入分辨率相关的分割图或置信度图。训练时的监督信号是标注好的车道线 mask一张图上每条线做成一个类别或二值图。CULane 把车道线分成了 4 类Tusimple 是固定 4 条线这决定了模型输出通道数的设置。实际读model.py的前向过程你会看到 backbone 提取特征后经过几个上采样模块逐步恢复分辨率最后用 1×1 卷积输出每个像素的类别概率。中间可能会插入辅助分类头做深度监督加速收敛。训练时主损失用交叉熵配合utils/loss.py里的辅助损失一起反传。调参时你需要关心的其实就几件事输入尺寸、batch size、学习率、类别数。真正改网络结构的场景在毕设里很少更多是调后处理和训练策略。我实际跑过之后的体会是CNN 在这个任务上的核心优势在于对光照变化和噪声的鲁棒性。车道线本身是细长条结构普通分割网络容易漏检所以很多方案会额外引入 row anchor 或线段约束但这套代码走的还是像素级分割路线好处是代码好懂、好改坏处是极端场景下容易出现断线。后续优化可以从后处理角度对输出 mask 做形态学闭运算把断线连起来最后一章我会单独讲。2.3 配置文件对比CULane 和 Tusimple 的路径与超参数差异configs/下两个文件内容不长核心是数据集路径、输入分辨率、类别数、训练轮数、学习率策略这些。CULane 和 Tusimple 是两种差异很大的数据分布前者是车载摄像头采集的城市道路场景复杂、车道线类型多后者是高速公路场景车道线相对规整。所以两个配置文件里的关键参数几乎都不一样训练入口也要区分开。对比维度CULane 配置Tusimple 配置标注类别多类别车道线固定 4 条线图片场景城市道路、拥堵、光照变化大高速、结构规整训练难度更高需要更多 epoch相对容易收敛适用的权重culane_18.pthtusimple_18.pth读配置的时候注意看data_root和dataset字段这两个直接决定跑训练的时候数据从哪读。很多人拿到源码喜欢直接把train.py跑起来结果报 FileNotFoundError八成就是路径没改。这套代码不会帮你自动下载数据集数据要自己准备下一章细说。3. 上手实操环境配置、数据集转换、训练与推理命令这一章的价值在于让你在 30 分钟内把 demo 跑通然后再决定要不要碰训练。整个过程分三步装环境、备数据、跑脚本。每一步都有容易翻车的地方我按自己的经验把检查顺序写出来。3.1 环境安装requirements.txt 与 PyTorch 版本匹配先看 requirements.txt 里锁了哪些版本再决定用 conda 还是 pip 装。常见做法是先建一个独立的虚拟环境避免把系统 Python 搞乱conda create -n lanedet python3.8 -y conda activate lanedet pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt这里有个细节--index-url指定的是 CUDA 11.8 的 PyTorch 预编译包如果你的显卡驱动版本较老可以换成 cu113 或 cu117。判断依据是nvidia-smi显示的 CUDA 版本只要驱动版本 PyTorch 要求的 CUDA 版本就行并不是驱动版本越高越好。如果机器没有 NVIDIA 显卡把--index-url去掉装 CPU 版 torch 也能跑 demo只是训练会慢到让你怀疑人生。装完验证一下环境python -c import torch; print(torch.__version__, torch.cuda.is_available())输出的torch.cuda.is_available()是True说明 GPU 可用。如果是False检查是不是装了 CPU 版或者驱动版本不对。这一步不确认清楚后面训练和推理可能出现各种玄学问题比如模型加载慢、显存报错、甚至直接段错误。3.2 数据准备CULane/Tusimple 目录结构与 convert_tusimple.py数据集是这次实操里最耗时间的一环。CULane 和 Tusimple 都需要自己下载代码不负责拉数据。下载完解压后目录结构要跟configs/culane.py里的data_root对得上。我通常的做法是先在配置里把data_root指到数据集所在绝对路径避免相对路径在不同机器上失效。Tusimple 原始标注是 JSON 格式一行一个标注包含 lane 的 x/y 坐标和类别。这套源码在evaluation/scripts/convert_tusimple.py里提供了转换脚本作用是把 JSON 标注转成训练需要的 mask 格式。运行方式一般是python evaluation/scripts/convert_tusimple.py \ --src /path/to/tusimple \ --dst /path/to/tusimple_converted转换脚本做的事情是把每条车道线的散点坐标连成线然后在 mask 图上把线宽画出来。这里有一个容易忽略的参数线宽。Tusimple 标注的原始点是单像素如果直接画成 1 像素宽训练时正负样本极度不平衡模型容易学成全预测为背景。常见做法是把线画成 8 像素宽或者用高斯分布生成一个软 label这样损失函数才有足够的正样本梯度。CULane 数据集本身提供的是分割 mask不需要转换直接配置路径就能用。但 CULane 的文件结构比较散要确认list/目录下的 train.txt、val.txt 里写的相对路径和实际文件位置一致否则 DataLoader 读不到图。3.3 训练与推理train.py 和 demo.py 的参数与输出环境装好、数据就位后先跑推理验证模型python demo.py --config configs/culane.py --weights culane_18.pth --img data/00000.jpg--config指定配置文件--weights指定权重路径--img指定输入图片。跑完之后会在out/目录下生成带车道线标注的结果图。注意权重和配置的对应关系CULane 权重配 CULane 配置Tusimple 权重配 Tusimple 配置。我见过有人把tusimple_18.pth塞给culane.py结果输出图像分辨率不对检测线位置全偏还以为是模型坏了。并不是模型坏了是配置和权重不匹配。推理没问题了再碰训练python train.py --config configs/culane.py训练时的关键参数在配置里改batch_size显存不够就调小optimizer一般用 SGD 或 Adamlr初学者建议从 1e-3 起步epochCULane 上一般要几十轮才能看到像样的效果。训练过程中 tensorboard 会记录 loss 和 mIoU建议每训练几分钟看一眼 loss 曲线如果 loss 完全不动赶紧停下来查不要干等。训练完成后在out/或tmp/下会保存 checkpoint里面是模型权重和优化器状态。加载时用torch.load读进来注意 map_location 参数checkpoint torch.load(culane_18.pth, map_locationcuda:0) model.load_state_dict(checkpoint[model])如果模型是单卡训的加载到多卡环境会碰到module.前缀问题。常见解决办法是加载时把 key 里的module.去掉或者strictFalse加载后再打印 missing_keys 逐项排查。这个属于经典坑我在下一章的排查清单里具体写。4. 避坑排查车道线检测从训练到部署的五个高频问题这一章整理的是我自己跑这套源码时真实踩过的坑按现象 → 原因 → 解决的格式写。每条都对应一个具体的排查路径新手照着走就行。4.1 训练时 Loss 不降或乱跳现象训练跑了十几个 epochloss 一直在 0.7 上下波动不下降或者从一个很大的值突然跳到 NaN。原因排查先分清是不降还是炸了。不降多半是学习率太低或者数据没对齐NaN多半是学习率太高、loss 里有除零、或者数据里带了异常值。最容易被忽略的是数据对齐问题——输入图是 BGR 还是 RGB、归一化参数是不是和预训练权重一致这些不对CNN 学到的特征全乱套。解决学习率先降到 1e-4 试试检查data/mytransforms.py里的归一化均值和标准差确认加载预训练权重时strictTrue没有被意外禁用导致 backbone 从随机初始化开始训。如果数据是 RGB而预训练模型是 BGR 顺序把通道换回来很多时候 loss 就正常了。4.2 推理结果全黑或车道线错位现象demo.py跑完输出的 mask 图全黑或者画出来的车道线整体偏移、交错。原因排查全黑说明模型输出的概率全部被阈值卡掉了大概率是配置文件里的类别数或输入尺寸和权重不匹配导致输出 shape 异常。车道线偏移常见原因是推理时输入图做了 resize但画结果时没做坐标变换直接在原图尺寸上叠加了 resize 后的 mask。解决先在demo.py里打印模型的输出 shape确认和配置里的num_classes一致。画线之前把 mask 用cv2.resize还原到原图尺寸或者干脆全程保持同一尺寸。我在调试时会加一行断言assert mask.shape[:2] img.shape[:2], mask 和原图尺寸不一致先 resize 再叠加这样问题能立刻暴露不用对着输出图猜。4.3 训练时显存溢出OOM现象CUDA out of memory训练跑不了几步就崩。原因排查显存溢出本质是 batch_size × 输入分辨率 × 模型参数量超出显存上限。CULane 输入分辨率高backbone 又占了大量显存默认 batch_size 太大很容易爆。解决先把 batch_size 调小一半试试。还不行就把输入分辨率从 800×288 降到 640×256 之类代价是检测精度下降。如果还爆启用梯度累积每 4 个 batch 反传一次等效增大 batch_size 但显存占用不变。我一般这样改optimizer.zero_grad() loss.backward() if step % accumulation_steps 0: optimizer.step()accumulation_steps设成 4相当于 batch_size 不变的情况下模拟了 4 倍 batch 的训练效果收敛会更稳定。4.4 加载 .pth 权重报错missing keys 或 unexpected keys现象load_state_dict报 missing keys 或 unexpected keys模型加载失败。原因排查权重是在某个具体模型结构下训练出来的如果代码里的模型类被改过——比如换了 backbone、改了输出类别数——权重就对应不上了。还有一种情况是训练时开了DataParallel保存的权重 key 都带module.前缀加载到单卡模型就会报 unexpected。解决先区分两类错误。missing keys 通常是模型比权重多了一些层常见于修改了 head 输出unexpected keys 通常是权重比模型多常见于 DataParallel 前缀。前者可以用strictFalse加载然后冻结 backbone 只训练新 head后者用字符串替换去掉module.state_dict torch.load(tusimple_18.pth, map_locationcpu)[model] new_state_dict {k.replace(module., ): v for k, v in state_dict.items()} model.load_state_dict(new_state_dict)map_locationcpu也是关键它避免在没 GPU 的机器上加载报错同时方便排查 key 的差异。4.5 评估脚本路径报错或指标对不上现象跑evaluation/下的脚本时 FileNotFoundError或者算出来的 F1/mIoU 数值明显不合理。原因排查评估脚本通常需要三类路径配置模型预测结果目录、标注 ground truth 目录、以及 list 文件。三个路径只要有一个不对脚本就挂。指标对不上多半是评估时用的预测分辨率或类别映射和 ground truth 不一致。解决先读eval_wrapper.py开头的路径配置把pred_dir、gt_dir、list_path都改成绝对路径。类别映射要在data/constant.py里核对CULane 的 4 类对应关系不能错。我在跑评估前会单独写一个脚本随机挑三张图的预测 mask 和 GT mask 叠加可视化肉眼确认对齐了再跑全量评估能省下不少排错时间。5. 评估与调参把车道线检测从能跑压到稳定模型跑通只是第一步毕设答辩和实际项目更关心的是精度指标和稳定性。这一章讲怎么用这套源码自带的评估体系以及哪些参数值得动。5.1 eval_wrapper.py 与官方评估逻辑evaluation/目录下分culane和tusimple两个子目录对应官方评估工具。eval_wrapper.py是统一入口它做两件事把模型输出的原始 mask 整理成评估工具能读的格式然后调用官方脚本计算指标。Tusimple 的官方指标是准确率和 FP/FNCULane 是逐帧 F1 分数。CULane 还按场景细分了正常、拥堵、夜间、雨夜等类别评估时会分别输出每个场景的 F1这个对论文里的消融实验很有用。跑评估前要确认模型输出被正确阈值化成二值 mask然后用骨架线提取算法把 mask 细化为单像素线再和 GT 做匹配。CULane 的匹配规则是预测线和 GT 线的 IoU 超过 0.5 才算 True Positive。这个阈值在官方代码里是写死的不用改但要理解它对结果的影响。5.2 关键参数对精度的真实影响输入分辨率、类别数、loss 权重调参之前先得知道哪些参数动了有效果。我在这套源码上做过的实验按影响力排序大概是输入分辨率 类别数设置 loss 权重 学习率策略。输入分辨率影响最直接。分辨率低小目标的细线很容易在降采样过程中丢失分辨率高显存占用和训练时间线性上涨。CULane 默认配置的输入尺寸是个平衡点但如果你想提升雨夜和夜间场景下的 F1把长边加到 800 以上会有肉眼可见的提升代价是单卡训练可能要等更久。类别数设置影响输出通道和损失计算。Tusimple 固定 4 条线是最简单的情况num_classes4输出 4 个通道取 argmax 得到线索引。CULane 的 4 类是按车道线属性划分的输出同样 4 通道。不要为了省事把num_classes改成 1 去做二分类会丢掉类别信息评估时的 F1 也会因为类别对应错乱而失真。loss 权重主要看utils/loss.py里的实现。如果代码里有主损失 辅助损失的结构辅助损失的权重一般取 0.3~0.5。这两个损失对梯度贡献的量级不同如果权重配比不合适训练早期会出现 loss 震荡。我的经验是先把辅助损失权重设为 0主损失训到稳定再逐步加上去否则问题互相干扰很难定位。5.3 一个可复用的调参实验流程我每次拿到未知数据集习惯用同一套调参流程这里分享出来第一步固定 backbone 和损失函数先用小数据集跑 5 个 epoch确认代码链路没有 bug。第二步用默认配置跑满训练记录 baseline 指标。第三步做单变量实验每次只改一个参数比如只改输入分辨率、只改学习率记录 F1 变化。第四步把效果最好的几个单变量结果合到一起再跑一轮确认。这套流程的关键是每次只改一个变量。很多人调参喜欢一次改五个地方最后哪个起作用了都不知道等于瞎调。训练日志里建议用tensorboard记录每个 epoch 的 train loss、val F1、学习率曲线调参时翻日志比翻代码快得多。还有一个容易被忽略的点backbone 的预训练权重。这套源码虽然带了训好的权重但如果你要重新训练backbone 部分最好加载 ImageNet 预训练模型而不是随机初始化。随机初始化的 CNN 在小数据集上很难收敛收敛了精度也差一截。看model/backbone.py的代码如果它支持传入 pretrained 参数pretrainedTrue会从 torchvision 拉权重如果没这个参数手动下载对应模型文件再加载也行。6. 进阶把模型接到视频流里做连续帧车道线检测模型在单张图片上效果不错但实际开着视频跑一遍就会发现逐帧推理的结果在时间轴上抖得厉害。原因是相邻帧车道线位置变化不大模型却会因为光照、阴影、车辆遮挡等噪声输出和上一帧相差甚远。解决思路就一个字平滑。我处理视频流时会维护一个车道线置信度缓冲队列取最近 5 帧的预测结果做加权平均权重给最近的帧更高。这样即使某一帧检测失败也不会导致画面突然消失。具体实现上OpenCV 读视频循环每帧预处理后走一遍模型拿到 mask 后先做形态学闭运算补断线再求加权平均import cv2 import numpy as np mask_queue [] alpha 0.7 # 当前帧权重 while cap.isOpened(): ret, frame cap.read() if not ret: break # 预处理resize 归一化和训练时保持一致 input_tensor transform(frame.copy()).unsqueeze(0) with torch.no_grad(): prob model(input_tensor).squeeze(0).cpu().numpy() mask (prob.argmax(0) * 255).astype(np.uint8) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) if len(mask_queue) 0: smoothed mask else: smoothed cv2.addWeighted(mask, alpha, mask_queue[-1], 1 - alpha, 0) mask_queue.append(smoothed) if len(mask_queue) 5: mask_queue.pop(0) # 把平滑后的 mask 叠回原图 overlay frame.copy() overlay[mask 0] (0, 255, 0) cv2.imshow(lane, overlay)这段代码里的alpha是关键参数。alpha太大平滑效果弱画面还是会闪太小画面拖影严重车道线位置明显滞后。我一般从 0.7 起步帧率 30 以上可以调到 0.5帧率低就调高保证实时性和稳定性的平衡。MORPH_CLOSE这个操作可能有人不熟。它是先膨胀再腐蚀作用是把预测 mask 上细小的断裂处连接起来。车道线是细长结构经过分割网络输出后经常出现中间断点闭运算能有效补上。核大小 5×5 是个起步值分辨率越高可以适当调大。从那以后我做车道线检测的演示和视频实验都会把这段平滑后处理强制加上哪怕只是临时跑个 demo 也不跳过。它不改变模型本身但让输出结果从能识别变成看起来真的稳。希望这套源码的拆解和排错经验能帮到你尤其是准备毕设的同学跑通之后花点时间做做消融实验答辩时就有讲不完的细节了。本文还有配套的精品资源点击获取
返回列表