☰
灯光检测自训数据全流程:从标注规范到YOLOv8模型收敛
2026/10/7 1:25:55 网站建设 项目流程

简介:这份资源面向计算机视觉入门与进阶开发者,提供一套基于YOLOv5的灯光检测自训练数据与完整工程代码,可用于自动驾驶、安防监控、无人机导航等场景下的灯光目标识别与定位练习。压缩包共1580个文件,约603.83MB,其中696张jpg图像与630个txt标注文件构成核心数据集,65个yaml与54个py脚本负责模型配置和训练流程,另有15个pt权重、18张png及若干sh、yml、md等辅助文件,覆盖数据采集、标注、增强到训练微调的完整链路。资源中附带TensorBoard训练日志与多版本YOLOv5配置,便于读者复现训练过程、观察损失曲线并调整锚框与模型规模。目前已有252人学习下载,适合希望掌握自训练数据流程、理解YOLOv5在灯光检测中应用细节的读者参考实践。

1. 灯光检测自训数据:从零标注到模型收敛的完整路径

工业质检、智慧路灯、车载视觉里都有灯光检测的影子,但真正卡住大多数人的不是模型结构,而是数据。公开数据集里灯光样本少、场景单一,直接拿 COCO 或 VOC 预训练权重去跑,夜间过曝、逆光、多光源重叠这几类场景几乎必翻车。自己训练数据做灯光检测,核心要解决三件事:灯光目标怎么定义、标注规则怎么统一、小目标与过曝区域怎么让模型学到。适合有 Python 基础、手头有几百到几千张现场图、想用 YOLO 系列或 DETR 系列跑通一套可迭代流程的工程师。下面按数据采集、标注、格式转换、训练调参、避坑、进阶验证的顺序讲透,每一步都给可抄的命令和参数。

2. 灯光检测的数据采集与标注规范:先定规则再动手

2.1 灯光目标的边界定义

灯光检测最容易翻车的地方是标注不一致。同一张图,有人把灯罩框进去,有人只框发光区域,有人把反光也算成灯。模型学到的就是一团混乱。我一般会先定三条硬规则:第一,只框发光体本身,灯罩、支架、电线杆一律不框;第二,过曝导致发光区域溢出到灯罩外时,以可见发光边缘为准,不脑补物理边界;第三,多灯重叠时分别框,重叠面积超过 70% 才合并为一个目标。这三条写进标注文档,所有标注人员先标 50 张交叉校验,IoU 一致性低于 0.85 就重新培训。灯光检测的类别通常不需要太细,路灯、车灯、信号灯、装饰灯四类足够覆盖大多数场景,类别越多单类样本越少,小目标召回掉得越快。

2.2 采集设备与场景覆盖

采集阶段决定模型上限。手机拍和工业相机拍出来的过曝特性完全不同,如果部署端是固定相机,训练数据就必须用同型号设备采集。场景覆盖按三个维度铺:时间维度覆盖白天、黄昏、夜间;天气维度覆盖晴、雨、雾;角度维度覆盖正对、侧向、俯视。每个维度至少 200 张,否则模型在边缘场景的漏检率会高得离谱。夜间过曝样本要单独凑够 300 张以上,这是灯光检测和普通目标检测最大的差异点。采集时关掉自动 HDR 和夜景增强,这些算法会改变发光区域的像素分布,导致训练和推理不一致。

2.3 用 LabelImg 和 X-AnyLabeling 做标注

标注工具选 LabelImg 做矩形框,X-AnyLabeling 做半自动预标注。先跑一遍预训练模型生成粗框,人工修正,效率能提升三到五倍。安装和启动命令如下:

pip install labelImg labelImg ./images ./classes.txt ./annotations

classes.txt每行一个类别名,顺序必须和后续训练配置里的names完全一致,否则类别索引错位,模型会把路灯学成车灯。标注保存为 Pascal VOC 格式的 XML,每张图一个文件,文件名和图片名一致。半自动预标注用 X-AnyLabeling 加载 YOLOv8 权重,导出同样选 VOC 格式,保持格式统一,后面转换脚本只写一套。

# 检查标注完整性:图片和XML是否一一对应 import os img_dir = "./images" xml_dir = "./annotations" imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir)} xmls = {os.path.splitext(f)[0] for f in os.listdir(xml_dir)} missing_xml = imgs - xmls missing_img = xmls - imgs print("缺标注的图:", len(missing_xml), "缺图的标注:", len(missing_img))

这段脚本在标注中期跑一次,能提前发现漏标和错名。missing_xml大于 5% 就停下来补标,不要带着脏数据进训练,否则后面调参全是玄学。

3. 从 VOC 到 YOLO 格式:转换脚本与四个边界坑

3.1 VOC 转 YOLO 的坐标归一化

YOLO 格式要求每行class_id x_center y_center width height,全部归一化到 0 到 1。VOC 的xmin ymin xmax ymax是绝对像素,转换时用图片实际宽高做分母。常见错误是用固定尺寸归一化,图片尺寸不一致时框全偏。转换脚本如下:

import os import xml.etree.ElementTree as ET from PIL import Image classes = ["street_light", "car_light", "signal_light", "decor_light"] def voc_to_yolo(xml_path, img_path, out_path): tree = ET.parse(xml_path) root = tree.getroot() w, h = Image.open(img_path).size lines = [] for obj in root.findall("object"): cls = obj.find("name").text if cls not in classes: continue cid = classes.index(cls) bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 边界裁剪,防止标注越界导致归一化后为负 xmin, ymin = max(0, xmin), max(0, ymin) xmax, ymax = min(w, xmax), min(h, ymax) xc = (xmin + xmax) / 2 / w yc = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cid} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines))

classes列表顺序必须和标注时一致。max(0, xmin)和min(w, xmax)是后悔药,标注人员手抖框出边界时,不裁剪会得到负坐标,训练时直接报错或学出诡异框。

3.2 数据集划分与 data.yaml 配置

转换完成后按 8:1:1 划分训练、验证、测试。划分要按场景分层,不能随机抽,否则夜间样本可能全进训练集,验证集全是白天,指标虚高。目录结构如下:

dataset/ images/ train/ val/ test/ labels/ train/ val/ test/ data.yaml

data.yaml内容:

path: ./dataset train: images/train val: images/val test: images/test nc: 4 names: ["street_light", "car_light", "signal_light", "decor_light"]

nc必须等于names长度,多一个少一个都会在训练启动时报索引越界。path用相对路径时,训练命令的工作目录要和 yaml 所在目录一致,否则找不到图片。

3.3 四个边界坑

第一个坑,图片和标签文件名不一致,YOLO 按文件名匹配,差一个下划线就丢样本,训练日志里train数量对不上时先查这个。第二个坑,中文路径,OpenCV 读图在部分环境直接返回 None,全链路用英文路径。第三个坑,标注框宽高为 0,转换后bw或bh为 0,训练时 loss 变 NaN,转换脚本里加if bw < 1e-6 or bh < 1e-6: continue。第四个坑,类别名大小写不一致,Street_light和street_light会被当成两类,nc对不上,统一小写加下划线。

4. YOLOv8 训练灯光检测:参数怎么设、指标怎么看

4.1 环境安装与最小训练命令

YOLOv8 用 ultralytics 包,一条命令跑通最小训练:

pip install ultralytics yolo detect train data=./dataset/data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0

model=yolov8s.pt是预训练权重,灯光检测样本少时用 s 或 n 版本,样本超过 5000 张再上 m。imgsz=640是输入尺寸,小目标多时提到 1280,但显存翻四倍,batch 要相应降到 4 或 8。device=0指定第一块 GPU,CPU 训练 100 epoch 要跑十几个小时,不推荐。

4.2 灯光检测必调的五个参数

参数默认值灯光检测建议原因
imgsz640960 或 1280远处路灯在 640 下只有几个像素
lr00.010.005小数据集高学习率容易震荡
mosaic1.00.5过曝样本拼接后发光区域叠加,干扰学习
hsv_v0.40.2亮度增强会模拟过曝,但过强导致发光区域失真
patience5030灯光检测收敛快,30 轮不升就停

mosaic=0.5是血泪经验,默认 1.0 时四张图拼一张,夜间过曝区域叠在一起,模型分不清哪个是灯。hsv_v降到 0.2 保留一定亮度鲁棒性,又不至于把正常灯变成过曝灯。

4.3 训练日志与指标解读

训练启动后看三个指标:box_loss、cls_loss、mAP50。box_loss降到 0.5 以下、cls_loss降到 0.3 以下、mAP50超过 0.85 算收敛。灯光检测的mAP50-95通常比mAP50低 0.2 以上,因为发光区域边界模糊,IoU 高阈值下框不准。如果mAP50高但mAP50-95极低,说明框的位置偏差大,检查标注边界是否统一。验证集mAP波动超过 0.05 时,把batch调大或lr0调小。

from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") metrics = model.val(data="./dataset/data.yaml", imgsz=960) print(metrics.box.map50, metrics.box.map)

这段在训练后跑,用best.pt在验证集上复算指标,确认训练日志没刷错。imgsz要和训练时一致,否则指标对不上。

5. 灯光检测训练避坑:五条现场排查记录

5.1 损失不降反升

现象:前 10 轮box_loss从 2.0 涨到 3.5。原因:学习率过高或标注框大量越界。解决:lr0降到 0.001,跑转换脚本检查是否有负坐标或宽高为 0 的框,清理后重训。

5.2 夜间样本全漏检

现象:白天mAP500.9,夜间 0.3。原因:夜间样本占比低于 10%,模型偏向白天特征。解决:夜间样本过采样,复制到训练集使占比到 30%,或copy_paste增强把夜间灯粘贴到白天背景。

5.3 过曝区域框过大

现象:预测框比实际发光区域大一圈。原因:标注时把光晕框进去了。解决:重新定义标注规则,只框发光体,光晕不框,已标注的过曝样本返工。

5.4 验证集指标虚高

现象:验证mAP500.95,测试集只有 0.6。原因:划分时同场景图片泄漏,验证集和训练集有同一路段不同帧。解决:按路段或时间段划分,同一路段的图只进一个集合。

5.5 推理速度不达标

现象:训练指标好,部署帧率只有 5 FPS。原因:imgsz1280 加 m 模型,算力不够。解决:换yolov8n,imgsz降到 640,或用 TensorRT 导出,FP16 推理速度翻倍。

yolo export model=best.pt format=engine half=True imgsz=640

导出 TensorRT 引擎,half=True开 FP16,精度掉 1% 以内,速度提升明显。导出后必须用同一批测试图验证精度,确认没掉太多再上线。

6. 灯光检测的进阶验证:用混淆矩阵和过曝切片定位短板

训练完别只看mAP,混淆矩阵能告诉你哪两类在互相误判。灯光检测里street_light和car_light最容易混,因为车灯在远处和路灯形态接近。跑混淆矩阵:

yolo detect val model=best.pt data=./dataset/data.yaml plots=True

plots=True会在runs/detect/val下生成confusion_matrix.png和val_batch0_pred.jpg。看混淆矩阵对角线,非对角线超过 10% 就说明两类特征区分度不够,要么合并类别,要么补这两类的对比样本。val_batch0_pred.jpg看预测框和真值框的偏差,过曝样本的框如果普遍偏大,回去改标注规则。

再做一个过曝切片验证:把测试集按亮度均值分成三档,低、中、高,分别跑val,看三档的mAP50。高亮度档掉超过 0.15,说明过曝鲁棒性不够,补过曝样本或加hsv_v增强。我一般会把这个切片验证写进训练脚本,每次重训自动跑,省得手动切。

import cv2, numpy as np, os from ultralytics import YOLO model = YOLO("best.pt") buckets = {"low": [], "mid": [], "high": []} for f in os.listdir("dataset/images/test"): img = cv2.imread(f"dataset/images/test/{f}") v = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:, :, 2].mean() key = "low" if v < 85 else ("mid" if v < 170 else "high") buckets[key].append(f"dataset/images/test/{f}") for k, paths in buckets.items(): if not paths: continue m = model.val(data="./dataset/data.yaml", imgsz=960, split="test") print(k, len(paths), m.box.map50)

这段按亮度分桶后分别验证,能直接看出模型在哪个亮度区间拉胯。v < 85和v < 170是经验阈值,按自己数据分布调。跑完发现高亮度档差,就针对性补 200 张过曝样本重训,比盲目加 epoch 有效得多。

灯光检测这个方向,数据质量比模型选型重要十倍。我踩过最大的坑是标注规则没统一就开训,结果调了两周参数,最后发现是标注边界不一致。现在我的习惯是:标注前先标 50 张做一致性校验,转换后跑完整性脚本,训练前按亮度分桶看分布,训练后跑混淆矩阵和切片验证。这套流程走下来,灯光检测的mAP50稳定在 0.9 以上,夜间漏检控制在 5% 以内。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询