红外数据集构建:从采集到标注的完整实战
做红外目标检测的嵌入式移植,第一步就是搞定数据集。说实话,这一步最容易被忽视,但也是坑最多的地方。我见过太多团队,算法调得再好,一到实际场景就翻车——十有八九是数据集没做好。
今天我就把红外数据集的构建流程,从采集到标注再到格式转换,完整地捋一遍。这些都是我在项目里踩过坑之后总结出来的经验。
红外数据采集方法
先说说采集。红外数据和可见光不一样,它捕捉的是热辐射。说白了,就是物体温度越高,在图像里越亮。
采集设备怎么选?
- 非制冷焦平面探测器:最常见,性价比高。我项目里用的就是这种,640×512分辨率,30fps左右
- 制冷型探测器:贵,但灵敏度高。军工项目里常见,民用场景一般用不上
- 热成像模组:像FLIR Lepton这种,便宜但分辨率低(80×60),适合入门验证
我的建议: 如果做嵌入式移植,优先选640×512分辨率的非制冷型。分辨率太低,小目标根本检测不到。我曾经用160×120的模组试过,人站在50米外就剩几个像素点了,算法再牛也白搭。
采集场景要注意什么?
- 多时段采集:白天、夜晚、黄昏都要有。红外图像在不同时段差异很大
- 多季节采集:夏天和冬天,背景温度差十几度,目标对比度完全不一样
- 多角度采集:俯视、平视、侧视。我有个项目只采集了平视数据,结果无人机俯视检测时直接崩了
- 多距离采集:近景(10m内)、中景(10-50m)、远景(50m以上)
注意: 红外图像容易受环境温度影响。夏天中午,地面温度可能比人体还高,目标反而变暗了。采集时一定要记录环境温度,方便后续数据清洗。
标注工具:LabelImg vs CVAT
数据采完了,接下来就是标注。我常用的工具就两个:LabelImg和CVAT。各有各的适用场景。
LabelImg
轻量级,单机运行。适合小规模数据集(几百到几千张)。
- 安装简单:
pip install labelImg就行 - 支持Pascal VOC格式(XML)和YOLO格式(TXT)
- 快捷键操作,标注效率还行
我刚开始做红外项目时,就用LabelImg。几百张图,一个人标了两天。嗯,那时候还没意识到后面要标几万张。
CVAT
在线标注平台,支持多人协作。适合大规模数据集(万级以上)。
- 基于Web,部署在服务器上
- 支持自动标注(用预训练模型辅助)
- 支持VOC、COCO、YOLO等多种格式导出
- 有审核机制,可以多人分工
核心区别: 个人项目用LabelImg,团队项目用CVAT。我后来那个万级数据集的项目,5个人同时标,CVAT的审核功能帮了大忙——不然标错的数据混进去,模型训练出来就是废的。
数据集格式转换
标注完了,格式不统一怎么办?不同算法框架要求的格式不一样。YOLO要TXT,SSD要VOC,Mask R-CNN要COCO。你得会转。
三种主流格式对比:
| 格式 | 标注文件 | 坐标格式 | 适用框架 |
|---|---|---|---|
| VOC | XML | 左上角+右下角 (xmin, ymin, xmax, ymax) | SSD、Faster R-CNN |
| COCO | JSON | 左上角+宽高 (x, y, w, h) | Mask R-CNN、Detectron2 |
| YOLO | TXT | 归一化中心点+宽高 (cx, cy, w, h) | YOLOv3/v5/v8 |
格式转换代码示例(VOC转YOLO):
import xml.etree.ElementTree as ET
import os
def voc_to_yolo(xml_file, classes, output_dir):
tree = ET.parse(xml_file)
root = tree.getroot()
# 获取图像尺寸
img_w = int(root.find('size/width').text)
img_h = int(root.find('size/height').text)
# 输出文件名
base_name = os.path.splitext(os.path.basename(xml_file))[0]
out_file = os.path.join(output_dir, base_name + '.txt')
with open(out_file, 'w') as f:
for obj in root.findall('object'):
class_name = obj.find('name').text
class_id = classes.index(class_name)
# 读取VOC坐标
xmin = int(obj.find('bndbox/xmin').text)
ymin = int(obj.find('bndbox/ymin').text)
xmax = int(obj.find('bndbox/xmax').text)
ymax = int(obj.find('bndbox/ymax').text)
# 转YOLO格式(归一化)
cx = (xmin + xmax) / 2.0 / img_w
cy = (ymin + ymax) / 2.0 / img_h
w = (xmax - xmin) / img_w
h = (ymax - ymin) / img_h
f.write(f"{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n")
# 使用示例
classes = ['person', 'car', 'dog'] # 你的类别列表
voc_to_yolo('000001.xml', classes, './yolo_labels/')
避坑指南: 我曾经在转换时忘了归一化,直接把像素坐标写进YOLO的TXT文件。训练时loss死活不降,查了两天才发现是坐标范围不对。记住:YOLO的坐标是0-1之间的浮点数!
知识体系总览
下面这张图,把红外数据集构建的整个流程串起来了。你照着这个路线走,基本不会出大问题。
你看,整个流程其实就三步:采集→标注→转换。但每一步都有讲究。数据采集决定了模型的上限,标注质量决定了模型能不能达到这个上限,格式转换则是让数据能被算法框架吃进去。
我个人习惯是:先小批量采集几百张,快速标注验证一下,看看模型能不能收敛。能收敛了,再大规模采集和标注。这样能避免白费功夫。
最后说一句: 红外数据集构建,没有捷径。你花在数据上的每一分钟,都会在模型精度上得到回报。别想着用可见光数据集凑合,红外和可见光的特征分布完全不同,硬凑只会让模型在部署时翻车。
公众号:蓝海资料掘金营,微信deep3321