如何使用labelme2voc.py将LabelMe JSON数据转换为Pascal VOC格式?
解决LabelMe标注转Pascal VOC格式的方案
一、正确使用labelme2voc.py的步骤
先确认你已经通过pip/conda正确安装了labelme,然后按以下步骤操作:
- 整理数据集结构:把所有标注好的
.json文件和对应的图片放在同一个文件夹(比如命名为labelme_raw),确保每个json和对应的图片文件名完全一致(仅后缀不同)。 - 创建类别定义文件:新建一个
labels.txt,第一行固定写_background_,后续每行写一个你的类别,示例:
注意类别名称要和你LabelMe标注时用的完全一致,不要出现中文或特殊字符。_background_ class1 class2 class3 class4 - 执行转换命令:打开终端,激活labelme所在的环境(如果用conda的话),运行以下命令:
其中labelme2voc.py labelme_raw/ output_voc --labels labels.txtlabelme_raw/是你的原始数据文件夹路径,output_voc是生成VOC格式数据的目标文件夹,--labels指定刚才创建的类别文件。 - 排查常见报错:
- 若提示找不到文件,检查路径是否正确,文件夹是否存在。
- 若出现编码错误,检查类别名和文件名是否有非ASCII字符,全部换成英文即可。
- 标注数量超过50个不会导致转换失败,不用为此担心,报错大概率是路径或类别配置问题。
二、其他可行转换方法
1. 手动编写Python脚本
如果labelme2voc.py始终无法正常工作,可以自己写脚本处理,核心逻辑如下:
- 遍历所有
.json文件,解析shapes字段获取每个目标的类别和边界框坐标(注意LabelMe的坐标是[x1,y1,x2,y2]格式,直接对应VOC的xmin/ymin/xmax/ymax)。 - 按照Pascal VOC的XML格式生成标注文件,XML的基础结构如下:
<annotation> <folder>JPEGImages</folder> <filename>sample.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>class1</name> <bndbox> <xmin>100</xmin> <ymin>200</ymin> <xmax>300</xmax> <ymax>400</ymax> </bndbox> </object> <!-- 其他标注目标依次添加 --> </annotation> - 同时将图片复制到VOC结构的
JPEGImages目录,标注文件放到Annotations目录,最后生成ImageSets/Main下的训练/测试划分文件。
2. 调用LabelMe API进行转换
直接用labelme的内置模块解析标注数据,灵活性更高,示例代码片段:
import json import os import cv2 from xml.etree.ElementTree import Element, SubElement, tostring from xml.dom.minidom import parseString def labelme_json_to_voc(json_file, voc_anno_dir, voc_img_dir, labels): # 读取json数据 with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) # 处理图片 img_name = os.path.basename(json_file).replace('.json', '.jpg') img_path = os.path.join(os.path.dirname(json_file), img_name) img = cv2.imread(img_path) h, w, _ = img.shape # 保存图片到VOC目录 cv2.imwrite(os.path.join(voc_img_dir, img_name), img) # 生成XML root = Element('annotation') SubElement(root, 'folder').text = 'JPEGImages' SubElement(root, 'filename').text = img_name size = SubElement(root, 'size') SubElement(size, 'width').text = str(w) SubElement(size, 'height').text = str(h) SubElement(size, 'depth').text = '3' # 遍历每个标注目标 for shape in data['shapes']: cls = shape['label'] if cls not in labels: continue points = shape['points'] xmin = int(min(p[0] for p in points)) ymin = int(min(p[1] for p in points)) xmax = int(max(p[0] for p in points)) ymax = int(max(p[1] for p in points)) obj = SubElement(root, 'object') SubElement(obj, 'name').text = cls bndbox = SubElement(obj, 'bndbox') SubElement(bndbox, 'xmin').text = str(xmin) SubElement(bndbox, 'ymin').text = str(ymin) SubElement(bndbox, 'xmax').text = str(xmax) SubElement(bndbox, 'ymax').text = str(ymax) # 格式化XML并保存 xml_str = parseString(tostring(root)).toprettyxml(indent=' ') xml_file = os.path.join(voc_anno_dir, img_name.replace('.jpg', '.xml')) with open(xml_file, 'w', encoding='utf-8') as f: f.write(xml_str) # 使用示例 labels = ['_background_', 'class1', 'class2', 'class3', 'class4'] os.makedirs('output_voc/JPEGImages', exist_ok=True) os.makedirs('output_voc/Annotations', exist_ok=True) for json_file in os.listdir('labelme_raw'): if json_file.endswith('.json'): labelme_json_to_voc(os.path.join('labelme_raw', json_file), 'output_voc/Annotations', 'output_voc/JPEGImages', labels)
这个脚本可以直接处理你的数据集,且能应对单张图50+标注的场景。
内容的提问来源于stack exchange,提问作者What
相关产品推荐
相关产品推荐

