简介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, encodingutf-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为rectanglepoints只有两个坐标点。矩形框的坑在于转 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 有几个操作实际用下来最提效标注完成后按CtrlS快速保存编辑模式下拖拽锚点调整边界右键点击标注对象可以编辑类别名或删除。批量标注图片时我习惯把同一类图片放在一个目录里启动后依次打开、标注、保存而不要频繁切换目录。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.pyCOCO 注解文件怎么读目标检测任务更常转到 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 pyqt55.15.* pyqt5-sip12.*然后重新跑labelme。如果还用conda环境也可以试试conda install pyqt5conda 的依赖解析通常比 pip 更会处理这类冲突。5.2 现象VOC 转换后掩码图全是黑色或类别错乱跑完labelme2voc.py生成的SegmentationClassPNG里掩码要么全黑要么类别区域颜色对不上号。原因类名不统一。标注时手滑同一类别有的写cat有的写Cat有的带了个空格labels.txt里只列了一种写法程序就把另一种当成未知类别丢弃了。解决转换前先用脚本统计所有 JSON 里的label值去重后和labels.txt对一遍不一致先批量改标注文件。从那以后我每次新建标注任务都会先建一份类名清单标注时直接复制粘贴绝不手敲。5.3 现象换电脑后打开旧 JSON图片显示不出来标注目录拷贝到另一台机器打开 JSON程序找不到原图标注区域悬空显示。原因JSON 里imageData为 nullimagePath是相对路径原图没有跟着一起搬过去或者目录结构变了。解决把图片目录和 JSON 目录保持同一相对层级再迁移如果之前已经打散可以写脚本为每个 JSON 重新注入图片的 base64 编码或逐张重新关联。日常使用中我倾向于把每张图连同 JSON 放在同一个子目录迁移时整目录拷贝不单独挑文件。5.4 现象重叠标注导致分割掩码互相覆盖语义分割时两个对象有重叠区域比如一个人站在车前面转换后掩码图上重叠部分只显示了一个类别。原因labelme2voc.py生成掩码时按标注顺序逐类绘制后画的类别覆盖先画的类别重叠区域只能保留一个值。解决标注重叠目标时遵循固定顺序比如前景物体后画如果任务必须保留重叠信息VOC 单通道掩码本身就不够需要转成多类别概率图的格式或者用其他工具做实例级标注。这个限制不是 bug是格式本身决定的遇到时心里要有数。5.5 现象标注点和图片边缘贴太近转换后坐标越界画矩形框或分割多边形时边界点正好落在图像最外围像素上转换到 YOLO 格式后中心点坐标或宽高出现大于 1 或小于 0 的值训练加载时报错。原因标注时没有留安全边距归一化公式本身不会帮你做越界纠正。解决标注协议里约定所有目标框距图像边缘至少 23 个像素转换脚本里加一层 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, encodingutf-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 的值回到标注文件里查是哪张图越界。再用一张图画出预测框对比原图目标位置确认坐标没有系统性偏移。就这一步能省下训练后才发现数据问题而返工的一整天。这个流程跑顺之后团队多人协作也简单了规定好统一的类名清单、标注工具版本、输出目录结构标注完由一个人跑全量转换和校验有问题直接在文档里记录下个批次规避。从那以后我每次新建数据集项目都强制走一遍「类名预定义 → 标注规范确认 → 转换脚本跑批 → 边缘值统计」这套固定动作后面训练阶段再没出过因为数据格式翻车的半夜事故。希望帮到你。本文还有配套的精品资源点击获取