
简介一份面向图像分割与标注初学者的Labelme安装与使用教程目标是帮助读者快速掌握从环境搭建到实际标注的全流程。内容基于Anaconda发行版展开涵盖下载安装的前置准备并针对Python2与Python3两种版本分别给出虚拟环境创建、Labelme安装与激活的完整命令同时补充Windows系统下打开cmd窗口的具体方法降低新手配置门槛。资源共1个docx文档压缩包整体仅14KB体积小巧、便于下载后随时查阅或打印。目前已有15213人学习参考适合计算机视觉、机器学习、数据科学及地理信息等需要完成图像标注任务的用户也为需要开展目标检测、语义分割等项目的学习者提供清晰的起步指引。文档不仅列出安装步骤还说明了启动Labelme后将JSON标注文件转换为可视化结果图片的实用操作帮助读者完成标注数据输出减少环境配置与工具使用中的常见弯路让读者能够快速把标注结果应用到后续模型训练或数据分析中。1. 为什么一个标注工具值得你花十分钟看完你有没有遇到过这种场景A同学花了两天标注完一千张图打开训练脚本却提示标签文件不存在最后发现他标完的 JSON 全留在另一个目录里训练脚本根本没读到。或者更常见的——标注到一半多边形撤销了一次整张图的标注全没了。这类问题在图像标注环节里反复出现而大部分时候不是工具不行是你还没把 labelme 的底层逻辑摸透。labelme 是目前做实例分割和语义分割最常用的图像标注工具之一它把每次标注的结果存成 JSON 文件里面记录了多边形坐标、类别名、图像路径和图像尺寸。本文不讲虚的直接按安装、界面操作、格式转换、常见坑、进阶技巧这条线走保证你看完就能上手标图而且标完的数据能直接喂给常见训练框架。2. labelme 安装conda、pip 和三种常踩的版本坑2.1 为什么推荐用 conda 装而不是直接 piplabelme 依赖 PyQt 和 Pillow这两者在不同 Python 版本下的兼容性差异很大。直接pip install labelme往往能装成功但启动时报错ModuleNotFoundError: No module named PyQt5或者ImportError: cannot import name QtWebEngineWidgets根源是系统 Python 环境太杂PyQt 版本被其他包干扰。所以我一般会先建一个干净的 conda 环境再在环境里用 pip 安装。这样做的好处是依赖隔离、卸载干净、换版本不污染主环境。# 创建独立环境指定 Python 版本3.8 到 3.10 均可 conda create -n labelme-env python3.9 -y # 激活环境 conda activate labelme-env # 安装 labelme 本体 pip install labelme这里的-n labelme-env是环境名可以随便起python3.9指定解释器版本实测 3.8~3.10 都能正常跑通不建议用 3.7 以下因为新版 PyQt 对旧解释器支持不好。pip install labelme会连带装好 PyQt5 和 Pillow不需要手动补装。装完验证一下# 查看安装位置确认不是系统全局环境 which labelme # 启动图形界面 labelme如果执行labelme能弹出主窗口说明安装完成。如果弹出报错大概率是下面 2.2 里说的几种情况。2.2 三个版本相关的坑第一个坑是 conda 环境里 Python 版本太高比如 3.11 或 3.12。PyQt5 的某些旧版本在这些解释器上无法直接安装报错通常是Could not find a version that satisfies the requirement PyQt5。解决办法是把 Python 版本降到 3.9 再装一步到位。第二个坑是系统里同时存在多个 Python导致labelme命令被指向一个旧环境。用which labelme看路径如果输出在/usr/local/bin而不是 conda 环境目录说明 PATH 顺序不对。执行conda activate labelme-env后检查echo $PATH确保环境目录在前面。第三个坑是 macOS 上装完双击启动没反应。这是因为 PyQt5 在 macOS 上和系统自带显示组件冲突终端里运行会看到Abort trap: 6。常见做法是升级 PyQt5 到 5.15 以上pip install PyQt55.15如果还不行改用 Python 3.8 环境再装一遍。2.3 离线环境怎么办如果你的机器不能联网需要提前在能联网的机器上把包下载好再拷贝过去安装# 在有网的机器上执行 pip download labelme PyQt5 Pillow -d ./labelme_pkgs # 拷贝到离线机器后执行 pip install --no-index --find-links./labelme_pkgs labelme参数说明--no-index让 pip 不访问 PyPI--find-links指向本地包目录。注意 PyQt5 的体积比较大下载时务必带上它否则离线安装会在最后一步报缺依赖。3. 标注界面与操作流程从打开图片到导出 JSON3.1 界面布局和文件加载方式打开 labelme 后界面主要由菜单栏、画布区域、文件列表和标注区组成。第一次用的人最容易迷的地方是到底怎么把一整批图片导进来而不是一张一张点打开。常见做法是先把要标注的图片统一放到一个文件夹里然后在 labelme 里通过File Open Dir选择这个文件夹。此时画布上方会出现一个图片滑动条右下角有上一张/下一张按钮标注完一张按D就能切到下一张。注意区分Open File和Open Dir前者只加载单张图后者加载整个目录。用目录模式标注时labelme 会自动把已标注的 JSON 文件和图片关联起来重新打开目录时能继续标注不用从头标。3.2 多边形标注画点、闭合、改点和取消这是 labelme 的核心操作。左侧工具栏选中Create Polygons然后在图上点击左键打点打完全部顶点后点击右键或按回车闭合多边形会弹出类别选择框输入类别名并确认。操作流程Windows / Linux 1. 点击左侧 Create Polygons 2. 左键逐个点击目标边缘打点 3. 打完后右键点击任意位置闭合 4. 在弹出的 Edit Label 里输入类别名 5. 按 D 保存当前 JSON 并切到下一张闭合后如果要修正选中Edit Polygons工具可以拖动已有顶点位置也可以框选多个顶点一起移动。如果某个点打错了想撤销按CtrlZ删除最近一个点。如果整个多边形都不想要选中它按Delete删除。这里有一个容易被忽略的操作在闭合多边形之后、切到下一张之前图片中央会出现一个小的编辑框要在这个框里输入类别名并回车确认。如果这一步直接点空白处跳过标注会丢失。3.3 标注结果 JSON 的结构你存下来的到底是什么每标完一张图labelme 会在图片同目录下生成同名 JSON 文件。打开一个最简单的 JSON内容结构大致是{ version: 5.2.0, flags: {}, shapes: [ { label: cat, points: [[120, 50], [130, 80], [110, 90]], group_id: null, shape_type: polygon, flags: {} } ], imagePath: cat_001.jpg, imageData: null, imageHeight: 600, imageWidth: 800 }字段说明shapes是所有标注对象的列表每个人标注对象包含label类别名、points多边形顶点坐标单位是像素、shape_type多边形、矩形、圆等默认是 polygonimagePath是它引用的原始图片名注意是相对路径imageData如果为 null表示 JSON 没有内嵌图片数据只保存了路径。这个结构直接决定了后面转换脚本怎么写也决定了训练框架怎么读。很多人训练时报找不到标注就是因为imagePath里写的是文件名而实际图片在子目录里路径拼接不上。3.4 矩形、圆形和其他标注模式labelme 不只支持多边形。新版左边工具栏里还有Create Rectangle和Create Circle。矩形适合目标本身接近矩形轮廓的场景如车牌、路牌圆适合标注细胞、按钮等圆形目标。创建矩形时按住左键拖动到目标对角即可松开后同样会弹出类别输入框。圆形标注则是先点击圆心再拖动到半径边缘后点击确认。不管用哪种形状输出的 JSON 结构不变只是shape_type字段不同。从实际项目角度建议尽量统一形状类型。混合使用多边形和矩形会极大增加后续数据清洗的成本因为转换脚本通常只针对 polygon 写矩形需要额外做一次顶点扩展。4. 把 labelme 的 JSON 转成训练格式YOLO 与 COCO 两种路径4.1 为什么不能直接拿 JSON 丢给训练框架labelme 的 JSON 是标注工具的存档格式不是训练框架的标准输入。YOLO 系列要求每张图对应一个同名 TXT里面每行是类别ID x_center y_center width height坐标全部归一化到 0~1Mask R-CNN 等框架通常用 COCO 格式的 JSON里面是图片、标注、类别三类信息的字典。如果你直接拿 labelme 的 JSON 去训练框架会报看不懂这不是训练代码的问题是格式没对齐。常见做法是写一段转换脚本或者用社区里的转换工具把 JSON 转成目标格式。4.2 转 YOLO 格式转换脚本与坐标归一化这里给一个自用的转换脚本只处理多边形跳过没有标注的图。把下面的脚本放在图片目录的上层调整input_dir和output_dir即可import json import os def convert_labelme_to_yolo(json_dir, output_dir, class_list): # 建立类别名到 ID 的映射 class_map {name: idx for idx, name in enumerate(class_list)} for json_file in os.listdir(json_dir): if not json_file.endswith(.json): continue with open(os.path.join(json_dir, json_file), r, encodingutf-8) as f: data json.load(f) # 读取图片尺寸必须在 JSON 里存在 img_w data[imageWidth] img_h data[imageHeight] txt_name os.path.splitext(json_file)[0] .txt txt_path os.path.join(output_dir, txt_name) lines [] for shape in data[shapes]: label shape[label] if label not in class_map: print(f跳过 {json_file} 中的未知类别: {label}) continue points shape[points] # 计算多边形外接框 xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / img_h width (x_max - x_min) / img_w height (y_max - y_min) / img_h class_id class_map[label] # YOLO 格式类别ID 中心x 中心y 宽 高全部归一化到 0~1 lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: convert_labelme_to_yolo( json_dir./annotations, output_dir./yolo_labels, class_list[cat, dog, car] )逻辑说明脚本先读取 JSON 里的imageWidth和imageHeight再遍历每个shape取其多边形的所有顶点横纵坐标的最小值和最大值组成外接框再用框的中心点和宽高分别除以图片宽高得到归一化坐标。参数说明class_list的排列顺序决定了类别的 ID顺序一旦改变已经生成的 TXT 里的 ID 全部无效需要重新生成。这是 YOLO 训练里最隐蔽的一个坑。4.3 转 COCO 格式从 JSON 到 annotations 字典COCO 格式适合 Mask R-CNN、Detectron2 这类框架。核心是把所有图片的信息放到images列表把标注信息放到annotations列表类别信息放到categories列表import json def labelme_to_coco(image_dir, label_dirs, coco_output_path): coco_format { images: [], annotations: [], categories: [] } category_names [cat, dog] # 先注册类别 for idx, name in enumerate(category_names): coco_format[categories].append({ id: idx 1, name: name, supercategory: object }) ann_id 1 image_id 1 for idx, json_file in enumerate(os.listdir(label_dirs)): with open(os.path.join(label_dirs, json_file), r, encodingutf-8) as f: data json.load(f) # 注册图片信息 coco_format[images].append({ id: image_id, file_name: os.path.join(image_dir, data[imagePath]), width: data[imageWidth], height: data[imageHeight] }) # 注册每一个多边形的标注 for shape in data[shapes]: points shape[points] # 展平为 x1, y1, x2, y2 ... 的格式 flat_points [coord for p in points for coord in p] xs [p[0] for p in points] ys [p[1] for p in points] annotation { id: ann_id, image_id: image_id, category_id: category_names.index(shape[label]) 1, segmentation: [flat_points], bbox: [min(xs), min(ys), max(xs) - min(xs), max(ys) - min(ys)], area: (max(xs) - min(xs)) * (max(ys) - min(ys)), iscrowd: 0 } coco_format[annotations].append(annotation) ann_id 1 image_id 1 with open(coco_output_path, w, encodingutf-8) as f: json.dump(coco_format, f, indent2)逻辑说明COCO 最关键的三个字段是segmentation多边形坐标展平列表、bbox左上角坐标加宽高、category_id。如果是矩形标注segmentation只有四个点没有实际分割轮廓很多人转换后训练 Mask R-CNN 发现 mask 是方形的就是这个原因。参数说明image_dir是训练代码读取图片时的基础路径COCO 的file_name是相对这个路径的一般写成原始imagePath即可不要拼成绝对路径否则数据集换机器就失效。4.4 输出验证转换完怎么看对没对转换完成后的第一件事不是训练而是随机抽几张图把生成的 YOLO 标签叠加到图上可视化。常见做法是用 OpenCV 画框import cv2 img cv2.imread(cat_001.jpg) # 读取对应的 YOLO txt把归一化坐标乘回像素值 with open(cat_001.txt) as f: for line in f: cls_id, x_center, y_center, w, h line.split() img_h, img_w img.shape[:2] x1 int(float(x_center) * img_w - float(w) * img_w / 2) y1 int(float(y_center) * img_h - float(h) * img_h / 2) x2 int(float(x_center) * img_w float(w) * img_w / 2) y2 int(float(y_center) * img_h float(h) * img_h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)这段验证代码的作用是肉眼比对标注框和目标实际位置的偏差。如果框整体偏移多半是归一化的时候除错了图片尺寸如果框是斜的说明转换脚本把多边形的点顺序弄乱了。5. labelme 常见问题排查五个翻车现场与修复办法5.1 标注完重启软件图片全不见了现象标注了一百多张关掉 labelme 再打开图片列表是空的。原因你之前用的是Open File逐张打开的图片没有用Open Dir。labelme 的目录模式会在目录下维护一个图片列表而单文件模式不会保存列表状态。解决以后统一用Open Dir打开图片所在文件夹。如果 JSON 文件已经生成但列表加载不出来检查图片和 JSON 是否在同一目录以及 JSON 里的imagePath是否和实际图片文件名一致。5.2 右键闭合后多边形直接没了现象打完所有顶点点击右键闭合结果多边形在画布上消失了或者弹出一个空白类别框但没法输入。原因常见有两种。一是点右键时鼠标悬停在画布边缘触发了画布缩放而不是闭合二是多边形闭合时顶点数少于 3 个无法构成有效区域。解决画布边缘缩放时等鼠标移动到画布中心再右键闭合。顶点少于 3 个时先按CtrlZ删除最近的点重新打够至少 3 个点再闭合。5.3 类别名不小心打错了几百个 JSON 都要改现象标注完检查时发现 cat 打成了 cats或者类别名混用了中英文比如 猫 和 cat 同时存在。一个个点开改不现实。解决直接批量改 JSON 文件里的label字段。写一个简单脚本遍历目录下所有 JSON把shapes里的label替换成标准名称import json import os def fix_label_in_json(json_dir, old_name, new_name): for json_file in os.listdir(json_dir): if not json_file.endswith(.json): continue path os.path.join(json_dir, json_file) with open(path, r, encodingutf-8) as f: data json.load(f) for shape in data[shapes]: if shape[label] old_name: shape[label] new_name with open(path, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) fix_label_in_json(./annotations, cats, cat)参数说明old_name是错误的名字new_name是统一后的正确名字。执行前建议先把json_dir指向的目录备份一份因为批量替换脚本会直接覆盖原文件没有后悔药。5.4 图片太大标注完保存 JSON 要等好几秒现象5000×3000 像素的大图每保存一张要等 3~5 秒整个标注效率被拖垮。原因labelme 的 JSON 里imageData字段默认保存了一张 base64 编码的图片缩略图图片越大编码越慢JSON 文件也越大。这个字段对后续训练通常没有用。解决保存前在File Save Auto关闭自动保存手动保存后如果 JSON 太大可以用脚本把imageData置空。实测一个 2MB 的图片 JSON清除 imageData 后体积能降到几十 KB。5.5 标注时图片看起来模糊细节看不清现象图片在 labelme 里显示模糊放大后边界也看不清很难标准。原因labelme 默认开启了图片缩放适配窗口图片分辨率高于显示区域时会被压缩显示这并不影响实际标注数据但影响标注精度。解决用滚轮放大图片后再打点。放大的快捷键是滚轮向上缩小是滚轮向下。注意放大后按住左键可以平移画布但此时左键也是打点手势容易误打点。我一般放大后先用Edit Polygons工具来拖动已有顶点不用打点模式。6. 标注前的数据体检批量校验与类别统计分析标注不是点完开始就算完训练前至少要做三重校验。第一重是校验每个 JSON 是否有空标注没有任何 shapes 的 JSON第二重是校验imagePath指向的图片是否真的存在第三重是统计各类别的样本数量看类别是否严重不平衡。我习惯把这些逻辑合成一个脚本每次换数据集都先跑一遍。import json import os from collections import Counter def check_annotations(json_dir, image_root): total 0 empty 0 class_counter Counter() missing_images [] for json_file in os.listdir(json_dir): if not json_file.endswith(.json): continue with open(os.path.join(json_dir, json_file), r, encodingutf-8) as f: data json.load(f) total 1 img_path os.path.join(image_root, data[imagePath]) if not os.path.exists(img_path): missing_images.append(data[imagePath]) if len(data[shapes]) 0: empty 1 continue for shape in data[shapes]: class_counter[shape[label]] 1 print(fJSON 总数: {total}) print(f空标注 JSON 数: {empty}) print(类别分布:, dict(class_counter)) if missing_images: print(缺失图片示例:, missing_images[:5]) else: print(所有图片路径均有效) check_annotations(./annotations, ./images)这段脚本会把三类问题一次性报告出来空标注文件数量、类别分布、缺失图片。空标注文件一定要处理因为很多训练框架读到空列表会直接报错类别分布如果某一类比另一类少一个数量级建议在标注阶段就补齐不要指望训练时做样本均衡权重。我自己的习惯是每次标注完、转换格式前必跑一遍这个校验脚本。这个习惯救过我一次某次标注到一半图片被移动过目录标签的 imagePath 全指向了不存在的路径如果不是校验跑得早后面转换出来的 YOLO 标签会全部作废。标注是基础工作但基础工作里也全是细节。希望这些内容能帮到你少走点弯路。本文还有配套的精品资源点击获取