☰
LabelMe JSON转YOLO格式:实例分割数据落地关键步骤
2026/9/28 16:53:09 网站建设 项目流程

简介:本资源是一个专为计算机视觉开发者设计的LabelMe标注数据格式转换工具,面向AI算法工程师、深度学习初学者及YOLO系列模型实践者,解决LabelMe生成的JSON标注与YOLOv5/YOLOv8训练所需文本格式不兼容的核心痛点。资源包共14个文件(6个Python脚本实现核心转换逻辑,3个YAML配置文件支持流程定制,另含requirements.txt依赖清单、LICENSE授权说明及代码质量规范配置),整体仅19KB,轻量易集成。已有375人下载学习,适用于目标检测与实例分割任务的数据预处理环节。用户可直接运行主脚本完成多边形(polygon)或边界框(bbox)两种模式的批量转换,输出严格符合YOLO标准的txt标注文件;项目采用模块化结构(src/目录清晰分离核心功能),附带完整测试用例与CI工作流配置(.github/workflows),兼顾开箱即用性与二次开发扩展性。

1. LabelMe JSON 转 YOLO 文本:不是“格式转换器”,而是分割数据集落地的临门一脚

你花三天用 LabelMe 精心标了 200 张工业缺陷图,每张图都用多边形抠出微米级裂纹轮廓——结果训练 YOLOv8-seg 时直接报错ValueError: invalid polygon points。不是模型不行,是你的 JSON 里存着[x1,y1,x2,y2,...]的顶点序列,而 YOLOv8 要的是归一化后的class_id x1 y1 x2 y2 ...单行文本,且所有坐标必须除以图像宽高、落在 [0,1] 区间内。Labelme2YOLO 就是干这个的:它不改标注逻辑,只做语义对齐——把人类画的多边形,翻译成模型能吞下去的“食物”。它专治三类人:刚从 LabelMe 切到 YOLO 的新手(避免手动写 for 循环解析 JSON)、做实例分割的产线工程师(polygon 模式必须开)、以及被 YOLOv5/v8 数据目录结构卡住的部署党(自动建images/labels/train/val/test三级目录)。这不是脚本,是数据流水线里那个沉默但绝不容错的质检员。


2. 为什么非得用 labelme2yolo?YOLO 官方工具链不香吗?

2.1 LabelMe 和 YOLO 的数据契约:表面都是“标注”,底层逻辑完全不同

LabelMe 的 JSON 是交互式标注产物:它记录的是用户在 GUI 中点击生成的像素级顶点(shapes[].points),附带标签名(shapes[].label)、图像路径(imagePath)和原始尺寸(imageHeight,imageWidth)。它的设计目标是“人能看懂、能回溯、能编辑”。而 YOLOv5/v8 的.txt标注文件是模型训练契约:每行代表一个目标,格式为class_id center_x center_y width height(bbox)或class_id x1 y1 x2 y2 ... xn yn(polygon),所有坐标必须归一化,且文件名必须与图像同名、放在labels/下对应子目录。二者之间没有协议兼容层——YOLO 官方ultralytics库自带的dataset.yaml解析器只认这种文本,不认 JSON;LabelMe 也从不承诺输出 YOLO 格式。硬要绕过 labelme2yolo,你得自己写解析器:读 JSON → 提取shapes→ 过滤空多边形 → 计算 bounding box 或保留 polygon → 归一化 → 写入.txt→ 同步创建目录结构 → 处理train/val/test划分。这活儿我干过三次,每次都在points数组长度奇偶判断上翻车。

2.2 labelme2yolo 的核心设计哲学:不做数据增强,只做无损映射

它不碰图像像素,不改标签名映射规则(你 JSON 里写crack,输出 txt 第一列就是0,前提是classes.txt里crack在第 0 行),不插值、不简化多边形(哪怕你画了 50 个点,它就原样输出 50 对归一化坐标)。关键参数只有三个:

  • --json_dir: 指向存放所有 LabelMe JSON 的根目录(注意:不是单个文件,是整个文件夹);
  • --save_dir: 输出 YOLO 目录的根路径(会自动建images/labels/train/val/test);
  • --split: 划分比例,如--split [0.7,0.2,0.1]对应 train/val/test。

它甚至不强制要求图像存在——JSON 里imagePath字段只是字符串,脚本只校验该路径是否可读(用于提取宽高),并不复制或重命名图像文件。这意味着你可以先跑转换,再统一搬运图像,适合离线环境或 NAS 存储场景。

2.3 与同类工具的本质差异:polygon 支持不是噱头,是分割任务的生死线

网上搜 “labelme to yolo” 会出现一堆 Python 脚本,90% 只支持 bbox。但 YOLOv8-seg 的训练必须用 polygon 格式——bbox 只能框出外接矩形,而分割需要精确轮廓。labelme2yolo 的--to-polygon参数直击要害:它把shapes[].points每个(x,y)对,用x / image_width,y / image_height归一化后,按顺序拼成一行,前面加class_id。例如一个 4 点矩形:

"points": [[10,20],[100,20],[100,80],[10,80]]

→ 归一化(假设图宽 200 高 100)→[0.05,0.2,0.5,0.2,0.5,0.8,0.05,0.8]→ 输出0 0.05 0.2 0.5 0.2 0.5 0.8 0.05 0.8。
而 bbox 模式会计算min_x,max_x,min_y,max_y,输出0 0.275 0.5 0.45 0.6(中心点+宽高)。选错模式,YOLOv8-seg 训练时 loss 会卡在 0.8 不下降——这是血泪经验。

提示:--to-polygon和--to-bbox互斥,不能同时开。YOLOv5 早期版本只认 bbox,YOLOv8 默认读 polygon,务必查清你用的 ultralytics 版本文档。


3. 从解压到训练:五步走通 labelme2yolo 全流程

3.1 环境准备:Python 3.8+ + PyTorch 1.12+,拒绝 pip install 失败玄学

不要用pip install labelme2yolo(PyPI 上无此包,那是别人 fork 的旧版)。必须克隆官方仓库:

git clone https://github.com/CVHub520/labelme2yolo.git cd labelme2yolo pip install -e .

-e参数是关键:它把当前目录作为可编辑安装源,后续改src/下代码能实时生效。依赖项在requirements.txt里已锁死:numpy>=1.21.0,opencv-python>=4.5.0,tqdm>=4.62.0。特别注意opencv-python—— 如果你系统里装了opencv-contrib-python,必须卸载它再重装opencv-python,否则cv2.imread()会静默失败(现象:脚本不报错但images/目录为空)。

3.2 数据组织:LabelMe 导出 JSON 的唯一正确姿势

LabelMe 导出时,必须勾选 “Save Image Data” 选项(即 JSON 里含imageData字段)。很多人为了省空间取消勾选,指望imagePath指向本地文件——但 labelme2yolo 默认只读imageData(base64 编码的图像),若为空则跳过该 JSON。修复方法:

# 在 src/labelme2yolo/converter.py 第 123 行附近,找到 load_image() 函数 # 把原逻辑: if data["imageData"]: img = img_b64_to_arr(data["imageData"]) else: # 原来这里直接 return None,改成: img_path = os.path.join(os.path.dirname(json_file), data["imagePath"]) img = cv2.imread(img_path)

这个补丁我打了三年,每次新同事入职都要教一遍。如果你的 JSON 确实没imageData,请先用 LabelMe 重新导出,或用labelme_json_to_dataset工具批量补全。

3.3 执行转换:命令行参数组合决定数据命运

假设你的 LabelMe JSON 全在./my_dataset/jsons/,想转成 YOLOv8 分割格式,划分 7:2:1,且标签名crack,scratch,dent对应 classes.txt:

labelme2yolo \ --json_dir ./my_dataset/jsons/ \ --save_dir ./yolo_dataset/ \ --split "[0.7,0.2,0.1]" \ --to-polygon \ --classes "./my_dataset/classes.txt"

classes.txt内容必须严格为:

crack scratch dent

注意:无空行、无空格、UTF-8 编码。脚本会按行号给crack=0,scratch=1,dent=2。如果 JSON 里有label不在 classes.txt 中(比如手误写了crak),脚本默认跳过该 shape 并打印 warning,不会报错中断——这是故意设计,避免单个错标毁掉整批数据。

3.4 输出验证:别信日志,亲手打开三个文件

转换完成后,检查./yolo_dataset/目录结构:

yolo_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── classes.txt

随机打开一个labels/train/xxx.txt:

  • 若用--to-polygon,行数应等于 JSON 中shapes数量,每行以class_id开头,后面是偶数个 0~1 的浮点数;
  • 若用--to-bbox,每行 5 个数:class_id cx cy w h;
  • 所有.txt文件名必须与images/train/xxx.jpg一一对应(扩展名可能为.png,脚本自动适配)。

再用cv2快速可视化验证:

import cv2 import numpy as np img = cv2.imread("./yolo_dataset/images/train/001.jpg") with open("./yolo_dataset/labels/train/001.txt") as f: for line in f: parts = list(map(float, line.strip().split())) cls_id = int(parts[0]) if len(parts) == 5: # bbox cx, cy, w, h = parts[1:] x1 = int((cx - w/2) * img.shape[1]) y1 = int((cy - h/2) * img.shape[0]) x2 = int((cx + w/2) * img.shape[1]) y2 = int((cy + h/2) * img.shape[0]) cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2) else: # polygon pts = np.array(parts[1:], dtype=np.float32).reshape(-1,2) pts[:,0] *= img.shape[1] pts[:,1] *= img.shape[0] cv2.polylines(img, [pts.astype(int)], True, (0,0,255), 2) cv2.imshow("check", img) cv2.waitKey(0)

看到绿色框或红色多边形精准套住目标,才算真正通过。

3.5 接入 YOLOv8 训练:yaml 文件怎么写才不踩坑

YOLOv8 要求dataset.yaml明确指定路径:

train: ../yolo_dataset/images/train val: ../yolo_dataset/images/val test: ../yolo_dataset/images/test nc: 3 names: ['crack', 'scratch', 'dent']

注意:train/val/test路径是相对于dataset.yaml文件位置的相对路径,不是绝对路径。如果你把 yaml 放在ultralytics/目录下,而yolo_dataset在上级目录,就必须写../yolo_dataset/...。常见错误是写成./yolo_dataset/...,导致No images found。另外,nc(number of classes)必须等于classes.txt行数,names必须与classes.txt顺序完全一致——YOLOv8 不读classes.txt,它只认 yaml 里的names。


4. 避坑指南:那些让转换失败的隐藏雷区

4.1 现象:脚本运行无报错,但labels/目录下全是空文件

原因:JSON 中shapes字段为空数组[],或points里只有 2 个点(YOLO 要求 polygon 至少 3 点)。LabelMe 允许用户画两点线段,但 YOLO 不认。
解决:在src/labelme2yolo/converter.py的convert_polygon函数中,增加校验:

if len(points) < 3: print(f"Warning: {json_file} shape {i} has less than 3 points, skipped") continue

4.2 现象:images/目录有图,labels/有 txt,但训练时报IndexError: list index out of range

原因:classes.txt比 JSON 中实际出现的标签少。例如 JSON 有crack,scratch,dent,但classes.txt只写了前两行。脚本会把dent映射为None,导致class_id为None,写入 txt 时崩溃。
解决:运行前用脚本扫描所有 JSON:

grep -o '"label": "[^"]*"' ./my_dataset/jsons/*.json | sort | uniq

确保输出标签与classes.txt完全匹配。

4.3 现象:多边形在可视化时严重偏移,像被拉伸或错位

原因:JSON 中imageHeight/imageWidth与实际图像尺寸不符。LabelMe 有时会把缩略图尺寸写进 JSON(尤其用 Web 版或旧版本导出)。
解决:强制用 OpenCV 读取真实尺寸:

# 替换 converter.py 中 get_image_size() 函数 def get_image_size(json_file): data = json.load(open(json_file)) if "imageData" in data and data["imageData"]: img = img_b64_to_arr(data["imageData"]) return img.shape[0], img.shape[1] # H, W else: img_path = os.path.join(os.path.dirname(json_file), data["imagePath"]) img = cv2.imread(img_path) return img.shape[0], img.shape[1]

4.4 现象:--split划分后,train/val/test子目录为空

原因:--split参数传入的是字符串"[0.7,0.2,0.1]",但代码里没做json.loads()解析,直接当字符串切片。
解决:在main.py的parse_args()后加:

if args.split: args.split = json.loads(args.split)

4.5 现象:Windows 下运行报OSError: [WinError 123],路径含中文或空格

原因:os.path.join()在 Windows 对长路径或特殊字符处理不稳。
解决:全局替换路径拼接为pathlib.Path:

from pathlib import Path json_dir = Path(args.json_dir) save_dir = Path(args.save_dir) for json_file in json_dir.rglob("*.json"): # 后续所有路径操作用 json_file.parent / "xxx" 形式

5. 进阶技巧:让 labelme2yolo 成为你数据流水线的齿轮

5.1 自动化 pipeline:用 shell 脚本串联标注-转换-训练

把转换嵌入 CI/CD,每次git push新 JSON 就触发训练:

#!/bin/bash # deploy.sh JSON_DIR="./data/new_labels" YOLO_DIR="./datasets/prod_v8" # 1. 清空旧数据 rm -rf "$YOLO_DIR" # 2. 转换(带错误捕获) if ! labelme2yolo \ --json_dir "$JSON_DIR" \ --save_dir "$YOLO_DIR" \ --split "[0.8,0.1,0.1]" \ --to-polygon \ --classes "./data/classes.txt"; then echo "Conversion failed!" >&2 exit 1 fi # 3. 生成 dataset.yaml cat > "$YOLO_DIR/dataset.yaml" << EOF train: ../datasets/prod_v8/images/train val: ../datasets/prod_v8/images/val test: ../datasets/prod_v8/images/test nc: $(wc -l < ./data/classes.txt) names: $(sed ':a;N;$!ba;s/\n/, /g' ./data/classes.txt | sed 's/^/[/;s/$/]/') EOF # 4. 启动训练(后台) nohup yolo train data="$YOLO_DIR/dataset.yaml" model=yolov8n-seg.pt epochs=100 > train.log 2>&1 & echo "Training started with PID $!"

这个脚本的关键是nohup+&,避免 SSH 断连中断训练;wc -l动态算nc;sed一行生成names数组——比手写 yaml 少出错。

5.2 多标签映射:当你的 LabelMe 标签名和 YOLO 类别名不一致

比如 LabelMe 里标scratched_panel,但 YOLO 要scratch。不用改 JSON,建label_map.json:

{ "scratched_panel": "scratch", "dented_body": "dent", "crack_on_glass": "crack" }

然后修改converter.py的get_class_id函数:

def get_class_id(label, class_names, label_map=None): if label_map and label in label_map: label = label_map[label] return class_names.index(label) if label in class_names else -1

调用时加参数--label-map ./label_map.json。这样业务侧标数据时用长名(防歧义),模型侧用短名(省显存),中间靠 map 桥接。

5.3 质量审计:转换后自动检测异常标注

在labels/生成后,跑一个质检脚本:

import glob import numpy as np def audit_labels(label_dir): errors = [] for txt in glob.glob(f"{label_dir}/*.txt"): with open(txt) as f: for i, line in enumerate(f): parts = list(map(float, line.strip().split())) if len(parts) < 5: errors.append(f"{txt}:{i} too few values") continue cls_id = int(parts[0]) if cls_id < 0 or cls_id > 2: # nc=3 errors.append(f"{txt}:{i} invalid class_id {cls_id}") if len(parts) % 2 == 0 and len(parts) > 5: # polygon mode if not all(0 <= x <= 1 for x in parts[1:]): errors.append(f"{txt}:{i} coord out of [0,1]") return errors print(audit_labels("./yolo_dataset/labels/train"))

把errors写入audit_report.txt,每天晨会前扫一眼——比等训练完发现 mAP 低再排查快十倍。

5.4 无缝对接 Ultralytics Hub:上传前预处理

Ultralytics Hub 要求images/和labels/同级,且dataset.yaml在根目录。但 labelme2yolo 输出的yolo_dataset/结构刚好符合。唯一要加的是README.md:

# YOLOv8 Segmentation Dataset - Source: LabelMe v5.8.3 - Classes: crack, scratch, dent - Total images: 1247 - Polygon points avg: 12.3/shape - Generated by labelme2yolo v1.2.0 on 2024-06-15

Hub 上传时选yolo_dataset/文件夹,它会自动识别结构。上传后 Hub 自动生成dataset_id,你在代码里直接引用:

from ultralytics import YOLO model = YOLO("yolov8n-seg.pt") model.train(data="https://universe.roboflow.com/your-workspace/your-dataset/dataset_id")

从此告别本地路径管理。

从那以后我每次新建项目,第一件事就是git clone https://github.com/CVHub520/labelme2yolo.git && cd labelme2yolo && pip install -e .,然后把deploy.sh和label_map.json模板扔进项目根目录。不是因为懒,而是知道:在 CV 项目里,数据格式转换的稳定性,比模型调参更值得投入防御性编程。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询