简介红外多目标检测数据集聚焦真实拍摄环境下的目标检测面向计算机视觉初学者与算法开发者图像场景丰富、标注框质量高。包内提供VOC、COCO、YOLO三种格式标签分别存放于独立文件夹可直接接入常见YOLO系列框架进行模型训练。压缩包共2000个文件以XML标注文件与TXT标签/配置为主同时包含若干HTML图文教程、Python数据划分脚本及YAML配置文件整体大小约96兆字节。资料还附带训练集、验证集、测试集划分脚本可按需灵活切分数据并提供Linux与Windows双平台下的YOLO环境搭建和训练案例教程用户可据此完成从数据准备、环境配置到模型训练的完整流程。目前已有456人学习浏览适合需要规范化红外数据并快速上手YOLO迁移训练的入门及进阶开发者。1. 为什么说YOLO红外多目标检测数据集是夜视项目的第一道坎前年做一个夜间厂区巡检项目白天可见光相机看得清清楚楚一到晚上红外相机开启人形目标在画面里就是一团灰白色亮斑YOLO模型练了半天mAP始终在0.5上下打转最后排查下来发现问题不在模型而在数据红外图像公开数据集本来就少标注格式又各说各话。标题里这个压缩包解决的就是这个最磨人的环节——1000张红外多目标检测图片配齐VOC、COCO和YOLO三种标签格式还把划分脚本和训练教程一并给齐。不管你是做夜视安防、夜间辅助驾驶还是搜救无人机都能在这份数据上把YOLO从数据准备到训练评估整套链路跑通老手则可以跳过格式转换和踩坑直接进调参阶段。2. 三种标签格式的转换链路VOC、COCO、YOLO格式差异与互转脚本2.1 为什么红外数据集要同时给VOC、COCO和YOLO三种格式我拿到这种打包好的数据集第一件事不是急着训练而是先确认标签目录结构。常见做法是三个独立目录分别放VOC的XML、COCO的JSON和YOLO的TXT方便不同下游工具直接读取。Pascal VOC格式是一张图对应一个同名XML文件标注对象被包在object节点里位置坐标是bndbox下的xmin、ymin、xmax、ymax四个绝对像素值。优点是单文件可读性强labelimg标注后默认导出的就是这种结构适合人工排查和可视化调试缺点是每帧图都要生成一个XML文件零散批量统计和跨数据集合并都不方便。COCO格式则把所有标注装进一个JSON文件内部按images、annotations、categories三个数组组织每个annotation除了bbox还经常带上segmentation多边形所以它不只是目标检测的事实标准也是实例分割任务的常见入口。红外场景以后如果要升级做实例分割在这份JSON上扩展比从VOC补省事很多。YOLO格式最精简每张图对应一个同名的txt文本每一行是class x_center y_center width height四个数值全部归一化到0~1区间。Ultralytics YOLO训练时读取的就是这种格式速度快、占用小缺点是不直观框到底准不准必须画到图上才看得见。维度VOCPascal VOCCOCOYOLO文件组织一张图一个XML整个数据集一个JSON一张图一个TXT坐标形式左上角右下角绝对像素左上角x,y宽高绝对像素中心点宽高归一化额外能力object级别属性扩展自带segmentation支持实例分割最省空间训练读取最快典型用途labelimg标注、人工核查跨数据集评估、实例分割Ultralytics YOLO训练这也解释了为什么要把三种标签都配齐想快速跑YOLO训练用txt目录想先人工看标注质量用XML想把红外数据和别的公开数据集合并做大训练用JSON做统一入口。数据集的价值一半在图片另一半在这套格式覆盖里。2.2 用Python脚本把VOC标签转成YOLO格式我一般优先把VOC转成YOLO因为XML里坐标最接近人的直觉错了也容易查。下面这个脚本读XML把bndbox的四角坐标换算成归一化中心点坐标。import os import xml.etree.ElementTree as ET # 类别名到ID的映射顺序必须和后续训练用的data.yaml保持一致 CLASS_MAP {person: 0, car: 1, dog: 2} def voc_to_yolo(xml_path, out_dir, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.findtext(name) if name not in CLASS_MAP: # 不在映射表里的类别直接跳过避免把脏数据带进训练集 continue bndbox obj.find(bndbox) xmin float(bndbox.findtext(xmin)) ymin float(bndbox.findtext(ymin)) xmax float(bndbox.findtext(xmax)) ymax float(bndbox.findtext(ymax)) # 绝对坐标转归一化坐标中心点除以图像宽高宽高也除以图像宽高 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) out_path os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines))这段代码的核心逻辑有两个一是ET解析出的字符串全部转成float再做运算避免精度丢失二是输出保留六个小数位就够YOLO读取太多反而让文件变大。img_width和img_height必须和实际图像尺寸严格一致常见翻车是手填1920图像实际3840所有框整体偏移一半正确做法是先用PIL或cv2读一次图片宽高再传进来。调用时外层用glob遍历XML目录逐个执行即可转换完随抽几张图把txt解析成框画回去对比眼见为实。2.3 COCO转YOLO的坐标换算与三个边界坑COCO转YOLO比VOC多一个步骤因为COCO的bbox字段是[x, y, width, height]x和y是左上角坐标不是中心点。换算时先算出中心点再归一化。# COCO annotation: bbox [x, y, width, height] 左上角绝对像素 x_center (bbox[0] bbox[2] / 2.0) / img_width y_center (bbox[1] bbox[3] / 2.0) / img_height width bbox[2] / img_width height bbox[3] / img_height这段代码没有额外依赖直接从字典取bbox字段就能完成换算。转换中最容易翻车的是三类边界情况。第一类是标注框跨出图像边界。红外目标经常出现在画面边缘标注时框子可能比图像还大一圈xmax大于img_width转换后得到大于1的归一化值训练时会被YOLO直接丢弃表现为这个目标永远学不到。处理办法是转换前做clamp把上下限压回图像范围内。第二类是空XML或空annotation。一张图确实没有目标应该生成一个空txt文件而不是报错跳过否则图片和标签配对数量对不上。第三类是类别名漂移。比如同一批数据里有人标person有人标people或者红外场景里person和pedestrian混用转换前先用脚本统计一遍全部类别名别让漏网的名字在训练时被静默跳过。COCO转VOC则反过来从JSON里取segmentation或bbox直接写绝对坐标进XML不常做但要在labelimg里二次修正标注时就得用上。3. 1000张红外图怎么划分把划分脚本改成可复用的训练集/验证集/测试集工具3.1 为什么不能直接按8:1:1随机划分连续帧泄漏是真问题很多人拿到1000张红外图第一反应是random.shuffle然后按比例切结果训练出来mAP很高一到真实场景就崩。原因大概率不在模型在划分方式。红外数据集大部分是从监控视频或无人机航拍里抽帧来的同一段视频里相邻帧背景基本不变目标只移动几十个像素如果把所有帧混在一起随机划分验证集里会混进大量和训练集几乎重复的亲戚帧。这种泄漏对指标影响非常大验证集mAP可能虚高5到10个百分点而且完全看不出来。我习惯把这种问题叫亲戚泄漏。解决方法是先按视频片段或场景ID分组再在组内部做随机划分保证来自同一场景的帧只落在一个集合里。如果压缩包里的划分脚本只有全局shuffle建议拿到手后先改成按场景分组。3.2 一个按场景分组的划分脚本同步搬运图片和三种标签下面这个脚本按场景分组做8:1:1划分同时搬运图片和三种格式标签固定随机种子保证两次运行结果完全一致。import random import shutil from pathlib import Path random.seed(42) # 固定随机种子保证可复现 data_root Path(红外数据集) images_root data_root / images # 假设原始图片都在这 train_root data_root / split / train val_root data_root / split / val test_root data_root / split / test # 先按文件名前缀把图片分组例如 record_00123.jpg 的场景ID是 record # 如果你的文件名没有前缀改成按子目录分组思路一样 scenes {} for img in images_root.glob(*.jpg): # 按实际扩展名改成 *.png 或 *.jpeg scene_id img.stem.split(_)[0] scenes.setdefault(scene_id, []).append(img) # 每个场景组内部打乱后切分场景之间不交叉 train_files, val_files, test_files [], [], [] for imgs in scenes.values(): random.shuffle(imgs) n len(imgs) train_files imgs[:int(n * 0.8)] val_files imgs[int(n * 0.8):int(n * 0.9)] test_files imgs[int(n * 0.9):] # 一个通用拷贝函数图片和对应标签都拷进 split 目录 def copy_split(files, split_root): for img in files: dst_img split_root / images / img.name dst_img.parent.mkdir(parentsTrue, exist_okTrue) shutil.copy(img, dst_img) # 三种标签格式xml、json、txt 后缀不同分别处理 for suffix in [xml, json, txt]: label_src img.parent.parent / (labels_ suffix) / (img.stem . suffix) if label_src.exists(): dst_label split_root / (labels_ suffix) / (img.stem . suffix) dst_label.parent.mkdir(parentsTrue, exist_okTrue) shutil.copy(label_src, dst_label) copy_split(train_files, train_root) copy_split(val_files, val_root) copy_split(test_files, test_root)这里的关键逻辑是把全局打乱改成先按场景分组再组内打乱代价是每个场景的边界帧采样比例可能略有浮动但换来的验证集更真实。标签文件这边要注意三种格式后缀不同XML和TXT是一图一文件直接按stem找COCO是单个JSON文件没法按图名一对一连过去划分后要另写一段过滤代码从原JSON里抽出对应图片的images和annotations重新生成一个split级的JSON。3.3 划分后自检三件事配对关系、类别分布、坐标尺寸划分完不要急着训练先跑一个自检确认图片、标签、类别分布都正常。自检脚本的核心逻辑是拿文件名集合做差集。def verify(data_root): img_dir data_root / images label_dir data_root / labels_txt imgs set(p.stem for p in img_dir.glob(*.jpg)) labels set(p.stem for p in label_dir.glob(*.txt)) print(缺标注的图片:, imgs - labels) print(没有图片的标签:, labels - imgs) print(图片总数:, len(imgs), 标签总数:, len(labels))差集为空说明配对基本成功。第三件必须检查的是类别分布红外多目标场景里常见类别数量差好几倍划分后要统计每个集合的类别出现次数如果验证集里某个类别一个样本都没有那这个类别的AP直接归零总mAP会把缺陷掩盖掉。COCO格式的二次过滤也要单独验证核心逻辑是先取验证集图片的file_name集合再按image_id过滤annotations。import json with open(labels_coco/annotations.json) as f: coco json.load(f) val_stems {img.stem for img in val_files} val_ids {img[id] for img in coco[images] if img[file_name] in val_stems} val_anns [ann for ann in coco[annotations] if ann[image_id] in val_ids] val_images [img for img in coco[images] if img[file_name] in val_stems] json.dump( {images: val_images, annotations: val_anns, categories: coco[categories]}, open(val_coco.json, w) )这段代码不复杂但要注意val_stems来自前面划分脚本已经确定的val集合必须用同一个变量不要重新读目录否则又引入随机性。最后再抽几张图画框对比确认转换过程中没有坐标漂移就可以进训练了。4. 用这个数据集跑通YOLO训练从环境搭建到调参落地4.1 PyCharm里搭YOLO环境最小安装与一条命令验证环境搭建是入门者最容易卡壳的环节。我的常规做法是用PyCharm配Conda环境先把Python版本固定到3.9或3.10然后装Ultralytics和PyTorch。conda create -n yolo python3.9 -y conda activate yolo pip install ultralytics torch torchvision yolo predict modelyolov8n.pt source红外测试图.jpg这条命令的含义是创建独立的yolo环境避免污染系统Python装Ultralytics的同时装torch和torchvision其中torch默认装CPU版本如果你的机器有NVIDIA显卡建议单独用pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118装CUDA版训练速度能差好几倍。装完在PyCharm的Settings里把解释器切到Conda环境的python路径通常是envs/yolo/bin/python。最后一条yolo predict用yolov8n.pt预训练权重跑一张红外图验证整条链路通不通。压缩包里的训练教程如果版本偏老注意Ultralytics的API在8.0前后有差异优先以代码里的实际写法为准。4.2 写数据集yamlnames顺序必须和标签ID严格一致数据集准备完训练前先写data.yaml。路径配置和names列表是两个最容易错的地方。# 红外多目标检测数据集配置 path: /home/user/红外数据集/split train: images/train val: images/val test: images/test names: 0: person 1: car 2: dogpath是split目录的绝对路径train、val、test相对path做拼接names字典的索引必须和标签txt第一列的类别ID一一对应第2章转换脚本里的CLASS_MAP顺序如果和这里不一致就会出现person被当成car训练这种严重错位。另外Ultralytics读取yaml时对中文注释兼容性不好建议配置文件里只用英文注释别给自己埋坑。4.3 训练超参数怎么按红外小目标调imgsz、epochs和损失函数红外图像和可见光图像的特征差异明显目标通常是低对比度亮斑、边缘模糊、尺寸偏小背景以大面积暗区为主。这些特性决定训练参数不能直接照抄可见光项目的默认值。最常见的是imgsz和epochs。1000张图属于中小规模数据默认300 epoch很容易在150轮附近过拟合建议epochs设200并打开早停imgsz更关键红外小目标在640分辨率下常常只剩十几个像素条件允许直接imgsz1280训练显存翻倍跑不动就先640。损失函数方面默认的CIoU Loss对可见光大目标表现稳定但红外小目标的边界框回归容易在小像素误差上卡住切图训练或提升输入分辨率比频繁换损失函数见效更快。参数红外小目标建议显存紧张时的备选imgsz1280或640切图640epochs200 patience20150 patience15batch16~328modelyolov8nyolov8nlr00.010.005训练命令如下yolo detect train data红外数据.yaml modelyolov8n.pt \ epochs200 imgsz640 batch16 patience20 device0model选择yolov8n而不是yolov8m/x是考虑1000张图数据量不大模型容量过大会直接放大过拟合。batch大小以显存能塞下为准24G显存可以上32。patience20表示验证指标连续20轮不涨就提前结束避免无效训练。4.4 训练日志盯什么loss曲线、mAP和漏检方向训练过程中每跑完一个epoch终端会打印box_loss、cls_loss、dfl_loss以及mAP50、mAP50-95。我的观察顺序是先看train loss和val loss是否同步下降train还在降但val开始横盘就是过拟合信号再看mAP50和mAP50-95的差值差值大到0.4以上说明框定位不准这个现象在红外小目标上特别明显。不要只盯着mAP50它只反映检测率不反映框质量。训练结束后打开runs/detect/train下的results.png看曲线再单独拿验证集里几张远距离红外图跑predict确认漏检方向。val_batch*.jpg这个文件也值得看它会画出模型在验证图上的预测框比数字直观得多。5. 红外多目标数据集训练避坑五条血泪经验按现象→原因→解决5.1 标签框整体左上偏图像缩放后XML没有跟着改现象训练loss下降得很快但画出来的框全部偏向左上角右下角总是差一截。原因XML里的bndbox还是原始大图的绝对坐标转换脚本传入的img_width和img_height却写成了缩放后的小图尺寸坐标换算被整体压缩。解决转换前先读一遍图像实际尺寸不要手填。简单做法是拿PIL或cv2读取每张图的宽高再传给转换函数一劳永逸。检查时可以选一张图把原图和txt框叠在一起看手工改脚本的十有八九都是这个问题。5.2 远距离目标全漏红外小目标只有十几个像素现象近距离目标检测正常50米外的行人完全没框mAP50有0.85但mAP50-95只有0.3。原因目标在640分辨率下面积太小缩放后只剩10到20个像素特征基本被背景淹没。解决把imgsz提升到1280同时对这类小目标单独统计像素面积分布。如果1280训练成本太高就做切图训练把大图切成小块再推理本质是让模型在小图上看到更大比例的目标。红外场景里小目标是常态不是异常要从数据准备阶段就留出这部分图。5.3 车检出来了人不见了类别严重失衡现象训练日志里car的AP接近0.9person只有0.2而训练集里car的样本数是person的8倍。原因模型把大部分学习容量分配给样本多的类别低频类别被压制。解决划分时先确认每个集合里各类别比例接近然后对低频类别做过采样或者给cls_loss加类别权重。红外监控场景里最常见的失衡是车辆目标远多于行人如果业务上人更重要就必须在数据层面做干预光靠训练参数拉不回来。统计类别分布可以在划分脚本里顺手输出别等训练完才看。5.4 验证集先降后升过拟合比预期来得快现象val_loss在第120轮开始抬头train_loss还在继续降mAP50涨不动。原因1000张图对YOLO属于小数据集模型容量太大训练后期在背训练集细节而不是学通用特征。解决换yolov8n小模型epochs控制在200以内早停patience开到15到20增强项里把HSV和翻转适度打开让有限的图被用得更充分。如果增强开太猛红外图的暗部细节会被噪声干扰增强强度要看着loss曲线微调。5.5 直接加载可见光预训练权重红外迁移效果很差现象用yolov8n.pt做预训练权重前50轮loss波动大收敛速度比随机初始化还慢。原因可见光三通道特征和红外单通道热像分布差异太大预训练权重的底层特征冗余甚至干扰。解决改成随机初始化从头训练或者保留预训练权重但把预热epoch拉长让模型在前期把底层特征逐步覆盖掉。红外项目上后者更常用因为预训练权重至少能提供目标检测的骨架先验关键是给它足够时间去适应红外域。我的习惯是epochs少于50就别用预训练权重烧不起这个预热成本。6. 训练完怎么验证用混淆矩阵与热力图给红外模型做复盘6.1 先看混淆矩阵再看PR曲线训练结束后的runs/detect/train目录下会生成confusion_matrix.png我每次第一眼都看它。主对角线清晰之外还要看person被误检成car这类跨类别错误以及最左侧一列background的误检率。红外背景下大块亮斑很容易被当成目标background FP如果超过10%说明模型对热源噪点没有判别力这种问题mAP看不出来但混淆矩阵一眼就暴露。6.2 用热力图确认模型有没有真正聚焦目标模型对单张图输出置信度的同时可以用Grad-CAM类可视化工具看特征激活区域。红外小目标场景里常见的问题是模型被大片明亮背景激活目标本身反而没有响应这时把imgsz提升到1280或做切图训练是值得的一步。从基于目标框的验证走到基于激活区域的验证才算完整复盘。6.3 部署验证导出ONNX并在AGX Orin上跑通训练收敛后我习惯把模型导出成ONNX做一次部署验证尤其是红外项目大多要落到边缘设备上。yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640导出时imgsz必须和训练时的推理尺寸一致否则输入尺寸变了输出坐标会整体偏移。想上AGX Orin这类边缘设备的话下一步把ONNX转成TensorRT再做INT8量化红外场景可以在低功耗下拿到接近实时的推理速度。导出后顺手用onnxruntime跑一张红外测试图确认输出shape和张量数值正常再交给部署组能省掉一整轮来回。我自己的习惯是训练完第一件事不是看mAP而是拿验证集里最难的几张远距离红外图跑一次预测看框有没有落在亮斑中心上。很多时候指标会骗人可视化不会。把划分脚本、格式转换和一次完整的训练跑通之后这个数据集的真正价值才会体现出来希望帮到你。本文还有配套的精品资源点击获取