☰
labelme图像分割标注全流程:安装、格式转换与避坑指南
2026/10/15 21:40:50 网站建设 项目流程

简介:这份资源是面向计算机视觉、机器学习初学者及数据标注从业者的 Labelme 安装与使用教程文档,重点解决图像分割与标注工具的环境搭建和上手问题。压缩包内共 1 个 docx 文件,约 14KB,以图文步骤形式组织内容,便于按需查阅和离线学习。教程围绕 Anaconda 环境准备、Labelme 虚拟环境创建与安装、工具启动及 JSON 标注文件转换等环节展开,区分了 Python2 与 Python3 两套安装指令,并给出 labelme_json_to_dataset 生成可视化结果的具体用法,能帮助读者快速完成从环境配置到标注结果查看的完整流程。目前已有 15225 人学习下载,适合需要掌握图像分割标注工具、为后续模型训练准备数据集的读者参考。

1. 标注工具选型:为什么 labelme 依然是图像分割任务的首选

如果你正在做图像分割、实例分割或者任何需要像素级标注的深度学习项目,大概率绕不开一个名字——labelme。这不是一个新鲜工具,但它在数据标注这个环节的江湖地位,至今没有被完全替代。我见过不少团队一开始图省事用在线标注平台,标了三千张图之后发现导出格式不兼容、多人协作版本混乱、精细边缘根本画不准,最后又灰溜溜回到本地工具重新标。这种翻车经历在标注圈子里太常见了。

labelme 解决的核心问题很明确:让你在本地用鼠标逐点勾勒多边形,把图像里每个目标物体的轮廓精确框出来,然后导出成 JSON 格式的标注文件,再转成 COCO、VOC、YOLO 等训练框架能直接吃的格式。它适合谁?适合做语义分割、实例分割的研究者和工程师,适合需要自定义标注类别、需要离线操作、需要对标注精度有完全控制权的场景。它不适合谁?不适合只想画矩形框做目标检测的人——那用 labelImg 更省事;也不适合完全不想碰命令行的纯业务人员。

这篇文章我会从安装、标注、格式转换、踩坑排查一路讲到进阶技巧,每一步都给出可复现的命令和参数说明。你跟着走一遍,基本能把 labelme 这套流程跑通,并且知道哪里容易出问题、怎么绕过去。

2. 安装与环境配置:三条路,选哪条不后悔

2.1 三种安装方式的取舍逻辑

labelme 的安装方式主要有三种:pip 直接装、conda 环境装、源码克隆装。很多人上来就pip install labelme,装完发现启动报错,然后开始怀疑人生。问题往往不在 labelme 本身,而在 Python 环境里 GUI 库的版本冲突。

我一般会先问自己三个问题:这台机器上有没有其他 Python 项目?需不需要固定 Python 版本?要不要改 labelme 源码?如果答案是“有”“需要”“要改”,那就老老实实建一个独立 conda 环境。如果只是临时用一下,pip 装也不是不行,但要做好隔离。

先看 pip 方式,最直接:

# 建议先升级 pip,避免依赖解析出问题 pip install --upgrade pip # 安装 labelme,指定版本可以避免自动拉到不兼容的新版 pip install labelme==5.4.1 # 验证安装是否成功,能打印出版本号说明基本没问题 labelme --version

逻辑说明:pip install labelme会自动拉取 PyQt5、numpy、Pillow 等依赖。指定版本号是因为 labelme 不同大版本之间配置文件格式有差异,团队协作时最好统一。labelme --version是最快的验证方式,如果这条命令报“command not found”,说明 Scripts 目录没在 PATH 里,需要手动加。

再看 conda 方式,适合需要环境隔离的场景:

# 创建独立环境,Python 版本建议 3.8 到 3.10 conda create -n labelme_env python=3.9 # 激活环境 conda activate labelme_env # 在环境内安装 labelme pip install labelme==5.4.1 # 验证 labelme --version

参数说明:Python 版本不要选太新的,3.11 以上某些 PyQt5 轮子还没跟上,容易编译失败。3.9 是我实测最稳的版本。conda 环境的好处是,即使你系统里已经有别的 PyQt 项目,也不会互相干扰。

源码方式适合需要改界面或加自定义功能的场景:

# 克隆仓库到本地 git clone https://github.com/wkentaro/labelme.git # 进入目录 cd labelme # 以可编辑模式安装,这样改源码后不用重新安装 pip install -e . # 启动验证 labelme --version

逻辑说明:-e参数是 editable 模式,安装后你修改源码里的 Python 文件,下次启动就会生效。如果你只是想用,不建议走这条路,因为依赖管理会更麻烦。

2.2 启动参数与常用配置

装好之后,直接敲labelme就会弹出图形界面。但实际用的时候,我建议带上几个参数,能省很多事:

# 指定标注文件保存目录,避免默认存到图片同级目录造成混乱 labelme --output ./annotations # 指定类别标签文件,每行一个类别名,启动后可以直接选 labelme --labels ./labels.txt # 关闭自动保存,防止误操作覆盖已有标注 labelme --nosave # 同时指定图片目录和输出目录 labelme ./images --output ./annotations --labels ./labels.txt

参数说明:--output把 JSON 文件统一放到一个目录,后期批量转换时不用满文件夹找。--labels需要你提前建一个文本文件,比如:

person car tree building

每行一个类别,启动后右侧标签列表就会显示这些,点一下就能切换,不用每次手输。--nosave在演示或试标时有用,正式标注别开。

提示:如果你在远程服务器上标注,没有图形界面,labelme 是跑不起来的。常见做法是在本地标注,然后把 JSON 和图片一起传到服务器训练。也有团队用 X11 转发,但延迟高,标精细边缘会让人崩溃。

3. 标注操作全流程:从打开图片到导出 JSON

3.1 多边形标注的实操步骤

打开 labelme 后,界面左侧是图片列表,中间是画布,右侧是标签和文件列表。标注一张图的完整流程如下:

第一步,打开图片目录。点击左侧 “Open Dir”,选择存放图片的文件夹。labelme 会自动列出所有图片文件,支持 jpg、png、bmp 等常见格式。

第二步,创建多边形。点击左侧工具栏的 “Create Polygons” 按钮,或者按快捷键Ctrl+N。然后在目标物体边缘依次点击,每点一下生成一个顶点。画完一圈后,点回第一个顶点闭合,或者按回车键闭合。

第三步,填写标签。闭合后会弹出一个输入框,输入类别名,比如 “person”。如果之前用--labels指定了标签文件,这里会变成下拉选择。

第四步,保存。按Ctrl+S保存,会在输出目录生成一个同名的 JSON 文件。JSON 里记录了图片路径、每个多边形的顶点坐标、标签名等信息。

第五步,切换下一张。按D键下一张,A键上一张。标完一批后,检查一下 JSON 文件数量是否和图片数量一致。

这里有个血泪经验:标注时尽量放大图片再画点。labelme 支持鼠标滚轮缩放,按住滚轮拖动画面。精细边缘如果不放大,顶点位置偏差几个像素,训练出来的分割 mask 边缘就会毛毛躁躁。

3.2 JSON 文件结构解析

理解 JSON 结构很重要,因为后面格式转换、数据校验都要跟它打交道。一个典型的 labelme JSON 长这样:

{ "version": "5.4.1", "flags": {}, "shapes": [ { "label": "person", "points": [[100, 150], [200, 150], [200, 300], [100, 300]], "group_id": null, "shape_type": "polygon", "flags": {} } ], "imagePath": "example.jpg", "imageData": null, "imageHeight": 480, "imageWidth": 640 }

字段说明:shapes是核心数组,每个元素代表一个标注对象。label是类别名,points是多边形顶点坐标列表,坐标是[x, y]格式,原点在左上角。shape_type常见值有polygon、rectangle、circle、line、point,做分割一般用polygon。imageData如果是 null,说明 JSON 里没嵌图片的 base64 数据,只存了路径,这样文件小,但移动图片后路径可能失效。

注意:如果你在标注时不小心改了图片文件名或移动了图片位置,JSON 里的imagePath就对不上了。批量转换前一定要检查路径一致性,否则转换脚本会报“找不到图片”。

3.3 批量转换:从 JSON 到 COCO 格式

labelme 自带转换脚本,但很多人不知道在哪、怎么用。源码仓库的examples目录下有labelme2coco.py,但如果你是用 pip 装的,本地没有这个文件。常见做法是手动下载这个脚本,或者用社区维护的版本。

我一般会自己写一个精简版转换脚本,逻辑透明,出问题好排查:

import json import os import numpy as np from PIL import Image from pycocotools import mask as mask_util def labelme_to_coco(json_dir, image_dir, output_file, categories): """ 将 labelme JSON 批量转换为 COCO 格式 json_dir: JSON 文件目录 image_dir: 图片目录 output_file: 输出 COCO JSON 路径 categories: 类别列表,如 ['person', 'car'] """ coco = { "images": [], "annotations": [], "categories": [] } # 构建类别映射 cat_name_to_id = {} for idx, name in enumerate(categories, start=1): coco["categories"].append({ "id": idx, "name": name, "supercategory": "none" }) cat_name_to_id[name] = idx ann_id = 1 img_id = 1 for json_file in sorted(os.listdir(json_dir)): if not json_file.endswith(".json"): continue json_path = os.path.join(json_dir, json_file) with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) img_name = data["imagePath"] img_path = os.path.join(image_dir, img_name) # 读取图片尺寸 with Image.open(img_path) as img: width, height = img.size # 添加图片信息 coco["images"].append({ "id": img_id, "file_name": img_name, "width": width, "height": height }) # 遍历每个标注形状 for shape in data["shapes"]: label = shape["label"] if label not in cat_name_to_id: continue points = shape["points"] # 计算多边形面积 x_coords = [p[0] for p in points] y_coords = [p[1] for p in points] area = 0.5 * abs( sum(x_coords[i] * y_coords[(i+1) % len(points)] - x_coords[(i+1) % len(points)] * y_coords[i] for i in range(len(points))) ) # 计算边界框 x_min = min(x_coords) y_min = min(y_coords) bbox_w = max(x_coords) - x_min bbox_h = max(y_coords) - y_min # 生成 RLE 分割掩码 rle = mask_util.frPyObjects([points], height, width) rle = mask_util.merge(rle) coco["annotations"].append({ "id": ann_id, "image_id": img_id, "category_id": cat_name_to_id[label], "segmentation": rle, "area": float(area), "bbox": [x_min, y_min, bbox_w, bbox_h], "iscrowd": 0 }) ann_id += 1 img_id += 1 with open(output_file, "w", encoding="utf-8") as f: json.dump(coco, f, ensure_ascii=False, indent=2) print(f"转换完成:{img_id-1} 张图片,{ann_id-1} 个标注") # 调用示例 labelme_to_coco( json_dir="./annotations", image_dir="./images", output_file="./coco_annotations.json", categories=["person", "car", "tree"] )

逻辑说明:这个脚本遍历 JSON 目录,逐个读取标注文件,把每个多边形的顶点坐标转成 COCO 要求的 RLE 格式。mask_util.frPyObjects把多边形转成 RLE,mask_util.merge合并同一张图里的多个多边形。面积用鞋带公式计算,边界框取顶点坐标的极值。

参数说明:categories列表必须和标注时的标签名完全一致,大小写敏感。如果某个 JSON 里的标签不在列表里,脚本会跳过该标注,不会报错,但你要留意输出里的标注数量是否对得上。iscrowd设为 0 表示单个实例,如果是密集人群可以设为 1,但大多数分割任务用 0。

4. 避坑与排查:标注路上最容易翻车的五个地方

4.1 启动报错 “qt.qpa.plugin: Could not load the Qt platform plugin”

现象:命令行敲labelme后,界面没出来,终端打印一堆 Qt 插件加载失败的信息。

原因:PyQt5 的插件路径和系统环境变量冲突,常见于 conda 环境和系统 Python 混用的情况。

解决:先确认当前 Python 环境是哪个,which python看一下。如果是 conda 环境,尝试重装 PyQt5:

pip uninstall PyQt5 PyQt5-Qt5 PyQt5-sip pip install PyQt5==5.15.9

如果还不行,设置环境变量强制指定插件路径:

export QT_QPA_PLATFORM_PLUGIN_PATH=$(python -c "import PyQt5; print(PyQt5.__path__[0])")/Qt5/plugins/platforms

这个坑我踩过三次,每次都是环境混用导致的。最彻底的办法是新建一个干净的 conda 环境,只装 labelme,不装其他 GUI 库。

4.2 标注文件丢失或 JSON 内容为空

现象:标了半天,打开 JSON 发现shapes是空数组,或者根本找不到 JSON 文件。

原因:两种情况。一是保存时没按Ctrl+S,直接关了窗口;二是--output目录没写对,JSON 存到了别的地方。

解决:养成标完一张就按Ctrl+S的习惯。启动时明确指定--output绝对路径,不要用相对路径。如果已经丢了,看看图片同级目录有没有同名 JSON,labelme 默认会存在图片旁边。

4.3 多边形顶点太多导致训练显存爆炸

现象:训练分割模型时,第一轮就 OOM,显存不够。

原因:标注时顶点太密,一个物体画了几百个点,转成 mask 后分辨率极高,训练时 batch 稍微大一点就撑爆。

解决:标注时控制顶点数量,一般物体 20 到 50 个点足够描述轮廓。如果已经标完了,可以在转换脚本里做顶点抽稀:

def simplify_polygon(points, epsilon=2.0): """ 使用 Douglas-Peucker 算法简化多边形 epsilon 越大,简化程度越高 """ import cv2 pts = np.array(points, dtype=np.float32).reshape(-1, 1, 2) simplified = cv2.approxPolyDP(pts, epsilon, True) return simplified.reshape(-1, 2).tolist()

参数说明:epsilon控制简化力度,2.0 表示偏离原轮廓 2 像素以内的点会被合并。对于 512x512 的图片,2.0 到 5.0 比较合适。简化后要检查一下轮廓是否还贴合目标边缘。

4.4 中文标签导致转换脚本报编码错误

现象:标签名用了中文,转换 COCO 时json.dump报 UnicodeDecodeError。

原因:Python 2 遗留的编码问题,或者文件打开时没指定encoding="utf-8"。

解决:所有文件读写都显式指定encoding="utf-8"。json.dump加ensure_ascii=False。如果还是报错,检查 JSON 文件本身是不是用 UTF-8 保存的,有些编辑器默认 GBK。

4.5 多人协作时标签命名不一致

现象:两个人标同一批图,一个用 “person”,一个用 “Person”,合并后类别翻倍。

原因:没有统一的标签规范,--labels文件没共享。

解决:团队协作前先定一个labels.txt,所有人启动时都带--labels参数。转换脚本里加一层校验,发现不在列表里的标签就打印警告:

if label not in cat_name_to_id: print(f"警告:{json_file} 中发现未知标签 '{label}',已跳过") continue

提示:标签命名建议全小写、用下划线连接,比如traffic_light、fire_hydrant。不要用空格和特殊字符,后期转 YOLO 格式时不用再处理。

5. 进阶技巧:用 Python 脚本批量校验与可视化标注结果

标完一批数据,直接扔进训练脚本是有风险的。我一般会先跑两个检查:一是校验 JSON 和图片是否一一对应,二是随机抽样可视化,肉眼看看标注质量。

先写一个校验脚本:

import json import os def validate_annotations(json_dir, image_dir): """ 校验标注文件与图片的对应关系 返回:缺失图片的 JSON 列表、缺失 JSON 的图片列表、空标注列表 """ json_files = set(f.replace(".json", "") for f in os.listdir(json_dir) if f.endswith(".json")) image_files = set(os.path.splitext(f)[0] for f in os.listdir(image_dir) if f.lower().endswith((".jpg", ".jpeg", ".png", ".bmp"))) missing_images = [] empty_shapes = [] for jf in json_files: json_path = os.path.join(json_dir, jf + ".json") with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) img_name = os.path.splitext(data["imagePath"])[0] if img_name not in image_files: missing_images.append(jf) if len(data.get("shapes", [])) == 0: empty_shapes.append(jf) missing_jsons = image_files - json_files print(f"JSON 总数:{len(json_files)}") print(f"图片总数:{len(image_files)}") print(f"缺失图片的 JSON:{len(missing_images)} 个") if missing_images: print(" " + ", ".join(missing_images[:10])) print(f"缺失 JSON 的图片:{len(missing_jsons)} 个") if missing_jsons: print(" " + ", ".join(list(missing_jsons)[:10])) print(f"空标注 JSON:{len(empty_shapes)} 个") if empty_shapes: print(" " + ", ".join(empty_shapes[:10])) return missing_images, missing_jsons, empty_shapes # 调用 validate_annotations("./annotations", "./images")

逻辑说明:这个脚本做三件事——检查每个 JSON 对应的图片是否存在、检查每张图片是否有对应 JSON、检查是否有空标注。输出前十个问题文件,方便快速定位。

参数说明:json_dir和image_dir传绝对路径更稳。如果图片格式有.JPG大写后缀,脚本里已经用lower()处理了。

再写一个可视化脚本,随机抽几张图,把标注轮廓画上去:

import json import os import random import cv2 import numpy as np def visualize_annotations(json_dir, image_dir, output_dir, num_samples=5): """ 随机抽样可视化标注结果 """ os.makedirs(output_dir, exist_ok=True) json_files = [f for f in os.listdir(json_dir) if f.endswith(".json")] samples = random.sample(json_files, min(num_samples, len(json_files))) # 为每个类别分配固定颜色 colors = {} for jf in samples: with open(os.path.join(json_dir, jf), "r", encoding="utf-8") as f: data = json.load(f) img_path = os.path.join(image_dir, data["imagePath"]) img = cv2.imread(img_path) if img is None: print(f"无法读取图片:{img_path}") continue for shape in data["shapes"]: label = shape["label"] if label not in colors: colors[label] = tuple(random.randint(50, 255) for _ in range(3)) pts = np.array(shape["points"], dtype=np.int32) cv2.polylines(img, [pts], True, colors[label], 2) # 在第一个顶点附近写标签 cv2.putText(img, label, tuple(pts[0]), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[label], 2) out_path = os.path.join(output_dir, "vis_" + jf.replace(".json", ".jpg")) cv2.imwrite(out_path, img) print(f"已保存:{out_path}") # 调用 visualize_annotations("./annotations", "./images", "./vis_output", num_samples=8)

逻辑说明:随机抽 8 张图,把每个多边形的轮廓用彩色线画出来,标签写在第一个顶点旁边。颜色按类别分配,同一类别颜色一致,方便检查类别是否标错。

参数说明:num_samples根据数据集大小调整,一般抽 5 到 10 张就能看出问题。cv2.polylines的线宽设为 2,太细看不清,太粗遮边缘。

这两个脚本跑完,基本能排除 90% 的数据问题。剩下的就是训练时看 loss 曲线和验证集效果了。

我自己的习惯是:每标完 500 张图,就跑一次校验和可视化,发现问题当场返工,绝不拖到训练阶段。标注这活儿,后悔药最贵,前期多花十分钟检查,后期省十小时排查。希望帮到你。

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

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

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

立即咨询