简介:本资源为基于YOLOv8的夜间车辆识别目标检测项目代码,面向具备一定深度学习与计算机视觉基础的开发者、自动驾驶研究者及智能交通方向的学生,用于解决夜间低照度环境下车辆轮廓与特征难以捕捉、识别准确率下降的问题。压缩包共474个文件,约24.43MB,以Python源码、Markdown说明文档、YAML配置、JPG/PNG示例图片及预训练权重文件为主,另含Dockerfile、C++推理代码与依赖清单,覆盖训练、推理与部署环节。项目提供main.py入口与requirements.txt环境配置,并附带yolov8n.pt预训练模型,可直接微调或预测,显著降低训练成本。目前已有169人学习下载,适合希望快速复现夜间车辆检测、理解YOLOv8工程结构并积累目标检测实战经验的读者参考。
1. 夜间车辆识别为什么总在路灯下翻车
白天跑得挺欢的 YOLOv8 车辆检测模型,一到晚上就集体失灵——远光灯糊成一片白斑、路灯下的车身只剩轮廓、暗处的车尾灯被当成噪点滤掉。这不是模型不行,是夜间成像的物理特性把检测任务从「找车」变成了「在低信噪比图像里找车」。YOLOv8 夜间车辆识别要解决的核心问题,就是在可见光严重退化、目标对比度骤降、运动模糊加剧的条件下,仍然稳定输出车辆的位置和类别。
这套方案适合三类人:做智慧交通或停车场管理的工程团队、需要夜间路侧感知的自动驾驶数据标注与预标注环节、以及手里有夜间监控视频想快速跑出检测结果的开发者。它不追求论文级 SOTA,追求的是「今晚就能在本地跑通、明天能换自己的数据」。下面从数据、模型、训练、部署到排错,按我实际落地的顺序拆开讲。
2. 夜间数据怎么准备:从采集到 YOLO 格式的完整链路
2.1 夜间图像的三个硬约束
夜间车辆检测的数据集和白天有本质区别,采集阶段就要盯住三件事。第一是光照动态范围:路灯直射区域和阴影区域亮度差可能超过 100dB,普通相机要么高光溢出要么暗部死黑,建议采集时开 HDR 或 RAW 格式,后期再做色调映射。第二是运动模糊:夜间曝光时间被迫拉长,车速 40km/h 时 1/30s 快门就能产生明显拖影,标注框要覆盖拖影范围而不是只框清晰部分。第三是色温漂移:钠灯偏黄、LED 偏冷白,同一辆车在不同光源下颜色差异极大,数据增强时必须做色温扰动。
我一般会按「场景 × 时段 × 天气」三个维度分层采集,每个组合至少 200 张,否则训出来的模型换个路口就崩。夜间数据里「无车但有光源干扰」的负样本要占 15% 左右,专门治路灯和霓虹灯误检。
2.2 标注规范:夜间框怎么画才不坑自己
夜间标注最容易犯的错是「只框亮的部分」。车尾灯亮、车身暗,新手往往只框尾灯,结果模型学成「尾灯检测器」。正确做法是:框住车辆完整可见轮廓,包括暗部车身,如果暗部完全不可见,至少框到能推断车辆朝向的范围。被远光灯完全淹没的车,标注为「困难样本」并在训练时降低权重,而不是直接丢弃。
标注格式用 YOLO 的 txt:每行class_id x_center y_center width height,全部归一化到 0-1。类别建议先粗分car / truck / bus / motorcycle,夜间细分车型意义不大,先保证「是不是车」的召回率。
2.3 从原始视频到 YOLO 数据集的转换脚本
采集回来的是视频,需要抽帧、筛选、生成标注文件。下面这个脚本做三件事:按间隔抽帧、用简单亮度阈值过滤全黑帧、生成 YOLO 目录结构。
import cv2 import os import numpy as np def extract_night_frames(video_path, out_dir, interval=30, min_brightness=15): """ 从夜间视频抽帧 interval: 每多少帧取一张 min_brightness: 平均亮度低于此值视为无效帧丢弃 """ os.makedirs(f"{out_dir}/images", exist_ok=True) os.makedirs(f"{out_dir}/labels", exist_ok=True) cap = cv2.VideoCapture(video_path) idx, saved = 0, 0 while True: ret, frame = cap.read() if not ret: break if idx % interval == 0: gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 夜间有效帧的平均亮度通常高于15,全黑帧直接丢 if np.mean(gray) > min_brightness: name = f"night_{saved:05d}" cv2.imwrite(f"{out_dir}/images/{name}.jpg", frame) # 生成空标签占位,后续用标注工具填充 open(f"{out_dir}/labels/{name}.txt", "w").close() saved += 1 idx += 1 cap.release() print(f"共抽取 {saved} 帧有效夜间图像") extract_night_frames("road_night.mp4", "dataset", interval=30, min_brightness=15)逻辑说明:interval=30对应 30fps 视频约每秒一帧,避免相邻帧高度重复;min_brightness=15是经验值,低于这个值的帧人眼也难分辨车辆,留着只会污染训练集。抽完帧后用 labelImg 或 CVAT 标注,导出 YOLO 格式即可。注意标签文件名必须和图像文件名一一对应,否则训练时会被静默跳过。
2.4 夜间专属的数据增强配置
YOLOv8 自带增强偏白天,夜间要额外加三样:随机伽马变换(模拟曝光差异)、随机色温偏移(模拟不同光源)、运动模糊(模拟长曝光拖影)。在data.yaml同级建一个增强配置,训练时通过参数挂载。
# night_aug.yaml augment: hsv_h: 0.015 # 色调扰动,治色温漂移 hsv_s: 0.7 # 饱和度扰动 hsv_v: 0.5 # 亮度扰动,夜间要加大 degrees: 0.0 translate: 0.1 scale: 0.5 fliplr: 0.5 mosaic: 1.0 mixup: 0.1 copy_paste: 0.1参数说明:hsv_v=0.5比默认的 0.4 更激进,专门覆盖暗光场景;mixup=0.1做图像混合能提升暗部目标的泛化;copy_paste=0.1把车辆实例粘贴到不同背景,缓解夜间样本不足。增强不是越多越好,mosaic=1.0在夜间可能把四张暗图拼成一张更暗的图,如果发现训练 loss 震荡,先把它降到 0.5 试试。
3. YOLOv8 模型选型与夜间训练参数怎么定
3.1 n/s/m 三档在夜间的真实差距
YOLOv8 提供 n/s/m/l/x 五档,夜间车辆检测我实测下来推荐s 或 m。n 档参数量太小,夜间低对比度特征提取能力不足,漏检率明显偏高;l/x 档在夜间数据量不足时容易过拟合,且推理速度掉得厉害。如果部署在边缘设备(如 Jetson 系列),s 档是性价比拐点;如果跑在服务器 GPU 上,m 档能多拿 3-5 个点 mAP。
选型时别只看 mAP,要看夜间召回率。白天 mAP 高但夜间召回低的模型,说明它学的是纹理而非形状特征,换个光照就废。训练时把验证集按光照强度分桶,分别看指标。
3.2 从预训练权重开始的训练命令
不要从零训,用 COCO 预训练权重做迁移。夜间车辆和 COCO 里的 car/truck/bus 高度重叠,迁移效果很好。
yolo detect train \ model=yolov8s.pt \ data=night_vehicle.yaml \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ warmup_epochs=5 \ cos_lr=True \ patience=30 \ device=0 \ project=runs/night \ name=yolov8s_night_v1逻辑说明:lr0=0.01是 SGD 的初始学习率,配合cos_lr=True余弦退火,夜间数据噪声大,余弦退火比阶梯下降更稳;warmup_epochs=5让模型先适应夜间分布再全速学习;patience=30连续 30 轮无提升就早停,夜间数据容易过拟合,早停是后悔药。imgsz=640是默认值,如果车辆在画面中占比很小(如高速远距离监控),可以提到 960 或 1280,但显存和速度要重新评估。
3.3 夜间训练的三个关键超参
除了常规参数,夜间训练要额外关注三个:
| 参数 | 建议值 | 作用 |
|---|---|---|
hsv_v | 0.5-0.6 | 亮度扰动幅度,治曝光差异 |
box | 7.5 | 框回归损失权重,夜间框模糊需加大 |
cls | 0.5 | 分类损失权重,夜间车型难分可降低 |
box=7.5比默认的 7.5 持平,但如果发现定位漂移严重,可以提到 8.0-9.0;cls降到 0.5 是因为夜间车型区分度低,强行分类反而拖累检测。这些值不是金科玉律,要在自己的验证集上网格搜一下。
3.4 训练过程怎么盯:看哪些曲线
启动训练后重点看三条曲线:train/box_loss是否稳定下降(震荡说明学习率偏高或 batch 太小)、metrics/mAP50在夜间验证集上的表现(和白天验证集对比,差距超过 15 个点说明模型没学到光照不变特征)、val/box_loss是否早于 train 开始上升(过拟合信号)。如果 mAP 卡住不动,先检查标注质量,夜间标注错误率通常比白天高 30% 以上。
4. 推理部署与夜间后处理:让模型在暗光下真正可用
4.1 推理前的图像预处理
模型训练时用了增强,推理时也要做对应的预处理才能对齐分布。夜间推理建议加一步自适应直方图均衡(CLAHE),把暗部细节拉出来,但要注意别过度,否则噪声也被放大。
import cv2 import numpy as np from ultralytics import YOLO def preprocess_night(img, clip_limit=2.0, tile_size=8): """夜间图像预处理:CLAHE + 轻度降噪""" lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(tile_size, tile_size)) l = clahe.apply(l) merged = cv2.merge([l, a, b]) out = cv2.cvtColor(merged, cv2.COLOR_LAB2BGR) # 轻度高斯降噪,核大小3够用,太大会糊掉车牌 out = cv2.GaussianBlur(out, (3, 3), 0) return out model = YOLO("runs/night/yolov8s_night_v1/weights/best.pt") img = cv2.imread("test_night.jpg") img = preprocess_night(img, clip_limit=2.0) results = model(img, conf=0.35, iou=0.5, imgsz=640)参数说明:clip_limit=2.0控制对比度增强上限,超过 3.0 会放大噪声导致误检;tile_size=8是分块大小,图像分辨率高时调到 16;conf=0.35比白天的 0.25 高,夜间误检多,提高阈值换精度;iou=0.5是 NMS 阈值,夜间车辆重叠少,可以适当降低到 0.45 减少漏检。
4.2 夜间误检的针对性后处理
夜间最常见的误检是路灯、反光路牌、车灯拖影被当成车辆。后处理加两条规则:长宽比过滤(车辆宽高比通常在 0.5-3.0 之间,过扁过长的框大概率是路灯杆)和亮度一致性检查(框内像素方差过大说明是光源而非车身)。
def filter_night_detections(results, img, min_ratio=0.5, max_ratio=3.0, max_var=3000): """过滤夜间误检:长宽比 + 框内方差""" keep = [] for box in results[0].boxes: x1, y1, x2, y2 = box.xyxy[0].cpu().numpy() w, h = x2 - x1, y2 - y1 ratio = w / h if h > 0 else 0 if ratio < min_ratio or ratio > max_ratio: continue roi = img[int(y1):int(y2), int(x1):int(x2)] if roi.size == 0: continue # 光源区域方差极大,车身区域方差相对小 if np.var(roi) > max_var: continue keep.append(box) return keep逻辑说明:max_var=3000是经验阈值,路灯直射区域方差常超过 5000,车身在 1000-2500 之间。这个值要按自己相机的动态范围调,过严会漏掉高对比度车身,过松治不了路灯。过滤后把keep重新组装成结果输出即可。
4.3 视频流推理的帧间策略
夜间视频推理不要每帧都跑模型,用帧间差分判断有没有新目标进入,只在变化大的帧上跑检测,能省 40% 以上算力。同时用轨迹平滑(如卡尔曼滤波)把抖动框稳住,夜间检测框跳变比白天严重得多。
import cv2 import numpy as np def frame_diff_trigger(prev_gray, curr_gray, threshold=25): """帧间差分判断是否需要跑检测""" diff = cv2.absdiff(prev_gray, curr_gray) _, thresh = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY) change_ratio = np.count_nonzero(thresh) / thresh.size return change_ratio > 0.02 # 变化超过2%才触发检测threshold=25是像素差阈值,夜间噪声大可以提到 30;change_ratio > 0.02表示画面 2% 以上像素发生变化,低于这个值大概率是静止场景,跳过检测。这个策略在停车场监控场景特别有效,车辆进出时才触发。
5. 夜间车辆检测的避坑清单:5 个血泪教训
5.1 坑一:白天数据混入夜间训练集
现象:训练 mAP 很高,一到晚上漏检严重。原因:数据集里白天样本占 80%,模型学到的是白天纹理特征,夜间分布完全没覆盖。解决:按光照分层采样,夜间样本至少占 50%,且验证集必须纯夜间。我一般会单独建一个night_only验证集,训练时只信这个集的指标。
5.2 坑二:标注框只框亮部导致尾灯检测器
现象:模型只检测车尾灯,暗色车身完全漏掉。原因:标注时图省事只框了亮的部分,模型把「亮斑」和「车辆」划了等号。解决:返工重标,框完整车身轮廓;已经训坏的模型没法救,只能重训。这个坑我踩过两次,返工成本极高,标注规范一定要在开始前定死。
5.3 坑三:CLAHE 参数过激引入噪声误检
现象:预处理后画面变亮,但误检率飙升,路灯全被框成车。原因:clip_limit设到 4.0 以上,暗部噪声被同步放大,模型把噪声团当成目标。解决:clip_limit控制在 2.0-2.5,配合高斯降噪;如果还不行,改用 Retinex 类算法做光照归一化,但速度会慢。
5.4 坑四:置信度阈值照搬白天导致漏检
现象:夜间召回率比白天低 20 个点。原因:夜间目标特征弱,模型输出的置信度整体偏低,用白天的conf=0.25会滤掉大量真目标。解决:夜间推理conf降到 0.15-0.2,同时用后处理规则压误检,用规则换召回比单纯调阈值更可控。
5.5 坑五:忽略相机参数导致换设备就崩
现象:在 A 相机上调好的模型,换到 B 相机上完全不能用。原因:不同相机的 ISO、白平衡、伽马曲线不同,夜间成像差异被放大。解决:训练时做相机风格增强(随机伽马、色温、噪声),或者干脆在目标相机上采一批数据做微调。跨相机泛化是夜间检测最难的工程问题,没有银弹,只能靠数据覆盖。
6. 把夜间检测推到可交付:验证方法与一个提点技巧
模型训完不是终点,要有一套验证方法证明它真的能用。我一般分三层验证:离线指标(夜间验证集 mAP50、召回率、误检率)、场景回放(拿几段典型夜间视频跑一遍,人工看漏检和误检分布)、压力测试(极端暗光、强逆光、雨夜各跑 100 帧,看指标衰减)。三层都过了才敢往生产环境推。
一个提点技巧:用检测结果反哺标注。把模型在未标注夜间视频上的高置信度检测框导出,人工修正后加入训练集,迭代两三轮,mAP 通常能再涨 5-8 个点。这比盲目加数据高效得多,因为模型挑出来的正是它当前最不确定的样本。
# 导出高置信度伪标签,供人工修正 yolo detect predict \ model=runs/night/yolov8s_night_v1/weights/best.pt \ source=unlabeled_night_videos/ \ conf=0.6 \ save_txt=True \ save_conf=True \ project=runs/pseudo_labelsconf=0.6只导出模型很确信的框,人工只需修正漏检和少量误检,标注成本能降一半以上。导出的 txt 直接放进训练集目录,改个名就能用。
最后说个我自己的习惯:每次训练前先跑 10 个 epoch 的快速验证,确认数据管道、标注格式、增强配置都没问题,再开全量训练。夜间数据标注成本高,一次训坏重来的代价太大,这 10 个 epoch 的「后悔药」值得吃。希望帮到你。
本文还有配套的精品资源,点击获取