1. 为什么我至今还在用 labelme 做数据标注
做视觉方向的项目,绕不开数据标注这个环节。不管你是做目标检测、语义分割、实例分割,还是最近比较火的视觉大模型微调,手里没有一批标注质量过硬的训练数据,后面模型怎么调都是白搭。我这些年用过不少标注工具,有在线的、有离线的、有商业的、有开源的,兜兜转转,labelme 始终是我本地标注的首选。原因很简单:轻量、开源、跨平台、标注格式通用,而且它输出的 JSON 格式几乎成了分割标注领域的一个事实标准,很多训练框架和转换脚本都原生支持直接读取。
这篇文章我打算把 labelme 从安装到日常使用的完整流程讲透。不是那种“点下一步下一步”的流水账,而是把我这几年踩过的坑、总结的技巧、以及一些能显著提升标注效率的配置方法都摊开来说。不管你是刚接触数据标注的新手,还是已经用过一段时间但总觉得效率不高的老手,应该都能从里面找到点有用的东西。
先明确一下 labelme 到底能干什么。它本质上是一个基于 Python 和 Qt 的图形化标注工具,核心功能是让你在图片上画多边形、矩形、圆形、线段、点,然后把这些标注信息保存成 JSON 文件。它最擅长的场景是多边形分割标注,也就是把一个物体的轮廓一个点一个点地勾出来。当然,做矩形框标注它也完全没问题,只是如果只做矩形框,可能有更轻量的选择。但一旦涉及不规则形状的分割,labelme 的体验在开源工具里算是相当能打的了。
适合谁来参考这篇文章?如果你是做计算机视觉相关项目的开发者、算法工程师、或者在校做科研的学生,需要自己动手准备分割数据集,那这篇文章基本可以当作一份完整的操作手册来用。如果你只是偶尔标几张图玩玩,那前面的安装部分看完就够用了。下面我按安装、配置、标注实操、格式转换、问题排查这个顺序来展开,每一块都会给出具体的命令和参数说明。
2. 安装环节的几种方案与选型逻辑
2.1 为什么推荐用 conda 而不是直接 pip
labelme 的安装方式有好几种,最省事的是直接pip install labelme,但我强烈建议你用一个独立的 conda 环境来装。原因在于 labelme 依赖 Qt 的 Python 绑定(PyQt5 或 PyQt6),而 Qt 这套东西在不同系统上跟其他库的兼容性有时候会出幺蛾子。如果你直接装在系统 Python 或者某个已经装了一堆包的主环境里,很容易出现版本冲突,轻则界面打不开,重则把原有环境搞崩。
用 conda 建一个干净环境,隔离性最好,出问题了直接删掉重建,成本极低。具体操作是这样:
conda create -n labelme python=3.9 -y conda activate labelme pip install labelme这里我特意指定了 Python 3.9,不是说其他版本不行,而是 3.9 在 Qt 绑定和各类科学计算库之间的兼容性经过大量实践验证,最稳。你要是用 3.11 或 3.12,大部分情况也没问题,但偶尔会遇到某些依赖还没跟上新版本的情况,徒增排查成本。新手没必要在这上面冒险。
装完之后在终端输入labelme回车,如果弹出一个图形界面,说明装好了。如果报错说找不到命令,大概率是环境没激活对,或者 pip 装的脚本路径没加到 PATH 里。这时候可以用python -m labelme来启动,效果一样。
2.2 不同操作系统的安装差异
Windows 用户相对省心,conda 环境建好之后 pip 装完基本就能用。需要注意的是如果你用的是 Windows 的 WSL,那图形界面需要额外的 X Server 配置,比较麻烦,不建议在 WSL 里跑 labelme,直接在 Windows 原生环境装就行。
macOS 用户要注意一点,如果你用的是 Apple Silicon 芯片的机器,某些老版本的 PyQt5 可能没有对应的 arm64 轮子,pip 会尝试从源码编译,过程又慢又容易失败。解决办法是优先装 PyQt6 版本的 labelme,或者确保你的 pip 是最新版,能拉到预编译好的 arm64 包。实测下来,在 M 系列芯片的 Mac 上,用 Python 3.9 加最新版 labelme,pip 直接装是能成功的。
Linux 用户的情况最复杂,因为发行版太多。Ubuntu 系一般问题不大,但如果你是在一个没有图形界面的服务器上装,那 labelme 是跑不起来的,因为它需要显示界面。这种情况下要么用带桌面的环境,要么考虑用 X11 转发,但转发方案延迟高,标注体验很差,不推荐。真要在服务器上标,建议用支持 Web 访问的标注工具,labelme 本身不太适合这种场景。
2.3 版本选择的一个经验判断
labelme 的版本迭代不算特别频繁,但不同版本之间在界面细节和输出格式上偶有差异。我的建议是:如果你是在跟着某个已有的项目流程走,先确认那个项目对 labelme 版本有没有要求,有的话就装指定版本。如果没有特殊要求,装最新稳定版就行。
查看当前版本用labelme --version。如果你想装某个特定版本,比如 5.4.1,就用pip install labelme==5.4.1。这里有个小坑:labelme 的大版本号从 4 跳到 5 的时候,输出 JSON 里的某些字段结构有过调整。如果你手头有旧版本标注的数据,升级前最好先拿一张图测试一下,确认新旧格式的兼容性,免得批量转换的时候才发现对不上。
3. 标注前的准备工作与界面配置
3.1 图片目录的组织方式
很多人拿到一堆图片就开始标,标到一半发现文件乱得不行。我的习惯是在项目根目录下建一个data文件夹,里面再按类别或者批次分子文件夹。比如:
project/ data/ batch_01/ img_001.jpg img_002.jpg batch_02/ img_003.jpg这样分的好处是,labelme 打开一个目录后,左侧文件列表不会一下子塞进去几千张图,找起来方便。而且标注到一半中断了,下次接着标的时候,你能清楚知道哪些批次已经完成。labelme 保存的 JSON 文件默认跟图片放在同一个目录,文件名跟图片名一致,只是后缀变成.json。这种设计很直观,图片和标注一一对应,不会错乱。
有一点要特别注意:图片文件名尽量不要用中文和空格。虽然 labelme 本身能处理中文路径,但后续你把 JSON 转成其他格式、或者喂给训练框架的时候,中文路径经常出问题。用英文、数字、下划线组合最保险。如果原始图片名就是中文,批量重命名一下再开始标,磨刀不误砍柴工。
3.2 启动参数与常用配置
labelme 支持不少命令行参数,用好了能省很多事。最常用的几个我列一下:
labelme data/batch_01 --labels labels.txt --autosave --nodata逐个解释。--labels labels.txt是指定一个预定义标签文件,里面每行一个类别名。这样你在标注的时候,右侧的标签列表就是固定的,直接勾选就行,不用每次手动输入类别名。这个功能在多人协作标注时尤其重要,能保证所有人的标签拼写完全一致,避免出现“person”和“Person”这种低级错误导致后面数据处理出问题。
--autosave是自动保存,每画完一个形状就自动写一次 JSON。这个功能我建议默认打开,因为标注是个体力活,标了几十张之后人容易走神,万一忘了保存或者程序崩了,损失就大了。开了自动保存,至少不会丢已经标好的部分。
--nodata是不把图片的 base64 编码存进 JSON。默认情况下,labelme 会把整张图片编码后塞进 JSON 文件里,导致一个 JSON 动辄几 MB。如果你图片多,磁盘很快就满了。加上--nodata之后,JSON 里只存标注坐标和图片路径,文件大小通常只有几 KB,清爽很多。代价是 JSON 文件不能脱离原图单独使用,但正常流程里图片本来就要一起带着,所以这个代价可以接受。
还有一个--nosortlabels参数,是让标签列表不按字母排序,保持你 labels.txt 里的原始顺序。如果你对标签顺序有讲究,可以加上。
3.3 界面里几个容易被忽略的设置
打开 labelme 之后,右侧面板有几个选项值得调一下。Save Automatically对应刚才说的自动保存,建议勾上。Save With Image Data如果没加--nodata参数,这里默认是勾选的,手动取消掉也能达到不存图片数据的效果。Keep Previous Annotation这个选项很实用,勾上之后,切换到下一张图时会保留上一张的标注形状,适合连续标注相似场景的图片,比如监控视频抽帧出来的连续帧,物体位置变化不大,直接微调上一帧的框就行,效率翻倍。
左侧的文件列表上方有个搜索框,图片多的时候用文件名关键词过滤,比滚动查找快得多。另外,File菜单里的Change Output Dir可以修改 JSON 的保存目录,如果你想把标注文件统一存到另一个地方,用这个功能。不过我个人还是习惯让 JSON 跟图片放一起,管理起来最不容易乱。
4. 核心标注操作与效率技巧
4.1 多边形标注的完整流程
多边形标注是 labelme 的核心功能,操作逻辑是:点击左侧工具栏的Create Polygons(快捷键是P),然后沿着物体边缘依次点击,每点一下生成一个顶点,最后点回起点或者按回车闭合多边形。闭合之后会弹出标签输入框,输入类别名或者从预定义列表里选,确认后这个标注就完成了。
听起来简单,但实际操作中有几个细节直接影响标注质量和效率。第一,顶点不是越多越好。有些人标注的时候恨不得每个像素都点一下,结果一个物体几百个顶点,JSON 文件巨大,训练的时候处理也慢。实际上对于大多数分割任务,物体轮廓用几十个顶点就能很好地近似了。我的经验是,直线段中间不需要加点,只在拐角处点;曲线段根据曲率决定密度,曲率大的地方点密一些,平缓的地方点稀一些。这样标出来的多边形既准确又精简。
第二,善用撤销和回退。标多边形的时候点错一个点是常事,Ctrl+Z可以撤销上一个点,不用整个重来。如果多边形已经闭合了想修改,双击那个多边形进入编辑模式,可以拖动顶点、增删顶点。这个编辑功能一定要熟练,因为很多时候第一遍标完发现边缘没对齐,微调比重新标快得多。
第三,注意多边形的方向。labelme 对多边形的顶点顺序没有强制要求,顺时针逆时针都行。但如果你后续要转成 COCO 格式或者某些特定框架要求的格式,可能会对方向有要求。保险起见,我习惯统一按顺时针方向标注,养成习惯之后就不会有这个问题。
4.2 矩形框、圆形和其他形状的使用场景
虽然 labelme 以多边形标注闻名,但它的矩形框功能也很常用。快捷键R创建矩形,对角拖拽就行。矩形框适合目标检测任务,或者作为分割标注的粗定位——先用矩形框快速框出所有目标,再对需要精细分割的物体逐个转成多边形。
圆形标注(快捷键C)用得相对少,适合标注圆形物体,比如某些工业质检场景里的圆孔、圆形零件。线段标注(快捷键L)可以用来标车道线、裂缝这类细长目标。点标注(快捷键P之后选 Point)适合标关键点,比如人脸特征点、人体骨骼关键点。
这里有个组合技巧:对于一张图里既有需要精细分割的主体,又有大量只需要框出来的次要目标,可以混合使用。主体用多边形,次要目标用矩形,一次标注搞定多种需求。labelme 的 JSON 里会记录每个形状的类型,后续转换的时候按类型分别处理就行。
4.3 提升标注速度的实操心得
标注是个重复性很高的工作,速度提升靠的是减少不必要的操作。我总结了几条实测有效的技巧。
第一,用快捷键代替鼠标点击。Ctrl+S保存,Ctrl+Z撤销,D下一张,A上一张,P多边形,R矩形,E编辑模式。这些快捷键用熟了,手不用在鼠标和键盘之间来回切换,效率提升非常明显。我刚开始标的时候全程鼠标点菜单,一天标不了多少张,后来强迫自己记快捷键,速度至少快了一倍。
第二,批量处理相似图片。前面提到的Keep Previous Annotation功能,配合视频抽帧的连续图片使用,效果极佳。比如一段视频每秒抽一帧,相邻帧之间物体位置变化很小,保留上一帧标注后,只需要微调几个顶点就行,比从零开始标快得多。
第三,合理设置图片缩放。标注精细物体的时候,滚轮放大到合适倍数再标,边缘对得更准。但也不要放太大,否则视野太窄,标大物体的时候要频繁拖动画面,反而慢。我的习惯是让目标物体占据画面三分之一到一半左右的大小,这个比例下标注精度和操作速度比较平衡。
第四,标签命名要规范且简短。类别名用英文小写加下划线,比如traffic_light、left_kidney。不要用中文,不要用空格,不要用特殊字符。简短的好处是输入快,规范的好处是后续处理不用做额外的清洗。
5. 标注文件的格式解析与批量转换
5.1 JSON 文件里到底存了什么
labelme 保存的 JSON 结构其实很清晰,搞懂它之后你就能自己写脚本处理,不用依赖现成的转换工具。一个典型的 JSON 长这样:
{ "version": "5.4.1", "flags": {}, "shapes": [ { "label": "cat", "points": [[100.5, 200.3], [150.2, 210.8], [160.0, 250.1]], "group_id": null, "shape_type": "polygon", "flags": {} } ], "imagePath": "img_001.jpg", "imageData": null, "imageHeight": 480, "imageWidth": 640 }shapes数组里每个元素就是一个标注对象。label是类别名,points是顶点坐标列表,注意坐标是浮点数,单位是像素。shape_type标明这个形状是多边形还是矩形还是圆。imageData如果加了--nodata参数就是 null,否则是一长串 base64 编码的图片数据。imageHeight和imageWidth是原图尺寸,这个信息在坐标归一化的时候很有用。
理解了这个结构,你就能明白为什么 labelme 的格式通用性这么强——它足够简单,信息足够全,任何格式转换本质上就是从这个结构里提取信息重新组织。
5.2 转 COCO 格式的实操方法
COCO 格式是目标检测和实例分割领域最常用的格式之一,很多训练框架都直接支持。labelme 官方提供了一个转换脚本labelme2coco.py,但那个脚本用起来有点门槛,我一般是自己写一个精简版的转换逻辑,更可控。
转换的核心思路是:遍历所有 JSON 文件,把每个 shape 的类别和坐标提取出来,按照 COCO 的结构组织。COCO 的标注格式里,分割信息用多边形顶点列表表示,边界框用[x, y, width, height]表示。多边形转边界框很简单,取所有顶点坐标的最小最大值就行。
这里有个容易出错的点:COCO 的坐标是从 0 开始的整数,而 labelme 存的是浮点数。转换的时候要取整,但直接int()截断会丢失精度,建议用round()四舍五入。另外,COCO 的category_id是从 1 开始的,0 通常保留给背景,这个细节不注意的话训练时会报错。
如果你不想自己写脚本,也可以用现成的工具库,比如pycocotools配合一些开源转换脚本。但我的建议是至少自己动手写一次转换代码,因为不同项目的需求千差万别,有时候需要过滤某些类别,有时候需要合并类别,自己会改脚本才能灵活应对。
5.3 转 YOLO 格式和掩码格式的要点
YOLO 格式的分割标注跟检测标注不太一样。检测的 YOLO 格式是class_id x_center y_center width height,都是归一化到 0 到 1 之间的相对坐标。分割的 YOLO 格式则是class_id x1 y1 x2 y2 ... xn yn,同样是归一化坐标。转换的时候用顶点坐标除以图片宽高就行。
掩码格式(mask)的转换稍微复杂一点,需要用opencv或者PIL把多边形填充成二值图。核心代码逻辑是创建一个全黑的单通道图,然后用cv2.fillPoly把多边形区域填成白色。每个类别生成一张掩码图,或者把所有类别合并到一张图里用不同的像素值区分。这个转换在语义分割任务里很常用。
不管转成哪种格式,转换完之后一定要抽样验证。随机挑几张图,把转换后的标注可视化出来,跟原图叠在一起看,确认坐标没偏移、类别没搞错。我见过太多次因为转换脚本里一个坐标顺序写反了,导致整个数据集白标的情况。验证这一步花不了多少时间,但能避免灾难性的返工。
6. 常见问题排查与避坑经验
6.1 启动和界面相关的故障
最常见的问题是启动时报 Qt 相关的错误,比如Could not load the Qt platform plugin "xcb"。这个在 Linux 上尤其常见,原因是系统缺少某些 Qt 运行库。解决办法是安装对应的系统包,Ubuntu 下一般是sudo apt-get install libxcb-xinerama0之类的。具体缺哪个库,错误信息里通常会提示,照着装就行。
另一个高频问题是界面字体太小或者显示模糊。这在 4K 显示器上比较常见,因为 Qt 默认的缩放策略可能不匹配。解决办法是设置环境变量QT_SCALE_FACTOR=1.5或者QT_AUTO_SCREEN_SCALE_FACTOR=1,具体值根据你的显示器调整。Windows 下可以在系统设置的显示缩放里调,但有时候 Qt 应用不跟随系统缩放,还是得靠环境变量。
还有一种情况是打开 labelme 后界面卡死无响应。这通常是因为图片太大,比如一张几千万像素的全景图,labelme 加载和渲染的时候会卡。解决办法是先把大图切分成小块再标,或者用--nodata减少内存占用。如果图片实在太大,建议换用支持分块加载的专业标注工具。
6.2 标注数据丢失的预防与补救
数据丢失是标注工作里最让人崩溃的事。我踩过的最大的坑是:标了一下午,结果因为没开自动保存,程序意外关闭,全部白干。从那以后我养成了两个习惯。第一,永远开着--autosave。第二,每隔一段时间手动Ctrl+S一次,双保险。
如果已经发生了数据丢失,先别急着重新标。labelme 在保存的时候是覆盖写入,如果 JSON 文件已经损坏或者被覆盖,恢复的可能性不大。但有一种情况可以补救:如果你之前转过格式,转换后的文件还在,可以尝试从转换后的格式反推回 labelme 格式。虽然麻烦,但总比重标强。所以我的另一个建议是,标注数据要定期备份,用 git 或者简单的文件同步都行,成本很低,关键时刻能救命。
还有一个隐蔽的坑:多人协作标注时,如果两个人标了同一张图,后保存的会覆盖先保存的。避免这个问题的方法是做好任务分配,确保每张图只由一个人负责。如果确实需要交叉验证,那就用不同的输出目录,标完之后再合并。
6.3 标注质量问题的自查清单
标完的数据在训练前一定要做质量检查,否则模型效果不好你都不知道是数据问题还是模型问题。我整理了一个自查清单,每次数据集交付前都会过一遍。
| 检查项 | 检查方法 | 常见问题 |
|---|---|---|
| 类别拼写一致性 | 统计所有 JSON 里的 label 值 | 出现大小写不一致、单复数混用 |
| 坐标越界 | 检查 points 是否超出图片宽高 | 标注时误拖到画面外 |
| 空标注文件 | 检查 shapes 数组是否为空 | 误保存了未标注的图片 |
| 多边形自交 | 可视化检查 | 顶点顺序混乱导致轮廓交叉 |
| 图片与 JSON 对应 | 核对文件名 | 图片删了但 JSON 还在,或反之 |
| 标签遗漏 | 随机抽样目视检查 | 漏标了某些目标 |
这个清单看起来简单,但每一条我都遇到过真实案例。尤其是类别拼写不一致,在多人协作时几乎必然出现,靠人工核对很费劲,写个脚本统计一下所有出现过的 label 值,一眼就能看出问题。
6.4 性能优化与大规模标注的建议
当你要标几千甚至几万张图的时候,labelme 本身的一些限制就会暴露出来。比如左侧文件列表加载几千个文件会变慢,这时候按批次分目录就很重要,每个目录控制在几百张以内。另外,JSON 文件数量多了之后,文件系统的读写也会变慢,建议用 SSD 存储标注数据。
如果标注量特别大,可以考虑用 labelme 的 Web 版本或者搭建一个多人协作的标注平台。但说实话,labelme 的定位就是单机小规模标注,真要上大规模标注,可能需要考虑更专业的标注系统。不过对于大多数个人项目和小团队来说,labelme 的容量完全够用,我最多用它标过上万张图,配合合理的目录组织和批次管理,体验还是可以接受的。
最后分享一个我个人的工作流:先用脚本把待标注图片按批次分好目录,生成对应的 labels.txt,然后写一个简单的 shell 脚本或者 bat 脚本,一键启动 labelme 并带上所有常用参数。这样每次开始标注只需要双击一下,不用记那些命令行参数。标注完成后,再跑一个转换脚本批量转成训练格式,整个流程就很顺了。这套流程我用了好几年,稳定可靠,希望对你有帮助。