news 2026/9/19 18:50:01

MMDetection 数据集定制完全指南:从 COCO 格式转换到自定义 Dataset 实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MMDetection 数据集定制完全指南:从 COCO 格式转换到自定义 Dataset 实现

MMDetection 数据集定制完全指南:从 COCO 格式转换到自定义 Dataset 实现

【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址: https://gitcode.com/gh_mirrors/mm/mmdetection

导读

本指南系统讲解 MMDetection 中如何接入全新的检测数据集:既包括把数据离线转换为 COCO / PASCAL 格式后仅修改配置的最小改动方案,也包括使用 MMEngine 中间格式、编写自定义BaseDetDataset子类加载任意文本标注的进阶方案,并覆盖数据集包装器(Dataset Wrapper)、类别子集训练与 COCO Panoptic 全景分割数据的接入方法。读完本文,你将能够独立完成"新数据集 → 配置文件 → 训练/评估"的完整链路,并理解 MMDetection 数据层在mmdet/datasets下的底层实现机制。

支持新数据格式:四条可选路径

面对一个全新的数据格式,MMDetection 提供了三种转换目标与两种转换时机:

转换目标说明后续工作
COCO 格式标注转成images/annotations/categories三个 key 的 JSON修改配置中的标注路径与类别即可
PASCAL VOC 格式转成 VOC XML 标注同上
MMEngine 中间格式转成包含metainfodata_list的 JSON / YAML / Pickle直接在配置中使用BaseDetDataset
转换时机做法适用场景
离线转换训练前用脚本一次性转换推荐做法
在线转换实现新 Dataset 类,在加载时转换标注格式特殊、无法离线归一

MMDetection 官方推荐把数据离线转换为 COCO 格式:转换完成后,你只需要修改配置文件中的ann_filedata_prefix和类别信息,无需改动任何训练代码;同时实例分割类数据集目前只支持 COCO 格式的 mask AP 评估

方案一:把新数据整理成 COCO 格式

COCO 标注 JSON 的必要字段

COCO 格式的标注 JSON 必须包含以下三个 key,缺一不可:

'images': [ { 'file_name': 'COCO_val2014_000000001268.jpg', 'height': 427, 'width': 640, 'id': 1268 }, ... ], 'annotations': [ { 'segmentation': [[192.81, 247.09, ... 219.03, 249.06]], # 如果有 mask 标注,且为多边形 XY 点坐标格式, # 需要保证至少包含 3 个点,否则为非法多边形。 'area': 1035.749, 'iscrowd': 0, 'image_id': 1268, 'bbox': [192.81, 224.8, 74.73, 33.43], 'category_id': 16, 'id': 42986 }, ... ], 'categories': [ {'id': 0, 'name': 'car'}, ]
  • images:图片信息列表,含file_nameheightwidthid
  • annotations:实例标注列表;
  • categories:类别名称与其 ID 的映射列表。

步骤 1:修改配置文件

以"5 个自定义类别 + COCO 格式"训练 Cascade Mask R-CNN R50-FPN 为例,修改涉及两个层面:

  1. data字段:在train_dataloader.datasetval_dataloader.datasettest_dataloader.dataset中显式添加metainfo=dict(classes=classes),且classes必须是tuple 类型
  2. model字段中的num_classes:把默认值(COCO 为 80)显式覆盖为你的类别数,所有头都要改。

configs/my_custom_config.py中:

# 新配置继承基础配置,突出必要修改 _base_ = './cascade_mask_rcnn_r50_fpn_1x_coco.py' # 1. 数据集设置 dataset_type = 'CocoDataset' classes = ('a', 'b', 'c', 'd', 'e') data_root='path/to/your/' train_dataloader = dict( batch_size=2, num_workers=2, dataset=dict( type=dataset_type, # 显式把你的类别名写入 metainfo 字段 metainfo=dict(classes=classes), data_root=data_root, ann_file='train/annotation_data', data_prefix=dict(img='train/image_data') ) ) val_dataloader = dict( batch_size=1, num_workers=2, dataset=dict( type=dataset_type, test_mode=True, # 显式把你的类别名写入 metainfo 字段 metainfo=dict(classes=classes), data_root=data_root, ann_file='val/annotation_data', data_prefix=dict(img='val/image_data') ) ) test_dataloader = dict( batch_size=1, num_workers=2, dataset=dict( type=dataset_type, test_mode=True, # 显式把你的类别名写入 metainfo 字段 metainfo=dict(classes=classes), data_root=data_root, ann_file='test/annotation_data', data_prefix=dict(img='test/image_data') ) ) # 2. 模型设置 # 显式把所有 num_classes 字段从默认 80 覆盖为 5 model = dict( roi_head=dict( bbox_head=[ dict( type='Shared2FCBBoxHead', # 显式把所有 num_classes 字段从默认 80 覆盖为 5 num_classes=5), dict( type='Shared2FCBBoxHead', # 显式把所有 num_classes 字段从默认 80 覆盖为 5 num_classes=5), dict( type='Shared2FCBBoxHead', # 显式把所有 num_classes 字段从默认 80 覆盖为 5 num_classes=5)], # 显式把所有 num_classes 字段从默认 80 覆盖为 5 mask_head=dict(num_classes=5)))

这段配置的底层逻辑可以在 mmdet/datasets/coco.py 中印证:CocoDataset.load_data_list通过self.coco.get_cat_ids(cat_names=self.metainfo['classes'])metainfo中的类别顺序解析cat_ids,再构造self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}完成"COCO category_id → 连续 label 索引"的映射;parse_data_info中遇到ann['category_id'] not in self.cat_ids的标注会直接跳过。也就是说,metainfo里的类别顺序直接决定了训练时 label 的编码顺序。

步骤 2:核对自定义数据集标注

假设你的数据集已是 COCO 格式,训练前请核对以下三点:

  1. 标注中categories字段的长度必须恰好等于配置中classes元组的长度(本例为 5);
  2. 配置中classes的元素与顺序必须和标注categories里的name完全一致。MMDetection 会自动把categories中不连续的id映射为连续 label 索引,因此name的字符串顺序会影响 label 索引顺序;同时配置中classes的字符串顺序也决定了可视化预测框时的类别文本;
  3. annotations中的category_id必须合法,即所有取值都应属于categories中的id

下面是一份合法的标注示例(categoriesid不连续,MMDetection 会自动映射为连续 label):

'annotations': [ { 'segmentation': [[192.81, 247.09, ... 219.03, 249.06]], # 如果有 mask 标注 'area': 1035.749, 'iscrowd': 0, 'image_id': 1268, 'bbox': [192.81, 224.8, 74.73, 33.43], 'category_id': 16, 'id': 42986 }, ... ], # MMDetection 自动把不连续的 id 映射为连续 label 索引。 'categories': [ {'id': 1, 'name': 'a'}, {'id': 3, 'name': 'b'}, {'id': 4, 'name': 'c'}, {'id': 16, 'name': 'd'}, {'id': 17, 'name': 'e'}, ]

官方实例:Cityscapes

这套"转换为 COCO 格式 + 微调"的流程在 Cityscapes 上被官方验证过:转换脚本位于 tools/dataset_converters/cityscapes.py(内含collect_filescollect_annotationscvt_annotations等函数,通过parse_args解析--img-dir--gt-dir--out-json等参数后写出 COCO JSON),配套的微调配置位于 configs/cityscapes。

注意事项

  1. 对于实例分割数据集,MMDetection 目前只支持评估 COCO 格式数据集的 mask AP
  2. 推荐在训练前离线完成数据转换,这样仍可使用现成的CocoDataset,只需要修改标注路径与训练类别。

方案二:把新数据整理成 MMEngine 中间格式

如果不想把标注转成 COCO 或 PASCAL 格式,也可以直接转换为 MMEngine 定义在BaseDataset中的简单中间格式。仓库中所有现有数据集最终都会被处理成与该格式兼容的结构,无论是离线还是在线。

中间格式的标注文件必须是jsonyamlymlpicklepkl之一;文件内存储的字典必须包含两个字段:

  • metainfo:字典,存放数据集元信息,如类别信息;
  • data_list:列表,每个元素是对应一张图片原始数据的字典,可包含一个或多个训练/测试样本。

示例:

{ 'metainfo': { 'classes': ('person', 'bicycle', 'car', 'motorcycle'), ... }, 'data_list': [ { "img_path": "xxx/xxx_1.jpg", "height": 604, "width": 640, "instances": [ { "bbox": [0, 0, 10, 20], "bbox_label": 1, "ignore_flag": 0 }, { "bbox": [10, 10, 110, 120], "bbox_label": 2, "ignore_flag": 0 } ] }, { "img_path": "xxx/xxx_2.jpg", "height": 320, "width": 460, "instances": [ { "bbox": [10, 0, 20, 20], "bbox_label": 3, "ignore_flag": 1, } ] }, ... ] }

有些数据集会提供 crowd / difficult / ignored 等类型的 bbox,MMDetection 用ignore_flag统一表达(1表示在训练/评估中忽略该实例)。

拿到上述标准格式后,可以直接在配置中使用 mmdet/datasets/base_det_dataset.py 中注册的BaseDetDataset,无需再写转换代码。从源码看,BaseDetDataset继承自 MMEngine 的BaseDataset,其full_init依次执行load_data_list(从标注文件加载)→load_proposals(如配置了 proposal 文件)→filter_data(按filter_cfg过滤)→ 子集切片与序列化,这也解释了为何自定义数据集只需实现load_data_list即可接入完整训练流程。

实战:为全新文本标注格式编写自定义数据集类

假设标注是如下文本格式:#为图片分隔符,第二行为图片名,第三行为宽高,第四行为 bbox 数量,随后每行是一条x1 y1 x2 y2 label格式的标注。

# 000001.jpg 1280 720 2 10 20 40 60 1 20 40 50 60 2 # 000002.jpg 1280 720 3 50 20 40 60 2 20 40 30 45 2 30 40 50 60 3

我们可以在mmdet/datasets/my_dataset.py中新建数据集类来加载它:

import mmengine from mmdet.base_det_dataset import BaseDetDataset from mmdet.registry import DATASETS @DATASETS.register_module() class MyDataset(BaseDetDataset): METAINFO = { 'classes': ('person', 'bicycle', 'car', 'motorcycle'), 'palette': [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230)] } def load_data_list(self, ann_file): ann_list = mmengine.list_from_file(ann_file) data_infos = [] for i, ann_line in enumerate(ann_list): if ann_line != '#': continue img_shape = ann_list[i + 2].split(' ') width = int(img_shape[0]) height = int(img_shape[1]) bbox_number = int(ann_list[i + 3]) instances = [] for anns in ann_list[i + 4:i + 4 + bbox_number]: instance = {} instance['bbox'] = [float(ann) for ann in anns.split(' ')[:4]] instance['bbox_label']=int(anns[4]) instances.append(instance) data_infos.append( dict( img_path=ann_list[i + 1], img_id=i, width=width, height=height, instances=instances )) return data_infos

几个要点:

  • 通过@DATASETS.register_module()注册类名,之后配置中可直接写type='MyDataset'
  • METAINFO中定义classespalette(调色板用于可视化);
  • 只需要实现load_data_list,返回符合中间格式的data_infos列表即可,其余初始化、过滤、序列化逻辑全部由BaseDetDataset承接;
  • 上述示例中instance['bbox_label'] = int(anns[4])实际上取的是整行字符串的字符,严格应写为int(anns.split(' ')[4]),即对每一行先split再取第 5 个字段作为类别标签,请在实际实现时留意。

随后在配置中像使用内置数据集一样使用它:

dataset_A_train = dict( type='MyDataset', ann_file = 'image_list.txt', pipeline=train_pipeline )

用数据集包装器混合与重分布数据

MMEngine 还提供了多种数据集包装器(Dataset Wrapper),用于混合数据集或调整训练时的数据分布。目前支持以下三种:

  • RepeatDataset:简单重复整个数据集;
  • ClassBalancedDataset:按类别均衡的方式重复数据,缓解类别不平衡;
  • ConcatDataset:拼接多个数据集。

它们与 MMDetection 注册体系兼容,可在train_dataloader.dataset位置直接嵌套使用,例如把多个MyDataset拼接后再送入训练。详细用法可查阅 MMEngine 的 Dataset Wrapper 文档。

修改数据集类别:只训练部分类别

基于现有数据集类型,我们可以通过修改其metainfo来训练标注的子集。例如只想训练当前数据集中三个类别时:

classes = ('person', 'bicycle', 'car') train_dataloader = dict( dataset=dict( metainfo=dict(classes=classes)) ) val_dataloader = dict( dataset=dict( metainfo=dict(classes=classes)) ) test_dataloader = dict( dataset=dict( metainfo=dict(classes=classes)) )

数据集会自动过滤掉其他类别的 GT 框(其机制见上文parse_data_infoann['category_id'] not in self.cat_ids的跳过逻辑)。

注意

  • 行为变更(v2.5.0 起):在此之前,一旦设置了classes,数据集会自动过滤无 GT 图片且无法通过配置关闭,这引发了不少困惑——因为未设置classes时,只有在filter_empty_gt=Truetest_mode=False时才会过滤空 GT 图片。v2.5.0 之后,图片过滤与类别修改被解耦:无论是否设置classes,只有filter_cfg=dict(filter_empty_gt=True)test_mode=False时才过滤空 GT 图片。因此,设置classes只影响参与训练的标注类别,是否过滤空 GT 图片完全由用户自己决定。从 mmdet/datasets/coco.py 的filter_data实现可见,过滤条件确实只与filter_cfgfilter_empty_gtmin_size)和test_mode相关,与metainfo['classes']无关;
  • 直接使用 MMEngine 的BaseDataset或 MMDetection 的BaseDetDataset时,无法仅靠配置过滤无 GT 图片,只能离线处理;
  • 务必记得同步修改检测头中的num_classes。自 v2.9.0(PR#4508)起,MMDetection 实现了 mmdet/engine/hooks/num_class_check_hook.py 中的NumClassCheckHook来校验二者是否一致。

NumClassCheckHook:训练前的自检机制

NumClassCheckHook继承自 MMEngine 的Hook,在before_train_epochbefore_val_epoch阶段调用_check_head:遍历模型所有模块,凡带有num_classes属性的模块(排除rpn_head、VGG 与FusedSemanticHead),都会断言module.num_classes == len(classes),不匹配时直接报错;若数据集metainfo中未设置classes,则会输出警告。它同时强制要求classes必须是str 的 tuple而非单个字符串(单类别需写成classes = ('cls',)这种带逗号的形式)。该 Hook 已注册在HOOKS注册表中,相关配置用法可见 configs/ssd/ssd300_coco.py、configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py 等;在 configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py 中则可以看到移除该 Hook 的注释示例,说明在个别特殊模型上可通过custom_hooks配置关闭这一自检。

COCO Panoptic 数据集

MMDetection 同样支持 COCO Panoptic(全景分割)数据集。其标注格式与普通 COCO 不同:前景与背景同时存在于标注文件中。JSON 的必要字段如下:

'images': [ { 'file_name': '000000001268.jpg', 'height': 427, 'width': 640, 'id': 1268 }, ... ] 'annotations': [ { 'filename': '000000001268.jpg', 'image_id': 1268, 'segments_info': [ { 'id':8345037, # 与标注图中的 id 一一对应 'category_id': 51, 'iscrowd': 0, 'bbox': (x1, y1, w, h), # 背景的 bbox 是其 mask 的外接矩形 'area': 24315 }, ... ] }, ... ] 'categories': [ # 同时包含前景类别与背景类别 {'id': 0, 'name': 'person'}, ... ]

此外,配置中必须通过data_prefixseg字段指定全景标注图片(panoptic annotation images)的路径。从源码看,mmdet/datasets/coco_panoptic.py 中的CocoPanopticDataset继承自CocoDataset,其METAINFO中同时定义了classes(thing + stuff 全部类别)、thing_classesstuff_classes,并通过seg_map_suffix关联seg前缀下的 PNG 标注图。

dataset_type = 'CocoPanopticDataset' data_root='path/to/your/' train_dataloader = dict( dataset=dict( type=dataset_type, data_root=data_root, data_prefix=dict( img='train/image_data/', seg='train/panoptic/image_annotation_data/') ) ) val_dataloader = dict( dataset=dict( type=dataset_type, data_root=data_root, data_prefix=dict( img='val/image_data/', seg='val/panoptic/image_annotation_data/') ) ) test_dataloader = dict( dataset=dict( type=dataset_type, data_root=data_root, data_prefix=dict( img='test/image_data/', seg='test/panoptic/image_annotation_data/') ) )

总结

接入新数据集的核心决策树可以概括为:优先离线转换为 COCO 格式(改动最小,实例分割 mask 评估也仅支持 COCO);标注结构特殊时转换为 MMEngine 中间格式并直接使用BaseDetDataset;若两者都不满足,则继承BaseDetDataset实现load_data_list在线解析。无论走哪条路径,都别忘了两个关键配套动作:在三个 dataloader 的metainfo中写全 tuple 类型的classes,并同步覆盖所有检测头的num_classes(交给NumClassCheckHook兜底校验)。配合 configs/base/datasets/coco_detection.py 中的基础数据配置、tools/dataset_converters/cityscapes.py 的转换脚本示例,以及 mmdet/datasets 目录下丰富的内置数据集实现(CocoDatasetCocoPanopticDatasetVOCDataset等),即可在 MMDetection 中高效复现本文的全部流程。

【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址: https://gitcode.com/gh_mirrors/mm/mmdetection

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 18:49:41

自动驾驶多源多模态数据冗余治理:从量化分析到全链路降本实践

1. 多源多模态数据为什么成了自动驾驶的"甜蜜负担"做自动驾驶数据闭环的同行应该都有同感:一辆测试车跑一天,激光雷达、毫米波雷达、前视/环视/侧视摄像头、IMU、GNSS、轮速计全开,轻轻松松产出几个TB的原始数据。我参与过一个中等…

作者头像 李华
网站建设 2026/9/19 18:49:34

STM32与MPU6050姿态检测实战:I2C通信、卡尔曼滤波与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 18:46:27

SSID是什么?一文搞懂Wi-Fi网络名称的查看与设置

遇到路由器时,经常会看到“SSID”这个词。对很多非网络专业的朋友来说,这三个字母组合看着很陌生,但其实就是你平时在手机上看到的一堆Wi-Fi名字之一。说直白点,SSID就是无线网络的名称,就是你打开手机Wi-Fi列表后看到…

作者头像 李华
网站建设 2026/9/19 18:45:12

高中化学原子结构课件的PDF结构化与教学增强实践

简介:本资源是一套面向高中化学选修三课程的系统性教学课件,专为高二学生、一线化学教师及高考复习备考者设计,聚焦原子结构、分子结构与晶体结构三大核心模块,助力深入理解物质性质与化学反应本质。课件以PDF格式整合为1个文件&a…

作者头像 李华
网站建设 2026/9/19 18:45:01

C盘又满了?从D盘无损扩容的完整指南(含工具与避坑)

C盘又红了,是不是?这个问题我太熟了。帮朋友修电脑,十台里有七八台都是C盘飘红、系统卡顿、Windows更新装不上。很多人第一反应是下个清理软件扫一遍,能腾出几个G,可过段时间又满了——治标不治本。真正立得住的办法&a…

作者头像 李华