简介:基于无人机航拍视角构建的HIT-UAV红外小目标数据集,面向计算机视觉目标检测与无人机遥感研究群体,可用于红外小目标识别算法的训练和评测。数据集包含从43470帧中提取的2898幅红外热图像,样本来自学校、停车场、道路、游乐场等不同场景,覆盖人、自行车、汽车及其他车辆目标,并提供60至130米飞行高度、30至90度相机视角及白天/夜晚光照差异等元数据,有助于评估模型在多变环境下的泛化能力。压缩包共2002个文件,大小约192.88MB,主要包括1994个XML标注文件、5个TXT划分文件和1个voc2yolo.py转换脚本;XML文件保存目标框与类别标注,TXT文件提供训练、验证、测试集划分,脚本可帮助将VOC格式转为YOLO格式,便于直接接入主流检测框架。已有1511人浏览学习,适合计算机视觉、无人机遥感方向的初学者与研究者开展实验;凭借多高度、多视角、多光照条件的真实航拍数据,可为红外弱小目标检测及相关算法验证提供扎实的样本基础。
1. 项目概述:HIT-UAV到底是什么,能解决什么问题
做计算机视觉的朋友,尤其是搞目标检测的,对可见光数据集肯定不陌生。COCO、VOC、ImageNet,翻来覆去就是那些,检测白天光线良好的场景确实够用。但只要你碰过红外图像、或者接触过无人机视角的项目,马上会发现一个尴尬的问题:公开数据集太少了。HIT-UAV红外小目标数据集,就是专门填补这个空档的。
简单来说,这是一个由哈尔滨工业大学团队构建并开源的无人机视角红外热成像目标检测数据集。它从无人机实际飞行拍摄的红外视频中抽帧,做了精细的标注,覆盖多个场景、多个时段、多个飞行高度。我经常在社区里看到有人问“有没有针对无人机红外场景的目标检测数据集”,HIT-UAV基本是绕不开的一个答案。
这套数据能做什么?核心就是三类任务:红外小目标检测、无人机视角目标识别、以及红外视频目标跟踪。适合谁来用?如果你在跑YOLO、MMDetection这类框架,或者研究小目标检测算法,又或者做电力巡检、应急救援、安防监控这类涉及无人机热成像的应用,那这个数据集非常值得入手。
我第一次用它的感受是:良心的不止是数据本身,还有那套标准VOC格式的XML标注。不用像某些数据集那样自己写半天的解析脚本,下载下来稍微处理一下,就能直接丢进训练管线。这一点,接下来展开聊。
2. 数据集的“里子”与“面子”:规模、标注与场景构成
2.1 图像与标注的基础指标
先列一组硬指标,方便你快速评估这套数据是否符合你的任务需求。HIT-UAV数据集包含43,470帧热红外图像,每帧图像分辨率统一为384×512,是标准的8位灰度热像图,以视频帧序列的形式组织。全部图像中标注了超过11万个目标实例,其中核心类别是person(行人)和car(车辆)两类。所有标注以VOC格式的XML文件随图像一同存放。
这里需要提醒一点:384×512的分辨率不算高。但这恰恰是红外成像设备和机载平台的常态,不是数据集的缺陷,而是这类任务的天然约束。你如果之前习惯在640×640或者更大的输入上训练,换成这个数据集后,目标在图像中的像素占比会明显变小,很多在COCO上表现优异的模型到这里可能会“水土不服”,这也是它的价值所在——更贴近真实部署场景。
2.2 场景与拍摄条件的设计逻辑
我仔细翻过数据集的图像序列,发现它的采集覆盖了多种典型环境。建筑物密集的城市街区、开阔道路、停车场、绿化带等场景都有收录;拍摄时段上,包含了白天、黄昏、夜间等多种光照条件。飞行高度从相对较低的几十米到上百米不等,相机拍摄角度也不同。部分图像还引入了遮挡、多目标重叠等复杂情况。
这种设计不是随便凑数的。红外目标检测的一个核心难点就是目标与背景的热对比度波动,比如白天太阳照射的屋顶、路面,温度高,红外图像中呈现亮斑,很容易和小目标混淆;而到了夜间,场景整体温度下降,目标反而更突出。如果一个数据集只在单一场景或单一时段采集,模型学到的根本不是“目标是什么”,而是“这个特定场景下的热模式是什么”,换到真实环境就废了。HIT-UAV的多场景覆盖,至少能给模型的泛化能力提供基本的训练素材。
2.3 红外模态与小目标的耦合关系
“红外小目标”这几个字,拆开看是三个词的组合,合起来却是一个极其特殊的问题域。
- 红外:意味着只有热辐射信息,没有颜色、没有纹理(至少纹理信息极其微弱)。传统RGB检测里非常依赖的颜色特征,在这里完全失效。
- 小目标:按照通用定义,小目标指像素尺寸小于32×32的目标,占比小于整幅图像的0.12%。在384×512的图像里,一个行人可能只有十几个到几十个像素,远小于这个阈值,几乎就是一个点。
- 无人机视角:意味着视角变化剧烈、目标尺度变化快、背景复杂且运动,还伴随平台抖动带来的模糊。
三个约束叠加,导致HIT-UAV上的检测难度远高于一般的可见光行人检测任务。很多在COCO上mAP超过50的模型,在这套数据上可能只能跑出十几二十个点。这不是模型不行,而是任务本身的难度摆在这里。换句话说,如果你能在HIT-UAV上取得不错的成绩,说明你的模型对“小目标+红外+运动平台”这个组合确实有了鲁棒的理解,这比刷榜COCO更有实际参考意义。
3. 从下载到能训练的全流程:目录结构与标注解析
3.1 获取与目录结构
数据集可以从其项目主页或公开的学术资源平台获取。下载解压后,目录结构大致如下:
HIT-UAV/ ├── infrared_images/ │ ├── seq1/ │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ ├── seq2/ │ │ └── ... │ └── ... ├── annotations/ │ ├── seq1/ │ │ ├── 000001.xml │ │ ├── 000002.xml │ │ └── ... │ ├── seq2/ │ │ └── ... │ └── ... └── README.txt图像序列按拍摄片段分组存放,标注文件与图像文件名一一对应。建议你解压后先把目录结构原样保留,不要自作主张移动到别的嵌套层级,后面写数据加载脚本时能省不少事。
3.2 XML标注内容解读
随便打开一个XML,你会看到典型的VOC风格标注:
<annotation> <folder>seq1</folder> <filename>000001.jpg</filename> <size> <width>512</width> <height>384</height> <depth>1</depth> </size> <object> <name>person</name> <bndbox> <xmin>213</xmin> <ymin>174</ymin> <xmax>229</xmax> <ymax>191</ymax> </bndbox> </object> <object> <name>car</name> <bndbox> <xmin>96</xmin> <ymin>240</ymin> <xmax>151</xmax> <ymax>285</ymax> </bndbox> </object> </annotation>注意三点。第一,width和height与图像的实际尺寸对应,depth为1表示单通道灰度图。第二,同一个文件里可以包含多个<object>,对应一张图中的多个目标。第三,坐标是绝对像素坐标,左上角为原点,这一点在后续转换成YOLO格式时务必注意方向。
3.3 XML解析脚本(Python)
如果你打算用PyTorch、MMDetection这类框架,可以用现成的VOC加载器;但如果你想做自定义逻辑或者转成其他格式,写个通用解析脚本更灵活。我用的解析脚本大致是:
import os import xml.etree.ElementTree as ET def parse_voc_xml(xml_path): tree = ET.parse(xml_path) root = tree.getroot() img_info = { "filename": root.find("filename").text, "width": int(root.find("size/width").text), "height": int(root.find("size/height").text), "objects": [] } for obj in root.iter("object"): name = obj.find("name").text bbox = obj.find("bndbox") xmin = int(bbox.find("xmin").text) ymin = int(bbox.find("ymin").text) xmax = int(bbox.find("xmax").text) ymax = int(bbox.find("ymax").text) img_info["objects"].append({ "name": name, "bbox": [xmin, ymin, xmax, ymax] }) return img_info # 使用示例 info = parse_voc_xml("annotations/seq1/000001.xml") print(info)这个脚本返回一个字典,包含了文件名、图像尺寸和目标框列表。你可以在它的基础上封装成一个Dataset类,加载全部图像的标注信息,也可以在读取某张图时实时解析对应的XML。前者适合数据量不大的场景(HIT-UAV全量也才4万多张,一次性加载标注完全不吃力),后者适合内存受限或需要流式读取的场景。
3.4 类别处理与统计
HIT-UAV的标注类别是person和car,但我的建议是:先做一次全量统计再动手训练。有些类别的目标框极小,甚至只有3×4像素,这种标注在常规NMS和loss计算里几乎起不到正向作用。用下面的脚本统计一下每个类别的目标数量、框尺寸分布:
import os import numpy as np def collect_statistics(annotation_dir): categories = {} all_boxes = [] for xml_name in os.listdir(annotation_dir): if not xml_name.endswith(".xml"): continue info = parse_voc_xml(os.path.join(annotation_dir, xml_name)) for obj in info["objects"]: name = obj["name"] categories[name] = categories.get(name, 0) + 1 x1, y1, x2, y2 = obj["bbox"] w = x2 - x1 h = y2 - y1 all_boxes.append([name, w, h]) return categories, np.array(all_boxes) cats, boxes = collect_statistics("annotations") print(cats) print("平均框宽高:", boxes[:, 1].astype(float).mean(), boxes[:, 2].astype(float).mean())我统计的结果是,person类别的目标数量远多于car,且两类目标的平均框尺寸都不大,集中在几十个像素的范围内。这个分布特点意味着你训练时不能简单套用COCO预训练权重后直接开训,很可能需要针对小目标做特殊处理,比如调整anchor尺寸、增大输入分辨率、或者使用多尺度训练。
4. 标签格式转换实操:从VOC到YOLO系
4.1 为什么要转格式
HIT-UAV官方提供的是VOC格式(XML),但社区里大量工具链是围绕YOLO格式设计的。Ultralytics YOLOv8、YOLOv5这些框架原生支持的数据格式是images+labels两个目录,每张图对应一个.txt文件,每行记录:
class_id x_center y_center width height其中坐标是归一化到0~1的浮点数,宽高也是相对图像尺寸的归一化值。VOC的绝对像素坐标必须经过换算才能用。这一步看起来简单,但翻车概率极高,主要是不注意归一化和类别编号映射。
4.2 转换代码与目录构建
以按序列划分训练集和验证集为例,完整转换流程如下:
import os import random import xml.etree.ElementTree as ET from shutil import copy2 def voc_to_yolo(xml_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in class_names: continue class_id = class_names.index(name) bbox = obj.find("bndbox") x1 = float(bbox.find("xmin").text) y1 = float(bbox.find("ymin").text) x2 = float(bbox.find("xmax").text) y2 = float(bbox.find("ymax").text) x_center = (x1 + x2) / 2 / img_w y_center = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return "\n".join(lines) class_names = ["person", "car"] src_img_dir = "infrared_images" src_ann_dir = "annotations" seqs = os.listdir(src_ann_dir) random.seed(42) train_seqs = random.sample(seqs, int(len(seqs) * 0.8)) val_seqs = [s for s in seqs if s not in train_seqs] for split, seq_list in [("train", train_seqs), ("val", val_seqs)]: os.makedirs(f"yolo_dataset/images/{split}", exist_ok=True) os.makedirs(f"yolo_dataset/labels/{split}", exist_ok=True) for seq in seq_list: ann_dir = os.path.join(src_ann_dir, seq) img_dir = os.path.join(src_img_dir, seq) for xml_name in os.listdir(ann_dir): if not xml_name.endswith(".xml"): continue xml_path = os.path.join(ann_dir, xml_name) img_name = xml_name.replace(".xml", ".jpg") img_path = os.path.join(img_dir, img_name) if not os.path.exists(img_path): continue yolo_content = voc_to_yolo(xml_path, class_names) label_path = os.path.join(f"yolo_dataset/labels/{split}", xml_name.replace(".xml", ".txt")) with open(label_path, "w") as f: f.write(yolo_content) copy2(img_path, os.path.join(f"yolo_dataset/images/{split}", img_name))有几个细节值得强调。
- 我按“序列”而非“单张图片”进行划分,这是刻意为之。同一段视频的相邻帧高度相似,如果随机打乱划分,训练集和验证集会互相“瞟见”几乎相同的图像,模型评估出来的是假的泛化能力。按序列划分,至少能保证验证集的场景是模型没见过的。
class_names的顺序就是训练时的类别编号顺序,保持在所有转换脚本和训练配置中一致,否则会出现“标签错乱但边界框全对”的诡异问题。- 坐标全部保留6位小数。对于384×512这种小尺寸图像,6位小数足够精确,再长只会增大文件体积。
4.3 转换结果自检
转换完后,别急着开训,先做一次自检。随机挑几张图,写个可视化脚本,把边界框画回去看一眼:
import cv2 def visualize_yolo(img_path, label_path, class_names): img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) h, w = img.shape with open(label_path) as f: for line in f.readlines(): parts = line.strip().split() cls_id = int(parts[0]) x_c, y_c, bw, bh = map(float, parts[1:]) x1 = int((x_c - bw / 2) * w) y1 = int((y_c - bh / 2) * h) x2 = int((x_c + bw / 2) * w) y2 = int((y_c + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), 255, 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, 255, 1) cv2.imshow("check", img) cv2.waitKey(0)这一步非常关键。我见过不少人在转换时把x和y搞反、或者类别编号错位,导致训练出来的模型完全乱套,却始终找不到原因。可视化自检能在训练前就把这类低级错误一次性堵死。
5. YOLOv8训练HIT-UAV的完整配置与心得
5.1 数据集配置文件
如果你使用Ultralytics YOLOv8,需要一个数据配置文件,内容是:
path: D:/datasets/hituav_yolo train: images/train val: images/val nc: 2 names: ["person", "car"]path字段指向数据集根目录,train和val是相对该目录的路径。注意YOLO系框架没有单独的test配置项,一般直接用val评估即可。nc必须和names列表长度一致,这是个老坑,少写一个类别,训练时就会报IndexError或者静默忽略某个类。
5.2 训练参数与模型选择
HIT-UAV的图像分辨率是384×512,属于偏小的输入,但小目标居多。我的做法是:
yolo detect train \ data=hituav.yaml \ model=yolov8s.pt \ imgsz=640 \ epochs=200 \ batch=16 \ patience=20 \ device=0 \ project=hituav_experiments \ name=exp_v8s为什么输入尺寸用640而不是继续用384?虽然原始图像是384×512,但在训练时缩放到640并不会显著损失信息,反而能让小目标在特征图上占据更多像素,有利于检测。代价是显存占用上升、训练速度下降,因此模型我选了yolov8s而不是更大的yolov8l或yolov8x,在平衡实测下来效果和资源占用比较合理。
patience=20的意思是连续20个epoch验证集指标没提升就提前停止。HIT-UAV的收敛曲线通常比较“钝”,前50个epoch提升明显,后期会进入平台期,提前停止能帮你省下大量时间。
5.3 训练过程中的几个观察
训练时我会开启Ultralytics的日志可视化(默认WandB或TensorBoard),重点盯下面几个指标:
train/box_loss和val/box_loss:边界框回归损失。如果val的box_loss降不下去,说明目标框太小,模型难以精确回归,此时考虑调整anchor或增大输入分辨率。metrics/mAP50(B)和metrics/mAP50-95(B):mAP是最终衡量指标。红外小目标数据集上,mAP50通常远高于mAP50-95,因为小目标的框和GT重叠对IoU阈值极为敏感,阈值一抬高,很多“差一点”的框就被判负。metrics/precision(B)和metrics/recall(B):查准率和查全率的平衡。
我在HIT-UAV上用yolov8s训过一轮,mAP50大概能到65~70左右,mAP50-95在35~40之间。这个成绩说不上顶尖,但在算力有限的前提下已经可用了。如果你用yolov8m或者更大的模型,再配合数据增强,mAP50上到75以上是有可能的。
5.4 数据增强的特殊考虑
红外图像的增强策略和可见光不太一样。颜色抖动、饱和度调整这类操作对单通道灰度图几乎无意义,甚至可能引入伪影。我建议优先使用几何增强,比如随机翻转、随机缩放、平移、旋转。Ultralytics默认开启的hsv_h、hsv_s、hsv_v增强,在单通道图上效果不明显,但也不会造成损害,可以保留。
另外一个建议是开启Mosaic增强(Ultralytics默认开启)。Mosaic把4张图拼接成1张,相当于人为增加了单张图中的目标密度,对提升小目标检测能力非常有帮助。缺点是在图像拼接处会出现伪边界,早期训练阶段模型可能会学到错误的特征,但随着训练进行一般能自我纠正。
5.5 对huge模型和小batch的取舍
有一类同学一上来就选yolov8x,显存不够就调小batch,最后batch=4甚至batch=2硬跑。我的建议是:别这样干。batch太小时BN层的统计量不稳定,模型收敛慢且容易震荡。与其用小batch跑大模型,不如用大batch跑小模型,效果通常更好。如果显存实在有限,优先考虑梯度累积,而不是一味地减batch。
6. 常见问题与排查技巧实录
6.1 训练时loss为NaN
训练过程中如果出现loss为NaN,首先检查学习率和batch size的匹配度。YOLOv8默认的lr0=0.01在batch=16、imgsz=640时通常没问题,但如果你的batch很小(比如4),学习率应相应调低到0.002~0.005。另外检查是否启用了混合精度训练(amp=True),在部分老显卡或驱动版本下,fp16和某些自定义loss组合会触发数值溢出,把amp关掉试试。
6.2 模型不收敛或mAP极低
这种问题十有八九出在标签上。我的排查顺序是:
先做标签可视化,确认框的位置和类别是否正确(用前面写过的可视化脚本)。
检查类别编号是否和
data.yaml中的names一致。检查图像读取通道。红外图像是单通道灰度,有些框架默认按3通道读图,读出来是假彩色或者三通道重复,这不会报错,但会影响特征提取。确保数据加载器正确读取单通道图像。
确认训练集和验证集没有重叠。按序列划分而不是按帧划分,能最大程度避免这个问题。
6.3 小目标漏检率高
假设训练正常、标签正确,但模型就是漏检小目标,这是HIT-UAV上最典型的痛点。可以从三个方向去改进:
- 增大输入分辨率:把imgsz从640提到768或896,虽然训练变慢,但对小目标效果提升立竿见影。
- 使用P2检测头:Ultralytics官方没有默认开启P2层,但可以通过修改模型配置增加浅层特征输出。原理是浅层特征图分辨率更高,保留了更多小目标的细节信息。
- 调整anchor或使用Anchor-Free变体:YOLOv8本身就是anchor-free,但如果你换用YOLOv5,记得用
kmeans_anchors重新聚类anchor,默认的COCO anchor对HIT-UAV的目标尺度并不匹配。
6.4 验证集mAP高但实际部署效果差
这个现象需要分析。如果验证集与训练集来自同一段视频序列,即使按序列划分,也可能因为拍摄时间接近而导致场景高度相似,验证集无法反映真实泛化能力。更严谨的做法是从不同时段、不同拍摄地点多留几段视频作为独立的测试集。HIT-UAV的序列划分中,如果有一段是夜间、一段是白天,建议在训练时有意把其中一种留出来当作域外测试集,观察模型在不同域之间的迁移能力。
6.5 一个容易忽略的细节:灰度图的归一化
红外灰度图的像素值范围和可见光一样是0~255,但数据的分布很不一样。有些红外图像对比度极低,背景和目标之间的灰度差只有十几个像素值。模型输入前会做归一化,但默认归一化对这类低对比度图像不敏感。我的经验是,先统计一下全数据集的灰度直方图,如果分布过于集中,可以在预处理阶段做一次直方图均衡化(CLAHE),能显著提升目标与背景的可分性。这个技巧在可见光数据上可能效果不明显,但在红外小目标数据上,往往比换更大的模型更管用。
7. 我的最终体会与扩展方向
跑了几轮HIT-UAV之后,最大的感受是:它逼着你重新审视目标检测里那些被默认设置的“理所当然”。在COCO上,anchor大小、输入分辨率、归一化策略,很多都是拍脑袋就能用的默认值;到了红外小目标场景,这些默认值全都会变成性能瓶颈。
我个人建议,如果时间允许,先用HIT-UAV跑通一版完整的训练评估流程,再去考虑调参优化。这个数据集的规模不算大,单卡训练一轮也就几个小时,非常适合做算法验证和比较实验。
后面可以尝试的扩展方向也不少。
- 将HIT-UAV与合成数据结合:用仿真工具生成带精确标注的红外场景,弥补真实数据的不足,再通过域适应缩小合成到真实的差距。
- 做半监督训练:利用大量无标注的红外视频帧做自监督预训练或一致性正则化,缓解小目标监督信号稀疏的问题。
- 部署到边缘设备:384×512的红外输入非常适合边缘设备,试试用TensorRT或OpenVINO做推理加速,看能不能在嵌入式平台上跑到实时帧率。
这个数据集的门槛不高,但天花板很高。能把它吃透,你对小目标检测和红外视觉的整体理解会上一个台阶。
本文还有配套的精品资源,点击获取