保姆级教程:用Python脚本将TT100K交通标志数据集转为YOLOv8格式(附完整源码与数据集)
从TT100K到YOLOv8:交通标志数据集转换实战指南
当你第一次拿到TT100K数据集时,可能会被它复杂的JSON标注结构弄得一头雾水。作为国内最大的交通标志数据集之一,TT100K包含了10万张图像和3万多个标注框,涵盖221类交通标志。但要将它用于YOLOv8训练,我们需要经历从JSON到YOLO格式的转换过程。本文将手把手带你完成这个转换,并提供完整的Python脚本和优化技巧。
1. 环境准备与数据理解
在开始转换前,我们需要先搭建好开发环境。推荐使用Python 3.8+和以下依赖库:
pip install opencv-python tqdm numpyTT100K数据集通常包含以下目录结构:
tt100k/ ├── annotations_all.json # 全部标注数据 ├── data/ # 原始图像 │ ├── 1.jpg │ ├── 2.jpg │ └── ... └── types.json # 类别定义关键数据结构解析:
- annotations_all.json:包含所有图像的标注信息
- imgs字段:存储每张图片的路径和对应的标注对象
- objects数组:每个元素代表一个交通标志的边界框和类别
提示:原始数据集包含221个类别,但实际应用中我们通常只需要关注出现频率较高的标志类型。
2. 数据预处理与类别筛选
首先我们需要筛选出出现频率较高的交通标志类别。以下代码统计各类别出现的图片数量:
def class_statistics(self): parent_path = 'your_dataset_path' # 修改为你的数据集路径 with open(os.path.join(parent_path, 'annotations_all.json')) as f: origin_dict = json.load(f) classes = origin_dict['types'] sta = {i: [] for i in classes} for img_id, img_info in origin_dict['imgs'].items(): for obj in img_info['objects']: label = obj['category'] if img_info['path'] not in sta[label]: sta[label].append(img_info['path']) # 筛选出现次数≥100的类别 filtered = {k: v for k, v in sta.items() if len(v) >= 100} print(f"保留{len(filtered)}个类别")执行后会生成statistics.json文件,记录筛选后的类别信息。这个阈值(100)可以根据你的需求调整:
| 阈值 | 类别数量 | 数据量 |
|---|---|---|
| 50 | 45 | 12K |
| 100 | 32 | 9K |
| 150 | 25 | 7K |
3. 数据集划分与格式转换
接下来我们将数据集按7:2:1的比例划分为训练集、验证集和测试集:
def split_dataset(self): with open('statistics.json') as f: selected = json.load(f) # 初始化COCO格式数据集 datasets = { 'train': {'images': [], 'annotations': []}, 'val': {'images': [], 'annotations': []}, 'test': {'images': [], 'annotations': []} } # 为每个类别分配ID class_ids = {cls: idx for idx, cls in enumerate(selected['type'])} for img_id, img_info in origin_dict['imgs'].items(): img_name = img_info['path'].split('/')[-1] if img_name not in selected['images']: continue # 确定数据划分(7:2:1) rand = random.random() phase = 'train' if rand < 0.7 else 'val' if rand < 0.9 else 'test' # 添加图像信息 img_data = { 'file_name': img_name, 'id': img_id, 'width': img_info['width'], 'height': img_info['height'] } datasets[phase]['images'].append(img_data) # 添加标注信息 for obj in img_info['objects']: if obj['category'] not in selected['type']: continue bbox = obj['bbox'] datasets[phase]['annotations'].append({ 'bbox': [bbox['xmin'], bbox['ymin'], bbox['xmax']-bbox['xmin'], bbox['ymax']-bbox['ymin']], 'category_id': class_ids[obj['category']], 'image_id': img_id, 'id': len(datasets[phase]['annotations'])+1 })4. COCO转YOLO格式的核心算法
YOLO格式要求每个图像对应一个.txt文件,每行表示一个对象,格式为:
class_id center_x center_y width height转换函数的关键部分:
def coco2yolo(self, json_file, output_dir): os.makedirs(output_dir, exist_ok=True) with open(json_file) as f: data = json.load(f) # 生成类别映射文件 with open(os.path.join(output_dir, 'classes.txt'), 'w') as f: for cat in sorted(data['categories'], key=lambda x: x['id']): f.write(f"{cat['name']}\n") # 处理每张图像 for img in data['images']: img_id = img['id'] anns = [a for a in data['annotations'] if a['image_id'] == img_id] txt_path = os.path.join(output_dir, os.path.splitext(img['file_name'])[0] + '.txt') with open(txt_path, 'w') as f: for ann in anns: # 转换坐标到YOLO格式 x, y, w, h = ann['bbox'] x_center = (x + w/2) / img['width'] y_center = (y + h/2) / img['height'] w_norm = w / img['width'] h_norm = h / img['height'] f.write(f"{ann['category_id']} {x_center:.6f} {y_center:.6f} " f"{w_norm:.6f} {h_norm:.6f}\n")5. 完整流程与常见问题解决
完整的转换流程如下:
数据准备
- 下载TT100K数据集
- 解压并检查annotations_all.json文件
执行转换
converter = TT100K2YOLO() converter.class_statistics() # 步骤1:类别筛选 converter.split_dataset() # 步骤2:数据集划分 converter.coco2yolo('train.json', 'labels/train') # 步骤3:格式转换 converter.coco2yolo('val.json', 'labels/val') converter.coco2yolo('test.json', 'labels/test')
常见问题及解决方案:
- 路径问题:确保所有文件路径正确,特别是在Windows系统中注意反斜杠转义
- 内存不足:对于大文件可分批次处理
- 类别不匹配:检查statistics.json中的类别是否与转换一致
最终得到的YOLO格式数据集结构:
yolo_tt100k/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ ├── test/ └── classes.txt6. YOLOv8训练配置
转换完成后,创建data.yaml文件配置训练:
# data.yaml train: ../yolo_tt100k/images/train val: ../yolo_tt100k/images/val test: ../yolo_tt100k/images/test nc: 32 # 类别数量 names: ['i2', 'i4', 'i5', 'il100', ...] # 你的类别列表然后使用YOLOv8命令行开始训练:
yolo train model=yolov8n.pt data=data.yaml epochs=100 imgsz=640在实际项目中,我发现以下几个技巧能显著提升模型性能:
- 对小型交通标志适当增加图像分辨率
- 使用--augment参数启用数据增强
- 对类别不平衡问题使用--weighted参数
