红外数据集构建:从采集到标注的完整实战

做红外目标检测的嵌入式移植,第一步就是搞定数据集。说实话,这一步最容易被忽视,但也是坑最多的地方。我见过太多团队,算法调得再好,一到实际场景就翻车——十有八九是数据集没做好。

今天我就把红外数据集的构建流程,从采集到标注再到格式转换,完整地捋一遍。这些都是我在项目里踩过坑之后总结出来的经验。

红外数据采集方法

先说说采集。红外数据和可见光不一样,它捕捉的是热辐射。说白了,就是物体温度越高,在图像里越亮。

采集设备怎么选?

  • 非制冷焦平面探测器:最常见,性价比高。我项目里用的就是这种,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之间的浮点数!

知识体系总览

下面这张图,把红外数据集构建的整个流程串起来了。你照着这个路线走,基本不会出大问题。

红外数据集构建流程 数据采集 设备选择 · 场景规划 数据标注 LabelImg / CVAT 格式转换 VOC → COCO → YOLO 多时段 · 多季节 · 多角度 · 多距离 矩形框标注 · 类别标签 · 审核校验 坐标归一化 · 格式统一 核心原则 数据多样性 > 数据数量 · 标注质量 > 标注速度 先小批量验证,再大规模标注

你看,整个流程其实就三步:采集→标注→转换。但每一步都有讲究。数据采集决定了模型的上限,标注质量决定了模型能不能达到这个上限,格式转换则是让数据能被算法框架吃进去。

我个人习惯是:先小批量采集几百张,快速标注验证一下,看看模型能不能收敛。能收敛了,再大规模采集和标注。这样能避免白费功夫。

最后说一句: 红外数据集构建,没有捷径。你花在数据上的每一分钟,都会在模型精度上得到回报。别想着用可见光数据集凑合,红外和可见光的特征分布完全不同,硬凑只会让模型在部署时翻车。

公众号:蓝海资料掘金营,微信deep3321