ARTICLE DETAIL

资讯详情

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

车道线检测实战:从CNN语义分割到毕业设计全流程指南

车道线检测实战:从CNN语义分割到毕业设计全流程指南 简介这套车道线检测源码与模型包面向自动驾驶、智能交通方向的学生和研究人员解决道路车道线识别与跟踪的工程落地问题。包内基于Python与卷积神经网络实现训练、验证和推理全流程并配有可直接运行的pth模型适合毕业设计或课程项目快速起步。资源共91个文件压缩包大小约548.39MB。其中21个py脚本覆盖数据加载、模型构建、训练与评估3个pth文件为预训练权重33张jpg用于原始图片和识别结果展示txt、docx与md提供配置说明、作业报告和使用文档另有cpp、hpp等C辅助文件便于拓展部署与二次开发。已有74人浏览学习该资源可作为车道线检测入门和课题复现的参考。通过文档说明可了解参数调优、图像预处理、模型结构设计等关键环节降低从零搭建算法的门槛为学术研究与工程实践提供完整起点。1. 车道线检测源码模型为什么这个项目能直接当毕业设计用车道线检测是自动驾驶视觉感知里最“入门但完整”的子任务也是课程设计和毕业设计里出现频率最高的方向之一。传统做法用 Canny 边缘检测加霍夫变换在正常路面尚能工作一旦遇到阴影、隧道口逆光、雨天反光检测结果就彻底翻车。换成卷积神经网络CNN做像素级分割之后模型学的是“每个像素是不是车道线”的概率分布对光照和路面纹理的鲁棒性明显提升。拿到这样一套源码加训练好的模型再配上文档意味着你不用从零写网络、不用攒数据集、不用等几天的训练周期第一天下载、第二天就能在本地视频上看到检测效果这正好满足“可运行、可讲解、可改进”的毕业设计要求。这个方案适合三类人一是要快速做毕设、需要先跑通再改论文点的人二是刚学完 Python 和卷积神经网络基础需要一个完整项目练手的人三是想搞懂分割模型工程落地细节而不是只看原理图的人。下面从模型选型讲到训练调参再落到排错和毕业设计答辩技巧全部按“能复现”的标准来。2. 车道线检测的模型选型为什么这个任务要卷积神经网络而不是 YOLO2.1 车道线是“像素级线条”不是“方框目标”很多第一次做车道线检测的人会下意识想用 YOLO 不是更快吗实际上车道线是细长线条一条车道线的宽度在图像里往往只有几个像素目标框会把大量背景包进来正负样本极不平衡而且车道线经常被车辆遮挡、被路面裂缝切断用方框标注本身就丢掉了“线条连续性”这个关键信息。所以自动驾驶领域的主流做法是把车道线检测建模成语义分割输出一张和输入图一样大的 mask每个像素的值表示它属于车道线的概率。这样做的好处是模型直接学习“形态逻辑”比如两条车道线在透视关系下会向消失点收拢这种全局结构关系是边缘检测和霍夫变换学不到的。卷积神经网络通过局部感受野一层层叠加先学习边缘和方向纹理中层的卷积核能响应一小段连续线段到了高层特征图则能看到整条车道线的走向这也是为什么 CNN 在这个任务上是标配而不是可选。拿到源码之后建议先用可视化工具把网络结构图和每层特征图的尺寸打出来确认模型是编码器-解码器结构还是类似 SCNN 的 Spatial CNN 结构这决定了你后面对训练参数的理解深度。2.2 输入输出与标签数据到底长什么样以最常见的分割方案为例输入是一张三通道 RGB 图像模型内部会把它归一化到 0~1 区间输出就是一张单通道的预测图。训练用的标签不是框也不是关键点而是一张二值 mask车道线像素为 1背景为 0。像 TuSimple 数据集里的标注是 polyline 形式的点列表预处理阶段需要把这些点用cv2.line画到一张全黑的画布上线宽建议 8~12 个像素太细会导致正样本过少太粗又会引入背景噪声。import cv2 import numpy as np def polyline_to_mask(points, shape(512, 256)): mask np.zeros((shape[1], shape[0]), dtypenp.uint8) # points 是形如 [[x1,y1], [x2,y2], ...] 的车道线关键点 for i in range(len(points) - 1): cv2.line(mask, (int(points[i][0]), int(points[i][1])), (int(points[i 1][0]), int(points[i 1][1])), color1, thickness8) return mask这段代码的关键在于cv2.line的thickness参数。我一般取 8既能保证 mask 里的车道线连续又不会让正样本区域过大影响类别平衡。shape参数要和训练输入尺寸保持一致的约定否则模型前向传播拿到的标签尺寸对不上后面训练必然报错。另一个容易忽略的问题是颜色通道顺序cv2.imread读进来是 BGRPIL读的是 RGB训练时两者混用会让模型看到完全不同的颜色分布表现就是损失函数在震荡中缓慢下降验证集指标一直上不去。2.3 骨干网络选型轻量 U-Net 是毕业设计性价比最高的起点卷积神经网络的选择决定了显存占用、训练速度和最终精度。以我帮人看过的大量车道线源码来看最常见的三类骨干是 VGG16 作为编码器的全卷积网络、ResNet18 作为编码器的分割网络以及各种轻量 U-Net 变体。纯 VGG16 结构简单但参数量大低显存显卡上 batch_size 只能开到很小ResNet18 下采样 32 倍之后特征图分辨率太低车道线这种细结构恢复起来损失大我实际最推荐的是把 U-Net 的编码器换成轻量化卷积块下采样 16 倍再用双线性插值和跳跃连接恢复边缘细节。骨架方案参数量级下采样倍数显存占用估算适合场景VGG16 编码器约 34M32x高有较好 GPU 且想堆精度ResNet18 编码器约 12M32x中中等显存、偏通用分类轻量 U-Net约 2~5M16x低毕业设计、低显存、快速迭代这里的“下采样倍数”是理解分割网络的关键参数。下采样倍数越大每层特征图的感受野越大能看到更长的车道线段但位置信息丢失越严重车道线需要像素级定位所以下采样 16 倍比 32 倍更适合这个任务。拿到源码后先改这个参数你会发现训练速度和最终检测连续性的变化非常明显。import torch import torch.nn as nn class ConvBlock(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), ) def forward(self, x): return self.conv(x) def build_encoder_decoder(in_channels3, num_classes1): enc1 ConvBlock(in_channels, 32) enc2 ConvBlock(32, 64) enc3 ConvBlock(64, 128) enc4 ConvBlock(128, 256) # 编码器每经过一个 ConvBlock 后接 2x2 最大池化 return enc1, enc2, enc3, enc4代码里把每一层的输出通道数写死了这是适合低显存机器跑的配置。如果显存 6G 以上可以把每层通道数翻倍精度会有提升。注意最后一个编码块一般不需要再接池化否则下采样达到 32 倍车道线边缘会模糊。这个结构对应“拿到源码先看结构”的步骤你在源码里找down_sample、upsample、skip_connection这些关键词就能快速判断作者用的是哪条技术路线。3. 用 Python 把源码跑通环境、目录、推理参数与最小命令3.1 环境对齐CUDA、PyTorch、OpenCV 的版本搭配是第一道坎这类源码最常见的问题是“我机器上明明安装好了 Python怎么一跑就报错”。Python 安装教程到处都是但真正的坑是虚拟环境里包版本冲突。下面这套组合是车道线检测源码里出现频率最高、兼容性也最稳的Python 3.8 或 3.10 都行PyTorch 1.10 到 2.x 均可CUDA 用 11.3 或 12.1配套的torchvision、opencv-python、numpy、pillow一并装好。依赖项推荐版本说明Python3.8 / 3.103.11 以上某些老源码的 setup.py 会出问题PyTorch1.10 ~ 2.1选和你 CUDA 匹配的那一版CUDA Toolkit11.3 / 12.1nvidia-smi显示的驱动版本必须高于 toolkit 要求opencv-python4.5 ~ 4.9太新的 4.10 偶尔和部分源码冲突numpy1.21 ~ 1.242.x 会让老代码的np.float直接报错conda create -n lane_det python3.10 conda activate lane_det pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install opencv-python4.8.1.78 pip install numpy1.24.3 pillow安装完成之后先跑一句python -c import torch; print(torch.cuda.is_available())输出True再继续。如果你只有 CPU 机器也不用放弃把上面的cu121换成cpu版本推理一张图只是慢一点不影响功能验证。这就是标题里说的“可直接运行”的底气依赖都不是冷门库conda 和 pip 两条路线都能装齐。这套环境一般能直接跑通不需要为了某个深度学习框子单独折腾编译。3.2 源码目录与 README 文档先花十分钟摸清布局拿到源码第一件事不是双击运行而是先看目录结构。这类项目通常按固定套路组织data/放训练和测试数据models/放网络定义weights/或checkpoints/放训练好的模型文件train.py是训练入口predict.py或demo.py是推理入口utils/里放着数据加载和后处理工具。如果作者贴心地写了README.md里面一般会给出 Python 版本、依赖清单、训练命令和推理命令照着执行通常就能看到效果。lane_detection_project/ ├── README.md ├── requirements.txt ├── train.py ├── predict.py ├── models/ │ ├── unet.py │ └── backbone.py ├── utils/ │ ├── dataset.py │ ├── postprocess.py │ └── visualize.py ├── weights/ │ ├── model_best.pth │ └── model_last.pth ├── data/ │ ├── images/ │ └── masks/ └── config.py这个布局基本是车道线分割源码的“标准答案”。你拿到项目后先检查weights/目录下的文件是否存在少了模型文件后面全部白搭再打开config.py看默认的输入尺寸、类别数、阈值这三个核心参数。输入尺寸决定了显卡显存用量类别数在二分类分割里通常是 1阈值影响检测线的粗细和断裂程度。README 里最容易被忽略的是“模型是在什么分辨率下训练的”这一条如果你用 512x288 训练完的模型去跑 1920x1080 的输入不做缩放会直接 OOM 或者检测效果崩坏。3.3 推理脚本逐段拆解加载模型、预处理、forward、后处理推理流程比训练简单得多一共四步读图像、预处理、网络前向、后处理。预处理里面有三个细节决定成败一是cv2.resize到模型输入尺寸二是 BGR 转 RGB三是除以 255 归一化。很多人在这一步漏掉通道转换导致模型输入的三通道数据分布和训练时不一致检测出来的车道线在彩色视频上偏移甚至完全消失。下面这段代码是标准的推理主干可以直接对照源码逐行看。import cv2 import torch import torch.nn.functional as F def run_inference(model, image, device, img_size(288, 512), threshold0.5): # image: 原始 BGR 图像先缩放到模型输入尺寸 h, w img_size img_resized cv2.resize(image, (w, h)) # BGR - RGB并转为连续内存的 numpy 数组 img_rgb cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) x torch.from_numpy(img_rgb).permute(2, 0, 1).unsqueeze(0).float() x x / 255.0 x x.to(device) with torch.no_grad(): output model(x) # 分割模型输出通常是未经过 sigmoid 的 logits prob torch.sigmoid(output) prob F.interpolate(prob, size(image.shape[0], image.shape[1]), modebilinear, align_cornersFalse) prob prob[0, 0].cpu().numpy() binary_mask (prob threshold).astype(uint8) return binary_mask这里最值得强调的参数是threshold它控制着二值化的严格程度。阈值取 0.5 是经验默认值但我会先跑三张不同场景的图看看效果如果车道线断断续续说明阈值偏高调到 0.3~0.4如果背景噪声大、路面裂缝也被当成车道线说明阈值偏低可以往 0.6 方向调。F.interpolate这一步是把低分辨率预测图拉回原始视频分辨率这样画线时坐标不用缩放省掉一层换算。注意permute(2,0,1)就是把 HWC 转成 CHWunsqueeze(0)是加 batch 维这两行在源码里经常被写成transpose或expand_dim本质一样别被不同写法绕晕。3.4 后处理滑动窗口切片与二次曲线拟合拿到二值 mask 之后直接画在视频上效果会很“碎”因为单个像素的预测结果有噪声。常见做法是把整幅 mask 按高度切成若干个横向滑动窗口在每个窗口里统计这条线上响应最亮的点然后把这些点用最小二乘法拟合一条二次曲线。这也恰好和“滑动窗口滤波”的思路对应窗口宽度决定了对噪声的平滑程度窗口太窄拟合曲线抖动窗口太宽弯道拟合误差大。def fit_lane_line(binary_mask, window_rows10, poly_degree2): h, w binary_mask.shape xs, ys [], [] # 从下往上扫因为靠近车头的车道线更可信 for y in range(h - window_rows, 0, -window_rows): window binary_mask[y:y window_rows, :] # 当前窗口所有非零像素的横坐标 cols np.where(window.sum(axis0) 0)[0] if len(cols) 0: continue xs.append(cols.mean()) ys.append(y window_rows // 2) if len(xs) 3: return None # polyfit 对异常点敏感先去掉偏离均值过远的点 return np.polyfit(ys, xs, poly_degree)window_rows我一般取 10 到 20 像素图像分辨率是 288 高的话取 10 比较合适。poly_degree取 2 表示二次曲线直线路段它自动退化成近似一次大弯道也能贴合取 3 以上容易发生过拟合车道线尾部会翘起来。拟合之前最好做一步离群点剔除否则路面上的反光点会把曲线带偏。这一步在论文里可以包装成“基于结构化先验的车道线后处理算法”属于可以写进毕业设计文档的技术点。源码的 README 文档里通常也会描述这一段照着参数调一遍就能感受到不同窗口尺寸的效果差异。4. 自己训练车道线模型数据准备、损失函数与参数调优4.1 训练数据组织images 和 masks 如何配对跑通推理只是第一步毕业设计要想有东西可写必须自己训练至少一遍。数据组织是最枯燥但最容易出错的环节。常见做法是data/images/放原始图像data/masks/放对应的二值标签文件名保持一致例如0001.jpg和0001.png。训练时按文件名去读而不是按顺序遍历能避免因为文件排序不一样导致的“图像和标签错位”这种致命伤。下面给一个最小 Dataset 实现。from torch.utils.data import Dataset from PIL import Image import os class LaneDataset(Dataset): def __init__(self, image_dir, mask_dir, img_size(512, 288)): self.image_dir image_dir self.mask_dir mask_dir self.img_size img_size self.names [f for f in os.listdir(image_dir) if f.endswith((.jpg, .png))] def __len__(self): return len(self.names) def __getitem__(self, idx): name self.names[idx] # 按文件名拼接路径避免错位 image Image.open(os.path.join(self.image_dir, name)).convert(RGB) mask Image.open(os.path.join(self.mask_dir, name.replace(.jpg, .png))) # 统一缩放且保证 image 和 mask 用同一插值方式 image image.resize((self.img_size[0], self.img_size[1])) mask mask.resize((self.img_size[0], self.img_size[1]), resampleImage.NEAREST) return image, mask这段代码的关键是mask的缩放必须用Image.NEAREST。因为 mask 是二值图如果用双线性插值0 和 1 之间会插出 0.3、0.7 这种小数训练时标签不再干净模型会学到一堆语义模糊的边界。另一个细节是image.resize里第一个参数是(宽, 高)也就是(512, 288)对应宽 512 高 288这个顺序和 OpenCV 的(宽, 高)一致但和numpy的(height, width)不一样经常有人在这里翻车导致矩阵转置后训练完全跑不动。4.2 训练入口参数表先知道每个参数在控制什么训练脚本一般会提供一个长长的参数列表理解这些参数是调优的前提。我给一个典型的train.py参数表也是在评估别人源码时重点检查的七个位置。参数名常见默认值作用与调整建议--epochs50数据量小时 30~50 就够太多开始过拟合--batch_size8显存不足时先降到 4同时按比例降低学习率--lr1e-3Adam 用 1e-3SGD 用 1e-2以 loss 是否平稳为准--img_size512x288保持和预训练权重一致否则需要从头训练--lossbce二分类优先用 bce类别不平衡时换 focal--save_every5每 5 个 epoch 保存一次 checkpoint防止覆盖--seed42固定随机种子保证实验可比python train.py \ --data_dir ./data \ --epochs 50 \ --batch_size 8 \ --lr 1e-3 \ --img_size 512 288 \ --loss bce \ --save_every 5--loss bce这句我一般会多看两眼如果源码里只写了这一种损失函数训练很快会进入精度瓶颈。车道线分割的正样本占比通常只有百分之几普通 BCE 会让模型倾向于把一切都预测成背景。但你也不要一上来就改损失函数先按默认参数训练 20 个 epoch记录 loss 曲线再把损失换成变体对比实验才有说服力这也是毕业设计论文里很有用的一个分析章节。4.3 类别不平衡是车道线分割最大的敌人车道线在图像中占据的像素比例非常低普通交叉熵损失很容易被背景主导。解决思路有两种一是给正样本更高的权重二是在线困难样本挖掘。第一种实现最简单在损失函数里传入一个权重向量正样本权重设为 5~10背景权重保持 1。第二种是 Focal Loss公式里增加调制因子让模型更关注难分类的像素。下面的代码是集成到训练循环里的 Focal Loss 实现直接用二分类形式。class FocalLoss(nn.Module): def __init__(self, alpha0.25, gamma2.0): super().__init__() self.alpha alpha self.gamma gamma def forward(self, logits, targets): # logits: [B, 1, H, W]targets: [B, 1, H, W] 取值 0/1 bce F.binary_cross_entropy_with_logits(logits, targets, reductionnone) prob torch.sigmoid(logits) p_t prob * targets (1 - prob) * (1 - targets) focal (1 - p_t) ** self.gamma * bce # alpha 对正样本放大 alpha_t self.alpha * targets (1 - self.alpha) * (1 - targets) return focal.mul(alpha_t).mean()alpha0.25和gamma2.0是 Focal Loss 论文里针对单阶段检测器调出来的经验值在车道线分割上可以直接用。当正样本比例低于 1% 时把alpha调到 0.5 以上会有效果反过来说如果正样本比例已经不低Focal Loss 反而会拖慢收敛。你用这个损失替换源码里的 BCE 之后一般 10 个 epoch 内就能看到预测图的线形明显干净很多。换损失函数是毕业设计最划算的“创新点”之一代码量小、效果直观、论文里还好写。4.4 训练循环的最小骨架loss 记录与 checkpoint 保存训练循环本身各家写法差异不大但有两个容易被忽视的坑一是忘记model.train()和model.eval()切换导致 BatchNorm 层的统计量在推理时崩坏二是只保存最后一个 epoch 的权重如果末尾过拟合了前面效果最好的 checkpoint 就浪费了。下面这个骨架是我每次都会参考的写法重点看验证集的使用方式。for epoch in range(start_epoch, epochs): model.train() train_loss 0.0 for images, masks in train_loader: images, masks images.to(device), masks.to(device) optimizer.zero_grad() logits model(images) loss criterion(logits, masks) loss.backward() optimizer.step() train_loss loss.item() # 每个 epoch 结束跑一次验证集挑选最优权重 model.eval() val_iou compute_iou(model, val_loader, device) if val_iou best_iou: best_iou val_iou torch.save(model.state_dict(), os.path.join(save_dir, model_best.pth))训练过程中我建议同时打印 train_loss 和验证集 IoU而不是只看 loss。loss 下降不代表模型学会了车道线它可能只是在学“把所有像素预测为背景”这个小 trick。只有 IoU 上升才说明模型真正把正样本识别出来了。model_best.pth和model_last.pth分别保存推理时优先加载model_best.pth这是标题“内有训练好的模型可直接运行”背后比较负责的做法。5. 车道线检测避坑指南训练和推理中最常见的 4 个翻车点5.1 训练 Loss 不降、预测结果全黑现象训练了二十多个 epochloss 始终在一个高位震荡把模型拿出来推理输出的 mask 全黑没有一条车道线。原因最常见的有三种。一是标签路径配错Dataset里读到的 mask 其实是全零图模型学不到正样本二是图像通道顺序不一致训练时用 PIL 读 RGB推理时用 OpenCV 读 BGR同一个torch.tensor进了模型分布完全不同三是 mask 里车道线的值不是 1 而是 255Sigmoid 之后目标值变成 0.99模型在拟合一个它很难达到的数。解决先写一段可视化脚本用plt.imshow同时看 image 和 mask确认 mask 真的画出了线再在训练代码里强制统一 BGR/RGB我建议统一用 PIL 读图像、mask 用cv2.imread(..., 0)读最后都转会到 RGB最后检查 mask 的像素值如果是 255 就除以 255 再训练。5.2 一调大 batch_size 就 OOM显存直接爆掉现象默认 batch_size 能跑调大一倍就报CUDA out of memory而且不是那种可以靠torch.cuda.empty_cache缓解的临时性溢出。原因车道线的原始训练图往往来自行车记录仪分辨率可能是 1280x720 甚至 1920x1080。源码里transform先做了固定 resizing但有些人会把输入尺寸设成和训练集原始分辨率一样大或者模型下采样倍数不够特征图数量又大显存自然扛不住。解决先把img_size从大的原始分辨率降到(512, 288)这是显存和精度相对平衡的点再把 batch_size 减半。低显存场景下还可以在训练循环里加一句torch.cuda.empty_cache()在每次验证之前调用。如果你只有 4G 显存把编码器第一层通道数从 64 改到 32效果远比硬调 batch_size 来得明显。5.3 检测出的车道线断断续续弯道处完全对不上现象直道检测效果不错但弯道处车道线断成几截拟合出来的曲线和实际道路严重偏离。这往往发生在后处理阶段。原因一个原因是二值化阈值太高概率只有 0.4 的车道线像素被滤掉导致滑窗里车道线的响应点太少另一个原因是滑窗宽度设得太大把弯道处两条相邻车道线混进了同一个窗口拟合时取平均横坐标当然会对不准。解决先把阈值从 0.5 降到 0.35 左右观察断线是否变少再把滑动窗口高度从 20 改成 10提高对弯道的分辨率最后在拟合前加离群点剔除把超过该窗口预测曲线三个标准差以上的点去掉。这些参数都集中在后处理脚本里调起来不需要重新训练改完立刻跑视频验证找感觉。5.4 换一台电脑跑不起来报 CUDA / OpenCV / Python 版本错误现象在作者的机器上能直接运行拷到自己的机器上之后要么import torch就崩要么cv2.imshow打开窗口闪退要么报一堆undefined symbol。原因深度学习代码对环境极其敏感。作者可能是 CUDA 11.3你机器装的是 CUDA 12.1而torch二进制包是强绑定 CUDA 版本的还有人是全球直装 Python没有建虚拟环境pip 把系统里的包版本冲得乱七八糟另外如果项目路径带了中文某些旧版 OpenCV 的VideoWriter会直接打不开文件。解决第一步在项目根目录创建一个environment.yml或写清 requirements第二步用 conda 从零建环境先锁numpy2再装和本机 CUDA 匹配的 PyTorch第三步CPU 机器先把predict.py里的.to(cuda)改成.to(cpu)跑通确认问题只在速度不在代码逻辑。这一步能排除掉八成环境类报错。5.5 验证集指标还可以演示视频里一塌糊涂现象训练结束之后验证集 IoU 达到 0.7自己录了一段演示视频结果车辆一换道、阳光一变角度检测线就开始乱跳。原因训练集和验证集来自同一段视频的不同帧场景分布过于接近模型其实没有学到跨场景泛化只是记性比较好。演示视频里出现了训练集里没有的汽车玻璃反光、隧道灯光、路面裂缝这些都会被当作车道线。解决我在前面章节里反复强调数据多样性的原因就在这里。你要么换一个包含多种天气的数据集重新训练要么在训练时把输入图像做随机亮度扰动和轻度透视变换强行让模型见过更多变化。毕业设计答辩时不要去挑好看的晴天视频而是拿一段包含阴影和弯道的视频来演示模型如果能稳住这一条故事的置信力就建立起来了。提示排查问题时养成记录“现象→原因→解决”的习惯。这个习惯除了能帮你定位刚才这些坑还是毕业设计“问题分析”章节最好的素材真实排查过程的含金量远高于默写模型结构。6. 让车道线检测更像个毕设交付验证指标与演示脚本6.1 把视频检测结果输出成可复现的 mp4训练完成、后处理调顺之后最后一步是把结果从“控制台打印”变成“一段视频”。准备一个demo.py读取一段行车视频逐帧推理、逐帧画线写入output.mp4。关键参数是VideoWriter的编码格式OpenCV 在 Linux 下用mp4vWindows 下用MJPG兼容性最好帧率要和原视频一致否则生成的文件不是快放就是慢放。import cv2 cap cv2.VideoCapture(demo_input.mp4) fps int(cap.get(cv2.CAP_PROP_FPS)) width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) writer cv2.VideoWriter(output.mp4, cv2.VideoWriter_fourcc(*mp4v), fps, (width, height)) while True: ret, frame cap.read() if not ret: break mask run_inference(model, frame, device) overlay draw_lane_lines(frame, mask) # 在原图上画拟合曲线 writer.write(overlay) writer.release() cap.release()这段代码本身没有复杂逻辑但有一个容易被忽略的细节fps必须从输入视频读取不能写死 30。如果用cap.get拿到的是0.0通常是视频编码问题换一段视频验证而不是手动补一个帧率。draw_lane_lines是我封装的一个函数里面用cv2.polylines把拟合出的曲线的点集画出来线宽取 6 像素颜色用蓝绿色这样在答辩投屏上看得清楚。6.2 答辩时的验证指标只看准确率是不够的很多同学在毕业设计里只写一个“准确率 95%”这个数字在车道线检测里很虚因为背景像素占了绝大多数全预测成背景都有 90% 以上准确率。业界更认的是 IoU交并比和 FPS每秒处理帧数这两个指标。IoU 衡量预测 mask 和真实 mask 的重叠程度FPS 衡量是否满足实时性。写论文时把这两个指标放一起再附上一张不同场景下的对比表格信息量远大于一个孤立准确率。def compute_iou_metric(pred_mask, gt_mask): intersection (pred_mask gt_mask).sum() union (pred_mask | gt_mask).sum() if union 0: return 1.0 return intersection / union这里的和|是按位运算输入必须是 0/1 的uint8数组。如果直接拿 0 和 255 的图算union 会被大量真值 255 撑大IoU 数值会明显偏低这也是很多人复现论文精度时对不上的原因之一。我一般会额外统计“只算车道线像素的 IoU”和“全图 IoU”两个数字前者对模型能力更敏感后者对工程效果更直观。6.3 文档要写得像一个交付物而不是实验记录最后说一句关于项目里那份“文档说明”的体会。拿到这类项目时我习惯先检验三件事依赖清单是否完整、训练入口是否有一条从零跑到出的完整命令、推理入口是否能脱离训练环境直接运行。给自己的项目写文档时也按这个标准来不要只贴一行python train.py。我见过太多人训练跑完就算结束了结果答辩前一天发现换台机器没有 GPU整个演示无法进行后来我把推理代码改成了 GPU 可用时自动用 GPU没有 GPU 就退回 CPU只改了一个device分支整场演示就再也没有出过硬件问题。这是我做这类项目吃过最大的亏也是我现在拿到源码后第一个动手改的位置。希望帮到你。本文还有配套的精品资源点击获取
返回列表