news 2026/9/28 1:37:44

LabelMe标注工具实战指南:从安装部署到数据集转换与避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LabelMe标注工具实战指南:从安装部署到数据集转换与避坑

简介:LabelMe是MIT开发的开源图像标注工具,支持语义分割、目标检测与关键点检测。这份源码包面向计算机视觉从业者与标注团队,可快速搭建本地标注环境,解决深度学习数据集的创建与转换难题。资源共251个文件,约12.4MB,含45个Python脚本、23个JSON标注示例、75张JPEG与48张PNG图像素材及配置文件,覆盖标注、格式转换到模型训练数据准备各环节。目前已有1073人学习。借助包内脚本,读者可掌握安装配置、各类标注操作,并将JSON转换为VOC或COCO格式,为Mask R-CNN、YOLO等模型训练打牢数据基础。

1. 深度学习数据集从哪来:LabelMe 是绕不开的那个标注工具

做深度学习数据集的人,都知道标注环节有多磨人。图像分类还能靠爬虫批量打标,一到语义分割、目标检测、关键点检测这类任务,就得靠工具一笔一画地把 ground truth 画出来。LabelMe 就是干这个的:一个开源图像标注工具,用 Python 写,装好之后本地起一个窗口,鼠标拖几下就能产出结构化的标注结果。它不挑任务类型,多边形、矩形框、关键点都能标,最后统一落成 JSON 文件,再转成 VOC 或 COCO 喂给训练框架。适合的人群也明确:正在给 YOLO、Mask R-CNN、U-Net 准备训练数据的研究生和工程师,以及小团队里需要自己造数据集的算法岗。我的建议是,如果手头标注需求不复杂,别急着写标注平台,先把 LabelMe 用熟。

2. 安装部署与工具链选型:Python 环境、pyqt5 与 JSON 输出

2.1 为什么要选离线本地版而不是在线工具

LabelMe 这个名字最早来自 MIT 的在线标注网站,但现在 GitHub 上维护的labelme项目已经是一个完整的桌面应用,仓库里带了 Dockerfile、图标资源和源码包。选择本地版的核心原因有三个:一是图像数据往往涉及隐私,尤其是医疗影像、工业质检这类场景,不能传到公网;二是本地版标注过程完全不依赖网络,标注一半断网也不影响;三是源码包可以二次修改,团队想加自定义快捷键、批量重命名、字段校验都很方便。

安装之前的选型要考虑 Python 环境。这个项目从 4.x 到 5.x 的大版本我都跑过,行为上大同小异,5.x 的界面更现代一点。无论哪个版本,底层依赖都绕不开 PyQt5 和 pyqt5-sip,这两个包是界面和 Python 桥接层的核心。我这里建议用虚拟环境装,不要直接怼进系统 Python,否则后面升级依赖时会和后装的其他深度学习库打架。

2.2 安装与启动:源码包和 pip 两条路

最简单的方式是用 pip 直接安装,国内网络环境下加清华镜像源会快很多:

python -m venv labelme_env source labelme_env/bin/activate # Windows 下改为 labelme_env\Scripts\activate pip install labelme -i https://pypi.tuna.tsinghua.edu.cn/simple

上面这个流程我一般会拆成两步来看:第一步创建虚拟环境是为了隔离依赖,第二步指定清华源是为了规避默认源下载慢的问题。如果后续要改源码或者想读脚本逻辑,再从 GitHub 把源码包拉下来,解压后进目录执行pip install -r requirements.txt,效果等价,区别在于源码包目录里能看到labelme2voc.py、labelme2coco.py这些转换工具,而 pip 只把labelme主程序装进环境,转换脚本要单独找。

装完验证一下能不能正常拉起界面:

labelme --version labelme

第一条命令确认安装成功,第二条会弹出标注主窗口。如果第二条直接报错或者闪退,多半是 PyQt5 相关依赖出了问题。见到pyqt5-sip报错,先不要急着重装 PyQt5,通常是版本约束没对上,解法在第五章避坑里细说。

2.3 JSON 输出结构:标注结果到底存了什么

LabelMe 的产出不是图片,而是和图片同名的.json文件。一张图标完保存后,打开这个 JSON,里面字段是固定的,我习惯先把它读清楚再想后续转换逻辑:

import json with open("example.json", "r", encoding="utf-8") as f: data = json.load(f) print("图像路径:", data["imagePath"]) print("图像尺寸:", data["imageHeight"], "x", data["imageWidth"]) for shape in data["shapes"]: print("标签:", shape["label"]) print("类型:", shape["shape_type"]) print("坐标点数:", len(shape["points"]))

这段代码做的事情是逐字段解析标注产物。imagePath存的是当前图片的相对路径,shapes是核心——它是一个数组,每个元素对应一个标注对象,包含label(类别名)、points(坐标列表)、shape_type(标注形状类型)。这里注意,points对多边形是边界点坐标的完整序列,对矩形框只存对角两点,对关键点则是单个坐标,不同shape_type解析方式不同。

JSON 里还有一个容易被忽略的字段imageData,它是图片的 base64 编码。文件较小时会直接内嵌,图片一多就会设为null以减小体积。后续换机器或者挪目录重开标注时,如果这个字段是空的,程序会按imagePath去找原图,找不到就会提示图片缺失,这是一个高频坑,后面系统讲。

3. 三种标注模式实战:语义分割、目标检测与关键点标注

3.1 语义分割:polygon 工具画多边形区域

LabelMe 主界面左侧工具栏里,创建语义分割掩码用的是Create Polygons功能。选中后沿目标边缘依次点击,每个落点会形成一个锚点,最后回到起点闭合区域,程序会自动生成一个多边形标注。完成一个对象后,会弹出对话框让输入类别名——这一步一定要敲对类名,因为后续labelme2voc.py转换时,类别名直接决定输出目录和标签映射,错了返工成本极高。

多边形闭合之后,这个区域的标注里就已经包含了所有边界点的坐标。多说一句细节:点太密文件体积会大,点太稀疏则边界不贴合。我一般的原则是,目标边缘平滑的部位隔几个像素打一个点,转角、凹陷处一个都不省。这样转换出来的掩码质量最好。

3.2 目标检测:rectangle 工具画边界框

目标检测标注在 LabelMe 里揉进了同一个界面:左侧工具栏选择Create Rectangle,然后在目标左上角和右下角各点一下,一个旋转角为 0 的矩形框就出来了。如果检测对象是倾斜的,LabelMe 还支持通过旋转变换调整框的方向,保存后同样的坐标会进shapes数组,shape_type为rectangle,points只有两个坐标点。

矩形框的坑在于:转 COCO 格式时,标注坐标会被解释成左上角(x, y)和右下角(x, y)两点,而 YOLO 训练所需的却是中心点坐标加宽高,两种格式之间要换算。如果你早早就决定用 YOLOv8 训练,可以在标注阶段就留意,画框时稍微保守一点,不要为了贴紧边缘把目标截掉一部分,否则转换后宽高归一化容易超出边界。

3.3 关键点与线:point、line 和 circle 的用处

关键点标注是很多人疏忽的功能,但它对姿态估计、人脸关键点这类项目是刚需。在Create Point模式下,点击目标部位(比如人眼、鼻尖、关节点),程序会记录单个点的像素坐标,多个点组合成一个形状,每个点都能单独赋类别名。这个模式对标注人员的要求最高——同一个语义点在不同图片里不能标错位置,建议团队协作时先出一份标注规范文档,约定坐标点顺序。

Create Line和Create Circle用得相对少。Line 适合车道线、边缘轮廓这种开放路径标注,Circle 适合圆形目标,比如工业零件、细胞切片。它们输出的坐标结构彼此不同,转换脚本未必都能兼容,所以我的习惯是:能用 polygon 和 rectangle 解决的,尽量不用特殊形状,避免后续编写自定义转换脚本时多写分支。

3.4 标注效率:几个常用快捷键和批量操作

标注过程最怕重复劳动,LabelMe 有几个操作实际用下来最提效:标注完成后按Ctrl+S快速保存;编辑模式下拖拽锚点调整边界;右键点击标注对象可以编辑类别名或删除。批量标注图片时,我习惯把同一类图片放在一个目录里,启动后依次打开、标注、保存,而不要频繁切换目录。LabelMe 的菜单里也支持上一张下一张切换,配合快捷键能很快批量过完一整批。

如果发现类名拼写错了,不用一张张改,直接批量改 JSON 文件中的label字段即可。这一步可以用 Python 脚本遍历目录统一替换,能省掉大量重复点击。但注意批量替换前先备份原始 JSON,改动过程中一旦漏掉某些字段的大小写,后续 VOC 转换时会麻烦。

4. 数据格式转换:从 JSON 到 VOC / COCO,带参数说明

4.1 labelme2voc.py:目录结构和标签映射规则

LabelMe 自带的 JSON 文件不能直接进 U-Net 或 Mask R-CNN 训练,转成 PASCAL VOC 格式是最常用的出路。源码包里的labelme2voc.py脚本专门干这个,用法是:

python labelme2voc.py data_annotated data_dataset_voc --labels labels.txt

参数说明:第一个参数data_annotated是存放 JSON 文件的输入目录,第二个参数data_dataset_voc是转换后的输出目录,--labels指定类别清单文件。执行完之后,输出目录下会出现JPEGImages、SegmentationClass、SegmentationClassPNG、SegmentationObject等子目录。JPEGImages里是所有参与转换的原图,SegmentationClassPNG里是每张图对应的语义分割掩码图,每个类别用唯一像素值区分。

跑完转换后一定要做的一步检查是统计类别像素值是否连续。VOC 格式对类别索引敏感:背景通常为 0,第一个类别为 1,第二个为 2。如果 labelme 标注时类别名和labels.txt顺序不一致,生成的掩码图类别索引就会错位,训练时 loss 直接乱掉。

4.2 labelme2coco.py:COCO 注解文件怎么读

目标检测任务更常转到 COCO 格式。labelme2coco.py会把整个输入目录的标注合并成一个 COCO 格式的 JSON 注解文件,再配合原始图片目录喂给检测框架。运行方式类似:

python labelme2coco.py data_annotated data_annotated_coco --labels labels.txt

第二个参数是输出目录,转换完成后目录下会生成data_annotated_coco.json这样的注解文件。COCO JSON 的三段结构要熟悉:categories里是类别名和 id 的映射,images里是每张图的文件名和尺寸信息,annotations里是每个标注实例的坐标和类别 id。检查转换是否成功,可以先数一下annotations数组的长度是否与标注总数一致,再核对categories数量是否符合预期。

值得一提的是,COCO 格式对rectangle的处理是直接取两个对角坐标,而对polygon的处理是把所有边界点铺到segmentation字段里。所以在标注时就按shape_type区分好用途:目标检测画框,分割画多边形,混用会导致转换后的字段缺失或解析错误。

4.3 转换后的边长检查:尺寸对齐与类别连续性

转换完成不等于数据能用。我每次转完都会跑一段校验脚本看三个指标:一是输出目录里的图片数量是否和输入 JSON 数量一致;二是读取一张掩码图的像素值,看看类别值是否从 0 开始连续分布;三是随机挑几张图,把标注框画回原图上,人眼确认坐标有没有偏移。这三个检查都过了,训练才敢开始。

像素值读取可以用一个极简脚本做:

from PIL import Image import numpy as np mask = np.array(Image.open("data_dataset_voc/SegmentationClassPNG/example.png")) print("掩码尺寸:", mask.shape) print("唯一像素值:", np.unique(mask))

这段脚本输出掩码图的唯一像素值列表,正常情况下应该是一小组从小到大的整数,比如[0, 1, 2]。如果看到杂散的大数值,或者类别数比标注类别多,说明转换过程或标注过程出了问题,优先检查labels.txt和标注时的类名拼写。

5. 避坑记录:标注工具最常翻车的 5 个位置

5.1 现象:安装后启动直接报pyqt5-sip相关错误

新环境装 labelme 后运行命令,界面没弹出来,终端里刷出一串AttributeError或ModuleNotFoundError,关键词指向pyqt5.sip。原因:labelme 对PyQt5和pyqt5-sip的版本搭配有要求,pip 默认安装时可能拿到相互不兼容的版本组合。解决:先卸载重装指定版本对,我的习惯是pip install pyqt5==5.15.* pyqt5-sip==12.*,然后重新跑labelme。如果还用conda环境,也可以试试conda install pyqt5,conda 的依赖解析通常比 pip 更会处理这类冲突。

5.2 现象:VOC 转换后掩码图全是黑色或类别错乱

跑完labelme2voc.py,生成的SegmentationClassPNG里掩码要么全黑,要么类别区域颜色对不上号。原因:类名不统一。标注时手滑,同一类别有的写cat,有的写Cat,有的带了个空格,labels.txt里只列了一种写法,程序就把另一种当成未知类别丢弃了。解决:转换前先用脚本统计所有 JSON 里的label值,去重后和labels.txt对一遍,不一致先批量改标注文件。从那以后我每次新建标注任务都会先建一份类名清单,标注时直接复制粘贴,绝不手敲。

5.3 现象:换电脑后打开旧 JSON,图片显示不出来

标注目录拷贝到另一台机器,打开 JSON,程序找不到原图,标注区域悬空显示。原因:JSON 里imageData为 null,imagePath是相对路径,原图没有跟着一起搬过去,或者目录结构变了。解决:把图片目录和 JSON 目录保持同一相对层级再迁移;如果之前已经打散,可以写脚本为每个 JSON 重新注入图片的 base64 编码,或逐张重新关联。日常使用中,我倾向于把每张图连同 JSON 放在同一个子目录,迁移时整目录拷贝,不单独挑文件。

5.4 现象:重叠标注导致分割掩码互相覆盖

语义分割时两个对象有重叠区域,比如一个人站在车前面,转换后掩码图上重叠部分只显示了一个类别。原因:labelme2voc.py生成掩码时按标注顺序逐类绘制,后画的类别覆盖先画的类别,重叠区域只能保留一个值。解决:标注重叠目标时遵循固定顺序,比如前景物体后画;如果任务必须保留重叠信息,VOC 单通道掩码本身就不够,需要转成多类别概率图的格式,或者用其他工具做实例级标注。这个限制不是 bug,是格式本身决定的,遇到时心里要有数。

5.5 现象:标注点和图片边缘贴太近,转换后坐标越界

画矩形框或分割多边形时,边界点正好落在图像最外围像素上,转换到 YOLO 格式后中心点坐标或宽高出现大于 1 或小于 0 的值,训练加载时报错。原因:标注时没有留安全边距,归一化公式本身不会帮你做越界纠正。解决:标注协议里约定所有目标框距图像边缘至少 2~3 个像素;转换脚本里加一层 clamp 操作防御:

cx = min(max((x1 + x2) / 2 / width, 0.0), 1.0)

这一行把中心点坐标约束在[0, 1]区间内,避免直接喂给训练器时报错,但根本解法还是标注阶段不要把点打在贴边位置。

6. 把数据集喂给 YOLOv8:转换脚本、验证与协作习惯

6.1 写一个 JSON 到 YOLO txt 的转换脚本

YOLOv8 训练自己的数据集,需要的是每张图对应一个 txt 文件,每行内容为class_id x_center y_center width height,全部坐标按图片尺寸归一化。LabelMe 自带的脚本不直接输出这个格式,但 COCO JSON 转 YOLO 的脚本很常见,也可以直接从 labelme JSON 一步转到位,我这里给出我常用的一段:

import json, os def labelme_to_yolo(json_path, output_dir, class_list): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) height, width = data["imageHeight"], data["imageWidth"] txt_path = os.path.join(output_dir, os.path.basename(json_path).replace(".json", ".txt")) with open(txt_path, "w") as out: for shape in data["shapes"]: label = shape["label"] if label not in class_list: continue cls_id = class_list.index(label) points = shape["points"] if shape["shape_type"] == "rectangle": (x1, y1), (x2, y2) = points[0], points[1] else: xs = [p[0] for p in points] ys = [p[1] for p in points] x1, y1, x2, y2 = min(xs), min(ys), max(xs), max(ys) dw = 1.0 / width dh = 1.0 / height cx = ((x1 + x2) / 2.0) * dw cy = ((y1 + y2) / 2.0) * dh w = (x2 - x1) * dw h = (y2 - y1) * dh out.write(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n")

这段脚本做的事情是逐 JSON 读取标注,把rectangle的对角坐标和多边形的包络框统一换算成中心点加宽高的归一化表示。代码里class_list是类别清单,顺序决定cls_id,一定要和训练配置里的类别顺序一致,否则模型训练出来类别全错位。dw和dh是归一化系数,格式上 YOLO 要求宽高统一除以图片实际尺寸。

6.2 验证数据集:训练前最后一道关

转换完先不看训练,把每个 txt 文件里最大的宽度和高度值统计一遍,如果出现大于 1 的值,回到标注文件里查是哪张图越界。再用一张图画出预测框对比原图目标位置,确认坐标没有系统性偏移。就这一步,能省下训练后才发现数据问题而返工的一整天。

这个流程跑顺之后,团队多人协作也简单了:规定好统一的类名清单、标注工具版本、输出目录结构,标注完由一个人跑全量转换和校验,有问题直接在文档里记录,下个批次规避。从那以后我每次新建数据集项目,都强制走一遍「类名预定义 → 标注规范确认 → 转换脚本跑批 → 边缘值统计」这套固定动作,后面训练阶段再没出过因为数据格式翻车的半夜事故。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 1:37:36

Win11企业版LTSC重装实战:从官方镜像到本地账户完整流程

如果你正考虑给自己的电脑重装一次 Windows,又恰好听说了“Win11 企业版 LTSC”这个名字,那这篇文章值得你从头到尾看一遍。很多人把 LTSC 理解成“精简版、破解版、装完没有广告的系统”,然后随便找个网站下载镜像,结果装完发现系…

作者头像 李华
网站建设 2026/9/28 1:37:13

STM32开发避坑指南:从编译下载到时钟串口外设的实战经验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 1:36:26

CH32V003 RISC-V开发环境搭建与避坑指南:从工具链到调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 1:36:13

链路预测源码复现:VGAE、Node2Vec与谱聚类实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 1:36:13

PLC能当动态数据采集仪用吗?应力应变与IEPE振动采集深度对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 1:35:54

AutoShop与ITP仿真联调:PLC和HMI全链路虚拟调试指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华