尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

VIA标注转COCO格式实战:用MMDetection训练Mask R-CNN

发布时间:2026/9/28 1:07:16

资讯中心
01
ARTICLE

VIA标注转COCO格式实战:用MMDetection训练Mask R-CNN

VIA标注转COCO格式实战:用MMDetection训练Mask R-CNN
简介面向实例分割与Mask R-CNN模型训练的COCO格式数据集已将气球检测任务的标注转换为COCO标准可直接在最新MMDetection框架中训练与评估。开发者、学生或研究人员无需自行处理标注格式适配即可快速验证目标检测与实例分割算法效果。资源包共76个文件核心为74张JPEG图像与2个JSON标注文件分别覆盖训练集与验证集的边界框、分割掩模及类别信息图像按目录组织结构清晰、便于加载。压缩包约36.89MB轻量精巧适合快速试跑模型或教学演示。目前已有512人学习下载测试效果反馈良好数据集标注格式已校验训练时只需修改config指定数据集路径即可起步省去自行收集、标注和格式转换的繁琐流程也适合作为入门MMDetection的配套练习数据方便后续算法对比实验。1. 气球mask-rcnn转COCO数据集为什么我劝你自己转一次而不是直接下载很多人一听到“数据集转换”就头痛觉得不如直接下载一个现成的COCO格式文件。但气球的mask-rcnn转coco的数据集我建议你亲手转一遍手工标注的气球数据集普遍是VIA导出的JSON直接拿给最新的mmdetection框架训练会报格式错误另一方面转一次能让你彻底搞懂COCO的annotations、segmentation、bbox这些字段的对应关系后续换任何小目标数据集都能快速上手。这个场景特别适合刚接触实例分割的工程师和学生——你不需要几十G的官方COCO只需要几十张气球图片就能在Mask R-CNN上跑出一个测试效果不错的模型。2. 先搞懂标注格式VIA JSON和COCO Annotation差在哪2.1 VIA导出的JSON气球数据集最常见的“半成品”格式我处理过不少VIA标出来的数据集导出文件通常是一个字典键是图片文件名值是这张图片的标注信息。每个标注信息里会有 filename、size、regions 这几个常见字段regions 里每一项代表一个标注区域区域里又分成 shape_attributes 和 region_attributes。对于气球这种多边形轮廓shape_attributes 的 name 通常是 polygon然后 all_points_x 和 all_points_y 分别记录多边形顶点的横坐标和纵坐标列表region_attributes 才是类别名比如 balloon也可能写 class 或者 name取决于标注人的随手习惯。这里有个容易忽略的点VIA 的 JSON 不是“图片数组标注数组”而是按文件名索引的字典。这意味着如果你直接把整个 JSON 丢给 mmdetection 的 CocoDataset它根本不认识。CocoDataset 期望的是 COCO 官方格式也就是 images、annotations、categories 三个数组。所以转换的第一步是把“按文件索引”的结构展开成“按标注对象排列”的结构。另外图片的宽高并不总是可靠地写在 JSON 里。有些版本 VIA 会写 size 字段包含 width 和 height有些只写文件大小甚至不写。为了避免训练时 resize 和目标框对齐出错最佳做法是用 PIL 或 OpenCV 读一下真实图片尺寸然后写进转换脚本。这也是我反复强调的不要相信标注文件里的 size要相信你手里的图片文件。2.2 COCO 的 images、annotations、categories 三段式COCO 是“标注容器”格式images 数组里每项是图片元信息包含 id、file_name、width、heightannotations 数组里每项是一个目标实例包含唯一 id、所属图片 id、category_id、segmentation、bbox、area、iscrowdcategories 数组里每个类别有 id、name、supercategory。对实例分割来说segmentation 是核心它有两种形式polygon多边形适用于非遮挡单实例和 RLE游程编码适用于 crowd 或复杂形状。Mask R-CNN 在训练时会从 polygon 生成二值 mask 作为监督信号所以手工标注的气球直接转 polygon 是最省事的。但要注意 polygon 的格式是“展平后的坐标数组套一层外包装”也就是[[x1,y1,x2,y2,...]]。VIA 给你的 all_points_x 和 all_points_y 是分离的两个列表如果不做 zip 和展平生成的 segmentation 就会变成[[x1,x2,...],[y1,y2,...]]这种错误结构训练时 mask 会乱套。bbox 字段也容易错。COCO 的 bbox 是[x1, y1, w, h]而 polygon 算出来的是左上角右下角。很多人直接把 polygon 里最小的 x、最小 y 当作 bbox 的 x、y却忘了把 w 和 h 算对。更隐晦的问题是 areaCOCO 的 area 是分割区域的真实面积不是 bbox 面积。如果用 bbox 面积替代mAP 按面积分组统计时会出错小目标和大目标的比例完全失真。2.3 转换前先做三件事确认类别、检查分割区域、统一图片路径我在做数据准备时拿到一份标注先不急着写代码花十分钟确认三件事。第一件类别名称到底存在哪个字段。很多标注者在 region_attributes 里写的不是 “class” 而是 “type” 或者 “label”甚至有人把类别写进了 shape_attributes 的 name。转换脚本一旦写死字段名最后跑出来 categories 是空的mmdetection 训练时会直接报“类别数为0”或者开始即退出。第二件一张图是不是只有一个 region。气球常常会互相重叠官方 VIA 可以给一张图画多个 region这是正常的每个 region 对应一个单独的 annotation。但有人会把一个气球拆成两个 region或者在同一个 region 里包含了多个不相连的轮廓。这时候要决定是合并成一个 annotation 还是保留多个。对于 Mask R-CNN 来说同一个实例有多个不相连区域也是允许的COCO 的 segmentation 可以是多个 polygon 组成的 list也就是[[x1,...],[x2,...]]这种嵌套结构但区域只能属于同一个实例。实践中我通常一个 region 生成一个 annotation避免二义性。第三件图片路径要统一。mmdetection 里 data_input 的 data_prefix 会拼接到图片路径前如果你转换时写的 file_name 带绝对路径训练机一换路径就全废。我习惯是把图片拷到一个 images/ 目录file_name 只写相对路径比如 “images/xxx.jpg”。这样在训练配置里 data_prefix 可以省略或者只用 ‘images/’ 作为前缀。3. 写一个可复用的转换脚本VIA标注到COCO JSON的完整实现3.1 从VIA JSON读取并构建categories和images我用最普通的方式写只用 Python 标准库 json、os、random外加 PIL 读图片尺寸。这样任何人拿到代码都能跑不需要额外安装 pycocotools。import json, os, random from PIL import Image base_dir data/balloon def load_via(via_path): with open(via_path, r, encodingutf-8) as f: via json.load(f) # via 是 {filename: { ... }} 结构 images [] for fname, record in via.items(): # 读取真实尺寸防止 via 里的 size 不可靠 img_path os.path.join(base_dir, record[filename]) try: with Image.open(img_path) as im: w, h im.size except Exception: w, h record.get(size, {}).get(width, 0), record.get(size, {}).get(height, 0) images.append({ id: len(images), file_name: record[filename], width: w, height: h, annotations: record[regions] }) return images说明这里base_dir是图片所在目录需要提前定义。关键是不能用 via 里的 size 当真实尺寸而用 PIL 读。record[regions]是 VIA 的 region 列表后面单独解析。3.2 把region的polygon转换成segmentation、bbox和area这一步是核心。每个 region 的 shape_attributes 里有 all_points_x 和 all_points_y长度相同。COCO 的 polygon 是“展平后每个点一个坐标对”的列表所以先 zip 再展平形成[[x1, y1, x2, y2, ...]]这种多一层结构然后算 bbox 和面积。def polygon_to_coco(shape_attrs): xs shape_attrs[all_points_x] ys shape_attrs[all_points_y] coords [] for x, y in zip(xs, ys): coords.extend([float(x), float(y)]) segmentation [coords] # 计算 bbox x_min min(xs); x_max max(xs) y_min min(ys); y_max max(ys) bbox [x_min, y_min, x_max - x_min, y_max - y_min] # 计算多边形面积鞋带公式 area 0.0 for i in range(len(xs)): j (i 1) % len(xs) area xs[i] * ys[j] - xs[j] * ys[i] area abs(area) / 2.0 return segmentation, bbox, area说明坐标全部转成 float因为标注可能画出了浮点坐标。area 用鞋带公式而不是 bbox 面积是为了保证 pycocotools 在按面积分桶时统计准确。bbox 是左上角加宽高的结构不是x_min, y_min, x_max, y_max。3.3 生成 annotation 列表并划分 train/val我这里设置类别只有 balloon 一类category_id 从1开始。为了便于小样本复现随机划分时固定 seed42按图片划而不是按样本划否则同一张图既出现在训练又出现在验证测试效果会虚高。def build_coco_json(images, output): coco { info: {description: balloon dataset}, licenses: [], images: [], annotations: [], categories: [{id: 1, name: balloon, supercategory: none}] } ann_id 1 for img in images: coco[images].append({ id: img[id], file_name: img[file_name], width: img[width], height: img[height] }) for region in img[annotations]: # 有的标注在 region_attributes 类别字段是 class 或 label取到后统一映射 seg, bbox, area polygon_to_coco(region[shape_attributes]) coco[annotations].append({ id: ann_id, image_id: img[id], category_id: 1, segmentation: seg, bbox: bbox, area: area, iscrowd: 0 }) ann_id 1 with open(output, w, encodingutf-8) as f: json.dump(coco, f, ensure_asciiFalse) print(f{output}: {len(coco[images])} images, {len(coco[annotations])} anns)说明如果 region_attributes 里类别不是 balloon你可以先读出来再映射成一个 int我在这里简写为全部映射到1。实际项目里建议把类别映射表打印出来核对一遍。调用脚本时我一般先把所有图片按文件名排序再按 8:2 切成两个列表分别调用 build_coco_json。注意这里的 file_name 只写相对路径比如images/xxx.jpg这样配置里的 data_prefix 就能正常工作。3.4 用pycocotools自检转换结果转换完必须自检不然 mmdetection 训练时还是要翻车。常见自检是全量遍历 annotation检查 segmentation 是否为空、bbox 是否在图片范围内、area 是否为正。from pycocotools.coco import COCO val_coco COCO(data/balloon/annotations/coco_val.json) ids val_coco.getAnnIds() for ann_id in ids: ann val_coco.loadAnns(ann_id)[0] assert len(ann[segmentation]) 0, ann_id img val_coco.loadImgs(ann[image_id])[0] x, y, w, h ann[bbox] assert 0 x w img[width], ann_id assert 0 y h img[height], ann_id print(Validation ok.)说明pycocotools 是 mmdetection 训练和评估都会用到的库直接用它加载自己的 json 是成本最低的校验方式。如果这里报错就回头改转换脚本如果这段能跑通数据格式基本没问题。4. 用MMDetection训练气球Mask R-CNN从环境到配置4.1 环境搭建最新mmdetection与依赖的匹配我建议用 conda 虚拟环境按 OpenMMLab 官方的顺序安装。最新 mmdetection3.x需要 mmengine 和 mmcv不要自己一个个 pip install用 openmim 能自动匹配版本组合。conda create -n mmdet python3.8 -y conda activate mmdet pip install openmim mim install mmengine mim install mmcv pip install mmdet安装完成后验证版本并确认能用 torch 检测 GPUpython -c import torch, mmcv, mmdet; print(torch.__version__, mmcv.__version__, mmdet.__version__)说明mmdet 3.x 对 torch 的版本要求由 mmcv 决定所以不要手动装 mmcv-full而是让 mim 去解析。如果你已有 PyTorch 环境也可以只换 mmcv我一般直接新建环境避免把原有的模型环境搞挂。4.2 数据目录与数据集注册让CocoDataset认识气球类别把转换好的 json 和图片按下面结构放data/balloon/ ├── annotations/ │ ├── coco_train.json │ └── coco_val.json └── images/mmdetection 3.x 里 CocoDataset 默认从 data_root 下找注释和图片。在配置文件里用 metainfo 声明类别不需要写自定义 Dataset 类。# balloon_config.py _base_ ../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py num_classes 1 data_root data/balloon/ metainfo dict(classes(balloon,), palette[(220, 20, 60)]) dataset_type CocoDataset train_dataloader dict( batch_size2, datasetdict( typedataset_type, data_rootdata_root, metainfometainfo, ann_fileannotations/coco_train.json, data_prefixdict(imgimages/))) val_dataloader dict( datasetdict( typedataset_type, data_rootdata_root, metainfometainfo, ann_fileannotations/coco_val.json, data_prefixdict(imgimages/))) max_epochs 50 train_cfg dict(typeEpochBasedTrainLoop, max_epochsmax_epochs, val_interval5) default_hooks dict( checkpointdict(typeCheckpointHook, interval10), loggerdict(typeLoggerHook, interval10))说明_base_指向官方 mask-rcnn_r50_fpn_1x_coco.py后面的字段都是覆盖。注意 train_dataloader 里 batch_size 我设置为2因为小格式图片内存占用很低如果你的 GPU 只有 8Gbatch_size2 通常也能跑。val_interval5 表示每5个epoch在验证集上评估一次。4.3 调整模型头和预训练权重小数据集的正确起步方式因为只有一类必须把模型的 bbox_head 和 mask_head 的 num_classes 改成1。官方配置默认80类不改成1会在反传时类别数不匹配。同时为了快速收敛建议把加载预训练权重的配置显式打开。model dict( roi_headdict( bbox_headdict(typeShared2FCBBoxHead, num_classesnum_classes), mask_headdict(num_classesnum_classes))) load_from checkpoints/mask_rcnn_r50_fpn_1x_coco.pth说明load_from接受本地权重文件路径。你需要先从 mmdetection 的 model zoo 下载对应权重放到 checkpoints/ 目录或者直接用官方 base 配置里已有的load_from。如果不配置模型会从 COCO 预训练权重开始微调效果也会好但收敛速度会慢很多。4.4 启动训练、日志解读与验证评估训练命令很简单python tools/train.py configs/balloon_config.py如果没有指定工作目录日志和权重会输出到work_dirs/balloon_config/。训练时关注终端输出的 loss_rpn_cls、loss_cls、loss_mask它们应该缓慢下降。因为气球数据集很小val mAP 可能会在第一次评估就直接跳到 0.8 甚至更高这是正常的——背景干净、目标单一、形状规则Mask R-CNN 很容易拟合。等训练结束用下面的命令在验证集上跑出 COCO 格式的指标python tools/test.py configs/balloon_config.py work_dirs/balloon_config/epoch_50.pth --metrics coco说明--metrics coco会输出包括 mask AP、bbox AP 在内的一组指标。你只需要重点看mask_AP和mask_AP50这两个值分别是全 IoU 下的平均 mask 精度和在 IoU0.5 下的 mask 精度。对气球这种形状规则的目标AP50 通常能到 0.9 以上。如果有多张 GPU想加速验证可以用分布式命令bash tools/dist_test.sh configs/balloon_config.py work_dirs/balloon_config/epoch_50.pth 2 --metrics coco说明命令最后的数字是 GPU 卡数。数据量小的话单卡测试也就几十秒完全不必要追求分布式。多卡更常见的用途是同时跑不同阈值探索而不是为了省那点时间。5. 转换与训练避坑5个让脚本翻车的细节这些坑我先替大家踩过很多从VIA转COCO的项目都折在这几个细节上。下面五条每一条都有明确的排查路径和解决方式。5.1 segmentation坐标必须展平否则训练崩溃现象训练刚开始两步就报segmentation格式错误或者 pycocotools 计算 mask IoU 时直接返回 0。更多时候是训练日志里出现TypeError: object of type float has no len()。原因VIA 给的是 all_points_x 和 all_points_y 两个平行列表如果把它们直接放进 segmentation结构会变成[[x1,x2,...],[y1,y2,...]]。在 COCO 里这表示两个多边形第一个只有 x 坐标第二个只有 y 坐标显然不可能是闭合轮廓。解决必须先把两个列表 zip 成点对再全部展平。我习惯专门写一个小函数跑一遍所有 region把 segmentation 打出来检查前三个点是不是[x1, y1, x2, y2, x3, y3]这样的顺序。转换完成后用 pycocotools 加载 json随机抽 20 张检查 segmentation 的长度都是偶数。5.2 图片宽高不写真实值处理时直接炸现象训练时不报错但验证时 mask AP 很低而且可视化发现 mask 整体偏移或者 resize 时报IndexError。原因VIA json 里的 size 字段可能来自标注时的预览图也可能只是文件大小mmdetection 的 CocoDataset 在 load dataset 时先读 images 的宽高再与真实图片做匹配。宽高不一致mask 和 bbox 就会错位。解决在转换脚本里用 PIL 或图像库读取真实尺寸。读取一次会多一点磁盘 I/O但总比训练到一半炸掉强。转换后也建议抽几张图比对 json 里的 width、height 和图像真实尺寸。5.3 训练集太小直接导致过拟合现象训练 loss 快速下降训练集 AP 高于验证集很多验证 mask AP 从 0.8 跌到 0.5。原因气球数据集往往只有几十到两百张图官方 Mask R-CNN 是在 COCO 上预训练的模型但微调时如果只跑 12 epoch模型容易把训练集图像细节背下来。另一个原因是尺度抖动范围太广小图像被拉到 1333、800 的尺寸然后 pad 成黑边浪费大量训练信息。解决把训练轮次提高到 50 到 100把数据增强改成更适合小图像的方式。我会在配置里把 train_pipeline 的 Resize 改成两个候选 scale比如 512 和 640并加 RandomFlip 概率 0.5。学习率也适当降低比如lr0.0005并且用weight_decay0.0001。这样能让模型在有限的样本里学到更通用的形状特征而不是背诵每张图。5.4 bbox的x,y用了坐标系不一致的值现象训练结束评估时mask AP50 正常但 mask AP 明显偏低且 AR100 低得离谱。原因bbox 的 w/h 出现负数或者零被 pycocotools 当作无效框丢弃。常见于从 polygon 算 bbox 时只存了 min 和 max但没按 (min_x, min_y) 和 (w,h) 输出也可能把 x 坐标错写成了 y 坐标。解决统一计算顺序先算 x_min、y_min、x_max、y_max再转成[x_min, y_min, x_max-x_min, y_max-y_min]。自检时写一个循环检查所有 bbox 的第三、第四个元素大于 0一旦出现负值就把对应 ann 号和 image 号打印出来回标注文件里看一眼是否有多边形自相交。5.5 palette不配分布式训练直接报错现象多卡训练时出现ValueError: Class names are not supported in palette或palette is required when use multi-gpu test。原因mmdetection 3.x 的 CocoDataset 在初始化时会检查metainfo[palette]如果没有给代码会在多进程回调里尝试生成颜色反而失败。解决在配置的metainfo里显式写palette[(220, 20, 60)]一个类别给一个颜色。这个颜色只用于可视化不影响训练。单卡训练虽然可能不报错但建议也写上避免调试可视化时黑屏。6. 从“能用”到“好用”用mmdet的推理接口可视化预测结果在验证集上跑完 COCO 指标只是第一步真正判断测试效果好我会把预测结果画在原图上肉眼检查边界和重叠。mmdet 3.x 的tools/test.py配合--out能保存预测结果但我更常用一行命令直接出图python demo/image_demo.py \ configs/balloon_config.py \ work_dirs/balloon_config/epoch_50.pth \ --images data/balloon/images \ --show-dir outputs说明--images传图片目录或单张图路径--show-dir指定可视化输出。跑完后打开 outputs 里的 png看 mask 和原图的贴合程度比看数字更直观。如果想要批量输出 COCO 格式的预测结果用于后续分析可以用python tools/test.py configs/balloon_config.py work_dirs/balloon_config/epoch_50.pth --out results.pkl之后用mmdet.utils里的脚本从 pkl 里转出 json再做业务逻辑处理。对于只有一类的气球分割我通常直接改image_demo.py的输入输出参数把结果插入到自己的推理管线里。最后说一个我的习惯小数据集训练我会把随机种子固定转换脚本固定 seed配置里也固定 seedmmdet 默认 fixed_seedTrue。否则同一份数据每次跑出来的 AP 都不同很难判断是改了参数还是随机性带来的提升。固定住随机性之后再去对比不同配置结论才可信。这个方案做下来最值钱的是你手里多了一条从任意 VIA 标注到 mmdetection 训练环境的成熟链路而不是某个特定数据集的结果。下次拿到任何手工标注的多边形数据你都可以直接复用这套转换脚本和配置模板把精力放在调参上。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。