From 8192c758b89917283cc7fd26260b695dedf5b539 Mon Sep 17 00:00:00 2001 From: qingqing01 Date: Thu, 19 Dec 2019 21:07:02 +0800 Subject: [PATCH] Polish reader to simplify preprocessing logic. (#112) * Polish Reader to simplify preprocessing logic. # sample_transforms-> make batch -> batch_transforms in Reader. * Clean some code * Imporve yolov3_r50vd_dcn_obj365_pretrained_coco 41.4 to 41.8. * Update all configs. --- .pre-commit-config.yaml | 12 +- configs/cascade_mask_rcnn_r50_fpn_1x.yml | 36 +- ..._rcnn_cls_aware_r101_vd_fpn_1x_softnms.yml | 81 +- configs/cascade_rcnn_r50_fpn_1x.yml | 35 +- configs/cascade_rcnn_r50_fpn_1x_ms_test.yml | 42 +- ...de_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.yml | 64 +- ...rcnn_dcnv2_se154_vd_fpn_gn_s1x_ms_test.yml | 66 +- ...n_cbr200_vd_fpn_dcnv2_nonlocal_softnms.yml | 67 +- ...are_r200_vd_fpn_dcnv2_nonlocal_softnms.yml | 72 +- .../dcn/cascade_rcnn_dcn_r101_vd_fpn_1x.yml | 35 +- configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml | 35 +- .../cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml | 37 +- .../dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml | 34 +- configs/dcn/faster_rcnn_dcn_r50_fpn_1x.yml | 37 +- configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml | 34 +- .../faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml | 36 +- configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml | 36 +- configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml | 33 +- configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml | 35 +- .../mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml | 36 +- configs/dcn/yolov3_r50vd_dcn.yml | 70 +- ...olov3_r50vd_dcn_obj365_pretrained_coco.yml | 136 +-- configs/face_detection/blazeface.yml | 53 +- configs/face_detection/blazeface_nas.yml | 46 +- configs/face_detection/faceboxes.yml | 45 +- configs/face_detection/faceboxes_lite.yml | 44 +- configs/faster_fpn_reader.yml | 101 ++ configs/faster_rcnn_cbr101_vd_dual_fpn_1x.yml | 35 +- configs/faster_rcnn_cbr50_vd_dual_fpn_1x.yml | 34 +- configs/faster_rcnn_r101_1x.yml | 26 +- configs/faster_rcnn_r101_fpn_1x.yml | 35 +- configs/faster_rcnn_r101_fpn_2x.yml | 35 +- configs/faster_rcnn_r101_vd_fpn_1x.yml | 35 +- configs/faster_rcnn_r101_vd_fpn_2x.yml | 35 +- configs/faster_rcnn_r50_1x.yml | 26 +- configs/faster_rcnn_r50_2x.yml | 26 +- configs/faster_rcnn_r50_fpn_1x.yml | 35 +- configs/faster_rcnn_r50_fpn_2x.yml | 35 +- configs/faster_rcnn_r50_vd_1x.yml | 26 +- configs/faster_rcnn_r50_vd_fpn_2x.yml | 35 +- configs/faster_rcnn_se154_vd_fpn_s1x.yml | 35 +- configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml | 37 +- configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml | 37 +- configs/faster_reader.yml | 88 ++ .../gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml | 36 +- configs/gn/faster_rcnn_r50_fpn_gn_2x.yml | 35 +- configs/gn/mask_rcnn_r50_fpn_gn_2x.yml | 34 +- configs/mask_fpn_reader.yml | 104 ++ configs/mask_rcnn_r101_fpn_1x.yml | 34 +- configs/mask_rcnn_r101_vd_fpn_1x.yml | 36 +- configs/mask_rcnn_r50_1x.yml | 23 +- configs/mask_rcnn_r50_2x.yml | 23 +- configs/mask_rcnn_r50_fpn_1x.yml | 34 +- configs/mask_rcnn_r50_fpn_2x.yml | 34 +- configs/mask_rcnn_r50_vd_fpn_2x.yml | 35 +- configs/mask_rcnn_se154_vd_fpn_s1x.yml | 35 +- configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml | 36 +- configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml | 36 +- configs/mask_reader.yml | 92 ++ ...are_r200_vd_fpn_dcnv2_nonlocal_softnms.yml | 65 +- ...cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas.yml | 72 +- ...are_r200_vd_fpn_dcnv2_nonlocal_softnms.yml | 61 +- configs/retinanet_r101_fpn_1x.yml | 23 +- configs/retinanet_r50_fpn_1x.yml | 23 +- configs/retinanet_x101_vd_64x4d_fpn_1x.yml | 26 +- configs/ssd/ssd_mobilenet_v1_voc.yml | 93 +- configs/ssd/ssd_vgg16_300.yml | 71 +- configs/ssd/ssd_vgg16_300_voc.yml | 69 +- configs/ssd/ssd_vgg16_512.yml | 69 +- configs/ssd/ssd_vgg16_512_voc.yml | 65 +- configs/yolov3_darknet.yml | 27 +- configs/yolov3_darknet_voc.yml | 35 +- configs/yolov3_mobilenet_v1.yml | 27 +- configs/yolov3_mobilenet_v1_fruit.yml | 69 +- configs/yolov3_mobilenet_v1_voc.yml | 33 +- configs/yolov3_r34.yml | 27 +- configs/yolov3_r34_voc.yml | 33 +- configs/yolov3_reader.yml | 102 ++ configs2/faster_rcnn_r50_1x.yml | 96 ++ configs2/faster_reader.yml | 106 ++ docs/GETTING_STARTED.md | 1 - docs/GETTING_STARTED_cn.md | 1 - ppdet/core/workspace.py | 53 +- ppdet/data/README.md | 1 - ppdet/data/README_cn.md | 1 - ppdet/data/__init__.py | 33 +- ppdet/data/data_feed.py | 1064 ----------------- ppdet/data/dataset.py | 64 - ppdet/data/{transform => }/parallel_map.py | 83 +- ppdet/data/reader.py | 480 ++++++-- .../{transform => }/shared_queue/__init__.py | 0 .../{transform => }/shared_queue/queue.py | 0 .../shared_queue/sharedmemory.py | 0 ppdet/data/source/__init__.py | 64 +- .../class_aware_sampling_roidb_source.py | 132 -- ppdet/data/source/coco.py | 159 +++ ppdet/data/source/coco_loader.py | 128 -- ppdet/data/source/dataset.py | 166 +++ ppdet/data/source/iterator_source.py | 100 -- ppdet/data/source/loader.py | 145 --- ppdet/data/source/roidb_source.py | 178 --- ppdet/data/source/simple_source.py | 109 -- ppdet/data/source/voc.py | 188 +++ ppdet/data/source/voc_loader.py | 267 ----- ppdet/data/source/widerface.py | 142 +++ ppdet/data/source/widerface_loader.py | 123 -- ppdet/data/tests/000012.jpg | Bin 56920 -> 0 bytes ppdet/data/tests/coco.yml | 48 - ppdet/data/tests/data/prepare_data.sh | 43 - ppdet/data/tests/rcnn_dataset.yml | 32 - ppdet/data/tests/run_all_tests.py | 45 - ppdet/data/tests/set_env.py | 51 - ppdet/data/tests/test.yml | 73 ++ ppdet/data/tests/test_dataset.py | 39 +- ppdet/data/tests/test_iterator_source.py | 74 -- ppdet/data/tests/test_loader.py | 210 ++-- ppdet/data/tests/test_loader_yaml.py | 110 ++ ppdet/data/tests/test_operator.py | 156 --- ppdet/data/tests/test_reader.py | 162 --- ppdet/data/tests/test_roidb_source.py | 74 -- ppdet/data/tests/test_transformer.py | 117 -- .../data/tools/generate_data_for_training.py | 147 --- ppdet/data/transform/__init__.py | 131 +- ppdet/data/transform/arrange_sample.py | 384 ------ ppdet/data/transform/batch_operators.py | 166 +++ ppdet/data/transform/operators.py | 224 +++- ppdet/data/transform/post_map.py | 152 --- ppdet/data/transform/transformer.py | 108 -- ppdet/modeling/architectures/blazeface.py | 43 +- .../architectures/cascade_mask_rcnn.py | 86 +- ppdet/modeling/architectures/cascade_rcnn.py | 77 +- .../architectures/cascade_rcnn_cls_aware.py | 103 +- ppdet/modeling/architectures/faceboxes.py | 43 +- ppdet/modeling/architectures/faster_rcnn.py | 82 +- ppdet/modeling/architectures/input_helper.py | 44 + ppdet/modeling/architectures/mask_rcnn.py | 100 +- ppdet/modeling/architectures/retinanet.py | 43 +- ppdet/modeling/architectures/ssd.py | 39 +- ppdet/modeling/architectures/yolov3.py | 41 +- ppdet/modeling/model_input.py | 127 -- ppdet/modeling/target_assigners.py | 7 +- ppdet/modeling/tests/test_architectures.py | 10 +- ppdet/utils/coco_eval.py | 5 +- ppdet/utils/download.py | 26 +- ppdet/utils/eval_utils.py | 18 +- ppdet/utils/voc_eval.py | 12 +- ppdet/utils/widerface_eval_utils.py | 2 +- requirements.txt | 1 + tools/eval.py | 68 +- tools/export_model.py | 9 +- tools/face_eval.py | 33 +- tools/infer.py | 26 +- tools/train.py | 43 +- 153 files changed, 4022 insertions(+), 7086 deletions(-) create mode 100644 configs/faster_fpn_reader.yml create mode 100644 configs/faster_reader.yml create mode 100644 configs/mask_fpn_reader.yml create mode 100644 configs/mask_reader.yml create mode 100644 configs/yolov3_reader.yml create mode 100644 configs2/faster_rcnn_r50_1x.yml create mode 100644 configs2/faster_reader.yml delete mode 120000 ppdet/data/README.md delete mode 120000 ppdet/data/README_cn.md delete mode 100644 ppdet/data/data_feed.py delete mode 100644 ppdet/data/dataset.py rename ppdet/data/{transform => }/parallel_map.py (82%) rename ppdet/data/{transform => }/shared_queue/__init__.py (100%) rename ppdet/data/{transform => }/shared_queue/queue.py (100%) rename ppdet/data/{transform => }/shared_queue/sharedmemory.py (100%) delete mode 100644 ppdet/data/source/class_aware_sampling_roidb_source.py create mode 100644 ppdet/data/source/coco.py delete mode 100644 ppdet/data/source/coco_loader.py create mode 100644 ppdet/data/source/dataset.py delete mode 100644 ppdet/data/source/iterator_source.py delete mode 100644 ppdet/data/source/loader.py delete mode 100644 ppdet/data/source/roidb_source.py delete mode 100644 ppdet/data/source/simple_source.py create mode 100644 ppdet/data/source/voc.py delete mode 100644 ppdet/data/source/voc_loader.py create mode 100644 ppdet/data/source/widerface.py delete mode 100644 ppdet/data/source/widerface_loader.py delete mode 100644 ppdet/data/tests/000012.jpg delete mode 100644 ppdet/data/tests/coco.yml delete mode 100755 ppdet/data/tests/data/prepare_data.sh delete mode 100644 ppdet/data/tests/rcnn_dataset.yml delete mode 100644 ppdet/data/tests/run_all_tests.py delete mode 100644 ppdet/data/tests/set_env.py create mode 100644 ppdet/data/tests/test.yml delete mode 100644 ppdet/data/tests/test_iterator_source.py create mode 100644 ppdet/data/tests/test_loader_yaml.py delete mode 100644 ppdet/data/tests/test_operator.py delete mode 100644 ppdet/data/tests/test_reader.py delete mode 100644 ppdet/data/tests/test_roidb_source.py delete mode 100644 ppdet/data/tests/test_transformer.py delete mode 100644 ppdet/data/tools/generate_data_for_training.py delete mode 100644 ppdet/data/transform/arrange_sample.py create mode 100644 ppdet/data/transform/batch_operators.py delete mode 100644 ppdet/data/transform/post_map.py delete mode 100644 ppdet/data/transform/transformer.py create mode 100644 ppdet/modeling/architectures/input_helper.py delete mode 100644 ppdet/modeling/model_input.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4102b69a9..1ab8d75f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,17 +11,17 @@ - id: detect-private-key files: (?!.*paddle)^.*$ - id: end-of-file-fixer - files: \.md$ + files: \.(md|yml)$ - id: trailing-whitespace - files: \.md$ + files: \.(md|yml)$ - repo: https://github.com/Lucas-C/pre-commit-hooks sha: v1.0.1 hooks: - id: forbid-crlf - files: \.md$ + files: \.(md|yml)$ - id: remove-crlf - files: \.md$ + files: \.(md|yml)$ - id: forbid-tabs - files: \.md$ + files: \.(md|yml)$ - id: remove-tabs - files: \.md$ + files: \.(md|yml)$ diff --git a/configs/cascade_mask_rcnn_r50_fpn_1x.yml b/configs/cascade_mask_rcnn_r50_fpn_1x.yml index 1d17f53c6..84bda4a2e 100644 --- a/configs/cascade_mask_rcnn_r50_fpn_1x.yml +++ b/configs/cascade_mask_rcnn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: CascadeMaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 180000 snapshot_iter: 10000 @@ -86,7 +83,7 @@ MaskAssigner: resolution: 28 CascadeBBoxHead: - head: CascadeTwoFCHead + head: CascadeTwoFCHead nms: keep_top_k: 100 nms_threshold: 0.5 @@ -113,33 +110,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/cascade_rcnn_cls_aware_r101_vd_fpn_1x_softnms.yml b/configs/cascade_rcnn_cls_aware_r101_vd_fpn_1x_softnms.yml index 4d449285e..4daebad5b 100644 --- a/configs/cascade_rcnn_cls_aware_r101_vd_fpn_1x_softnms.yml +++ b/configs/cascade_rcnn_cls_aware_r101_vd_fpn_1x_softnms.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNNClsAware -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -107,80 +104,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - sample_transforms: - - !DecodeImage - to_rgb: True - with_mixup: False - - !NormalizeImage - is_channel_first: false - is_scale: True - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 - - !ResizeImage - interp: 1 - target_size: - - 800 - max_size: 1333 - use_cv2: true - - !Permute - to_bgr: false - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - sample_transforms: - - !DecodeImage - to_rgb: True - with_mixup: False - - !NormalizeImage - is_channel_first: false - is_scale: True - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 - - !ResizeImage - interp: 1 - target_size: - - 800 - max_size: 1333 - use_cv2: true - - !Permute - to_bgr: false - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 \ No newline at end of file diff --git a/configs/cascade_rcnn_r50_fpn_1x.yml b/configs/cascade_rcnn_r50_fpn_1x.yml index 47c089c1d..50247b82b 100644 --- a/configs/cascade_rcnn_r50_fpn_1x.yml +++ b/configs/cascade_rcnn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -104,34 +101,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml b/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml index c345aeedb..35d314843 100644 --- a/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml +++ b/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -111,23 +108,22 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: + +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 -FasterRCNNEvalFeed: +EvalReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] + multi_scale: true + num_scales: 18 + use_flip: true dataset: + !COCODataSet dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + anno_path: annotations/instances_val2017.json image_dir: val2017 sample_transforms: - !DecodeImage @@ -160,18 +156,6 @@ FasterRCNNEvalFeed: - !Permute channel_first: true to_bgr: false - batch_transforms: - - !PadMSTest - pad_to_stride: 32 - num_scale: 18 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch + - !PadMultiScaleTest pad_to_stride: 32 - drop_last: false - num_workers: 2 + worker_num: 2 diff --git a/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.yml b/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.yml index 6d666470f..da1876b8f 100755 --- a/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.yml +++ b/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.yml @@ -1,14 +1,11 @@ architecture: CascadeMaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 300000 snapshot_iter: 10 use_gpu: true log_iter: 20 log_smooth_window: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/SENet154_vd_caffe_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/SENet154_vd_caffe_pretrained.tar weights: output/cascade_mask_rcnn_dcn_se154_vd_fpn_gn_s1x/model_final/ metric: COCO num_classes: 81 @@ -96,7 +93,7 @@ MaskAssigner: resolution: 28 CascadeBBoxHead: - head: CascadeXConvNormHead + head: CascadeXConvNormHead nms: keep_top_k: 100 nms_threshold: 0.5 @@ -123,17 +120,19 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: +TrainReader: # batch size per device batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_mask'] dataset: + !COCODataSet dataset_dir: dataset/coco image_dir: train2017 - annotation: annotations/instances_train2017.json - sample_transforms: + anno_path: annotations/instances_train2017.json + sample_transforms: - !DecodeImage - to_rgb: False - with_mixup: False + to_rgb: false - !RandomFlipImage is_mask_flip: true is_normalized: false @@ -142,13 +141,13 @@ MaskRCNNTrainFeed: is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !ResizeImage interp: 1 target_size: @@ -161,7 +160,7 @@ MaskRCNNTrainFeed: - 608 - 640 - 672 - - 704 + - 704 - 736 - 768 - 800 @@ -192,29 +191,32 @@ MaskRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - num_workers: 8 + worker_num: 8 + shuffle: true -MaskRCNNEvalFeed: +EvalReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: + !COCODataSet dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + anno_path: annotations/instances_val2017.json image_dir: val2017 - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: False - with_mixup: False - !NormalizeImage is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !ResizeImage interp: 1 target_size: @@ -227,16 +229,20 @@ MaskRCNNEvalFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - num_workers: 2 + worker_num: 2 + drop_empty: false -MaskRCNNTestFeed: +TestReader: batch_size: 1 + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json + !ImageFolder + anno_path: annotations/instances_val2017.json sample_transforms: - !DecodeImage to_rgb: False - with_mixup: False - !NormalizeImage is_channel_first: false is_scale: False @@ -254,4 +260,4 @@ MaskRCNNTestFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - num_workers: 2 + worker_num: 2 diff --git a/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x_ms_test.yml b/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x_ms_test.yml index 0e5b7bef4..b1d5d497e 100644 --- a/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x_ms_test.yml +++ b/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x_ms_test.yml @@ -1,14 +1,11 @@ architecture: CascadeMaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 300000 snapshot_iter: 10000 use_gpu: true log_iter: 20 log_smooth_window: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/SENet154_vd_caffe_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/SENet154_vd_caffe_pretrained.tar weights: output/cascade_mask_rcnn_dcn_se154_vd_fpn_gn_s1x/model_final/ metric: COCO num_classes: 81 @@ -96,7 +93,7 @@ MaskAssigner: resolution: 28 CascadeBBoxHead: - head: CascadeXConvNormHead + head: CascadeXConvNormHead nms: keep_top_k: 100 nms_threshold: 0.5 @@ -130,14 +127,17 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: +TrainReader: # batch size per device batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_mask'] dataset: + !COCODataSet dataset_dir: dataset/coco image_dir: train2017 - annotation: annotations/instances_train2017.json - sample_transforms: + anno_path: annotations/instances_train2017.json + sample_transforms: - !DecodeImage to_rgb: False with_mixup: False @@ -149,13 +149,13 @@ MaskRCNNTrainFeed: is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !ResizeImage interp: 1 target_size: @@ -168,7 +168,7 @@ MaskRCNNTrainFeed: - 608 - 640 - 672 - - 704 + - 704 - 736 - 768 - 800 @@ -199,28 +199,36 @@ MaskRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - num_workers: 8 + worker_num: 8 + shuffle: true -MaskRCNNEvalFeed: +EvalReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] + multi_scale: true + # num_scale = (len(target_size) + 1) * (1 + use_flip) + num_scales: 18 + use_flip: true dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + !COCODataSet image_dir: val2017 - sample_transforms: + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: - !DecodeImage to_rgb: False - !NormalizeImage is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !MultiscaleTestResize origin_target_size: 800 origin_max_size: 1333 @@ -239,16 +247,17 @@ MaskRCNNEvalFeed: channel_first: true to_bgr: false batch_transforms: - - !PadMSTest + - !PadMultiScaleTest pad_to_stride: 32 - # num_scale = (len(target_size) + 1) * (1 + use_flip) - num_scale: 18 - num_workers: 2 + worker_num: 2 -MaskRCNNTestFeed: +TestReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json + !ImageFolder + anno_path: annotations/instances_val2017.json sample_transforms: - !DecodeImage to_rgb: False @@ -269,4 +278,3 @@ MaskRCNNTestFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - num_workers: 2 diff --git a/configs/dcn/cascade_rcnn_cbr200_vd_fpn_dcnv2_nonlocal_softnms.yml b/configs/dcn/cascade_rcnn_cbr200_vd_fpn_dcnv2_nonlocal_softnms.yml index c783b611f..545280e0f 100644 --- a/configs/dcn/cascade_rcnn_cbr200_vd_fpn_dcnv2_nonlocal_softnms.yml +++ b/configs/dcn/cascade_rcnn_cbr200_vd_fpn_dcnv2_nonlocal_softnms.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 460000 snapshot_iter: 10000 use_gpu: true @@ -109,16 +106,18 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +TrainReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - sample_transforms: + sample_transforms: - !DecodeImage - to_rgb: True - with_mixup: False + to_rgb: true - !RandomFlipImage prob: 0.5 - !NormalizeImage @@ -131,7 +130,7 @@ FasterRCNNTrainFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: [416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152, 1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408] @@ -142,16 +141,19 @@ FasterRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - drop_last: false - num_workers: 2 + worker_num: 2 + shuffle: true -FasterRCNNEvalFeed: +EvalReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + !COCODataSet image_dir: val2017 - sample_transforms: + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: - !DecodeImage to_rgb: True with_mixup: False @@ -165,7 +167,7 @@ FasterRCNNEvalFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: @@ -177,13 +179,34 @@ FasterRCNNEvalFeed: batch_transforms: - !PadBatch pad_to_stride: 32 + worker_num: 2 -FasterRCNNTestFeed: - batch_size: 1 +TestReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false batch_transforms: - !PadBatch pad_to_stride: 32 - drop_last: false - num_workers: 2 + use_padded_im_info: true + batch_size: 1 + worker_num: 2 diff --git a/configs/dcn/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml b/configs/dcn/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml index 0a54c3529..bfc216e9b 100644 --- a/configs/dcn/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml +++ b/configs/dcn/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNNClsAware -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 460000 snapshot_iter: 10000 use_gpu: true @@ -109,16 +106,17 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - batch_size: 1 +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json + !COCODataSet image_dir: train2017 - sample_transforms: + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + sample_transforms: - !DecodeImage - to_rgb: True - with_mixup: False + to_rgb: true - !RandomFlipImage prob: 0.5 - !NormalizeImage @@ -131,7 +129,7 @@ FasterRCNNTrainFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: [416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152, 1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408] @@ -142,16 +140,20 @@ FasterRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 + batch_size: 1 + shuffle: true drop_last: false - num_workers: 2 + worker_num: 2 -FasterRCNNEvalFeed: - batch_size: 1 +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + !COCODataSet image_dir: val2017 - sample_transforms: + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: - !DecodeImage to_rgb: True with_mixup: False @@ -165,7 +167,7 @@ FasterRCNNEvalFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: @@ -177,13 +179,37 @@ FasterRCNNEvalFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - -FasterRCNNTestFeed: batch_size: 1 + worker_num: 2 + drop_empty: false + +TestReader: + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false batch_transforms: - !PadBatch pad_to_stride: 32 - drop_last: false - num_workers: 2 + use_padded_im_info: true + batch_size: 1 + worker_num: 2 diff --git a/configs/dcn/cascade_rcnn_dcn_r101_vd_fpn_1x.yml b/configs/dcn/cascade_rcnn_dcn_r101_vd_fpn_1x.yml index 93373adb3..df88a47d4 100644 --- a/configs/dcn/cascade_rcnn_dcn_r101_vd_fpn_1x.yml +++ b/configs/dcn/cascade_rcnn_dcn_r101_vd_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: '../faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml b/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml index 4c74bd877..237540e8e 100644 --- a/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml +++ b/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: '../faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/dcn/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml b/configs/dcn/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml index dbbe2d801..e2a7ddc4f 100644 --- a/configs/dcn/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml +++ b/configs/dcn/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -26,7 +23,7 @@ ResNeXt: depth: 101 feature_maps: [2, 3, 4, 5] freeze_at: 2 - group_width: 4 + group_width: 4 groups: 64 variant: d dcn_v2_stages: [3, 4, 5] @@ -108,34 +105,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: '../faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml b/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml index d6a949efb..936cf4424 100644 --- a/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml +++ b/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,7 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: '../faster_fpn_reader.yml' +TrainReader: # batch size per device batch_size: 2 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 diff --git a/configs/dcn/faster_rcnn_dcn_r50_fpn_1x.yml b/configs/dcn/faster_rcnn_dcn_r50_fpn_1x.yml index 2048d61e4..b825b7ba8 100644 --- a/configs/dcn/faster_rcnn_dcn_r50_fpn_1x.yml +++ b/configs/dcn/faster_rcnn_dcn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 use_gpu: true snapshot_iter: 10000 @@ -105,34 +102,8 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 +_READER_: '../faster_fpn_reader.yml' +TrainReader: + # batch size per device + batch_size: 2 diff --git a/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml b/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml index f0cb0ba27..ba5417553 100644 --- a/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml +++ b/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,7 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: '../faster_fpn_reader.yml' +TrainReader: # batch size per device batch_size: 2 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 diff --git a/configs/dcn/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml b/configs/dcn/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml index b6d7b81cc..32e73f89a 100644 --- a/configs/dcn/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml +++ b/configs/dcn/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -108,36 +105,5 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - shuffle: true -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - shuffle: false +_READER_: '../faster_fpn_reader.yml' diff --git a/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml b/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml index f5a0b7c45..9451cdab7 100644 --- a/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml +++ b/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml @@ -1,14 +1,11 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true log_smooth_window: 20 log_iter: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_pretrained.tar weights: output/mask_rcnn_dcn_r101_vd_fpn_1x/model_final metric: COCO num_classes: 81 @@ -114,33 +111,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: '../mask_fpn_reader.yml' diff --git a/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml b/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml index 04653001e..f0609a148 100644 --- a/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml +++ b/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 180000 snapshot_iter: 10000 @@ -113,33 +110,5 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: '../mask_fpn_reader.yml' diff --git a/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml b/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml index d008fb84d..a03ac725a 100644 --- a/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml +++ b/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 360000 snapshot_iter: 10000 @@ -114,34 +111,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: '../mask_fpn_reader.yml' diff --git a/configs/dcn/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml b/configs/dcn/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml index 8076d1a5d..97603d8f1 100644 --- a/configs/dcn/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml +++ b/configs/dcn/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml @@ -1,14 +1,11 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true log_smooth_window: 20 log_iter: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar weights: output/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x/model_final metric: COCO num_classes: 81 @@ -116,33 +113,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: '../mask_fpn_reader.yml' diff --git a/configs/dcn/yolov3_r50vd_dcn.yml b/configs/dcn/yolov3_r50vd_dcn.yml index 1525bcb99..6a1c9471a 100755 --- a/configs/dcn/yolov3_r50vd_dcn.yml +++ b/configs/dcn/yolov3_r50vd_dcn.yml @@ -1,14 +1,11 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 500000 log_smooth_window: 20 save_dir: output snapshot_iter: 20000 metric: COCO -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar weights: output/yolov3_r50vd_dcn/model_final num_classes: 80 @@ -62,67 +59,4 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - sample_transforms: - - !DecodeImage - to_rgb: True - with_mixup: True - - !MixupImage - alpha: 1.5 - beta: 1.5 - - !NormalizeBox {} - - !RandomDistort {} - - !ExpandImage - max_ratio: 4 - prob: 0.5 - mean: - - 123.675 - - 116.28 - - 103.53 - - !CropImage - batch_sampler: [[1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0]] - - !RandomInterpImage - target_size: 608 - - !RandomFlipImage - is_normalized: True - - !NormalizeImage - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 - is_scale: True - is_channel_first: False - - !Permute - to_bgr: False - num_workers: 8 - bufsize: 128 - use_process: true - -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: '../yolov3_reader.yml' diff --git a/configs/dcn/yolov3_r50vd_dcn_obj365_pretrained_coco.yml b/configs/dcn/yolov3_r50vd_dcn_obj365_pretrained_coco.yml index 096054099..32929c51f 100755 --- a/configs/dcn/yolov3_r50vd_dcn_obj365_pretrained_coco.yml +++ b/configs/dcn/yolov3_r50vd_dcn_obj365_pretrained_coco.yml @@ -1,14 +1,11 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 55000 log_smooth_window: 20 save_dir: output snapshot_iter: 10000 metric: COCO -pretrain_weights: https://paddlemodels.bj.bcebos.com/object_detection/ResNet50_vd_obj365_pretrained.tar +pretrain_weights: https://paddlemodels.bj.bcebos.com/object_detection/ResNet50_vd_obj365_pretrained.tar weights: output/yolov3_r50vd_dcn_obj365_pretrained_coco/model_final num_classes: 80 @@ -62,98 +59,97 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 +TrainReader: + inputs_def: + fields: ['image', 'gt_bbox', 'gt_class', 'gt_score'] + num_max_boxes: 50 dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json + !COCODataSet image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + with_background: false sample_transforms: - !DecodeImage to_rgb: True - with_mixup: False - - !NormalizeBox {} - - !CropImage - batch_sampler: [[1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 1.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0]] - - !RandomInterpImage - target_size: 608 + - !RandomCrop {} - !RandomFlipImage - is_normalized: True + is_normalized: false + - !NormalizeBox {} + - !PadBox + num_max_boxes: 50 + - !BboxXYXY2XYWH {} + batch_transforms: + - !RandomShape + sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608] + random_inter: True - !NormalizeImage - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 - is_scale: False - is_channel_first: False + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + is_scale: True + is_channel_first: false - !Permute - to_bgr: False - num_workers: 8 - bufsize: 128 + to_bgr: false + channel_first: True + batch_size: 8 + shuffle: true + drop_last: true + worker_num: 8 + bufsize: 32 use_process: true -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] +EvalReader: + inputs_def: + image_shape: [3, 608, 608] + fields: ['image', 'im_size', 'im_id'] + num_max_boxes: 50 dataset: + !COCODataSet dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + anno_path: annotations/instances_val2017.json image_dir: val2017 + with_background: false sample_transforms: - !DecodeImage to_rgb: True - with_mixup: False + with_mixup: false - !ResizeImage - interp: 2 + interp: 2 target_size: 608 - !NormalizeImage - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 - is_scale: False - is_channel_first: False + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + is_scale: True + is_channel_first: false - !Permute - to_bgr: False - + to_bgr: false + channel_first: True + batch_size: 8 + drop_empty: false + worker_num: 8 + bufsize: 32 -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] +TestReader: + inputs_def: + image_shape: [3, 608, 608] + fields: ['image', 'im_size', 'im_id'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json + !ImageFolder + anno_path: annotations/instances_val2017.json + with_background: false sample_transforms: - !DecodeImage to_rgb: True - with_mixup: False + with_mixup: false - !ResizeImage - interp: 2 + interp: 2 target_size: 608 - !NormalizeImage - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 - is_scale: False - is_channel_first: False + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + is_scale: True + is_channel_first: false - !Permute - to_bgr: False - + to_bgr: false + channel_first: True + batch_size: 1 diff --git a/configs/face_detection/blazeface.yml b/configs/face_detection/blazeface.yml index 692f14a7c..ee382ac22 100644 --- a/configs/face_detection/blazeface.yml +++ b/configs/face_detection/blazeface.yml @@ -1,9 +1,6 @@ architecture: BlazeFace max_iters: 320000 -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed -pretrain_weights: +pretrain_weights: use_gpu: true snapshot_iter: 10000 log_smooth_window: 20 @@ -43,18 +40,18 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: - batch_size: 8 - use_process: True +TrainReader: + inputs_def: + image_shape: [3, 640, 640] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_train_bbx_gt.txt + anno_path: wider_face_split/wider_face_train_bbx_gt.txt image_dir: WIDER_train/images - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 @@ -82,49 +79,41 @@ SSDTrainFeed: is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] + batch_size: 8 + use_process: true + shuffle: true -SSDEvalFeed: - batch_size: 1 - use_process: false - fields: ['image', 'im_id', 'gt_box'] +EvalReader: + inputs_def: + fields: ['image', 'im_id'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_val_bbx_gt.txt + anno_path: wider_face_split/wider_face_val_bbx_gt.txt image_dir: WIDER_val/images - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - - !ResizeImage - interp: 1 - target_size: 640 - use_cv2: false - !Permute {} - !NormalizeImage is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] - -SSDTestFeed: batch_size: 1 - use_process: false + +TestReader: + inputs_def: + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder use_default_label: true - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !ResizeImage - interp: 1 - target_size: 640 - use_cv2: false - !Permute {} - !NormalizeImage is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] + batch_size: 1 diff --git a/configs/face_detection/blazeface_nas.yml b/configs/face_detection/blazeface_nas.yml index 45356bda7..7739bf9da 100644 --- a/configs/face_detection/blazeface_nas.yml +++ b/configs/face_detection/blazeface_nas.yml @@ -1,9 +1,6 @@ architecture: BlazeFace max_iters: 320000 -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed -pretrain_weights: +pretrain_weights: use_gpu: true snapshot_iter: 10000 log_smooth_window: 20 @@ -45,18 +42,18 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: - batch_size: 8 - use_process: True +TrainReader: + inputs_def: + image_shape: [3, 640, 640] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_train_bbx_gt.txt + anno_path: wider_face_split/wider_face_train_bbx_gt.txt image_dir: WIDER_train/images - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 @@ -84,21 +81,21 @@ SSDTrainFeed: is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] + batch_size: 8 + use_process: true + shuffle: true -SSDEvalFeed: - batch_size: 1 - use_process: false - fields: ['image', 'im_id', 'gt_box'] +EvalReader: + inputs_def: + fields: ['image', 'im_id'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_val_bbx_gt.txt + anno_path: wider_face_split/wider_face_val_bbx_gt.txt image_dir: WIDER_val/images - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - !ResizeImage interp: 1 @@ -109,18 +106,18 @@ SSDEvalFeed: is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] - -SSDTestFeed: batch_size: 1 - use_process: false + +TestReader: + inputs_def: + image_shape: [3,640,640] + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder use_default_label: true - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !ResizeImage interp: 1 target_size: 640 @@ -130,3 +127,4 @@ SSDTestFeed: is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] + batch_size: 1 diff --git a/configs/face_detection/faceboxes.yml b/configs/face_detection/faceboxes.yml index b27872329..7d9b1c940 100644 --- a/configs/face_detection/faceboxes.yml +++ b/configs/face_detection/faceboxes.yml @@ -1,7 +1,4 @@ architecture: FaceBoxes -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed pretrain_weights: use_gpu: true max_iters: 320000 @@ -43,18 +40,21 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: +TrainReader: batch_size: 8 use_process: True + shuffle: true + inputs_def: + image_shape: [3, 640, 640] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_train_bbx_gt.txt + anno_path: wider_face_split/wider_face_train_bbx_gt.txt image_dir: WIDER_train/images - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 @@ -83,48 +83,37 @@ SSDTrainFeed: mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] -SSDEvalFeed: +EvalReader: batch_size: 1 use_process: false - fields: ['image', 'im_id', 'gt_box'] + inputs_def: + fields: ['image', 'im_id'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_val_bbx_gt.txt + anno_path: wider_face_split/wider_face_val_bbx_gt.txt image_dir: WIDER_val/images - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !NormalizeBox {} - - !ResizeImage - interp: 1 - target_size: 640 - use_cv2: false - !Permute {} - !NormalizeImage is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] -SSDTestFeed: - batch_size: 1 - use_process: false +TestReader: + inputs_def: + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder use_default_label: true - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !ResizeImage - interp: 1 - target_size: 640 - use_cv2: false - !Permute {} - !NormalizeImage is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] + batch_size: 1 diff --git a/configs/face_detection/faceboxes_lite.yml b/configs/face_detection/faceboxes_lite.yml index 157f0337e..587825994 100644 --- a/configs/face_detection/faceboxes_lite.yml +++ b/configs/face_detection/faceboxes_lite.yml @@ -1,7 +1,4 @@ architecture: FaceBoxes -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed pretrain_weights: use_gpu: true max_iters: 320000 @@ -43,18 +40,21 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: +TrainReader: batch_size: 8 use_process: True + shuffle: true + inputs_def: + image_shape: [3, 640, 640] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_train_bbx_gt.txt + anno_path: wider_face_split/wider_face_train_bbx_gt.txt image_dir: WIDER_train/images - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 @@ -83,48 +83,38 @@ SSDTrainFeed: mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] -SSDEvalFeed: +EvalReader: batch_size: 1 use_process: false - fields: ['image', 'im_id', 'gt_box'] + inputs_def: + fields: ['image', 'im_id'] dataset: + !WIDERFaceDataSet dataset_dir: dataset/wider_face - annotation: wider_face_split/wider_face_val_bbx_gt.txt + anno_path: wider_face_split/wider_face_val_bbx_gt.txt image_dir: WIDER_val/images - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - !NormalizeBox {} - - !ResizeImage - interp: 1 - target_size: 640 - use_cv2: false - !Permute {} - !NormalizeImage is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] -SSDTestFeed: - batch_size: 1 - use_process: false +TestReader: + inputs_def: + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder use_default_label: true - drop_last: false - image_shape: [3, 640, 640] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !ResizeImage - interp: 1 - target_size: 640 - use_cv2: false - !Permute {} - !NormalizeImage is_scale: false mean: [104, 117, 123] std: [127.502231, 127.502231, 127.502231] + batch_size: 1 diff --git a/configs/faster_fpn_reader.yml b/configs/faster_fpn_reader.yml new file mode 100644 index 000000000..ff0844acb --- /dev/null +++ b/configs/faster_fpn_reader.yml @@ -0,0 +1,101 @@ +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] + dataset: + !COCODataSet + image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !RandomFlipImage + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + target_size: 800 + max_size: 1333 + interp: 1 + use_cv2: true + - !Permute + to_bgr: false + channel_first: true + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: false + batch_size: 1 + shuffle: true + worker_num: 2 + use_process: false + +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] + # for voc + #fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false + drop_empty: false + worker_num: 2 + +TestReader: + inputs_def: + # set image_shape if needed + fields: ['image', 'im_info', 'im_id', 'im_shape'] + dataset: + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false diff --git a/configs/faster_rcnn_cbr101_vd_dual_fpn_1x.yml b/configs/faster_rcnn_cbr101_vd_dual_fpn_1x.yml index 0c5067a2b..4db5668bd 100644 --- a/configs/faster_rcnn_cbr101_vd_dual_fpn_1x.yml +++ b/configs/faster_rcnn_cbr101_vd_dual_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 diff --git a/configs/faster_rcnn_cbr50_vd_dual_fpn_1x.yml b/configs/faster_rcnn_cbr50_vd_dual_fpn_1x.yml index b7fa3cd78..262defab8 100644 --- a/configs/faster_rcnn_cbr50_vd_dual_fpn_1x.yml +++ b/configs/faster_rcnn_cbr50_vd_dual_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,7 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: # batch size per device batch_size: 2 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 diff --git a/configs/faster_rcnn_r101_1x.yml b/configs/faster_rcnn_r101_1x.yml index c72c34d4d..bc1a1141c 100644 --- a/configs/faster_rcnn_r101_1x.yml +++ b/configs/faster_rcnn_r101_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed use_gpu: true max_iters: 180000 log_smooth_window: 20 @@ -91,25 +88,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'faster_reader.yml' diff --git a/configs/faster_rcnn_r101_fpn_1x.yml b/configs/faster_rcnn_r101_fpn_1x.yml index c11d6f214..d48bbcd8a 100644 --- a/configs/faster_rcnn_r101_fpn_1x.yml +++ b/configs/faster_rcnn_r101_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -103,34 +100,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_rcnn_r101_fpn_2x.yml b/configs/faster_rcnn_r101_fpn_2x.yml index 29838c78b..7b14845e2 100644 --- a/configs/faster_rcnn_r101_fpn_2x.yml +++ b/configs/faster_rcnn_r101_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 360000 snapshot_iter: 10000 use_gpu: true @@ -103,34 +100,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_rcnn_r101_vd_fpn_1x.yml b/configs/faster_rcnn_r101_vd_fpn_1x.yml index 2ef717ffc..78800cc06 100644 --- a/configs/faster_rcnn_r101_vd_fpn_1x.yml +++ b/configs/faster_rcnn_r101_vd_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -104,34 +101,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_rcnn_r101_vd_fpn_2x.yml b/configs/faster_rcnn_r101_vd_fpn_2x.yml index 763d447c8..5e27cf9ae 100644 --- a/configs/faster_rcnn_r101_vd_fpn_2x.yml +++ b/configs/faster_rcnn_r101_vd_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 360000 snapshot_iter: 10000 use_gpu: true @@ -104,34 +101,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_rcnn_r50_1x.yml b/configs/faster_rcnn_r50_1x.yml index 12d349612..4348ee24a 100644 --- a/configs/faster_rcnn_r50_1x.yml +++ b/configs/faster_rcnn_r50_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed use_gpu: true max_iters: 180000 log_smooth_window: 20 @@ -91,25 +88,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'faster_reader.yml' diff --git a/configs/faster_rcnn_r50_2x.yml b/configs/faster_rcnn_r50_2x.yml index 255cd0266..fa4f788a6 100644 --- a/configs/faster_rcnn_r50_2x.yml +++ b/configs/faster_rcnn_r50_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed use_gpu: true max_iters: 360000 log_smooth_window: 20 @@ -91,25 +88,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'faster_reader.yml' diff --git a/configs/faster_rcnn_r50_fpn_1x.yml b/configs/faster_rcnn_r50_fpn_1x.yml index c71910610..d7df636c6 100644 --- a/configs/faster_rcnn_r50_fpn_1x.yml +++ b/configs/faster_rcnn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 use_gpu: true snapshot_iter: 10000 @@ -104,34 +101,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/faster_rcnn_r50_fpn_2x.yml b/configs/faster_rcnn_r50_fpn_2x.yml index 12ae624f6..394c1aab2 100644 --- a/configs/faster_rcnn_r50_fpn_2x.yml +++ b/configs/faster_rcnn_r50_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 use_gpu: true snapshot_iter: 10000 @@ -104,34 +101,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/faster_rcnn_r50_vd_1x.yml b/configs/faster_rcnn_r50_vd_1x.yml index f39a144a4..d06e4893c 100644 --- a/configs/faster_rcnn_r50_vd_1x.yml +++ b/configs/faster_rcnn_r50_vd_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed use_gpu: true max_iters: 180000 log_smooth_window: 20 @@ -93,25 +90,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - drop_last: false - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'faster_reader.yml' diff --git a/configs/faster_rcnn_r50_vd_fpn_2x.yml b/configs/faster_rcnn_r50_vd_fpn_2x.yml index 4b944ef93..bfb231f5e 100644 --- a/configs/faster_rcnn_r50_vd_fpn_2x.yml +++ b/configs/faster_rcnn_r50_vd_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -104,34 +101,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 diff --git a/configs/faster_rcnn_se154_vd_fpn_s1x.yml b/configs/faster_rcnn_se154_vd_fpn_s1x.yml index c3dd761e6..2c37587a8 100644 --- a/configs/faster_rcnn_se154_vd_fpn_s1x.yml +++ b/configs/faster_rcnn_se154_vd_fpn_s1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 260000 snapshot_iter: 10000 use_gpu: true @@ -106,34 +103,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml b/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml index adb607b6e..5a58ae9be 100644 --- a/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml +++ b/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -107,36 +104,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - shuffle: true - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - shuffle: false +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml b/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml index ee36efbe8..c0f0c5cf9 100644 --- a/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml +++ b/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 360000 snapshot_iter: 10000 use_gpu: true @@ -106,36 +103,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - shuffle: true - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - shuffle: false +_READER_: 'faster_fpn_reader.yml' diff --git a/configs/faster_reader.yml b/configs/faster_reader.yml new file mode 100644 index 000000000..193f224af --- /dev/null +++ b/configs/faster_reader.yml @@ -0,0 +1,88 @@ +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] + dataset: + !COCODataSet + image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !RandomFlipImage + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + target_size: 800 + max_size: 1333 + interp: 1 + use_cv2: true + - !Permute + to_bgr: false + channel_first: true + batch_size: 1 + shuffle: true + worker_num: 2 + use_process: false + +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] + # for voc + #fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_size: 1 + shuffle: false + drop_empty: false + worker_num: 2 + +TestReader: + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] + dataset: + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_size: 1 + shuffle: false diff --git a/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml b/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml index 52c61ad4b..3906a14d1 100644 --- a/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml +++ b/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml @@ -1,7 +1,4 @@ architecture: CascadeMaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -88,7 +85,7 @@ MaskAssigner: resolution: 28 CascadeBBoxHead: - head: CascadeXConvNormHead + head: CascadeXConvNormHead nms: keep_top_k: 100 nms_threshold: 0.5 @@ -115,33 +112,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: +_READER_: '../mask_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 diff --git a/configs/gn/faster_rcnn_r50_fpn_gn_2x.yml b/configs/gn/faster_rcnn_r50_fpn_gn_2x.yml index a86deb4f6..a5d9fa788 100644 --- a/configs/gn/faster_rcnn_r50_fpn_gn_2x.yml +++ b/configs/gn/faster_rcnn_r50_fpn_gn_2x.yml @@ -1,7 +1,4 @@ architecture: FasterRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true @@ -104,34 +101,6 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: '../faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 16 - -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - drop_last: false - num_workers: 2 diff --git a/configs/gn/mask_rcnn_r50_fpn_gn_2x.yml b/configs/gn/mask_rcnn_r50_fpn_gn_2x.yml index bffe3ba45..3907705e6 100644 --- a/configs/gn/mask_rcnn_r50_fpn_gn_2x.yml +++ b/configs/gn/mask_rcnn_r50_fpn_gn_2x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 360000 snapshot_iter: 10000 use_gpu: true @@ -113,33 +110,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: '../mask_fpn_reader.yml' diff --git a/configs/mask_fpn_reader.yml b/configs/mask_fpn_reader.yml new file mode 100644 index 000000000..fa7e75cf0 --- /dev/null +++ b/configs/mask_fpn_reader.yml @@ -0,0 +1,104 @@ +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_mask'] + dataset: + !COCODataSet + image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !RandomFlipImage + prob: 0.5 + is_mask_flip: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + target_size: 800 + max_size: 1333 + interp: 1 + use_cv2: true + - !Permute + to_bgr: false + channel_first: true + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: false + batch_size: 1 + shuffle: true + worker_num: 2 + drop_last: false + use_process: false + +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] + # for voc + #fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false + drop_last: false + drop_empty: false + worker_num: 2 + +TestReader: + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] + dataset: + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false + drop_last: false diff --git a/configs/mask_rcnn_r101_fpn_1x.yml b/configs/mask_rcnn_r101_fpn_1x.yml index 12229a074..49930daa2 100644 --- a/configs/mask_rcnn_r101_fpn_1x.yml +++ b/configs/mask_rcnn_r101_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 180000 snapshot_iter: 10000 @@ -111,33 +108,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_r101_vd_fpn_1x.yml b/configs/mask_rcnn_r101_vd_fpn_1x.yml index 91bb40c2f..92af84fbe 100644 --- a/configs/mask_rcnn_r101_vd_fpn_1x.yml +++ b/configs/mask_rcnn_r101_vd_fpn_1x.yml @@ -1,13 +1,10 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true log_smooth_window: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_vd_pretrained.tar weights: output/mask_rcnn_r101_vd_fpn_1x/model_final metric: COCO num_classes: 81 @@ -112,33 +109,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_r50_1x.yml b/configs/mask_rcnn_r50_1x.yml index 6c3dd8418..39fc19d9f 100644 --- a/configs/mask_rcnn_r50_1x.yml +++ b/configs/mask_rcnn_r50_1x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 180000 snapshot_iter: 10000 @@ -102,22 +99,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'mask_reader.yml' diff --git a/configs/mask_rcnn_r50_2x.yml b/configs/mask_rcnn_r50_2x.yml index 091b0cf89..70bfd0cbb 100644 --- a/configs/mask_rcnn_r50_2x.yml +++ b/configs/mask_rcnn_r50_2x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 360000 snapshot_iter: 10000 @@ -104,22 +101,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'mask_reader.yml' diff --git a/configs/mask_rcnn_r50_fpn_1x.yml b/configs/mask_rcnn_r50_fpn_1x.yml index a889ea283..2e6d37e9a 100644 --- a/configs/mask_rcnn_r50_fpn_1x.yml +++ b/configs/mask_rcnn_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 180000 snapshot_iter: 10000 @@ -111,33 +108,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_r50_fpn_2x.yml b/configs/mask_rcnn_r50_fpn_2x.yml index 08977bba3..4a3355d7d 100644 --- a/configs/mask_rcnn_r50_fpn_2x.yml +++ b/configs/mask_rcnn_r50_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 360000 snapshot_iter: 10000 use_gpu: true @@ -111,33 +108,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_r50_vd_fpn_2x.yml b/configs/mask_rcnn_r50_vd_fpn_2x.yml index 12a505752..8bf799377 100644 --- a/configs/mask_rcnn_r50_vd_fpn_2x.yml +++ b/configs/mask_rcnn_r50_vd_fpn_2x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed use_gpu: true max_iters: 360000 snapshot_iter: 10000 @@ -112,34 +109,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_se154_vd_fpn_s1x.yml b/configs/mask_rcnn_se154_vd_fpn_s1x.yml index 443005571..77c8b7725 100644 --- a/configs/mask_rcnn_se154_vd_fpn_s1x.yml +++ b/configs/mask_rcnn_se154_vd_fpn_s1x.yml @@ -1,7 +1,4 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 260000 snapshot_iter: 10000 use_gpu: true @@ -114,34 +111,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - # batch size per device - batch_size: 1 - dataset: - dataset_dir: dataset/coco - image_dir: train2017 - annotation: annotations/instances_train2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml b/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml index 75653ce24..6f8834c5f 100644 --- a/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml +++ b/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml @@ -1,13 +1,10 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 180000 snapshot_iter: 10000 use_gpu: true log_smooth_window: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar weights: output/mask_rcnn_x101_vd_64x4d_fpn_1x/model_final metric: COCO num_classes: 81 @@ -114,33 +111,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml b/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml index c5a711363..fee9c2183 100644 --- a/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml +++ b/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml @@ -1,13 +1,10 @@ architecture: MaskRCNN -train_feed: MaskRCNNTrainFeed -eval_feed: MaskRCNNEvalFeed -test_feed: MaskRCNNTestFeed max_iters: 360000 snapshot_iter: 10000 use_gpu: true log_smooth_window: 20 save_dir: output -pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar weights: output/mask_rcnn_x101_vd_64x4d_fpn_2x/model_final metric: COCO num_classes: 81 @@ -114,33 +111,4 @@ OptimizerBuilder: factor: 0.0001 type: L2 -MaskRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 - -MaskRCNNTestFeed: - batch_size: 1 - dataset: - annotation: annotations/instances_val2017.json - batch_transforms: - - !PadBatch - pad_to_stride: 32 - num_workers: 2 +_READER_: 'mask_fpn_reader.yml' diff --git a/configs/mask_reader.yml b/configs/mask_reader.yml new file mode 100644 index 000000000..2a3a14376 --- /dev/null +++ b/configs/mask_reader.yml @@ -0,0 +1,92 @@ +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_mask'] + dataset: + !COCODataSet + image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !RandomFlipImage + prob: 0.5 + is_mask_flip: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + target_size: 800 + max_size: 1333 + interp: 1 + use_cv2: true + - !Permute + to_bgr: false + channel_first: true + batch_size: 1 + shuffle: true + worker_num: 2 + drop_last: false + use_process: false + +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] + # for voc + #fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_size: 1 + shuffle: false + drop_last: false + drop_empty: false + worker_num: 2 + +TestReader: + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] + dataset: + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_size: 1 + shuffle: false + drop_last: false diff --git a/configs/obj365/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml b/configs/obj365/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml index 0de0e9f52..b24eeb996 100644 --- a/configs/obj365/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml +++ b/configs/obj365/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNNClsAware -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 800000 snapshot_iter: 10000 use_gpu: true @@ -110,16 +107,17 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - batch_size: 1 +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] dataset: + !COCODataSet dataset_dir: dataset/obj365 - annotation: train.json + anno_path: train.json image_dir: train - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: True - with_mixup: False - !RandomFlipImage prob: 0.5 - !NormalizeImage @@ -132,7 +130,7 @@ FasterRCNNTrainFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: [416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152, 1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408] @@ -143,16 +141,20 @@ FasterRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 + batch_size: 1 + shuffle: true drop_last: false - num_workers: 2 + worker_num: 2 -FasterRCNNEvalFeed: - batch_size: 1 +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: + !COCODataSet dataset_dir: dataset/obj365 - annotation: val.json + anno_path: val.json image_dir: val - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: True with_mixup: False @@ -166,7 +168,7 @@ FasterRCNNEvalFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: @@ -178,13 +180,36 @@ FasterRCNNEvalFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - -FasterRCNNTestFeed: batch_size: 1 + worker_num: 2 + drop_empty: false + +TestReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - annotation: dataset/obj365/val.json + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false batch_transforms: - !PadBatch pad_to_stride: 32 - drop_last: false - num_workers: 2 + use_padded_im_info: true + batch_size: 1 + worker_num: 2 diff --git a/configs/obj365/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas.yml b/configs/obj365/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas.yml index be8004252..7b0e67a7a 100644 --- a/configs/obj365/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas.yml +++ b/configs/obj365/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNN -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 500000 snapshot_iter: 10000 use_gpu: true @@ -85,7 +82,7 @@ CascadeBBoxAssigner: fg_fraction: 0.25 CascadeBBoxHead: - head: CascadeXConvNormHead + head: CascadeXConvNormHead nms: keep_top_k: 100 nms_threshold: 0.5 @@ -115,16 +112,17 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - batch_size: 1 +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] dataset: + !COCODataSet dataset_dir: dataset/objects365 - annotation: annotations/train.json + anno_path: annotations/train.json image_dir: train - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: False - with_mixup: False - !RandomFlipImage is_mask_flip: true is_normalized: false @@ -133,13 +131,13 @@ FasterRCNNTrainFeed: is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !ResizeImage interp: 1 target_size: @@ -152,7 +150,7 @@ FasterRCNNTrainFeed: - 608 - 640 - 672 - - 704 + - 704 - 736 - 768 - 800 @@ -183,30 +181,34 @@ FasterRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 - num_workers: 4 + batch_size: 1 + worker_num: 4 + shuffle: true class_aware_sampling: true + use_process: false -FasterRCNNEvalFeed: - batch_size: 1 +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: + !COCODataSet dataset_dir: dataset/objects365 - annotation: annotations/val.json + anno_path: annotations/val.json image_dir: val - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: False - with_mixup: False - !NormalizeImage is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !ResizeImage target_size: 800 max_size: 1333 @@ -217,31 +219,33 @@ FasterRCNNEvalFeed: batch_transforms: - !PadBatch pad_to_stride: 32 + batch_size: 1 + drop_empty: false + worker_num: 2 -FasterRCNNTestFeed: +TestReader: batch_size: 1 dataset: - annotation: dataset/obj365/annotations/val.json - sample_transforms: + !ImageFolder + anno_path: dataset/obj365/annotations/val.json + sample_transforms: - !DecodeImage to_rgb: False - with_mixup: False - !NormalizeImage is_channel_first: false is_scale: False mean: - - 102.9801 + - 102.9801 - 115.9465 - 122.7717 std: - - 1.0 - - 1.0 - - 1.0 + - 1.0 + - 1.0 + - 1.0 - !Permute channel_first: true to_bgr: false batch_transforms: - !PadBatch pad_to_stride: 32 - drop_last: false - num_workers: 2 + worker_num: 2 diff --git a/configs/oidv5/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml b/configs/oidv5/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml index be07dd55a..2d464467d 100644 --- a/configs/oidv5/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml +++ b/configs/oidv5/cascade_rcnn_cls_aware_r200_vd_fpn_dcnv2_nonlocal_softnms.yml @@ -1,7 +1,4 @@ architecture: CascadeRCNNClsAware -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 1500000 snapshot_iter: 10000 use_gpu: true @@ -109,16 +106,17 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - batch_size: 1 +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] dataset: + !COCODataSet dataset_dir: dataset/oid - annotation: train.json + anno_path: train.json image_dir: train - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: True - with_mixup: False - !RandomFlipImage prob: 0.5 - !NormalizeImage @@ -131,7 +129,7 @@ FasterRCNNTrainFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: [416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152, 1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408] @@ -142,16 +140,20 @@ FasterRCNNTrainFeed: batch_transforms: - !PadBatch pad_to_stride: 32 + batch_size: 1 drop_last: false - num_workers: 2 + shuffle: true + worker_num: 2 -FasterRCNNEvalFeed: - batch_size: 1 +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: + !COCODataSet dataset_dir: dataset/oidv5 - annotation: val.json + anno_path: val.json image_dir: val - sample_transforms: + sample_transforms: - !DecodeImage to_rgb: True with_mixup: False @@ -165,7 +167,7 @@ FasterRCNNEvalFeed: std: - 0.229 - 0.224 - - 0.225 + - 0.225 - !ResizeImage interp: 1 target_size: @@ -177,13 +179,34 @@ FasterRCNNEvalFeed: batch_transforms: - !PadBatch pad_to_stride: 32 + batch_size: 1 + worker_num: 2 + drop_empty: false -FasterRCNNTestFeed: +TestReader: batch_size: 1 + inputs_def: + fields: ['image', 'im_info', 'im_id', 'im_shape'] dataset: - annotation: dataset/oidv5/val.json + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false batch_transforms: - !PadBatch pad_to_stride: 32 - drop_last: false - num_workers: 2 + worker_num: 2 diff --git a/configs/retinanet_r101_fpn_1x.yml b/configs/retinanet_r101_fpn_1x.yml index 1864f3798..ae92734e3 100644 --- a/configs/retinanet_r101_fpn_1x.yml +++ b/configs/retinanet_r101_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: RetinaNet -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 use_gpu: true pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_pretrained.tar @@ -73,33 +70,21 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 batch_transforms: - !PadBatch pad_to_stride: 128 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - num_workers: 2 -FasterRCNNEvalFeed: +EvalReader: batch_size: 2 batch_transforms: - !PadBatch pad_to_stride: 128 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - num_workers: 2 -FasterRCNNTestFeed: +TestReader: batch_size: 1 batch_transforms: - !PadBatch pad_to_stride: 128 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json - num_workers: 2 diff --git a/configs/retinanet_r50_fpn_1x.yml b/configs/retinanet_r50_fpn_1x.yml index 8c24ef4de..51510adf5 100644 --- a/configs/retinanet_r50_fpn_1x.yml +++ b/configs/retinanet_r50_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: RetinaNet -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 90000 use_gpu: true pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar @@ -73,33 +70,21 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 batch_transforms: - !PadBatch pad_to_stride: 128 - num_workers: 2 -FasterRCNNEvalFeed: +EvalReader: batch_size: 2 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 batch_transforms: - !PadBatch pad_to_stride: 128 - num_workers: 2 -FasterRCNNTestFeed: +TestReader: batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json batch_transforms: - !PadBatch pad_to_stride: 128 - num_workers: 2 diff --git a/configs/retinanet_x101_vd_64x4d_fpn_1x.yml b/configs/retinanet_x101_vd_64x4d_fpn_1x.yml index 6cc33fafa..c1e6874e3 100644 --- a/configs/retinanet_x101_vd_64x4d_fpn_1x.yml +++ b/configs/retinanet_x101_vd_64x4d_fpn_1x.yml @@ -1,7 +1,4 @@ architecture: RetinaNet -train_feed: FasterRCNNTrainFeed -eval_feed: FasterRCNNEvalFeed -test_feed: FasterRCNNTestFeed max_iters: 180000 use_gpu: true pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNeXt101_vd_64x4d_pretrained.tar @@ -76,33 +73,18 @@ OptimizerBuilder: factor: 0.0001 type: L2 -FasterRCNNTrainFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 +_READER_: 'faster_fpn_reader.yml' +TrainReader: batch_transforms: - !PadBatch pad_to_stride: 128 - num_workers: 2 -FasterRCNNEvalFeed: - batch_size: 1 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 +EvalReader: batch_transforms: - !PadBatch pad_to_stride: 128 - num_workers: 2 -FasterRCNNTestFeed: - batch_size: 1 - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +TestReader: batch_transforms: - !PadBatch pad_to_stride: 128 - num_workers: 2 diff --git a/configs/ssd/ssd_mobilenet_v1_voc.yml b/configs/ssd/ssd_mobilenet_v1_voc.yml index a360830f0..b0d22730e 100644 --- a/configs/ssd/ssd_mobilenet_v1_voc.yml +++ b/configs/ssd/ssd_mobilenet_v1_voc.yml @@ -1,7 +1,4 @@ architecture: SSD -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed pretrain_weights: https://paddlemodels.bj.bcebos.com/object_detection/ssd_mobilenet_v1_coco_pretrained.tar use_gpu: true max_iters: 28000 @@ -56,25 +53,91 @@ OptimizerBuilder: factor: 0.00005 type: L2 -SSDTrainFeed: - batch_size: 32 - use_process: true +TrainReader: + inputs_def: + image_shape: [3, 300, 300] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !VOCDataSet + anno_path: trainval.txt dataset_dir: dataset/voc - annotation: trainval.txt use_default_label: true - -SSDEvalFeed: - batch_size: 64 + sample_transforms: + - !DecodeImage + to_rgb: true + - !RandomDistort + brightness_lower: 0.875 + brightness_upper: 1.125 + is_order: true + - !RandomExpand + fill_value: [127.5, 127.5, 127.5] + - !RandomCrop + allow_no_crop: false + - !NormalizeBox {} + - !ResizeImage + interp: 1 + target_size: 300 + use_cv2: false + - !RandomFlipImage + is_normalized: true + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [127.5, 127.5, 127.5] + std: [127.502231, 127.502231, 127.502231] + batch_size: 32 + shuffle: true + drop_last: true + worker_num: 8 + bufsize: 16 use_process: true + +EvalReader: + inputs_def: + image_shape: [3, 300, 300] + fields: ['image', 'gt_bbox', 'gt_class', 'im_shape', 'im_id', 'is_difficult'] dataset: + !VOCDataSet + anno_path: test.txt dataset_dir: dataset/voc - annotation: test.txt use_default_label: true - drop_last: false + sample_transforms: + - !DecodeImage + to_rgb: true + - !NormalizeBox {} + - !ResizeImage + interp: 1 + target_size: 300 + use_cv2: false + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [127.5, 127.5, 127.5] + std: [127.502231, 127.502231, 127.502231] + batch_size: 32 + worker_num: 8 + bufsize: 32 + use_process: false -SSDTestFeed: - batch_size: 1 +TestReader: + inputs_def: + image_shape: [3,300,300] + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder + anno_path: test.txt use_default_label: true - drop_last: false + sample_transforms: + - !DecodeImage + to_rgb: true + - !ResizeImage + interp: 1 + max_size: 0 + target_size: 300 + use_cv2: false + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [127.5, 127.5, 127.5] + std: [127.502231, 127.502231, 127.502231] + batch_size: 1 diff --git a/configs/ssd/ssd_vgg16_300.yml b/configs/ssd/ssd_vgg16_300.yml index f5e987bcb..a3266d879 100644 --- a/configs/ssd/ssd_vgg16_300.yml +++ b/configs/ssd/ssd_vgg16_300.yml @@ -1,7 +1,4 @@ architecture: SSD -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed use_gpu: true max_iters: 400000 snapshot_iter: 10000 @@ -60,37 +57,27 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: - batch_size: 8 +TrainReader: + inputs_def: + image_shape: [3, 300, 300] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - image_shape: [3, 300, 300] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 brightness_upper: 1.125 is_order: true - - !ExpandImage - max_ratio: 4 - mean: [104, 117, 123] - prob: 0.5 - - !CropImage - avoid_no_bbox: true - batch_sampler: - - [1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0] - satisfy_all: false + - !RandomExpand + fill_value: [104, 117, 123] + - !RandomCrop + allow_no_crop: true + - !NormalizeBox {} - !ResizeImage interp: 1 target_size: 300 @@ -103,19 +90,27 @@ SSDTrainFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 8 + shuffle: true + worker_num: 8 + bufsize: 32 + use_process: true + drop_empty: true -SSDEvalFeed: - batch_size: 16 +EvalReader: + inputs_def: + image_shape: [3, 300, 300] + fields: ['image', 'gt_bbox', 'gt_class', 'im_shape', 'im_id', 'is_difficult'] dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + !COCODataSet image_dir: val2017 - drop_last: false - image_shape: [3, 300, 300] + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco sample_transforms: - !DecodeImage to_rgb: true with_mixup: false + - !NormalizeBox {} - !ResizeImage interp: 1 target_size: 300 @@ -126,12 +121,17 @@ SSDEvalFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 16 + worker_num: 8 + bufsize: 32 -SSDTestFeed: - batch_size: 1 +TestReader: + inputs_def: + image_shape: [3,300,300] + fields: ['image', 'im_id', 'im_shape'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json - image_shape: [3, 300, 300] + !ImageFolder + anno_path: annotations/instances_val2017.json sample_transforms: - !DecodeImage to_rgb: true @@ -147,3 +147,4 @@ SSDTestFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 1 diff --git a/configs/ssd/ssd_vgg16_300_voc.yml b/configs/ssd/ssd_vgg16_300_voc.yml index 36c053759..4ce91f2c3 100644 --- a/configs/ssd/ssd_vgg16_300_voc.yml +++ b/configs/ssd/ssd_vgg16_300_voc.yml @@ -1,7 +1,4 @@ architecture: SSD -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed use_gpu: true max_iters: 120001 snapshot_iter: 10000 @@ -60,41 +57,31 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: - batch_size: 8 +TrainReader: + inputs_def: + image_shape: [3, 300, 300] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: trainval.txt + anno_path: trainval.txt use_default_label: true - image_shape: [3, 300, 300] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 brightness_upper: 1.125 is_order: true - - !ExpandImage - max_ratio: 4 - mean: [104, 117, 123] - prob: 0.5 - - !CropImage - avoid_no_bbox: true - batch_sampler: - - [1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0] - satisfy_all: false + - !RandomExpand + fill_value: [104, 117, 123] + - !RandomCrop + allow_no_crop: true + - !NormalizeBox {} - !ResizeImage interp: 1 target_size: 300 - use_cv2: False + use_cv2: false - !RandomFlipImage is_normalized: true - !Permute @@ -103,15 +90,21 @@ SSDTrainFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 8 + shuffle: true + worker_num: 8 + bufsize: 32 + use_process: 8 -SSDEvalFeed: - batch_size: 32 +EvalReader: + inputs_def: + image_shape: [3, 300, 300] + fields: ['image', 'gt_bbox', 'gt_class', 'im_shape', 'im_id', 'is_difficult'] dataset: + !VOCDataSet + anno_path: test.txt dataset_dir: dataset/voc - annotation: test.txt use_default_label: true - drop_last: false - image_shape: [3, 300, 300] sample_transforms: - !DecodeImage to_rgb: true @@ -125,15 +118,20 @@ SSDEvalFeed: to_bgr: false - !NormalizeImage is_scale: false - mean: [104, 117, 123] + mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 32 + worker_num: 8 + bufsize: 32 -SSDTestFeed: - batch_size: 1 +TestReader: + inputs_def: + image_shape: [3,300,300] + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder + anno_path: test.txt use_default_label: true - drop_last: false - image_shape: [3, 300, 300] sample_transforms: - !DecodeImage to_rgb: true @@ -149,3 +147,4 @@ SSDTestFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 1 diff --git a/configs/ssd/ssd_vgg16_512.yml b/configs/ssd/ssd_vgg16_512.yml index 621432764..9dabca255 100644 --- a/configs/ssd/ssd_vgg16_512.yml +++ b/configs/ssd/ssd_vgg16_512.yml @@ -1,7 +1,4 @@ architecture: SSD -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed use_gpu: true max_iters: 400000 snapshot_iter: 10000 @@ -62,37 +59,28 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: - batch_size: 8 +TrainReader: + inputs_def: + image_shape: [3, 512, 512] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - image_shape: [3, 512, 512] sample_transforms: - !DecodeImage to_rgb: true with_mixup: false - - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 brightness_upper: 1.125 is_order: true - - !ExpandImage - max_ratio: 4 - mean: [104, 117, 123] - prob: 0.5 - - !CropImage - avoid_no_bbox: true - batch_sampler: - - [1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0] - satisfy_all: false + - !RandomExpand + fill_value: [104, 117, 123] + - !RandomCrop + allow_no_crop: true + - !NormalizeBox {} - !ResizeImage interp: 1 target_size: 512 @@ -105,15 +93,21 @@ SSDTrainFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] - -SSDEvalFeed: batch_size: 8 + shuffle: true + worker_num: 8 + bufsize: 32 + use_process: 8 + +EvalReader: + inputs_def: + image_shape: [3,512,512] + fields: ['image', 'gt_bbox', 'gt_class', 'im_shape', 'im_id', 'is_difficult'] dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json + !COCODataSet image_dir: val2017 - drop_last: false - image_shape: [3, 512, 512] + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco sample_transforms: - !DecodeImage to_rgb: true @@ -128,12 +122,18 @@ SSDEvalFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 8 + worker_num: 8 + bufsize: 32 + drop_empty: false -SSDTestFeed: - batch_size: 1 +TestReader: + inputs_def: + image_shape: [3,512,512] + fields: ['image', 'im_id', 'im_shape'] dataset: - annotation: dataset/coco/annotations/instances_val2017.json - image_shape: [3, 512, 512] + !ImageFolder + anno_path: annotations/instances_val2017.json sample_transforms: - !DecodeImage to_rgb: true @@ -149,3 +149,4 @@ SSDTestFeed: is_scale: false mean: [104, 117, 123] std: [1, 1, 1] + batch_size: 1 diff --git a/configs/ssd/ssd_vgg16_512_voc.yml b/configs/ssd/ssd_vgg16_512_voc.yml index b2028e0b1..1fd12a2f1 100644 --- a/configs/ssd/ssd_vgg16_512_voc.yml +++ b/configs/ssd/ssd_vgg16_512_voc.yml @@ -1,7 +1,4 @@ architecture: SSD -train_feed: SSDTrainFeed -eval_feed: SSDEvalFeed -test_feed: SSDTestFeed use_gpu: true max_iters: 120000 snapshot_iter: 10000 @@ -64,37 +61,27 @@ OptimizerBuilder: factor: 0.0005 type: L2 -SSDTrainFeed: - batch_size: 8 +TrainReader: + inputs_def: + image_shape: [3, 512, 512] + fields: ['image', 'gt_bbox', 'gt_class'] dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: trainval.txt + anno_path: trainval.txt use_default_label: true - image_shape: [3, 512, 512] sample_transforms: - !DecodeImage to_rgb: true - with_mixup: false - - !NormalizeBox {} - !RandomDistort brightness_lower: 0.875 brightness_upper: 1.125 is_order: true - - !ExpandImage - max_ratio: 4 - mean: [123, 117, 104] - prob: 0.5 - - !CropImage - avoid_no_bbox: true - batch_sampler: - - [1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 0.0] - - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0] - satisfy_all: false + - !RandomExpand + fill_value: [123, 117, 104] + - !RandomCrop + allow_no_crop: true + - !NormalizeBox {} - !ResizeImage interp: 1 target_size: 512 @@ -107,15 +94,21 @@ SSDTrainFeed: is_scale: false mean: [123, 117, 104] std: [1, 1, 1] + batch_size: 8 + shuffle: true + worker_num: 8 + bufsize: 32 + use_process: 8 -SSDEvalFeed: - batch_size: 32 +EvalReader: + inputs_def: + image_shape: [3, 512, 512] + fields: ['image', 'gt_bbox', 'gt_class', 'im_shape', 'im_id', 'is_difficult'] dataset: + !VOCDataSet + anno_path: test.txt dataset_dir: dataset/voc - annotation: test.txt use_default_label: true - drop_last: false - image_shape: [3, 512, 512] sample_transforms: - !DecodeImage to_rgb: true @@ -131,13 +124,18 @@ SSDEvalFeed: is_scale: false mean: [123, 117, 104] std: [1, 1, 1] + batch_size: 32 + worker_num: 8 + bufsize: 32 -SSDTestFeed: - batch_size: 1 +TestReader: + inputs_def: + image_shape: [3,512,512] + fields: ['image', 'im_id', 'im_shape'] dataset: + !ImageFolder + anno_path: test.txt use_default_label: true - drop_last: false - image_shape: [3, 512, 512] sample_transforms: - !DecodeImage to_rgb: true @@ -153,3 +151,4 @@ SSDTestFeed: is_scale: false mean: [123, 117, 104] std: [1, 1, 1] + batch_size: 1 diff --git a/configs/yolov3_darknet.yml b/configs/yolov3_darknet.yml index 9a1c243b8..bbb4df712 100644 --- a/configs/yolov3_darknet.yml +++ b/configs/yolov3_darknet.yml @@ -1,7 +1,4 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 500200 log_smooth_window: 20 @@ -57,26 +54,4 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - num_workers: 8 - bufsize: 128 - use_process: true - -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'yolov3_reader.yml' diff --git a/configs/yolov3_darknet_voc.yml b/configs/yolov3_darknet_voc.yml index 876d380ff..df74e0360 100644 --- a/configs/yolov3_darknet_voc.yml +++ b/configs/yolov3_darknet_voc.yml @@ -1,7 +1,4 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 70000 log_smooth_window: 20 @@ -58,27 +55,31 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 +_READER_: 'yolov3_reader.yml' +TrainReader: + inputs_def: + fields: ['image', 'gt_bbox', 'gt_class', 'gt_score'] + num_max_boxes: 50 dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: trainval.txt + anno_path: trainval.txt use_default_label: true - num_workers: 8 - bufsize: 128 - use_process: true - mixup_epoch: 250 + with_background: false -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] +EvalReader: + inputs_def: + fields: ['image', 'im_size', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + num_max_boxes: 50 dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: test.txt + anno_path: test.txt use_default_label: true + with_background: false -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] +TestReader: dataset: + !ImageFolder use_default_label: true + with_background: false diff --git a/configs/yolov3_mobilenet_v1.yml b/configs/yolov3_mobilenet_v1.yml index 3e622025b..1e9bcddd0 100644 --- a/configs/yolov3_mobilenet_v1.yml +++ b/configs/yolov3_mobilenet_v1.yml @@ -1,7 +1,4 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 500200 log_smooth_window: 20 @@ -58,26 +55,4 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - num_workers: 8 - bufsize: 128 - use_process: true - -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'yolov3_reader.yml' diff --git a/configs/yolov3_mobilenet_v1_fruit.yml b/configs/yolov3_mobilenet_v1_fruit.yml index 2897b051d..84bc71b9f 100644 --- a/configs/yolov3_mobilenet_v1_fruit.yml +++ b/configs/yolov3_mobilenet_v1_fruit.yml @@ -1,12 +1,9 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 20000 log_smooth_window: 20 save_dir: output -snapshot_iter: 1000 +snapshot_iter: 200 metric: VOC map_type: 11point pretrain_weights: https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar @@ -60,16 +57,19 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 1 +_READER_: 'yolov3_reader.yml' +# will merge TrainReader into yolov3_reader.yml +TrainReader: + inputs_def: + image_shape: [3, 608, 608] + fields: ['image', 'gt_bbox', 'gt_class', 'gt_score'] + num_max_boxes: 50 dataset: + !VOCDataSet dataset_dir: dataset/fruit - annotation: fruit-detection/train.txt + anno_path: train.txt + with_background: false use_default_label: false - num_workers: 16 - bufsize: 128 - use_process: true - mixup_epoch: -1 sample_transforms: - !DecodeImage to_rgb: true @@ -83,40 +83,45 @@ YoloTrainFeed: max_size: 0 target_size: 608 - !RandomFlipImage - is_mask_flip: false is_normalized: true prob: 0.5 - !NormalizeImage - is_channel_first: false + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] is_scale: true - mean: - - 0.485 - - 0.456 - - 0.406 - std: - - 0.229 - - 0.224 - - 0.225 + is_channel_first: false + - !PadBox + num_max_boxes: 50 + - !BboxXYXY2XYWH {} + batch_transforms: + - !RandomShape + sizes: [608] - !Permute channel_first: true to_bgr: false - batch_transforms: - - !RandomShape - sizes: [608] - with_background: false + batch_size: 1 + shuffle: true + mixup_epoch: -1 + worker_num: 4 + bufsize: 64 + use_process: true -YoloEvalFeed: +EvalReader: batch_size: 1 - image_shape: [3, 608, 608] + inputs_def: + image_shape: [3, 608, 608] + fields: ['image', 'im_size', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + num_max_boxes: 50 dataset: + !VOCDataSet dataset_dir: dataset/fruit - annotation: fruit-detection/val.txt + anno_path: val.txt use_default_label: false - + with_background: false -YoloTestFeed: +TestReader: batch_size: 1 - image_shape: [3, 608, 608] dataset: - dataset_dir: dataset/fruit + !ImageFolder use_default_label: false + with_background: false diff --git a/configs/yolov3_mobilenet_v1_voc.yml b/configs/yolov3_mobilenet_v1_voc.yml index 63e4cbea9..d8734f0b7 100644 --- a/configs/yolov3_mobilenet_v1_voc.yml +++ b/configs/yolov3_mobilenet_v1_voc.yml @@ -1,7 +1,4 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 70000 log_smooth_window: 20 @@ -59,27 +56,29 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 +_READER_: 'yolov3_reader.yml' +TrainReader: dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: trainval.txt + anno_path: trainval.txt use_default_label: true - num_workers: 8 - bufsize: 128 - use_process: true - mixup_epoch: 250 + with_background: false -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] +EvalReader: + inputs_def: + image_shape: [3, 608, 608] + fields: ['image', 'im_size', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + num_max_boxes: 50 dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: test.txt + anno_path: test.txt use_default_label: true + with_background: false -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] +TestReader: dataset: + !ImageFolder use_default_label: true + with_background: false diff --git a/configs/yolov3_r34.yml b/configs/yolov3_r34.yml index e864f8fd9..db7fa056c 100644 --- a/configs/yolov3_r34.yml +++ b/configs/yolov3_r34.yml @@ -1,7 +1,4 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 500200 log_smooth_window: 20 @@ -60,26 +57,4 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_train2017.json - image_dir: train2017 - num_workers: 8 - bufsize: 128 - use_process: true - -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] - dataset: - dataset_dir: dataset/coco - annotation: annotations/instances_val2017.json - image_dir: val2017 - -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] - dataset: - annotation: dataset/coco/annotations/instances_val2017.json +_READER_: 'yolov3_reader.yml' diff --git a/configs/yolov3_r34_voc.yml b/configs/yolov3_r34_voc.yml index aa152e8fe..b8db29bb7 100644 --- a/configs/yolov3_r34_voc.yml +++ b/configs/yolov3_r34_voc.yml @@ -1,7 +1,4 @@ architecture: YOLOv3 -train_feed: YoloTrainFeed -eval_feed: YoloEvalFeed -test_feed: YoloTestFeed use_gpu: true max_iters: 70000 log_smooth_window: 20 @@ -61,27 +58,29 @@ OptimizerBuilder: factor: 0.0005 type: L2 -YoloTrainFeed: - batch_size: 8 +_READER_: 'yolov3_reader.yml' +TrainReader: dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: trainval.txt + anno_path: trainval.txt use_default_label: true - num_workers: 8 - bufsize: 128 - use_process: true - mixup_epoch: 250 + with_background: false -YoloEvalFeed: - batch_size: 8 - image_shape: [3, 608, 608] +EvalReader: + inputs_def: + image_shape: [3, 608, 608] + fields: ['image', 'im_size', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + num_max_boxes: 50 dataset: + !VOCDataSet dataset_dir: dataset/voc - annotation: test.txt + anno_path: test.txt use_default_label: true + with_background: false -YoloTestFeed: - batch_size: 1 - image_shape: [3, 608, 608] +TestReader: dataset: + !ImageFolder use_default_label: true + with_background: false diff --git a/configs/yolov3_reader.yml b/configs/yolov3_reader.yml new file mode 100644 index 000000000..70941aaf2 --- /dev/null +++ b/configs/yolov3_reader.yml @@ -0,0 +1,102 @@ +TrainReader: + inputs_def: + fields: ['image', 'gt_bbox', 'gt_class', 'gt_score'] + num_max_boxes: 50 + dataset: + !COCODataSet + image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco + with_background: false + sample_transforms: + - !DecodeImage + to_rgb: True + with_mixup: True + - !MixupImage + alpha: 1.5 + beta: 1.5 + - !ColorDistort {} + - !RandomExpand + fill_value: [123.675, 116.28, 103.53] + - !RandomCrop {} + - !RandomFlipImage + is_normalized: false + - !NormalizeBox {} + - !PadBox + num_max_boxes: 50 + - !BboxXYXY2XYWH {} + batch_transforms: + - !RandomShape + sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608] + random_inter: True + - !NormalizeImage + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + is_scale: True + is_channel_first: false + - !Permute + to_bgr: false + channel_first: True + batch_size: 8 + shuffle: true + mixup_epoch: 250 + drop_last: true + worker_num: 8 + bufsize: 32 + use_process: true + + +EvalReader: + inputs_def: + fields: ['image', 'im_size', 'im_id'] + num_max_boxes: 50 + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + with_background: false + sample_transforms: + - !DecodeImage + to_rgb: True + - !ResizeImage + target_size: 608 + interp: 2 + - !NormalizeImage + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + is_scale: True + is_channel_first: false + - !PadBox + num_max_boxes: 50 + - !Permute + to_bgr: false + channel_first: True + batch_size: 8 + drop_empty: false + worker_num: 8 + bufsize: 32 + +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 + - !ResizeImage + target_size: 608 + interp: 2 + - !NormalizeImage + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + is_scale: True + is_channel_first: false + - !Permute + to_bgr: false + channel_first: True + batch_size: 1 diff --git a/configs2/faster_rcnn_r50_1x.yml b/configs2/faster_rcnn_r50_1x.yml new file mode 100644 index 000000000..c37c55e66 --- /dev/null +++ b/configs2/faster_rcnn_r50_1x.yml @@ -0,0 +1,96 @@ +architecture: FasterRCNN +use_gpu: true +max_iters: 180000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 10000 +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/faster_rcnn_r50_1x/model_final +num_classes: 81 + +FasterRCNN: + backbone: ResNet + rpn_head: RPNHead + roi_extractor: RoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + norm_type: affine_channel + depth: 50 + feature_maps: 4 + freeze_at: 2 + +ResNetC5: + depth: 50 + norm_type: affine_channel + +RPNHead: + anchor_generator: + anchor_sizes: [32, 64, 128, 256, 512] + aspect_ratios: [0.5, 1.0, 2.0] + stride: [16.0, 16.0] + variance: [1.0, 1.0, 1.0, 1.0] + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_negative_overlap: 0.3 + rpn_positive_overlap: 0.7 + rpn_straddle_thresh: 0.0 + use_random: true + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 12000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 6000 + post_nms_top_n: 1000 + +RoIAlign: + resolution: 14 + sampling_ratio: 0 + spatial_scale: 0.0625 + +BBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [0.1, 0.1, 0.2, 0.2] + bg_thresh_hi: 0.5 + bg_thresh_lo: 0.0 + fg_fraction: 0.25 + fg_thresh: 0.5 + +BBoxHead: + head: ResNetC5 + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0001 + type: L2 + +_LOADER_: 'faster_reader.yml' +TrainLoader: + inputs_def: + image_shape: [3,800,800] + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] + batch_size: 3 diff --git a/configs2/faster_reader.yml b/configs2/faster_reader.yml new file mode 100644 index 000000000..c32c57247 --- /dev/null +++ b/configs2/faster_reader.yml @@ -0,0 +1,106 @@ +TrainReader: + inputs_def: + image_shape: [3,NULL,NULL] + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_transforms: + - !DecodeImage + to_rgb: true + - !RandomFlipImage + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + target_size: 800 + max_size: 1333 + interp: 1 + use_cv2: true + - !Permute + to_bgr: false + channel_first: true + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: false + batch_size: 1 + shuffle: true + worker_num: 2 + drop_last: false + use_multi_process: false + +EvalReader: + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] + # for voc + #fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_difficult'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + #sample_num: 100 + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false + drop_last: false +# worker_num: 2 + +TestReader: + inputs_def: + image_shape: [3,800,1333] + fields: ['image', 'im_info', 'im_id', 'im_shape'] + dataset: + !ImageFolder + anno_path: annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false + drop_last: false diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 199b343ed..15b2a9a0f 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -34,7 +34,6 @@ list below can be viewed by `--help` | -r/--resume_checkpoint | train | Checkpoint path for resuming training | None | `-r output/faster_rcnn_r50_1x/10000` | | --eval | train | Whether to perform evaluation in training | False | | | --output_eval | train/eval | json path in evalution | current path | `--output_eval ./json_result` | -| -d/--dataset_dir | train/eval | path for dataset, same as dataset_dir in configs | None | `-d dataset/coco` | | --fp16 | train | Whether to enable mixed precision training | False | GPU training is required | | --loss_scale | train | Loss scaling factor for mixed precision training | 8.0 | enable when `--fp16` is True | | --json_eval | eval | Whether to evaluate with already existed bbox.json or mask.json | False | json path is set in `--output_eval` | diff --git a/docs/GETTING_STARTED_cn.md b/docs/GETTING_STARTED_cn.md index 4190c38f9..916eccc79 100644 --- a/docs/GETTING_STARTED_cn.md +++ b/docs/GETTING_STARTED_cn.md @@ -31,7 +31,6 @@ python tools/infer.py -c configs/faster_rcnn_r50_1x.yml --infer_img=demo/0000005 | -r/--resume_checkpoint | train | 从某一检查点恢复训练 | None | `-r output/faster_rcnn_r50_1x/10000` | | --eval | train | 是否边训练边测试 | False | | | --output_eval | train/eval | 编辑评测保存json路径 | 当前路径 | `--output_eval ./json_result` | -| -d/--dataset_dir | train/eval | 数据集路径, 同配置文件里的dataset_dir | None | `-d dataset/coco` | | --fp16 | train | 是否使用混合精度训练模式 | False | 需使用GPU训练 | | --loss_scale | train | 设置混合精度训练模式中损失值的缩放比例 | 8.0 | 需先开启`--fp16`后使用 | | --json_eval | eval | 是否通过已存在的bbox.json或者mask.json进行评估 | False | json文件路径在`--output_eval`中设置 | diff --git a/ppdet/core/workspace.py b/ppdet/core/workspace.py index bf505d6e4..cbcdcefc4 100644 --- a/ppdet/core/workspace.py +++ b/ppdet/core/workspace.py @@ -22,6 +22,7 @@ import sys import yaml import copy +import collections from .config.schema import SchemaDict, SharedConfig, extract_schema from .config.yaml_helpers import serializable @@ -65,6 +66,8 @@ class AttrDict(dict): global_config = AttrDict() +READER_KEY = '_READER_' + def load_config(file_path): """ @@ -77,25 +80,59 @@ def load_config(file_path): """ _, ext = os.path.splitext(file_path) assert ext in ['.yml', '.yaml'], "only support yaml files for now" + + cfg = AttrDict() with open(file_path) as f: - merge_config(yaml.load(f, Loader=yaml.Loader)) + cfg = merge_config(yaml.load(f, Loader=yaml.Loader), cfg) + + if READER_KEY in cfg: + reader_cfg = cfg[READER_KEY] + if reader_cfg.startswith("~"): + reader_cfg = os.path.expanduser(reader_cfg) + if not reader_cfg.startswith('/'): + reader_cfg = os.path.join(os.path.dirname(file_path), reader_cfg) + + with open(reader_cfg) as f: + merge_config(yaml.load(f, Loader=yaml.Loader)) + del cfg[READER_KEY] + + merge_config(cfg) return global_config -def merge_config(config): +def dict_merge(dct, merge_dct): + """ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of + updating only top-level keys, dict_merge recurses down into dicts nested + to an arbitrary depth, updating keys. The ``merge_dct`` is merged into + ``dct``. + + Args: + dct: dict onto which the merge is executed + merge_dct: dct merged into dct + + Returns: dct + """ + for k, v in merge_dct.items(): + if (k in dct and isinstance(dct[k], dict) and + isinstance(merge_dct[k], collections.Mapping)): + dict_merge(dct[k], merge_dct[k]) + else: + dct[k] = merge_dct[k] + return dct + + +def merge_config(config, another_cfg=None): """ - Merge config into global config. + Merge config into global config or another_cfg. Args: config (dict): Config to be merged. Returns: global config """ - for key, value in config.items(): - if isinstance(value, dict) and key in global_config: - global_config[key].update(value) - else: - global_config[key] = value + global global_config + dct = another_cfg if another_cfg is not None else global_config + return dict_merge(dct, config) def get_registered_modules(): diff --git a/ppdet/data/README.md b/ppdet/data/README.md deleted file mode 120000 index 238fc99bf..000000000 --- a/ppdet/data/README.md +++ /dev/null @@ -1 +0,0 @@ -docs/DATA.md \ No newline at end of file diff --git a/ppdet/data/README_cn.md b/ppdet/data/README_cn.md deleted file mode 120000 index c8e59f305..000000000 --- a/ppdet/data/README_cn.md +++ /dev/null @@ -1 +0,0 @@ -docs/DATA_cn.md \ No newline at end of file diff --git a/ppdet/data/__init__.py b/ppdet/data/__init__.py index 1104c33f6..1a6576e78 100644 --- a/ppdet/data/__init__.py +++ b/ppdet/data/__init__.py @@ -12,35 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# function: -# module to prepare data for detection model training -# -# implementation notes: -# - Dateset -# basic interface to accessing data samples in stream mode -# -# - xxxSource (RoiDbSource) -# * subclass of 'Dataset' -# * load data from local files and other source data -# -# - xxxOperator (DecodeImage) -# * subclass of 'BaseOperator' -# * each op can transform a sample, eg: decode/resize/crop image -# * each op must obey basic rules defined in transform.operator.base -# -# - transformer -# * subclass of 'Dataset' -# * 'MappedDataset' accept a 'xxxSource' and a list of 'xxxOperator' -# to build a transformed 'Dataset' - from __future__ import absolute_import -from .dataset import Dataset -from .reader import Reader -import traceback -if traceback.extract_stack()[0][ - 0] == 'ppdet/data/tools/generate_data_for_training.py': - __all__ = ['Dataset', 'Reader'] -else: - from .data_feed import create_reader - __all__ = ['Dataset', 'Reader', 'create_reader'] +from .reader import * +from .source import * +from .transform import * diff --git a/ppdet/data/data_feed.py b/ppdet/data/data_feed.py deleted file mode 100644 index d8fa69915..000000000 --- a/ppdet/data/data_feed.py +++ /dev/null @@ -1,1064 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import print_function -from __future__ import division - -import os -import inspect - -from ppdet.core.workspace import register, serializable -from ppdet.utils.download import get_dataset_path - -from ppdet.data.reader import Reader -# XXX these are for triggering the decorator -from ppdet.data.transform.operators import ( - DecodeImage, MixupImage, NormalizeBox, NormalizeImage, RandomDistort, - RandomFlipImage, RandomInterpImage, ResizeImage, ExpandImage, CropImage, - Permute, MultiscaleTestResize, Resize, ColorDistort, NormalizePermute, - RandomExpand, RandomCrop) -from ppdet.data.transform.arrange_sample import ( - ArrangeRCNN, ArrangeEvalRCNN, ArrangeTestRCNN, ArrangeSSD, ArrangeEvalSSD, - ArrangeTestSSD, ArrangeYOLO, ArrangeEvalYOLO, ArrangeTestYOLO) - -__all__ = [ - 'PadBatch', 'MultiScale', 'RandomShape', 'PadMSTest', 'DataSet', - 'CocoDataSet', 'DataFeed', 'TrainFeed', 'EvalFeed', 'FasterRCNNTrainFeed', - 'MaskRCNNTrainFeed', 'FasterRCNNEvalFeed', 'MaskRCNNEvalFeed', - 'FasterRCNNTestFeed', 'MaskRCNNTestFeed', 'SSDTrainFeed', 'SSDEvalFeed', - 'SSDTestFeed', 'YoloTrainFeed', 'YoloEvalFeed', 'YoloTestFeed', - 'create_reader' -] - - -def _prepare_data_config(feed, args_path): - # if `DATASET_DIR` does not exists, search ~/.paddle/dataset for a directory - # named `DATASET_DIR` (e.g., coco, pascal), if not present either, download - dataset_home = args_path if args_path else feed.dataset.dataset_dir - if dataset_home: - annotation = getattr(feed.dataset, 'annotation', None) - image_dir = getattr(feed.dataset, 'image_dir', None) - dataset_dir = get_dataset_path(dataset_home, annotation, image_dir) - if annotation: - feed.dataset.annotation = os.path.join(dataset_dir, annotation) - if image_dir: - feed.dataset.image_dir = os.path.join(dataset_dir, image_dir) - - mixup_epoch = -1 - if getattr(feed, 'mixup_epoch', None) is not None: - mixup_epoch = feed.mixup_epoch - - data_config = { - 'ANNO_FILE': feed.dataset.annotation, - 'IMAGE_DIR': feed.dataset.image_dir, - 'USE_DEFAULT_LABEL': feed.dataset.use_default_label, - 'IS_SHUFFLE': feed.shuffle, - 'SAMPLES': feed.samples, - 'WITH_BACKGROUND': feed.with_background, - 'MIXUP_EPOCH': mixup_epoch, - 'TYPE': type(feed.dataset).__source__ - } - - if feed.mode == 'TRAIN': - data_config['CLASS_AWARE_SAMPLING'] = getattr( - feed, 'class_aware_sampling', False) - - if len(getattr(feed.dataset, 'images', [])) > 0: - data_config['IMAGES'] = feed.dataset.images - - return data_config - - -def create_reader(feed, max_iter=0, args_path=None, my_source=None): - """ - Return iterable data reader. - - Args: - max_iter (int): number of iterations. - my_source (callable): callable function to create a source iterator - which is used to provide source data in 'ppdet.data.reader' - """ - - # if `DATASET_DIR` does not exists, search ~/.paddle/dataset for a directory - # named `DATASET_DIR` (e.g., coco, pascal), if not present either, download - data_config = _prepare_data_config(feed, args_path) - - bufsize = getattr(feed, 'bufsize', 10) - use_process = getattr(feed, 'use_process', False) - memsize = getattr(feed, 'memsize', '3G') - transform_config = { - 'WORKER_CONF': { - 'bufsize': bufsize, - 'worker_num': feed.num_workers, - 'use_process': use_process, - 'memsize': memsize - }, - 'BATCH_SIZE': feed.batch_size, - 'DROP_LAST': feed.drop_last, - 'USE_PADDED_IM_INFO': feed.use_padded_im_info, - } - - batch_transforms = feed.batch_transforms - pad = [t for t in batch_transforms if isinstance(t, PadBatch)] - rand_shape = [t for t in batch_transforms if isinstance(t, RandomShape)] - multi_scale = [t for t in batch_transforms if isinstance(t, MultiScale)] - pad_ms_test = [t for t in batch_transforms if isinstance(t, PadMSTest)] - - if any(pad): - transform_config['IS_PADDING'] = True - if pad[0].pad_to_stride != 0: - transform_config['COARSEST_STRIDE'] = pad[0].pad_to_stride - if any(rand_shape): - transform_config['RANDOM_SHAPES'] = rand_shape[0].sizes - if any(multi_scale): - transform_config['MULTI_SCALES'] = multi_scale[0].scales - if any(pad_ms_test): - transform_config['ENABLE_MULTISCALE_TEST'] = True - transform_config['NUM_SCALE'] = feed.num_scale - transform_config['COARSEST_STRIDE'] = pad_ms_test[0].pad_to_stride - - if hasattr(inspect, 'getfullargspec'): - argspec = inspect.getfullargspec - else: - argspec = inspect.getargspec - - ops = [] - for op in feed.sample_transforms: - op_dict = op.__dict__.copy() - argnames = [ - arg for arg in argspec(type(op).__init__).args if arg != 'self' - ] - op_dict = {k: v for k, v in op_dict.items() if k in argnames} - op_dict['op'] = op.__class__.__name__ - ops.append(op_dict) - transform_config['OPS'] = ops - - return Reader.create(feed.mode, data_config, transform_config, max_iter, - my_source) - - -# XXX batch transforms are only stubs for now, actually handled by `post_map` -@serializable -class PadBatch(object): - """ - Pad a batch of samples to same dimensions - - Args: - pad_to_stride (int): pad to multiple of strides, e.g., 32 - """ - - def __init__(self, pad_to_stride=0): - super(PadBatch, self).__init__() - self.pad_to_stride = pad_to_stride - - -@serializable -class MultiScale(object): - """ - Randomly resize image by scale - - Args: - scales (list): list of int, randomly resize to one of these scales - """ - - def __init__(self, scales=[]): - super(MultiScale, self).__init__() - self.scales = scales - - -@serializable -class RandomShape(object): - """ - Randomly reshape a batch - - Args: - sizes (list): list of int, random choose a size from these - """ - - def __init__(self, sizes=[]): - super(RandomShape, self).__init__() - self.sizes = sizes - - -@serializable -class PadMSTest(object): - """ - Padding for multi-scale test - - Args: - pad_to_stride (int): pad to multiple of strides, e.g., 32 - """ - - def __init__(self, pad_to_stride=0): - super(PadMSTest, self).__init__() - self.pad_to_stride = pad_to_stride - - -@serializable -class DataSet(object): - """ - Dataset, e.g., coco, pascal voc - - Args: - annotation (str): annotation file path - image_dir (str): directory where image files are stored - shuffle (bool): shuffle samples - """ - __source__ = 'RoiDbSource' - - def __init__(self, - annotation, - image_dir=None, - dataset_dir=None, - use_default_label=None): - super(DataSet, self).__init__() - self.dataset_dir = dataset_dir - self.annotation = annotation - self.image_dir = image_dir - self.use_default_label = use_default_label - - -COCO_DATASET_DIR = 'dataset/coco' -COCO_TRAIN_ANNOTATION = 'annotations/instances_train2017.json' -COCO_TRAIN_IMAGE_DIR = 'train2017' -COCO_VAL_ANNOTATION = 'annotations/instances_val2017.json' -COCO_VAL_IMAGE_DIR = 'val2017' - - -@serializable -class CocoDataSet(DataSet): - def __init__(self, - dataset_dir=COCO_DATASET_DIR, - annotation=COCO_TRAIN_ANNOTATION, - image_dir=COCO_TRAIN_IMAGE_DIR): - super(CocoDataSet, self).__init__( - dataset_dir=dataset_dir, annotation=annotation, image_dir=image_dir) - - -VOC_DATASET_DIR = 'dataset/voc' -VOC_TRAIN_ANNOTATION = 'train.txt' -VOC_VAL_ANNOTATION = 'val.txt' -VOC_IMAGE_DIR = None -VOC_USE_DEFAULT_LABEL = True - - -@serializable -class VocDataSet(DataSet): - __source__ = 'VOCSource' - - def __init__(self, - dataset_dir=VOC_DATASET_DIR, - annotation=VOC_TRAIN_ANNOTATION, - image_dir=VOC_IMAGE_DIR, - use_default_label=VOC_USE_DEFAULT_LABEL): - super(VocDataSet, self).__init__( - dataset_dir=dataset_dir, - annotation=annotation, - image_dir=image_dir, - use_default_label=use_default_label) - - -@serializable -class SimpleDataSet(DataSet): - __source__ = 'SimpleSource' - - def __init__(self, - dataset_dir=None, - annotation=None, - image_dir=None, - use_default_label=None): - super(SimpleDataSet, self).__init__( - dataset_dir=dataset_dir, annotation=annotation, image_dir=image_dir) - self.images = [] - - def add_images(self, images): - self.images.extend(images) - - -@serializable -class DataFeed(object): - """ - DataFeed encompasses all data loading related settings - - Args: - dataset (object): a `Dataset` instance - fields (list): list of data fields needed - image_shape (list): list of image dims (C, MAX_DIM, MIN_DIM) - sample_transforms (list): list of sample transformations to use - batch_transforms (list): list of batch transformations to use - batch_size (int): number of images per device - shuffle (bool): if samples should be shuffled - drop_last (bool): drop last batch if size is uneven - num_workers (int): number of workers processes (or threads) - bufsize (int): size of queue used to buffer results from workers - use_process (bool): use process or thread as workers - memsize (str): size of shared memory used in result queue - when 'use_process' is True, default to '3G' - """ - __category__ = 'data' - - def __init__(self, - dataset, - fields, - image_shape, - sample_transforms=None, - batch_transforms=None, - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - with_background=True, - num_workers=2, - bufsize=10, - use_process=False, - memsize=None, - use_padded_im_info=False, - class_aware_sampling=False): - super(DataFeed, self).__init__() - self.fields = fields - self.image_shape = image_shape - self.sample_transforms = sample_transforms - self.batch_transforms = batch_transforms - self.batch_size = batch_size - self.shuffle = shuffle - self.samples = samples - self.drop_last = drop_last - self.with_background = with_background - self.num_workers = num_workers - self.bufsize = bufsize - self.use_process = use_process - self.memsize = memsize - self.dataset = dataset - self.use_padded_im_info = use_padded_im_info - self.class_aware_sampling = class_aware_sampling - if isinstance(dataset, dict): - self.dataset = DataSet(**dataset) - - -# for custom (i.e., Non-preset) datasets -@register -class TrainFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset, - fields, - image_shape, - sample_transforms=[], - batch_transforms=[], - batch_size=1, - shuffle=True, - samples=-1, - drop_last=False, - with_background=True, - num_workers=2, - bufsize=10, - use_process=True, - memsize=None): - super(TrainFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - with_background=with_background, - num_workers=num_workers, - bufsize=bufsize, - use_process=use_process, - memsize=memsize) - - -@register -class EvalFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset, - fields, - image_shape, - sample_transforms=[], - batch_transforms=[], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - with_background=True, - num_workers=2): - super(EvalFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - with_background=with_background, - num_workers=num_workers) - - -@register -class TestFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset, - fields, - image_shape, - sample_transforms=[], - batch_transforms=[], - batch_size=1, - shuffle=False, - drop_last=False, - with_background=True, - num_workers=2): - super(TestFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - drop_last=drop_last, - with_background=with_background, - num_workers=num_workers) - - -# yapf: disable -@register -class FasterRCNNTrainFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=CocoDataSet().__dict__, - fields=[ - 'image', 'im_info', 'im_id', 'gt_box', 'gt_label', - 'is_crowd' - ], - image_shape=[None, 3, None, None], - sample_transforms=[ - DecodeImage(to_rgb=True), - RandomFlipImage(prob=0.5), - NormalizeImage(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - ResizeImage(target_size=800, max_size=1333, interp=1), - Permute(to_bgr=False) - ], - batch_transforms=[PadBatch()], - batch_size=1, - shuffle=True, - samples=-1, - drop_last=False, - bufsize=10, - num_workers=2, - use_process=False, - memsize=None, - class_aware_sampling=False): - # XXX this should be handled by the data loader, since `fields` is - # given, just collect them - sample_transforms.append(ArrangeRCNN()) - super(FasterRCNNTrainFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - bufsize=bufsize, - num_workers=num_workers, - use_process=use_process, - memsize=memsize, - class_aware_sampling=class_aware_sampling) - # XXX these modes should be unified - self.mode = 'TRAIN' - - -@register -class FasterRCNNEvalFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=CocoDataSet(COCO_VAL_ANNOTATION, - COCO_VAL_IMAGE_DIR).__dict__, - fields=['image', 'im_info', 'im_id', 'im_shape', 'gt_box', - 'gt_label', 'is_difficult'], - image_shape=[None, 3, None, None], - sample_transforms=[ - DecodeImage(to_rgb=True), - NormalizeImage(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - ResizeImage(target_size=800, max_size=1333, interp=1), - Permute(to_bgr=False) - ], - batch_transforms=[PadBatch()], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - num_workers=2, - use_padded_im_info=True, - enable_multiscale=False, - num_scale=1, - enable_aug_flip=False): - sample_transforms.append(ArrangeEvalRCNN()) - super(FasterRCNNEvalFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - use_padded_im_info=use_padded_im_info) - self.mode = 'VAL' - self.enable_multiscale = enable_multiscale - self.num_scale = num_scale - self.enable_aug_flip = enable_aug_flip - - -@register -class FasterRCNNTestFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=SimpleDataSet(COCO_VAL_ANNOTATION, - COCO_VAL_IMAGE_DIR).__dict__, - fields=['image', 'im_info', 'im_id', 'im_shape'], - image_shape=[None, 3, None, None], - sample_transforms=[ - DecodeImage(to_rgb=True), - NormalizeImage(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - ResizeImage(target_size=800, max_size=1333, interp=1), - Permute(to_bgr=False) - ], - batch_transforms=[PadBatch()], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - num_workers=2, - use_padded_im_info=True): - sample_transforms.append(ArrangeTestRCNN()) - if isinstance(dataset, dict): - dataset = SimpleDataSet(**dataset) - super(FasterRCNNTestFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - use_padded_im_info=use_padded_im_info) - self.mode = 'TEST' - - -# XXX currently use two presets, in the future, these should be combined into a -# single `RCNNTrainFeed`. Mask (and keypoint) should be processed -# automatically if `gt_mask` (or `gt_keypoints`) is in the required fields -@register -class MaskRCNNTrainFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=CocoDataSet().__dict__, - fields=[ - 'image', 'im_info', 'im_id', 'gt_box', 'gt_label', - 'is_crowd', 'gt_mask' - ], - image_shape=[None, 3, None, None], - sample_transforms=[ - DecodeImage(to_rgb=True), - RandomFlipImage(prob=0.5, is_mask_flip=True), - NormalizeImage(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - ResizeImage(target_size=800, - max_size=1333, - interp=1, - use_cv2=True), - Permute(to_bgr=False, channel_first=True) - ], - batch_transforms=[PadBatch()], - batch_size=1, - shuffle=True, - samples=-1, - drop_last=False, - num_workers=2, - use_process=False, - use_padded_im_info=False): - sample_transforms.append(ArrangeRCNN(is_mask=True)) - super(MaskRCNNTrainFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - use_process=use_process) - self.mode = 'TRAIN' - - -@register -class MaskRCNNEvalFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=CocoDataSet(COCO_VAL_ANNOTATION, - COCO_VAL_IMAGE_DIR).__dict__, - fields=['image', 'im_info', 'im_id', 'im_shape'], - image_shape=[None, 3, None, None], - sample_transforms=[ - DecodeImage(to_rgb=True), - NormalizeImage(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - ResizeImage(target_size=800, - max_size=1333, - interp=1, - use_cv2=True), - Permute(to_bgr=False, channel_first=True) - ], - batch_transforms=[PadBatch()], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - num_workers=2, - use_process=False, - use_padded_im_info=True, - enable_multiscale=False, - num_scale=1, - enable_aug_flip=False): - sample_transforms.append(ArrangeTestRCNN()) - super(MaskRCNNEvalFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - use_process=use_process, - use_padded_im_info=use_padded_im_info) - self.mode = 'VAL' - self.enable_multiscale = enable_multiscale - self.num_scale = num_scale - self.enable_aug_flip = enable_aug_flip - - -@register -class MaskRCNNTestFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=SimpleDataSet(COCO_VAL_ANNOTATION, - COCO_VAL_IMAGE_DIR).__dict__, - fields=['image', 'im_info', 'im_id', 'im_shape'], - image_shape=[None, 3, None, None], - sample_transforms=[ - DecodeImage(to_rgb=True), - NormalizeImage( - mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - ResizeImage(target_size=800, max_size=1333, interp=1), - Permute(to_bgr=False, channel_first=True) - ], - batch_transforms=[PadBatch()], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - num_workers=2, - use_process=False, - use_padded_im_info=True): - sample_transforms.append(ArrangeTestRCNN()) - if isinstance(dataset, dict): - dataset = SimpleDataSet(**dataset) - super(MaskRCNNTestFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - use_process=use_process, - use_padded_im_info=use_padded_im_info) - self.mode = 'TEST' - - -@register -class SSDTrainFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=VocDataSet().__dict__, - fields=['image', 'gt_box', 'gt_label'], - image_shape=[3, 300, 300], - sample_transforms=[ - DecodeImage(to_rgb=True, with_mixup=False), - NormalizeBox(), - RandomDistort(brightness_lower=0.875, - brightness_upper=1.125, - is_order=True), - ExpandImage(max_ratio=4, prob=0.5), - CropImage(batch_sampler=[[1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0]], - satisfy_all=False, avoid_no_bbox=False), - ResizeImage(target_size=300, use_cv2=False, interp=1), - RandomFlipImage(is_normalized=True), - Permute(), - NormalizeImage(mean=[127.5, 127.5, 127.5], - std=[127.502231, 127.502231, 127.502231], - is_scale=False) - ], - batch_transforms=[], - batch_size=32, - shuffle=True, - samples=-1, - drop_last=True, - num_workers=8, - bufsize=10, - use_process=True, - memsize=None): - sample_transforms.append(ArrangeSSD()) - super(SSDTrainFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - bufsize=bufsize, - use_process=use_process, - memsize=None) - self.mode = 'TRAIN' - - -@register -class SSDEvalFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__( - self, - dataset=VocDataSet(VOC_VAL_ANNOTATION).__dict__, - fields=['image', 'im_shape', 'im_id', 'gt_box', - 'gt_label', 'is_difficult'], - image_shape=[3, 300, 300], - sample_transforms=[ - DecodeImage(to_rgb=True, with_mixup=False), - NormalizeBox(), - ResizeImage(target_size=300, use_cv2=False, interp=1), - Permute(), - NormalizeImage( - mean=[127.5, 127.5, 127.5], - std=[127.502231, 127.502231, 127.502231], - is_scale=False) - ], - batch_transforms=[], - batch_size=64, - shuffle=False, - samples=-1, - drop_last=True, - num_workers=8, - bufsize=10, - use_process=False, - memsize=None): - sample_transforms.append(ArrangeEvalSSD(fields)) - super(SSDEvalFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - bufsize=bufsize, - use_process=use_process, - memsize=memsize) - self.mode = 'VAL' - - -@register -class SSDTestFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=SimpleDataSet(VOC_VAL_ANNOTATION).__dict__, - fields=['image', 'im_id', 'im_shape'], - image_shape=[3, 300, 300], - sample_transforms=[ - DecodeImage(to_rgb=True), - ResizeImage(target_size=300, use_cv2=False, interp=1), - Permute(), - NormalizeImage( - mean=[127.5, 127.5, 127.5], - std=[127.502231, 127.502231, 127.502231], - is_scale=False) - ], - batch_transforms=[], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - num_workers=8, - bufsize=10, - use_process=False, - memsize=None): - sample_transforms.append(ArrangeTestSSD()) - if isinstance(dataset, dict): - dataset = SimpleDataSet(**dataset) - super(SSDTestFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - num_workers=num_workers, - bufsize=bufsize, - use_process=use_process, - memsize=memsize) - self.mode = 'TEST' - - -@register -class YoloTrainFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=CocoDataSet().__dict__, - fields=['image', 'gt_box', 'gt_label', 'gt_score'], - image_shape=[3, 608, 608], - sample_transforms=[ - DecodeImage(to_rgb=True, with_mixup=True), - MixupImage(alpha=1.5, beta=1.5), - ColorDistort(), - RandomExpand(fill_value=[123.675, 116.28, 103.53]), - RandomCrop(), - RandomFlipImage(is_normalized=False), - Resize(target_dim=608, interp='random'), - NormalizePermute( - mean=[123.675, 116.28, 103.53], - std=[58.395, 57.120, 57.375]), - NormalizeBox(), - ], - batch_transforms=[ - RandomShape(sizes=[ - 320, 352, 384, 416, 448, 480, 512, 544, 576, 608 - ]) - ], - batch_size=8, - shuffle=True, - samples=-1, - drop_last=True, - with_background=False, - num_workers=8, - bufsize=128, - use_process=True, - memsize=None, - num_max_boxes=50, - mixup_epoch=250, - class_aware_sampling=False): - sample_transforms.append(ArrangeYOLO()) - super(YoloTrainFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - with_background=with_background, - num_workers=num_workers, - bufsize=bufsize, - use_process=use_process, - memsize=memsize, - class_aware_sampling=class_aware_sampling) - self.num_max_boxes = num_max_boxes - self.mixup_epoch = mixup_epoch - self.mode = 'TRAIN' - - -@register -class YoloEvalFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=CocoDataSet(COCO_VAL_ANNOTATION, - COCO_VAL_IMAGE_DIR).__dict__, - fields=['image', 'im_size', 'im_id', 'gt_box', - 'gt_label', 'is_difficult'], - image_shape=[3, 608, 608], - sample_transforms=[ - DecodeImage(to_rgb=True), - ResizeImage(target_size=608, interp=2), - NormalizeImage( - mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - Permute(to_bgr=False), - ], - batch_transforms=[], - batch_size=8, - shuffle=False, - samples=-1, - drop_last=False, - with_background=False, - num_workers=8, - num_max_boxes=50, - use_process=False, - memsize=None): - sample_transforms.append(ArrangeEvalYOLO()) - super(YoloEvalFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - with_background=with_background, - num_workers=num_workers, - use_process=use_process, - memsize=memsize) - self.num_max_boxes = num_max_boxes - self.mode = 'VAL' - self.bufsize = 128 - - # support image shape config, resize image with image_shape - for i, trans in enumerate(sample_transforms): - if isinstance(trans, ResizeImage): - sample_transforms[i] = ResizeImage( - target_size=self.image_shape[-1], - interp=trans.interp) - if isinstance(trans, Resize): - sample_transforms[i].target_dim = self.image_shape[-1] - - -@register -class YoloTestFeed(DataFeed): - __doc__ = DataFeed.__doc__ - - def __init__(self, - dataset=SimpleDataSet(COCO_VAL_ANNOTATION, - COCO_VAL_IMAGE_DIR).__dict__, - fields=['image', 'im_size', 'im_id'], - image_shape=[3, 608, 608], - sample_transforms=[ - DecodeImage(to_rgb=True), - ResizeImage(target_size=608, interp=2), - NormalizeImage(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225], - is_scale=True, - is_channel_first=False), - Permute(to_bgr=False), - ], - batch_transforms=[], - batch_size=1, - shuffle=False, - samples=-1, - drop_last=False, - with_background=False, - num_workers=8, - num_max_boxes=50, - use_process=False, - memsize=None): - sample_transforms.append(ArrangeTestYOLO()) - if isinstance(dataset, dict): - dataset = SimpleDataSet(**dataset) - super(YoloTestFeed, self).__init__( - dataset, - fields, - image_shape, - sample_transforms, - batch_transforms, - batch_size=batch_size, - shuffle=shuffle, - samples=samples, - drop_last=drop_last, - with_background=with_background, - num_workers=num_workers, - use_process=use_process, - memsize=memsize) - self.mode = 'TEST' - self.bufsize = 128 - - # support image shape config, resize image with image_shape - for i, trans in enumerate(sample_transforms): - if isinstance(trans, ResizeImage): - sample_transforms[i] = ResizeImage( - target_size=self.image_shape[-1], - interp=trans.interp) - if isinstance(trans, Resize): - sample_transforms[i].target_dim = self.image_shape[-1] -# yapf: enable diff --git a/ppdet/data/dataset.py b/ppdet/data/dataset.py deleted file mode 100644 index 0ee38d12f..000000000 --- a/ppdet/data/dataset.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# function: -# interface for accessing data samples in stream - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - -class Dataset(object): - """interface to access a stream of data samples""" - - def __init__(self): - self._epoch = -1 - self._pos = 0 - - def __next__(self): - return self.next() - - def __iter__(self): - return self - - def __str__(self): - return "{}(epoch:{:d}, size:{:d}, pos:{:d})".format( - type(self).__name__, self._epoch, - self.size(), self._pos) - - def next(self): - """get next sample""" - raise NotImplementedError('%s.next not available' % - (self.__class__.__name__)) - - def reset(self): - """reset to initial status and begins a new epoch""" - raise NotImplementedError('%s.reset not available' % - (self.__class__.__name__)) - - def size(self): - """get number of samples in this dataset""" - raise NotImplementedError('%s.size not available' % - (self.__class__.__name__)) - - def drained(self): - """whether all sampled has been readed out for this epoch""" - raise NotImplementedError('%s.drained not available' % - (self.__class__.__name__)) - - def epoch_id(self): - """return epoch id for latest sample""" - raise NotImplementedError('%s.epoch_id not available' % - (self.__class__.__name__)) diff --git a/ppdet/data/transform/parallel_map.py b/ppdet/data/parallel_map.py similarity index 82% rename from ppdet/data/transform/parallel_map.py rename to ppdet/data/parallel_map.py index bb4ff6e48..14b7069a2 100644 --- a/ppdet/data/transform/parallel_map.py +++ b/ppdet/data/parallel_map.py @@ -13,7 +13,7 @@ # limitations under the License. # function: -# transform samples in 'source' using 'mapper' +# transform samples in 'source' using 'worker' from __future__ import absolute_import from __future__ import division @@ -30,7 +30,7 @@ import uuid import logging import signal import threading -from .transformer import ProxiedDataset +import traceback logger = logging.getLogger(__name__) @@ -38,13 +38,14 @@ logger = logging.getLogger(__name__) class EndSignal(object): """ signal used to notify worker to exit """ + def __init__(self, id, errno=0, errmsg=''): self.id = id self.errno = errno self.errmsg = errmsg -class ParallelMappedDataset(ProxiedDataset): +class ParallelMap(object): """ Transform samples to mapped samples which is similar to 'basic.MappedDataset', but multiple workers (threads or processes) @@ -54,45 +55,48 @@ class ParallelMappedDataset(ProxiedDataset): this class is not thread-safe """ - def __init__(self, source, mapper, worker_args): - super(ParallelMappedDataset, self).__init__(source) - worker_args = {k.lower(): v for k, v in worker_args.items()} - - args = { - 'bufsize': 100, - 'worker_num': 8, - 'use_process': False, - 'memsize': '3G' - } - args.update(worker_args) - if args['use_process'] and type(args['memsize']) is str: - assert args['memsize'][-1].lower() == 'g', \ - "invalid param for memsize[{}], should " \ - "be ended with 'G' or 'g'".format(args['memsize']) - gb = args['memsize'][:-1] - args['memsize'] = int(gb) * 1024 ** 3 - - self._worker_args = args + def __init__(self, + source, + worker, + worker_num, + bufsize=100, + use_process=False, + memsize='3G'): + self._worker_num = worker_num + self._bufsize = bufsize + self._use_process = use_process + if self._use_process and type(memsize) is str: + assert memsize[-1].lower() == 'g', \ + "invalid param for memsize[%s], should be ended with 'G' or 'g'" % (memsize) + gb = memsize[:-1] + self._memsize = int(gb) * 1024**3 self._started = False self._source = source - self._mapper = mapper + self._worker = worker self._exit = False self._setup() + self._souce_drained = False + + def __iter__(self): + return self + + def __next__(self): + return self.next() def _setup(self): """setup input/output queues and workers """ - use_process = self._worker_args.get('use_process', False) + use_process = self._use_process if use_process and sys.platform == "win32": logger.info("Use multi-thread reader instead of " "multi-process reader on Windows.") use_process = False - bufsize = self._worker_args['bufsize'] + bufsize = self._bufsize if use_process: from .shared_queue import SharedQueue as Queue from multiprocessing import Process as Worker from multiprocessing import Event - memsize = self._worker_args['memsize'] + memsize = self._memsize self._inq = Queue(bufsize, memsize=memsize) self._outq = Queue(bufsize, memsize=memsize) else: @@ -105,7 +109,7 @@ class ParallelMappedDataset(ProxiedDataset): self._inq = Queue(bufsize) self._outq = Queue(bufsize) - consumer_num = self._worker_args['worker_num'] + consumer_num = self._worker_num id = str(uuid.uuid4())[-3:] self._producer = threading.Thread( target=self._produce, @@ -118,8 +122,7 @@ class ParallelMappedDataset(ProxiedDataset): consumer_id = 'consumer-' + id + '-' + str(i) p = Worker( target=self._consume, - args=(consumer_id, self._inq, self._outq, - self._mapper)) + args=(consumer_id, self._inq, self._outq, self._worker)) self._consumers.append(p) p.daemon = True setattr(p, 'id', consumer_id) @@ -137,9 +140,11 @@ class ParallelMappedDataset(ProxiedDataset): if self._exit: break try: - inq.put(source.next()) + s = source.next() + inq.put(s) self._produced += 1 except StopIteration: + self._souce_drained = True self._feeding_ev.clear() self._feeding_ev.wait() except Exception as e: @@ -149,11 +154,11 @@ class ParallelMappedDataset(ProxiedDataset): inq.put(endsig) break - def _consume(self, id, inq, outq, mapper): + def _consume(self, id, inq, outq, worker): """Fetch data from 'inq', process it and put result to 'outq'""" - if self._worker_args['use_process']: + if self._use_process: # handle SIGTERM signal to exit to prevent print stack frame - signal.signal(signal.SIGTERM, lambda signum, frame : sys.exit()) + signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) endsig = EndSignal(id) while True: @@ -166,7 +171,7 @@ class ParallelMappedDataset(ProxiedDataset): break try: - result = mapper(sample) + result = worker(sample) outq.put(result) except Exception as e: endsig.errno = -2 @@ -192,12 +197,12 @@ class ParallelMappedDataset(ProxiedDataset): for w in self._consumers: if not w.is_alive() and w.id not in self._consumer_endsig: abnormal_num += 1 - if self._worker_args['use_process']: + if self._use_process: errmsg = "consumer[{}] exit abnormally with exitcode[{}]" \ .format(w.pid, w.exitcode) else: errmsg = "consumer[{}] exit abnormally".format(w.ident) - + logger.warn(errmsg) if abnormal_num > 0: @@ -255,7 +260,8 @@ class ParallelMappedDataset(ProxiedDataset): " for some consumers exited abnormally before!!!" if not self.drained(): - logger.warn("reset before epoch[{}] finishes".format(self._epoch)) + logger.warn("reset before epoch[{}] finishes".format( + self._epoch)) self._produced = self._produced - self._consumed else: self._produced = 0 @@ -266,10 +272,11 @@ class ParallelMappedDataset(ProxiedDataset): + " cannot start another epoch" self._source.reset() + self._souce_drained = False self._consumed = 0 self._feeding_ev.set() # FIXME(dengkaipeng): fix me if you have better impliment # handle terminate reader process, do not print stack frame -signal.signal(signal.SIGTERM, lambda signum, frame : sys.exit()) +signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) diff --git a/ppdet/data/reader.py b/ppdet/data/reader.py index b2d4d0785..f905bbdba 100644 --- a/ppdet/data/reader.py +++ b/ppdet/data/reader.py @@ -12,131 +12,393 @@ # See the License for the specific language governing permissions and # limitations under the License. -# function: -# Interface to build readers for detection data like COCO or VOC -# - from __future__ import absolute_import from __future__ import division from __future__ import print_function -from __future__ import unicode_literals - -from numbers import Integral +import copy +import functools +import collections +import traceback +import numpy as np import logging -from .source import build_source -from .transform import build_mapper, map, batch, batch_map + +from ppdet.core.workspace import register, serializable + +from .parallel_map import ParallelMap + +__all__ = ['Reader', 'create_reader'] logger = logging.getLogger(__name__) +class Compose(object): + def __init__(self, transforms, ctx=None): + self.transforms = transforms + self.ctx = ctx + + def __call__(self, data): + ctx = self.ctx if self.ctx else {} + for f in self.transforms: + try: + data = f(data, ctx) + except Exception as e: + stack_info = traceback.format_exc() + logger.info("fail to map op [{}] with error: {} and stack:\n{}". + format(f, e, str(stack_info))) + raise e + return data + + +def _calc_img_weights(roidbs): + """ calculate the probabilities of each sample + """ + imgs_cls = [] + num_per_cls = {} + img_weights = [] + for i, roidb in enumerate(roidbs): + img_cls = set([k for cls in roidbs[i]['gt_class'] for k in cls]) + imgs_cls.append(img_cls) + for c in img_cls: + if c not in num_per_cls: + num_per_cls[c] = 1 + else: + num_per_cls[c] += 1 + + for i in range(len(roidbs)): + weights = 0 + for c in imgs_cls[i]: + weights += 1 / num_per_cls[c] + img_weights.append(weights) + # probabilities sum to 1 + img_weights = img_weights / np.sum(img_weights) + return img_weights + + +def _has_empty(item): + def empty(x): + if isinstance(x, np.ndarray) and x.size == 0: + return True + elif isinstance(x, collections.Sequence) and len(x) == 0: + return True + else: + return False + + if isinstance(item, collections.Sequence) and len(item) == 0: + return True + if item is None: + return True + if empty(item): + return True + return False + + +def _segm(samples): + assert 'gt_poly' in samples + segms = samples['gt_poly'] + if 'is_crowd' in samples: + is_crowd = samples['is_crowd'] + if len(segms) != 0: + assert len(segms) == is_crowd.shape[0] + + gt_masks = [] + valid = True + for i in range(len(segms)): + segm = segms[i] + gt_segm = [] + if 'is_crowd' in samples and is_crowd[i]: + gt_segm.append([[0, 0]]) + else: + for poly in segm: + if len(poly) == 0: + valid = False + break + gt_segm.append(np.array(poly).reshape(-1, 2)) + if (not valid) or len(gt_segm) == 0: + break + gt_masks.append(gt_segm) + return gt_masks + + +def batch_arrange(batch_samples, fields): + def im_shape(samples, dim=3): + # hard code + assert 'h' in samples + assert 'w' in samples + if dim == 3: # RCNN, .. + return np.array((samples['h'], samples['w'], 1), dtype=np.float32) + else: # YOLOv3, .. + return np.array((samples['h'], samples['w']), dtype=np.int32) + + arrange_batch = [] + for samples in batch_samples: + one_ins = () + for i, field in enumerate(fields): + if field == 'gt_mask': + one_ins += (_segm(samples), ) + elif field == 'im_shape': + one_ins += (im_shape(samples), ) + elif field == 'im_size': + one_ins += (im_shape(samples, 2), ) + else: + if field == 'is_difficult': + field = 'difficult' + assert field in samples, '{} not in samples'.format(field) + one_ins += (samples[field], ) + arrange_batch.append(one_ins) + return arrange_batch + + +@register +@serializable class Reader(object): - """Interface to make readers for training or evaluation""" + """ + Args: + dataset (DataSet): DataSet object + sample_transforms (list of BaseOperator): a list of sample transforms + operators. + batch_transforms (list of BaseOperator): a list of batch transforms + operators. + batch_size (int): batch size. + shuffle (bool): whether shuffle dataset or not. Default False. + drop_last (bool): whether drop last batch or not. Default False. + drop_empty (bool): whether drop sample when it's gt is empty or not. + Default True. + mixup_epoch (int): mixup epoc number. Default is -1, meaning + not use mixup. + class_aware_sampling (bool): whether use class-aware sampling or not. + Default False. + worker_num (int): number of working threads/processes. + Default -1, meaning not use multi-threads/multi-processes. + use_process (bool): whether use multi-processes or not. + It only works when worker_num > 1. Default False. + bufsize (int): buffer size for multi-threads/multi-processes, + please note, one instance in buffer is one batch data. + memsize (str): size of shared memory used in result queue when + use_process is true. Default 3G. + inputs_def (dict): network input definition use to get input fields, + which is used to determine the order of returned data. + """ + + def __init__(self, + dataset=None, + sample_transforms=None, + batch_transforms=None, + batch_size=None, + shuffle=False, + drop_last=False, + drop_empty=True, + mixup_epoch=-1, + class_aware_sampling=False, + worker_num=-1, + use_process=False, + bufsize=100, + memsize='3G', + inputs_def=None): + self._dataset = dataset + self._roidbs = self._dataset.get_roidb() + self._fields = copy.deepcopy(inputs_def[ + 'fields']) if inputs_def else None + + # transform + self._sample_transforms = Compose(sample_transforms, + {'fields': self._fields}) + self._batch_transforms = None + if batch_transforms: + self._batch_transforms = Compose(batch_transforms, + {'fields': self._fields}) + + # data + if inputs_def and inputs_def.get('multi_scale', False): + from ppdet.modeling.architectures.input_helper import multiscale_def + im_shape = inputs_def[ + 'image_shape'] if 'image_shape' in inputs_def else [ + 3, None, None + ] + _, ms_fields = multiscale_def(im_shape, inputs_def['num_scales'], + inputs_def['use_flip']) + self._fields += ms_fields + self._batch_size = batch_size + self._shuffle = shuffle + self._drop_last = drop_last + self._drop_empty = drop_empty + + # sampling + self._mixup_epoch = mixup_epoch + self._class_aware_sampling = class_aware_sampling - def __init__(self, data_cf, trans_conf, maxiter=-1): - self._data_cf = data_cf - self._trans_conf = trans_conf - self._maxiter = maxiter - self._cname2cid = None - assert isinstance(self._maxiter, Integral), "maxiter should be int" + self._load_img = False + self._sample_num = len(self._roidbs) - def _make_reader(self, mode, my_source=None): - """Build reader for training or validation""" - if my_source is None: - file_conf = self._data_cf[mode] + if self._class_aware_sampling: + self.img_weights = _calc_img_weights(self._roidbs) + self._indexes = None - # 1, Build data source + self._pos = -1 + self._epoch = -1 + self._drained = False - sc_conf = {'data_cf': file_conf, 'cname2cid': self._cname2cid} - sc = build_source(sc_conf) + # multi-process + self._worker_num = worker_num + self._parallel = None + if self._worker_num > -1: + task = functools.partial(self.worker, self._drop_empty) + self._parallel = ParallelMap(self, task, worker_num, bufsize, + use_process, memsize) + + def __call__(self): + if self._worker_num > -1: + return self._parallel + else: + return self + + def __iter__(self): + return self + + def reset(self): + """implementation of Dataset.reset + """ + self.indexes = [i for i in range(self.size())] + if self._class_aware_sampling: + self.indexes = np.random.choice( + self._sample_num, + self._sample_num, + replace=False, + p=self.img_weights) + + if self._shuffle: + np.random.shuffle(self.indexes) + + if self._mixup_epoch > 0 and len(self.indexes) < 2: + logger.info("Disable mixup for dataset samples " + "less than 2 samples") + self._mixup_epoch = -1 + + if self._epoch < 0: + self._epoch = 0 else: - sc = my_source - - # 2, Buid a transformed dataset - ops = self._trans_conf[mode]['OPS'] - batchsize = self._trans_conf[mode]['BATCH_SIZE'] - drop_last = False if 'DROP_LAST' not in \ - self._trans_conf[mode] else self._trans_conf[mode]['DROP_LAST'] - - mapper = build_mapper(ops, {'is_train': mode == 'TRAIN'}) - - worker_args = None - if 'WORKER_CONF' in self._trans_conf[mode]: - worker_args = self._trans_conf[mode]['WORKER_CONF'] - worker_args = {k.lower(): v for k, v in worker_args.items()} - - mapped_ds = map(sc, mapper, worker_args) - # In VAL mode, gt_bbox, gt_label can be empty, and should - # not be dropped - batched_ds = batch( - mapped_ds, batchsize, drop_last, drop_empty=(mode != "VAL")) - - trans_conf = {k.lower(): v for k, v in self._trans_conf[mode].items()} - need_keys = { - 'is_padding', - 'coarsest_stride', - 'random_shapes', - 'multi_scales', - 'use_padded_im_info', - 'enable_multiscale_test', - 'num_scale', - } - bm_config = { - key: value - for key, value in trans_conf.items() if key in need_keys - } - - batched_ds = batch_map(batched_ds, bm_config) - - batched_ds.reset() - if mode.lower() == 'train': - if self._cname2cid is not None: - logger.warn('cname2cid already set, it will be overridden') - self._cname2cid = getattr(sc, 'cname2cid', None) - - # 3, Build a reader - maxit = -1 if self._maxiter <= 0 else self._maxiter - - def _reader(): - n = 0 - while True: - for _batch in batched_ds: + self._epoch += 1 + + self._pos = 0 + self._drained = False + + def __next__(self): + return self.next() + + def next(self): + if self._epoch < 0: + self.reset() + if self.drained(): + raise StopIteration + batch = self._load_batch() + if self._drop_last and len(batch) < self._batch_size: + raise StopIteration + if self._worker_num > -1: + return batch + else: + return self.worker(self._drop_empty, batch) + + def _load_batch(self): + batch = [] + bs = 0 + while bs != self._batch_size: + if self._pos >= self.size(): + break + pos = self.indexes[self._pos] + sample = copy.deepcopy(self._roidbs[pos]) + self._pos += 1 + + if self._drop_empty and self._fields and 'gt_mask' in self._fields: + if _has_empty(_segm(sample)): + #logger.warn('gt_mask is empty or not valid in {}'.format( + # sample['im_file'])) + continue + if self._drop_empty and self._fields and 'gt_bbox' in self._fields: + if _has_empty(sample['gt_bbox']): + #logger.warn('gt_bbox {} is empty or not valid in {}, ' + # 'drop this sample'.format( + # sample['im_file'], sample['gt_bbox'])) + continue + + if self._load_img: + sample['image'] = self._load_image(sample['im_file']) + + if self._epoch < self._mixup_epoch: + num = len(self.indexes) + mix_idx = np.random.randint(1, num) + mix_idx = self.indexes[(mix_idx + self._pos - 1) % num] + sample['mixup'] = copy.deepcopy(self._roidbs[mix_idx]) + if self._load_img: + sample['mixup']['image'] = self._load_image(sample['mixup'][ + 'im_file']) + + batch.append(sample) + bs += 1 + return batch + + def worker(self, drop_empty=True, batch_samples=None): + """ + sample transform and batch transform. + """ + batch = [] + for sample in batch_samples: + sample = self._sample_transforms(sample) + if drop_empty and 'gt_bbox' in sample: + if _has_empty(sample['gt_bbox']): + #logger.warn('gt_bbox {} is empty or not valid in {}, ' + # 'drop this sample'.format( + # sample['im_file'], sample['gt_bbox'])) + continue + batch.append(sample) + if len(batch) > 0 and self._batch_transforms: + batch = self._batch_transforms(batch) + if len(batch) > 0 and self._fields: + batch = batch_arrange(batch, self._fields) + return batch + + def _load_image(self, filename): + with open(filename, 'rb') as f: + return f.read() + + def size(self): + """ implementation of Dataset.size + """ + return self._sample_num + + def drained(self): + """ implementation of Dataset.drained + """ + assert self._epoch >= 0, 'The first epoch has not begin!' + return self._pos >= self.size() + + def stop(self): + if self._parallel: + self._parallel.stop() + + +def create_reader(cfg, max_iter=0): + """ + Return iterable data reader. + + Args: + max_iter (int): number of iterations. + """ + if not isinstance(cfg, dict): + raise TypeError("The config should be a dict when creating reader.") + + reader = Reader(**cfg)() + + def _reader(): + n = 0 + while True: + for _batch in reader: + if len(_batch) > 0: yield _batch n += 1 - if maxit > 0 and n == maxit: - return - batched_ds.reset() - if maxit <= 0: + if max_iter > 0 and n == max_iter: return + reader.reset() + if max_iter <= 0: + return - if hasattr(sc, 'get_imid2path'): - _reader.imid2path = sc.get_imid2path() - - return _reader - - def train(self): - """Build reader for training""" - return self._make_reader('TRAIN') - - def val(self): - """Build reader for validation""" - return self._make_reader('VAL') - - def test(self): - """Build reader for inference""" - return self._make_reader('TEST') - - @classmethod - def create(cls, - mode, - data_config, - transform_config, - max_iter=-1, - my_source=None, - ret_iter=True): - """ create a specific reader """ - reader = Reader({mode: data_config}, {mode: transform_config}, max_iter) - if ret_iter: - return reader._make_reader(mode, my_source) - else: - return reader + return _reader diff --git a/ppdet/data/transform/shared_queue/__init__.py b/ppdet/data/shared_queue/__init__.py similarity index 100% rename from ppdet/data/transform/shared_queue/__init__.py rename to ppdet/data/shared_queue/__init__.py diff --git a/ppdet/data/transform/shared_queue/queue.py b/ppdet/data/shared_queue/queue.py similarity index 100% rename from ppdet/data/transform/shared_queue/queue.py rename to ppdet/data/shared_queue/queue.py diff --git a/ppdet/data/transform/shared_queue/sharedmemory.py b/ppdet/data/shared_queue/sharedmemory.py similarity index 100% rename from ppdet/data/transform/shared_queue/sharedmemory.py rename to ppdet/data/shared_queue/sharedmemory.py diff --git a/ppdet/data/source/__init__.py b/ppdet/data/source/__init__.py index e55df6962..c5c26a16f 100644 --- a/ppdet/data/source/__init__.py +++ b/ppdet/data/source/__init__.py @@ -12,62 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function +from . import coco +from . import voc +from . import widerface -import copy - -from .roidb_source import RoiDbSource -from .simple_source import SimpleSource -from .iterator_source import IteratorSource -from .class_aware_sampling_roidb_source import ClassAwareSamplingRoiDbSource - - -def build_source(config): - """ - Build dataset from source data, default source type is 'RoiDbSource' - Args: - config (dict): should have following structure: - { - data_cf (dict): - anno_file (str): label file or image list file path - image_dir (str): root directory for images - samples (int): number of samples to load, -1 means all - is_shuffle (bool): should samples be shuffled - load_img (bool): should images be loaded - mixup_epoch (int): parse mixup in first n epoch - with_background (bool): whether load background as a class - cname2cid (dict): the label name to id dictionary - } - """ - if 'data_cf' in config: - data_cf = config['data_cf'] - data_cf['cname2cid'] = config['cname2cid'] - else: - data_cf = config - - data_cf = {k.lower(): v for k, v in data_cf.items()} - - args = copy.deepcopy(data_cf) - # defaut type is 'RoiDbSource' - source_type = 'RoiDbSource' - if 'type' in data_cf: - if data_cf['type'] in ['VOCSource', 'COCOSource', 'RoiDbSource']: - if 'class_aware_sampling' in args and args['class_aware_sampling']: - source_type = 'ClassAwareSamplingRoiDbSource' - else: - source_type = 'RoiDbSource' - if 'class_aware_sampling' in args: - del args['class_aware_sampling'] - else: - source_type = data_cf['type'] - del args['type'] - if source_type == 'RoiDbSource': - return RoiDbSource(**args) - elif source_type == 'SimpleSource': - return SimpleSource(**args) - elif source_type == 'ClassAwareSamplingRoiDbSource': - return ClassAwareSamplingRoiDbSource(**args) - else: - raise ValueError('source type not supported: ' + source_type) +from .coco import * +from .voc import * +from .widerface import * diff --git a/ppdet/data/source/class_aware_sampling_roidb_source.py b/ppdet/data/source/class_aware_sampling_roidb_source.py deleted file mode 100644 index 0175037c3..000000000 --- a/ppdet/data/source/class_aware_sampling_roidb_source.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#function: -# interface to load data from local files and parse it for samples, -# eg: roidb data in pickled files - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import os -import random - -import copy -import collections -import pickle as pkl -import numpy as np -from .roidb_source import RoiDbSource - - -class ClassAwareSamplingRoiDbSource(RoiDbSource): - """ interface to load class aware sampling roidb data from files - """ - - def __init__(self, - anno_file, - image_dir=None, - samples=-1, - is_shuffle=True, - load_img=False, - cname2cid=None, - use_default_label=None, - mixup_epoch=-1, - with_background=True): - """ Init - - Args: - fname (str): label file path - image_dir (str): root dir for images - samples (int): samples to load, -1 means all - is_shuffle (bool): whether to shuffle samples - load_img (bool): whether load data in this class - cname2cid (dict): the label name to id dictionary - use_default_label (bool):whether use the default mapping of label to id - mixup_epoch (int): parse mixup in first n epoch - with_background (bool): whether load background - as a class - """ - super(ClassAwareSamplingRoiDbSource, self).__init__( - anno_file=anno_file, - image_dir=image_dir, - samples=samples, - is_shuffle=is_shuffle, - load_img=load_img, - cname2cid=cname2cid, - use_default_label=use_default_label, - mixup_epoch=mixup_epoch, - with_background=with_background) - self._img_weights = None - - def __str__(self): - return 'ClassAwareSamplingRoidbSource(fname:%s,epoch:%d,size:%d)' \ - % (self._fname, self._epoch, self.size()) - - def next(self): - """ load next sample - """ - if self._epoch < 0: - self.reset() - - _pos = np.random.choice( - self._samples, 1, replace=False, p=self._img_weights)[0] - sample = copy.deepcopy(self._roidb[_pos]) - - if self._load_img: - sample['image'] = self._load_image(sample['im_file']) - else: - sample['im_file'] = os.path.join(self._image_dir, sample['im_file']) - - return sample - - def _calc_img_weights(self): - """ calculate the probabilities of each sample - """ - imgs_cls = [] - num_per_cls = {} - img_weights = [] - for i, roidb in enumerate(self._roidb): - img_cls = set( - [k for cls in self._roidb[i]['gt_class'] for k in cls]) - imgs_cls.append(img_cls) - for c in img_cls: - if c not in num_per_cls: - num_per_cls[c] = 1 - else: - num_per_cls[c] += 1 - - for i in range(len(self._roidb)): - weights = 0 - for c in imgs_cls[i]: - weights += 1 / num_per_cls[c] - img_weights.append(weights) - # Probabilities sum to 1 - img_weights = img_weights / np.sum(img_weights) - return img_weights - - def reset(self): - """ implementation of Dataset.reset - """ - if self._roidb is None: - self._roidb = self._load() - - if self._img_weights is None: - self._img_weights = self._calc_img_weights() - - self._samples = len(self._roidb) - - if self._epoch < 0: - self._epoch = 0 diff --git a/ppdet/data/source/coco.py b/ppdet/data/source/coco.py new file mode 100644 index 000000000..22a0d7e3a --- /dev/null +++ b/ppdet/data/source/coco.py @@ -0,0 +1,159 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import numpy as np +from pycocotools.coco import COCO + +from .dataset import DataSet +from ppdet.core.workspace import register, serializable + +import logging +logger = logging.getLogger(__name__) + + +@register +@serializable +class COCODataSet(DataSet): + """ + Load COCO records with annotations in json file 'anno_path' + + Args: + dataset_dir (str): root directory for dataset. + image_dir (str): directory for images. + anno_path (str): json file path. + sample_num (int): number of samples to load, -1 means all. + with_background (bool): whether load background as a class. + if True, total class number will be 81. default True. + """ + + def __init__(self, + image_dir=None, + anno_path=None, + dataset_dir=None, + sample_num=-1, + with_background=True): + super(COCODataSet, self).__init__( + image_dir=image_dir, + anno_path=anno_path, + dataset_dir=dataset_dir, + sample_num=sample_num, + with_background=with_background) + self.anno_path = anno_path + self.sample_num = sample_num + self.with_background = with_background + # `roidbs` is list of dict whose structure is: + # { + # 'im_file': im_fname, # image file name + # 'im_id': img_id, # image id + # 'h': im_h, # height of image + # 'w': im_w, # width + # 'is_crowd': is_crowd, + # 'gt_score': gt_score, + # 'gt_class': gt_class, + # 'gt_bbox': gt_bbox, + # 'gt_poly': gt_poly, + # } + self.roidbs = None + # a dict used to map category name to class id + self.cname2cid = None + + def load_roidb_and_cname2cid(self): + anno_path = os.path.join(self.dataset_dir, self.anno_path) + image_dir = os.path.join(self.dataset_dir, self.image_dir) + + assert anno_path.endswith('.json'), \ + 'invalid coco annotation file: ' + anno_path + coco = COCO(anno_path) + img_ids = coco.getImgIds() + cat_ids = coco.getCatIds() + records = [] + ct = 0 + + # when with_background = True, mapping category to classid, like: + # background:0, first_class:1, second_class:2, ... + catid2clsid = dict({ + catid: i + int(self.with_background) + for i, catid in enumerate(cat_ids) + }) + cname2cid = dict({ + coco.loadCats(catid)[0]['name']: clsid + for catid, clsid in catid2clsid.items() + }) + + for img_id in img_ids: + img_anno = coco.loadImgs(img_id)[0] + im_fname = img_anno['file_name'] + im_w = float(img_anno['width']) + im_h = float(img_anno['height']) + + ins_anno_ids = coco.getAnnIds(imgIds=img_id, iscrowd=False) + instances = coco.loadAnns(ins_anno_ids) + + bboxes = [] + for inst in instances: + x, y, box_w, box_h = inst['bbox'] + x1 = max(0, x) + y1 = max(0, y) + x2 = min(im_w - 1, x1 + max(0, box_w - 1)) + y2 = min(im_h - 1, y1 + max(0, box_h - 1)) + if inst['area'] > 0 and x2 >= x1 and y2 >= y1: + inst['clean_bbox'] = [x1, y1, x2, y2] + bboxes.append(inst) + else: + logger.warn( + 'Found an invalid bbox in annotations: im_id: {}, ' + 'area: {} x1: {}, y1: {}, x2: {}, y2: {}.'.format( + img_id, float(inst['area']), x1, y1, x2, y2)) + num_bbox = len(bboxes) + + gt_bbox = np.zeros((num_bbox, 4), dtype=np.float32) + gt_class = np.zeros((num_bbox, 1), dtype=np.int32) + gt_score = np.ones((num_bbox, 1), dtype=np.float32) + is_crowd = np.zeros((num_bbox, 1), dtype=np.int32) + difficult = np.zeros((num_bbox, 1), dtype=np.int32) + gt_poly = [None] * num_bbox + + for i, box in enumerate(bboxes): + catid = box['category_id'] + gt_class[i][0] = catid2clsid[catid] + gt_bbox[i, :] = box['clean_bbox'] + is_crowd[i][0] = box['iscrowd'] + if 'segmentation' in box: + gt_poly[i] = box['segmentation'] + + im_fname = os.path.join(image_dir, + im_fname) if image_dir else im_fname + coco_rec = { + 'im_file': im_fname, + 'im_id': np.array([img_id]), + 'h': im_h, + 'w': im_w, + 'is_crowd': is_crowd, + 'gt_class': gt_class, + 'gt_bbox': gt_bbox, + 'gt_score': gt_score, + 'gt_poly': gt_poly, + 'difficult': difficult + } + + logger.debug('Load file: {}, im_id: {}, h: {}, w: {}.'.format( + im_fname, img_id, im_h, im_w)) + records.append(coco_rec) + ct += 1 + if self.sample_num > 0 and ct >= self.sample_num: + break + assert len(records) > 0, 'not found any coco record in %s' % (anno_path) + logger.info('{} samples in file {}'.format(ct, anno_path)) + self.roidbs, self.cname2cid = records, cname2cid diff --git a/ppdet/data/source/coco_loader.py b/ppdet/data/source/coco_loader.py deleted file mode 100644 index db1849890..000000000 --- a/ppdet/data/source/coco_loader.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -from pycocotools.coco import COCO - -import logging -logger = logging.getLogger(__name__) - - -def load(anno_path, sample_num=-1, with_background=True): - """ - Load COCO records with annotations in json file 'anno_path' - - Args: - anno_path (str): json file path - sample_num (int): number of samples to load, -1 means all - with_background (bool): whether load background as a class. - if True, total class number will - be 81. default True - - Returns: - (records, cname2cid) - 'records' is list of dict whose structure is: - { - 'im_file': im_fname, # image file name - 'im_id': img_id, # image id - 'h': im_h, # height of image - 'w': im_w, # width - 'is_crowd': is_crowd, - 'gt_score': gt_score, - 'gt_class': gt_class, - 'gt_bbox': gt_bbox, - 'gt_poly': gt_poly, - } - 'cname2cid' is a dict used to map category name to class id - """ - assert anno_path.endswith('.json'), 'invalid coco annotation file: ' \ - + anno_path - coco = COCO(anno_path) - img_ids = coco.getImgIds() - cat_ids = coco.getCatIds() - records = [] - ct = 0 - - # when with_background = True, mapping category to classid, like: - # background:0, first_class:1, second_class:2, ... - catid2clsid = dict( - {catid: i + int(with_background) - for i, catid in enumerate(cat_ids)}) - cname2cid = dict({ - coco.loadCats(catid)[0]['name']: clsid - for catid, clsid in catid2clsid.items() - }) - - for img_id in img_ids: - img_anno = coco.loadImgs(img_id)[0] - im_fname = img_anno['file_name'] - im_w = float(img_anno['width']) - im_h = float(img_anno['height']) - - ins_anno_ids = coco.getAnnIds(imgIds=img_id, iscrowd=False) - instances = coco.loadAnns(ins_anno_ids) - - bboxes = [] - for inst in instances: - x, y, box_w, box_h = inst['bbox'] - x1 = max(0, x) - y1 = max(0, y) - x2 = min(im_w - 1, x1 + max(0, box_w - 1)) - y2 = min(im_h - 1, y1 + max(0, box_h - 1)) - if inst['area'] > 0 and x2 >= x1 and y2 >= y1: - inst['clean_bbox'] = [x1, y1, x2, y2] - bboxes.append(inst) - else: - logger.warn( - 'Found an invalid bbox in annotations: im_id: {}, area: {} x1: {}, y1: {}, x2: {}, y2: {}.'. - format(img_id, float(inst['area']), x1, y1, x2, y2)) - num_bbox = len(bboxes) - - gt_bbox = np.zeros((num_bbox, 4), dtype=np.float32) - gt_class = np.zeros((num_bbox, 1), dtype=np.int32) - gt_score = np.ones((num_bbox, 1), dtype=np.float32) - is_crowd = np.zeros((num_bbox, 1), dtype=np.int32) - difficult = np.zeros((num_bbox, 1), dtype=np.int32) - gt_poly = [None] * num_bbox - - for i, box in enumerate(bboxes): - catid = box['category_id'] - gt_class[i][0] = catid2clsid[catid] - gt_bbox[i, :] = box['clean_bbox'] - is_crowd[i][0] = box['iscrowd'] - if 'segmentation' in box: - gt_poly[i] = box['segmentation'] - - coco_rec = { - 'im_file': im_fname, - 'im_id': np.array([img_id]), - 'h': im_h, - 'w': im_w, - 'is_crowd': is_crowd, - 'gt_class': gt_class, - 'gt_bbox': gt_bbox, - 'gt_score': gt_score, - 'gt_poly': gt_poly, - 'difficult': difficult - } - - logger.debug('Load file: {}, im_id: {}, h: {}, w: {}.'.format( - im_fname, img_id, im_h, im_w)) - records.append(coco_rec) - ct += 1 - if sample_num > 0 and ct >= sample_num: - break - assert len(records) > 0, 'not found any coco record in %s' % (anno_path) - logger.info('{} samples in file {}'.format(ct, anno_path)) - return records, cname2cid diff --git a/ppdet/data/source/dataset.py b/ppdet/data/source/dataset.py new file mode 100644 index 000000000..9f604fe9a --- /dev/null +++ b/ppdet/data/source/dataset.py @@ -0,0 +1,166 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import numpy as np + +try: + from collections.abc import Sequence +except Exception: + from collections import Sequence + +from ppdet.core.workspace import register, serializable +from ppdet.utils.download import get_dataset_path + + +@serializable +class DataSet(object): + """ + Dataset, e.g., coco, pascal voc + + Args: + annotation (str): annotation file path + image_dir (str): directory where image files are stored + shuffle (bool): shuffle samples + """ + + def __init__(self, + dataset_dir=None, + image_dir=None, + anno_path=None, + sample_num=-1, + with_background=True, + use_default_label=None, + **kwargs): + super(DataSet, self).__init__() + self.anno_path = anno_path + self.image_dir = image_dir if image_dir is not None else '' + self.dataset_dir = dataset_dir if dataset_dir is not None else '' + self.sample_num = sample_num + self.with_background = with_background + self.use_default_label = use_default_label + + self.cname2cid = None + self._imid2path = None + + def load_roidb_and_cname2cid(self): + """load dataset""" + raise NotImplementedError('%s.load_roidb_and_cname2cid not available' % + (self.__class__.__name__)) + + def get_roidb(self): + if not self.roidbs: + data_dir = get_dataset_path(self.dataset_dir, self.anno_path, + self.image_dir) + if data_dir: + self.dataset_dir = data_dir + self.load_roidb_and_cname2cid() + + return self.roidbs + + def get_cname2cid(self): + if not self.cname2cid: + self.load_roidb_and_cname2cid() + return self.cname2cid + + def get_anno(self): + return os.path.join(self.dataset_dir, self.anno_path) + + def get_imid2path(self): + return self._imid2path + + def get_cname2cid(self): + return self.cname2cid + + +def _is_valid_file(f, extensions=('.jpg', '.jpeg', '.png', '.bmp')): + return f.lower().endswith(extensions) + + +def _make_dataset(dir): + dir = os.path.expanduser(dir) + if not os.path.isdir(d): + raise ('{} should be a dir'.format(dir)) + images = [] + for root, _, fnames in sorted(os.walk(dir, followlinks=True)): + for fname in sorted(fnames): + path = os.path.join(root, fname) + if is_valid_file(path): + images.append(path) + return images + + +@register +@serializable +class ImageFolder(DataSet): + """ + Args: + dataset_dir (str): root directory for dataset. + image_dir(list|str): list of image folders or list of image files + anno_path (str): annotation file path. + samples (int): number of samples to load, -1 means all + """ + + def __init__(self, + dataset_dir=None, + image_dir=None, + anno_path=None, + sample_num=-1, + with_background=True, + **kwargs): + super(ImageFolder, self).__init__(image_dir, anno_path, sample_num, + dataset_dir, with_background) + self.anno_path = anno_path + self.sample_num = sample_num + self.with_background = with_background + self.roidbs = None + self._imid2path = {} + + def get_roidb(self): + if not self.roidbs: + self.roidbs = self._load_images() + return self.roidbs + + def set_images(self, images): + self.image_dir = images + self.roidbs = self._load_images() + + def _parse(self): + image_dir = self.image_dir + if not isinstance(image_dir, Sequence): + image_dir = [image_dir] + images = [] + for im_dir in image_dir: + if os.path.isdir(im_dir): + im_dir = os.path.join(self.dataset_dir, im_dir) + images.extend(_make_dataset(im_dir)) + elif os.path.isfile(im_dir) and _is_valid_file(im_dir): + images.append(im_dir) + return images + + def _load_images(self): + images = self._parse() + ct = 0 + records = [] + for image in images: + assert image != '' and os.path.isfile(image), \ + "Image {} not found".format(image) + if self.sample_num > 0 and ct >= self.sample_num: + break + rec = {'im_id': np.array([ct]), 'im_file': image} + self._imid2path[ct] = image + ct += 1 + records.append(rec) + assert len(records) > 0, "No image file found" + return records diff --git a/ppdet/data/source/iterator_source.py b/ppdet/data/source/iterator_source.py deleted file mode 100644 index ef9edc004..000000000 --- a/ppdet/data/source/iterator_source.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import numpy as np -import copy - -import logging -logger = logging.getLogger(__name__) - -from ..dataset import Dataset - - -class IteratorSource(Dataset): - """ - Load data samples from iterator in stream mode - - Args: - iter_maker (callable): callable function to generate a iter - samples (int): number of samples to load, -1 means all - """ - - def __init__(self, iter_maker, samples=-1, **kwargs): - super(IteratorSource, self).__init__() - self._epoch = -1 - - self._iter_maker = iter_maker - self._data_iter = None - self._pos = -1 - self._drained = False - self._samples = samples - self._sample_num = -1 - - def next(self): - if self._epoch < 0: - self.reset() - - if self._data_iter is not None: - try: - sample = next(self._data_iter) - self._pos += 1 - ret = sample - except StopIteration as e: - if self._sample_num <= 0: - self._sample_num = self._pos - elif self._sample_num != self._pos: - logger.info('num of loaded samples is different ' - 'with previouse setting[prev:%d,now:%d]' % - (self._sample_num, self._pos)) - self._sample_num = self._pos - - self._data_iter = None - self._drained = True - raise e - else: - raise StopIteration("no more data in " + str(self)) - - if self._samples > 0 and self._pos >= self._samples: - self._data_iter = None - self._drained = True - raise StopIteration("no more data in " + str(self)) - else: - return ret - - def reset(self): - if self._data_iter is None: - self._data_iter = self._iter_maker() - - if self._epoch < 0: - self._epoch = 0 - else: - self._epoch += 1 - - self._pos = 0 - self._drained = False - - def size(self): - return self._sample_num - - def drained(self): - assert self._epoch >= 0, "the first epoch has not started yet" - return self._pos >= self.size() - - def epoch_id(self): - return self._epoch diff --git a/ppdet/data/source/loader.py b/ppdet/data/source/loader.py deleted file mode 100644 index 86009e760..000000000 --- a/ppdet/data/source/loader.py +++ /dev/null @@ -1,145 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# function: -# load data records from local files(maybe in COCO or VOC data formats) - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import os - -import numpy as np -import logging -import pickle as pkl - -logger = logging.getLogger(__name__) - - -def check_records(records): - """ check the fields of 'records' must contains some keys - """ - needed_fields = [ - 'im_file', 'im_id', 'h', 'w', 'is_crowd', 'gt_class', 'gt_bbox', - 'gt_poly' - ] - - for i, rec in enumerate(records): - for k in needed_fields: - assert k in rec, 'not found field[%s] in record[%d]' % (k, i) - - -def load_roidb(anno_file, sample_num=-1): - """ load normalized data records from file - 'anno_file' which is a pickled file. - And the records should has a structure: - { - 'im_file': str, # image file name - 'im_id': int, # image id - 'h': int, # height of image - 'w': int, # width of image - 'is_crowd': bool, - 'gt_class': list of np.ndarray, # classids info - 'gt_bbox': list of np.ndarray, # bounding box info - 'gt_poly': list of int, # poly info - } - - Args: - anno_file (str): file name for picked records - sample_num (int): number of samples to load - - Returns: - list of records for detection model training - """ - - assert anno_file.endswith('.roidb'), 'invalid roidb file[%s]' % (anno_file) - with open(anno_file, 'rb') as f: - roidb = f.read() - # for support python3 and python2 - try: - records, cname2cid = pkl.loads(roidb, encoding='bytes') - except: - records, cname2cid = pkl.loads(roidb) - - assert type(records) is list, 'invalid data type from roidb' - - if sample_num > 0 and sample_num < len(records): - records = records[:sample_num] - - return records, cname2cid - - -def load(fname, - samples=-1, - with_background=True, - with_cat2id=False, - use_default_label=None, - cname2cid=None): - """ Load data records from 'fnames' - - Args: - fnames (str): file name for data record, eg: - instances_val2017.json or COCO17_val2017.roidb - samples (int): number of samples to load, default to all - with_background (bool): whether load background as a class. - default True. - with_cat2id (bool): whether return cname2cid info out - use_default_label (bool): whether use the default mapping of label to id - cname2cid (dict): the mapping of category name to id - - Returns: - list of loaded records whose structure is: - { - 'im_file': str, # image file name - 'im_id': int, # image id - 'h': int, # height of image - 'w': int, # width of image - 'is_crowd': bool, - 'gt_class': list of np.ndarray, # classids info - 'gt_bbox': list of np.ndarray, # bounding box info - 'gt_poly': list of int, # poly info - } - - """ - - if fname.endswith('.roidb'): - records, cname2cid = load_roidb(fname, samples) - elif fname.endswith('.json'): - from . import coco_loader - records, cname2cid = coco_loader.load(fname, samples, with_background) - elif "wider_face" in fname: - from . import widerface_loader - records = widerface_loader.load(fname, samples) - return records - elif os.path.isfile(fname): - from . import voc_loader - if use_default_label is None or cname2cid is not None: - records, cname2cid = voc_loader.get_roidb( - fname, samples, cname2cid, with_background=with_background) - else: - records, cname2cid = voc_loader.load( - fname, - samples, - use_default_label, - with_background=with_background) - else: - raise ValueError('invalid file type when load data from file[%s]' % - (fname)) - check_records(records) - if with_cat2id: - return records, cname2cid - else: - return records diff --git a/ppdet/data/source/roidb_source.py b/ppdet/data/source/roidb_source.py deleted file mode 100644 index 44317974a..000000000 --- a/ppdet/data/source/roidb_source.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#function: -# interface to load data from local files and parse it for samples, -# eg: roidb data in pickled files - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import os -import random - -import copy -import pickle as pkl -from ..dataset import Dataset - -import logging -logger = logging.getLogger(__name__) - - -class RoiDbSource(Dataset): - """ interface to load roidb data from files - """ - - def __init__(self, - anno_file, - image_dir=None, - samples=-1, - is_shuffle=True, - load_img=False, - cname2cid=None, - use_default_label=None, - mixup_epoch=-1, - with_background=True): - """ Init - - Args: - fname (str): label file path - image_dir (str): root dir for images - samples (int): samples to load, -1 means all - is_shuffle (bool): whether to shuffle samples - load_img (bool): whether load data in this class - cname2cid (dict): the label name to id dictionary - use_default_label (bool):whether use the default mapping of label to id - mixup_epoch (int): parse mixup in first n epoch - with_background (bool): whether load background - as a class - """ - super(RoiDbSource, self).__init__() - self._epoch = -1 - assert os.path.isfile(anno_file) or os.path.isdir(anno_file), \ - 'anno_file {} is not a file or a directory'.format(anno_file) - self._fname = anno_file - self._image_dir = image_dir if image_dir is not None else '' - if image_dir is not None: - assert os.path.isdir(image_dir), \ - 'image_dir {} is not a directory'.format(image_dir) - self._roidb = None - self._pos = -1 - self._drained = False - self._samples = samples - self._is_shuffle = is_shuffle - self._load_img = load_img - self.use_default_label = use_default_label - self._mixup_epoch = mixup_epoch - self._with_background = with_background - self.cname2cid = cname2cid - self._imid2path = None - - def __str__(self): - return 'RoiDbSource(epoch:%d,size:%d,pos:%d,fname:%s)' \ - % (self._epoch, self.size(), self._pos, self._fname) - - def next(self): - """ load next sample - """ - if self._epoch < 0: - self.reset() - if self._pos >= self._samples: - self._drained = True - raise StopIteration('%s no more data' % (str(self))) - sample = copy.deepcopy(self._roidb[self._pos]) - if self._load_img: - sample['image'] = self._load_image(sample['im_file']) - else: - sample['im_file'] = os.path.join(self._image_dir, sample['im_file']) - - if self._epoch < self._mixup_epoch: - mix_idx = random.randint(1, self._samples - 1) - mix_pos = (mix_idx + self._pos) % self._samples - sample['mixup'] = copy.deepcopy(self._roidb[mix_pos]) - if self._load_img: - sample['mixup']['image'] = \ - self._load_image(sample['mixup']['im_file']) - else: - sample['mixup']['im_file'] = \ - os.path.join(self._image_dir, sample['mixup']['im_file']) - self._pos += 1 - return sample - - def _load(self): - """ load data from file - """ - from . import loader - records, cname2cid = loader.load(self._fname, self._samples, - self._with_background, True, - self.use_default_label, self.cname2cid) - self.cname2cid = cname2cid - return records - - def _load_image(self, where): - fn = os.path.join(self._image_dir, where) - with open(fn, 'rb') as f: - return f.read() - - def reset(self): - """ implementation of Dataset.reset - """ - if self._roidb is None: - self._roidb = self._load() - - self._samples = len(self._roidb) - if self._is_shuffle: - random.shuffle(self._roidb) - - if self._mixup_epoch > 0 and self._samples < 2: - logger.info("Disable mixup for dataset samples " - "less than 2 samples") - self._mixup_epoch = -1 - - if self._epoch < 0: - self._epoch = 0 - else: - self._epoch += 1 - - self._pos = 0 - self._drained = False - - def size(self): - """ implementation of Dataset.size - """ - return len(self._roidb) - - def drained(self): - """ implementation of Dataset.drained - """ - assert self._epoch >= 0, 'The first epoch has not begin!' - return self._pos >= self.size() - - def epoch_id(self): - """ return epoch id for latest sample - """ - return self._epoch - - def get_imid2path(self): - """return image id to image path map""" - if self._imid2path is None: - self._imid2path = {} - for record in self._roidb: - im_id = record['im_id'] - im_id = im_id if isinstance(im_id, int) else im_id[0] - im_path = os.path.join(self._image_dir, record['im_file']) - self._imid2path[im_id] = im_path - return self._imid2path diff --git a/ppdet/data/source/simple_source.py b/ppdet/data/source/simple_source.py deleted file mode 100644 index f76f172d7..000000000 --- a/ppdet/data/source/simple_source.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# function: -# interface to load data from txt file. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import numpy as np -import copy -from ..dataset import Dataset - - -class SimpleSource(Dataset): - """ - Load image files for testing purpose - - Args: - images (list): list of path of images - samples (int): number of samples to load, -1 means all - load_img (bool): should images be loaded - """ - - def __init__(self, images=[], samples=-1, load_img=True, **kwargs): - super(SimpleSource, self).__init__() - self._epoch = -1 - for image in images: - assert image != '' and os.path.isfile(image), \ - "Image {} not found".format(image) - self._images = images - self._fname = None - self._simple = None - self._pos = -1 - self._drained = False - self._samples = samples - self._load_img = load_img - self._imid2path = {} - - def next(self): - if self._epoch < 0: - self.reset() - - if self._pos >= self.size(): - self._drained = True - raise StopIteration("no more data in " + str(self)) - else: - sample = copy.deepcopy(self._simple[self._pos]) - if self._load_img: - sample['image'] = self._load_image(sample['im_file']) - - self._pos += 1 - return sample - - def _load(self): - ct = 0 - records = [] - for image in self._images: - if self._samples > 0 and ct >= self._samples: - break - rec = {'im_id': np.array([ct]), 'im_file': image} - self._imid2path[ct] = image - ct += 1 - records.append(rec) - assert len(records) > 0, "no image file found" - return records - - def _load_image(self, where): - with open(where, 'rb') as f: - return f.read() - - def reset(self): - if self._simple is None: - self._simple = self._load() - - if self._epoch < 0: - self._epoch = 0 - else: - self._epoch += 1 - - self._pos = 0 - self._drained = False - - def size(self): - return len(self._simple) - - def drained(self): - assert self._epoch >= 0, "the first epoch has not started yet" - return self._pos >= self.size() - - def epoch_id(self): - return self._epoch - - def get_imid2path(self): - """return image id to image path map""" - return self._imid2path diff --git a/ppdet/data/source/voc.py b/ppdet/data/source/voc.py new file mode 100644 index 000000000..4b12751f7 --- /dev/null +++ b/ppdet/data/source/voc.py @@ -0,0 +1,188 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import numpy as np + +import xml.etree.ElementTree as ET + +from ppdet.core.workspace import register, serializable + +from .dataset import DataSet + + +@register +@serializable +class VOCDataSet(DataSet): + """ + Load dataset with PascalVOC format. + + Notes: + `anno_path` must contains xml file and image file path for annotations. + + Args: + dataset_dir (str): root directory for dataset. + image_dir (str): directory for images. + anno_path (str): voc annotation file path. + sample_num (int): number of samples to load, -1 means all. + use_default_label (bool): whether use the default mapping of + label to integer index. Default True. + with_background (bool): whether load background as a class, + default True. + label_list (str): if use_default_label is False, will load + mapping between category and class index. + """ + + def __init__(self, + dataset_dir=None, + image_dir=None, + anno_path=None, + sample_num=-1, + use_default_label=True, + with_background=True, + label_list='label_list.txt'): + super(VOCDataSet, self).__init__( + image_dir=image_dir, + anno_path=anno_path, + sample_num=sample_num, + dataset_dir=dataset_dir, + with_background=with_background) + # roidbs is list of dict whose structure is: + # { + # 'im_file': im_fname, # image file name + # 'im_id': im_id, # image id + # 'h': im_h, # height of image + # 'w': im_w, # width + # 'is_crowd': is_crowd, + # 'gt_class': gt_class, + # 'gt_bbox': gt_bbox, + # 'gt_poly': gt_poly, + # } + self.roidbs = None + # 'cname2id' is a dict to map category name to class id + self.cname2cid = None + self.label_list = label_list + + def load_roidb_and_cname2cid(self): + anno_path = os.path.join(self.dataset_dir, self.anno_path) + image_dir = os.path.join(self.dataset_dir, self.image_dir) + + # mapping category name to class id + # if with_background is True: + # background:0, first_class:1, second_class:2, ... + # if with_background is False: + # first_class:0, second_class:1, ... + records = [] + ct = 0 + cname2cid = {} + if not self.use_default_label: + label_path = os.path.join(self.dataset_dir, self.label_list) + if not os.path.exists(label_path): + raise ValueError("label_list {} does not exists".format( + label_path)) + with open(label_path, 'r') as fr: + label_id = int(self.with_background) + for line in fr.readlines(): + cname2cid[line.strip()] = label_id + label_id += 1 + else: + cname2cid = pascalvoc_label(self.with_background) + + with open(anno_path, 'r') as fr: + while True: + line = fr.readline() + if not line: + break + img_file, xml_file = [os.path.join(image_dir, x) \ + for x in line.strip().split()[:2]] + if not os.path.isfile(xml_file): + continue + tree = ET.parse(xml_file) + if tree.find('id') is None: + im_id = np.array([ct]) + else: + im_id = np.array([int(tree.find('id').text)]) + + objs = tree.findall('object') + im_w = float(tree.find('size').find('width').text) + im_h = float(tree.find('size').find('height').text) + gt_bbox = np.zeros((len(objs), 4), dtype=np.float32) + gt_class = np.zeros((len(objs), 1), dtype=np.int32) + gt_score = np.ones((len(objs), 1), dtype=np.float32) + is_crowd = np.zeros((len(objs), 1), dtype=np.int32) + difficult = np.zeros((len(objs), 1), dtype=np.int32) + for i, obj in enumerate(objs): + cname = obj.find('name').text + gt_class[i][0] = cname2cid[cname] + _difficult = int(obj.find('difficult').text) + x1 = float(obj.find('bndbox').find('xmin').text) + y1 = float(obj.find('bndbox').find('ymin').text) + x2 = float(obj.find('bndbox').find('xmax').text) + y2 = float(obj.find('bndbox').find('ymax').text) + x1 = max(0, x1) + y1 = max(0, y1) + x2 = min(im_w - 1, x2) + y2 = min(im_h - 1, y2) + gt_bbox[i] = [x1, y1, x2, y2] + is_crowd[i][0] = 0 + difficult[i][0] = _difficult + voc_rec = { + 'im_file': img_file, + 'im_id': im_id, + 'h': im_h, + 'w': im_w, + 'is_crowd': is_crowd, + 'gt_class': gt_class, + 'gt_score': gt_score, + 'gt_bbox': gt_bbox, + 'gt_poly': [], + 'difficult': difficult + } + if len(objs) != 0: + records.append(voc_rec) + + ct += 1 + if self.sample_num > 0 and ct >= self.sample_num: + break + assert len(records) > 0, 'not found any voc record in %s' % ( + self.anno_path) + self.roidbs, self.cname2cid = records, cname2cid + + +def pascalvoc_label(with_background=True): + labels_map = { + 'aeroplane': 1, + 'bicycle': 2, + 'bird': 3, + 'boat': 4, + 'bottle': 5, + 'bus': 6, + 'car': 7, + 'cat': 8, + 'chair': 9, + 'cow': 10, + 'diningtable': 11, + 'dog': 12, + 'horse': 13, + 'motorbike': 14, + 'person': 15, + 'pottedplant': 16, + 'sheep': 17, + 'sofa': 18, + 'train': 19, + 'tvmonitor': 20 + } + if not with_background: + labels_map = {k: v - 1 for k, v in labels_map.items()} + return labels_map diff --git a/ppdet/data/source/voc_loader.py b/ppdet/data/source/voc_loader.py deleted file mode 100644 index d474ce488..000000000 --- a/ppdet/data/source/voc_loader.py +++ /dev/null @@ -1,267 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import numpy as np - -import xml.etree.ElementTree as ET - - -def get_roidb(anno_path, sample_num=-1, cname2cid=None, with_background=True): - """ - Load VOC records with annotations in xml directory 'anno_path' - - Notes: - ${anno_path} must contains xml file and image file path for annotations - - Args: - anno_path (str): root directory for voc annotation data - sample_num (int): number of samples to load, -1 means all - cname2cid (dict): the label name to id dictionary - with_background (bool): whether load background as a class. - if True, total class number will - be 81. default True - - Returns: - (records, catname2clsid) - 'records' is list of dict whose structure is: - { - 'im_file': im_fname, # image file name - 'im_id': im_id, # image id - 'h': im_h, # height of image - 'w': im_w, # width - 'is_crowd': is_crowd, - 'gt_class': gt_class, - 'gt_bbox': gt_bbox, - 'gt_poly': gt_poly, - } - 'cname2id' is a dict to map category name to class id - """ - - data_dir = os.path.dirname(anno_path) - - records = [] - ct = 0 - existence = False if cname2cid is None else True - if cname2cid is None: - cname2cid = {} - - # mapping category name to class id - # background:0, first_class:1, second_class:2, ... - with open(anno_path, 'r') as fr: - while True: - line = fr.readline() - if not line: - break - img_file, xml_file = [os.path.join(data_dir, x) \ - for x in line.strip().split()[:2]] - if not os.path.isfile(xml_file): - continue - tree = ET.parse(xml_file) - if tree.find('id') is None: - im_id = np.array([ct]) - else: - im_id = np.array([int(tree.find('id').text)]) - - objs = tree.findall('object') - im_w = float(tree.find('size').find('width').text) - im_h = float(tree.find('size').find('height').text) - gt_bbox = np.zeros((len(objs), 4), dtype=np.float32) - gt_class = np.zeros((len(objs), 1), dtype=np.int32) - gt_score = np.ones((len(objs), 1), dtype=np.float32) - is_crowd = np.zeros((len(objs), 1), dtype=np.int32) - difficult = np.zeros((len(objs), 1), dtype=np.int32) - for i, obj in enumerate(objs): - cname = obj.find('name').text - if not existence and cname not in cname2cid: - # the background's id is 0, so need to add 1. - cname2cid[cname] = len(cname2cid) + int(with_background) - elif existence and cname not in cname2cid: - raise KeyError( - 'Not found cname[%s] in cname2cid when map it to cid.' % - (cname)) - gt_class[i][0] = cname2cid[cname] - _difficult = int(obj.find('difficult').text) - x1 = float(obj.find('bndbox').find('xmin').text) - y1 = float(obj.find('bndbox').find('ymin').text) - x2 = float(obj.find('bndbox').find('xmax').text) - y2 = float(obj.find('bndbox').find('ymax').text) - x1 = max(0, x1) - y1 = max(0, y1) - x2 = min(im_w - 1, x2) - y2 = min(im_h - 1, y2) - gt_bbox[i] = [x1, y1, x2, y2] - is_crowd[i][0] = 0 - difficult[i][0] = _difficult - voc_rec = { - 'im_file': img_file, - 'im_id': im_id, - 'h': im_h, - 'w': im_w, - 'is_crowd': is_crowd, - 'gt_class': gt_class, - 'gt_score': gt_score, - 'gt_bbox': gt_bbox, - 'gt_poly': [], - 'difficult': difficult - } - if len(objs) != 0: - records.append(voc_rec) - - ct += 1 - if sample_num > 0 and ct >= sample_num: - break - assert len(records) > 0, 'not found any voc record in %s' % (anno_path) - return [records, cname2cid] - - -def load(anno_path, sample_num=-1, use_default_label=True, - with_background=True): - """ - Load VOC records with annotations in - xml directory 'anno_path' - - Notes: - ${anno_path} must contains xml file and image file path for annotations - - Args: - @anno_path (str): root directory for voc annotation data - @sample_num (int): number of samples to load, -1 means all - @use_default_label (bool): whether use the default mapping of label to id - @with_background (bool): whether load background as a class. - if True, total class number will - be 81. default True - - Returns: - (records, catname2clsid) - 'records' is list of dict whose structure is: - { - 'im_file': im_fname, # image file name - 'im_id': im_id, # image id - 'h': im_h, # height of image - 'w': im_w, # width - 'is_crowd': is_crowd, - 'gt_class': gt_class, - 'gt_bbox': gt_bbox, - 'gt_poly': gt_poly, - } - 'cname2id' is a dict to map category name to class id - """ - - data_dir = os.path.dirname(anno_path) - - # mapping category name to class id - # if with_background is True: - # background:0, first_class:1, second_class:2, ... - # if with_background is False: - # first_class:0, second_class:1, ... - records = [] - ct = 0 - cname2cid = {} - if not use_default_label: - label_path = os.path.join(data_dir, 'label_list.txt') - with open(label_path, 'r') as fr: - label_id = int(with_background) - for line in fr.readlines(): - cname2cid[line.strip()] = label_id - label_id += 1 - else: - cname2cid = pascalvoc_label(with_background) - - with open(anno_path, 'r') as fr: - while True: - line = fr.readline() - if not line: - break - img_file, xml_file = [os.path.join(data_dir, x) \ - for x in line.strip().split()[:2]] - if not os.path.isfile(xml_file): - continue - tree = ET.parse(xml_file) - if tree.find('id') is None: - im_id = np.array([ct]) - else: - im_id = np.array([int(tree.find('id').text)]) - - objs = tree.findall('object') - im_w = float(tree.find('size').find('width').text) - im_h = float(tree.find('size').find('height').text) - gt_bbox = np.zeros((len(objs), 4), dtype=np.float32) - gt_class = np.zeros((len(objs), 1), dtype=np.int32) - gt_score = np.ones((len(objs), 1), dtype=np.float32) - is_crowd = np.zeros((len(objs), 1), dtype=np.int32) - difficult = np.zeros((len(objs), 1), dtype=np.int32) - for i, obj in enumerate(objs): - cname = obj.find('name').text - gt_class[i][0] = cname2cid[cname] - _difficult = int(obj.find('difficult').text) - x1 = float(obj.find('bndbox').find('xmin').text) - y1 = float(obj.find('bndbox').find('ymin').text) - x2 = float(obj.find('bndbox').find('xmax').text) - y2 = float(obj.find('bndbox').find('ymax').text) - x1 = max(0, x1) - y1 = max(0, y1) - x2 = min(im_w - 1, x2) - y2 = min(im_h - 1, y2) - gt_bbox[i] = [x1, y1, x2, y2] - is_crowd[i][0] = 0 - difficult[i][0] = _difficult - voc_rec = { - 'im_file': img_file, - 'im_id': im_id, - 'h': im_h, - 'w': im_w, - 'is_crowd': is_crowd, - 'gt_class': gt_class, - 'gt_score': gt_score, - 'gt_bbox': gt_bbox, - 'gt_poly': [], - 'difficult': difficult - } - if len(objs) != 0: - records.append(voc_rec) - - ct += 1 - if sample_num > 0 and ct >= sample_num: - break - assert len(records) > 0, 'not found any voc record in %s' % (anno_path) - return [records, cname2cid] - - -def pascalvoc_label(with_background=True): - labels_map = { - 'aeroplane': 1, - 'bicycle': 2, - 'bird': 3, - 'boat': 4, - 'bottle': 5, - 'bus': 6, - 'car': 7, - 'cat': 8, - 'chair': 9, - 'cow': 10, - 'diningtable': 11, - 'dog': 12, - 'horse': 13, - 'motorbike': 14, - 'person': 15, - 'pottedplant': 16, - 'sheep': 17, - 'sofa': 18, - 'train': 19, - 'tvmonitor': 20 - } - if not with_background: - labels_map = {k: v - 1 for k, v in labels_map.items()} - return labels_map diff --git a/ppdet/data/source/widerface.py b/ppdet/data/source/widerface.py new file mode 100644 index 000000000..bc718a5ca --- /dev/null +++ b/ppdet/data/source/widerface.py @@ -0,0 +1,142 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import numpy as np +import logging +logger = logging.getLogger(__name__) + +from ppdet.core.workspace import register, serializable +from .dataset import DataSet + + +@register +@serializable +class WIDERFaceDataSet(DataSet): + """ + Load WiderFace records with 'anno_path' + + Args: + dataset_dir (str): root directory for dataset. + image_dir (str): directory for images. + anno_path (str): root directory for voc annotation data + sample_num (int): number of samples to load, -1 means all + with_background (bool): whether load background as a class. + if True, total class number will be 2. default True. + """ + + def __init__(self, + dataset_dir=None, + image_dir=None, + anno_path=None, + sample_num=-1, + with_background=True): + super(WIDERFaceDataSet, self).__init__( + image_dir=image_dir, + anno_path=anno_path, + sample_num=sample_num, + dataset_dir=dataset_dir, + with_background=with_background) + self.anno_path = anno_path + self.sample_num = sample_num + self.with_background = with_background + self.roidbs = None + self.cname2cid = None + + def load_roidb_and_cname2cid(self): + anno_path = os.path.join(self.dataset_dir, self.anno_path) + image_dir = os.path.join(self.dataset_dir, self.image_dir) + + txt_file = anno_path + + records = [] + ct = 0 + file_lists = _load_file_list(txt_file) + cname2cid = widerface_label(self.with_background) + + for item in file_lists: + im_fname = item[0] + im_id = np.array([ct]) + gt_bbox = np.zeros((len(item) - 2, 4), dtype=np.float32) + gt_class = np.ones((len(item) - 2, 1), dtype=np.int32) + for index_box in range(len(item)): + if index_box >= 2: + temp_info_box = item[index_box].split(' ') + xmin = float(temp_info_box[0]) + ymin = float(temp_info_box[1]) + w = float(temp_info_box[2]) + h = float(temp_info_box[3]) + # Filter out wrong labels + if w < 0 or h < 0: + continue + xmin = max(0, xmin) + ymin = max(0, ymin) + xmax = xmin + w + ymax = ymin + h + gt_bbox[index_box - 2] = [xmin, ymin, xmax, ymax] + + im_fname = os.path.join(image_dir, + im_fname) if image_dir else im_fname + widerface_rec = { + 'im_file': im_fname, + 'im_id': im_id, + 'gt_bbox': gt_bbox, + 'gt_class': gt_class, + } + # logger.debug + if len(item) != 0: + records.append(widerface_rec) + + ct += 1 + if self.sample_num > 0 and ct >= self.sample_num: + break + assert len(records) > 0, 'not found any widerface in %s' % (anno_path) + logger.info('{} samples in file {}'.format(ct, anno_path)) + self.roidbs, self.cname2cid = records, cname2cid + + +def _load_file_list(input_txt): + with open(input_txt, 'r') as f_dir: + lines_input_txt = f_dir.readlines() + + file_dict = {} + num_class = 0 + for i in range(len(lines_input_txt)): + line_txt = lines_input_txt[i].strip('\n\t\r') + if '.jpg' in line_txt: + if i != 0: + num_class += 1 + file_dict[num_class] = [] + file_dict[num_class].append(line_txt) + if '.jpg' not in line_txt: + if len(line_txt) > 6: + split_str = line_txt.split(' ') + x1_min = float(split_str[0]) + y1_min = float(split_str[1]) + x2_max = float(split_str[2]) + y2_max = float(split_str[3]) + line_txt = str(x1_min) + ' ' + str(y1_min) + ' ' + str( + x2_max) + ' ' + str(y2_max) + file_dict[num_class].append(line_txt) + else: + file_dict[num_class].append(line_txt) + + return list(file_dict.values()) + + +def widerface_label(with_background=True): + labels_map = {'face': 1} + if not with_background: + labels_map = {k: v - 1 for k, v in labels_map.items()} + return labels_map diff --git a/ppdet/data/source/widerface_loader.py b/ppdet/data/source/widerface_loader.py deleted file mode 100644 index 09d323c68..000000000 --- a/ppdet/data/source/widerface_loader.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import numpy as np -import logging -logger = logging.getLogger(__name__) - - -def load(anno_path, sample_num=-1, cname2cid=None, with_background=True): - """ - Load WiderFace records with 'anno_path' - - Args: - anno_path (str): root directory for voc annotation data - sample_num (int): number of samples to load, -1 means all - with_background (bool): whether load background as a class. - if True, total class number will - be 2. default True - - Returns: - (records, catname2clsid) - 'records' is list of dict whose structure is: - { - 'im_file': im_fname, # image file name - 'im_id': im_id, # image id - 'gt_class': gt_class, - 'gt_bbox': gt_bbox, - } - 'cname2id' is a dict to map category name to class id - """ - - txt_file = anno_path - - records = [] - ct = 0 - file_lists = _load_file_list(txt_file) - cname2cid = widerface_label(with_background) - - for item in file_lists: - im_fname = item[0] - im_id = np.array([ct]) - gt_bbox = np.zeros((len(item) - 2, 4), dtype=np.float32) - gt_class = np.ones((len(item) - 2, 1), dtype=np.int32) - for index_box in range(len(item)): - if index_box >= 2: - temp_info_box = item[index_box].split(' ') - xmin = float(temp_info_box[0]) - ymin = float(temp_info_box[1]) - w = float(temp_info_box[2]) - h = float(temp_info_box[3]) - # Filter out wrong labels - if w < 0 or h < 0: - continue - xmin = max(0, xmin) - ymin = max(0, ymin) - xmax = xmin + w - ymax = ymin + h - gt_bbox[index_box - 2] = [xmin, ymin, xmax, ymax] - - widerface_rec = { - 'im_file': im_fname, - 'im_id': im_id, - 'gt_bbox': gt_bbox, - 'gt_class': gt_class, - } - # logger.debug - if len(item) != 0: - records.append(widerface_rec) - - ct += 1 - if sample_num > 0 and ct >= sample_num: - break - assert len(records) > 0, 'not found any widerface in %s' % (anno_path) - logger.info('{} samples in file {}'.format(ct, anno_path)) - return records, cname2cid - - -def _load_file_list(input_txt): - with open(input_txt, 'r') as f_dir: - lines_input_txt = f_dir.readlines() - - file_dict = {} - num_class = 0 - for i in range(len(lines_input_txt)): - line_txt = lines_input_txt[i].strip('\n\t\r') - if '.jpg' in line_txt: - if i != 0: - num_class += 1 - file_dict[num_class] = [] - file_dict[num_class].append(line_txt) - if '.jpg' not in line_txt: - if len(line_txt) > 6: - split_str = line_txt.split(' ') - x1_min = float(split_str[0]) - y1_min = float(split_str[1]) - x2_max = float(split_str[2]) - y2_max = float(split_str[3]) - line_txt = str(x1_min) + ' ' + str(y1_min) + ' ' + str( - x2_max) + ' ' + str(y2_max) - file_dict[num_class].append(line_txt) - else: - file_dict[num_class].append(line_txt) - - return list(file_dict.values()) - - -def widerface_label(with_background=True): - labels_map = {'face': 1} - if not with_background: - labels_map = {k: v - 1 for k, v in labels_map.items()} - return labels_map diff --git a/ppdet/data/tests/000012.jpg b/ppdet/data/tests/000012.jpg deleted file mode 100644 index b829107b842f6f15706744fdcbea05ec7341b311..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56920 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 diff --git a/ppdet/data/tests/coco.yml b/ppdet/data/tests/coco.yml deleted file mode 100644 index 80ae7ed9e..000000000 --- a/ppdet/data/tests/coco.yml +++ /dev/null @@ -1,48 +0,0 @@ -DATA: - TRAIN: - ANNO_FILE: data/coco.test/train2017.roidb - IMAGE_DIR: data/coco.test/train2017 - SAMPLES: 10 - TYPE: RoiDbSource - VAL: - ANNO_FILE: data/coco.test/val2017.roidb - IMAGE_DIR: data/coco.test/val2017 - SAMPLES: 10 - TYPE: RoiDbSource -TRANSFORM: - TRAIN: - OPS: - - OP: DecodeImage - TO_RGB: False - - OP: RandomFlipImage - PROB: 0.5 - - OP: NormalizeImage - MEAN: [102.9801, 115.9465, 122.7717] - IS_SCALE: False - IS_CHANNEL_FIRST: False - - OP: ResizeImage - TARGET_SIZE: 800 - MAX_SIZE: 1333 - - OP: Permute - TO_BGR: False - - OP: ArrangeRCNN - BATCH_SIZE: 1 - IS_PADDING: True - DROP_LAST: False - WORKER_CONF: - BUFSIZE: 100 - WORKER_NUM: 4 - USE_PROCESS: True - MEMSIZE: 2G - VAL: - OPS: - - OP: DecodeImage - TO_RGB: True - - OP: ResizeImage - TARGET_SIZE: 224 - - OP: ArrangeSSD - BATCH_SIZE: 1 - WORKER_CONF: - BUFSIZE: 100 - WORKER_NUM: 4 - USE_PROCESS: True diff --git a/ppdet/data/tests/data/prepare_data.sh b/ppdet/data/tests/data/prepare_data.sh deleted file mode 100755 index a81abc5d8..000000000 --- a/ppdet/data/tests/data/prepare_data.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -#function: -# prepare coco data for testing - -root=$(dirname `readlink -f ${BASH_SOURCE}[0]`) -cwd=`pwd` - -if [[ $cwd != $root ]];then - pushd $root 2>&1 1>/dev/null -fi - -test_coco_python2_url="http://filecenter.matrix.baidu.com/api/v1/file/wanglong03/coco.test.python2.zip/20190603095315/download" -test_coco_python3_url="http://filecenter.matrix.baidu.com/api/v1/file/wanglong03/coco.test.python3.zip/20190603095447/download" - -if [[ $1 = "python2" ]];then - test_coco_data_url=${test_coco_python2_url} - coco_zip_file="coco.test.python2.zip" -else - test_coco_data_url=${test_coco_python3_url} - coco_zip_file="coco.test.python3.zip" -fi -echo "download testing coco from url[${test_coco_data_url}]" -coco_root_dir=${coco_zip_file/.zip/} - -# clear already exist file or directory -rm -rf ${coco_root_dir} ${coco_zip_file} - -wget ${test_coco_data_url} -O ${coco_zip_file} -if [ -e $coco_zip_file ];then - echo "succeed to download ${coco_zip_file}, so unzip it" - unzip ${coco_zip_file} >/dev/null 2>&1 -fi - -if [ -e ${coco_root_dir} ];then - rm -rf coco.test - ln -s ${coco_root_dir} coco.test - echo "succeed to generate coco data in[${coco_root_dir}] for testing" - exit 0 -else - echo "failed to generate coco data" - exit 1 -fi diff --git a/ppdet/data/tests/rcnn_dataset.yml b/ppdet/data/tests/rcnn_dataset.yml deleted file mode 100644 index b57fd55e9..000000000 --- a/ppdet/data/tests/rcnn_dataset.yml +++ /dev/null @@ -1,32 +0,0 @@ -DATA: - TRAIN: - ANNO_FILE: data/coco.test/train2017.roidb - IMAGE_DIR: data/coco.test/train2017 - SAMPLES: 10 - IS_SHUFFLE: True - TYPE: RoiDbSource -TRANSFORM: - TRAIN: - OPS: - - OP: DecodeImage - TO_RGB: False - - OP: RandomFlipImage - PROB: 0.5 - - OP: NormalizeImage - MEAN: [102.9801, 115.9465, 122.7717] - IS_SCALE: False - IS_CHANNEL_FIRST: False - - OP: ResizeImage - TARGET_SIZE: 800 - MAX_SIZE: 1333 - - OP: Permute - TO_BGR: False - - OP: ArrangeRCNN - BATCH_SIZE: 1 - IS_PADDING: True - DROP_LAST: False - WORKER_CONF: - BUFSIZE: 100 - WORKER_NUM: 4 - MEMSIZE: 2G - USE_PROCESS: True diff --git a/ppdet/data/tests/run_all_tests.py b/ppdet/data/tests/run_all_tests.py deleted file mode 100644 index a1882d5dd..000000000 --- a/ppdet/data/tests/run_all_tests.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -#!/usr/bin/python -#-*-coding:utf-8-*- -"""Run all tests -""" - -import unittest -import test_loader -import test_operator -import test_roidb_source -import test_iterator_source -import test_transformer -import test_reader - -if __name__ == '__main__': - alltests = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(t) \ - for t in [ - test_loader.TestLoader, - test_operator.TestBase, - test_roidb_source.TestRoiDbSource, - test_iterator_source.TestIteratorSource, - test_transformer.TestTransformer, - test_reader.TestReader, - ] - ]) - - was_succ = unittest\ - .TextTestRunner(verbosity=2)\ - .run(alltests)\ - .wasSuccessful() - - exit(0 if was_succ else 1) diff --git a/ppdet/data/tests/set_env.py b/ppdet/data/tests/set_env.py deleted file mode 100644 index bc46ac0f1..000000000 --- a/ppdet/data/tests/set_env.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import sys -import os -import six -import logging - -import matplotlib -matplotlib.use('Agg', force=False) - -prefix = os.path.dirname(os.path.abspath(__file__)) - -#coco data for testing -if six.PY3: - version = 'python3' -else: - version = 'python2' - -data_root = os.path.join(prefix, 'data/coco.test.%s' % (version)) - -# coco data for testing -coco_data = { - 'TRAIN': { - 'ANNO_FILE': os.path.join(data_root, 'train2017.roidb'), - 'IMAGE_DIR': os.path.join(data_root, 'train2017') - }, - 'VAL': { - 'ANNO_FILE': os.path.join(data_root, 'val2017.roidb'), - 'IMAGE_DIR': os.path.join(data_root, 'val2017') - } -} - -script = os.path.join(os.path.dirname(__file__), 'data/prepare_data.sh') - -if not os.path.exists(data_root): - ret = os.system('bash %s %s' % (script, version)) - if ret != 0: - logging.error('not found file[%s], you should manually prepare ' - 'your data using "data/prepare_data.sh"' % (data_root)) - sys.exit(1) diff --git a/ppdet/data/tests/test.yml b/ppdet/data/tests/test.yml new file mode 100644 index 000000000..885d5ab1e --- /dev/null +++ b/ppdet/data/tests/test.yml @@ -0,0 +1,73 @@ +TrainReader: + inputs_def: + fields: ['image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_mask'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_num: 10 + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !RandomFlipImage + is_mask_flip: true + is_normalized: false + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: false + batch_size: 1 + shuffle: true + worker_num: 2 + drop_last: false + use_process: false + +EvalReader: + inputs_def: + fields: ['image', 'im_info', 'im_id'] + dataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco + sample_num: 10 + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485,0.456,0.406] + std: [0.229, 0.224,0.225] + - !ResizeImage + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + use_padded_im_info: true + batch_size: 1 + shuffle: false + drop_last: false diff --git a/ppdet/data/tests/test_dataset.py b/ppdet/data/tests/test_dataset.py index db99f464b..6b35b3620 100644 --- a/ppdet/data/tests/test_dataset.py +++ b/ppdet/data/tests/test_dataset.py @@ -19,22 +19,26 @@ import logging import random import copy -import set_env +from ppdet.data.parallel_map import ParallelMap -import ppdet.data.transform as tf -from ppdet.data.dataset import Dataset -class MemorySource(Dataset): +class MemorySource(object): """ memory data source for testing """ + def __init__(self, samples): - super(MemorySource, self).__init__() self._epoch = -1 self._pos = -1 self._drained = False self._samples = samples + def __iter__(self): + return self + + def __next__(self): + return self.next() + def next(self): if self._epoch < 0: self.reset() @@ -95,20 +99,20 @@ class TestDataset(unittest.TestCase): def test_transform_with_abnormal_worker(self): """ test dataset transform with abnormally exit process """ - samples = list(range(1000)) - ds = MemorySource(samples) + samples = list(range(20)) + mem_sc = MemorySource(samples) - def _mapper(sample): + def _worker(sample): if sample == 3: sys.exit(1) return 2 * sample - worker_conf = {'WORKER_NUM': 2, 'use_process': True} - mapped = tf.map(ds, _mapper, worker_conf) + test_worker = ParallelMap( + mem_sc, _worker, worker_num=2, use_process=True) ct = 0 - for i, d in enumerate(mapped): + for i, d in enumerate(test_worker): ct += 1 self.assertTrue(d / 2 in samples) @@ -117,20 +121,20 @@ class TestDataset(unittest.TestCase): def test_transform_with_delay_worker(self): """ test dataset transform with delayed process """ - samples = list(range(1000)) - ds = MemorySource(samples) + samples = list(range(20)) + mem_sc = MemorySource(samples) - def _mapper(sample): + def _worker(sample): if sample == 3: time.sleep(30) return 2 * sample - worker_conf = {'WORKER_NUM': 2, 'use_process': True} - mapped = tf.map(ds, _mapper, worker_conf) + test_worker = ParallelMap( + mem_sc, _worker, worker_num=2, use_process=True) ct = 0 - for i, d in enumerate(mapped): + for i, d in enumerate(test_worker): ct += 1 self.assertTrue(d / 2 in samples) @@ -140,4 +144,3 @@ class TestDataset(unittest.TestCase): if __name__ == '__main__': logging.basicConfig() unittest.main() - diff --git a/ppdet/data/tests/test_iterator_source.py b/ppdet/data/tests/test_iterator_source.py deleted file mode 100644 index bf3666573..000000000 --- a/ppdet/data/tests/test_iterator_source.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import time -import unittest -import sys -import logging - -import set_env -from ppdet.data.source import IteratorSource - - -def _generate_iter_maker(num=10): - def _reader(): - for i in range(num): - yield {'image': 'image_' + str(i), 'label': i} - - return _reader - - -class TestIteratorSource(unittest.TestCase): - """Test cases for dataset.source.roidb_source - """ - - @classmethod - def setUpClass(cls): - """ setup - """ - pass - - @classmethod - def tearDownClass(cls): - """ tearDownClass """ - pass - - def test_basic(self): - """ test basic apis 'next/size/drained' - """ - iter_maker = _generate_iter_maker() - iter_source = IteratorSource(iter_maker) - for i, sample in enumerate(iter_source): - self.assertTrue('image' in sample) - self.assertGreater(len(sample['image']), 0) - self.assertTrue(iter_source.drained()) - self.assertEqual(i + 1, iter_source.size()) - - def test_reset(self): - """ test functions 'reset/epoch_id' - """ - iter_maker = _generate_iter_maker() - iter_source = IteratorSource(iter_maker) - - self.assertTrue(iter_source.next() is not None) - self.assertEqual(iter_source.epoch_id(), 0) - - iter_source.reset() - - self.assertEqual(iter_source.epoch_id(), 1) - self.assertTrue(iter_source.next() is not None) - - -if __name__ == '__main__': - unittest.main() diff --git a/ppdet/data/tests/test_loader.py b/ppdet/data/tests/test_loader.py index dc835aa0f..9f373ad99 100644 --- a/ppdet/data/tests/test_loader.py +++ b/ppdet/data/tests/test_loader.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,96 +12,154 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -import os -import time import unittest -import sys -import logging -import numpy as np +import os + +from ppdet.data.source.coco import COCODataSet +from ppdet.data.reader import Reader +from ppdet.utils.download import get_path +from ppdet.utils.download import DATASET_HOME -import set_env +from ppdet.data.transform.operators import DecodeImage, ResizeImage, Permute +from ppdet.data.transform.batch_operators import PadBatch +COCO_VAL_URL = 'http://images.cocodataset.org/zips/val2017.zip' +COCO_VAL_MD5SUM = '442b8da7639aecaf257c1dceb8ba8c80' +COCO_ANNO_URL = 'http://images.cocodataset.org/annotations/annotations_trainval2017.zip' +COCO_ANNO_MD5SUM = 'f4bbac642086de4f52a3fdda2de5fa2c' -class TestLoader(unittest.TestCase): - """Test cases for dataset.source.loader - """ +class TestReader(unittest.TestCase): @classmethod def setUpClass(cls): """ setup """ - cls.prefix = os.path.dirname(os.path.abspath(__file__)) - # json data - cls.anno_path = os.path.join(cls.prefix, - 'data/coco/instances_val2017.json') - cls.image_dir = os.path.join(cls.prefix, 'data/coco/val2017') - cls.anno_path1 = os.path.join(cls.prefix, - "data/voc/ImageSets/Main/train.txt") - cls.image_dir1 = os.path.join(cls.prefix, "data/voc/JPEGImages") + root_path = os.path.join(DATASET_HOME, 'coco') + _, _ = get_path(COCO_VAL_URL, root_path, COCO_VAL_MD5SUM) + _, _ = get_path(COCO_ANNO_URL, root_path, COCO_ANNO_MD5SUM) + cls.anno_path = 'annotations/instances_val2017.json' + cls.image_dir = 'val2017' + cls.root_path = root_path @classmethod def tearDownClass(cls): """ tearDownClass """ pass - def test_load_coco_in_json(self): - """ test loading COCO data in json file - """ - from ppdet.data.source.coco_loader import load - if not os.path.exists(self.anno_path): - logging.warn('not found %s, so skip this test' % (self.anno_path)) - return - samples = 10 - records, cname2id = load(self.anno_path, samples) - self.assertEqual(len(records), samples) - self.assertGreater(len(cname2id), 0) - - def test_load_coco_in_roidb(self): - """ test loading COCO data in pickled records - """ - anno_path = os.path.join(self.prefix, - 'data/roidbs/instances_val2017.roidb') - - if not os.path.exists(anno_path): - logging.warn('not found %s, so skip this test' % (anno_path)) - return - - samples = 10 - from ppdet.data.source.loader import load_roidb - records, cname2cid = load_roidb(anno_path, samples) - self.assertEqual(len(records), samples) - self.assertGreater(len(cname2cid), 0) - - def test_load_voc_in_xml(self): - """ test loading VOC data in xml files - """ - from ppdet.data.source.voc_loader import load - if not os.path.exists(self.anno_path1): - logging.warn('not found %s, so skip this test' % (self.anno_path1)) - return - samples = 3 - records, cname2cid = load(self.anno_path1, samples) - self.assertEqual(len(records), samples) - self.assertGreater(len(cname2cid), 0) - - def test_load_voc_in_roidb(self): - """ test loading VOC data in pickled records - """ - anno_path = os.path.join(self.prefix, 'data/roidbs/train.roidb') - - if not os.path.exists(anno_path): - logging.warn('not found %s, so skip this test' % (anno_path)) - return - - samples = 3 - from ppdet.data.source.loader import load_roidb - records, cname2cid = load_roidb(anno_path, samples) - self.assertEqual(len(records), samples) - self.assertGreater(len(cname2cid), 0) + def test_loader(self): + coco_loader = COCODataSet( + dataset_dir=self.root_path, + image_dir=self.image_dir, + anno_path=self.anno_path, + sample_num=10) + sample_trans = [ + DecodeImage(to_rgb=True), ResizeImage( + target_size=800, max_size=1333, interp=1), Permute(to_bgr=False) + ] + batch_trans = [PadBatch(pad_to_stride=32, use_padded_im_info=True), ] + + inputs_def = { + 'fields': [ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', + 'gt_mask' + ], + } + data_loader = Reader( + coco_loader, + sample_transforms=sample_trans, + batch_transforms=batch_trans, + batch_size=2, + shuffle=True, + drop_empty=True, + inputs_def=inputs_def)() + for i in range(2): + for samples in data_loader: + for sample in samples: + im_shape = sample[0].shape + self.assertEqual(im_shape[0], 3) + self.assertEqual(im_shape[1] % 32, 0) + self.assertEqual(im_shape[2] % 32, 0) + + im_info_shape = sample[1].shape + self.assertEqual(im_info_shape[-1], 3) + + im_id_shape = sample[2].shape + self.assertEqual(im_id_shape[-1], 1) + + gt_bbox_shape = sample[3].shape + self.assertEqual(gt_bbox_shape[-1], 4) + + gt_class_shape = sample[4].shape + self.assertEqual(gt_class_shape[-1], 1) + self.assertEqual(gt_class_shape[0], gt_bbox_shape[0]) + + is_crowd_shape = sample[5].shape + self.assertEqual(is_crowd_shape[-1], 1) + self.assertEqual(is_crowd_shape[0], gt_bbox_shape[0]) + + mask = sample[6] + self.assertEqual(len(mask), gt_bbox_shape[0]) + self.assertEqual(mask[0][0].shape[-1], 2) + data_loader.reset() + + def test_loader_multi_threads(self): + coco_loader = COCODataSet( + dataset_dir=self.root_path, + image_dir=self.image_dir, + anno_path=self.anno_path, + sample_num=10) + sample_trans = [ + DecodeImage(to_rgb=True), ResizeImage( + target_size=800, max_size=1333, interp=1), Permute(to_bgr=False) + ] + batch_trans = [PadBatch(pad_to_stride=32, use_padded_im_info=True), ] + + inputs_def = { + 'fields': [ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd', + 'gt_mask' + ], + } + data_loader = Reader( + coco_loader, + sample_transforms=sample_trans, + batch_transforms=batch_trans, + batch_size=2, + shuffle=True, + drop_empty=True, + worker_num=2, + use_process=False, + bufsize=8, + inputs_def=inputs_def)() + for i in range(2): + for samples in data_loader: + for sample in samples: + im_shape = sample[0].shape + self.assertEqual(im_shape[0], 3) + self.assertEqual(im_shape[1] % 32, 0) + self.assertEqual(im_shape[2] % 32, 0) + + im_info_shape = sample[1].shape + self.assertEqual(im_info_shape[-1], 3) + + im_id_shape = sample[2].shape + self.assertEqual(im_id_shape[-1], 1) + + gt_bbox_shape = sample[3].shape + self.assertEqual(gt_bbox_shape[-1], 4) + + gt_class_shape = sample[4].shape + self.assertEqual(gt_class_shape[-1], 1) + self.assertEqual(gt_class_shape[0], gt_bbox_shape[0]) + + is_crowd_shape = sample[5].shape + self.assertEqual(is_crowd_shape[-1], 1) + self.assertEqual(is_crowd_shape[0], gt_bbox_shape[0]) + + mask = sample[6] + self.assertEqual(len(mask), gt_bbox_shape[0]) + self.assertEqual(mask[0][0].shape[-1], 2) + data_loader.reset() if __name__ == '__main__': diff --git a/ppdet/data/tests/test_loader_yaml.py b/ppdet/data/tests/test_loader_yaml.py new file mode 100644 index 000000000..121f52729 --- /dev/null +++ b/ppdet/data/tests/test_loader_yaml.py @@ -0,0 +1,110 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import os +import yaml +import logging + +from ppdet.utils.download import get_path +from ppdet.utils.download import DATASET_HOME +from ppdet.core.workspace import load_config, merge_config + +from ppdet.data.reader import create_reader + +COCO_VAL_URL = 'http://images.cocodataset.org/zips/val2017.zip' +COCO_VAL_MD5SUM = '442b8da7639aecaf257c1dceb8ba8c80' +COCO_ANNO_URL = 'http://images.cocodataset.org/annotations/annotations_trainval2017.zip' +COCO_ANNO_MD5SUM = 'f4bbac642086de4f52a3fdda2de5fa2c' + +FORMAT = '[%(asctime)s-%(filename)s-%(levelname)s:%(message)s]' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +class TestReaderYAML(unittest.TestCase): + @classmethod + def setUpClass(cls): + """ setup + """ + root_path = os.path.join(DATASET_HOME, 'coco') + _, _ = get_path(COCO_VAL_URL, root_path, COCO_VAL_MD5SUM) + _, _ = get_path(COCO_ANNO_URL, root_path, COCO_ANNO_MD5SUM) + cls.anno_path = 'annotations/instances_val2017.json' + cls.image_dir = 'val2017' + cls.root_path = root_path + + @classmethod + def tearDownClass(cls): + """ tearDownClass """ + pass + + def test_loader_yaml(self): + cfg_file = 'ppdet/data/tests/test.yml' + cfg = load_config(cfg_file) + data_cfg = '[!COCODataSet {{image_dir: {0}, dataset_dir: {1}, ' \ + 'anno_path: {2}, sample_num: 10}}]'.format( + self.image_dir, self.root_path, self.anno_path) + dataset_ins = yaml.load(data_cfg, Loader=yaml.Loader) + update_train_cfg = {'TrainReader': {'dataset': dataset_ins[0]}} + update_test_cfg = {'EvalReader': {'dataset': dataset_ins[0]}} + merge_config(update_train_cfg) + merge_config(update_test_cfg) + + reader = create_reader(cfg['TrainReader'], 10)() + for samples in reader: + for sample in samples: + im_shape = sample[0].shape + self.assertEqual(im_shape[0], 3) + self.assertEqual(im_shape[1] % 32, 0) + self.assertEqual(im_shape[2] % 32, 0) + + im_info_shape = sample[1].shape + self.assertEqual(im_info_shape[-1], 3) + + im_id_shape = sample[2].shape + self.assertEqual(im_id_shape[-1], 1) + + gt_bbox_shape = sample[3].shape + self.assertEqual(gt_bbox_shape[-1], 4) + + gt_class_shape = sample[4].shape + self.assertEqual(gt_class_shape[-1], 1) + self.assertEqual(gt_class_shape[0], gt_bbox_shape[0]) + + is_crowd_shape = sample[5].shape + self.assertEqual(is_crowd_shape[-1], 1) + self.assertEqual(is_crowd_shape[0], gt_bbox_shape[0]) + + mask = sample[6] + self.assertEqual(len(mask), gt_bbox_shape[0]) + self.assertEqual(mask[0][0].shape[-1], 2) + + reader = create_reader(cfg['EvalReader'], 10)() + for samples in reader: + for sample in samples: + im_shape = sample[0].shape + self.assertEqual(im_shape[0], 3) + self.assertEqual(im_shape[1] % 32, 0) + self.assertEqual(im_shape[2] % 32, 0) + + im_info_shape = sample[1].shape + self.assertEqual(im_info_shape[-1], 3) + + im_id_shape = sample[2].shape + self.assertEqual(im_id_shape[-1], 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/ppdet/data/tests/test_operator.py b/ppdet/data/tests/test_operator.py deleted file mode 100644 index 85d5b229d..000000000 --- a/ppdet/data/tests/test_operator.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import unittest -import logging -import numpy as np -import set_env -import ppdet.data.transform as tf -logging.basicConfig(level=logging.INFO) - - -class TestBase(unittest.TestCase): - """Test cases for dataset.transform.operator - """ - - @classmethod - def setUpClass(cls, with_mixup=False): - """ setup - """ - roidb_fname = set_env.coco_data['TRAIN']['ANNO_FILE'] - image_dir = set_env.coco_data['TRAIN']['IMAGE_DIR'] - import pickle as pkl - with open(roidb_fname, 'rb') as f: - roidb = f.read() - roidb = pkl.loads(roidb) - fn = os.path.join(image_dir, roidb[0][0]['im_file']) - with open(fn, 'rb') as f: - roidb[0][0]['image'] = f.read() - if with_mixup: - mixup_fn = os.path.join(image_dir, roidb[0][1]['im_file']) - roidb[0][0]['mixup'] = roidb[0][1] - with open(fn, 'rb') as f: - roidb[0][0]['mixup']['image'] = f.read() - cls.sample = roidb[0][0] - - @classmethod - def tearDownClass(cls): - """ tearDownClass """ - pass - - def test_ops_all(self): - """ test operators - """ - # ResizeImage - ops_conf = [{ - 'op': 'DecodeImage' - }, { - 'op': 'ResizeImage', - 'target_size': 300, - 'max_size': 1333 - }] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - data = self.sample.copy() - result0 = mapper(data) - self.assertIsNotNone(result0['image']) - self.assertEqual(len(result0['image'].shape), 3) - # RandFlipImage - ops_conf = [{'op': 'RandomFlipImage'}] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - result1 = mapper(result0) - self.assertEqual(result1['image'].shape, result0['image'].shape) - self.assertEqual(result1['gt_bbox'].shape, result0['gt_bbox'].shape) - # NormalizeImage - ops_conf = [{'op': 'NormalizeImage', 'is_channel_first': False}] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - result2 = mapper(result1) - im1 = result1['image'] - count = np.where(im1 <= 1)[0] - if im1.dtype == 'float64': - self.assertEqual(count, im1.shape[0] * im1.shape[1], im1.shape[2]) - # ArrangeSample - ops_conf = [{'op': 'ArrangeRCNN'}] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - result3 = mapper(result2) - self.assertEqual(type(result3), tuple) - - def test_ops_part1(self): - """test Crop and Resize - """ - ops_conf = [{ - 'op': 'DecodeImage' - }, { - 'op': 'NormalizeBox' - }, { - 'op': 'CropImage', - 'batch_sampler': [[1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.1, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.3, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.5, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.7, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.9, 0.0], - [1, 50, 0.3, 1.0, 0.5, 2.0, 0.0, 1.0]] - }] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - data = self.sample.copy() - result = mapper(data) - self.assertEqual(len(result['image'].shape), 3) - - def test_ops_part2(self): - """test Expand and RandomDistort - """ - ops_conf = [{ - 'op': 'DecodeImage' - }, { - 'op': 'NormalizeBox' - }, { - 'op': 'ExpandImage', - 'max_ratio': 1.5, - 'prob': 1 - }] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - data = self.sample.copy() - result = mapper(data) - self.assertEqual(len(result['image'].shape), 3) - self.assertGreater(result['gt_bbox'].shape[0], 0) - - def test_ops_part3(self): - """test Mixup and RandomInterp - """ - ops_conf = [{ - 'op': 'DecodeImage', - 'with_mixup': True, - }, { - 'op': 'MixupImage', - }, { - 'op': 'RandomInterpImage', - 'target_size': 608 - }] - mapper = tf.build_mapper(ops_conf) - self.assertTrue(mapper is not None) - data = self.sample.copy() - result = mapper(data) - self.assertEqual(len(result['image'].shape), 3) - self.assertGreater(result['gt_bbox'].shape[0], 0) - #self.assertGreater(result['gt_score'].shape[0], 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/ppdet/data/tests/test_reader.py b/ppdet/data/tests/test_reader.py deleted file mode 100644 index fdc6a61a6..000000000 --- a/ppdet/data/tests/test_reader.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import time -import unittest -import sys -import logging -import numpy as np -import yaml - -import set_env -from ppdet.data.reader import Reader -from ppdet.data.source import build_source -from ppdet.data.source import IteratorSource - - -class TestReader(unittest.TestCase): - """Test cases for dataset.reader - """ - - @classmethod - def setUpClass(cls): - """ setup - """ - prefix = os.path.dirname(os.path.abspath(__file__)) - coco_yml = os.path.join(prefix, 'coco.yml') - with open(coco_yml, 'rb') as f: - cls.coco_conf = yaml.load(f.read()) - - cls.coco_conf['DATA']['TRAIN'] = set_env.coco_data['TRAIN'] - cls.coco_conf['DATA']['VAL'] = set_env.coco_data['VAL'] - - rcnn_yml = os.path.join(prefix, 'rcnn_dataset.yml') - - with open(rcnn_yml, 'rb') as f: - cls.rcnn_conf = yaml.load(f.read()) - - cls.rcnn_conf['DATA']['TRAIN'] = set_env.coco_data['TRAIN'] - cls.rcnn_conf['DATA']['VAL'] = set_env.coco_data['VAL'] - - @classmethod - def tearDownClass(cls): - """ tearDownClass """ - pass - - def test_train(self): - """ Test reader for training - """ - coco = Reader( - self.coco_conf['DATA'], self.coco_conf['TRANSFORM'], maxiter=1000) - train_rd = coco.train() - self.assertTrue(train_rd is not None) - - ct = 0 - total = 0 - bytes = 0 - prev_ts = None - for sample in train_rd(): - if prev_ts is None: - start_ts = time.time() - prev_ts = start_ts - - ct += 1 - bytes += 4 * sample[0][0].size * len(sample[0]) - self.assertTrue(sample is not None) - cost = time.time() - prev_ts - if cost >= 1.0: - total += ct - qps = total / (time.time() - start_ts) - bps = bytes / (time.time() - start_ts) - - logging.info('got %d/%d samples in %.3fsec with qps:%d bps:%d' % - (ct, total, cost, qps, bps)) - bytes = 0 - ct = 0 - prev_ts = time.time() - - total += ct - self.assertEqual(total, coco._maxiter) - - def test_val(self): - """ Test reader for validation - """ - coco = Reader(self.coco_conf['DATA'], self.coco_conf['TRANSFORM'], 10) - val_rd = coco.val() - self.assertTrue(val_rd is not None) - - # test 3 epoches - for _ in range(3): - ct = 0 - for sample in val_rd(): - ct += 1 - self.assertTrue(sample is not None) - self.assertGreaterEqual(ct, coco._maxiter) - - def test_rcnn_train(self): - """ Test reader for training - """ - anno = self.rcnn_conf['DATA']['TRAIN']['ANNO_FILE'] - if not os.path.exists(anno): - logging.error('exit test_rcnn for not found file[%s]' % (anno)) - return - - rcnn = Reader(self.rcnn_conf['DATA'], self.rcnn_conf['TRANSFORM'], 10) - rcnn_rd = rcnn.train() - self.assertTrue(rcnn_rd is not None) - - ct = 0 - out = None - for sample in rcnn_rd(): - out = sample - ct += 1 - self.assertTrue(sample is not None) - self.assertEqual(out[0][0].shape[0], 3) - self.assertEqual(out[0][1].shape[0], 3) - self.assertEqual(out[0][3].shape[1], 4) - self.assertEqual(out[0][4].shape[1], 1) - self.assertEqual(out[0][5].shape[1], 1) - self.assertGreaterEqual(ct, rcnn._maxiter) - - def test_create(self): - """ Test create a reader using my source - """ - - def _my_data_reader(): - mydata = build_source(self.rcnn_conf['DATA']['TRAIN']) - for i, sample in enumerate(mydata): - yield sample - - my_source = IteratorSource(_my_data_reader) - mode = 'TRAIN' - train_rd = Reader.create( - mode, - self.rcnn_conf['DATA'][mode], - self.rcnn_conf['TRANSFORM'][mode], - max_iter=10, - my_source=my_source) - - out = None - for sample in train_rd(): - out = sample - self.assertTrue(sample is not None) - self.assertEqual(out[0][0].shape[0], 3) - self.assertEqual(out[0][1].shape[0], 3) - self.assertEqual(out[0][3].shape[1], 4) - self.assertEqual(out[0][4].shape[1], 1) - self.assertEqual(out[0][5].shape[1], 1) - - -if __name__ == '__main__': - unittest.main() diff --git a/ppdet/data/tests/test_roidb_source.py b/ppdet/data/tests/test_roidb_source.py deleted file mode 100644 index 105cc9cd7..000000000 --- a/ppdet/data/tests/test_roidb_source.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import time -import unittest -import sys -import logging - -import set_env -from ppdet.data.source import build_source - - -class TestRoiDbSource(unittest.TestCase): - """Test cases for dataset.source.roidb_source - """ - - @classmethod - def setUpClass(cls): - """ setup - """ - anno_path = set_env.coco_data['TRAIN']['ANNO_FILE'] - image_dir = set_env.coco_data['TRAIN']['IMAGE_DIR'] - cls.config = { - 'data_cf': { - 'anno_file': anno_path, - 'image_dir': image_dir, - 'samples': 100, - 'load_img': True - }, - 'cname2cid': None - } - - @classmethod - def tearDownClass(cls): - """ tearDownClass """ - pass - - def test_basic(self): - """ test basic apis 'next/size/drained' - """ - roi_source = build_source(self.config) - for i, sample in enumerate(roi_source): - self.assertTrue('image' in sample) - self.assertGreater(len(sample['image']), 0) - self.assertTrue(roi_source.drained()) - self.assertEqual(i + 1, roi_source.size()) - - def test_reset(self): - """ test functions 'reset/epoch_id' - """ - roi_source = build_source(self.config) - - self.assertTrue(roi_source.next() is not None) - self.assertEqual(roi_source.epoch_id(), 0) - - roi_source.reset() - - self.assertEqual(roi_source.epoch_id(), 1) - self.assertTrue(roi_source.next() is not None) - - -if __name__ == '__main__': - unittest.main() diff --git a/ppdet/data/tests/test_transformer.py b/ppdet/data/tests/test_transformer.py deleted file mode 100644 index 912366929..000000000 --- a/ppdet/data/tests/test_transformer.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import time -import unittest -import sys -import logging -import numpy as np - -import set_env -import ppdet.data.transform as tf -from ppdet.data.source import build_source - -logger = logging.getLogger(__name__) - -logging.basicConfig(level=logging.INFO) - - -class TestTransformer(unittest.TestCase): - """Test cases for dataset.transform.transformer - """ - - @classmethod - def setUpClass(cls): - """ setup - """ - - prefix = os.path.dirname(os.path.abspath(__file__)) - # json data - anno_path = set_env.coco_data['TRAIN']['ANNO_FILE'] - image_dir = set_env.coco_data['TRAIN']['IMAGE_DIR'] - cls.sc_config = { - 'anno_file': anno_path, - 'image_dir': image_dir, - 'samples': 200 - } - - cls.ops = [{ - 'op': 'DecodeImage', - 'to_rgb': True - }, { - 'op': 'ResizeImage', - 'target_size': 800, - 'max_size': 1333 - }, { - 'op': 'ArrangeRCNN', - 'is_mask': False - }] - - @classmethod - def tearDownClass(cls): - """ tearDownClass """ - pass - - def test_map(self): - """ test transformer.map - """ - mapper = tf.build_mapper(self.ops) - ds = build_source(self.sc_config) - mapped_ds = tf.map(ds, mapper) - ct = 0 - for sample in mapped_ds: - self.assertTrue(type(sample[0]) is np.ndarray) - ct += 1 - - self.assertEqual(ct, mapped_ds.size()) - - def test_parallel_map(self): - """ test transformer.map with concurrent workers - """ - mapper = tf.build_mapper(self.ops) - ds = build_source(self.sc_config) - worker_conf = {'WORKER_NUM': 2, 'use_process': True} - mapped_ds = tf.map(ds, mapper, worker_conf) - - ct = 0 - for sample in mapped_ds: - self.assertTrue(type(sample[0]) is np.ndarray) - ct += 1 - - self.assertTrue(mapped_ds.drained()) - self.assertEqual(ct, mapped_ds.size()) - mapped_ds.reset() - - ct = 0 - for sample in mapped_ds: - self.assertTrue(type(sample[0]) is np.ndarray) - ct += 1 - - self.assertEqual(ct, mapped_ds.size()) - - def test_batch(self): - """ test batched dataset - """ - batchsize = 2 - mapper = tf.build_mapper(self.ops) - ds = build_source(self.sc_config) - mapped_ds = tf.map(ds, mapper) - batched_ds = tf.batch(mapped_ds, batchsize, True) - for sample in batched_ds: - out = sample - self.assertEqual(len(out), batchsize) - - -if __name__ == '__main__': - unittest.main() diff --git a/ppdet/data/tools/generate_data_for_training.py b/ppdet/data/tools/generate_data_for_training.py deleted file mode 100644 index 30b196f61..000000000 --- a/ppdet/data/tools/generate_data_for_training.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# function: -# tool used convert COCO or VOC data to a pickled file whose -# schema for each sample is the same. -# -# notes: -# Original data format of COCO or VOC can also be directly -# used by 'PPdetection' to train. -# This tool just convert data to a unified schema, -# and it's useful when debuging with small dataset. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import argparse - -import os -import sys -import logging -import pickle as pkl - -path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../') -if path not in sys.path: - sys.path.insert(0, path) - -from data.source import loader - - -def parse_args(): - """ parse arguments - """ - parser = argparse.ArgumentParser( - description='Generate Standard Dataset for PPdetection') - - parser.add_argument( - '--type', - type=str, - default='json', - help='file format of label file, eg: json for COCO and xml for VOC') - parser.add_argument( - '--annotation', - type=str, - help='label file name for COCO or VOC dataset, ' - 'eg: instances_val2017.json or train.txt') - parser.add_argument( - '--save-dir', - type=str, - default='roidb', - help='directory to save roidb file which contains pickled samples') - parser.add_argument( - '--samples', - type=int, - default=-1, - help='number of samples to dump, default to all') - - args = parser.parse_args() - return args - - -def dump_coco_as_pickle(args): - """ Load COCO data, and then save it as pickled file. - - Notes: - label file of COCO contains a json which consists - of label info for each sample - """ - samples = args.samples - save_dir = args.save_dir - if not os.path.exists(save_dir): - os.makedirs(save_dir) - anno_path = args.annotation - roidb, cat2id = loader.load(anno_path, samples, with_cat2id=True) - samples = len(roidb) - dsname = os.path.basename(anno_path).rstrip('.json') - roidb_fname = save_dir + "/%s.roidb" % (dsname) - with open(roidb_fname, "wb") as fout: - pkl.dump((roidb, cat2id), fout) - - #for rec in roidb: - # sys.stderr.write('%s\n' % (rec['im_file'])) - logging.info('dumped %d samples to file[%s]' % (samples, roidb_fname)) - - -def dump_voc_as_pickle(args): - """ Load VOC data, and then save it as pickled file. - - Notes: - we assume label file of VOC contains lines - each of which corresponds to a xml file - that contains it's label info - """ - samples = args.samples - save_dir = args.save_dir - if not os.path.exists(save_dir): - os.makedirs(save_dir) - save_dir = args.save_dir - anno_path = os.path.expanduser(args.annotation) - roidb, cat2id = loader.load( - anno_path, samples, with_cat2id=True, use_default_label=None) - samples = len(roidb) - part = anno_path.split('/') - dsname = part[-4] - roidb_fname = save_dir + "/%s.roidb" % (dsname) - with open(roidb_fname, "wb") as fout: - pkl.dump((roidb, cat2id), fout) - anno_path = os.path.join(anno_path.split('/train.txt')[0], 'label_list.txt') - with open(anno_path, 'w') as fw: - for key in cat2id.keys(): - fw.write(key + '\n') - logging.info('dumped %d samples to file[%s]' % (samples, roidb_fname)) - - -if __name__ == "__main__": - """ Make sure you have already downloaded original COCO or VOC data, - then you can convert it using this tool. - - Usage: - python generate_data_for_training.py --type=json - --annotation=./annotations/instances_val2017.json - --save-dir=./roidb --samples=100 - """ - args = parse_args() - - # VOC data are organized in xml files - if args.type == 'xml': - dump_voc_as_pickle(args) - # COCO data are organized in json file - elif args.type == 'json': - dump_coco_as_pickle(args) - else: - TypeError('Can\'t deal with {} type. '\ - 'Only xml or json file format supported'.format(args.type)) diff --git a/ppdet/data/transform/__init__.py b/ppdet/data/transform/__init__.py index f4d15e9c3..c5deb535a 100644 --- a/ppdet/data/transform/__init__.py +++ b/ppdet/data/transform/__init__.py @@ -12,132 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import -from __future__ import print_function +from . import operators +from . import batch_operators -import copy -import logging -import traceback - -from .transformer import MappedDataset, BatchedDataset -from .post_map import build_post_map -from .parallel_map import ParallelMappedDataset -from .operators import BaseOperator, registered_ops - -__all__ = ['build_mapper', 'map', 'batch', 'batch_map'] - -logger = logging.getLogger(__name__) - - -def build_mapper(ops, context=None): - """ - Build a mapper for operators in 'ops' - - Args: - ops (list of operator.BaseOperator or list of op dict): - configs for oprators, eg: - [{'name': 'DecodeImage', 'params': {'to_rgb': True}}, {xxx}] - context (dict): a context object for mapper - - Returns: - a mapper function which accept one argument 'sample' and - return the processed result - """ - new_ops = [] - for _dict in ops: - new_dict = {} - for i, j in _dict.items(): - new_dict[i.lower()] = j - new_ops.append(new_dict) - ops = new_ops - op_funcs = [] - op_repr = [] - for op in ops: - if type(op) is dict and 'op' in op: - op_func = getattr(BaseOperator, op['op']) - params = copy.deepcopy(op) - del params['op'] - o = op_func(**params) - elif not isinstance(op, BaseOperator): - op_func = getattr(BaseOperator, op['name']) - params = {} if 'params' not in op else op['params'] - o = op_func(**params) - else: - assert isinstance(op, BaseOperator), \ - "invalid operator when build ops" - o = op - op_funcs.append(o) - op_repr.append('{{{}}}'.format(str(o))) - op_repr = '[{}]'.format(','.join(op_repr)) - - def _mapper(sample): - ctx = {} if context is None else copy.deepcopy(context) - for f in op_funcs: - try: - out = f(sample, ctx) - sample = out - except Exception as e: - stack_info = traceback.format_exc() - logger.warn("fail to map op [{}] with error: {} and stack:\n{}". - format(f, e, str(stack_info))) - raise e - - return out - - _mapper.ops = op_repr - return _mapper - - -def map(ds, mapper, worker_args=None): - """ - Apply 'mapper' to 'ds' - - Args: - ds (instance of Dataset): dataset to be mapped - mapper (function): action to be executed for every data sample - worker_args (dict): configs for concurrent mapper - Returns: - a mapped dataset - """ - - if worker_args is not None: - return ParallelMappedDataset(ds, mapper, worker_args) - else: - return MappedDataset(ds, mapper) - - -def batch(ds, batchsize, drop_last=False, drop_empty=True): - """ - Batch data samples to batches - Args: - batchsize (int): number of samples for a batch - drop_last (bool): drop last few samples if not enough for a batch - - Returns: - a batched dataset - """ - - return BatchedDataset( - ds, batchsize, drop_last=drop_last, drop_empty=drop_empty) - - -def batch_map(ds, config): - """ - Post process the batches. - - Args: - ds (instance of Dataset): dataset to be mapped - mapper (function): action to be executed for every batch - Returns: - a batched dataset which is processed - """ - - mapper = build_post_map(**config) - return MappedDataset(ds, mapper) - - -for nm in registered_ops: - op = getattr(BaseOperator, nm) - locals()[nm] = op +from .operators import * +from .batch_operators import * +__all__ = [] __all__ += registered_ops diff --git a/ppdet/data/transform/arrange_sample.py b/ppdet/data/transform/arrange_sample.py deleted file mode 100644 index bebce691d..000000000 --- a/ppdet/data/transform/arrange_sample.py +++ /dev/null @@ -1,384 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# function: -# operators to process sample, -# eg: decode/resize/crop image - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import logging -import numpy as np -from .operators import BaseOperator, register_op - -logger = logging.getLogger(__name__) - - -@register_op -class ArrangeRCNN(BaseOperator): - """ - Transform dict to tuple format needed for training. - - Args: - is_mask (bool): whether to use include mask data - """ - - def __init__(self, is_mask=False): - super(ArrangeRCNN, self).__init__() - self.is_mask = is_mask - assert isinstance(self.is_mask, bool), "wrong type for is_mask" - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing following items - (image, im_info, im_id, gt_bbox, gt_class, is_crowd, gt_masks) - """ - im = sample['image'] - gt_bbox = sample['gt_bbox'] - gt_class = sample['gt_class'] - keys = list(sample.keys()) - if 'is_crowd' in keys: - is_crowd = sample['is_crowd'] - else: - raise KeyError("The dataset doesn't have 'is_crowd' key.") - if 'im_info' in keys: - im_info = sample['im_info'] - else: - raise KeyError("The dataset doesn't have 'im_info' key.") - im_id = sample['im_id'] - - outs = (im, im_info, im_id, gt_bbox, gt_class, is_crowd) - gt_masks = [] - if self.is_mask and len(sample['gt_poly']) != 0 \ - and 'is_crowd' in keys: - valid = True - segms = sample['gt_poly'] - assert len(segms) == is_crowd.shape[0] - for i in range(len(sample['gt_poly'])): - segm, iscrowd = segms[i], is_crowd[i] - gt_segm = [] - if iscrowd: - gt_segm.append([[0, 0]]) - else: - for poly in segm: - if len(poly) == 0: - valid = False - break - gt_segm.append(np.array(poly).reshape(-1, 2)) - if (not valid) or len(gt_segm) == 0: - break - gt_masks.append(gt_segm) - outs = outs + (gt_masks, ) - return outs - - -@register_op -class ArrangeEvalRCNN(BaseOperator): - """ - Transform dict to the tuple format needed for evaluation. - """ - - def __init__(self): - super(ArrangeEvalRCNN, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: - (image, im_info, im_id, im_shape, gt_bbox, - gt_class, difficult) - """ - ims = [] - keys = sorted(list(sample.keys())) - for k in keys: - if 'image' in k: - ims.append(sample[k]) - if 'im_info' in keys: - im_info = sample['im_info'] - else: - raise KeyError("The dataset doesn't have 'im_info' key.") - im_id = sample['im_id'] - h = sample['h'] - w = sample['w'] - # For rcnn models in eval and infer stage, original image size - # is needed to clip the bounding boxes. And box clip op in - # bbox prediction needs im_info as input in format of [N, 3], - # so im_shape is appended by 1 to match dimension. - im_shape = np.array((h, w, 1), dtype=np.float32) - gt_bbox = sample['gt_bbox'] - gt_class = sample['gt_class'] - difficult = sample['difficult'] - remain_list = [im_info, im_id, im_shape, gt_bbox, gt_class, difficult] - ims.extend(remain_list) - outs = tuple(ims) - return outs - - -@register_op -class ArrangeTestRCNN(BaseOperator): - """ - Transform dict to the tuple format needed for training. - """ - - def __init__(self): - super(ArrangeTestRCNN, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: - (image, im_info, im_id, im_shape) - """ - ims = [] - keys = sorted(list(sample.keys())) - for k in keys: - if 'image' in k: - ims.append(sample[k]) - if 'im_info' in keys: - im_info = sample['im_info'] - else: - raise KeyError("The dataset doesn't have 'im_info' key.") - im_id = sample['im_id'] - h = sample['h'] - w = sample['w'] - # For rcnn models in eval and infer stage, original image size - # is needed to clip the bounding boxes. And box clip op in - # bbox prediction needs im_info as input in format of [N, 3], - # so im_shape is appended by 1 to match dimension. - im_shape = np.array((h, w, 1), dtype=np.float32) - remain_list = [im_info, im_id, im_shape] - ims.extend(remain_list) - outs = tuple(ims) - return outs - - -@register_op -class ArrangeSSD(BaseOperator): - """ - Transform dict to tuple format needed for training. - """ - - def __init__(self): - super(ArrangeSSD, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: - (image, gt_bbox, gt_class, difficult) - """ - im = sample['image'] - gt_bbox = sample['gt_bbox'] - gt_class = sample['gt_class'] - outs = (im, gt_bbox, gt_class) - return outs - - -@register_op -class ArrangeEvalSSD(BaseOperator): - """ - Transform dict to tuple format needed for training. - """ - - def __init__(self, fields): - super(ArrangeEvalSSD, self).__init__() - self.fields = fields - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: (image) - """ - outs = [] - if len(sample['gt_bbox']) != len(sample['gt_class']): - raise ValueError("gt num mismatch: bbox and class.") - for field in self.fields: - if field == 'im_shape': - h = sample['h'] - w = sample['w'] - im_shape = np.array((h, w)) - outs.append(im_shape) - elif field == 'is_difficult': - outs.append(sample['difficult']) - elif field == 'gt_box': - outs.append(sample['gt_bbox']) - elif field == 'gt_label': - outs.append(sample['gt_class']) - else: - outs.append(sample[field]) - - outs = tuple(outs) - - return outs - - -@register_op -class ArrangeTestSSD(BaseOperator): - """ - Transform dict to tuple format needed for training. - - Args: - is_mask (bool): whether to use include mask data - """ - - def __init__(self): - super(ArrangeTestSSD, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: (image) - """ - im = sample['image'] - im_id = sample['im_id'] - h = sample['h'] - w = sample['w'] - im_shape = np.array((h, w)) - outs = (im, im_id, im_shape) - return outs - - -@register_op -class ArrangeYOLO(BaseOperator): - """ - Transform dict to the tuple format needed for training. - """ - - def __init__(self): - super(ArrangeYOLO, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: - (image, gt_bbox, gt_class, gt_score, - is_crowd, im_info, gt_masks) - """ - im = sample['image'] - if len(sample['gt_bbox']) != len(sample['gt_class']): - raise ValueError("gt num mismatch: bbox and class.") - if len(sample['gt_bbox']) != len(sample['gt_score']): - raise ValueError("gt num mismatch: bbox and score.") - gt_bbox = np.zeros((50, 4), dtype=im.dtype) - gt_class = np.zeros((50, ), dtype=np.int32) - gt_score = np.zeros((50, ), dtype=im.dtype) - gt_num = min(50, len(sample['gt_bbox'])) - if gt_num > 0: - gt_bbox[:gt_num, :] = sample['gt_bbox'][:gt_num, :] - gt_class[:gt_num] = sample['gt_class'][:gt_num, 0] - gt_score[:gt_num] = sample['gt_score'][:gt_num, 0] - # parse [x1, y1, x2, y2] to [x, y, w, h] - gt_bbox[:, 2:4] = gt_bbox[:, 2:4] - gt_bbox[:, :2] - gt_bbox[:, :2] = gt_bbox[:, :2] + gt_bbox[:, 2:4] / 2. - outs = (im, gt_bbox, gt_class, gt_score) - return outs - - -@register_op -class ArrangeEvalYOLO(BaseOperator): - """ - Transform dict to the tuple format needed for evaluation. - """ - - def __init__(self): - super(ArrangeEvalYOLO, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: - (image, im_shape, im_id, gt_bbox, gt_class, - difficult) - """ - im = sample['image'] - if len(sample['gt_bbox']) != len(sample['gt_class']): - raise ValueError("gt num mismatch: bbox and class.") - im_id = sample['im_id'] - h = sample['h'] - w = sample['w'] - im_shape = np.array((h, w)) - gt_bbox = np.zeros((50, 4), dtype=im.dtype) - gt_class = np.zeros((50, ), dtype=np.int32) - difficult = np.zeros((50, ), dtype=np.int32) - gt_num = min(50, len(sample['gt_bbox'])) - if gt_num > 0: - gt_bbox[:gt_num, :] = sample['gt_bbox'][:gt_num, :] - gt_class[:gt_num] = sample['gt_class'][:gt_num, 0] - difficult[:gt_num] = sample['difficult'][:gt_num, 0] - outs = (im, im_shape, im_id, gt_bbox, gt_class, difficult) - return outs - - -@register_op -class ArrangeTestYOLO(BaseOperator): - """ - Transform dict to the tuple format needed for inference. - """ - - def __init__(self): - super(ArrangeTestYOLO, self).__init__() - - def __call__(self, sample, context=None): - """ - Args: - sample: a dict which contains image - info and annotation info. - context: a dict which contains additional info. - Returns: - sample: a tuple containing the following items: - (image, gt_bbox, gt_class, gt_score, is_crowd, - im_info, gt_masks) - """ - im = sample['image'] - im_id = sample['im_id'] - h = sample['h'] - w = sample['w'] - im_shape = np.array((h, w)) - outs = (im, im_shape, im_id) - return outs diff --git a/ppdet/data/transform/batch_operators.py b/ppdet/data/transform/batch_operators.py new file mode 100644 index 000000000..9cc88d520 --- /dev/null +++ b/ppdet/data/transform/batch_operators.py @@ -0,0 +1,166 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +try: + from collections.abc import Sequence +except Exception: + from collections import Sequence + +import logging +import cv2 +import numpy as np + +from .operators import register_op, BaseOperator + +logger = logging.getLogger(__name__) + + +@register_op +class PadBatch(BaseOperator): + """ + Pad a batch of samples so they can be divisible by a stride. + The layout of each image should be 'CHW'. + + Args: + pad_to_stride (int): If `pad_to_stride > 0`, pad zeros to ensure + height and width is divisible by `pad_to_stride`. + """ + + def __init__(self, pad_to_stride=0, use_padded_im_info=True): + super(PadBatch, self).__init__() + self.pad_to_stride = pad_to_stride + self.use_padded_im_info = use_padded_im_info + + def __call__(self, samples, context=None): + """ + Args: + samples (list): a batch of sample, each is dict. + """ + coarsest_stride = self.pad_to_stride + if coarsest_stride == 0: + return samples + max_shape = np.array([data['image'].shape for data in samples]).max( + axis=0) + + if coarsest_stride > 0: + max_shape[1] = int( + np.ceil(max_shape[1] / coarsest_stride) * coarsest_stride) + max_shape[2] = int( + np.ceil(max_shape[2] / coarsest_stride) * coarsest_stride) + + padding_batch = [] + for data in samples: + im = data['image'] + im_c, im_h, im_w = im.shape[:] + padding_im = np.zeros( + (im_c, max_shape[1], max_shape[2]), dtype=np.float32) + padding_im[:, :im_h, :im_w] = im + data['image'] = padding_im + if self.use_padded_im_info: + data['im_info'][:2] = max_shape[1:3] + return samples + + +@register_op +class RandomShape(BaseOperator): + """ + Randomly reshape a batch. If random_inter is True, also randomly + select one an interpolation algorithm [cv2.INTER_NEAREST, cv2.INTER_LINEAR, + cv2.INTER_AREA, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4]. If random_inter is + False, use cv2.INTER_NEAREST. + + Args: + sizes (list): list of int, random choose a size from these + random_inter (bool): whether to randomly interpolation, defalut true. + """ + + def __init__(self, sizes=[], random_inter=False): + super(RandomShape, self).__init__() + self.sizes = sizes + self.random_inter = random_inter + self.interps = [ + cv2.INTER_NEAREST, + cv2.INTER_LINEAR, + cv2.INTER_AREA, + cv2.INTER_CUBIC, + cv2.INTER_LANCZOS4, + ] if random_inter else [] + + def __call__(self, samples, context=None): + shape = np.random.choice(self.sizes) + method = np.random.choice(self.interps) if self.random_inter \ + else cv2.INTER_NEAREST + for i in range(len(samples)): + im = samples[i]['image'] + h, w = im.shape[:2] + scale_x = float(shape) / w + scale_y = float(shape) / h + im = cv2.resize( + im, None, None, fx=scale_x, fy=scale_y, interpolation=method) + samples[i]['image'] = im + return samples + + +@register_op +class PadMultiScaleTest(BaseOperator): + """ + Pad the image so they can be divisible by a stride for multi-scale testing. + + Args: + pad_to_stride (int): If `pad_to_stride > 0`, pad zeros to ensure + height and width is divisible by `pad_to_stride`. + """ + + def __init__(self, pad_to_stride=0): + super(PadMultiScaleTest, self).__init__() + self.pad_to_stride = pad_to_stride + + def __call__(self, samples, context=None): + coarsest_stride = self.pad_to_stride + if coarsest_stride == 0: + return samples + + batch_input = True + if not isinstance(samples, Sequence): + batch_input = False + samples = [samples] + if len(samples) != 1: + raise ValueError("Batch size must be 1 when using multiscale test, " + "but now batch size is {}".format(len(samples))) + for i in range(len(samples)): + sample = samples[i] + for k in sample.keys(): + # hard code + if k.startswith('image'): + im = sample[k] + im_c, im_h, im_w = im.shape + max_h = int( + np.ceil(im_h / coarsest_stride) * coarsest_stride) + max_w = int( + np.ceil(im_w / coarsest_stride) * coarsest_stride) + padding_im = np.zeros( + (im_c, max_h, max_w), dtype=np.float32) + + padding_im[:, :im_h, :im_w] = im + sample[k] = padding_im + info_name = 'im_info' if k == 'image' else 'im_info_' + k + # update im_info + sample[info_name][:2] = [max_h, max_w] + if not batch_input: + samples = samples[0] + return samples diff --git a/ppdet/data/transform/operators.py b/ppdet/data/transform/operators.py index 4b9c3d4b9..b3efe56d3 100644 --- a/ppdet/data/transform/operators.py +++ b/ppdet/data/transform/operators.py @@ -32,6 +32,7 @@ import logging import random import math import numpy as np + import cv2 from PIL import Image, ImageEnhance @@ -182,10 +183,10 @@ class MultiscaleTestResize(BaseOperator): base_name_list = ['image'] origin_ims['image'] = im if self.use_flip: - sample['flip_image'] = im[:, ::-1, :] - base_name_list.append('flip_image') - origin_ims['flip_image'] = sample['flip_image'] - im_info = [] + sample['image_flip'] = im[:, ::-1, :] + base_name_list.append('image_flip') + origin_ims['image_flip'] = sample['image_flip'] + for base_name in base_name_list: im_scale = float(self.origin_target_size) / float(im_size_min) # Prevent the biggest axis from being more than max_size @@ -203,8 +204,12 @@ class MultiscaleTestResize(BaseOperator): fx=im_scale_x, fy=im_scale_y, interpolation=self.interp) - im_info.extend([resize_h, resize_w, im_scale]) + sample[base_name] = im_resize + info_name = 'im_info' if base_name == 'image' else 'im_info_image_flip' + sample[base_name] = im_resize + sample[info_name] = np.array( + [resize_h, resize_w, im_scale], dtype=np.float32) for i, size in enumerate(self.target_size): im_scale = float(size) / float(im_size_min) if np.round(im_scale * im_size_max) > self.max_size: @@ -220,10 +225,15 @@ class MultiscaleTestResize(BaseOperator): fx=im_scale_x, fy=im_scale_y, interpolation=self.interp) - im_info.extend([resize_h, resize_w, im_scale]) + + im_info = [resize_h, resize_w, im_scale] + # hard-code here, must be consistent with + # ppdet/modeling/architectures/input_helper.py name = base_name + '_scale_' + str(i) + info_name = 'im_info_' + name sample[name] = im_resize - sample['im_info'] = np.array(im_info, dtype=np.float32) + sample[info_name] = np.array( + [resize_h, resize_w, im_scale], dtype=np.float32) return sample @@ -315,6 +325,7 @@ class ResizeImage(BaseOperator): raise TypeError( 'If you set max_size to cap the maximum size of image,' 'please set use_cv2 to True to resize the image.') + im = im.astype('uint8') im = Image.fromarray(im) im = im.resize((int(resize_w), int(resize_h)), self.interp) im = np.array(im) @@ -383,34 +394,44 @@ class RandomFlipImage(BaseOperator): sample: the image, bounding box and segmentation part in sample are flipped. """ - gt_bbox = sample['gt_bbox'] - im = sample['image'] - if not isinstance(im, np.ndarray): - raise TypeError("{}: image is not a numpy array.".format(self)) - if len(im.shape) != 3: - raise ImageError("{}: image is not 3-dimensional.".format(self)) - height, width, _ = im.shape - if np.random.uniform(0, 1) < self.prob: - im = im[:, ::-1, :] - if gt_bbox.shape[0] == 0: - return sample - oldx1 = gt_bbox[:, 0].copy() - oldx2 = gt_bbox[:, 2].copy() - if self.is_normalized: - gt_bbox[:, 0] = 1 - oldx2 - gt_bbox[:, 2] = 1 - oldx1 - else: - gt_bbox[:, 0] = width - oldx2 - 1 - gt_bbox[:, 2] = width - oldx1 - 1 - if gt_bbox.shape[0] != 0 and (gt_bbox[:, 2] < gt_bbox[:, 0]).all(): - m = "{}: invalid box, x2 should be greater than x1".format(self) - raise BboxError(m) - sample['gt_bbox'] = gt_bbox - if self.is_mask_flip and len(sample['gt_poly']) != 0: - sample['gt_poly'] = self.flip_segms(sample['gt_poly'], height, - width) - sample['flipped'] = True - sample['image'] = im + + samples = sample + batch_input = True + if not isinstance(samples, Sequence): + batch_input = False + samples = [samples] + for sample in samples: + gt_bbox = sample['gt_bbox'] + im = sample['image'] + if not isinstance(im, np.ndarray): + raise TypeError("{}: image is not a numpy array.".format(self)) + if len(im.shape) != 3: + raise ImageError("{}: image is not 3-dimensional.".format(self)) + height, width, _ = im.shape + if np.random.uniform(0, 1) < self.prob: + im = im[:, ::-1, :] + if gt_bbox.shape[0] == 0: + return sample + oldx1 = gt_bbox[:, 0].copy() + oldx2 = gt_bbox[:, 2].copy() + if self.is_normalized: + gt_bbox[:, 0] = 1 - oldx2 + gt_bbox[:, 2] = 1 - oldx1 + else: + gt_bbox[:, 0] = width - oldx2 - 1 + gt_bbox[:, 2] = width - oldx1 - 1 + if gt_bbox.shape[0] != 0 and ( + gt_bbox[:, 2] < gt_bbox[:, 0]).all(): + m = "{}: invalid box, x2 should be greater than x1".format( + self) + raise BboxError(m) + sample['gt_bbox'] = gt_bbox + if self.is_mask_flip and len(sample['gt_poly']) != 0: + sample['gt_poly'] = self.flip_segms(sample['gt_poly'], + height, width) + sample['flipped'] = True + sample['image'] = im + sample = samples if batch_input else samples[0] return sample @@ -444,22 +465,31 @@ class NormalizeImage(BaseOperator): 1.(optional) Scale the image to [0,1] 2. Each pixel minus mean and is divided by std """ - for k in sample.keys(): - if 'image' in k: - im = sample[k] - im = im.astype(np.float32, copy=False) - if self.is_channel_first: - mean = np.array(self.mean)[:, np.newaxis, np.newaxis] - std = np.array(self.std)[:, np.newaxis, np.newaxis] - else: - mean = np.array(self.mean)[np.newaxis, np.newaxis, :] - std = np.array(self.std)[np.newaxis, np.newaxis, :] - if self.is_scale: - im = im / 255.0 - im -= mean - im /= std - sample[k] = im - return sample + samples = sample + batch_input = True + if not isinstance(samples, Sequence): + batch_input = False + samples = [samples] + for sample in samples: + for k in sample.keys(): + # hard code + if k.startswith('image'): + im = sample[k] + im = im.astype(np.float32, copy=False) + if self.is_channel_first: + mean = np.array(self.mean)[:, np.newaxis, np.newaxis] + std = np.array(self.std)[:, np.newaxis, np.newaxis] + else: + mean = np.array(self.mean)[np.newaxis, np.newaxis, :] + std = np.array(self.std)[np.newaxis, np.newaxis, :] + if self.is_scale: + im = im / 255.0 + im -= mean + im /= std + sample[k] = im + if not batch_input: + samples = samples[0] + return samples @register_op @@ -899,17 +929,26 @@ class Permute(BaseOperator): raise TypeError("{}: input type is invalid.".format(self)) def __call__(self, sample, context=None): - assert 'image' in sample, "image data not found" - for k in sample.keys(): - if 'image' in k: - im = sample[k] - if self.channel_first: - im = np.swapaxes(im, 1, 2) - im = np.swapaxes(im, 1, 0) - if self.to_bgr: - im = im[[2, 1, 0], :, :] - sample[k] = im - return sample + samples = sample + batch_input = True + if not isinstance(samples, Sequence): + batch_input = False + samples = [samples] + for sample in samples: + assert 'image' in sample, "image data not found" + for k in sample.keys(): + # hard code + if k.startswith('image'): + im = sample[k] + if self.channel_first: + im = np.swapaxes(im, 1, 2) + im = np.swapaxes(im, 1, 0) + if self.to_bgr: + im = im[[2, 1, 0], :, :] + sample[k] = im + if not batch_input: + samples = samples[0] + return samples @register_op @@ -1233,6 +1272,7 @@ class RandomExpand(BaseOperator): sample['image'] = canvas if 'gt_bbox' in sample and len(sample['gt_bbox']) > 0: sample['gt_bbox'] += np.array([x, y] * 2, dtype=np.float32) + return sample @@ -1361,3 +1401,63 @@ class RandomCrop(BaseOperator): def _crop_image(self, img, crop): x1, y1, x2, y2 = crop return img[y1:y2, x1:x2, :] + + +@register_op +class PadBox(BaseOperator): + def __init__(self, num_max_boxes=50): + """ + Pad zeros to bboxes if number of bboxes is less than num_max_boxes. + Args: + num_max_boxes (int): the max number of bboxes + """ + self.num_max_boxes = num_max_boxes + super(PadBox, self).__init__() + + def __call__(self, sample, context=None): + assert 'gt_bbox' in sample + bbox = sample['gt_bbox'] + gt_num = min(self.num_max_boxes, len(bbox)) + num_max = self.num_max_boxes + fields = context['fields'] if context else [] + pad_bbox = np.zeros((num_max, 4), dtype=np.float32) + if gt_num > 0: + pad_bbox[:gt_num, :] = bbox[:gt_num, :] + sample['gt_bbox'] = pad_bbox + if 'gt_class' in fields: + pad_class = np.zeros((num_max), dtype=np.int32) + if gt_num > 0: + pad_class[:gt_num] = sample['gt_class'][:gt_num, 0] + sample['gt_class'] = pad_class + if 'gt_score' in fields: + pad_score = np.zeros((num_max), dtype=np.float32) + if gt_num > 0: + pad_score[:gt_num] = sample['gt_score'][:gt_num, 0] + sample['gt_score'] = pad_score + # in training, for example in op ExpandImage, + # the bbox and gt_class is expandded, but the difficult is not, + # so, judging by it's length + if 'is_difficult' in fields: + pad_diff = np.zeros((num_max), dtype=np.int32) + if gt_num > 0: + pad_diff[:gt_num] = sample['difficult'][:gt_num, 0] + sample['difficult'] = pad_diff + return sample + + +@register_op +class BboxXYXY2XYWH(BaseOperator): + """ + Convert bbox XYXY format to XYWH format. + """ + + def __init__(self): + super(BboxXYXY2XYWH, self).__init__() + + def __call__(self, sample, context=None): + assert 'gt_bbox' in sample + bbox = sample['gt_bbox'] + bbox[:, 2:4] = bbox[:, 2:4] - bbox[:, :2] + bbox[:, :2] = bbox[:, :2] + bbox[:, 2:4] / 2. + sample['gt_bbox'] = bbox + return sample diff --git a/ppdet/data/transform/post_map.py b/ppdet/data/transform/post_map.py deleted file mode 100644 index d556160e5..000000000 --- a/ppdet/data/transform/post_map.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import logging -import cv2 -import numpy as np - -logger = logging.getLogger(__name__) - - -def build_post_map(coarsest_stride=1, - is_padding=False, - random_shapes=[], - multi_scales=[], - use_padded_im_info=False, - enable_multiscale_test=False, - num_scale=1): - """ - Build a mapper for post-processing batches - - Args: - config (dict of parameters): - { - coarsest_stride (int): stride of the coarsest FPN level - is_padding (bool): whether to padding in minibatch - random_shapes (list of int): resize to image to random shapes, - [] for not resize. - multi_scales (list of int): resize image by random scales, - [] for not resize. - use_padded_im_info (bool): whether to update im_info after padding - enable_multiscale_test (bool): whether to use multiscale test. - num_scale (int) : the number of scales for multiscale test. - } - Returns: - a mapper function which accept one argument 'batch' and - return the processed result - """ - - def padding_minibatch(batch_data): - if len(batch_data) == 1 and coarsest_stride == 1: - return batch_data - max_shape = np.array([data[0].shape for data in batch_data]).max(axis=0) - if coarsest_stride > 1: - max_shape[1] = int( - np.ceil(max_shape[1] / coarsest_stride) * coarsest_stride) - max_shape[2] = int( - np.ceil(max_shape[2] / coarsest_stride) * coarsest_stride) - padding_batch = [] - for data in batch_data: - im_c, im_h, im_w = data[0].shape[:] - padding_im = np.zeros( - (im_c, max_shape[1], max_shape[2]), dtype=np.float32) - padding_im[:, :im_h, :im_w] = data[0] - if use_padded_im_info: - data[1][:2] = max_shape[1:3] - padding_batch.append((padding_im, ) + data[1:]) - return padding_batch - - def padding_multiscale_test(batch_data): - if len(batch_data) != 1: - raise NotImplementedError( - "Batch size must be 1 when using multiscale test, but now batch size is {}". - format(len(batch_data))) - if coarsest_stride > 1: - padding_batch = [] - padding_images = [] - data = batch_data[0] - for i, input in enumerate(data): - if i < num_scale: - im_c, im_h, im_w = input.shape - max_h = int( - np.ceil(im_h / coarsest_stride) * coarsest_stride) - max_w = int( - np.ceil(im_w / coarsest_stride) * coarsest_stride) - padding_im = np.zeros( - (im_c, max_h, max_w), dtype=np.float32) - padding_im[:, :im_h, :im_w] = input - data[num_scale][3 * i:3 * i + 2] = [max_h, max_w] - padding_batch.append(padding_im) - else: - padding_batch.append(input) - return [tuple(padding_batch)] - # no need to padding - return batch_data - - def random_shape(batch_data): - # For YOLO: gt_bbox is normalized, is scale invariant. - shape = np.random.choice(random_shapes) - scaled_batch = [] - h, w = batch_data[0][0].shape[1:3] - scale_x = float(shape) / w - scale_y = float(shape) / h - for data in batch_data: - im = cv2.resize( - data[0].transpose((1, 2, 0)), - None, - None, - fx=scale_x, - fy=scale_y, - interpolation=cv2.INTER_NEAREST) - scaled_batch.append((im.transpose(2, 0, 1), ) + data[1:]) - return scaled_batch - - def multi_scale_resize(batch_data): - # For RCNN: image shape in record in im_info. - scale = np.random.choice(multi_scales) - scaled_batch = [] - for data in batch_data: - im = cv2.resize( - data[0].transpose((1, 2, 0)), - None, - None, - fx=scale, - fy=scale, - interpolation=cv2.INTER_NEAREST) - im_info = [im.shape[:2], scale] - scaled_batch.append((im.transpose(2, 0, 1), im_info) + data[2:]) - return scaled_batch - - def _mapper(batch_data): - try: - if is_padding: - batch_data = padding_minibatch(batch_data) - if len(random_shapes) > 0: - batch_data = random_shape(batch_data) - if len(multi_scales) > 0: - batch_data = multi_scale_resize(batch_data) - if enable_multiscale_test: - batch_data = padding_multiscale_test(batch_data) - except Exception as e: - errmsg = "post-process failed with error: " + str(e) - logger.warn(errmsg) - raise e - - return batch_data - - return _mapper diff --git a/ppdet/data/transform/transformer.py b/ppdet/data/transform/transformer.py deleted file mode 100644 index ad8845677..000000000 --- a/ppdet/data/transform/transformer.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np -import functools -import collections -from ..dataset import Dataset - - -class ProxiedDataset(Dataset): - """proxy method called to 'self._ds' when if not defined""" - - def __init__(self, ds): - super(ProxiedDataset, self).__init__() - self._ds = ds - methods = filter(lambda k: not k.startswith('_'), - Dataset.__dict__.keys()) - for m in methods: - func = functools.partial(self._proxy_method, getattr(self, m)) - setattr(self, m, func) - - def _proxy_method(self, func, *args, **kwargs): - """ - proxy call to 'func', if not available then call self._ds.xxx - whose name is the same with func.__name__ - """ - method = func.__name__ - try: - return func(*args, **kwargs) - except NotImplementedError: - ds_func = getattr(self._ds, method) - return ds_func(*args, **kwargs) - - -class MappedDataset(ProxiedDataset): - def __init__(self, ds, mapper): - super(MappedDataset, self).__init__(ds) - self._ds = ds - self._mapper = mapper - - def next(self): - sample = self._ds.next() - return self._mapper(sample) - - -class BatchedDataset(ProxiedDataset): - """ - Batching samples - - Args: - ds (instance of Dataset): dataset to be batched - batchsize (int): sample number for each batch - drop_last (bool): drop last samples when not enough for one batch - drop_empty (bool): drop samples which have empty field - """ - - def __init__(self, ds, batchsize, drop_last=False, drop_empty=True): - super(BatchedDataset, self).__init__(ds) - self._batchsz = batchsize - self._drop_last = drop_last - self._drop_empty = drop_empty - - def next(self): - """proxy to self._ds.next""" - - def empty(x): - if isinstance(x, np.ndarray) and x.size == 0: - return True - elif isinstance(x, collections.Sequence) and len(x) == 0: - return True - else: - return False - - def has_empty(items): - if any(x is None for x in items): - return True - if any(empty(x) for x in items): - return True - return False - - batch = [] - for _ in range(self._batchsz): - try: - out = self._ds.next() - while self._drop_empty and has_empty(out): - out = self._ds.next() - batch.append(out) - except StopIteration: - if not self._drop_last and len(batch) > 0: - return batch - else: - raise StopIteration - return batch diff --git a/ppdet/modeling/architectures/blazeface.py b/ppdet/modeling/architectures/blazeface.py index cc9a2bb33..0a02e70ce 100644 --- a/ppdet/modeling/architectures/blazeface.py +++ b/ppdet/modeling/architectures/blazeface.py @@ -17,8 +17,9 @@ from __future__ import division from __future__ import print_function import numpy as np -from paddle import fluid +from collections import OrderedDict +from paddle import fluid from paddle.fluid.param_attr import ParamAttr from paddle.fluid.regularizer import L2Decay @@ -74,8 +75,8 @@ class BlazeFace(object): def build(self, feed_vars, mode='train'): im = feed_vars['image'] if mode == 'train': - gt_box = feed_vars['gt_box'] - gt_label = feed_vars['gt_label'] + gt_bbox = feed_vars['gt_bbox'] + gt_class = feed_vars['gt_class'] body_feats = self.backbone(im) locs, confs, box, box_var = self._multi_box_head( @@ -88,8 +89,8 @@ class BlazeFace(object): loss = fluid.layers.ssd_loss( locs, confs, - gt_box, - gt_label, + gt_bbox, + gt_class, box, box_var, overlap_threshold=0.35, @@ -169,6 +170,38 @@ class BlazeFace(object): box_vars = fluid.layers.concat(vars) return face_mbox_loc, face_mbox_conf, prior_boxes, box_vars + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'im_shape': {'shape': [None, 3], 'dtype': 'int32', 'lod_level': 0}, + } + # yapf: enable + return inputs_def + + def build_inputs( + self, + image_shape=[3, None, None], + fields=['image', 'im_id', 'gt_bbox', 'gt_class'], # for train + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + feed_vars = OrderedDict([(key, fluid.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/cascade_mask_rcnn.py b/ppdet/modeling/architectures/cascade_mask_rcnn.py index f77ee6275..e04b9af3e 100644 --- a/ppdet/modeling/architectures/cascade_mask_rcnn.py +++ b/ppdet/modeling/architectures/cascade_mask_rcnn.py @@ -17,12 +17,15 @@ from __future__ import division from __future__ import print_function from collections import OrderedDict +import copy import paddle.fluid as fluid from ppdet.experimental import mixed_precision_global_state from ppdet.core.workspace import register +from .input_helper import multiscale_def + __all__ = ['CascadeMaskRCNN'] @@ -82,7 +85,7 @@ class CascadeMaskRCNN(object): def build(self, feed_vars, mode='train'): if mode == 'train': required_fields = [ - 'gt_label', 'gt_box', 'gt_mask', 'is_crowd', 'im_info' + 'gt_class', 'gt_bbox', 'gt_mask', 'is_crowd', 'im_info' ] else: required_fields = ['im_shape', 'im_info'] @@ -90,7 +93,7 @@ class CascadeMaskRCNN(object): im = feed_vars['image'] if mode == 'train': - gt_box = feed_vars['gt_box'] + gt_bbox = feed_vars['gt_bbox'] is_crowd = feed_vars['is_crowd'] im_info = feed_vars['im_info'] @@ -116,7 +119,7 @@ class CascadeMaskRCNN(object): rpn_rois = self.rpn_head.get_proposals(body_feats, im_info, mode=mode) if mode == 'train': - rpn_loss = self.rpn_head.get_loss(im_info, gt_box, is_crowd) + rpn_loss = self.rpn_head.get_loss(im_info, gt_bbox, is_crowd) else: if self.rpn_only: im_scale = fluid.layers.slice( @@ -174,7 +177,7 @@ class CascadeMaskRCNN(object): mask_rois, roi_has_mask_int32, mask_int32 = self.mask_assigner( rois=rois, - gt_classes=feed_vars['gt_label'], + gt_classes=feed_vars['gt_class'], is_crowd=feed_vars['is_crowd'], gt_segms=feed_vars['gt_mask'], im_info=feed_vars['im_info'], @@ -204,25 +207,16 @@ class CascadeMaskRCNN(object): required_fields = ['image', 'im_info'] self._input_check(required_fields, feed_vars) - ims = [] - for k in feed_vars.keys(): - if 'image' in k: - ims.append(feed_vars[k]) result = {} - if not mask_branch: assert 'im_shape' in feed_vars, \ "{} has no im_shape field".format(feed_vars) result.update(feed_vars) - for i, im in enumerate(ims): - im_info = fluid.layers.slice( - input=feed_vars['im_info'], - axes=[1], - starts=[3 * i], - ends=[3 * i + 3]) + for i in range(len(self.im_info_names) // 2): + im = feed_vars[self.im_info_names[2 * i]] + im_info = feed_vars[self.im_info_names[2 * i + 1]] body_feats = self.backbone(im) - result.update(body_feats) # FPN if self.fpn is not None: @@ -282,7 +276,6 @@ class CascadeMaskRCNN(object): else: mask_name = 'mask_pred_' + str(i) bbox_pred = feed_vars['bbox'] - result.update({im.name: im}) if 'flip' in im.name: mask_name += '_flip' bbox_pred = feed_vars['bbox_flip'] @@ -372,6 +365,65 @@ class CascadeMaskRCNN(object): return refined_bbox + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_info': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'im_shape': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_crowd': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'gt_mask': {'shape': [None, 2], 'dtype': 'float32', 'lod_level': 3}, # polygon coordinates + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs(self, + image_shape=[3, None, None], + fields=[ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', + 'is_crowd', 'gt_mask' + ], + multi_scale=False, + num_scales=-1, + use_flip=None, + use_dataloader=True, + iterable=False, + mask_branch=False): + inputs_def = self._inputs_def(image_shape) + fields = copy.deepcopy(fields) + if multi_scale: + ms_def, ms_fields = multiscale_def(image_shape, num_scales, + use_flip) + inputs_def.update(ms_def) + fields += ms_fields + self.im_info_names = ['image', 'im_info'] + ms_fields + if mask_branch: + box_fields = ['bbox', 'bbox_flip'] if use_flip else ['bbox'] + for key in box_fields: + inputs_def[key] = { + 'shape': [6], + 'dtype': 'float32', + 'lod_level': 1 + } + fields += box_fields + feed_vars = OrderedDict([(key, fluid.layers.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + use_dataloader = use_dataloader and not mask_branch + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/cascade_rcnn.py b/ppdet/modeling/architectures/cascade_rcnn.py index b80a8d7f6..5942a445d 100644 --- a/ppdet/modeling/architectures/cascade_rcnn.py +++ b/ppdet/modeling/architectures/cascade_rcnn.py @@ -16,12 +16,14 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import copy from collections import OrderedDict import paddle.fluid as fluid from ppdet.experimental import mixed_precision_global_state from ppdet.core.workspace import register +from .input_helper import multiscale_def __all__ = ['CascadeRCNN'] @@ -75,7 +77,7 @@ class CascadeRCNN(object): def build(self, feed_vars, mode='train'): if mode == 'train': - required_fields = ['gt_label', 'gt_box', 'is_crowd', 'im_info'] + required_fields = ['gt_class', 'gt_bbox', 'is_crowd', 'im_info'] else: required_fields = ['im_shape', 'im_info'] self._input_check(required_fields, feed_vars) @@ -84,7 +86,7 @@ class CascadeRCNN(object): im_info = feed_vars['im_info'] if mode == 'train': - gt_box = feed_vars['gt_box'] + gt_bbox = feed_vars['gt_bbox'] is_crowd = feed_vars['is_crowd'] mixed_precision_enabled = mixed_precision_global_state() is not None @@ -108,7 +110,9 @@ class CascadeRCNN(object): rpn_rois = self.rpn_head.get_proposals(body_feats, im_info, mode=mode) if mode == 'train': - rpn_loss = self.rpn_head.get_loss(im_info, gt_box, is_crowd) + #fluid.layers.Print(gt_bbox) + #fluid.layers.Print(is_crowd) + rpn_loss = self.rpn_head.get_loss(im_info, gt_bbox, is_crowd) else: if self.rpn_only: im_scale = fluid.layers.slice( @@ -171,19 +175,14 @@ class CascadeRCNN(object): def build_multi_scale(self, feed_vars): required_fields = ['image', 'im_shape', 'im_info'] self._input_check(required_fields, feed_vars) - ims = [] - for k in feed_vars.keys(): - if 'image' in k: - ims.append(feed_vars[k]) + result = {} - result.update(feed_vars) - for i, im in enumerate(ims): - im_info = fluid.layers.slice( - input=feed_vars['im_info'], - axes=[1], - starts=[3 * i], - ends=[3 * i + 3]) - im_shape = feed_vars['im_shape'] + im_shape = feed_vars['im_shape'] + result['im_shape'] = im_shape + + for i in range(len(self.im_info_names) // 2): + im = feed_vars[self.im_info_names[2 * i]] + im_info = feed_vars[self.im_info_names[2 * i + 1]] # backbone body_feats = self.backbone(im) @@ -277,6 +276,54 @@ class CascadeRCNN(object): return refined_bbox + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_info': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_shape': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_crowd': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs(self, + image_shape=[3, None, None], + fields=[ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', + 'is_crowd' + ], + multi_scale=False, + num_scales=-1, + use_flip=None, + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + fields = copy.deepcopy(fields) + if multi_scale: + ms_def, ms_fields = multiscale_def(image_shape, num_scales, + use_flip) + inputs_def.update(ms_def) + fields += ms_fields + self.im_info_names = ['image', 'im_info'] + ms_fields + + feed_vars = OrderedDict([(key, fluid.layers.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/cascade_rcnn_cls_aware.py b/ppdet/modeling/architectures/cascade_rcnn_cls_aware.py index bbc6fb6cf..e52ee831e 100644 --- a/ppdet/modeling/architectures/cascade_rcnn_cls_aware.py +++ b/ppdet/modeling/architectures/cascade_rcnn_cls_aware.py @@ -19,6 +19,9 @@ from __future__ import print_function import numpy as np import sys +from collections import OrderedDict +import copy + import paddle.fluid as fluid from ppdet.core.workspace import register @@ -48,14 +51,14 @@ class CascadeRCNNClsAware(object): 'bbox_head' ] - def __init__(self, - backbone, - rpn_head, - roi_extractor='FPNRoIAlign', - bbox_head='CascadeBBoxHead', - bbox_assigner='CascadeBBoxAssigner', - fpn='FPN', - ): + def __init__( + self, + backbone, + rpn_head, + roi_extractor='FPNRoIAlign', + bbox_head='CascadeBBoxHead', + bbox_assigner='CascadeBBoxAssigner', + fpn='FPN', ): super(CascadeRCNNClsAware, self).__init__() assert fpn is not None, "cascade RCNN requires FPN" self.backbone = backbone @@ -78,9 +81,9 @@ class CascadeRCNNClsAware(object): im = feed_vars['image'] im_info = feed_vars['im_info'] if mode == 'train': - gt_box = feed_vars['gt_box'] + gt_bbox = feed_vars['gt_bbox'] is_crowd = feed_vars['is_crowd'] - gt_label = feed_vars['gt_label'] + gt_class = feed_vars['gt_class'] else: im_shape = feed_vars['im_shape'] @@ -95,7 +98,7 @@ class CascadeRCNNClsAware(object): rpn_rois = self.rpn_head.get_proposals(body_feats, im_info, mode=mode) if mode == 'train': - rpn_loss = self.rpn_head.get_loss(im_info, gt_box, is_crowd) + rpn_loss = self.rpn_head.get_loss(im_info, gt_bbox, is_crowd) proposal_list = [] roi_feat_list = [] @@ -103,10 +106,11 @@ class CascadeRCNNClsAware(object): rcnn_target_list = [] bbox_pred = None - + self.cascade_var_v = [] for stage in range(3): - var_v = np.array(self.cascade_bbox_reg_weights[stage], dtype="float32") + var_v = np.array( + self.cascade_bbox_reg_weights[stage], dtype="float32") prior_box_var = fluid.layers.create_tensor(dtype="float32") fluid.layers.assign(input=var_v, output=prior_box_var) self.cascade_var_v.append(prior_box_var) @@ -124,42 +128,37 @@ class CascadeRCNNClsAware(object): outs = self.bbox_assigner( input_rois=pool_rois, feed_vars=feed_vars, curr_stage=stage) pool_rois = outs[0] - rcnn_target_list.append( outs ) - + rcnn_target_list.append(outs) + # extract roi features roi_feat = self.roi_extractor(body_feats, pool_rois, spatial_scale) roi_feat_list.append(roi_feat) - + # bbox head cls_score, bbox_pred = self.bbox_head.get_output( roi_feat, cls_agnostic_bbox_reg=self.bbox_head.num_classes, wb_scalar=1.0 / self.cascade_rcnn_loss_weight[stage], - name='_' + str(stage + 1) ) + name='_' + str(stage + 1)) cls_prob = fluid.layers.softmax(cls_score, use_cudnn=False) - + decoded_box, decoded_assign_box = fluid.layers.box_decoder_and_assign( - pool_rois, - self.cascade_var_v[stage], - bbox_pred, - cls_prob, - self.bbox_clip) - + pool_rois, self.cascade_var_v[stage], bbox_pred, cls_prob, + self.bbox_clip) + if mode == "train": decoded_box.stop_gradient = True decoded_assign_box.stop_gradient = True else: - self.cascade_cls_prob.append( cls_prob ) + self.cascade_cls_prob.append(cls_prob) self.cascade_decoded_box.append(decoded_box) - + rcnn_pred_list.append((cls_score, bbox_pred)) - - + # out loop if mode == 'train': - loss = self.bbox_head.get_loss(rcnn_pred_list, - rcnn_target_list, + loss = self.bbox_head.get_loss(rcnn_pred_list, rcnn_target_list, self.cascade_rcnn_loss_weight) loss.update(rpn_loss) total_loss = fluid.layers.sum(list(loss.values())) @@ -167,12 +166,46 @@ class CascadeRCNNClsAware(object): return loss else: pred = self.bbox_head.get_prediction_cls_aware( - im_info, im_shape, - self.cascade_cls_prob, - self.cascade_decoded_box, - self.cascade_bbox_reg_weights) + im_info, im_shape, self.cascade_cls_prob, + self.cascade_decoded_box, self.cascade_bbox_reg_weights) return pred - + + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_info': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'im_shape': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_crowd': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs(self, + image_shape=[3, None, None], + fields=[ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', + 'is_crowd', 'gt_mask' + ], + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + feed_vars = OrderedDict([(key, fluid.layers.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/faceboxes.py b/ppdet/modeling/architectures/faceboxes.py index 194b3a7e8..ba9d3e09b 100644 --- a/ppdet/modeling/architectures/faceboxes.py +++ b/ppdet/modeling/architectures/faceboxes.py @@ -17,8 +17,9 @@ from __future__ import division from __future__ import print_function import numpy as np -from paddle import fluid +from collections import OrderedDict +from paddle import fluid from paddle.fluid.param_attr import ParamAttr from paddle.fluid.regularizer import L2Decay @@ -66,8 +67,8 @@ class FaceBoxes(object): def build(self, feed_vars, mode='train'): im = feed_vars['image'] if mode == 'train': - gt_box = feed_vars['gt_box'] - gt_label = feed_vars['gt_label'] + gt_bbox = feed_vars['gt_bbox'] + gt_class = feed_vars['gt_class'] body_feats = self.backbone(im) locs, confs, box, box_var = self._multi_box_head( @@ -77,8 +78,8 @@ class FaceBoxes(object): loss = fluid.layers.ssd_loss( locs, confs, - gt_box, - gt_label, + gt_bbox, + gt_class, box, box_var, overlap_threshold=0.35, @@ -141,6 +142,38 @@ class FaceBoxes(object): box_vars = fluid.layers.concat(vars) return face_mbox_loc, face_mbox_conf, prior_boxes, box_vars + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'im_shape': {'shape': [None, 3], 'dtype': 'int32', 'lod_level': 0}, + } + # yapf: enable + return inputs_def + + def build_inputs( + self, + image_shape=[3, None, None], + fields=['image', 'im_id', 'gt_bbox', 'gt_class'], # for train + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + feed_vars = OrderedDict([(key, fluid.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/faster_rcnn.py b/ppdet/modeling/architectures/faster_rcnn.py index e0ef7355c..c1730a1d7 100644 --- a/ppdet/modeling/architectures/faster_rcnn.py +++ b/ppdet/modeling/architectures/faster_rcnn.py @@ -17,12 +17,15 @@ from __future__ import division from __future__ import print_function from collections import OrderedDict +import copy from paddle import fluid from ppdet.experimental import mixed_precision_global_state from ppdet.core.workspace import register +from .input_helper import multiscale_def + __all__ = ['FasterRCNN'] @@ -64,7 +67,7 @@ class FasterRCNN(object): def build(self, feed_vars, mode='train'): if mode == 'train': - required_fields = ['gt_label', 'gt_box', 'is_crowd', 'im_info'] + required_fields = ['gt_class', 'gt_bbox', 'is_crowd', 'im_info'] else: required_fields = ['im_shape', 'im_info'] self._input_check(required_fields, feed_vars) @@ -72,7 +75,7 @@ class FasterRCNN(object): im = feed_vars['image'] im_info = feed_vars['im_info'] if mode == 'train': - gt_box = feed_vars['gt_box'] + gt_bbox = feed_vars['gt_bbox'] is_crowd = feed_vars['is_crowd'] else: im_shape = feed_vars['im_shape'] @@ -97,15 +100,15 @@ class FasterRCNN(object): rois = self.rpn_head.get_proposals(body_feats, im_info, mode=mode) if mode == 'train': - rpn_loss = self.rpn_head.get_loss(im_info, gt_box, is_crowd) + rpn_loss = self.rpn_head.get_loss(im_info, gt_bbox, is_crowd) # sampled rpn proposals - for var in ['gt_label', 'is_crowd', 'gt_box', 'im_info']: + for var in ['gt_class', 'is_crowd', 'gt_bbox', 'im_info']: assert var in feed_vars, "{} has no {}".format(feed_vars, var) outs = self.bbox_assigner( rpn_rois=rois, - gt_classes=feed_vars['gt_label'], + gt_classes=feed_vars['gt_class'], is_crowd=feed_vars['is_crowd'], - gt_boxes=feed_vars['gt_box'], + gt_boxes=feed_vars['gt_bbox'], im_info=feed_vars['im_info']) rois = outs[0] @@ -145,21 +148,14 @@ class FasterRCNN(object): def build_multi_scale(self, feed_vars): required_fields = ['image', 'im_info', 'im_shape'] self._input_check(required_fields, feed_vars) - ims = [] - for k in feed_vars.keys(): - if 'image' in k: - ims.append(feed_vars[k]) + result = {} - result.update(feed_vars) - for i, im in enumerate(ims): - im_info = fluid.layers.slice( - input=feed_vars['im_info'], - axes=[1], - starts=[3 * i], - ends=[3 * i + 3]) - im_shape = feed_vars['im_shape'] + im_shape = feed_vars['im_shape'] + result['im_shape'] = im_shape + for i in range(len(self.im_info_names) // 2): + im = feed_vars[self.im_info_names[2 * i]] + im_info = feed_vars[self.im_info_names[2 * i + 1]] body_feats = self.backbone(im) - result.update(body_feats) body_feat_names = list(body_feats.keys()) if self.fpn is not None: @@ -192,6 +188,54 @@ class FasterRCNN(object): assert var in feed_vars, \ "{} has no {} field".format(feed_vars, var) + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_info': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'im_shape': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_crowd': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs( + self, + image_shape=[3, None, None], + fields=[ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd' + ], # for train + multi_scale=False, + num_scales=-1, + use_flip=None, + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + fields = copy.deepcopy(fields) + if multi_scale: + ms_def, ms_fields = multiscale_def(image_shape, num_scales, + use_flip) + inputs_def.update(ms_def) + fields += ms_fields + self.im_info_names = ['image', 'im_info'] + ms_fields + + feed_vars = OrderedDict([(key, fluid.layers.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/input_helper.py b/ppdet/modeling/architectures/input_helper.py new file mode 100644 index 000000000..691f069ed --- /dev/null +++ b/ppdet/modeling/architectures/input_helper.py @@ -0,0 +1,44 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def multiscale_def(image_shape, num_scale, use_flip=True): + base_name_list = ['image'] + multiscale_def = {} + ms_def_names = [] + if use_flip: + num_scale //= 2 + base_name_list.append('image_flip') + multiscale_def['im_info_image_flip'] = { + 'shape': [None, 3], + 'dtype': 'float32', + 'lod_level': 0 + } + for base_name in base_name_list: + for i in range(0, num_scale - 1): + name = base_name + '_scale_' + str(i) + multiscale_def[name] = { + 'shape': [None] + image_shape, + 'dtype': 'float32', + 'lod_level': 0 + } + im_info_name = 'im_info_' + name + multiscale_def[im_info_name] = { + 'shape': [None, 3], + 'dtype': 'float32', + 'lod_level': 0 + } + ms_def_names.append(name) + ms_def_names.append(im_info_name) + return multiscale_def, ms_def_names diff --git a/ppdet/modeling/architectures/mask_rcnn.py b/ppdet/modeling/architectures/mask_rcnn.py index 715f3efa9..9e068531e 100644 --- a/ppdet/modeling/architectures/mask_rcnn.py +++ b/ppdet/modeling/architectures/mask_rcnn.py @@ -17,12 +17,15 @@ from __future__ import division from __future__ import print_function from collections import OrderedDict +import copy import paddle.fluid as fluid from ppdet.experimental import mixed_precision_global_state from ppdet.core.workspace import register +from .input_helper import multiscale_def + __all__ = ['MaskRCNN'] @@ -71,7 +74,7 @@ class MaskRCNN(object): def build(self, feed_vars, mode='train'): if mode == 'train': required_fields = [ - 'gt_label', 'gt_box', 'gt_mask', 'is_crowd', 'im_info' + 'gt_class', 'gt_bbox', 'gt_mask', 'is_crowd', 'im_info' ] else: required_fields = ['im_shape', 'im_info'] @@ -101,14 +104,14 @@ class MaskRCNN(object): rois = self.rpn_head.get_proposals(body_feats, im_info, mode=mode) if mode == 'train': - rpn_loss = self.rpn_head.get_loss(im_info, feed_vars['gt_box'], + rpn_loss = self.rpn_head.get_loss(im_info, feed_vars['gt_bbox'], feed_vars['is_crowd']) outs = self.bbox_assigner( rpn_rois=rois, - gt_classes=feed_vars['gt_label'], + gt_classes=feed_vars['gt_class'], is_crowd=feed_vars['is_crowd'], - gt_boxes=feed_vars['gt_box'], + gt_boxes=feed_vars['gt_bbox'], im_info=feed_vars['im_info']) rois = outs[0] labels_int32 = outs[1] @@ -124,7 +127,7 @@ class MaskRCNN(object): mask_rois, roi_has_mask_int32, mask_int32 = self.mask_assigner( rois=rois, - gt_classes=feed_vars['gt_label'], + gt_classes=feed_vars['gt_class'], is_crowd=feed_vars['is_crowd'], gt_segms=feed_vars['gt_mask'], im_info=feed_vars['im_info'], @@ -160,25 +163,16 @@ class MaskRCNN(object): required_fields = ['image', 'im_info'] self._input_check(required_fields, feed_vars) - ims = [] - for k in feed_vars.keys(): - if 'image' in k: - ims.append(feed_vars[k]) result = {} - if not mask_branch: assert 'im_shape' in feed_vars, \ "{} has no im_shape field".format(feed_vars) result.update(feed_vars) - for i, im in enumerate(ims): - im_info = fluid.layers.slice( - input=feed_vars['im_info'], - axes=[1], - starts=[3 * i], - ends=[3 * i + 3]) + for i in range(len(self.im_info_names) // 2): + im = feed_vars[self.im_info_names[2 * i]] + im_info = feed_vars[self.im_info_names[2 * i + 1]] body_feats = self.backbone(im) - result.update(body_feats) # FPN if self.fpn is not None: @@ -205,7 +199,7 @@ class MaskRCNN(object): else: mask_name = 'mask_pred_' + str(i) bbox_pred = feed_vars['bbox'] - result.update({im.name: im}) + #result.update({im.name: im}) if 'flip' in im.name: mask_name += '_flip' bbox_pred = feed_vars['bbox_flip'] @@ -223,12 +217,12 @@ class MaskRCNN(object): im_shape, spatial_scale, bbox_pred=None): - if self.fpn is None: - last_feat = body_feats[list(body_feats.keys())[-1]] - roi_feat = self.roi_extractor(last_feat, rois) - else: - roi_feat = self.roi_extractor(body_feats, rois, spatial_scale) if not bbox_pred: + if self.fpn is None: + last_feat = body_feats[list(body_feats.keys())[-1]] + roi_feat = self.roi_extractor(last_feat, rois) + else: + roi_feat = self.roi_extractor(body_feats, rois, spatial_scale) bbox_pred = self.bbox_head.get_prediction(roi_feat, rois, im_info, im_shape) bbox_pred = bbox_pred['bbox'] @@ -258,6 +252,7 @@ class MaskRCNN(object): mask_rois = bbox * im_scale if self.fpn is None: + last_feat = body_feats[list(body_feats.keys())[-1]] mask_feat = self.roi_extractor(last_feat, mask_rois) mask_feat = self.bbox_head.get_head_feat(mask_feat) else: @@ -273,6 +268,65 @@ class MaskRCNN(object): assert var in feed_vars, \ "{} has no {} field".format(feed_vars, var) + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_info': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'im_shape': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_crowd': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'gt_mask': {'shape': [None, 2], 'dtype': 'float32', 'lod_level': 3}, # polygon coordinates + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs(self, + image_shape=[3, None, None], + fields=[ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', + 'is_crowd', 'gt_mask' + ], + multi_scale=False, + num_scales=-1, + use_flip=None, + use_dataloader=True, + iterable=False, + mask_branch=False): + inputs_def = self._inputs_def(image_shape) + fields = copy.deepcopy(fields) + if multi_scale: + ms_def, ms_fields = multiscale_def(image_shape, num_scales, + use_flip) + inputs_def.update(ms_def) + fields += ms_fields + self.im_info_names = ['image', 'im_info'] + ms_fields + if mask_branch: + box_fields = ['bbox', 'bbox_flip'] if use_flip else ['bbox'] + for key in box_fields: + inputs_def[key] = { + 'shape': [6], + 'dtype': 'float32', + 'lod_level': 1 + } + fields += box_fields + feed_vars = OrderedDict([(key, fluid.layers.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + use_dataloader = use_dataloader and not mask_branch + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/retinanet.py b/ppdet/modeling/architectures/retinanet.py index 4ce5ac500..00478da92 100644 --- a/ppdet/modeling/architectures/retinanet.py +++ b/ppdet/modeling/architectures/retinanet.py @@ -50,8 +50,8 @@ class RetinaNet(object): im = feed_vars['image'] im_info = feed_vars['im_info'] if mode == 'train': - gt_box = feed_vars['gt_box'] - gt_label = feed_vars['gt_label'] + gt_bbox = feed_vars['gt_bbox'] + gt_class = feed_vars['gt_class'] is_crowd = feed_vars['is_crowd'] mixed_precision_enabled = mixed_precision_global_state() is not None @@ -73,7 +73,7 @@ class RetinaNet(object): # retinanet head if mode == 'train': loss = self.retina_head.get_loss(body_feats, spatial_scale, im_info, - gt_box, gt_label, is_crowd) + gt_bbox, gt_class, is_crowd) total_loss = fluid.layers.sum(list(loss.values())) loss.update({'loss': total_loss}) return loss @@ -82,6 +82,43 @@ class RetinaNet(object): im_info) return pred + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_info': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'im_shape': {'shape': [None, 3], 'dtype': 'float32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_crowd': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs( + self, + image_shape=[3, None, None], + fields=[ + 'image', 'im_info', 'im_id', 'gt_bbox', 'gt_class', 'is_crowd' + ], # for-train + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + feed_vars = OrderedDict([(key, fluid.layers.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/ssd.py b/ppdet/modeling/architectures/ssd.py index e899075f2..de7dd461d 100644 --- a/ppdet/modeling/architectures/ssd.py +++ b/ppdet/modeling/architectures/ssd.py @@ -59,8 +59,8 @@ class SSD(object): def build(self, feed_vars, mode='train'): im = feed_vars['image'] if mode == 'train' or mode == 'eval': - gt_box = feed_vars['gt_box'] - gt_label = feed_vars['gt_label'] + gt_bbox = feed_vars['gt_bbox'] + gt_class = feed_vars['gt_class'] mixed_precision_enabled = mixed_precision_global_state() is not None # cast inputs to FP16 @@ -82,7 +82,7 @@ class SSD(object): inputs=body_feats, image=im, num_classes=self.num_classes) if mode == 'train': - loss = fluid.layers.ssd_loss(locs, confs, gt_box, gt_label, box, + loss = fluid.layers.ssd_loss(locs, confs, gt_bbox, gt_class, box, box_var) loss = fluid.layers.reduce_sum(loss) return {'loss': loss} @@ -90,6 +90,39 @@ class SSD(object): pred = self.output_decoder(locs, confs, box, box_var) return {'bbox': pred} + def _inputs_def(self, image_shape): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, 4], 'dtype': 'float32', 'lod_level': 1}, + 'gt_class': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + 'im_shape': {'shape': [None, 3], 'dtype': 'int32', 'lod_level': 0}, + 'is_difficult': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 1}, + } + # yapf: enable + return inputs_def + + def build_inputs( + self, + image_shape=[3, None, None], + fields=['image', 'im_id', 'gt_bbox', 'gt_class'], # for train + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape) + feed_vars = OrderedDict([(key, fluid.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, 'train') diff --git a/ppdet/modeling/architectures/yolov3.py b/ppdet/modeling/architectures/yolov3.py index 2912ffda5..d7e3948fe 100644 --- a/ppdet/modeling/architectures/yolov3.py +++ b/ppdet/modeling/architectures/yolov3.py @@ -64,18 +64,53 @@ class YOLOv3(object): body_feats = [fluid.layers.cast(v, 'float32') for v in body_feats] if mode == 'train': - gt_box = feed_vars['gt_box'] - gt_label = feed_vars['gt_label'] + gt_bbox = feed_vars['gt_bbox'] + gt_class = feed_vars['gt_class'] gt_score = feed_vars['gt_score'] return { - 'loss': self.yolo_head.get_loss(body_feats, gt_box, gt_label, + 'loss': self.yolo_head.get_loss(body_feats, gt_bbox, gt_class, gt_score) } else: im_size = feed_vars['im_size'] return self.yolo_head.get_prediction(body_feats, im_size) + def _inputs_def(self, image_shape, num_max_boxes): + im_shape = [None] + image_shape + # yapf: disable + inputs_def = { + 'image': {'shape': im_shape, 'dtype': 'float32', 'lod_level': 0}, + 'im_size': {'shape': [None, 2], 'dtype': 'int32', 'lod_level': 0}, + 'im_id': {'shape': [None, 1], 'dtype': 'int32', 'lod_level': 0}, + 'gt_bbox': {'shape': [None, num_max_boxes, 4], 'dtype': 'float32', 'lod_level': 0}, + 'gt_class': {'shape': [None, num_max_boxes], 'dtype': 'int32', 'lod_level': 0}, + 'gt_score': {'shape': [None, num_max_boxes], 'dtype': 'float32', 'lod_level': 0}, + 'is_difficult': {'shape': [None, num_max_boxes],'dtype': 'int32', 'lod_level': 0}, + } + # yapf: enable + return inputs_def + + def build_inputs( + self, + image_shape=[3, None, None], + fields=['image', 'gt_bbox', 'gt_class', 'gt_score'], # for train + num_max_boxes=50, + use_dataloader=True, + iterable=False): + inputs_def = self._inputs_def(image_shape, num_max_boxes) + feed_vars = OrderedDict([(key, fluid.data( + name=key, + shape=inputs_def[key]['shape'], + dtype=inputs_def[key]['dtype'], + lod_level=inputs_def[key]['lod_level'])) for key in fields]) + loader = fluid.io.DataLoader.from_generator( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=iterable) if use_dataloader else None + return feed_vars, loader + def train(self, feed_vars): return self.build(feed_vars, mode='train') diff --git a/ppdet/modeling/model_input.py b/ppdet/modeling/model_input.py deleted file mode 100644 index 0e7e3bf05..000000000 --- a/ppdet/modeling/model_input.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import print_function -from __future__ import division - -from collections import OrderedDict -from ppdet.data.transform.operators import * - -from paddle import fluid - -__all__ = ['create_feed'] - -# yapf: disable -feed_var_def = [ - {'name': 'im_info', 'shape': [3], 'dtype': 'float32', 'lod_level': 0}, - {'name': 'im_id', 'shape': [1], 'dtype': 'int32', 'lod_level': 0}, - {'name': 'gt_box', 'shape': [4], 'dtype': 'float32', 'lod_level': 1}, - {'name': 'gt_label', 'shape': [1], 'dtype': 'int32', 'lod_level': 1}, - {'name': 'is_crowd', 'shape': [1], 'dtype': 'int32', 'lod_level': 1}, - {'name': 'gt_mask', 'shape': [2], 'dtype': 'float32', 'lod_level': 3}, - {'name': 'is_difficult', 'shape': [1], 'dtype': 'int32', 'lod_level': 1}, - {'name': 'gt_score', 'shape': [1], 'dtype': 'float32', 'lod_level': 0}, - {'name': 'im_shape', 'shape': [3], 'dtype': 'float32', 'lod_level': 0}, - {'name': 'im_size', 'shape': [2], 'dtype': 'int32', 'lod_level': 0}, -] -# yapf: enable - - -def create_feed(feed, iterable=False, sub_prog_feed=False): - image_shape = feed.image_shape - feed_var_map = {var['name']: var for var in feed_var_def} - feed_var_map['image'] = { - 'name': 'image', - 'shape': image_shape, - 'dtype': 'float32', - 'lod_level': 0 - } - - # tensor padding with 0 is used instead of LoD tensor when - # num_max_boxes is set - if getattr(feed, 'num_max_boxes', None) is not None: - feed_var_map['gt_label']['shape'] = [feed.num_max_boxes] - feed_var_map['gt_score']['shape'] = [feed.num_max_boxes] - feed_var_map['gt_box']['shape'] = [feed.num_max_boxes, 4] - feed_var_map['is_difficult']['shape'] = [feed.num_max_boxes] - feed_var_map['gt_label']['lod_level'] = 0 - feed_var_map['gt_score']['lod_level'] = 0 - feed_var_map['gt_box']['lod_level'] = 0 - feed_var_map['is_difficult']['lod_level'] = 0 - - base_name_list = ['image'] - num_scale = getattr(feed, 'num_scale', 1) - sample_transform = feed.sample_transforms - multiscale_test = False - aug_flip = False - for t in sample_transform: - if isinstance(t, MultiscaleTestResize): - multiscale_test = True - aug_flip = t.use_flip - assert (len(t.target_size)+1)*(aug_flip+1) == num_scale, \ - "num_scale: {} is not equal to the actual number of scale: {}."\ - .format(num_scale, (len(t.target_size)+1)*(aug_flip+1)) - break - - if aug_flip: - num_scale //= 2 - base_name_list.insert(0, 'flip_image') - feed_var_map['flip_image'] = { - 'name': 'flip_image', - 'shape': image_shape, - 'dtype': 'float32', - 'lod_level': 0 - } - - image_name_list = [] - if multiscale_test: - for base_name in base_name_list: - for i in range(0, num_scale): - name = base_name if i == 0 else base_name + '_scale_' + str(i - - 1) - feed_var_map[name] = { - 'name': name, - 'shape': image_shape, - 'dtype': 'float32', - 'lod_level': 0 - } - image_name_list.append(name) - feed_var_map['im_info']['shape'] = [feed.num_scale * 3] - feed.fields = image_name_list + feed.fields[1:] - if sub_prog_feed: - box_names = ['bbox', 'bbox_flip'] - for box_name in box_names: - sub_prog_feed = { - 'name': box_name, - 'shape': [6], - 'dtype': 'float32', - 'lod_level': 1 - } - - feed.fields = feed.fields + [box_name] - feed_var_map[box_name] = sub_prog_feed - - feed_vars = OrderedDict([(key, fluid.layers.data( - name=feed_var_map[key]['name'], - shape=feed_var_map[key]['shape'], - dtype=feed_var_map[key]['dtype'], - lod_level=feed_var_map[key]['lod_level'])) for key in feed.fields]) - - loader = fluid.io.DataLoader.from_generator( - feed_list=list(feed_vars.values()), - capacity=64, - use_double_buffer=True, - iterable=iterable) if not sub_prog_feed else None - return loader, feed_vars diff --git a/ppdet/modeling/target_assigners.py b/ppdet/modeling/target_assigners.py index 72297aff9..520d56287 100644 --- a/ppdet/modeling/target_assigners.py +++ b/ppdet/modeling/target_assigners.py @@ -59,9 +59,9 @@ class CascadeBBoxAssigner(object): ] outs = fluid.layers.generate_proposal_labels( rpn_rois=input_rois, - gt_classes=feed_vars['gt_label'], + gt_classes=feed_vars['gt_class'], is_crowd=feed_vars['is_crowd'], - gt_boxes=feed_vars['gt_box'], + gt_boxes=feed_vars['gt_bbox'], im_info=feed_vars['im_info'], batch_size_per_im=self.batch_size_per_im, fg_thresh=self.fg_thresh[curr_stage], @@ -71,5 +71,6 @@ class CascadeBBoxAssigner(object): use_random=self.use_random, class_nums=self.class_nums if self.class_aware else 2, is_cls_agnostic=not self.class_aware, - is_cascade_rcnn=True if curr_stage > 0 and not self.class_aware else False) + is_cascade_rcnn=True + if curr_stage > 0 and not self.class_aware else False) return outs diff --git a/ppdet/modeling/tests/test_architectures.py b/ppdet/modeling/tests/test_architectures.py index 7df958066..f74b0aeaa 100644 --- a/ppdet/modeling/tests/test_architectures.py +++ b/ppdet/modeling/tests/test_architectures.py @@ -37,16 +37,18 @@ class TestFasterRCNN(unittest.TestCase): @prog_scope() def test_train(self): - train_feed = create(self.cfg['train_feed']) model = create(self.detector_type) - _, feed_vars = create_feed(train_feed) + inputs_def = self.cfg['TrainReader']['inputs_def'] + inputs_def['image_shape'] = [3, None, None] + feed_vars, _ = model.build_inputs(**inputs_def) train_fetches = model.train(feed_vars) @prog_scope() def test_test(self): - test_feed = create(self.cfg['eval_feed']) + inputs_def = self.cfg['EvalReader']['inputs_def'] + inputs_def['image_shape'] = [3, None, None] model = create(self.detector_type) - _, feed_vars = create_feed(test_feed) + feed_vars, _ = model.build_inputs(**inputs_def) test_fetches = model.eval(feed_vars) diff --git a/ppdet/utils/coco_eval.py b/ppdet/utils/coco_eval.py index cb5df475f..f67f356c4 100644 --- a/ppdet/utils/coco_eval.py +++ b/ppdet/utils/coco_eval.py @@ -22,8 +22,6 @@ import sys import json import cv2 import numpy as np -import matplotlib -matplotlib.use('Agg') import logging logger = logging.getLogger(__name__) @@ -221,7 +219,8 @@ def bbox2out(results, clsid2catid, is_bbox_normalized=False): clip_bbox([xmin, ymin, xmax, ymax]) w = xmax - xmin h = ymax - ymin - im_height, im_width = t['im_shape'][0][i].tolist() + im_shape = t['im_shape'][0][i].tolist() + im_height, im_width = int(im_shape[0]), int(im_shape[1]) xmin *= im_width ymin *= im_height w *= im_width diff --git a/ppdet/utils/download.py b/ppdet/utils/download.py index f561e3edd..caf869258 100644 --- a/ppdet/utils/download.py +++ b/ppdet/utils/download.py @@ -30,7 +30,10 @@ from .voc_utils import create_list import logging logger = logging.getLogger(__name__) -__all__ = ['get_weights_path', 'get_dataset_path', 'download_dataset', 'create_voc_list'] +__all__ = [ + 'get_weights_path', 'get_dataset_path', 'download_dataset', + 'create_voc_list' +] WEIGHTS_HOME = osp.expanduser("~/.cache/paddle/weights") DATASET_HOME = osp.expanduser("~/.cache/paddle/dataset") @@ -72,8 +75,9 @@ DATASETS = { 'a4a898d6193db4b9ef3260a68bad0dc7', ), ], ["WIDER_train", "WIDER_val", "wider_face_split"]), 'fruit': ([( - 'https://dataset.bj.bcebos.com/PaddleDetection_demo/fruit-detection.tar', - 'ee4a1bf2e321b75b0850cc6e063f79d7', ), ], ["fruit-detection"]), + 'https://dataset.bj.bcebos.com/PaddleDetection_demo/fruit.tar', + 'baa8806617a54ccf3685fa7153388ae6', ), ], + ['Annotations', 'JPEGImages']), 'objects365': (), } @@ -101,17 +105,19 @@ def get_dataset_path(path, annotation, image_dir): "downloading dataset...".format( osp.realpath(path), DATASET_HOME)) + data_name = os.path.split(path.strip().lower())[-1] for name, dataset in DATASETS.items(): - if os.path.split(path.strip().lower())[-1] == name: + if data_name == name: logger.info("Parse dataset_dir {} as dataset " "{}".format(path, name)) if name == 'objects365': raise NotImplementedError( - "Dataset {} is not valid for download automatically. Please apply and download the dataset from https://www.objects365.org/download.html". - format(name)) + "Dataset {} is not valid for download automatically. " + "Please apply and download the dataset from " + "https://www.objects365.org/download.html".format(name)) data_dir = osp.join(DATASET_HOME, name) # For voc, only check dir VOCdevkit/VOC2012, VOCdevkit/VOC2007 - if name == 'voc': + if name == 'voc' or name == 'fruit': exists = True for sub_dir in dataset[1]: check_dir = osp.join(data_dir, sub_dir) @@ -123,7 +129,7 @@ def get_dataset_path(path, annotation, image_dir): return data_dir # voc exist is checked above, voc is not exist here - check_exist = name != 'voc' + check_exist = name != 'voc' and name != 'fruit' for url, md5sum in dataset[0]: get_path(url, data_dir, md5sum, check_exist) @@ -147,7 +153,7 @@ def create_voc_list(data_dir, devkit_subdir='VOCdevkit'): # NOTE: since using auto download VOC # dataset, VOC default label list should be used, # do not generate label_list.txt here. For default - # label, see ../data/source/voc_loader.py + # label, see ../data/source/voc.py create_list(devkit_dir, years, data_dir) logger.info("Create voc file list finished") @@ -345,6 +351,8 @@ def _move_and_merge_tree(src, dst): """ if not osp.exists(dst): shutil.move(src, dst) + elif osp.isfile(src): + shutil.move(src, dst) else: for fp in os.listdir(src): src_fp = osp.join(src, fp) diff --git a/ppdet/utils/eval_utils.py b/ppdet/utils/eval_utils.py index a1b92b911..c9e79c007 100644 --- a/ppdet/utils/eval_utils.py +++ b/ppdet/utils/eval_utils.py @@ -23,8 +23,8 @@ import time import paddle.fluid as fluid -from ppdet.utils.voc_eval import bbox_eval as voc_bbox_eval -from ppdet.utils.post_process import mstest_box_post_process, mstest_mask_post_process, box_flip +from .voc_eval import bbox_eval as voc_bbox_eval +from .post_process import mstest_box_post_process, mstest_mask_post_process, box_flip __all__ = ['parse_fetches', 'eval_run', 'eval_results', 'json_eval_results'] @@ -41,7 +41,7 @@ def parse_fetches(fetches, prog=None, extra_keys=None): for k, v in fetches.items(): if hasattr(v, 'name'): keys.append(k) - v.persistable = True + #v.persistable = True values.append(v.name) else: cls.append(v) @@ -174,19 +174,19 @@ def eval_run(exe, def eval_results(results, - feed, metric, num_classes, resolution=None, is_bbox_normalized=False, output_directory=None, - map_type='11point'): + map_type='11point', + dataset=None): """Evaluation for evaluation program results""" box_ap_stats = [] if metric == 'COCO': from ppdet.utils.coco_eval import proposal_eval, bbox_eval, mask_eval - anno_file = getattr(feed.dataset, 'annotation', None) - with_background = getattr(feed, 'with_background', True) + anno_file = dataset.get_anno() + with_background = dataset.with_background if 'proposal' in results[0]: output = 'proposal.json' if output_directory: @@ -224,13 +224,13 @@ def eval_results(results, return box_ap_stats -def json_eval_results(feed, metric, json_directory=None): +def json_eval_results(metric, json_directory=None, dataset=None): """ cocoapi eval with already exists proposal.json, bbox.json or mask.json """ assert metric == 'COCO' from ppdet.utils.coco_eval import cocoapi_eval - anno_file = getattr(feed.dataset, 'annotation', None) + anno_file = dataset.get_anno() json_file_list = ['proposal.json', 'bbox.json', 'mask.json'] if json_directory: assert os.path.exists( diff --git a/ppdet/utils/voc_eval.py b/ppdet/utils/voc_eval.py index 458a010bb..1312ac1e2 100644 --- a/ppdet/utils/voc_eval.py +++ b/ppdet/utils/voc_eval.py @@ -21,7 +21,7 @@ import os import sys import numpy as np -from ..data.source.voc_loader import pascalvoc_label +from ..data.source.voc import pascalvoc_label from .map_utils import DetectionMAP from .coco_eval import bbox2out @@ -69,13 +69,13 @@ def bbox_eval(results, if bboxes.shape == (1, 1) or bboxes is None: continue - gt_boxes = t['gt_box'][0] - gt_labels = t['gt_label'][0] + gt_boxes = t['gt_bbox'][0] + gt_labels = t['gt_class'][0] difficults = t['is_difficult'][0] if not evaluate_difficult \ else None - if len(t['gt_box'][1]) == 0: - # gt_box, gt_label, difficult read as zero padded Tensor + if len(t['gt_bbox'][1]) == 0: + # gt_bbox, gt_class, difficult read as zero padded Tensor bbox_idx = 0 for i in range(len(gt_boxes)): gt_box = gt_boxes[i] @@ -90,7 +90,7 @@ def bbox_eval(results, bbox_idx += bbox_num else: # gt_box, gt_label, difficult read as LoDTensor - gt_box_lengths = t['gt_box'][1][0] + gt_box_lengths = t['gt_bbox'][1][0] bbox_idx = 0 gt_box_idx = 0 for i in range(len(bbox_lengths)): diff --git a/ppdet/utils/widerface_eval_utils.py b/ppdet/utils/widerface_eval_utils.py index a19cd0835..6a7d25418 100644 --- a/ppdet/utils/widerface_eval_utils.py +++ b/ppdet/utils/widerface_eval_utils.py @@ -19,7 +19,7 @@ from __future__ import print_function import os import numpy as np -from ppdet.data.source.widerface_loader import widerface_label +from ppdet.data.source.widerface import widerface_label from ppdet.utils.coco_eval import bbox2out import logging diff --git a/requirements.txt b/requirements.txt index 141cf8a9a..9fc1995d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ docstring_parser @ http://github.com/willthefrog/docstring_parser/tarball/master typeguard ; python_version >= '3.4' tb-paddle tensorboard >= 1.15 +pycocotools diff --git a/tools/eval.py b/tools/eval.py index 779d30b1b..bdd21a1e5 100644 --- a/tools/eval.py +++ b/tools/eval.py @@ -36,8 +36,9 @@ import paddle.fluid as fluid from ppdet.utils.eval_utils import parse_fetches, eval_run, eval_results, json_eval_results import ppdet.utils.checkpoint as checkpoint from ppdet.utils.check import check_gpu, check_version -from ppdet.modeling.model_input import create_feed -from ppdet.data.data_feed import create_reader + +from ppdet.data.reader import create_reader + from ppdet.core.workspace import load_config, merge_config, create from ppdet.utils.cli import ArgsParser @@ -63,11 +64,6 @@ def main(): # check if paddlepaddle version is satisfied check_version() - if 'eval_feed' not in cfg: - eval_feed = create(main_arch + 'EvalFeed') - else: - eval_feed = create(cfg.eval_feed) - multi_scale_test = getattr(cfg, 'MultiScaleTEST', None) # define executor @@ -80,13 +76,15 @@ def main(): eval_prog = fluid.Program() with fluid.program_guard(eval_prog, startup_prog): with fluid.unique_name.guard(): - loader, feed_vars = create_feed(eval_feed) + inputs_def = cfg['EvalReader']['inputs_def'] + feed_vars, loader = model.build_inputs(**inputs_def) if multi_scale_test is None: fetches = model.eval(feed_vars) else: fetches = model.eval(feed_vars, multi_scale_test) eval_prog = eval_prog.clone(True) - reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + + reader = create_reader(cfg.EvalReader) loader.set_sample_list_generator(reader, place) # eval already exists json file @@ -96,30 +94,26 @@ def main(): "output_eval directly. And proposal.json, bbox.json and mask.json " "will be detected by default.") json_eval_results( - eval_feed, cfg.metric, json_directory=FLAGS.output_eval) + cfg.metric, json_directory=FLAGS.output_eval, dataset=dataset) return compile_program = fluid.compiler.CompiledProgram( eval_prog).with_data_parallel() - # load model - exe.run(startup_prog) - if 'weights' in cfg: - checkpoint.load_params(exe, eval_prog, cfg.weights) - assert cfg.metric != 'OID', "eval process of OID dataset \ is not supported." + if cfg.metric == "WIDERFACE": raise ValueError("metric type {} does not support in tools/eval.py, " "please use tools/face_eval.py".format(cfg.metric)) assert cfg.metric in ['COCO', 'VOC'], \ "unknown metric type {}".format(cfg.metric) extra_keys = [] - + if cfg.metric == 'COCO': extra_keys = ['im_info', 'im_id', 'im_shape'] if cfg.metric == 'VOC': - extra_keys = ['gt_box', 'gt_label', 'is_difficult'] + extra_keys = ['gt_bbox', 'gt_class', 'is_difficult'] keys, values, cls = parse_fetches(fetches, eval_prog, extra_keys) @@ -129,6 +123,8 @@ def main(): callable(model.is_bbox_normalized): is_bbox_normalized = model.is_bbox_normalized() + dataset = cfg['EvalReader']['dataset'] + sub_eval_prog = None sub_keys = None sub_values = None @@ -137,32 +133,44 @@ def main(): sub_eval_prog = fluid.Program() with fluid.program_guard(sub_eval_prog, startup_prog): with fluid.unique_name.guard(): - _, feed_vars = create_feed(eval_feed, False, sub_prog_feed=True) + inputs_def = cfg['EvalReader']['inputs_def'] + inputs_def['mask_branch'] = True + feed_vars, eval_loader = model.build_inputs(**inputs_def) sub_fetches = model.eval( feed_vars, multi_scale_test, mask_branch=True) - extra_keys = [] - if cfg.metric == 'COCO': - extra_keys = ['im_id', 'im_shape'] - if cfg.metric == 'VOC': - extra_keys = ['gt_box', 'gt_label', 'is_difficult'] + assert cfg.metric == 'COCO' + extra_keys = ['im_id', 'im_shape'] sub_keys, sub_values, _ = parse_fetches(sub_fetches, sub_eval_prog, extra_keys) sub_eval_prog = sub_eval_prog.clone(True) - if 'weights' in cfg: - checkpoint.load_params(exe, sub_eval_prog, cfg.weights) + #if 'weights' in cfg: + # checkpoint.load_params(exe, sub_eval_prog, cfg.weights) + + # load model + exe.run(startup_prog) + if 'weights' in cfg: + checkpoint.load_params(exe, startup_prog, cfg.weights) results = eval_run(exe, compile_program, loader, keys, values, cls, cfg, sub_eval_prog, sub_keys, sub_values) + #print(cfg['EvalReader']['dataset'].__dict__) # evaluation resolution = None if 'mask' in results[0]: resolution = model.mask_head.resolution # if map_type not set, use default 11point, only use in VOC eval map_type = cfg.map_type if 'map_type' in cfg else '11point' - eval_results(results, eval_feed, cfg.metric, cfg.num_classes, resolution, - is_bbox_normalized, FLAGS.output_eval, map_type) + eval_results( + results, + cfg.metric, + cfg.num_classes, + resolution, + is_bbox_normalized, + FLAGS.output_eval, + map_type, + dataset=dataset) if __name__ == '__main__': @@ -172,12 +180,6 @@ if __name__ == '__main__': action='store_true', default=False, help="Whether to re eval with already exists bbox.json or mask.json") - parser.add_argument( - "-d", - "--dataset_dir", - default=None, - type=str, - help="Dataset path, same as DataFeed.dataset.dataset_dir") parser.add_argument( "-f", "--output_eval", diff --git a/tools/export_model.py b/tools/export_model.py index 4db937dd6..c7de582f2 100644 --- a/tools/export_model.py +++ b/tools/export_model.py @@ -82,11 +82,6 @@ def main(): merge_config(FLAGS.opt) - if 'test_feed' not in cfg: - test_feed = create(main_arch + 'TestFeed') - else: - test_feed = create(cfg.test_feed) - # Use CPU for exporting inference model instead of GPU place = fluid.CPUPlace() exe = fluid.Executor(place) @@ -97,7 +92,9 @@ def main(): infer_prog = fluid.Program() with fluid.program_guard(infer_prog, startup_prog): with fluid.unique_name.guard(): - _, feed_vars = create_feed(test_feed, iterable=True) + inputs_def = cfg['TestReader']['inputs_def'] + inputs_def['use_dataloader'] = False + feed_vars, _ = model.build_inputs(**inputs_def) test_fetches = model.test(feed_vars) infer_prog = infer_prog.clone(True) diff --git a/tools/face_eval.py b/tools/face_eval.py index 5ddfd17d5..7f66021e9 100644 --- a/tools/face_eval.py +++ b/tools/face_eval.py @@ -29,7 +29,6 @@ from ppdet.utils.check import check_gpu from ppdet.utils.widerface_eval_utils import get_shrink, bbox_vote, \ save_widerface_bboxes, save_fddb_bboxes, to_chw_bgr from ppdet.core.workspace import load_config, merge_config, create -from ppdet.modeling.model_input import create_feed import logging FORMAT = '%(asctime)s-%(levelname)s: %(message)s' @@ -53,7 +52,7 @@ def face_img_process(image, def face_eval_run(exe, compile_program, fetches, - img_root_dir, + image_dir, gt_file, pred_dir='output/pred', eval_mode='widerface', @@ -73,9 +72,10 @@ def face_eval_run(exe, dets_dist = OrderedDict() for iter_id, im_path in enumerate(imid2path): - image_path = os.path.join(img_root_dir, im_path) + image_path = os.path.join(image_dir, im_path) if eval_mode == 'fddb': image_path += '.jpg' + assert os.path.exists(image_path) image = Image.open(image_path).convert('RGB') if multi_scale: shrink, max_shrink = get_shrink(image.size[1], image.size[0]) @@ -220,11 +220,6 @@ def main(): # check if set use_gpu=True in paddlepaddle cpu version check_gpu(cfg.use_gpu) - if 'eval_feed' not in cfg: - eval_feed = create(main_arch + 'EvalFeed') - else: - eval_feed = create(cfg.eval_feed) - # define executor place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() exe = fluid.Executor(place) @@ -235,7 +230,9 @@ def main(): eval_prog = fluid.Program() with fluid.program_guard(eval_prog, startup_prog): with fluid.unique_name.guard(): - _, feed_vars = create_feed(eval_feed, iterable=True) + inputs_def = cfg['EvalReader']['inputs_def'] + inputs_def['use_dataloader'] = False + feed_vars, _ = model.build_inputs(**inputs_def) fetches = model.eval(feed_vars) eval_prog = eval_prog.clone(True) @@ -248,21 +245,19 @@ def main(): assert cfg.metric in ['WIDERFACE'], \ "unknown metric type {}".format(cfg.metric) - annotation_file = getattr(eval_feed.dataset, 'annotation', None) - dataset_dir = FLAGS.dataset_dir if FLAGS.dataset_dir else \ - getattr(eval_feed.dataset, 'dataset_dir', None) - img_root_dir = dataset_dir - if FLAGS.eval_mode == "widerface": - image_dir = getattr(eval_feed.dataset, 'image_dir', None) - img_root_dir = os.path.join(dataset_dir, image_dir) - gt_file = os.path.join(dataset_dir, annotation_file) + dataset = cfg['EvalReader']['dataset'] + + annotation_file = dataset.get_anno() + dataset_dir = dataset.dataset_dir + image_dir = dataset.image_dir + pred_dir = FLAGS.output_eval if FLAGS.output_eval else 'output/pred' face_eval_run( exe, eval_prog, fetches, - img_root_dir, - gt_file, + image_dir, + annotation_file, pred_dir=pred_dir, eval_mode=FLAGS.eval_mode, multi_scale=FLAGS.multi_scale) diff --git a/tools/infer.py b/tools/infer.py index 5e303da4c..89591080d 100644 --- a/tools/infer.py +++ b/tools/infer.py @@ -38,8 +38,6 @@ set_paddle_flags( from paddle import fluid from ppdet.core.workspace import load_config, merge_config, create -from ppdet.modeling.model_input import create_feed -from ppdet.data.data_feed import create_reader from ppdet.utils.eval_utils import parse_fetches from ppdet.utils.cli import ArgsParser @@ -47,6 +45,8 @@ from ppdet.utils.check import check_gpu, check_version from ppdet.utils.visualizer import visualize_results import ppdet.utils.checkpoint as checkpoint +from ppdet.data.reader import create_reader + import logging FORMAT = '%(asctime)s-%(levelname)s: %(message)s' logging.basicConfig(level=logging.INFO, format=FORMAT) @@ -110,13 +110,10 @@ def main(): # check if paddlepaddle version is satisfied check_version() - if 'test_feed' not in cfg: - test_feed = create(main_arch + 'TestFeed') - else: - test_feed = create(cfg.test_feed) + dataset = cfg.TestReader['dataset'] test_images = get_test_images(FLAGS.infer_dir, FLAGS.infer_img) - test_feed.dataset.add_images(test_images) + dataset.set_images(test_images) place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() exe = fluid.Executor(place) @@ -127,11 +124,13 @@ def main(): infer_prog = fluid.Program() with fluid.program_guard(infer_prog, startup_prog): with fluid.unique_name.guard(): - loader, feed_vars = create_feed(test_feed, iterable=True) + inputs_def = cfg['TestReader']['inputs_def'] + inputs_def['iterable'] = True + feed_vars, loader = model.build_inputs(**inputs_def) test_fetches = model.test(feed_vars) infer_prog = infer_prog.clone(True) - reader = create_reader(test_feed) + reader = create_reader(cfg.TestReader) loader.set_sample_list_generator(reader, place) exe.run(startup_prog) @@ -158,9 +157,10 @@ def main(): if cfg.metric == "WIDERFACE": from ppdet.utils.widerface_eval_utils import bbox2out, get_category_info - anno_file = getattr(test_feed.dataset, 'annotation', None) - with_background = getattr(test_feed, 'with_background', True) - use_default_label = getattr(test_feed, 'use_default_label', False) + anno_file = dataset.get_anno() + with_background = dataset.with_background + use_default_label = dataset.use_default_label + clsid2catid, catid2name = get_category_info(anno_file, with_background, use_default_label) @@ -177,7 +177,7 @@ def main(): tb_image_step = 0 tb_image_frame = 0 # each frame can display ten pictures at most. - imid2path = reader.imid2path + imid2path = dataset.get_imid2path() for iter_id, data in enumerate(loader()): outs = exe.run(infer_prog, feed=data, diff --git a/tools/train.py b/tools/train.py index 04d1c621c..1e464e2e9 100644 --- a/tools/train.py +++ b/tools/train.py @@ -39,7 +39,7 @@ from paddle import fluid from ppdet.experimental import mixed_precision_context from ppdet.core.workspace import load_config, merge_config, create -from ppdet.data.data_feed import create_reader +from ppdet.data.reader import create_reader from ppdet.utils.cli import print_total_cfg from ppdet.utils import dist_utils @@ -48,7 +48,6 @@ from ppdet.utils.stats import TrainingStats from ppdet.utils.cli import ArgsParser from ppdet.utils.check import check_gpu, check_version import ppdet.utils.checkpoint as checkpoint -from ppdet.modeling.model_input import create_feed import logging FORMAT = '%(asctime)s-%(levelname)s: %(message)s' @@ -89,17 +88,6 @@ def main(): else: devices_num = int(os.environ.get('CPU_NUM', 1)) - if 'train_feed' not in cfg: - train_feed = create(main_arch + 'TrainFeed') - else: - train_feed = create(cfg.train_feed) - - if FLAGS.eval: - if 'eval_feed' not in cfg: - eval_feed = create(main_arch + 'EvalFeed') - else: - eval_feed = create(cfg.eval_feed) - if 'FLAGS_selected_gpus' in env: device_id = int(env['FLAGS_selected_gpus']) else: @@ -116,8 +104,6 @@ def main(): with fluid.program_guard(train_prog, startup_prog): with fluid.unique_name.guard(): model = create(main_arch) - train_loader, feed_vars = create_feed(train_feed) - if FLAGS.fp16: assert (getattr(model.backbone, 'norm_type', None) != 'affine_channel'), \ @@ -125,8 +111,9 @@ def main(): ' please modify backbone settings to use batch norm' with mixed_precision_context(FLAGS.loss_scale, FLAGS.fp16) as ctx: + inputs_def = cfg['TrainReader']['inputs_def'] + feed_vars, train_loader = model.build_inputs(**inputs_def) train_fetches = model.train(feed_vars) - loss = train_fetches['loss'] if FLAGS.fp16: loss *= ctx.get_loss_scale_var() @@ -145,11 +132,12 @@ def main(): with fluid.program_guard(eval_prog, startup_prog): with fluid.unique_name.guard(): model = create(main_arch) - eval_loader, feed_vars = create_feed(eval_feed) + inputs_def = cfg['EvalReader']['inputs_def'] + feed_vars, eval_loader = model.build_inputs(**inputs_def) fetches = model.eval(feed_vars) eval_prog = eval_prog.clone(True) - eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + eval_reader = create_reader(cfg.EvalReader) eval_loader.set_sample_list_generator(eval_reader, place) # parse eval fetches @@ -157,9 +145,9 @@ def main(): if cfg.metric == 'COCO': extra_keys = ['im_info', 'im_id', 'im_shape'] if cfg.metric == 'VOC': - extra_keys = ['gt_box', 'gt_label', 'is_difficult'] + extra_keys = ['gt_bbox', 'gt_class', 'is_difficult'] if cfg.metric == 'WIDERFACE': - extra_keys = ['im_id', 'im_shape', 'gt_box'] + extra_keys = ['im_id', 'im_shape', 'gt_bbox'] eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, extra_keys) @@ -206,8 +194,8 @@ def main(): checkpoint.load_params( exe, train_prog, cfg.pretrain_weights, ignore_params=ignore_params) - train_reader = create_reader(train_feed, (cfg.max_iters - start_iter) * - devices_num, FLAGS.dataset_dir) + train_reader = create_reader(cfg.TrainReader, + (cfg.max_iters - start_iter) * devices_num) train_loader.set_sample_list_generator(train_reader, place) # whether output bbox is normalized in model output layer @@ -273,8 +261,9 @@ def main(): if 'mask' in results[0]: resolution = model.mask_head.resolution box_ap_stats = eval_results( - results, eval_feed, cfg.metric, cfg.num_classes, resolution, - is_bbox_normalized, FLAGS.output_eval, map_type) + results, cfg.metric, cfg.num_classes, resolution, + is_bbox_normalized, FLAGS.output_eval, map_type, + cfg['EvalReader']['dataset']) # use tb_paddle to log mAP if FLAGS.use_tb: @@ -320,12 +309,6 @@ if __name__ == '__main__': default=None, type=str, help="Evaluation directory, default is current directory.") - parser.add_argument( - "-d", - "--dataset_dir", - default=None, - type=str, - help="Dataset path, same as DataFeed.dataset.dataset_dir") parser.add_argument( "--use_tb", type=bool, -- GitLab