资讯动态

告别手动转换!用Python脚本一键搞定LabelImg的YOLO txt与VOC xml格式互转

发布时间:2026/9/12 4:36:47 来源:尧图企业网站定制
告别手动转换用Python脚本一键搞定LabelImg的YOLO txt与VOC xml格式互转数据标注是计算机视觉项目中最耗时但至关重要的环节。当你用LabelImg完成标注后却发现不同框架需要不同格式——YOLO要求txtVOC需要xml。手动转换不仅效率低下还容易出错。本文将带你用Python脚本实现两种格式的智能互转彻底解决这个痛点。1. 理解标注格式的本质差异1.1 YOLO格式的坐标体系YOLO使用的.txt文件采用相对坐标表示法每行对应一个标注对象格式为class_id x_center y_center width height其中所有数值都是相对于图像宽高的比例值0-1之间。例如0 0.435 0.512 0.120 0.300表示类别ID为0的对象中心点位于图像宽度的43.5%和高度的51.2%处宽度占图像总宽的12%高度占30%1.2 VOC格式的XML结构PASCAL VOC的.xml文件则采用绝对坐标包含完整的元信息annotation size width1920/width height1080/height /size object nameperson/name bndbox xmin500/xmin ymin300/ymin xmax700/xmax ymax800/ymax /bndbox /object /annotation关键区别在于使用具体像素值而非比例包含完整的图像路径和尺寸信息支持更多属性如difficult、truncated等2. 转换核心算法解析2.1 YOLO转VOC的数学原理转换核心是将相对坐标转为绝对坐标计算公式为xmin (x_center - width/2) * image_width xmax (x_center width/2) * image_width ymin (y_center - height/2) * image_height ymax (y_center height/2) * image_heightPython实现关键代码def yolo_to_voc(x_center, y_center, width, height, img_w, img_h): xmin int((x_center - width/2) * img_w) xmax int((x_center width/2) * img_w) ymin int((y_center - height/2) * img_h) ymax int((y_center height/2) * img_h) return xmin, ymin, xmax, ymax2.2 VOC转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对应代码实现def voc_to_yolo(xmin, ymin, xmax, ymax, img_w, img_h): x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h return x_center, y_center, width, height3. 完整脚本实现与优化3.1 批量转换脚本架构建议采用以下目录结构convert_tool/ ├── input/ │ ├── images/ # 原始图片 │ ├── yolo_txt/ # YOLO格式标注 │ └── voc_xml/ # VOC格式标注 ├── output/ ├── classes.txt # 类别定义文件 └── converter.py # 转换脚本3.2 增强版转换脚本import os import cv2 import xml.etree.ElementTree as ET from tqdm import tqdm # 进度条显示 class LabelConverter: def __init__(self, class_file): with open(class_file) as f: self.classes [line.strip() for line in f.readlines()] def txt_to_xml(self, txt_path, img_path, output_dir): YOLO txt转VOC xml img cv2.imread(img_path) h, w img.shape[:2] xml_content [] xml_content.append(annotation) xml_content.append(ffilename{os.path.basename(img_path)}/filename) xml_content.append(size) xml_content.append(fwidth{w}/width) xml_content.append(fheight{h}/height) xml_content.append(depth3/depth) xml_content.append(/size) with open(txt_path) as f: for line in f: class_id, xc, yc, bw, bh map(float, line.split()) xmin, ymin, xmax, ymax self._yolo_to_voc(xc, yc, bw, bh, w, h) xml_content.append(object) xml_content.append(fname{self.classes[int(class_id)]}/name) xml_content.append(bndbox) xml_content.append(fxmin{xmin}/xmin) xml_content.append(fymin{ymin}/ymin) xml_content.append(fxmax{xmax}/xmax) xml_content.append(fymax{ymax}/ymax) xml_content.append(/bndbox) xml_content.append(/object) xml_content.append(/annotation) output_path os.path.join(output_dir, os.path.splitext(os.path.basename(txt_path))[0] .xml) with open(output_path, w) as f: f.write(\n.join(xml_content)) def batch_convert(self, input_dir, output_dir, img_dir, modetxt2xml): 批量转换入口 os.makedirs(output_dir, exist_okTrue) if mode txt2xml: for txt_file in tqdm(os.listdir(input_dir)): if txt_file.endswith(.txt): img_name os.path.splitext(txt_file)[0] .jpg self.txt_to_xml( os.path.join(input_dir, txt_file), os.path.join(img_dir, img_name), output_dir ) elif mode xml2txt: # 实现XML到TXT的转换逻辑 pass staticmethod def _yolo_to_voc(xc, yc, bw, bh, img_w, img_h): 坐标转换核心方法 xmin int((xc - bw/2) * img_w) xmax int((xc bw/2) * img_w) ymin int((yc - bh/2) * img_h) ymax int((yc bh/2) * img_h) return max(0, xmin), max(0, ymin), min(img_w, xmax), min(img_h, ymax)提示脚本中添加了边界检查确保转换后的坐标不会超出图像范围4. 实战问题排查指南4.1 常见错误及解决方案错误现象可能原因解决方法转换后坐标异常图像尺寸读取错误使用OpenCV的imread检查图像加载类别ID越界classes.txt与标注不匹配确认类别文件与标注使用相同顺序文件路径错误相对路径处理不当使用os.path.abspath转为绝对路径内存不足大尺寸图像批量处理分批次处理或优化图像加载方式4.2 高级调试技巧可视化验证转换后使用以下代码检查标注是否准确import matplotlib.pyplot as plt import matplotlib.patches as patches def plot_boxes(img_path, xml_path): img plt.imread(img_path) fig, ax plt.subplots(1) ax.imshow(img) tree ET.parse(xml_path) for obj in tree.findall(object): box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) rect patches.Rectangle( (xmin, ymin), xmax-xmin, ymax-ymin, linewidth2, edgecolorr, facecolornone) ax.add_patch(rect) plt.show()性能优化处理大规模数据集时使用多进程加速from multiprocessing import Pool def parallel_convert(args): converter, txt, img, out args converter.txt_to_xml(txt, img, out) with Pool(4) as p: # 4个进程 p.map(parallel_convert, task_list)异常处理增强在转换函数中添加完整性检查def safe_convert(txt_path, img_path, output_dir): try: if not os.path.exists(img_path): raise FileNotFoundError(fMissing image: {img_path}) # 检查标注文件非空 if os.path.getsize(txt_path) 0: print(fWarning: Empty annotation {txt_path}) return # 执行转换 self.txt_to_xml(txt_path, img_path, output_dir) except Exception as e: print(fError processing {txt_path}: {str(e)}) with open(conversion_errors.log, a) as f: f.write(f{txt_path}\t{str(e)}\n)5. 扩展应用场景5.1 与其他工具的集成将转换脚本集成到标注流水线中LabelImg插件通过修改LabelImg源码在保存时自动生成两种格式CI/CD流程在模型训练前自动统一标注格式数据增强管道格式转换与图像增强同步进行5.2 支持更多格式扩展脚本以支持更多流行格式COCO JSON适用于MMDetection等框架TFRecordTensorFlow标准格式CSV简化版表格格式添加新格式的转换只需实现对应的坐标计算逻辑例如COCO格式def to_coco(self, txt_path, img_path, img_id, ann_id): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path) as f: annotations [] for line in f: class_id, xc, yc, bw, bh map(float, line.split()) xmin, ymin, xmax, ymax self._yolo_to_voc(xc, yc, bw, bh, w, h) annotations.append({ id: ann_id[0], image_id: img_id, category_id: int(class_id), bbox: [xmin, ymin, xmax-xmin, ymax-ymin], area: (xmax-xmin)*(ymax-ymin), iscrowd: 0 }) ann_id[0] 1 return { images: [{ id: img_id, width: w, height: h, file_name: os.path.basename(img_path) }], annotations: annotations, categories: [ {id: i, name: name} for i, name in enumerate(self.classes) ] }

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价