首页 / 资讯中心 / 文章详情

Mask R-CNN图像标注与COCO/YOLO格式转换实战指南

Mask R-CNN图像标注与COCO/YOLO格式转换实战指南 ★ FEATURED ARTICLE
简介这是一款面向深度学习初学者与数据工程师的轻量级图像标注工具专为Mask R-CNN等实例分割模型的数据集制作而设计支持矩形框与多边形标注兼顾VIA和LabelMe的交互逻辑同时提供完整C#源码便于二次开发与教学演示。资源包共65个文件含20个核心C#源码如Form1.cs、DrawShapes.cs、SqLiteHelper.cs、3个可执行程序exe及配套配置config、图标ico、资源文件resx/resources和数据库db结构清晰模块职责分明附带《使用说明.docx》与Visual Studio解决方案sln开箱即用。目前已有2340人学习下载适合需快速构建高质量分割样本、理解标注工具底层实现或开展课程实验的开发者与学生。1. 图像标注工具包专为 Mask R-CNN 样本制作而生的轻量级落地方案你手头有一批工业缺陷图、医疗切片或遥感影像想训一个能抠出像素级轮廓的 Mask R-CNN 模型但卡在第一步——标注。不是标框是标掩码mask不是标一次是标成千上万张带语义分割标签的图不是用 Photoshop 手动画而是要导出 COCO 或 VOC 格式、能直接喂进 detectron2/mmdetection 的训练 pipeline。这时候VIA 和 LabelMe 这类通用工具就暴露短板了VIA 不原生支持 mask 导出为 polygon 坐标序列LabelMe 默认导出 JSON 结构松散、字段命名不统一和 detectron2 的COCODataset加载器对不上号。本资源不是另一个 GUI 工具而是一套经过某实验室真实项目验证的「标注-清洗-转换」三件套含定制化 LabelMe 启动脚本、VIA 配置模板、以及核心的mask_to_coco.py/voc_to_yolo_mask.py转换器。它不替代标注动作本身但把标注后 70% 的脏活——格式校验、坐标归一化、类别映射、无效 mask 过滤、小目标剔除——全写死了。适合正在跑通第一个 Mask R-CNN pipeline 的工程师也适合带学生做课程设计的某高校导师。如果你已经标注了 300 张图却还在手动改 JSON 字段这篇就是你的后悔药。2. 为什么选 VIA LabelMe 组合不是图省事是图可控性与可审计性Mask R-CNN 对样本质量极其敏感一个错位的多边形顶点、一个漏标的 instance、一个坐标越界的 mask都可能让 loss 突然爆炸或 AP 在 val 上掉 15 个点。因此标注阶段不能只看“有没有”更要看“能不能被模型正确读取”。VIA 和 LabelMe 并非最优 UI但它们在三个关键维度上胜过商业工具一是输出结构完全透明JSON 纯文本无二进制黑匣子二是支持离线部署不依赖云服务或账号体系三是配置项可编程控制比如 VIA 的region_shape_attributes字段可强制限定为polygonLabelMe 的--nodata参数可跳过空图。本资源正是基于这个前提构建所有转换脚本的输入假设都严格对齐 VIA 1.0.19 和 LabelMe 5.4.1 的原始输出规范不加任何中间层抽象。这意味着你不需要学新工具只需微调已有流程——这比推倒重来、迁移到某个“更智能”的标注平台风险更低、复现更快。2.1 VIA 标注实操从启动到导出 JSON 的四步闭环VIA 的默认行为对 Mask R-CNN 不友好它允许用户画矩形、椭圆、点等多种 shape但 Mask R-CNN 只认polygon它默认把 region 存在regions数组里但没强制要求shape_attributes必须含all_points_x/all_points_y它导出的 JSON 顶层是metadata而 detectron2 期望的是imagesannotations结构。本资源提供via_config.json模板必须在启动 VIA 前加载{ project: { name: mask_rcnn_sample, file: {image: {enable: true, dir: ./images/}}, region: {shape: [polygon], attribute: [category]} } }提示region: {shape: [polygon]}是硬性约束——VIA 会禁用所有非 polygon 的绘图按钮从源头杜绝误操作。attribute: [category]则强制每个 polygon 必须填类别名如scratch、crack避免后期靠索引猜类别。启动命令Linux/macOSpython3 via-standalone.html --port8001 --configvia_config.json注意via-standalone.html是 VIA 官方单文件版本资源已打包对应版本v3.1.0无需 npm install。标注完成后点击右上角「Export Project」→ 选择「Export as JSON (VIA project format)」保存为via_export.json。此文件即为后续转换的唯一输入源。2.2 LabelMe 标注实操规避 JSON 字段漂移的启动参数LabelMe 的坑在于版本迭代导致 JSON schema 变动LabelMe 4.x 的shapes里points是[x,y]对数组5.x 改为[[x,y],[x,y]]imagePath在 4.x 是相对路径5.x 默认绝对路径。本资源适配 LabelMe 5.4.1Python 3.8启动时必须加两个参数labelme --nodata --output-format polygon --labels labels.txt--nodata跳过图像数据嵌入即不把 base64 图存进 JSON减小文件体积避免imageData字段干扰解析--output-format polygon强制所有标注 shape 输出为polygon类型即使你画了矩形也会转成 4 点 polygon保证结构一致性--labels labels.txt指定类别文件内容为每行一个类别名如defect,background确保shape[label]字段值严格来自该列表防止拼写错误。标注完后LabelMe 自动生成xxx.json其关键结构如下{ version: 5.4.1, flags: {}, shapes: [ { label: scratch, points: [[120.5, 80.2], [150.1, 80.3], [150.0, 110.8], [120.6, 110.7]], shape_type: polygon } ], imagePath: IMG_001.jpg, imageHeight: 1024, imageWidth: 1280 }注意points必须是浮点数非整数这是 detectron2 多边形渲染的隐式要求imageHeight/imageWidth必须存在且准确否则坐标归一化会出错。本资源的labelme_to_coco.py脚本会校验这两项缺失则报错退出不静默修复。2.3 标注质量预检三行命令筛出 90% 的低质样本标注完成不等于可用。我们曾在一个 PCB 缺陷项目中发现12% 的标注图存在points少于 3 个顶点无法构成 polygon、8% 的imageWidth/imageHeight为 0、5% 的label值不在labels.txt中。这些图若直接进训练会在dataloader的__getitem__阶段随机 crash极难定位。本资源提供precheck.py运行后生成quality_report.csvpython3 precheck.py \ --input_dir ./labelme_annos/ \ --labels_file labels.txt \ --min_vertices 3 \ --min_dim 64参数说明--input_dirLabelMe 生成的.json文件所在目录--labels_file与labelme --labels指定的同一文件用于校验 label 合法性--min_vertices 3polygon 至少含 3 个顶点三角形是最小合法 polygon--min_dim 64图像宽高均不得小于 64 像素过滤过小缩略图。脚本逻辑遍历每个 JSON提取shapes→ 检查len(points) 3→ 检查imageWidth 64 and imageHeight 64→ 检查label in labels_set。任一失败则记录到 CSV并输出具体文件名与错误类型。这不是可选项是必跑步骤——我带过的三个课程设计组全部在第一次 run train 之前漏了这步结果 debug 时间远超标注时间。3. 核心转换器详解从原始 JSON 到 detectron2 可加载的 COCO 格式Mask R-CNN 的训练入口如 detectron2 的DefaultTrainer只认标准 COCO 格式一个instances_train2017.json文件含images、annotations、categories三大顶级 key。VIA 和 LabelMe 的输出与此结构相去甚远需做字段映射、坐标变换、ID 重编。本资源的mask_to_coco.py不是简单 copy-paste 的脚本而是按 detectron2 v0.6 的COCODataset加载逻辑反向设计的——它生成的 JSON能直接作为register_coco_instances(my_dataset, {}, path/to/instances_train2017.json, path/to/images/)的输入。3.1 VIA JSON → COCOvia_to_coco.py的字段映射逻辑VIA 导出的via_export.json是扁平结构顶层metadata下挂file_list和file_attributesregion 数据分散在各file_list[i][regions]中。via_to_coco.py的核心任务是重组为 COCO 的三层嵌套images数组每个元素含id自增 int、file_name相对路径、width、heightannotations数组每个元素含id自增 int、image_id对应images[i][id]、category_id查表得、segmentationpolygon 坐标转[x1,y1,x2,y2,...]、area多边形面积用 shoelace 公式计算、bbox包围盒[x,y,width,height]categories数组每个元素含id从 1 开始、name类别名、supercategory固定为none。执行命令python3 via_to_coco.py \ --via_json via_export.json \ --image_dir ./images/ \ --output_json coco_via_train.json \ --categories defect,scratch,crack参数说明--via_jsonVIA 导出的原始 JSON--image_dir图像所在目录用于提取width/height若 JSON 中缺失--output_json输出 COCO 格式文件路径--categories逗号分隔的类别字符串顺序即为category_iddefect1,scratch2,crack3。关键细节segmentation字段必须是 float 类型的一维数组非嵌套 list且长度为偶数area必须为正数脚本会过滤 area≤0 的 polygonbbox的x/y是左上角坐标非中心点。这些在 detectron2 的COCOInstanceNewBaselineDatasetMapper中有硬性校验本脚本提前满足。3.2 LabelMe JSON → COCOlabelme_to_coco.py的坐标归一化处理LabelMe 的points是像素坐标而 detectron2 的segmentation字段虽也是像素坐标但其bbox计算逻辑依赖imageWidth/imageHeight的准确性。labelme_to_coco.py的核心增强在于它不直接复制points而是先做一次「坐标归一化检查」——计算每个点是否在[0, imageWidth] × [0, imageHeight]范围内越界则 clip 并 warn# 伪代码逻辑实际在 labelme_to_coco.py 中 for point in shape[points]: x, y point x np.clip(x, 0, image_width) y np.clip(y, 0, image_height) if abs(x - point[0]) 1e-3 or abs(y - point[1]) 1e-3: warnings.warn(fClipped point {point} to ({x:.1f},{y:.1f}) in {json_path})执行命令python3 labelme_to_coco.py \ --input_dir ./labelme_annos/ \ --output_json coco_labelme_train.json \ --labels_file labels.txt \ --image_ext .jpg参数说明--input_dirLabelMe JSON 目录--output_json输出 COCO JSON--labels_file类别文件用于生成categories--image_ext图像扩展名.jpg或.png用于匹配 JSON 中的imagePath。血泪经验某次实验因imagePath写成IMG_001.JPG大写 JPG而image_ext设为.jpg脚本找不到图imageWidth/imageHeight取 0后续所有 bbox 计算失效。从此我养成了习惯运行前先ls ./labelme_annos/*.json | head -3 | xargs -I{} jq -r .imagePath {}看实际后缀。3.3 COCO 格式验证用 detectron2 自带工具做最终确认生成coco_train.json后别急着 train。先用 detectron2 的COCODataset加载器做一次「冷启动验证」确认无字段缺失、ID 错位、坐标越界from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.coco import load_coco_json # 注册数据集仅验证不训练 DatasetCatalog.register( my_coco_test, lambda: load_coco_json(coco_labelme_train.json, path/to/images/, my_coco_test) ) MetadataCatalog.get(my_coco_test).set(thing_classes[defect, scratch, crack]) # 尝试加载第一条数据 dataset_dicts DatasetCatalog.get(my_coco_test) print(Loaded, len(dataset_dicts), images) print(First image:, dataset_dicts[0][file_name]) print(First annotation bbox:, dataset_dicts[0][annotations][0][bbox])若成功打印说明 JSON 结构 100% 合规若报KeyError: image_id或IndexError说明via_to_coco.py或labelme_to_coco.py的 ID 映射有 bug需回查脚本中的image_id生成逻辑应为i1非i。4. 避坑指南标注与转换阶段最常翻车的五个现场这些坑我在三个不同项目里至少踩过两次。不是文档没写是它们藏得太深往往在训练跑了一半才暴露debug 成本极高。以下按「现象 → 原因 → 解决」列出每一条都对应真实报错日志。4.1 现象RuntimeError: Expected all tensors to be on the same device原因VIA 导出的 JSON 中某些 region 的shape_attributes缺失all_points_x/all_points_y字段比如用户画了矩形但未切换为 polygon导致via_to_coco.py生成的segmentation为[]后续torch.tensor([])默认在 CPU而模型在 GPUdevice mismatch。解决在via_to_coco.py的parse_region()函数开头加强制校验if all_points_x not in region[shape_attributes] or all_points_y not in region[shape_attributes]: print(fSKIP region without polygon points in {file_name}) continue4.2 现象ValueError: not enough values to unpack (expected 4, got 0)原因LabelMe 的points数组为空[]常见于用户点了「Create Polygons」但未画任何点就按 CtrlEnter。labelme_to_coco.py将其转为segmentation[]detectron2 的PolygonMasks初始化时报错。解决在labelme_to_coco.py的process_shape()函数中添加if len(shape[points]) 3: print(fSKIP shape with {len(shape[points])} points in {json_path}) continue4.3 现象AssertionError: A tensor is not a valid mask!原因segmentation坐标是整数如[120,80,150,80,...]但 detectron2 的mask_utils.decode()要求 float32。VIA 的all_points_x默认是 string若未转 float 会出错。解决via_to_coco.py中all_points_x/all_points_y必须显式转floatx_coords [float(x) for x in region[shape_attributes][all_points_x]] y_coords [float(y) for y in region[shape_attributes][all_points_y]]4.4 现象IndexError: list index out of range在dataloader的__getitem__原因coco_train.json中annotations数组为空即没有一条 annotation但images有 100 条。detectron2 默认认为每张图至少有一个 instance空图会被跳过但若 batch_size2 且两张图都空则batch为[]后续操作崩。解决precheck.py已包含--min_annotations 1参数运行时必须开启python3 precheck.py --input_dir ./labelme_annos/ --min_annotations 1并删除所有annotations为空的 JSON。4.5 现象训练 loss 为 nanval AP0原因segmentation坐标中存在inf或nan如某次 LabelMe 导出时points为[[inf, nan], [150,80], ...]源于图像元数据损坏。解决在labelme_to_coco.py的process_shape()中添加 numpy 级校验import numpy as np points np.array(shape[points]) if not np.isfinite(points).all(): print(fSKIP shape with inf/nan points in {json_path}) continue5. 进阶技巧用coco_to_yolo_mask.py一键生成 YOLOv8-Seg 兼容格式虽然 Mask R-CNN 是主流但越来越多团队在边缘设备部署时转向 YOLOv8-Seg——它 inference 更快对小目标更鲁棒且官方提供了ultralytics库的完整训练 pipeline。但 YOLOv8-Seg 的标注格式和 COCO 完全不同它要求每张图一个.txt文件每行一个 instance格式为class_id center_x center_y width height x1 y1 x2 y2 ...polygon 点坐标归一化到[0,1]。手动转1000 张图就是 1000 个 txt每个 txt 里几十行坐标错一位就 mask 错位。coco_to_yolo_mask.py就是为此而生。5.1 YOLOv8-Seg 格式规范与coco_to_yolo_mask.py的映射规则YOLOv8-Seg 的.txt文件如IMG_001.txt内容示例0 0.521 0.432 0.124 0.087 0.512 0.421 0.530 0.421 0.530 0.443 0.512 0.443 1 0.712 0.623 0.098 0.072 0.705 0.615 0.719 0.615 0.719 0.631 0.705 0.631第 1 列class_id从 0 开始第 2~5 列center_x center_y width height归一化 bbox第 6 列起x1 y1 x2 y2 ...归一化 polygon 点必须为偶数个且 ≥6 个。coco_to_yolo_mask.py的核心逻辑读取coco_train.json对每个image创建同名.txt如IMG_001.jpg→IMG_001.txt对每个annotation计算center_x (bbox[0] bbox[2]/2) / image_width同理center_y、width、height对segmentation将每个x,y归一化x_norm x / image_widthy_norm y / image_height拼接为一行写入.txt。执行命令python3 coco_to_yolo_mask.py \ --coco_json coco_labelme_train.json \ --image_dir ./images/ \ --output_dir ./yolo_labels/ \ --classes defect,scratch,crack参数说明--coco_json输入 COCO JSON--image_dir图像目录用于读取width/height--output_dir输出.txt目录--classes类别字符串用于映射category_id→class_iddefect0,scratch1,crack2。注意YOLOv8-Seg 要求所有 polygon 点数一致如全为 20 点但实际标注点数不等。本脚本不做插值而是保留原始点数——ultralytics的AutoShape会自动处理变长点序列无需 pad。5.2 验证 YOLOv8-Seg 标签用plot_yolo_mask.py可视化检查生成.txt后不能只信文件存在。必须可视化抽查确认归一化无偏移、类别 ID 正确、polygon 闭合。plot_yolo_mask.py读取一张图及其.txt用 OpenCV 绘制 mask 轮廓并叠加原图python3 plot_yolo_mask.py \ --image_path ./images/IMG_001.jpg \ --label_path ./yolo_labels/IMG_001.txt \ --classes defect,scratch,crack \ --output_path ./vis/IMG_001_vis.jpg它会生成IMG_001_vis.jpg其中原图灰度显示defect用红色轮廓scratch用绿色轮廓crack用蓝色轮廓每个轮廓旁标注class_id和点数。若看到轮廓严重偏移如整个 mask 在图外、颜色错乱class_id0却画成绿色说明--classes顺序与coco_to_yolo_mask.py中的category_id映射不一致需核对coco_train.json的categories数组。5.3 从 COCO 到 YOLO 的边界为什么不用coco_to_yolo.py非 seg 版网上很多coco_to_yolo.py只转 bbox不转 segmentation。它们输出的.txt是0 0.521 0.432 0.124 0.087 1 0.712 0.623 0.098 0.072这只能用于 YOLOv8-Detect不能用于 YOLOv8-Seg。YOLOv8-Seg 的训练必须有 polygon 坐标否则loss_mask项为 0模型只学 bbox。本资源的coco_to_yolo_mask.py是专为 Seg 场景写的它确保第 6 列起永远是成对的x_norm y_norm且数量 ≥6。这是区分「能跑」和「能训好」的关键。从那以后我每次导出 YOLO 格式都强制走一遍plot_yolo_mask.py抽查 5 张图——哪怕项目 deadline 压得只剩 2 小时。因为 mask 错位的模型训得再久也是在拟合噪声。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站