ARTICLE DETAIL

资讯详情

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

基于YOLOV5的红外车辆检测实战:数据、训练与部署全流程

基于YOLOV5的红外车辆检测实战:数据、训练与部署全流程 简介基于YOLOV5的红外车辆检测完整方案整合源码、预训练模型与标注数据集面向计算机视觉开发者、智能交通研究人员解决夜间或恶劣天气下车辆目标难以识别的问题。压缩包共128个文件以Python脚本py/pyc、模型权重pt、网络配置yaml、标注信息xml/json及红外图像jpg/png为主另有演示视频mp4/mkv与说明文档整体约263.77MB涵盖训练、推理与部署全流程。已有1061人学习下载适合需要快速搭建红外车辆实时检测系统的工程师。压缩包内目录结构清晰便于按需查阅。资源内含训练脚本、数据预处理与模型架构定义提供的pt模型可直接加载推理配合测试代码可实现摄像头场景下的实时识别若结合自有红外数据集进行微调可进一步提升特定环境与车型的适配能力是一套可落地复用的目标检测实战参考。1. 红外车辆检测为什么不能直接复用白天模型把在COCO上训练的YOLOV5权重直接接到红外夜视画面过车虚警率往往高到没法用——这不是模型不行而是红外图像和可见光在数据分布上根本不是一回事。YOLOV5做红外车辆识别要拆成两条路走先用对口的红外数据集把模型微调成“认得车”再把推理管线压到能跟着视频流实时出框。这篇笔记按数据、训练、落地、排错四个环节把一套可复现的红外车辆检测方案讲清楚适合手里有红外或热成像摄像头、想基于现成YOLOV5源码快速出效果的工程师。红外画面里车辆常是亮斑或暗斑纹理信息少车灯和高反光件又容易过曝成一片。白天模型学到的边缘、轮廓、色彩统计在红外场景基本失效这不是换个阈值就能解决的事。想实时、可靠地出检测框就得先把数据和训练策略拨正再谈部署优化。2. 红外车辆数据集怎么做采集、标注与切分的完整流程2.1 红外图像和可见光数据的三个结构性差异第一是通道。红外摄像机输出的原始画面通常是单通道灰度而YOLOV5默认按RGB三通道读图。直接把灰度图丢进去模型看到的是同一份数据复制三遍本身没问题问题在于很多人把通道顺序搞错或者把16位红外原始数据直接当成8位图读结果画面灰成一团训练半天loss不降。第二是纹理。可见光下车辆有清晰的车身线条、车牌、玻璃反光这些特征在红外图像里基本消失。车在红外下往往是“一团亮斑”或“一团暗斑”轮廓模糊边缘过渡平缓。这意味着模型不能只靠局部纹理判断得靠整体形状和温度对比度。第三是目标尺度分布。红外车载摄像头视野里远处车辆可能只占二三十个像素近处车辆又可能撑满整个画面。同一段路上车辆尺度跨度比可见光数据集大得多直接沿用默认锚框和输入尺寸小目标漏检率会很高。还有一个常被忽略的点红外图像里天气和季节影响很大。雨天路面和车体温差小冬天车胎和轮拱区域温度高这些都会改变目标外观。采集数据时最好覆盖不同时段、不同天气、不同朝向否则验证集指标好看一上线就现原形。2.2 你需要的文件组织方式和标签格式YOLOV5训练自己的数据集图省事的话把数据按下面的目录结构放好infrared_dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.png │ │ └── ... │ └── val/ │ ├── 000101.png │ └── ... └── labels/ ├── train/ │ ├── 000001.txt │ └── ... └── val/ ├── 000101.txt └── ...每个txt标签文件里的每一行对应一个目标格式是“类别id 归一化中心x 归一化中心y 归一化宽 归一化高”比如0 0.5234 0.4861 0.0825 0.0652这里类别id从0开始如果你的标注软件导出的格式是“xmin ymin xmax ymax”或“cx cy w h”的像素值要写脚本转成归一化格式。红外场景通常只关心“车辆”这一类所以nc1最常见。类别少不等于随便标标注边界要贴着车体轮廓不要把路面热残留圈进去。很多公开红外数据集自带的是可见光对齐标注框直接用会有偏差因为红外和可见光镜头之间存在视差。我一般会在训练前抽样两百张把标注框叠到红外图上肉眼检查一遍。这一步花一小时能省后面反复重训的三五天。2.3 切分训练集验证集的脚本三个参数与一个雷区下面这个切分脚本可以直接用按种子随机切分保证验证集里有足够的正样本import os import random import shutil random.seed(42) src_images infrared_dataset/images # 原始图像目录 src_labels infrared_dataset/labels # 原始标签目录 val_ratio 0.15 # 验证集比例 out_dir yolo_dataset # 输出根目录 for split in [train, val]: os.makedirs(os.path.join(out_dir, images, split), exist_okTrue) os.makedirs(os.path.join(out_dir, labels, split), exist_okTrue) files [f for f in os.listdir(src_labels) if f.endswith(.txt)] random.shuffle(files) val_count int(len(files) * val_ratio) for i, label_name in enumerate(files): split val if i val_count else train stem os.path.splitext(label_name)[0] img_name stem .png # jpg/jpeg按实际后缀改 shutil.copy(os.path.join(src_labels, label_name), os.path.join(out_dir, labels, split, label_name)) shutil.copy(os.path.join(src_images, img_name), os.path.join(out_dir, images, split, img_name))这段逻辑很直接关键看三个参数val_ratio不要低于10%红外数据集通常不大150到300张验证图已经能给出比较稳定的指标random.seed(42)固定下来方便别人复现和对比图像后缀要按你的数据集实际格式改红外采集常输出png或tiff别写死成jpg。这里有个雷区切分前必须保证每个标签文件非空且类别id连续从0开始。红外采集时经常有空文件也就是图像里没目标这些文件留在训练集里会让模型学会“输出空框”最稳妥的做法是切分时把空标签直接过滤掉并在日志里打印过滤数量。2.4 训练前的数据体检用一段脚本验证标注和图像通道花五分钟做一次数据体检比训练到一半发现loss异常再排查强。下面脚本检查三件事标签坐标是否越界、图像通道和位深、以及每张图的目标数量分布from PIL import Image import os import glob label_paths glob.glob(yolo_dataset/labels/train/*.txt) for lp in label_paths[:500]: with open(lp) as f: for line in f: parts line.split() cls int(parts[0]) x, y, w, h map(float, parts[1:]) if x - w/2 0 or x w/2 1: print(f越界: {lp} - cx{x} w{w}) if y - h/2 0 or y h/2 1: print(f越界: {lp} - cy{y} h{h}) if cls ! 0: print(f类别id异常: {lp} - {cls}) img_paths glob.glob(yolo_dataset/images/train/*.png) for ip in img_paths[:200]: img Image.open(ip) if img.mode ! RGB and img.mode ! L: print(f异常图像: {ip} - mode{img.mode}) if img.size[0] 320 or img.size[1] 320: print(f尺寸过小: {ip} - {img.size})YOLOV5训练时会自动把单通道灰度图转成三通道所以modeL不必报错但你要心里有数如果数据集里混着L和RGB两种模式预处理路径不一致验证集指标会有抖动。坐标越界的标签训练时会被自动裁剪但如果同时存在大量导致整框出界的噪声标框梯度会被带偏。体检脚本的输出应当是零报错出现任何一行提示都要回到标注环节处理而不是带着问题继续往下走。3. 训练YOLOV5红外模型配置文件改法与关键超参数3.1 单类别红外模型的data.yaml和模型配置怎么改YOLOV5环境配置好之后训练的第一步不是敲命令是先写对data.yaml。单类别红外数据集的配置很简单path: ./yolo_dataset train: images/train val: images/val nc: 1 names: 0: vehiclepath推荐写相对路径避免把机器相关的绝对路径带进配置。nc改成1后模型输出头的通道数会跟着变类别名写vehicle就行。不要在这里把红外图像预处理参数写进去预处理要么在数据管线里统一做要么在训练脚本里统一做不要两边各改一半。有人习惯直接把数据集拷贝到YOLOV5目录下运行我一般不会这样做。数据集体积大训练中又涉及频繁读取放在机械盘和固态盘上训练速度差距明显。另外path指向的目录里不要出现中文和空格Windows下尤其容易踩路径编码问题。3.2 预训练权重加载方式从COCO迁移红外场景的两种选择YOLOV5官方提供的yolov5s.pt在COCO上训练过直接拿来做红外车辆检测的起点是常见做法但迁移方式要分情况处理。第一种是直接全量微调python train.py --data infrared.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 200这种做法的优点是利用了COCO特征提取器的通用能力适合你的红外数据集图像分布相对稳定、目标形态和COCO里的车接近的场景。缺点是红外域差异大时模型要花更多epoch去“忘掉”可见光特征。第二种是先冻结backbone训练再解冻微调python train.py --data infrared.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --freeze 10--freeze 10表示冻结前10层也就是让backbone先不动只训练检测头去适配红外特征。跑完100轮后再用同一份数据解冻全模型继续微调。这种做法在红外图像质量差、可见光预训练特征用处有限的场景下更稳不容易一开始就把预训练权重破坏掉。3.3 影响红外训练的四个超参数epoch、batch、mosaic和锚框红外数据集通常不大超参数选择和可见光大不相同。我按影响程度排序说说。epoch方面红外场景域差异大200轮是底线我习惯设250到300轮。不要因为数据量小就只跑50轮模型还没适应红外分布就停了指标必然难看。但要注意配合早停机制YOLOV5默认开启耐性停止建议设置--patience 50避免过拟合后还傻跑。batch大小直接受显存约束。低显存运行模型时把batch压到8甚至4但训练速度和BN统计稳定性都会下降。这里有个折中做法固定batch后把图像输入尺寸从默认640降到512目标不大的场景下512足够显存占用却能明显下降。mosaic增强在红外场景要谨慎。YOLOV5默认开启mosaic把四张图拼在一起训练对可见光目标检测提升明显。但红外夜间场景中四张低对比度灰度图拼在一起会造成大量互相干扰的亮度区块模型反而学出很多“假阳性纹理”。我会在训练后半程关闭mosaicYOLOV5官方代码里这个参数叫mosaic从超参数文件里设为0.5或者使用--mosaic 0.5。如果发现损失震荡直接关到0跑完剩余轮次。锚框是红外车辆检测最容易忽略的一环。红外车辆的宽高比和可见光差异不大但尺度分布差异大——远距离小目标占比高长约宽比接近11甚至竖长条。用脚本重算锚框python utils/autoanchor.py --data infrared.yaml --img 640 --rect运行后确认脚本给出的新锚框和默认锚框的差异。如果平均宽高比变化超过20%说明默认锚框确实不匹配你的红外数据训练时用新的锚框。如果差异不大保留默认值即可不用为了改而改。3.4 跑一轮训练前的清单和训练日志怎么看启动训练前我会过一遍清单标注框有没有越界train和val的文件数量比例是否合理类别id是不是只有0超参数文件里mosaic是不是被改成0.5或更低预训练权重路径存在。然后跑一个短迭代确认能正常出losspython train.py --data infrared.yaml --weights yolov5s.pt --img 640 --batch 8 --epochs 5这5轮用来验证数据管线和显存占用不追求指标。确认没报错后再跑正式训练。训练日志里真正值得盯的是这几个数值val/obj_loss、mAP0.5和P/R。train/box_loss降得快不代表模型好用训练loss低到接近0而验证mAP不涨基本就是过拟合信号。红外场景数据量小过拟合比可见光场景来得更早我更关注验证集指标从第几轮开始停滞——如果连续40轮验证mAP不涨就可以停了追求最后那0.5%的mAP没有工程意义。4. 实时检测落地从模型到视频流的工程接线4.1 用训练好的权重跑摄像头推理的最小脚本YOLOV5官方仓库的detect.py本身就能接摄像头做实时检测但工程上我更习惯写一个精简的推理脚本把后处理逻辑露出来方便按场景调参import torch import cv2 import numpy as np model torch.hub.load(./yolov5, custom, pathruns/train/exp/weights/best.pt, sourcelocal, device0) model.conf 0.35 # 置信度阈值红外场景建议先高后低 model.iou 0.45 # NMS的IoU阈值 model.max_det 50 # 单帧最大目标数 cap cv2.VideoCapture(0) # 红外摄像头设备号或RTSP地址 while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, size640) boxes results.pred[0].cpu().numpy() for x1, y1, x2, y2, conf, cls in boxes: cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.imshow(infrared, frame) if cv2.waitKey(1) 0xFF ord(q): break这段代码的核心是后处理三段。model.conf、model.iou和model.max_det对应YOLOV5后处理里的置信度过滤、NMS和数量上限红外场景配高置信度阈值能压掉路面热残留造成的虚警。results.pred[0]返回的是已经做过NMS的框每行是xyxy坐标、置信度和类别id。红外摄像头接入方式和普通USB摄像头一样VideoCapture(0)是设备索引换成RTSP地址就是网络摄像头。注意红外视频帧率本身只有15到25帧如果推理跟不上画面会有明显延迟感。轮询模式在帧率低时没问题但多路或者高分辨率场景就必须做线程化改造见下文。4.2 实时性卡在哪预处理、推理和后处理的时间分账把一帧红外图像从摄像头拉到出框时间主要花在三段预处理、推理、后处理。用推理一百帧的平均耗时去看预处理通常占10%左右后处理在目标密集时能占到30%大头还是模型推理。预处理慢在图像缩放和格式转换。摄像头来的帧分辨率常是1920x1080而模型输入是640x640每次resize都是整帧操作。可以用448或512的输入尺寸降推理时间红外目标不需要特别精细的纹理448输入对小目标不够友好但会牺牲一些远距离检测能力要在帧率与距离之间找平衡。推理提速最直接的是半精度推理。如果你的显卡支持FP16加载权重后把模型切成半精度model.model.half() model.conf 0.35半精度推理在多数专业卡上能带来40%以上的提速memory占用还能降一半。需要注意红外图像预处理时要把输入数据也转成torch.float16否则类型不匹配直接报错。后处理慢的根源是NMS在CPU上逐框计算。目标密集的红外场景一帧有几十个候选框每帧跑NMS开销可观。不要每秒都重算一次阈值固定住conf和iou参数就能让后处理路径稳定。如果单帧目标实在太多把max_det降到30能压住边缘场景的延迟抖动。4.3 多路视频流下的工程化安排多路红外摄像头是更常见的部署场景。每路一路视频流开一个推理线程GPU会被反复刷成瓶颈因为单张卡的显存和计算单元有限。更好的做法是把多路帧收集到公共队列合并成batch推理import queue import threading frame_queue queue.Queue(maxsize16) batch_size 4 def collect_loop(cap_index): cap cv2.VideoCapture(cap_index) while True: ret, frame cap.read() if ret: frame_queue.put(frame, blockTrue) def infer_loop(): frames_batch [] while True: frame frame_queue.get() frames_batch.append(frame) if len(frames_batch) batch_size: results model(frames_batch, size640) frames_batch []这种写法的核心是让多路采集线程只管读帧推理线程按batch合并后一次跑多张GPU利用率显著提高。代价是延迟增加因为要攒够batch_size才推理对实时性要求高的单路场景不适用。一条经验线是单路摄像头用单帧推理三路以上再考虑batch合并否则延迟带来的体验损失大于吞吐收益。5. 红外车辆检测的常见坑与排查5.1 红外车灯过曝导致检测框跳动现象夜间会车时车灯区域过曝成一大团亮斑检测框在灯和车身之间跳来跳去置信度忽高忽低。原因红外图像中车灯和路面温差大传感器动态范围有限灯周围一片过曝。模型学的目标形状不稳定同一辆车在画面里呈现两种形态。解决先在数据增强阶段对所有训练图做一次灰度对比度裁剪把过曝亮斑压到目标范围内让模型学习车体整体轮廓而不是灯斑。部署端再把model.conf从默认0.25提到0.35到0.40可以明显减少过曝区域的抖动框。如果用的摄像头支持手动增益或AGC关闭尽量固定曝光参数让推理输入分布和训练分布一致。5.2 模型在路面热残留区域产生虚警现象车开走后路面上留下明显的轮胎热痕迹模型持续输出“车辆”框有时连续几十帧不消失。原因胎痕区域温度高形状偏长条形与被检车辆的远处轮廓相似。数据里缺少这类负样本模型把它们当成了正样本。解决从实际场景采集路面残留、热斑区域的图像加入训练集但不要标注成车辆而是作为背景图混入让模型看到更多“车开走之后路面长什么样”的反例。数据处理时也可以做区域随机抠除把温度热点区域从训练样本里随机抹掉一部分降低模型对这类纹理的敏感度。加上这些样本后虚警通常一轮训练就能压下来。5.3 训练loss正常但验证mAP上不去现象训练集loss稳定下降P/R曲线却徘徊在0.8以下尤其在小目标上漏检严重。原因这是红外数据集最常见的“分布矛盾”之一。数据量小目标尺度跨度大小目标只占少量标注框模型整体向中大型目标倾斜。另一种可能是标注框本身偏大把背景框了进去模型学出的目标边界是“车外加一圈”小目标被标注框稀释得更厉害。解决先跑autoanchor重算锚框再看验证集里小目标的占比。如果小目标占比确实低把验证切分改成按尺度分层抽样保证小目标在验证集里有足够数量否则mAP评价本身就失真。同时把输入尺寸从640提到768小目标的特征图响应会强很多——代价是推理帧率下降需要在训练阶段就决定部署尺寸不要训练用640、部署用768两者指标对不上。5.4 推理端帧率上不去、延迟波动明显现象单路摄像头显卡占用率只有40%但帧率只有8到10帧且每隔几秒卡顿一次。原因帧率瓶颈往往不在GPU推理而在CPU上的视频解码、图像resize和画框。红外摄像头输出的YUV格式在CPU上转BGR本身就耗时再加上每帧缩放到640和绘制矩形框大量CPU操作拖慢了整条链路。另一处容易被忽视的是帧读取阻塞cap.read()读完一帧再推理摄像头缓冲和推理跟不上时延迟逐步累积。解决把读取、预处理、推理、绘制拆成两个线程采集线程只负责读帧放进队列推理线程从队列取帧处理。延迟波动一般就消失了。顺手把可视化绘制降到每三帧画一次框或者只输出检测结果不上屏CPU占用立减。5.5 换了一路摄像头后检测效果全面下滑现象A摄像头标定调试正常换到B摄像头后漏检率暴涨框的位置整体偏移。原因两个摄像头的视场角、安装角度、红外波段响应不同。YOLOV5训练数据来自A摄像头模型学到的目标尺度分布和温标特征全部偏向A。B摄像头的画面亮度和对比度分布不同甚至红外图像是反相的模型直接失效。解决这是红外项目最常见的上线事故。先把B摄像头的图像做直方图匹配让亮度分布接近A再看指标能否恢复。如果还不行就把B摄像头的部分帧加入训练集做增量微调。第二次做数据时我就学乖了训练一个红外模型前先确认它能覆盖至少两种不同摄像头参数或在部署流程里加上简单的画面自检跳过亮度分布偏差过大的输入。6. 验证与进阶交付出前最后做的事情6.1 用验证集指标给交付贴标签训练结束后不要只截一张loss曲线图就算交付。用YOLOV5自带的验证命令一次性输出全部评价指标python val.py --data infrared.yaml --weights runs/train/exp/weights/best.pt --img 640 --conf 0.35 --iou 0.45输出会包含mAP0.5、mAP0.5:0.95、precision和recall。红外单类别场景我一般以mAP0.5大于0.85作为交付基准mAP0.5:0.95大于0.55说明框的位置精度也够用。如果后者偏低往往是标注边界不齐或锚框匹配差不要急着改模型先抽几十张验证图把标注框叠上去看贴合度。可视化检查同样重要。把推理结果拼接成视频重点看三个片段远距离小目标有没有连续漏检、夜间车灯过曝时框是否稳定、车停着不动时会不会掉框。连续帧之间的检测稳定性比单帧mAP更能反映部署效果。6.2 导出ONNX做半精度推理把帧率再压一档训练好best.pt之后我会导出为ONNX格式再转成TensorRT或OpenVINO来跑推理。YOLOV5官方脚本一行就能完成导出python export.py --weights runs/train/exp/weights/best.pt --include onnx --half导出后验证一次输出精度没有明显下降再把原来的PyTorch推理换成ONNX Runtime推理。同样的显卡这一套操作通常能把帧率再提50%CPU部署的红外检测系统转成ONNX后配合OpenVINO是性价比最高的加速路径。我的习惯是导出前先用原始PyTorch跑一次验证集导出后再跑一次对比两次mAP差距不超过0.01才继续超过就回退到原始格式排查。这步检测相当于给部署上了一道保险。6.3 交付时顺便把阈值参数埋成配置最后说一个让我少跑好几趟现场的习惯把置信度阈值、NMS阈值、输入尺寸、最大检测数全部提到外部配置项。项目第一阶段调试时阈值写死在代码里方便真到部署环境现场灯光、摄像头角度一变就得重新调这三四个参数。如果它们是配置文件里的键值远程改配置比重新发一版程序简单得多。红外车辆检测的坑大多不在模型结构本身而在数据和部署环境的差异。训练前把数据体检做到位训练时盯着验证集指标而不是loss部署后用线程化和半精度把延迟压住这套流程走下来从拿到红外摄像头到稳定出框一周内就能看到可以交付的版本。希望帮到你。本文还有配套的精品资源点击获取
返回列表