简介:这是一份将气球Mask R-CNN标注转换为COCO格式的现成数据集,专为使用最新MMDetection框架训练实例分割模型而准备,适合正在学习深度学习目标检测或需要快速搭建分割实验的研究者与开发者。压缩包内共有76个文件,包括74张不同场景下的气球JPEG原图,以及instances_train2017.json和instances_val2017.json两份标准COCO标注文件,压缩后整体大小仅36.89MB。数据集已按COCO规范组织,包含边界框与逐像素分割掩模,可直接被MMDetection读取,省去自行转换标注的繁琐过程,下载即可投入训练。利用这份数据集可快速验证Mask R-CNN在气球类别上的分割效果,也能作为进阶学习或课程设计的基础数据,帮助理解实例分割从数据准备到模型推理的完整链路。目前已有512人浏览学习,对于想避开格式坑、专注调参和评估的实践者而言,是一个性价比很高的入门数据集。
1. 让 Mask R-CNN 在 MMDetection 里跑通:为什么这份气球 COCO 数据集值得先试
几十张气球照片,真的能训练出一个可用的 Mask R-CNN?我最初也怀疑。亲手把这份已经转成 COCO 格式的气球数据集接进 MMDetection 后,发现它的价值不在“刷点”,而在用最小成本把实例分割的完整链路走一遍。资源本身不复杂:一个解压后就是 train2017、val2017 和两个 instances JSON 的包,直接替换官方 COCO 数据集的路径就能训练。
它解决的是最烦人的数据集适配问题。原版 balloon 示例用的是 VIA 标注格式,而 MMDetection 默认吃 COCO JSON,如果自己写转换脚本,光是 polygon 转 segmentation、算 bbox 和面积就够折腾半天。这份资源把这些做完了,训练和测试效果也经过验证。
适合两类人:刚接触 MMDetection、想确认环境与训练链路的人;以及业务上要做实例分割、想先用小数据集摸清 Mask R-CNN 收敛行为的人。下面从数据结构开始拆,再讲怎么接进框架、参数怎么调,最后把几个典型翻车点摆出来。
2. 数据集本体:COCO 化到底改了什么
2.1 从 VIA 标注到 COCO JSON:转换的核心差异
原版 Matterport Mask R-CNN 的气球示例,标注是基于 VIA(VGG Image Annotator)生成的。VIA 的导出结构是“一张图片一条记录”,每个标注区域放在regions里,坐标是 polygon 点列,没有统一的类别表,也没有全局的 image id 和 annotation id。这种格式能被原仓库的utils直接读取,但喂给 MMDetection 就完全不是一回事。
把这个数据集转成 COCO 格式,本质上做了四件事:
第一,把每张图片的 metadata(文件名、宽高、id)汇总到images数组,并和文件夹里的图片一一对应。第二,把每个标注区域的 polygon 坐标改写成 COCO 的segmentation字段,形式是[[x1,y1,x2,y2,...]]这种嵌套列表,同时计算出bbox和area。第三,建立全局的categories表,气球这个类别放在id: 1。第四,把iscrowd统一设为 0,表示这些标注都是非密集遮挡的单实例。
这里有个细节值得注意:COCO 的bbox是[x, y, width, height],而 VIA 记录的是 polygon 顶点,必须从顶点坐标里取最小 x、最小 y、最大 x、最大 y 反推。看起来是小学数学,但转换脚本如果没处理好浮点数的边界,生成的 bbox 会跑到图片外面,训练时 loss 直接异常。这份资源里的 JSON 我做过头尾检查,bbox都在画布范围内,area也和 polygon 实际面积对得上,说明转换时是认真算过的。
2.2 解压后的文件布局
解压ballon_mask_rcnn.rar之后,目录结构基本长这样:
ballon_mask_rcnn/ ├── annotations/ │ ├── instances_train2017.json │ └── instances_val2017.json ├── train2017/ │ ├── 6810773040_3d81036d05_k.jpg │ └── ...(训练图片) └── val2017/ ├── 3825919971_93fb1ec581_b.jpg └── ...(验证图片)| 文件/目录 | 作用 | 在 MMDetection 里的用途 |
|---|---|---|
train2017/ | 训练图片目录 | data_prefix.img指向它 |
val2017/ | 验证图片目录 | test_dataloader的图片路径 |
instances_train2017.json | 训练集 COCO 标注 | train_dataloader的ann_file |
instances_val2017.json | 验证集 COCO 标注 | val_evaluator的ann_file |
train2017、val2017、instances_*2017.json这些名字都是沿用官方 COCO 的命名习惯,但它和官方 COCO 没有任何关系。官方 COCO 里没有气球这个类别,这里只是借用了这套目录命名,方便 MMDetection 的 CocoDataset 直接识别。别把这两个 JSON 合并到官方 COCO 的标注里,类别 ID 会冲突,这是后文避坑部分要强调的。
2.3 文件名里的 Flickr 痕迹
如果你点开图片目录,会发现文件名都是3825919971_93fb1ec581_b.jpg这种长串。这不是随机字符串,它的前缀3825919971是 Flickr 上的 Photo ID,后面跟的是图片 secret 和尺寸后缀_b、_k等。来源是原版 balloon 数据集从 Flickr 抓取的公开图片,文件名保留了原始 ID,便于溯源。文件多的时候,不要试图用 JSON 里的id和文件名前缀做算术对应,这是没规律的,一切以file_name字段为准。
训练前其实应该先确认一件事:JSON 里images的file_name是否真的能对上目录里的文件。曾经遇到过一个转换包,图拷了 80%,JSON 里却有 100 条记录,训练到一半直接 FileNotFoundError。所以我在下面给了一个检查脚本,这是拿到任何 COCO 格式资源后我都要干的第一件事。
2.4 训练之前:用 10 行脚本检查标注质量
不要拿到 JSON 就急着配 config,先跑这段脚本:
# inspect_balloon_coco.py import json from collections import Counter, defaultdict with open('data/balloon/annotations/instances_train2017.json') as f: train = json.load(f) print('类别表:', train['categories']) print('图片数:', len(train['images']), '标注数:', len(train['annotations'])) # 统计每张图的标注数量,找出空标注图片 per_img = Counter(a['image_id'] for a in train['annotations']) img_ids = {im['id'] for im in train['images']} empty = [im['file_name'] for im in train['images'] if im['id'] not in per_img] print('空标注图片数:', len(empty)) # 检查 segmentation 字段类型:list 表示 polygon,dict 表示 RLE seg_types = defaultdict(int) for a in train['annotations']: seg_types[type(a['segmentation']).__name__] += 1 print('segmentation 类型分布:', dict(seg_types)) # 类别 ID 分布,确认 id 从 1 开始 for cid, cnt in Counter(a['category_id'] for a in train['annotations']).items(): print('category_id:', cid, '数量:', cnt)这段脚本有几个用途。segmentation类型分布如果全是list,说明标注是 polygon 格式,MMDetection 的 CocoDataset 能直接读取并转成BitmapMasks;如果出现dict,说明是 RLE 格式,框架同样支持,但你要知道它内部走的是另一套解码逻辑。类别 ID 分布是为了确认category_id是从 1 而不是 0 开始的,因为 COCO API 在 mmdet 内部会把 id 1 映射到索引 0,如果手动改过 JSON 里的 id,索引就会错位,训练出的模型类别全对不上。
我一般的习惯是再挑两张图,用matplotlib把 polygon 画在原图上看看边缘是否贴合气球轮廓。这一步花不了两分钟,但能省掉后面排查 mask loss 异常的时间。
3. 接到 MMDetection:三个关键配置位
3.1 CocoDataset 的加载流程
MMDetection 的 CocoDataset 读取这份资源时,有三个关键字段决定它能不能找到数据:data_root、ann_file、data_prefix。
data_root是数据集根目录,ann_file是相对于data_root的标注 JSON 路径,data_prefix.img是图片相对于data_root的目录名。框架内部先用 pycocotools 加载 JSON,建立 image id 到 annotation 的索引,再根据file_name拼接出完整图片路径。也就是说,只要这三个字段对,图片目录名是叫train2017还是images都不重要,但一定要和 JSON 里的file_name匹配。
metainfo则负责类别映射。COCO JSON 里categories的 id 是 1,但在 MMDetection 内部训练时用的是从 0 开始的索引,框架会根据你在 config 里声明的classes=('balloon',)自动完成映射。这里不需要去改 JSON,只需要保证类别名的顺序和数量与categories表一致。
3.2 目录摆放建议
把解压后的train2017、val2017、annotations直接放到 MMDetection 项目的data/balloon/下:
cd mmdetection mkdir -p data/balloon # 把解压出的三个文件/目录移进去,最终结构如下: # data/balloon/ # ├── annotations/ # │ ├── instances_train2017.json # │ └── instances_val2017.json # ├── train2017/ # └── val2017/这个布局是 MMDetection 最常见的数据集组织方式,后面写 config 时data_root统一填'data/balloon/'就行。注意不要画蛇添足,在data/balloon/下再套一层images/train2017,那样data_prefix得多写一层,而且避坑章节的第一个报错就是这么来的。
3.3 Mask R-CNN 训练配置
我按 MMDetection 3.x 的写法,在configs/balloon/下新建一个mask_rcnn_r50_fpn_balloon.py,它继承官方 Mask R-CNN 的 base config,只覆盖数据集相关字段:
# configs/balloon/mask_rcnn_r50_fpn_balloon.py _base_ = '../mask_rcnn/mask_rcnn_r50_fpn_1x_coco.py' data_root = 'data/balloon/' metainfo = dict( classes=('balloon',), palette=[(220, 20, 60)] ) train_dataloader = dict( batch_size=2, dataset=dict( data_root=data_root, metainfo=metainfo, ann_file='annotations/instances_train2017.json', data_prefix=dict(img='train2017/'), filter_cfg=dict(filter_empty_gt=True, min_size=32), ), ) val_dataloader = dict( dataset=dict( data_root=data_root, metainfo=metainfo, ann_file='annotations/instances_val2017.json', data_prefix=dict(img='val2017/'), test_mode=True, ), ) test_dataloader = val_dataloader val_evaluator = dict( type='CocoMetric', ann_file=data_root + 'annotations/instances_val2017.json', metric=['bbox', 'segm'], ) test_evaluator = val_evaluator # 小数据集没必要硬跑 COCO 12 epoch train_cfg = dict(max_epochs=50, val_interval=5) default_hooks = dict( checkpoint=dict(interval=5, save_best='coco/segm_mAP') )这段配置有几个参数值得解释。batch_size=2是考虑到气球原图分辨率不低、Mask R-CNN 的 mask head 很吃显存,如果你只有 11G 左右的显卡,先从这里起步,OOM 再降到 1。filter_cfg.filter_empty_gt=True会把那些没有任何标注的图片从训练里滤掉,因为 COCO JSON 里允许存在空标注图片,但训练时喂进去会让 RPN 的监督信号混乱。min_size=32过滤尺寸过小的目标,避免极端小目标干扰。save_best='coco/segm_mAP'让每轮验证时按分割 mAP 保存最优权重,而不是只按 bbox mAP,这对实例分割任务更合理。
val_dataloader里的test_mode=True表示验证阶段不做数据增强,只走归一化和尺寸缩放。CocoMetric同时计算bbox和segm两套指标,因为 Mask R-CNN 是检测加分割双头输出,只看检测框会漏掉 mask 分支的问题。
3.4 开训命令与日志里看什么
配置写完后,启动训练:
cd mmdetection python tools/train.py configs/balloon/mask_rcnn_r50_fpn_balloon.py --work-dir work_dirs/balloon第一次跑,加载阶段会卡一会儿,那是 pycocotools 在建立标注索引,几十张图几秒钟就完成。真正训练开始后,日志里重点看三行:loss_cls是分类损失,loss_bbox是检测框回归损失,loss_mask是掩膜分割损失。正常情况三者一起下降,到 20 轮左右loss_mask会降到 0.1 以下。
如果发现loss_mask一直在 0.5 附近波动却不降,多半是标注里的 segmentation 有问题;如果loss_bbox先降后涨,可能是学习率太大,配合 batch_size 减小后要注意学习率补偿,常见做法是把 base config 里的学习率按 batch_size 比例下调。MMDetection 3.x 的 base config 带有自动学习率缩放,但auto_scale_lr只在开启时生效,手动训练时我会刻意看一眼初始 lr 是不是 0.02,如果是,batch=2 时建议改到 0.0025。
4. 避坑:从读不到图到 mAP 为 0 的五个现场
4.1 FileNotFoundError:训练到一半找不到图
现象:训练跑到第一个 epoch 末尾,日志抛FileNotFoundError: data/balloon/train2017/xxx.jpg,或者 pycocotools 在加载时直接提示图片缺失。
原因:最常见是目录结构多套了一层。比如把解压后的内容放成了data/balloon/images/train2017/,但配置里data_prefix=dict(img='train2017/'),拼接出来的路径是data/balloon/train2017/,自然找不到。另一个原因是 JSON 里的file_name和实际文件名大小写不一致,转换包有时会把扩展名统一成.JPG,而目录里是.jpg。
解决:先跑一遍前面给的检查脚本,把 JSON 里的file_name逐个和os.path.exists对照。我一般在data/balloon/下直接用tree确认层数,保证图片和annotations是兄弟目录,而不是父子嵌套。
4.2 mask_loss 出现 NaN 或训练中断
现象:训练日志里loss_mask在某个 epoch 突然变成nan,或者直接报RuntimeError: CUDA error: device-side assert triggered。
原因:COCO 标注里的segmentation是空列表。VIA 转 COCO 时,如果某个 polygon 坐标解析失败,转换脚本常常写成segmentation: [],而不是跳过这个标注。MMDetection 的 mask 分支拿空 mask 做监督,反向传播时就会出现 NaN。
解决:用 2.4 的脚本统计segmentation为空列表的标注数量,如果存在,直接把对应 annotation 从 JSON 里删掉,或者重新用 polygon 坐标生成。注意不是删图片,只删有问题的标注。处理完 JSON 后,再把 JSON 里的annotations数量对一遍,确保没有误伤正常标注。
4.3 训练正常但单张推理 mask 全黑
现象:训练曲线很漂亮,验证 mAP 也不错,但拿一张验证图做推理,框是有的,mask 却一片黑或者只覆盖一半。
原因:大概率是置信度阈值设得过高。Mask R-CNN 的 mask 分支输出是软 mask,最终显示时要经过阈值二值化,score_thr设成 0.8 时,只有非常确信的区域才显示。气球这种边缘柔和的物体,mask 分数普遍比框分数低一截。
解决:推理时把score_thr降到 0.3 到 0.5 之间。如果你在model.show_result里传了score_thr,先确认它没有被外界参数覆盖;如果用的是tools/test.py --show-dir,注意它默认只保存检测框可视化,要看 mask 需要自己在代码里调用show_result。
4.4 验证 mAP 忽高忽低
现象:val_interval=5时,第 10 轮 mAP 0.42,第 15 轮掉到 0.31,第 20 轮又涨回 0.45,曲线像过山车。
原因:数据集只有几十张图,验证集可能就十来张,一轮随机采样和增强带来的波动远大于模型实际的能力变化。这不算 bug,是小数据集的统计噪声。
解决:固定随机种子,config里加一段randomness = dict(seed=42, deterministic=True)。然后把验证间隔调大到 10 轮,少看几次中间的波动,最终以save_best保存的最优权重为准。另外,训练阶段的增强不要开太猛,气球数据集本来就小,过度增强会让模型学不到稳定的纹理特征。
4.5 显存 OOM:batch=2 都跑不动
现象:启动训练后直接CUDA out of memory,或者跑几个 iteration 后才爆。
原因:Mask R-CNN 的 mask head 对显存非常敏感,尤其原图是 Flickr 大图,缩放后仍然有 800 像素级别,特征图加 mask 分支的显存占用比纯检测模型高 30% 以上。如果显卡是 8G 的老卡,batch=2 确实勉强。
解决:先降 batch_size 到 1,这是最直接的办法。还不够就把train_pipeline里的img_scale从(1333, 800)改成(1000, 600),但要注意这会影响小目标召回。再不行就开混合精度训练,在命令后面加--cfg-options optim_wrapper.type='AmpOptimWrapper',需要环境里有 apex 或 mmcv 的 amp 支持。实在不行,建议直接把 backbone 换成resnet50的轻量版本,或者用mask_rcnn_r50_fpn_mstrain_3x_coco这类对显存更友好的配置。
5. 用 val 集做端到端验证:指标和 mask 可视化
5.1 验证配置已经就位
前面给的 config 里,test_dataloader已经指到了val2017,test_evaluator也配好了CocoMetric,所以训练完不用改任何代码,直接跑工具命令就能拿到验证指标。
5.2 跑一次正式验证
cd mmdetection python tools/test.py configs/balloon/mask_rcnn_r50_fpn_balloon.py work_dirs/balloon/epoch_50.pth --show-dir work_dirs/balloon/vistest.py会根据配置里的test_evaluator自动计算指标,输出里会同时出现bbox_mAP和segm_mAP两行。对于气球这种单类别数据集,segm_mAP才是掩膜质量的真正指标,bbox_mAP再高,mask 边缘烂也是白搭。如果你用的是旧版 MMDetection 2.x,才需要手动加--eval bbox segm,3.x 已经不需要。
5.3 单张推理视觉验证
指标是数字,最终还得看肉眼效果。我用一段最简推理脚本:
from mmdet.apis import init_detector, inference_detector config = 'configs/balloon/mask_rcnn_r50_fpn_balloon.py' checkpoint = 'work_dirs/balloon/epoch_50.pth' model = init_detector(config, checkpoint, device='cuda:0') img = 'data/balloon/val2017/3825919971_93fb1ec581_b.jpg' result = inference_detector(model, img) model.show_result(img, result, out_file='balloon_pred.jpg', score_thr=0.5)init_detector负责加载配置和权重,inference_detector跑完整前向,show_result把 bbox、mask 和类别标签画到原图上。score_thr=0.5是显示阈值,如果 mask 显示残缺,降到 0.3 再看。看到 mask 边缘基本贴合气球轮廓,且没有把背景天空误分进去,说明训练链路是通的。
做完这一轮验证,这份数据集在你手里就不再是一个黑匣子。我个人的习惯是,以后无论从哪拿到“已经整理好”的 COCO 格式资源,第一件事永远是先跑那个 10 行检查脚本,确认类别表、空标注、segmentation 类型,再挑一张图做可视化,然后才敢把data_root指过去训练。这套流程帮我躲开了不少标注质量导致的玄学问题,从那以后每次换数据集都强制走一遍,翻车率明显降下来。希望帮到你。
本文还有配套的精品资源,点击获取