共计 2712 个字符,预计需要花费 7 分钟才能阅读完成。
背景介绍
BDD100K 是伯克利大学发布的大规模自动驾驶数据集,包含 10 万张图像,涵盖多种天气和光照条件。其标注格式采用 JSON 文件存储,每个 JSON 文件对应一个图像或视频帧,包含物体类别、边界框坐标(左上角 x,y 和宽高 w,h)等信息。而 YOLO 格式则需要每个图像对应一个 txt 文件,每行存储类别 ID 和归一化后的中心坐标 (x_center, y_center) 及宽高(width, height)。

两种格式的主要差异包括:
- 坐标表示方式:BDD100K 使用绝对像素坐标,YOLO 需要归一化的相对坐标
- 文件结构:BDD100K 集中存储,YOLO 分散存储
- 类别标识:BDD100K 使用字符串类别名,YOLO 使用数字 ID
技术方案
1. 坐标系统转换
YOLO 格式要求将边界框坐标归一化为 0 - 1 之间的相对值,计算步骤如下:
- 获取图像宽度 (img_w) 和高度(img_h)
- 计算边界框中心点 x 坐标:x_center = (x + w/2) / img_w
- 计算边界框中心点 y 坐标:y_center = (y + h/2) / img_h
- 计算归一化宽度:width = w / img_w
- 计算归一化高度:height = h / img_h
2. 类别 ID 映射
BDD100K 包含 10 个主要类别,我们需要建立类别名到数字 ID 的映射关系。例如:
class_map = {
'pedestrian': 0,
'rider': 1,
'car': 2,
'truck': 3,
'bus': 4,
'train': 5,
'motorcycle': 6,
'bicycle': 7,
'traffic light': 8,
'traffic sign': 9
}
3. 多对象处理
每个图像可能包含多个标注对象,需要将所有对象的转换结果写入同一个 txt 文件,每个对象占一行。
代码实现
以下是完整的 Python 转换脚本:
import json
import os
import cv2
# 配置路径
json_dir = 'bdd100k/labels/'
image_dir = 'bdd100k/images/'
output_dir = 'bdd100k/yolo_labels/'
# 类别映射
class_map = {# ... 同上...}
def convert_bdd_to_yolo():
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
# 遍历所有 JSON 标注文件
for json_file in os.listdir(json_dir):
if not json_file.endswith('.json'):
continue
# 解析 JSON
with open(os.path.join(json_dir, json_file)) as f:
data = json.load(f)
# 获取对应的图像文件
image_path = os.path.join(image_dir, data['name'].replace('.json', '.jpg'))
if not os.path.exists(image_path):
print(f'Warning: Missing image {image_path}')
continue
# 获取图像尺寸
img = cv2.imread(image_path)
img_h, img_w = img.shape[:2]
# 准备 YOLO 格式内容
yolo_lines = []
for obj in data['labels']:
if obj['category'] not in class_map:
continue
# 坐标转换
x, y, w, h = obj['box2d']['x1'], obj['box2d']['y1'], \
obj['box2d']['x2']-obj['box2d']['x1'], \
obj['box2d']['y2']-obj['box2d']['y1']
x_center = (x + w/2) / img_w
y_center = (y + h/2) / img_h
width = w / img_w
height = h / img_h
# 格式化为 YOLO 行
yolo_line = f"{class_map[obj['category']]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}"
yolo_lines.append(yolo_line)
# 写入 YOLO 格式文件
output_file = os.path.join(output_dir, data['name'].replace('.json', '.txt'))
with open(output_file, 'w') as f:
f.write('\n'.join(yolo_lines))
if __name__ == '__main__':
convert_bdd_to_yolo()
避坑指南
-
文件名匹配:BDD100K 中视频帧的文件名可能与 JSON 文件名不完全对应,需要仔细检查命名规则
-
特殊字符处理:某些图像文件名可能包含特殊字符,建议使用 os.path 处理路径
-
内存优化:处理大规模数据集时,避免同时加载所有图像,应该逐个处理
-
多进程处理:对于超大规模数据集,可以考虑使用多进程加速
验证方法
可以使用以下 Python 代码可视化验证转换结果:
import cv2
def visualize_yolo_label(image_path, label_path):
img = cv2.imread(image_path)
h, w = img.shape[:2]
with open(label_path) as f:
for line in f:
class_id, xc, yc, bw, bh = map(float, line.strip().split())
# 转换回像素坐标
x1 = int((xc - bw/2) * w)
y1 = int((yc - bh/2) * h)
x2 = int((xc + bw/2) * w)
y2 = int((yc + bh/2) * h)
# 绘制边界框
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow('Validation', img)
cv2.waitKey(0)
性能考量
- 图像尺寸获取是主要瓶颈,可以考虑缓存图像尺寸信息
- 对于超大规模数据集,JSON 解析可能成为性能瓶颈
- 文件 I / O 操作可以批量处理以减少系统调用
实际应用
这套转换方法不仅适用于 BDD100K 数据集,经过适当调整后可以应用于其他 JSON 格式标注的数据集转换。在实际项目中,你可能需要:
- 调整类别映射关系
- 处理不同的坐标表示方式
- 添加额外的属性字段
建议读者在自己的数据集上尝试这种方法,并根据具体需求进行调整。格式转换是计算机视觉项目中的常见任务,掌握这套方法可以大大提高数据准备的效率。
