共计 3575 个字符,预计需要花费 9 分钟才能阅读完成。
背景与需求分析
CCPD 数据集是车牌检测领域的常用数据集,但其标注格式为 VOC 风格的 XML 文件,而 YOLO 系列目标检测框架要求特定的 TXT 标注格式。这种格式差异导致直接使用 CCPD 数据集进行 YOLO 模型训练存在障碍。

主要差异点包括:
- 坐标表示方式:CCPD 使用绝对像素坐标,YOLO 要求归一化的相对坐标
- 文件结构:CCPD 每个 XML 对应一张图片,YOLO 需要将所有标注合并到单个 TXT
- 类别编码:YOLO 需要用数字索引替代字符串类别名
技术方案设计
我们设计了一个 Python 自动化脚本,主要处理流程如下:
- 遍历指定目录下的所有 XML 标注文件
- 解析每个 XML 文件中的车牌位置信息
- 将绝对坐标转换为 YOLO 格式的归一化坐标
- 生成对应名称的 TXT 标注文件
- 提供验证机制确保转换准确性
坐标转换算法
YOLO 格式要求将边界框坐标归一化为相对图像宽高的比例值,转换公式为:
x_center = (xmin + xmax) / (2 * image_width)
y_center = (ymin + ymax) / (2 * image_height)
width = (xmax - xmin) / image_width
height = (ymax - ymin) / image_height
代码实现
以下是完整的转换脚本,包含详细注释和类型注解:
import argparse
import xml.etree.ElementTree as ET
from pathlib import Path
from tqdm import tqdm
import cv2
import logging
# 配置日志记录
logging.basicConfig(filename='conversion.log', level=logging.INFO)
def parse_args() -> argparse.Namespace:
"""解析命令行参数"""
parser = argparse.ArgumentParser(description='CCPD to YOLO format converter')
parser.add_argument('--xml-dir', type=str, required=True, help='Directory containing CCPD XML files')
parser.add_argument('--output-dir', type=str, required=True, help='Output directory for YOLO txt files')
parser.add_argument('--img-dir', type=str, required=True, help='Directory containing corresponding images')
parser.add_argument('--class-id', type=int, default=0, help='Class ID for license plates')
return parser.parse_args()
def convert_bbox(size: tuple[int, int], box: tuple[float, float, float, float]) -> tuple[float, float, float, float]:
"""将 VOC 格式边界框转换为 YOLO 格式"""
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def process_xml(xml_file: Path, output_dir: Path, img_dir: Path, class_id: int) -> bool:
"""处理单个 XML 文件"""
try:
tree = ET.parse(xml_file)
root = tree.getroot()
# 获取图片尺寸
size = root.find('size')
width = int(size.find('width').text)
height = int(size.find('height').text)
# 获取边界框信息
obj = root.find('object')
bndbox = obj.find('bndbox')
xmin = float(bndbox.find('xmin').text)
xmax = float(bndbox.find('xmax').text)
ymin = float(bndbox.find('ymin').text)
ymax = float(bndbox.find('ymax').text)
# 坐标转换
yolo_box = convert_bbox((width, height), (xmin, xmax, ymin, ymax))
# 写入 TXT 文件
txt_path = output_dir / f"{xml_file.stem}.txt"
with open(txt_path, 'w') as f:
f.write(f"{class_id} {yolo_box[0]} {yolo_box[1]} {yolo_box[2]} {yolo_box[3]}")
return True
except Exception as e:
logging.error(f"Error processing {xml_file}: {str(e)}")
return False
def main():
args = parse_args()
xml_dir = Path(args.xml_dir)
output_dir = Path(args.output_dir)
img_dir = Path(args.img_dir)
# 创建输出目录
output_dir.mkdir(parents=True, exist_ok=True)
# 获取所有 XML 文件
xml_files = list(xml_dir.glob('*.xml'))
# 处理每个文件
success_count = 0
for xml_file in tqdm(xml_files, desc="Processing files"):
if process_xml(xml_file, output_dir, img_dir, args.class_id):
success_count += 1
print(f"Successfully processed {success_count}/{len(xml_files)} files")
if __name__ == '__main__':
main()
验证转换结果
为确保转换准确性,我们可以使用 OpenCV 可视化标注结果:
def visualize_yolo_annotation(img_path: Path, txt_path: Path):
"""可视化 YOLO 标注结果"""
img = cv2.imread(str(img_path))
h, w = img.shape[:2]
with open(txt_path) as f:
line = f.readline().strip()
class_id, x, y, bw, bh = map(float, line.split())
# 转换回像素坐标
x1 = int((x - bw/2) * w)
y1 = int((y - bh/2) * h)
x2 = int((x + bw/2) * w)
y2 = int((y + bh/2) * h)
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow('Validation', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
常见问题处理
- 坐标越界问题:
- 转换后的 YOLO 坐标必须在 [0,1] 范围内
-
解决方案:在
convert_bbox函数中添加边界检查 -
图像路径编码问题:
- 某些系统上路径可能包含非 ASCII 字符
-
解决方案:使用
pathlib模块处理路径 -
内存优化:
- 处理大型数据集时避免一次性加载所有文件
- 解决方案:使用生成器逐文件处理
扩展思考
本方案可扩展适配其他检测框架:
- COCO 格式:
- 需要构建 JSON 格式的标注文件
-
维护图像 ID 和标注 ID 的映射关系
-
PASCAL VOC 格式:
- 保持 XML 结构但调整内容字段
-
可能需要处理多类别情况
-
TFRecord 格式:
- 需要实现 ProtoBuf 序列化
- 适合 TensorFlow 训练管道
通过修改输出模块,同一套解析逻辑可以支持多种输出格式,提高代码复用性。
总结
本文提供的自动化转换方案解决了 CCPD 数据集与 YOLO 框架的格式兼容问题。关键点包括:
- 精确的坐标转换算法
- 健壮的错误处理机制
- 直观的验证方法
- 可扩展的架构设计
该方案已在多个实际车牌检测项目中验证,能够显著减少数据预处理时间。开发者可以根据具体需求进一步定制脚本功能。
正文完
