简介:面向红外小目标飞机检测的数据集,定位于计算机视觉、无人机监控与红外遥感场景,适合算法工程师与科研人员训练和评估以air为单一类别的检测模型。压缩包内共包含两千个文件,整体大小约三十七点四兆字节,文件构成包括一百四十七幅BMP红外图像、九百二十六个XML标注文件、九百二十六个TXT标签文件及一个缓存文件;其中XML为VOC格式目标标注,TXT为YOLO格式训练标签,可直接接入常见目标检测框架。数据集已提供train.txt与test.txt的划分,类别仅包含air,按注释将数据路径指向./data/VOC2007/后即可开始训练和验证。目前已有169人学习或下载,对于需要快速获得红外飞机检测标准数据、节省数据采集与标注整理时间的用户,这份资源能够直接用于算法验证与效果对比,降低实验准备成本。整体结构简洁,便于快速理解与使用。
1. 红外小目标飞机检测数据集:一个 train 目录为什么比模型权重更值钱
你刚拿到一份号称“红外小目标飞机检测数据集 train”的资源,解压后里面是几千张灰度图和一排 txt,于是按可见光检测的老套路直接丢给 YOLOv11 训练。三天后 Loss 降得漂亮,验证集 mAP 看着还行,可一到傍晚实拍就漏检成筛子。这个场景我见过太多次,问题往往不在模型,而在你根本没读懂“train”这个目录本身在表达什么约束:红外图的信噪比、飞机所占的像素面积、标注框的真实边界,这些全被数据集的目录结构和文件格式藏起来了。这个标题的核心不是“下载”,而是“怎么把一份红外小目标检测数据集变成一辆训练不出问题的车”。我按目录解析、划分、训练、避坑的顺序,把这套流程讲透。适合正在做红外夜视巡检、无人机反制和电力设施监控的人。
2. 拆解红外小目标飞机检测数据集:目录结构、标注格式与质量评估
一份能直接用来训练的红外小目标飞机检测数据集,通常不只是“一堆图加一堆框”。你拿到手的往往是三个子目录:train、val、test,而真正决定你训练成败的,是每个子目录内部的文件组织方式。常见做法是images和labels平行放置,labels里是 YOLO 格式的 txt,每一行对应一个目标,格式是class x_center y_center width height,四个坐标值都做了归一化。如果你收到的是 XML 或 JSON,第一件事是转成 YOLO 格式,而不是直接喂给 Ultralytics 的接口。
2.1 train 目录下到底该有什么:逐文件核对清单
我一般拿到数据集后不急着训练,先做一次“目录体检”,确认每个目录的职责。典型结构长这样:
. ├── train │ ├── images │ │ ├── seq_001_frame_0001.png │ │ ├── seq_001_frame_0002.png │ │ └── ... │ └── labels │ ├── seq_001_frame_0001.txt │ ├── seq_001_frame_0002.txt │ └── ... ├── val │ ├── images │ └── labels ├── test │ └── images └── dataset.yaml核对的第一个指标是“图文一一对应”:每张images里的图,在labels里必须有同名 txt,即使这张图里没有目标,txt 也应该是空文件而不是缺失。缺失的 txt 在 YOLO 训练时会被当成未标注样本,如果你用背景图做训练,模型会把背景学成负样本,这是合理的;但如果一张有飞机的图漏了 txt,它就成了“标注噪声”,直接把模型带偏。第二个指标是类别编号一致性,dataset.yaml里的names顺序必须和 txt 里第一列的整数严格对应,常见翻车现场是“0 表示飞机,1 表示背景”,结果训练时把所有目标全判成背景。
下面这个 bash 脚本可以快速检查图文配对情况和空标注比例:
#!/bin/bash # 检查 train 目录下图文配对情况 for img in train/images/*.png; do base=$(basename "$img" .png) if [ ! -f "train/labels/${base}.txt" ]; then echo "Missing label: ${base}" fi done # 统计空标注文件比例 total=$(ls train/images/*.png | wc -l) empty=$(find train/labels -name "*.txt" -empty | wc -l) echo "Total images: ${total}, Empty labels: ${empty}"这里不查缺失的图片对应哪些标签,因为训练时 YOLO 只遍历图像文件,缺失标签会被日志警告但不会中断。你可能觉得空标注多没关系,但如果空标注超过总量的一半,模型会倾向于输出“无目标”,这类数据集需要先做难负例挖掘,不能直接训练。
2.2 红外图像与可见光的本质差异:为什么飞机只剩几个像素
可见光检测里,一架飞机在 640x512 的画面里通常占几百个像素,轮廓清晰、纹理丰富。红外小目标场景完全不同:探测距离通常在 5 到 20 公里,飞机在焦平面上只占 3x3 到 9x9 像素,没有形状、没有纹理,只是一个比背景稍亮的斑点。你没法靠“机翼形状”或“机尾轮廓”做识别,只能靠“局部灰度对比度”和“时序一致性”。
所以这类数据集和普通飞机检测数据集有本质区别。普通数据集训练出的模型,学到的是“飞机的形状”;红外小目标数据集训练出的模型,学到的是“一个亮点在局部区域相对背景的显著性”。如果你把 ImageNet 预训练权重直接拿来做迁移学习,第一层卷积核仍然是在找颜色和纹理,而红外图只有一个通道,且目标区域几乎没有纹理,迁移效果会大打折扣。这也是为什么我在做红外小目标检测时,更看重数据集的信噪比分布和局部对比度,而不是只看标注框面积。
2.3 五条质量指标评估数据集:训练前不看会翻车
拿到一份“红外小目标飞机检测数据集”,你至少该在训练前统计这五个指标,它们直接决定你这个模型的上限:
| 指标 | 统计方式 | 对训练的影响 |
|---|---|---|
| 目标有效像素 | 按标注框宽高统计,看最大值/中位数/最小值 | 小目标占比越高,越需要高分辨率输入与切片策略 |
| 信噪比分布 | 统计目标区域与周围背景的灰度差值比噪声方差 | 低信噪比样本少,模型学不到“微弱目标”的判别力 |
| 虚警源密度 | 图中是否存在高温亮斑、热气团、地面热源 | 决定你是否需要加难负例,或引入背景抑制预处理 |
| 标注框一致性 | 边框是否紧贴目标灰度扩散区域 | 标注松一圈,训练时 IoU 计算就会漂移,收敛变慢 |
| 时间连续性 | 同一目标是否多帧连续出现 | 直接影响训练集/验证集划分方式,乱切会导致指标虚高 |
其中“标注框一致性”是我最常发现的隐性坑。有些数据集在标注小目标时,标注员习惯把框画得比真实目标大一圈,因为人眼在放大后难以精确落在 5 个像素内。训练时模型被处处“放松”的框约束,输出的预测框尺寸会偏大,在评测时用固定 IoU 阈值会明显吃亏。统计方法很简单:把标注框的面积分布画成直方图,看是否存在大量异常大的“小目标”。如果中位数像素面积超过20x20,而你的应用场景是5x5级别,那这份数据集更适合做“近距探测”而不是“远距预警”,你得考虑加切片或专门的超分辨率支路。
3. 用 train 目录训练 YOLOv11:划分脚本、增强参数与训练配置
当目录结构确认无误、质量评估心里有数之后,就要把train目录变成真正的训练资产。这里最容易被忽略的是“划分方式”。很多开源数据集的train/val是直接按文件序号随机切的,这在普通目标检测里问题不大,但在红外小目标场景里是致命的——因为同一架飞机在同一条航迹上会连续出现几十帧,如果这些连续帧同时出现在训练集和验证集里,你的验证指标会虚高到离谱,模型实战时立刻现原形。
3.1 从 train 目录生成规范的 test 与 val:按序列切分而非按帧切分
我建议你拿到数据集后重新划分。如果文件名里带seq_001、seq_002这类序列编号,按序列边界划分;如果没有,按时间戳间隔采样划分。下面这个脚本把“帧级随机”改成“序列级随机”:
import os import random from collections import defaultdict random.seed(42) img_dir = "train/images" label_dir = "train/labels" # 按文件名前缀聚合序列帧 seq_frames = defaultdict(list) for fname in os.listdir(img_dir): if not fname.endswith(".png"): continue seq_id = fname.split("_")[0] + "_" + fname.split("_")[1] seq_frames[seq_id].append(fname) # 打乱序列列表 seq_ids = list(seq_frames.keys()) random.shuffle(seq_ids) # 按序列 7:2:1 划分 n_total = len(seq_ids) n_train = int(n_total * 0.7) n_val = int(n_total * 0.2) train_seqs = set(seq_ids[:n_train]) val_seqs = set(seq_ids[n_train:n_train + n_val]) test_seqs = set(seq_ids[n_train + n_val:]) # 写 list 文件 with open("train.txt", "w") as f: for seq in train_seqs: for fname in seq_frames[seq]: f.write(os.path.join(img_dir, fname) + "\n") with open("val.txt", "w") as f: for seq in val_seqs: for fname in seq_frames[seq]: f.write(os.path.join(img_dir, fname) + "\n") with open("test.txt", "w") as f: for seq in test_seqs: for fname in seq_frames[seq]: f.write(os.path.join(img_dir, fname) + "\n")这段代码的关键在seq_id的提取方式:我用下划线切割取前两段,假设文件名形如seq_001_frame_0001.png。如果你的数据集命名规则不一样,需要先看一眼文件名规律再改。这样划分能保证同一目标的时序片段全部落在同一个集合中,验证集才能代表“模型从未见过的航迹”,而不是被记忆过的重复帧。参数上,7:2:1是常见做法,但如果你数据量很少(比如只有 2000 张),建议改成8:1:1,val 集保有 200 张以上,否则单次验证的波动太大,你会分不清改模型是在优化还是过拟合。
3.2 针对小目标的增强参数:mosaic、copy_paste 与 scale 的取舍
YOLOv11 默认的增强参数是为常规目标设计的,小目标场景必须改。重点看四个:mosaic、copy_paste、scale和close_mosaic。
# data_augment.yaml mosaic: 0.5 mixup: 0.2 copy_paste: 0.3 scale: 0.3 translate: 0.1 fliplr: 0.5 close_mosaic: 10mosaic把四张图拼成一张,相当于把四个小目标区域合成到同一个训练样本里。对小目标检测,我建议你把mosaic从默认的1.0降到0.5,因为四张图拼接后每张图的尺寸被压缩到原来的 1/2,5x5 像素的目标缩成 2x2 甚至消失,模型反而学不到有效特征。copy_paste在小目标场景很管用:把标注框内的目标抠出来,随机贴到另一张图的任意位置。对红外小目标来说,目标只是一小片高亮像素,复制粘贴不会产生可见光场景里那种边缘割裂感,所以可以放心用到0.3。scale控制随机缩放的幅度,默认的0.9对可见光大目标没问题,但对 5x5 的红外小目标太暴力,改成0.3让目标尺寸只在原尺寸 70% 到 130% 之间浮动即可。
close_mosaic是训练最后几个 epoch 关闭 mosaic 增强的参数,YOLOv11 默认是 10。这个参数对小目标非常关键:mosaic 生成的拼接图里目标尺寸偏小,如果训练全程都带 mosaic,模型对真实尺寸目标的响应会被拉偏。保留 10 个 epoch 的“清纯”训练,是为了让模型在最后阶段回归到真实分布。
3.3 落到 YOLOv11 的训练配置:yaml 与启动命令
YOLOv11 对红外单通道图和小目标做适配,我一般会改两个地方:数据配置和模型 head 分支。
# infrared_aircraft.yaml path: ./infrared_dataset train: train.txt val: val.txt test: test.txt nc: 1 names: 0: aircraftnc是类别数,这里只有飞机一类。重点是后面训练命令里的imgsz参数。
yolo detect train \ model=yolo11s.pt \ data=infrared_aircraft.yaml \ imgsz=640 \ epochs=100 \ batch=16 \ lr0=0.01 \ cos_lr=True \ optimizer=AdamW \ val=True \ device=0imgsz=640是常见选择,但如果你的目标中位数只有 5x5,640 输入意味着目标只占 8x8 区域,经过 5 次下采样后特征图上只剩一个点。我建议至少试imgsz=960甚至1280,这会让显存占用上升,但你换来的是小目标在特征图上多保留一到两个像素。batch=16在 24G 显存下跑 960 输入比较安全,显存不足就降到 8,同时把workers调到 8 避免 CPU 成为瓶颈。这一套配置下来,就完成了从train目录到可训练数据资产的全部准备。
4. 训练与验收避坑:红外小目标飞机检测的 5 个常见问题
这一章是血泪经验。红外的坑和可见光完全不在一个量级,很多问题你不踩一遍根本想不到它存在。下面五条是我在红外小目标飞机检测上最常见的翻车记录。
4.1 现象:Loss 正常下降但检测全为空,原因:三通道预处理与灰度分布冲突,解决:统一单通道适配
训练日志里 Loss 从 2.0 降到 0.3,看起来一切正常,验证集 mAP 也有 0.8,但一推理就是一张全黑的图,检测框一个没有。这类问题常见于你把红外灰度图直接按三通道方式加载。有些推理脚本会对输入做归一化,mean和std用的是 ImageNet 的[0.485, 0.456, 0.406],红外灰度图的像素分布是单峰且集中在 8000 到 14000(14 位图)或 0 到 255(8 位图),拿可见光的归一化参数套上去,目标与背景的对比度被压缩到几乎为零。解决方式很简单:加载后先转成单通道,按数据集的灰度分布做 min-max 归一化或百分位截断。这一步比调任何模型参数都重要。
4.2 现象:mAP 高但实测漏检,原因:下采样倍率吞掉小目标,解决:提高输入分辨率或用 P2 层
即使你设置了imgsz=960,YOLOv11 的主干网络下采样倍率依然会把 5x5 像素的目标压成一个特征点。Mosaic 增强对小目标有双面性,它在训练时提供了丰富的背景上下文,但推理时又是纯原图,目标尺寸没有变大。我通常先做一次“特征层检查”:把一张已知有飞机的测试图送进模型,在 P3 层(下采样 8 倍)可视化特征响应。如果在 P3 层就已经看不到响应,说明目标在进入 P4/P5 前就丢了,这时需要启用 P2 层输出,或者用切片推理把大图切成小块放大目标。
4.3 现象:把机场跑道热源误检成飞机,原因:训练集缺少难负例,解决:加入背景热源图做负样本挖掘
跑道、机库、地面车辆,这些热源在红外图里和目标一样亮,形状也接近。如果你的数据集中在训练时把这类背景图全部放在test目录里当作“负样本验证”,那模型会堂而皇之地把它们认成飞机。解决方式是挖难负例:从误检样本里截取跑道热源区域,放进训练集,并保留为空标签的图片(即 txt 文件为空),让模型见过“亮但不一定是飞机”的样本。这个负例池最好保持在总样本量的 15% 到 30% 之间。
4.4 现象:验证指标虚高,换场景后性能崩塌,原因:按帧随机划分导致序列帧泄漏,解决:按序列边界重新划分
只有飞机数据集特别容易出现这个问题,因为同一目标连续几十帧的形态和亮度变化很小。如果你在划分train/val时只用random_split,验证集里大量样本与训练集高度相似,模型没学“泛化”,只学了“背答案”。验证指标看起来 0.9 很高,一换机场、换时间段就掉到 0.3。解决方式就是第 3.1 节里的序列级划分,按航迹边界切,保证验证集是模型完全没见过的航线。
4.5 现象:预训练权重迁移后训练发散,原因:灰度图分布与 ImageNet 差异过大,解决:从头训练或仅冻结前两层
YOLOv11 提供的yolo11s.pt是在 ImageNet 上预训练的彩色权重。红外图只有单通道,且分布与自然图像差异巨大,直接迁移时前三层卷积核学到的“颜色纹理”特征不仅没用,还会干扰梯度方向。我一般会做两次实验:第一次从头训练 100 epoch 作为基线,第二次用yolo11s.pt做迁移并冻结前两层。如果从头训练的效果与迁移训练差距在 2 个 mAP 以内,我倾向从头训练,省去预训练权重带来的分布偏移隐患。
5. 提升红外小目标召回率的两个后手:切片检测与可视化验证
当主训练流程稳定后,如果你的红外小目标召回率仍然不够,不要再盲目调超参数。我通常先做两个动作:一是切片检测,二是热力图可视化。前者直接提高小目标的输入分辨率,后者快速定位模型到底在“看”什么。
切片检测的思路,是把原始大图切成若干小块,每个小块独立送入模型推理,最后把检测框合并回原图坐标。这一步对红外小目标尤其有效,因为相当于把 5x5 的目标放大到 20x20 再让模型检测。常见做法是用 SAHI 这类库,但它对大图和切片重叠率有要求。
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type="ultralytics", model_path="runs/detect/train/weights/best.pt", confidence_threshold=0.3, image_size=640, device="cuda:0" ) result = get_sliced_prediction( "test_ir.png", detection_model, slice_height=256, slice_width=256, overlap_height_ratio=0.2, overlap_width_ratio=0.2, postprocess_type="NMS", postprocess_match_metric="IOS", postprocess_match_threshold=0.5, )参数上,slice_height和slice_width用 256 是为了让目标在切片里占据足够比例。overlap_height_ratio和overlap_width_ratio设为 0.2,保证跨切片的同一目标不会被切成两半后重复检测。后处理用 NMS 加IOS(交集除以较小面积)匹配,因为小目标的框面积小,普通的 IoU 阈值对轻微偏移过于敏感。
切片尺寸的选择有讲究。切得越小,目标放大倍数越高,但推理时间线性增加,而且切片越多,边界被切断的目标也越多。我一般看目标中位像素尺寸:中位 5x5 就切 256,中位 10x10 就切 512,这是一个够用的经验值,不必追求极致放大。
热力图验证则是用 Grad-CAM 对你选定的内部层生成响应图,叠加在原图上。如果响应集中在飞机的高亮像素周围,说明模型学到的是目标本身的红外特征;如果响应集中在整片亮区或背景边缘,说明模型只是在“亮度大就判为飞机”。这个可视化步骤我建议写进每个版本的验证环节,它能比 mAP 更快暴露模型学到了什么假特征。
我自己的习惯是每训完一个版本,固定抽取十张代表性图片做切片检测和热力图验证,再决定要不要回灌难例,而不是只盯着验证集 Loss。红外小目标飞机检测数据集的“train”目录,看着只是几千张图,但它背后是信噪比、目标尺寸、虚警来源、划分方式这一整条链路。把这些链路理顺,模型才能真正在夜航的实际场景里扛住。希望帮到你。
本文还有配套的精品资源,点击获取