xxx_rec = {
'im_file': im_fname, # 一张图像的完整路径
'im_id': np.array([img_id]), # 一张图像的ID序号
'h': im_h, # 图像高度
'w': im_w, # 图像宽度
'is_crowd': is_crowd, # 是否是群落对象, 默认为0 (VOC中无此字段,为了使个别模型同时适配于COCO与VOC)
'gt_class': gt_class, # 标注框标签名称的ID序号
'gt_bbox': gt_bbox, # 标注框坐标(xmin, ymin, xmax, ymax)
'gt_score': gt_score, # 标注框置信度得分 (此字段为了适配Mixup操作)
'gt_poly': gt_poly, # 分割掩码,此字段只在coco_rec中出现,默认为None
'difficult': difficult # 是否是困难样本,此字段只在voc_rec中出现,默认为0
然后将所有xxx_rec
字典封装到records
列表中,最后传入self.roidbs
中方便后续调用。
cname2cid
:保存了类别名到id的映射的一个dict。
- COCO数据集中:会根据COCO API自动加载cname2cid。
- VOC数据集中:如果在yaml配置文件中设置
use_default_label=False
,将从label_list.txt
中读取类别列表,
反之则可以没有label_list.txt
文件,PaddleDetection会使用source/voc.py
里的默认类别字典。
label_list.txt
的格式如下所示,每一行文本表示一个类别:
aeroplane
bicycle
COCO数据源
该数据集目前分为COCO2014和COCO2017,主要由json文件和image文件组成,其组织结构如下所示:
dataset/coco/
├── annotations
│ ├── instances_train2014.json
│ ├── instances_train2017.json
│ ├── instances_val2014.json
│ ├── instances_val2017.json
│ │ ...
├── train2017
│ ├── 000000000009.jpg
│ ├── 000000580008.jpg
│ │ ...
├── val2017
│ ├── 000000000139.jpg
│ ├── 000000000285.jpg
│ │ ...
在source/coco.py
中定义并注册了COCODataSet
数据源类,它继承自DataSet
基类,并重写了load_roidb_and_cname2cid
:
根据标注文件路径(anno_path),调用COCO API加载并解析COCO格式数据源roidbs
和cname2cid
。
Pascal VOC数据源
该数据集目前分为VOC2007和VOC2012,主要由xml文件和image文件组成,其组织结构如下所示:
dataset/voc/
├── trainval.txt
├── test.txt
├── label_list.txt (optional)
├── VOCdevkit/VOC2007
│ ├── Annotations
│ ├── 001789.xml
│ │ ...
│ ├── JPEGImages
│ ├── 001789.jpg
│ │ ...
│ ├── ImageSets
│ | ...
├── VOCdevkit/VOC2012
│ ├── Annotations
│ ├── 2011_003876.xml
│ │ ...
│ ├── JPEGImages
│ ├── 2011_003876.jpg
│ │ ...
│ ├── ImageSets
│ │ ...
在source/voc.py
中定义并注册了VOCDataSet
数据源类,它继承自DataSet
基类,并重写了load_roidb_and_cname2cid
,解析VOC数据集中xml格式标注文件,更新roidbs
和cname2cid
。
添加新数据源
- (1)新建
./source/xxx.py
,定义类XXXDataSet
继承自DataSet
基类,完成注册与序列化,并重写load_roidb_and_cname2cid
方法对roidbs
与cname2cid
更新:
@register
@serializable
class XXXDataSet(DataSet):
def __init__(self,
dataset_dir=None,
image_dir=None,
anno_path=None,
self.roidbs = None
self.cname2cid = None
def load_roidb_and_cname2cid(self):
省略具体解析数据逻辑
self.roidbs, self.cname2cid = records, cname2cid
- (2)在
source/__init__.py
中添加引用:
from . import xxx
from .xxx import *
完成以上两步就将新的数据源XXXDataSet
添加好了,操作非常简单。
数据预处理
数据增强算子
PaddleDetection中支持了种类丰富的数据增强算子,有单图像数据增强算子与批数据增强算子两种方式,您可选取合适的算子组合使用,已支持的单图像数据增强算子详见下表:
Permute
对图像的通道进行排列并转为BGR格式。假如输入是HWC顺序,通道C上是RGB格式,设置channel_first=True,将变成CHW,设置to_bgr=True,通道C上变成BGR格式。
MixupImage
按比例叠加两张图像
RandomInterpImage
使用随机的插值方式调整图像大小
Resize
根据特定的插值方式同时调整图像与bounding box的大小
MultiscaleTestResize
将图像重新缩放为多尺度list的每个尺寸
ColorDistort
根据特定的亮度、对比度、饱和度和色相为图像增加噪声
NormalizePermute
归一化图像并改变图像通道顺序
RandomExpand
原理同ExpandImage,以随机比例与角度对图像进行裁剪、缩放和翻转
RandomCrop
原理同CropImage,以随机比例与IoU阈值进行处理
PadBox
如果bounding box的数量少于num_max_boxes,则将零填充到bbox
BboxXYXY2XYWH
将bounding box从(xmin,ymin,xmax,ymin)形式转换为(xmin,ymin,width,height)格式
几点说明:
- 上表中的数据增强算子的输入与输出都是单张图片
sample
,sample
是由{'image':xx, 'im_info': xxx, ...}组成,来自于上文提到的roidbs
中的字典信息。
- 数据增强算子注册后即可生效,在配置yaml文件中添加即可,配置文件中配置方法见下文。
- Mixup的操作可参考论文。
批数据增强算子列表如下:
输入与输出
假如我们定义一个新的单图像数据增强算子XXXImage
。
- 在
transform/operators.py
中增加类XXXImage
继承自BaseOperator,并注册:
@register_op
class XXXImage(BaseOperator):
def __init__(self,...):
super(XXXImage, self).__init__()
def __call__(self, sample, context=None):
省略对输入的sample具体操作
return sample
如此就完成新增单图像数据增强算子XXXImage
,操作非常简单。
自定义批量数据增强算子方法同上,在transform/batch_operators.py
中定义即可。
组建Reader迭代器
如上面提到,Reader预处理流程为: 单张图像处理 -> 组batch -> batch图像处理。用户一般不需要关注这些方法。
在reader.py
中构建了Reader迭代器类,其中包含如下方法:
TrainReader:
inputs_def: # 网络输入的定义
fields: ['image', 'gt_bbox', 'gt_class', 'gt_score']
dataset: # 数据源
!COCODataSet #序列化COCO数据源
dataset_dir: dataset/coco # 数据集根目录
anno_path: annotations/instances_train2017.json # 标注文件基于数据集根目录的相对路径
image_dir: train2017 # 图像数据基于数据集根目录的相对路径
with_background: false # 背景是否作为一类标签
sample_transforms: # 单图像数据增强算子的列表
- !DecodeImage # 序列化DecodeImage算子,详细参数设置参见源码
to_rgb: true
with_mixup: true
- !MixupImage # 序列化MixupImage算子,详细参数设置参见源码
alpha: 1.5
beta: 1.5
- !ColorDistort {} # 序列化ColorDistort算子,详细参数设置参见源码
batch_transforms: # 批数据数据增强算子的列表 (可选)
- !RandomShape # 序列化RandomShape算子,详细参数设置参见源码
sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608]
random_inter: True
batch_size: 8 # 以下定义见上文Reader参数列表
shuffle: true
mixup_epoch: 250
worker_num: 8
use_process: true
几点说明:
训练-数据处理
模块的名称统一为TrainReader
;
- PaddleDetection的yml配置文件中,使用
!
直接序列化模块实例(可以是函数、类等);
dataset
下需要序列化数据源实例,如COCODataSet
、VOCDataSe
和自定义的XXXDataSet
;
inputs_def
的具体定义与使用请参考模型技术文档
- Reader的参数可选择性配置,如未在yml配置文件中定义,则会选取各参数在源码中的默认值。
评估配置
EvalReader:
inputs_def:
fields: ['image', 'im_size', 'im_id']
dataset:
!COCODataSet
dataset_dir: dataset/coco
anno_path: annotations/instances_val2017.json
image_dir: val2017
with_background: false
sample_transforms:
- !DecodeImage
to_rgb: True
batch_size: 8
drop_empty: false
几点说明:
评估-数据处理
模块的名称统一为EvalReader
;
- 在评估配置中,数据增强需要去除各种含有随机操作的数据处理算子与操作。
推理配置
TestReader:
inputs_def:
image_shape: [3, 608, 608]
fields: ['image', 'im_size', 'im_id']
dataset:
!ImageFolder
anno_path: annotations/instances_val2017.json
with_background: false
sample_transforms:
- !DecodeImage
to_rgb: True
batch_size: 1
几点说明:
推理-数据处理
模块的名称统一为TestReader
;
- 在推理配置中
dataset
的数据源一般都设置为ImageFolder
数据源。ImageFolder可以指定图片的文件夹地址,将读取该文件夹下的所有图片。
到此就完成了yml配置文件中的TrainReader
、EvalReader
和TestReader
的编写,您也可以将Reader部分封装到单独的yml文件xxx_reader.yml
中,利用如下命令进行加载即可:
_READER_: 'xxx_reader.yml'
加载完成后可以重写Reader中的方法,比如:
_READER_: 'xxx_reader.yml'
TrainReader:
batch_size: 2
EvalReader:
这样就可以复用同一份Reader配置文件了,重写更新其中某些参数也很方便。
在PaddleDetection的训练、评估和测试运行程序中,都通过创建Reader迭代器,然后将reader封装在DataLoader对象中,
DataLoader
的API详见fluid.io.DataLoader。
具体步骤如下:
- 在train.py、eval.py和infer.py里创建训练时的Reader:
# 创建DataLoader对象
inputs_def = cfg['TestReader']['inputs_def']
_, loader = model.build_inputs(**inputs_def)
# 创建Reader迭代器
from ppdet.data.reader import create_reader
# train
train_reader = create_reader(cfg.TrainReader, max_iter=0, global_cfg=cfg)
# eval
reader = create_reader(cfg.EvalReader)
# infer
reader = create_reader(cfg.TestReader)
# 将reader设置为DataLoader数据源
loader.set_sample_list_generator(reader, place)
在运行程序中设置完数据处理模块后,就可以开始训练、评估与测试了,具体请参考相应运行程序python源码。
关于数据处理模块,如您有其他问题或建议,请给我们提issue,我们非常欢迎您的反馈。