本文档用于归档 AI-TOD 遥感小目标检测数据集的完整整理流程,涵盖数据下载、数据处理、格式转换三个核心阶段,流程规范可复现,适配后续 YOLO/MMDetection 等框架的模型训练需求。

一、 数据下载(阶段 1)

1.1 下载所需数据集

AI-TOD 数据集的生成依赖两个核心数据源,需分别下载并归档:

  1. xView 原始数据集(小目标素材库)
    • 下载地址:xView 官方发布渠道(或学术数据集平台如 Kaggle)需注册(https://challenge.xviewdataset.org/profile
    • 归档内容:下载得到的压缩包、解压后的 846 张遥感图像(.tif 格式)、官方标注文件 xView_train.geojson
    • 归档路径:/media/lfq/EAGET/AI-TOD/AI-TOD/xview/(统一存放,方便后续调用)
    • 关键说明:xView 数据集仅需训练集(846 张),无需测试集,其核心价值是提供带精准标注的小目标图像块。

     2.  AI-TOD_wo_xview 数据集(基础背景画布库)

  • 下载地址:https://drive.google.com/drive/folders/1uNY_rcOO5LrWibXRY6l2dvqSbK6xikJp
  • 归档内容:下载得到的压缩包、解压后的标注文件(.json 格式,存放至 annotations 目录)、图像文件(.png 格式,按 train/val/test 三级目录存放)
  • 归档路径:/media/lfq/EAGET/AI-TOD/AI-TOD/aitod/(保持官方指定目录结构不变)
  • 关键说明:该数据集是 AI-TOD 的基础背景库,图像数量分别为 train 7510 张、val 2804 张、test 9351 张,无需修改其目录结构,避免影响后续合成流程。

1.2 下载配套工具

  1. AI-TOD 官方合成脚本:从 AI-TOD 官方 GitHub 仓库下载 generate_aitod_imgs.py 及相关配置文件,归档路径:/media/lfq/EAGET/AI-TOD/AI-TOD/
  2. wwtool 工具库:用于地理数据 / 标注格式处理,后续若需简化操作可安装,克隆地址:https://github.com/jwwangchn/wwtool.git

1.3 环境准备

  • 激活专属 Conda 环境:conda activate aitod(Python 3.7,适配 AI-TOD 相关工具依赖)
  • 预装基础依赖:pip install numpy pillow tqdm shapely fiona(为后续数据处理、格式转换铺路)

二、 数据处理(阶段 2)

2.1 数据解压与目录规整

  1. 分别解压 xView 数据集和 AI-TOD_wo_xview 数据集,确保解压后目录结构符合以下规范,无冗余压缩包:
    ├─aitod
    │  ├─annotations ## put the downloaded annotations of AI-TOD_wo_xview (.json)
    │  └─images ## unzip the downloaded AI-TOD_wo_xview image sets, put them (.png) in the corresponding folder
    │      ├─test ## directly put the images in it without extra folder
    │      ├─train 
    │      ├─trainval 
    │      └─val 
    ├─aitod_xview ## here are six files (.txt)
    ├─xview
    │  ├─ori
    │  │   └─train_images ## unzip the downloaded xView training set images, put them (.tif) here
    │  └─xView_train.geojson ## the annotation file of xView training set
    └─generate_aitod_imgs.py ## end-to-end tool

    核对目录完整性:

  2. xView 目录:xview/ori/train_images/ 包含 846 张 .tif 图像,xview/ 下存在 xView_train.geojson 标注,无缺失文件。
  3. AI-TOD_wo_xview 目录:aitod/annotations/ 包含完整 .json 标注,aitod/images/ 下 train/val/test 三级结构完整,图像数量分别为 7510/2804/9351 张,无损坏图像。
  4. aitod_xview/ 目录:包含 6 个 .txt 文件,无缺失、无损坏

2.2 运行 AI-TOD 数据合成脚本

核心目标:将 xView 小目标素材嵌入 AI-TOD_wo_xview 背景图像,生成完整的 AI-TOD 数据集(9306 张,train/3704 张、val/935 张、test/4667 张)。

切换至脚本目录:cd /media/lfq/EAGET/AI-TOD/AI-TOD/

        1. 准备wwtool工具

git clone https://github.com/jwwangchn/wwtool.git
cd wwtool
python setup.py develop

        安装其他重要的包

cd ..
cd aitodtoolkit
pip install -r requirements.txt

运行运行合成脚本:

python generate_aitod_imgs.py

  2. 监控运行日志:无报错中断、无缺失文件提示,等待合成完成(耗时根据硬件配置有所差异)。
   3. 验证合成结果:
           查看输出目录 /media/lfq/EAGET/AI-TOD/AI-TOD/aitodtoolkit/xview/xview_aitod_sets/,确认存在 train/val/test 三级目录。
          随机打开若干图像,检查小目标嵌入自然,无明显拼接痕迹,图像无损坏。
          核对图像数量:train 3704 张、val 935 张、test 4667 张,总数 9306 张,符合官方标准。

2.3 原始标注归档

合成完成后,将生成的原始标注(VOC 风格 .txt 格式,分布在 train/val/test 三级目录下)分别归档至对应目录的 ./voc_anno/ 文件夹,路径示例:

  • train 集标注:./xview/xview_aitod_sets/train/voc_anno/
  • val 集标注:./xview/xview_aitod_sets/val/voc_anno/
  • test 集标注:./xview/xview_aitod_sets/test/voc_anno/确保标注文件与图像文件一一对应(同名,仅后缀不同),无缺失、无冗余。

三、 数据格式转换(阶段 3)

核心目标:将 VOC 风格 .txt 标注(xmin ymin xmax ymax class_name)转换为 YOLO 标准格式 .txt 标注(class_id x_center y_center w h,归一化至 0-1),适配 YOLOv5/YOLOv8 等框架训练。

3.1 步骤 1:提取全量类别(多目录批量提取)

  1. 新建辅助脚本 extract_classes_multi_dir.py,配置 train/val/test 三级标注目录路径(对应 ./xview/xview_aitod_sets/ 下的三级 voc_anno 目录)。
  2. 运行脚本:python extract_classes_multi_dir.py
  3. 提取结果归档:
    • 记录提取到的全量类别数量(与 AI-TOD 官方 28 类核对,确保无遗漏)。
    • 复制脚本输出的 CLASS_MAP(类别 - 数字 ID 映射表,ID 从 0 开始连续排列),归档至文档,方便后续复用。
    • 记录各类别标注数量及分布(train/val/test),判断数据均衡性,归档统计结果。
import os

# ---------------------- 配置区(修改为你的三个标注目录路径)----------------------
# 填写 train、val、test 三个目录的完整路径(按你的实际路径修改)
VOC_TXT_DIRS = [
    "/media/lfq/EAGET/AI-TOD/AI-TOD/aitodtoolkit/xview/xview_aitod_sets/trainval/labels",
    "/media/lfq/EAGET/AI-TOD/AI-TOD/aitodtoolkit/xview/xview_aitod_sets/test/labels",

]
# --------------------------------------------------------------------------------

# 1. 初始化数据结构(去重+统计)
class_set = set()  # 存储所有不重复类别
class_count_total = {}  # 统计所有目录的总标注数量
class_count_per_dir = {}  # 统计每个目录的标注数量(方便查看分布)

# 2. 遍历所有指定目录
for voc_txt_dir in VOC_TXT_DIRS:
    # 初始化当前目录的类别统计
    dir_name = os.path.basename(os.path.dirname(voc_txt_dir))  # 提取目录名(train/val/test)
    class_count_per_dir[dir_name] = {}

    # 检查目录是否存在
    if not os.path.exists(voc_txt_dir):
        print(f"警告:目录不存在,跳过 → {voc_txt_dir}")
        continue

    # 遍历当前目录下的所有 .txt 标注文件
    voc_txt_files = [f for f in os.listdir(voc_txt_dir) if f.endswith(".txt")]
    if not voc_txt_files:
        print(f"警告:目录下无 .txt 标注文件,跳过 → {voc_txt_dir}")
        continue

    # 解析每个标注文件
    for txt_file in voc_txt_files:
        txt_path = os.path.join(voc_txt_dir, txt_file)
        with open(txt_path, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue  # 跳过空行

                # 解析每行标注(格式:xmin ymin xmax ymax class_name)
                parts = line.split()
                if len(parts) != 5:
                    continue  # 跳过格式错误的行

                # 提取类别名称
                class_name = parts[-1]

                # 3. 加入全局集合(自动去重)
                class_set.add(class_name)

                # 4. 统计总数量
                if class_name in class_count_total:
                    class_count_total[class_name] += 1
                else:
                    class_count_total[class_name] = 1

                # 5. 统计当前目录的数量
                if class_name in class_count_per_dir[dir_name]:
                    class_count_per_dir[dir_name][class_name] += 1
                else:
                    class_count_per_dir[dir_name][class_name] = 1

# 3. 转换为有序列表(方便分配 YOLO 类别 ID)
class_list = sorted(list(class_set))

# 4. 输出结果
print("=" * 60)
print(f"成功从 {len([d for d in VOC_TXT_DIRS if os.path.exists(d)])} 个有效目录中提取到 {len(class_list)} 个类别:")
print("=" * 60)

# 输出所有类别(带全局序号和总数量)
for idx, class_name in enumerate(class_list):
    total_count = class_count_total.get(class_name, 0)
    print(f"类别 ID {idx} → {class_name}(总标注数量:{total_count})")

# 输出每个目录的类别分布(可选,方便查看数据均衡性)
print("\n" + "=" * 60)
print("各目录类别标注数量分布:")
print("=" * 60)
for dir_name, class_count in class_count_per_dir.items():
    print(f"\n【{dir_name} 目录】")
    for class_name in class_list:
        dir_count = class_count.get(class_name, 0)
        print(f"  {class_name}:{dir_count} 条标注")

# 输出可直接复制的 CLASS_MAP
print("\n" + "=" * 60)
print("直接可复制到 VOC → YOLO 转换脚本的 CLASS_MAP:")
print("=" * 60)
class_map_code = "CLASS_MAP = {\n"
for idx, class_name in enumerate(class_list):
    class_map_code += f'    "{class_name}": {idx},\n'
class_map_code = class_map_code.rstrip(",\n") + "\n}"
print(class_map_code)

3.2 步骤 2:批量转换标注格式

  1. 新建转换脚本 voc_txt2yolo_txt.py,粘贴步骤 1 中提取的 CLASS_MAP,配置图像尺寸 1024x1024。
  2. 分三次运行脚本(分别对应 train/val/test 集),每次修改脚本中「输入 VOC 标注目录」和「输出 YOLO 标注目录」:
    • train 集转换:输入 ./xview/xview_aitod_sets/train/voc_anno/,输出 ./xview/xview_aitod_sets/train/yolo_anno/
    • val 集转换:输入 ./xview/xview_aitod_sets/val/voc_anno/,输出 ./xview/xview_aitod_sets/val/yolo_anno/
    • test 集转换:输入 ./xview/xview_aitod_sets/test/voc_anno/,输出 ./xview/xview_aitod_sets/test/yolo_anno/
  3. 运行命令:python voc_txt2yolo_txt.py,监控无「未知类别」警告、无格式错误警告。
import os
from tqdm import tqdm

# ---------------------- 配置区(必须根据你的实际情况修改)----------------------
# 1. 你的 VOC 风格 .txt 标注文件夹路径
input_voc_txt_dir = "/media/lfq/EAGET/AI-TOD/AI-TOD/aitodtoolkit/xview/xview_aitod_sets/trainval/labels"  # 替换为你的 VOC .txt 文件夹路径
# 2. YOLO 格式 .txt 标注输出文件夹路径
output_yolo_txt_dir = "/media/lfq/EAGET/AI-TOD/AI-TOD/aitodtoolkit/xview/xview_aitod_sets/trainval/yolo-labels"
# 3. AI-TOD 图像尺寸(默认 1024x1024,无需修改,若不同请调整)
IMG_WIDTH, IMG_HEIGHT = 1024, 1024
# 4. 类别映射表(key:你的 VOC 标注中的文本类别,value:YOLO 数字 ID(从 0 开始))
# 示例:如果你的标注只有 "vehicle" 类,就只保留这一行;有多个类就依次添加
CLASS_MAP = {
    "airplane": 0,
    "ship": 1,
    "storage-tank": 2,
    "vehicle": 3
}

# --------------------------------------------------------------------------------

# 1. 确保输出目录存在
os.makedirs(output_yolo_txt_dir, exist_ok=True)

# 2. 遍历所有 VOC 风格 .txt 标注文件
voc_txt_files = [f for f in os.listdir(input_voc_txt_dir) if f.endswith(".txt")]
if not voc_txt_files:
    print("错误:未在输入目录中找到 .txt 标注文件")
    exit(1)

for txt_file in tqdm(voc_txt_files, desc="转换 VOC → YOLO"):
    # 2.1 拼接输入/输出文件路径
    input_txt_path = os.path.join(input_voc_txt_dir, txt_file)
    output_txt_path = os.path.join(output_yolo_txt_dir, txt_file)

    # 2.2 读取 VOC 标注,转换并写入 YOLO 标注
    with open(input_txt_path, 'r', encoding='utf-8') as f_in, \
            open(output_txt_path, 'w', encoding='utf-8') as f_out:

        for line in f_in:
            line = line.strip()
            if not line:
                continue  # 跳过空行

            # 解析 VOC 格式一行数据(xmin ymin xmax ymax class_name)
            # 注意:如果你的数据有多余空格/制表符,split() 会自动处理
            parts = line.split()
            if len(parts) != 5:
                print(f"警告:跳过无效行(格式错误)→ {line}")
                continue

            xmin, ymin, xmax, ymax, class_name = parts

            # 2.3 转换为数值类型(像素坐标)
            try:
                xmin = float(xmin)
                ymin = float(ymin)
                xmax = float(xmax)
                ymax = float(ymax)
            except ValueError:
                print(f"警告:跳过无效行(坐标非数字)→ {line}")
                continue

            # 2.4 检查类别是否在映射表中
            if class_name not in CLASS_MAP:
                print(f"警告:跳过未知类别 → {class_name}")
                continue
            class_id = CLASS_MAP[class_name]

            # 2.5 转换为 YOLO 格式(归一化 + 中心坐标+宽高)
            # 计算中心坐标
            x_center = (xmin + xmax) / 2.0
            y_center = (ymin + ymax) / 2.0
            # 计算宽高
            bbox_w = xmax - xmin
            bbox_h = ymax - ymin
            # 归一化(除以图像宽高,保留 6 位小数)
            x_center_norm = round(x_center / IMG_WIDTH, 6)
            y_center_norm = round(y_center / IMG_HEIGHT, 6)
            bbox_w_norm = round(bbox_w / IMG_WIDTH, 6)
            bbox_h_norm = round(bbox_h / IMG_HEIGHT, 6)

            # 2.6 写入 YOLO 标注文件
            f_out.write(f"{class_id} {x_center_norm} {y_center_norm} {bbox_w_norm} {bbox_h_norm}\n")

# 3. 转换完成提示
print(f"\n转换成功!YOLO 标注文件已保存至:{output_yolo_txt_dir}")
print(f"共处理 {len(voc_txt_files)} 个标注文件")

3.3 步骤 3:验证转换结果

  1. 核对标注文件:输出目录下 YOLO 标注文件与输入 VOC 标注文件、图像文件一一对应,数量一致。
  2. 核对标注格式:
    • 打开任意 YOLO 标注文件,内容为「class_id 四个归一化数值」,数值范围 0-1,保留 6 位小数。
    • 随机选取 1-2 个标注文件,手动验证坐标转换正确性(通过 VOC 坐标反推 YOLO 坐标,确认无计算错误)。
  3. 归档转换结果:将 train/val/test 三级目录下的 YOLO 标注文件永久归档,路径与图像文件对应,方便后续模型训练调用。

3.4 步骤 4:生成 YOLO 框架配置文件

新建 aitod.yaml 配置文件,填写数据集路径、类别信息,归档至 ./xview/xview_aitod_sets/ 目录,内容示例:

path: /media/lfq/EAGET/AI-TOD/AI-TOD/aitodtoolkit/xview/xview_aitod_sets  # 数据集根路径
train: train  # 训练集图像目录(相对根路径)
val: val      # 验证集图像目录(相对根路径)
test: test    # 测试集图像目录(相对根路径)
nc: 4       # 类别总数(AI-TOD 官方 28 类)
names: ["airplane", "ship", "storage-tank","vehicle"]  # 完整类别名称列表(与 CLASS_MAP 一一对应)

四、 最终归档与总结

  1. 完整目录结构归档:将整理后的 AI-TOD 数据集按「图像 + VOC 标注 + YOLO 标注 + 配置文件」的结构归档,确保目录清晰、路径可追溯。
  2. 文档归档:将本文档、辅助脚本、转换脚本、类别统计结果、配置文件统一归档至 ./docs/ 目录,方便后续团队复用、复现实验。
  3. 关键结论:
    • 本次整理的 AI-TOD 数据集符合官方标准,图像数量 9306 张,标注完整、格式正确。
    • 转换后的 YOLO 格式标注可直接用于 YOLO 系列框架训练,无需额外处理。
    • 数据集小目标密集,适配遥感小目标检测任务,后续训练需针对性优化模型参数与数据增强策略。

总结

  1. AI-TOD 数据集整理核心分为「下载、处理、格式转换」三阶段,需保证数据源完整、合成流程无报错、格式转换精准,同时严格遵循修正后的目录结构与路径要求。
  2. 数据处理的关键是运行官方合成脚本,生成符合标准的 9306 张图像并存放至指定路径 xview_aitod_sets;格式转换的核心是「类别提取 - 映射 - 坐标归一化」,适配目标训练框架。
  3. 全程需做好目录规整与结果归档,确保流程可复现、数据可追溯,为后续模型训练奠定坚实基础。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐